--- a/ui/cocoa/button.m Sat Oct 04 14:54:25 2025 +0200 +++ b/ui/cocoa/button.m Sun Oct 19 21:20:08 2025 +0200 @@ -31,8 +31,12 @@ #import "Container.h" #import <objc/runtime.h> +#import <cx/buffer.h> +#import <cx/json.h> + UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { NSButton *button = [[NSButton alloc] init]; + button.translatesAutoresizingMaskIntoConstraints = NO; if(args->label) { NSString *label = [[NSString alloc] initWithUTF8String:args->label]; button.title = label; @@ -272,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; +}