# HG changeset patch # User Olaf Wintermann # Date 1760182309 -7200 # Node ID a0e1ff1005127a4680c9ec0596b54b7b309b30dd # Parent 8801df33144ff998377d920b32ed7f6be9f7c35d add link button (Cocoa) diff -r 8801df33144f -r a0e1ff100512 make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj --- a/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj Sat Oct 11 12:00:07 2025 +0200 +++ b/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj Sat Oct 11 13:31:49 2025 +0200 @@ -53,6 +53,9 @@ ED8687E52D999CF3002F3EC2 /* menu.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8687E42D999CF3002F3EC2 /* menu.m */; }; ED99F04A2E5CBD2E00A4CC97 /* widget.m in Sources */ = {isa = PBXBuildFile; fileRef = ED99F0492E5CBD2E00A4CC97 /* widget.m */; }; EDB452C32E302C65006FB12D /* image.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB452C22E302C65006FB12D /* image.m */; }; + EDC315A92E9A739300403776 /* json.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A62E9A739300403776 /* json.c */; }; + EDC315AA2E9A739300403776 /* properties.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A72E9A739300403776 /* properties.c */; }; + EDC315AB2E9A739300403776 /* streams.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A82E9A739300403776 /* streams.c */; }; EDCD22272E59EEF5000612AF /* list.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22262E59EEF5000612AF /* list.m */; }; EDCD22352E59F3B1000612AF /* ListDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22342E59F3B1000612AF /* ListDataSource.m */; }; EDCD22382E5A160A000612AF /* ListDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22372E5A160A000612AF /* ListDelegate.m */; }; @@ -175,6 +178,12 @@ ED99F04C2E5CBE5000A4CC97 /* widget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = widget.h; path = /Users/olaf/Projekte/toolkit/ui/ui/widget.h; sourceTree = ""; }; EDB452C12E302C65006FB12D /* image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = image.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/image.h; sourceTree = ""; }; EDB452C22E302C65006FB12D /* image.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = image.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/image.m; sourceTree = ""; }; + EDC315A32E9A736900403776 /* json.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = json.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/json.h; sourceTree = ""; }; + EDC315A42E9A736900403776 /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/properties.h; sourceTree = ""; }; + EDC315A52E9A736900403776 /* streams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = streams.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/streams.h; sourceTree = ""; }; + EDC315A62E9A739300403776 /* json.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = json.c; path = /Users/olaf/Projekte/toolkit/ucx/json.c; sourceTree = ""; }; + EDC315A72E9A739300403776 /* properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = properties.c; path = /Users/olaf/Projekte/toolkit/ucx/properties.c; sourceTree = ""; }; + EDC315A82E9A739300403776 /* streams.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = streams.c; path = /Users/olaf/Projekte/toolkit/ucx/streams.c; sourceTree = ""; }; EDCD22252E59EEF5000612AF /* list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = list.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/list.h; sourceTree = ""; }; EDCD22262E59EEF5000612AF /* list.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = list.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/list.m; sourceTree = ""; }; EDCD22332E59F3B1000612AF /* ListDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ListDataSource.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/ListDataSource.h; sourceTree = ""; }; @@ -258,6 +267,9 @@ ED6580F82CFF1A1200F5402F /* ucx */ = { isa = PBXGroup; children = ( + EDC315A62E9A739300403776 /* json.c */, + EDC315A72E9A739300403776 /* properties.c */, + EDC315A82E9A739300403776 /* streams.c */, ED6580F92CFF1A3000F5402F /* allocator.c */, ED6580FA2CFF1A3000F5402F /* array_list.c */, ED6580FB2CFF1A3000F5402F /* buffer.c */, @@ -299,6 +311,9 @@ ED65810D2CFF1A3000F5402F /* test.h */, ED65810E2CFF1A3000F5402F /* tree.h */, ED65810F2CFF1A3000F5402F /* utils.h */, + EDC315A32E9A736900403776 /* json.h */, + EDC315A42E9A736900403776 /* properties.h */, + EDC315A52E9A736900403776 /* streams.h */, ); name = cx; path = /Users/olaf/Projekte/toolkit/ucx/cx; @@ -471,6 +486,9 @@ ED2F55AE2E34FAD800A84793 /* Toolbar.m in Sources */, ED65811E2CFF1A3000F5402F /* tree.c in Sources */, ED6581202CFF1A3000F5402F /* mempool.c in Sources */, + EDC315A92E9A739300403776 /* json.c in Sources */, + EDC315AA2E9A739300403776 /* properties.c in Sources */, + EDC315AB2E9A739300403776 /* streams.c in Sources */, ED6581212CFF1A3000F5402F /* map.c in Sources */, ED6581222CFF1A3000F5402F /* hash_map.c in Sources */, ED6581232CFF1A3000F5402F /* array_list.c in Sources */, diff -r 8801df33144f -r a0e1ff100512 make/xcode/toolkit/toolkit/main.m --- a/make/xcode/toolkit/toolkit/main.m Sat Oct 11 12:00:07 2025 +0200 +++ b/make/xcode/toolkit/toolkit/main.m Sat Oct 11 13:31:49 2025 +0200 @@ -41,6 +41,7 @@ UiList *list2; UiList *sidebar_list; UiList *sidebar_list2; + UiString *link; } MyDocument; MyDocument* create_doc(void) { @@ -76,6 +77,8 @@ ui_list_append(doc->sidebar_list2, "Item 3"); ui_list_append(doc->sidebar_list2, "Item 4"); + doc->link = ui_string_new(ctx, "link"); + return doc; } @@ -138,12 +141,21 @@ ui_left_panel0(obj) { - ui_button(obj, .label = "left"); + ui_grid(obj, .margin_left = 10, .margin_right = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) { + ui_button(obj, .label = "left", .hexpand = TRUE, .hfill = TRUE); + ui_newline(obj); + + ui_linkbutton(obj, .varname = "link", .hexpand = TRUE, .hfill = TRUE); + } } ui_right_panel0(obj) { - ui_button(obj, .label = "right"); + ui_grid(obj, .margin_left = 10, .margin_right = 10, .fill = TRUE) { + ui_button(obj, .label = "right", .hexpand = TRUE, .hfill = TRUE); + } } + + ui_linkbutton_value_set(doc->link, "unixwork", "https://unixwork.de/"); UiModel *model = ui_model(obj->ctx, UI_STRING, "Column 0", UI_STRING, "Column 1", UI_STRING, "Column 2", -1); diff -r 8801df33144f -r a0e1ff100512 ui/cocoa/button.h --- a/ui/cocoa/button.h Sat Oct 11 12:00:07 2025 +0200 +++ b/ui/cocoa/button.h Sat Oct 11 13:31:49 2025 +0200 @@ -40,6 +40,21 @@ @end +@interface UiLinkButtonData : NSObject +@property UiObject *obj; +@property (weak) NSTextField *textfield; +@property (strong) NSString *label; +@property (strong) NSString *uri; +@property BOOL visited; +@property ui_callback onclick; +@property void *onclickdata; + +- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield; +- (void)setLinkDataFromJson:(const char*)jsonStr; +- (void)buildLink; + +@end + int64_t ui_togglebutton_get(UiInteger *i); void ui_togglebutton_set(UiInteger *i, int64_t value); @@ -49,3 +64,6 @@ int64_t ui_radiobuttons_get(UiInteger *i); void ui_radiobuttons_set(UiInteger *i, int64_t value); + +char* ui_linkbutton_get(UiString *s); +void ui_linkbutton_set(UiString *s, const char *str); diff -r 8801df33144f -r a0e1ff100512 ui/cocoa/button.m --- a/ui/cocoa/button.m Sat Oct 11 12:00:07 2025 +0200 +++ b/ui/cocoa/button.m Sat Oct 11 13:31:49 2025 +0200 @@ -31,6 +31,9 @@ #import "Container.h" #import +#import +#import + UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { NSButton *button = [[NSButton alloc] init]; button.translatesAutoresizingMaskIntoConstraints = NO; @@ -273,3 +276,187 @@ index++; } } + + +/* --------------------------- Link Button --------------------------- */ + +@implementation UiLinkButtonData + +- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield { + _obj = obj; + _textfield = textfield; + return self; +} + +- (void)setLinkDataFromJson:(const char*)jsonStr { + CxJson json; + cxJsonInit(&json, NULL); + cxJsonFill(&json, jsonStr); + + CxJsonValue *value; + if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) { + if(cxJsonIsObject(value)) { + CxJsonValue *label = cxJsonObjGet(value, "label"); + CxJsonValue *uri = cxJsonObjGet(value, "uri"); + CxJsonValue *visited = cxJsonObjGet(value, "visited"); + if(label) { + char *str = cxJsonIsString(label) ? cxJsonAsString(label) : NULL; + if(str) { + _label = [[NSString alloc]initWithUTF8String:str]; + } else { + _label = nil; + } + } + if(uri) { + char *str = cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL; + if(str) { + _uri = [[NSString alloc]initWithUTF8String:str]; + } else { + _uri = nil; + } + } + if(visited) { + _visited = cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE; + } + } + cxJsonValueFree(value); + } + cxJsonDestroy(&json); + + [self buildLink]; +} + +- (void)buildLink { + NSString *label = _label ? _label : @""; + + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:label]; + [attrString beginEditing]; + if(_uri) { + [attrString addAttribute:NSLinkAttributeName value:_uri range:NSMakeRange(0, attrString.length)]; + } + [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor systemBlueColor] range:NSMakeRange(0, attrString.length)]; + [attrString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, attrString.length)]; + [attrString endEditing]; + + [_textfield setAttributedStringValue:attrString]; +} + +@end + +static char* create_linkbutton_jsonvalue(const char *label, const char *uri, UiBool include_null, UiBool visited, UiBool set_visited) { + CxJsonValue *obj = cxJsonCreateObj(NULL); + if(label) { + cxJsonObjPutString(obj, CX_STR("label"), label); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL); + } + + if(uri) { + cxJsonObjPutString(obj, CX_STR("uri"), uri); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL); + } + + if(set_visited) { + cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); + } + + CxJsonWriter writer = cxJsonWriterCompact(); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND); + cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer); + cxJsonValueFree(obj); + cxBufferTerminate(&buf); + + return buf.space; +} + +UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) { + NSTextField *label = [[NSTextField alloc] init]; + label.editable = NO; + label.bezeled = NO; + label.drawsBackground = NO; + label.allowsEditingTextAttributes = YES; + label.selectable = YES; + + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, label, &layout); + + UiLinkButtonData *data = [[UiLinkButtonData alloc]init:obj textfield:label]; + objc_setAssociatedObject(label, "linkdata", data, OBJC_ASSOCIATION_RETAIN); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *s = var->value; + s->obj = (__bridge void*)data; + s->get = ui_linkbutton_get; + s->set = ui_linkbutton_set; + + if(s->value.ptr) { + [data setLinkDataFromJson:s->value.ptr]; + } + } + + return (__bridge void*)label; +} + +char* ui_linkbutton_get(UiString *s) { + return NULL; // TODO +} + +void ui_linkbutton_set(UiString *s, const char *str) { + UiLinkButtonData *data = (__bridge UiLinkButtonData*)s->obj; + [data setLinkDataFromJson:str]; +} + + + +void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) { + char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_label(UiString *str, const char *label) { + char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_uri(UiString *str, const char *uri) { + char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) { + char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE); + ui_set(str, value); + free(value); +} + +// TODO + +void ui_linkbutton_set_label(UIWIDGET button, const char *label) { + +} + +void ui_linkbutton_set_uri(UIWIDGET button, const char *label) { + +} + +void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) { + +} + +char* ui_linkbutton_get_label(UIWIDGET button) { + return NULL; +} + +char* ui_linkbutton_get_uri(UIWIDGET button) { + return NULL; +} + +UiBool ui_linkbutton_get_visited(UIWIDGET button) { + return FALSE; +}