From 4cae5f871f67857454bcf07c9b8b2f19dffad18c Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Wed, 5 Feb 2025 23:14:27 +0100 Subject: [PATCH] add ui for note list / content --- application/window.c | 15 ++++ ui/cocoa/EventData.h | 19 +++-- ui/cocoa/EventData.m | 19 ++++- ui/cocoa/MainWindow.m | 4 +- ui/cocoa/button.h | 9 +++ ui/cocoa/button.m | 175 ++++++++++++++++++++++++++++++++++++++++++ ui/gtk/container.c | 62 +++++++++++++++ ui/gtk/container.h | 26 +++---- ui/ui/button.h | 1 + ui/ui/container.h | 31 +++++++- 10 files changed, 338 insertions(+), 23 deletions(-) diff --git a/application/window.c b/application/window.c index d44a15e..4e97ed9 100644 --- a/application/window.c +++ b/application/window.c @@ -45,6 +45,21 @@ void window_create() { } } + ui_hsplitpane(obj, .initial_position = 200) { + // splitpane left: table + UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING_FREE, "Last Modified", -1); + ui_table(obj, .model = model, .varname = "notes"); + + // splitpane right: content + ui_grid(obj, .columnspacing = 10, .rowspacing = 10) { + ui_label(obj, .label = "Title"); + ui_textfield(obj, .varname = "note_title", .hexpand = TRUE); + ui_newline(obj); + + ui_textarea(obj, .varname = "note_content", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2); + } + } + ui_show(obj); } diff --git a/ui/cocoa/EventData.h b/ui/cocoa/EventData.h index 31f0107..8da0ee4 100644 --- a/ui/cocoa/EventData.h +++ b/ui/cocoa/EventData.h @@ -29,15 +29,24 @@ #import "../ui/toolkit.h" #import "../common/context.h" +typedef void(*get_eventdata_func)(id sender, UiVar *var, void **eventdata, int *value); + @interface EventData : NSObject -@property UiObject *obj; -@property ui_callback callback; -@property void *userdata; -@property void *data; -@property int value; +@property UiObject *obj; +@property UiVar *var; +@property int vartype; +@property ui_callback callback; +@property void *userdata; +@property void *data; +@property int value; +@property get_eventdata_func get_eventdata; - (EventData*)init:(ui_callback)cb userdata:(void*)userdata; - (void)handleEvent:(id)sender; +- (void)handleEventWithEventData:(id)sender; + +- (SEL)addDynamicMethod:(unsigned long long)method_id; + @end diff --git a/ui/cocoa/EventData.m b/ui/cocoa/EventData.m index 85992f1..f851343 100644 --- a/ui/cocoa/EventData.m +++ b/ui/cocoa/EventData.m @@ -28,6 +28,9 @@ #import "EventData.h" +#import + + @implementation EventData - (EventData*)init:(ui_callback)cb userdata:(void*)userdata { @@ -37,7 +40,7 @@ } - (void)handleEvent:(id)sender { - if(self.callback) { + if(_callback) { UiEvent event; event.obj = self.obj; event.window = event.obj->window; @@ -48,5 +51,19 @@ } } +- (void)handleEventWithEventData:(id)sender { + UiEvent event; + event.obj = self.obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.intval = 0; + if(_get_eventdata) { + _get_eventdata(sender, _var, &event.eventdata, &event.intval); + } + if(self.callback) { + self.callback(&event, self.userdata); + } +} @end diff --git a/ui/cocoa/MainWindow.m b/ui/cocoa/MainWindow.m index a46ab57..232f93f 100644 --- a/ui/cocoa/MainWindow.m +++ b/ui/cocoa/MainWindow.m @@ -46,8 +46,8 @@ defer:false]; // create a vertical stackview as default container - //BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; - GridLayout *vbox = [[GridLayout alloc] init]; + BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; + //GridLayout *vbox = [[GridLayout alloc] init]; vbox.translatesAutoresizingMaskIntoConstraints = false; [self.contentView addSubview:vbox]; [NSLayoutConstraint activateConstraints:@[ diff --git a/ui/cocoa/button.h b/ui/cocoa/button.h index 8857d2f..8c9311b 100644 --- a/ui/cocoa/button.h +++ b/ui/cocoa/button.h @@ -29,3 +29,12 @@ #import "toolkit.h" #import "../ui/button.h" + +int64_t ui_togglebutton_get(UiInteger *i); +void ui_togglebutton_set(UiInteger *i, int64_t value); + +int64_t ui_switch_get(UiInteger *i); +void ui_switch_set(UiInteger *i, int64_t value); + +int64_t ui_radiobuttons_get(UiInteger *i); +void ui_radiobuttons_set(UiInteger *i, int64_t value); diff --git a/ui/cocoa/button.m b/ui/cocoa/button.m index b153553..5ac939b 100644 --- a/ui/cocoa/button.m +++ b/ui/cocoa/button.m @@ -51,3 +51,178 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { 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; + } + + 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, FALSE); + + 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, FALSE); + + 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; +} + +static void radiobutton_eventdata(id button, UiVar *var, void **eventdata, int *value) { + if(var) { + printf("switch radiobutton\n"); + } +} + +UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) { + NSButton *button = [[NSButton alloc] init]; + [button setButtonType:NSButtonTypeRadio]; + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER); + 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, FALSE); + + return (__bridge void*)button; +} + +int64_t ui_radiobuttons_get(UiInteger *i) { + return 0; +} + +void ui_radiobuttons_set(UiInteger *i, int64_t value) { + +} diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 9a24822..17b4e4c 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -957,6 +957,68 @@ static GtkWidget* create_paned(UiOrientation orientation) { +static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs args) { + UiObject* current = uic_current_obj(obj); + + GtkWidget *pane0 = create_paned(orientation); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, pane0, TRUE); + + int max = args.max_panes == 0 ? 2 : args.max_panes; + + UiObject *newobj = uic_object_new(obj, pane0); + newobj->container = ui_splitpane_container(obj, pane0, orientation, max); + uic_obj_add(obj, newobj); + + return pane0; +} + +UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs args) { + return splitpane_create(obj, UI_HORIZONTAL, args); +} + +UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs args) { + return splitpane_create(obj, UI_VERTICAL, args); +} + +UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max) { + UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer)); + ct->container.widget = pane; + ct->container.add = ui_splitpane_container_add; + ct->current_pane = pane; + ct->orientation = orientation; + ct->max = max; + return (UiContainer*)ct; +} + +void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + UiSplitPaneContainer *s = (UiSplitPaneContainer*)ct; + + if(s->nchildren >= s->max) { + fprintf(stderr, "splitpane: maximum number of children reached\n"); + return; + } + + if(s->pos == 0) { + gtk_paned_set_start_child(GTK_PANED(s->current_pane), widget); + s->pos++; + s->nchildren++; + } else { + if(s->nchildren+1 == s->max) { + gtk_paned_set_end_child(GTK_PANED(s->current_pane), widget); + } else { + GtkWidget *pane = create_paned(s->orientation); + gtk_paned_set_start_child(GTK_PANED(pane), widget); + gtk_paned_set_end_child(GTK_PANED(s->current_pane), pane); + s->current_pane = pane; + } + + s->pos = 0; + s->nchildren++; + } +} + /* -------------------- ItemList Container -------------------- */ static void remove_item(void *data, void *item) { diff --git a/ui/gtk/container.h b/ui/gtk/container.h index 3ea8fbb..271ed50 100644 --- a/ui/gtk/container.h +++ b/ui/gtk/container.h @@ -31,6 +31,7 @@ #include "../ui/toolkit.h" #include "../ui/container.h" +#include "toolkit.h" #include #include @@ -97,16 +98,6 @@ typedef struct UiGridContainer { #endif } UiGridContainer; -/* -typedef struct UiPanedContainer { - UiContainer container; - GtkWidget *current_pane; - int orientation; - int max; - int cur; -} UiPanedContainer; -*/ - typedef struct UiTabViewContainer { UiContainer container; } UiTabViewContainer; @@ -127,6 +118,16 @@ typedef struct UiGtkTabView { int rowspacing; } UiGtkTabView; + +typedef struct UiSplitPaneContainer { + UiContainer container; + GtkWidget *current_pane; + UiOrientation orientation; + int pos; + int max; + int nchildren; +} UiSplitPaneContainer; + typedef struct UiHeaderbarContainer { UiContainer container; GtkWidget *centerbox; @@ -185,10 +186,9 @@ void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview); void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); -void ui_paned_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max); +void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); -void ui_split_container_add1(UiContainer *ct, GtkWidget *widget, UiBool fill); -void ui_split_container_add2(UiContainer *ct, GtkWidget *widget, UiBool fill); UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview); diff --git a/ui/ui/button.h b/ui/ui/button.h index 17407b7..c53912e 100644 --- a/ui/ui/button.h +++ b/ui/ui/button.h @@ -94,6 +94,7 @@ UIEXPORT UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args); + #ifdef __cplusplus } #endif diff --git a/ui/ui/container.h b/ui/ui/container.h index dda4867..ea14d8c 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -160,6 +160,28 @@ typedef struct UiSidebarArgs { int spacing; } UiSidebarArgs; +typedef struct UiSplitPaneArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + int margin; + int spacing; + int columnspacing; + int rowspacing; + + int initial_position; + UiInteger *value; + const char* varname; + int max_panes; +} UiSplitPaneArgs; + typedef struct UiItemListContainerArgs { UiTri fill; UiBool hexpand; @@ -234,6 +256,11 @@ struct UiContainerX { #define ui_headerbar0(obj) for(ui_headerbar_create(obj, (UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_sidebar0(obj) for(ui_sidebar_create(obj, (UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, (UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_vsplitpane0(obj) for(ui_vsplitpane_create(obj, (UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) + #define ui_tab(obj, label) for(ui_tab_create(obj, label);ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar_start(obj) for(ui_headerbar_start_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) @@ -267,8 +294,8 @@ UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args); UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args); -UIEXPORT UIWIDGET ui_hsplitpane(UiObject *obj, int max); // TODO -UIEXPORT UIWIDGET ui_vsplitpane(UiObject *obj, int max); // TODO +UIEXPORT UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs args); +UIEXPORT UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs args); // box container layout functions -- 2.43.5