/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2024 Olaf Wintermann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#import "button.h"
#import "EventData.h"
#import "Container.h"
#import "image.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;
}
if(args->icon) {
button.image = ui_cocoa_named_icon(args->icon);;
}
if(args->onclick) {
EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
event.obj = obj;
button.target = event;
button.action = @selector(handleEvent:);
objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
}
UiLayout layout = UI_INIT_LAYOUT(args);
ui_container_add(obj, button, &layout);
return (__bridge void*)button;
}
static void togglebutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
NSButton *btn = (NSButton*)button;
NSControlStateValue state = btn.state;
*value = (int)state;
}
UIWIDGET togglebutton_create(UiObject* obj, UiToggleArgs *args, enum NSButtonType type) {
NSButton *button = [[NSButton alloc] init];
[button setButtonType:type];
//[button setAllowsMixedState:YES];
if(args->label) {
NSString *label = [[NSString alloc] initWithUTF8String:args->label];
button.title = label;
}
if(args->icon) {
button.image = ui_cocoa_named_icon(args->icon);
}
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
if(var) {
UiInteger *i = var->value;
i->obj = (__bridge void*)button;
i->get = ui_togglebutton_get;
i->set = ui_togglebutton_set;
}
if(args->onchange) {
EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
event.get_eventdata = togglebutton_eventdata;
event.obj = obj;
event.var = var;
event.vartype = UI_VAR_INTEGER;
button.target = event;
button.action = @selector(handleEventWithEventData:);
objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
}
UiLayout layout = UI_INIT_LAYOUT(args);
ui_container_add(obj, button, &layout);
return (__bridge void*)button;
}
int64_t ui_togglebutton_get(UiInteger *i) {
NSButton *button = (__bridge NSButton*)i->obj;
NSControlStateValue state = button.state;
i->value = (int64_t)state;
return (int64_t)state;
}
void ui_togglebutton_set(UiInteger *i, int64_t value) {
NSButton *button = (__bridge NSButton*)i->obj;
NSControlStateValue state;
switch(value) {
case 0: state = NSControlStateValueOff; break;
case 1: state = NSControlStateValueOff; break;
default: state = NSControlStateValueMixed;
}
i->value = (int64_t)state;
button.state = state;
}
UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
return togglebutton_create(obj, args, NSButtonTypePushOnPushOff);
}
UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
return togglebutton_create(obj, args, NSButtonTypeSwitch);
}
static void switch_eventdata(id button, UiVar *var, void **eventdata, int *value) {
NSSwitch *btn = (NSSwitch*)button;
NSControlStateValue state = btn.state;
*value = (int)state;
}
UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
NSSwitch *button = [[NSSwitch alloc] init];
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
if(var) {
UiInteger *i = var->value;
i->obj = (__bridge void*)button;
i->get = ui_switch_get;
i->set = ui_switch_set;
}
if(args->onchange) {
EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
event.get_eventdata = switch_eventdata;
event.obj = obj;
event.var = var;
event.vartype = UI_VAR_INTEGER;
button.target = event;
button.action = @selector(handleEventWithEventData:);
objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
}
UiLayout layout = UI_INIT_LAYOUT(args);
ui_container_add(obj, button, &layout);
return (__bridge void*)button;
}
int64_t ui_switch_get(UiInteger *i) {
NSSwitch *button = (__bridge NSSwitch*)i->obj;
NSControlStateValue state = button.state;
i->value = (int64_t)state;
return (int64_t)state;
}
void ui_switch_set(UiInteger *i, int64_t value) {
NSSwitch *button = (__bridge NSSwitch*)i->obj;
NSControlStateValue state;
switch(value) {
case 0: state = NSControlStateValueOff; break;
case 1: state = NSControlStateValueOff; break;
default: state = NSControlStateValueMixed;
}
i->value = (int64_t)state;
button.state = state;
}
@implementation UiRadioButton
- (UiRadioButton*)init {
self = [super init];
_direct_state = NO;
[self setButtonType:NSButtonTypeRadio];
return self;
}
- (void)setState:(NSControlStateValue)state {
// NOOP
}
@end
static void radiobutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
if(var) {
UiInteger *value = var->value;
NSMutableArray *buttons = (__bridge NSMutableArray*)value->obj;
for(UiRadioButton *b in buttons) {
if(b != button) {
b.direct_state = YES;
[[b cell] setState:0];
b.direct_state = NO;
}
}
}
}
UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
UiRadioButton *button = [[UiRadioButton alloc] init];
if(args->label) {
button.title = [[NSString alloc] initWithUTF8String:args->label];
}
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
button.var = var;
NSMutableArray *buttons = nil;
if(var) {
UiInteger *i = var->value;
buttons = (__bridge NSMutableArray*)i->obj;
if(!buttons) {
buttons = [[NSMutableArray alloc] init];
i->obj = (__bridge void*)buttons;
i->get = ui_radiobuttons_get;
i->set = ui_radiobuttons_set;
}
[buttons addObject:button];
objc_setAssociatedObject(button, "radiogroup", buttons, OBJC_ASSOCIATION_RETAIN);
}
if(args->onchange || var) {
EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
event.get_eventdata = radiobutton_eventdata;
event.obj = obj;
event.var = var;
event.vartype = UI_VAR_INTEGER;
button.target = event;
button.action = @selector(handleEventWithEventData:);
objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
}
UiLayout layout = UI_INIT_LAYOUT(args);
ui_container_add(obj, button, &layout);
return (__bridge void*)button;
}
int64_t ui_radiobuttons_get(UiInteger *i) {
NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj;
int64_t index = 0;
for(UiRadioButton *b in buttons) {
if([b cell].state != 0) {
i->value = index + 1;
break;
}
index++;
}
return i->value;
}
void ui_radiobuttons_set(UiInteger *i, int64_t value) {
NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj;
int64_t index = 1;
for(UiRadioButton *b in buttons) {
if(index == value) {
[b cell].state = NSControlStateValueOn;
} else {
[b cell].state = NSControlStateValueOff;
}
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;
}