From be6c7473c3241bde1363058ecafc2c25e4a27dfa Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sun, 24 Aug 2025 15:28:03 +0200 Subject: [PATCH] update toolkit, adjust ui code --- application/window.c | 2 +- ui/cocoa/GridLayout.m | 2 +- ui/{qt/tree.cpp => cocoa/ListDataSource.h} | 18 +- ui/cocoa/ListDataSource.m | 110 ++++++ ui/cocoa/ListDelegate.h | 46 +++ ui/cocoa/ListDelegate.m | 99 ++++++ ui/cocoa/MainWindow.m | 16 +- ui/cocoa/Toolbar.h | 77 +++++ ui/cocoa/Toolbar.m | 245 +++++++++++++ ui/cocoa/{UiJob.m => UiThread.h} | 18 +- ui/cocoa/UiThread.m | 68 ++++ ui/cocoa/button.m | 8 +- ui/cocoa/container.h | 4 +- ui/cocoa/container.m | 14 +- ui/{qt/tree.h => cocoa/image.h} | 15 +- ui/cocoa/image.m | 67 ++++ ui/cocoa/{UiJob.h => label.h} | 5 +- ui/cocoa/label.m | 104 ++++++ ui/cocoa/list.h | 57 +++ ui/cocoa/list.m | 356 +++++++++++++++++++ ui/cocoa/menu.h | 36 +- ui/cocoa/menu.m | 88 ++++- ui/cocoa/objs.mk | 1 + ui/cocoa/text.m | 63 +++- ui/cocoa/toolkit.h | 13 + ui/cocoa/toolkit.m | 46 ++- ui/common/args.c | 320 +++++++++++++++++ ui/common/args.h | 69 ++++ ui/common/properties.c | 5 + ui/common/types.c | 16 +- ui/common/wrapper.c | 35 ++ ui/common/wrapper.h | 7 + ui/gtk/button.c | 287 +++++++++++++++ ui/gtk/button.h | 13 + ui/gtk/display.c | 2 +- ui/gtk/headerbar.c | 7 +- ui/gtk/list.c | 385 ++++++++++++--------- ui/gtk/list.h | 3 + ui/gtk/text.c | 2 +- ui/gtk/toolkit.c | 43 ++- ui/gtk/webview.c | 2 +- ui/gtk/window.c | 135 ++++---- ui/motif/toolkit.c | 14 +- ui/qt/button.cpp | 10 + ui/qt/container.cpp | 22 +- ui/qt/list.cpp | 16 +- ui/qt/model.cpp | 14 +- ui/qt/model.h | 22 +- ui/qt/qt4.pro | 63 ---- ui/qt/toolkit.cpp | 20 +- ui/qt/window.cpp | 59 +++- ui/ui/button.h | 54 ++- ui/ui/display.h | 10 +- ui/ui/toolkit.h | 16 +- ui/ui/tree.h | 30 +- ui/ui/window.h | 10 +- 56 files changed, 2809 insertions(+), 460 deletions(-) rename ui/{qt/tree.cpp => cocoa/ListDataSource.h} (75%) create mode 100644 ui/cocoa/ListDataSource.m create mode 100644 ui/cocoa/ListDelegate.h create mode 100644 ui/cocoa/ListDelegate.m create mode 100644 ui/cocoa/Toolbar.h create mode 100644 ui/cocoa/Toolbar.m rename ui/cocoa/{UiJob.m => UiThread.h} (80%) create mode 100644 ui/cocoa/UiThread.m rename ui/{qt/tree.h => cocoa/image.h} (88%) create mode 100644 ui/cocoa/image.m rename ui/cocoa/{UiJob.h => label.h} (89%) create mode 100644 ui/cocoa/label.m create mode 100644 ui/cocoa/list.h create mode 100644 ui/cocoa/list.m delete mode 100644 ui/qt/qt4.pro diff --git a/application/window.c b/application/window.c index 6bf6754..0cff93e 100644 --- a/application/window.c +++ b/application/window.c @@ -64,11 +64,11 @@ void window_create() { // splitpane left: table UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING_FREE, "Last Modified", -1); model->columnsize[0] = -1; - model->getvalue = window_notelist_getvalue; ui_table(obj, .model = model, .varname = "notes", + .getvalue = window_notelist_getvalue, .contextmenu = get_notelist_context_menu(), .multiselection = TRUE, .onselection = action_note_selected, diff --git a/ui/cocoa/GridLayout.m b/ui/cocoa/GridLayout.m index c937948..3e97cf4 100644 --- a/ui/cocoa/GridLayout.m +++ b/ui/cocoa/GridLayout.m @@ -293,7 +293,7 @@ return self.preferredSize; } -- (void) addView:(NSView*)view fill:(BOOL)fill { +- (void) addView:(NSView*)view { if(_newline) { _y++; _x = 0; diff --git a/ui/qt/tree.cpp b/ui/cocoa/ListDataSource.h similarity index 75% rename from ui/qt/tree.cpp rename to ui/cocoa/ListDataSource.h index 336041a..4dc142f 100644 --- a/ui/qt/tree.cpp +++ b/ui/cocoa/ListDataSource.h @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2025 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: @@ -26,11 +26,17 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "tree.h" -#include "container.h" +#import "toolkit.h" +#import "../ui/tree.h" -#include -#include -#include +@interface ListDataSource : NSObject +@property NSArray *columns; +@property UiVar *var; +@property ui_getvaluefunc2 getvalue; +@property void *getvaluedata; +@property UiModel *model; +- (id) init:(NSArray*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata; + +@end diff --git a/ui/cocoa/ListDataSource.m b/ui/cocoa/ListDataSource.m new file mode 100644 index 0000000..3be08f7 --- /dev/null +++ b/ui/cocoa/ListDataSource.m @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "ListDataSource.h" + +@implementation ListDataSource + +- (id) init:(NSArray*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata { + _columns = columns; + _var = var; + _getvalue = getvaluefunc; + _getvaluedata = userdata; + return self; +} + +- (NSInteger) numberOfRowsInTableView:(NSTableView *) tableView { + if(_var) { + UiList *list = _var->value; + if(list->count) { + return list->count(list); + } + } + return 0; +} + +- (id) tableView:(NSTableView *) tableView +objectValueForTableColumn:(NSTableColumn *) tableColumn + row:(NSInteger) row +{ + id ret = nil; + UiList *list = _var->value; + void *elm = list->get(list, (int)row); + if(elm) { + // get column index + NSUInteger colIndex = [_columns indexOfObject:tableColumn]; + if(colIndex == NSNotFound) { + return nil; + } + + // get UI model type for this column + UiModelType type = UI_STRING; + UiModel *model = _model; + if(model) { + if(colIndex >= model->columns) { + return nil; + } + type = model->types[colIndex]; + } + + // convert the list element + UiBool freeResult = FALSE; + void *data = _getvalue(list, elm, (int)row, (int)colIndex, _getvaluedata, &freeResult); + + switch(type) { + case UI_STRING: { + ret = [[NSString alloc] initWithUTF8String:data]; + break; + } + case UI_STRING_FREE: { + ret = [[NSString alloc] initWithUTF8String:data]; + freeResult = TRUE; + break; + } + case UI_INTEGER: { + break; + } + case UI_ICON: { + break; + } + case UI_ICON_TEXT: { + break; + } + case UI_ICON_TEXT_FREE: { + break; + } + } + + if(freeResult) { + free(data); + } + } + return ret; +} + +@end diff --git a/ui/cocoa/ListDelegate.h b/ui/cocoa/ListDelegate.h new file mode 100644 index 0000000..27762f7 --- /dev/null +++ b/ui/cocoa/ListDelegate.h @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "toolkit.h" + +@interface ListDelegate : NSObject + +@property (weak) NSTableView *tableview; +@property UiObject *obj; +@property ui_callback onselection; +@property void *onselectiondata; +@property ui_callback onactivate; +@property void *onactivatedata; + +- (id)init:(NSTableView*) tableview obj:(UiObject*)obj; + +- (void)activateEvent:(id)sender; + +@end + +UiListSelection ui_tableview_selection(NSTableView *tableview); diff --git a/ui/cocoa/ListDelegate.m b/ui/cocoa/ListDelegate.m new file mode 100644 index 0000000..7e15f3d --- /dev/null +++ b/ui/cocoa/ListDelegate.m @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "ListDelegate.h" + +@implementation ListDelegate + +- (id)init:(NSTableView*) tableview obj:(UiObject*)obj { + _tableview = tableview; + _obj = obj; + return self; +} + +- (void)activateEvent:(id)sender { + NSTableView *table = sender; + if(_onactivate) { + int row = (int)table.clickedRow; + + UiListSelection sel; + sel.count = 1; + sel.rows = &row; + + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &sel; + event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; + event.intval = row; + event.set = ui_get_setop(); + + _onactivate(&event, _onactivatedata); + } +} + +- (void) tableViewSelectionDidChange:(NSNotification *) notification { + if(_onselection) { + UiListSelection sel = ui_tableview_selection(_tableview); + + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &sel; + event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; + event.intval = 0; + event.set = ui_get_setop(); + + _onselection(&event, _onselectiondata); + } +} + +@end + +UiListSelection ui_tableview_selection(NSTableView *tableview) { + NSIndexSet *indexSet = tableview.selectedRowIndexes; + NSUInteger count = [indexSet count]; + + if(count == 0) { + return (UiListSelection){0, NULL}; + } + + int *rows = calloc(count, sizeof(int)); + + __block NSUInteger i = 0; + [indexSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) { + rows[i++] = (int)index; + }]; + + UiListSelection sel; + sel.count = (int)count; + sel.rows = rows; + return sel; +} diff --git a/ui/cocoa/MainWindow.m b/ui/cocoa/MainWindow.m index 27f34fd..45c8e30 100644 --- a/ui/cocoa/MainWindow.m +++ b/ui/cocoa/MainWindow.m @@ -34,6 +34,7 @@ #import "EventData.h" #import "menu.h" +#import "Toolbar.h" @implementation MainWindow @@ -48,6 +49,12 @@ backing:NSBackingStoreBuffered defer:false]; + if(uic_toolbar_isenabled()) { + UiToolbar *toolbar = [[UiToolbar alloc]initWithObject:obj]; + [self setToolbar:toolbar]; + } + + // create a vertical stackview as default container BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; //GridLayout *vbox = [[GridLayout alloc] init]; @@ -134,8 +141,13 @@ - (void)menuItemAction:(id)sender { EventData *event = objc_getAssociatedObject(sender, "eventdata"); if(event) { - event.obj = self.uiobj; // temporary set the event object - [event handleEvent:sender]; + if(event.obj) { + [event handleEvent:sender]; + } else { + event.obj = self.uiobj; + [event handleEvent:sender]; + event.obj = NULL; + } } } diff --git a/ui/cocoa/Toolbar.h b/ui/cocoa/Toolbar.h new file mode 100644 index 0000000..4e50e7c --- /dev/null +++ b/ui/cocoa/Toolbar.h @@ -0,0 +1,77 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "toolkit.h" +#import "../common/toolbar.h" + +/* + * UiToolbarDelegate + */ +@interface UiToolbarDelegate : NSObject { + NSMutableArray *allowedItems; + NSMutableArray *defaultItems; +} + +- (UiToolbarDelegate*) init; + +@end + +/* + * UiToolbar + */ +@interface UiToolbar : NSToolbar { + NSMutableArray *allowedItems; + NSMutableArray *defaultItems; +} + +@property UiObject *obj; + +- (UiToolbar*) initWithObject:(UiObject*)object; + +@end + + +@interface UiToolbarToggleEventHandler : NSObject +@property UiObject *obj; +@property UiVar *var; +@property ui_callback callback; +@property void *userdata; + +- (UiToolbarToggleEventHandler*)init; +- (void)handleEvent:(id)sender; + +@end + +void ui_toolbar_init(void); + +NSToolbarItem* ui_nstoolbaritem_create_item(UiObject *obj, UiToolbarItem *item, NSString *identifier); +NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem *item, NSString *identifier); +NSToolbarItem* ui_nstoolbaritem_create_menu(UiObject *obj, UiToolbarMenuItem *item, NSString *identifier); + +int64_t ui_toolbar_seg_toggleitem_get(UiInteger *i); +void ui_toolbar_seg_toggleitem_set(UiInteger *i, int64_t value); diff --git a/ui/cocoa/Toolbar.m b/ui/cocoa/Toolbar.m new file mode 100644 index 0000000..747b469 --- /dev/null +++ b/ui/cocoa/Toolbar.m @@ -0,0 +1,245 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "Toolbar.h" +#import "EventData.h" +#import "image.h" +#import "menu.h" +#import + +#include "../common/toolbar.h" + + +void ui_toolbar_init(void) { + +} + + + +/* --------------------- UiToolbar --------------------- */ + +@implementation UiToolbar + +- (UiToolbar*) initWithObject:(UiObject*)object { + self = [super initWithIdentifier:@"UiToolbar"]; + _obj = object; + + allowedItems = [[NSMutableArray alloc]initWithCapacity:16]; + defaultItems = [[NSMutableArray alloc]initWithCapacity:16]; + + CxMap *toolbarItems = uic_get_toolbar_items(); + CxMapIterator i = cxMapIteratorKeys(toolbarItems); + cx_foreach(CxHashKey *, key, i) { + NSString *s = [[NSString alloc]initWithBytes:key->data length:key->len encoding:NSUTF8StringEncoding]; + [allowedItems addObject:s]; + } + [allowedItems addObject: NSToolbarFlexibleSpaceItemIdentifier]; + [allowedItems addObject: NSToolbarSpaceItemIdentifier]; + + CxList *tbitems[3]; + tbitems[0] = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + tbitems[1] = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + tbitems[2] = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + for(int t=0;t<3;t++) { + CxIterator iter = cxListIterator(tbitems[t]); + cx_foreach(char *, name, iter) { + NSString *s = [[NSString alloc] initWithUTF8String:name]; + [defaultItems addObject:s]; + } + } + + [self setDelegate:self]; + [self setAllowsUserCustomization:YES]; + return self; +} + +// implementation of NSToolbarDelegate methods + +- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar { + return allowedItems; +} + +- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar { + return defaultItems; +} + +- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar + itemForItemIdentifier:(NSString *)itemIdentifier + willBeInsertedIntoToolbar:(BOOL)flag { + CxMap *items = uic_get_toolbar_items(); + UiToolbarItemI *item = cxMapGet(items, itemIdentifier.UTF8String); + if(!item) { + return nil; + } + + switch(item->type) { + default: return nil; + case UI_TOOLBAR_ITEM: { + return ui_nstoolbaritem_create_item(_obj, (UiToolbarItem*)item, itemIdentifier); + } + case UI_TOOLBAR_TOGGLEITEM: { + return ui_nstoolbaritem_create_toggle(_obj, (UiToolbarToggleItem*)item, itemIdentifier); + } + case UI_TOOLBAR_MENU: { + return ui_nstoolbaritem_create_menu(_obj, (UiToolbarMenuItem*)item, itemIdentifier); + } + } + + return nil; +} + +@end + +@implementation UiToolbarToggleEventHandler + +- (UiToolbarToggleEventHandler*)init { + return self; +} + +- (void)handleEvent:(id)sender { + if(_callback == nil) { + return; + } + + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = ui_get_setop(); + + if([sender isKindOfClass:[NSSegmentedControl class]]) { + NSSegmentedControl *seg = (NSSegmentedControl*)sender; + event.intval = [seg isSelectedForSegment:0]; + } + + _callback(&event, _userdata); +} + +@end + +NSToolbarItem* ui_nstoolbaritem_create_item(UiObject *obj, UiToolbarItem *item, NSString *identifier) { + NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier: identifier]; + button.bordered = YES; + + if(item->args.label) { + NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; + button.paletteLabel = label; + button.label = label; + } + if(item->args.icon) { + button.image = ui_cocoa_named_icon(item->args.icon); + } + + if(item->args.onclick) { + EventData *event = [[EventData alloc] init:item->args.onclick userdata:item->args.onclickdata]; + event.obj = obj; + button.target = event; + button.action = @selector(handleEvent:); + objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + return button; +} + +NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem *item, NSString *identifier) { + UiToolbarToggleEventHandler *event = [[UiToolbarToggleEventHandler alloc]init]; + event.obj = obj; + event.callback = item->args.onchange; + event.userdata = item->args.onchangedata; + + NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier:identifier]; + if(item->args.label) { + NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; + button.paletteLabel = label; + button.label = label; + } + objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + + NSSegmentedControl *seg; + if(!item->args.icon) { + NSArray *labels = @[[[NSString alloc] initWithUTF8String:item->args.label]]; + seg = [NSSegmentedControl segmentedControlWithLabels:labels trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)]; + button.view = seg; + } else { + NSArray *images = @[ui_cocoa_named_icon(item->args.icon)]; + seg = [NSSegmentedControl segmentedControlWithImages:images trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)]; + } + button.view = seg; + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER); + if(var) { + event.var = var; + UiInteger *i = var->value; + if(i->get) { + if(ui_get(i) != 0) { + [seg setEnabled:YES forSegment:0]; + } + + } + i->obj = (__bridge void*)seg; + i->get = ui_toolbar_seg_toggleitem_get; + i->set = ui_toolbar_seg_toggleitem_set; + } + + return button; +} + +int64_t ui_toolbar_seg_toggleitem_get(UiInteger *i) { + NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj; + i->value = [seg isSelectedForSegment:0]; + return i->value; +} + +void ui_toolbar_seg_toggleitem_set(UiInteger *i, int64_t value) { + NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj; + i->value = value; + [seg setSelected:value != 0 forSegment:0]; +} + +NSToolbarItem* ui_nstoolbaritem_create_menu(UiObject *obj, UiToolbarMenuItem *item, NSString *identifier) { + NSMenuToolbarItem *button = [[NSMenuToolbarItem alloc] initWithItemIdentifier: identifier]; + button.bordered = YES; + + if(item->args.label) { + NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; + button.paletteLabel = label; + button.label = label; + } + if(item->args.icon) { + button.image = ui_cocoa_named_icon(item->args.icon); + } + + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + ui_add_menu_items(obj, menu, 0, &item->menu); + + button.menu = menu; + + return button; +} diff --git a/ui/cocoa/UiJob.m b/ui/cocoa/UiThread.h similarity index 80% rename from ui/cocoa/UiJob.m rename to ui/cocoa/UiThread.h index b3e0830..1c4120d 100644 --- a/ui/cocoa/UiJob.m +++ b/ui/cocoa/UiThread.h @@ -26,4 +26,20 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import "UiJob.h" +#import "toolkit.h" + +@interface UiThread : NSObject + +@property UiObject *obj; +@property ui_threadfunc job; +@property void *jobdata; +@property ui_callback finish_callback; +@property void *finish_userdata; + +- (id)init:(UiObject*)obj jobfunc:(ui_threadfunc)job jobdata:(void*)jobdata; + +- (void)start; +- (void) runJob:(id)n; +- (void) finish:(id)n; + +@end diff --git a/ui/cocoa/UiThread.m b/ui/cocoa/UiThread.m new file mode 100644 index 0000000..2916d2f --- /dev/null +++ b/ui/cocoa/UiThread.m @@ -0,0 +1,68 @@ +/* + * 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 "UiThread.h" + + +@implementation UiThread + +- (id) init:(UiObject*)obj jobfunc:(ui_threadfunc)job jobdata:(void*)jobdata { + _obj = obj; + _job = job; + _jobdata = jobdata; + return self; +} + +- (void) start { + [NSThread detachNewThreadSelector:@selector(runJob:) + toTarget:self + withObject:nil]; +} + + +- (void) runJob:(id)n { + int result = _job(_jobdata); + if(!result) { + [self performSelectorOnMainThread:@selector(finish:) + withObject:nil + waitUntilDone:NO]; + } +} + +- (void) finish:(id)n { + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.intval = 0; + _finish_callback(&event, _finish_userdata); +} + +@end + diff --git a/ui/cocoa/button.m b/ui/cocoa/button.m index 06793f3..e8e753d 100644 --- a/ui/cocoa/button.m +++ b/ui/cocoa/button.m @@ -47,7 +47,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { } UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, button, &layout, FALSE); + ui_container_add(obj, button, &layout); return (__bridge void*)button; } @@ -89,7 +89,7 @@ UIWIDGET togglebutton_create(UiObject* obj, UiToggleArgs *args, enum NSButtonTyp } UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, button, &layout, FALSE); + ui_container_add(obj, button, &layout); return (__bridge void*)button; } @@ -150,7 +150,7 @@ UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) { } UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, button, &layout, FALSE); + ui_container_add(obj, button, &layout); return (__bridge void*)button; } @@ -242,7 +242,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) { } UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, button, &layout, FALSE); + ui_container_add(obj, button, &layout); return (__bridge void*)button; } diff --git a/ui/cocoa/container.h b/ui/cocoa/container.h index d969fa8..1ff66b1 100644 --- a/ui/cocoa/container.h +++ b/ui/cocoa/container.h @@ -71,7 +71,7 @@ struct UiLayout { @property const char *label; @property UiBool newline; -- (void) addView:(NSView*)view fill:(BOOL)fill; +- (void) addView:(NSView*)view; @end @@ -87,4 +87,4 @@ struct UiLayout { UiContainerX* ui_create_container(UiObject *obj, id container); -void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill); +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout); diff --git a/ui/cocoa/container.m b/ui/cocoa/container.m index 6e9b466..c73c429 100644 --- a/ui/cocoa/container.m +++ b/ui/cocoa/container.m @@ -57,10 +57,8 @@ return self; } -- (void) addView:(NSView*)view fill:(BOOL)fill { - if(_uilayout.fill != UI_LAYOUT_UNDEFINED) { - fill = ui_lb2bool(_uilayout.fill); - } +- (void) addView:(NSView*)view { + UiBool fill = _uilayout.fill; [self addArrangedSubview:view]; @@ -101,7 +99,7 @@ static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, NSUserInterf // add box to the parent UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, box, &layout, TRUE); + ui_container_add(obj, box, &layout); // add new box to the obj container chain uic_object_push_container(obj, ui_create_container(obj, box)); @@ -123,7 +121,7 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { // add box to the parent UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, grid, &layout, TRUE); + ui_container_add(obj, grid, &layout); // add new box to the obj container chain uic_object_push_container(obj, ui_create_container(obj, grid)); @@ -157,11 +155,11 @@ UiContainerX* ui_create_container(UiObject *obj, id container) { return ctn; } -void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill) { +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout) { UiContainerX *ctn = obj->container_end; id container = (__bridge id)ctn->container; container.uilayout = *layout; - [container addView:view fill:fill]; + [container addView:view]; } /* ---------------------- public layout functions ----------------------- */ diff --git a/ui/qt/tree.h b/ui/cocoa/image.h similarity index 88% rename from ui/qt/tree.h rename to ui/cocoa/image.h index 664b32b..2e1a05b 100644 --- a/ui/qt/tree.h +++ b/ui/cocoa/image.h @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2025 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: @@ -26,15 +26,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TREE_H -#define TREE_H +#include "../ui/image.h" -#include "../ui/tree.h" -#include "model.h" +#include "Container.h" -#include - - - -#endif /* TREE_H */ +void ui_icon_init(void); +NSImage* ui_cocoa_named_icon(const char *name); diff --git a/ui/cocoa/image.m b/ui/cocoa/image.m new file mode 100644 index 0000000..e00c39a --- /dev/null +++ b/ui/cocoa/image.m @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "image.h" + +static NSDictionary *standardIconNames; + +void ui_icon_init(void) { + standardIconNames = @{ + @"NSImageNameActionTemplate": NSImageNameActionTemplate, + @"NSImageNameAddTemplate": NSImageNameAddTemplate, + @"NSImageNameAdvanced": NSImageNameAdvanced, + @"NSImageNameApplicationIcon": NSImageNameApplicationIcon, + @"NSImageNameBluetoothTemplate": NSImageNameBluetoothTemplate, + @"NSImageNameBonjour": NSImageNameBonjour, + @"NSImageNameBookmarksTemplate": NSImageNameBookmarksTemplate, + @"NSImageNameCaution": NSImageNameCaution, + // TODO + @"NSImageNameRefreshTemplate": NSImageNameRefreshTemplate, + @"NSImageNameFolder": NSImageNameFolder, + @"NSImageNameGoForwardTemplate": NSImageNameGoForwardTemplate, + @"NSImageNameGoBackTemplate": NSImageNameGoBackTemplate + }; +} + +NSImage* ui_cocoa_named_icon(const char *name) { + NSString *imageName = [[NSString alloc] initWithUTF8String:name]; + NSString *imgName = [standardIconNames objectForKey:imageName]; + if(imgName) { + imageName = imgName; + } + return [NSImage imageNamed:imageName]; +} + + +void ui_image_ref(UIIMAGE img) { + // TODO +} + +void ui_image_unref(UIIMAGE img) { + // TODO +} diff --git a/ui/cocoa/UiJob.h b/ui/cocoa/label.h similarity index 89% rename from ui/cocoa/UiJob.h rename to ui/cocoa/label.h index a4c5988..bb7e97c 100644 --- a/ui/cocoa/UiJob.h +++ b/ui/cocoa/label.h @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2024 Olaf Wintermann. All rights reserved. + * Copyright 2025 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: @@ -27,4 +27,7 @@ */ #import "toolkit.h" +#import "../ui/display.h" +char* ui_label_get(UiString *s); +void ui_label_set(UiString *s, const char *str); diff --git a/ui/cocoa/label.m b/ui/cocoa/label.m new file mode 100644 index 0000000..f29a650 --- /dev/null +++ b/ui/cocoa/label.m @@ -0,0 +1,104 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "label.h" +#import "container.h" + +#import + +static UIWIDGET create_label(UiObject *obj, UiLabelArgs *args) { + NSTextField *label = [[NSTextField alloc] init]; + label.editable = NO; + label.bezeled = NO; + label.drawsBackground = NO; + label.selectable = NO; + + NSTextAlignment alignment; + switch(args->align) { + case UI_ALIGN_LEFT: alignment = NSTextAlignmentLeft; break; + case UI_ALIGN_RIGHT: alignment = NSTextAlignmentRight; break; + default: alignment = NSTextAlignmentCenter; + } + label.alignment = alignment; + + if(args->label) { + NSString *str = [[NSString alloc] initWithUTF8String:args->label]; + label.stringValue = str; + } + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, label, &layout); + + 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*)label; + s->get = ui_label_get; + s->set = ui_label_set; + + if(s->value.ptr) { + label.stringValue = [[NSString alloc]initWithUTF8String:s->value.ptr]; + } + } + + return (__bridge void*)label; +} + + +UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) { + return create_label(obj, args); +} + +UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs *args) { + args->align = UI_ALIGN_LEFT; + return create_label(obj, args); +} + +UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs *args) { + args->align = UI_ALIGN_RIGHT; + return create_label(obj, args); +} + + +char* ui_label_get(UiString *s) { + NSTextField *label = (__bridge NSTextField*)s->obj; + if(s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = strdup(label.stringValue.UTF8String); + return s->value.ptr; +} + +void ui_label_set(UiString *s, const char *str) { + NSTextField *label = (__bridge NSTextField*)s->obj; + if(s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = NULL; + label.stringValue = [[NSString alloc] initWithUTF8String:str]; +} diff --git a/ui/cocoa/list.h b/ui/cocoa/list.h new file mode 100644 index 0000000..03557b7 --- /dev/null +++ b/ui/cocoa/list.h @@ -0,0 +1,57 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "toolkit.h" +#import "Container.h" +#import "../ui/tree.h" + +#import "ListDataSource.h" + +@interface UiDropDown : NSObject + +@property UiObject *obj; +@property ui_callback onactivate; +@property void *onactivatedata; +@property ui_callback onselection; +@property void *onselectiondata; +@property ui_getvaluefunc2 getvalue; +@property void *getvaluedata; +@property UiVar *var; +@property (weak) NSComboBox *combobox; + +- (id)init:(UiObject*)obj; + +@end + +void ui_tableview_update(UiList *list, int i); +UiListSelection ui_tableview_getselection(UiList *list); +void ui_tableview_setselection(UiList *list, UiListSelection selection); + +void ui_dropdown_update(UiList *list, int i); +UiListSelection ui_dropdown_getselection(UiList *list); +void ui_dropdown_setselection(UiList *list, UiListSelection selection); diff --git a/ui/cocoa/list.m b/ui/cocoa/list.m new file mode 100644 index 0000000..6c4e39b --- /dev/null +++ b/ui/cocoa/list.m @@ -0,0 +1,356 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 "list.h" +#import "ListDelegate.h" +#import + +static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; + return getvalue(elm, col); +} + +static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return elm; +} + +/* --------------------------- ListView --------------------------- */ + +/* + * adds a NSTableViewDelegate that handles all events and calls + * callbacks specified in the UiListArgs + */ +static void add_listdelegate(UiObject *obj, NSTableView *tableview, UiListArgs *args) { + ListDelegate *delegate = [[ListDelegate alloc] init:tableview obj:obj]; + delegate.onactivate = args->onactivate; + delegate.onactivatedata = args->onactivatedata; + delegate.onselection = args->onselection; + delegate.onselectiondata = args->onselectiondata; + tableview.delegate = delegate; + objc_setAssociatedObject(tableview, "ui_listdelegate", delegate, OBJC_ASSOCIATION_RETAIN); + tableview.doubleAction = @selector(activateEvent:); + tableview.target = delegate; +} + +static void bind_list_to_tableview(UiList *list, NSTableView *tableview) { + list->obj = (__bridge void*)tableview; + list->update = ui_tableview_update; + list->getselection = ui_tableview_getselection; + list->setselection = ui_tableview_setselection; +} + +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) { + NSScrollView *scrollview = [[NSScrollView alloc] init]; + + NSTableView *tableview = [[NSTableView alloc] init]; + tableview.autoresizingMask = NSViewWidthSizable; + tableview.headerView = nil; + + if(args->multiselection) { + tableview.allowsMultipleSelection = YES; + } + + scrollview.documentView = tableview; + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, scrollview, &layout); + + add_listdelegate(obj, tableview, args); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + bind_list_to_tableview(list, tableview); + + ui_getvaluefunc2 getvalue = args->getvalue2; + void *getvaluedata = args->getvalue2data; + if(!getvalue) { + if(args->getvalue) { + getvalue = getvalue_wrapper; + getvaluedata = (void*)args->getvalue; + } else { + getvalue = str_getvalue; // by default list values are interpreted as strings + } + } + + NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"column"]; + [tableview addTableColumn:column]; + + ListDataSource *dataSource = [[ListDataSource alloc] init:tableview.tableColumns var:var getvalue:getvalue getvaluedata:getvaluedata]; + + tableview.dataSource = dataSource; + [tableview reloadData]; + + objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN); + } + + return (__bridge void*)scrollview; +} + +/* --------------------------- TableView --------------------------- */ + +UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) { + NSScrollView *scrollview = [[NSScrollView alloc] init]; + + NSTableView *tableview = [[NSTableView alloc] init]; + tableview.autoresizingMask = NSViewWidthSizable; + tableview.columnAutoresizingStyle = NSTableViewSequentialColumnAutoresizingStyle; + + if(args->multiselection) { + tableview.allowsMultipleSelection = YES; + } + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, scrollview, &layout); + + add_listdelegate(obj, tableview, args); + + // convert model + NSMutableArray *cols = [[NSMutableArray alloc] init]; + UiModel *model = args->model; + if(model) { + for(int i=0;icolumns;i++) { + char *title = model->titles[i]; + UiModelType type = model->types[i]; + int width = model->columnsize[i]; + NSString *identifier = [[NSString alloc] initWithUTF8String:title]; + NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:identifier]; + column.title = identifier; + column.resizingMask = NSTableColumnUserResizingMask; + if(width > 0) { + column.width = width; + } else if(width < 0) { + column.resizingMask = NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask; + } + if(type >= UI_ICON) { + // TODO + } + [tableview addTableColumn:column]; + [cols addObject:column]; + } + } + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + bind_list_to_tableview(list, tableview); + + ui_getvaluefunc2 getvalue = args->getvalue2; + void *getvaluedata = args->getvalue2data; + if(!getvalue) { + if(args->getvalue) { + getvalue = getvalue_wrapper; + getvaluedata = (void*)args->getvalue; + } else { + fprintf(stderr, "Error: tableview requires getvalue or getvalue2 func\n"); + return (__bridge void*)scrollview; + } + } + + ListDataSource *dataSource = [[ListDataSource alloc] init:cols var:var getvalue:getvalue getvaluedata:getvaluedata]; + if(model) { + dataSource.model = ui_model_copy(obj->ctx, model); + } + + tableview.dataSource = dataSource; + [tableview reloadData]; + + objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN); + } + + scrollview.documentView = tableview; + + return (__bridge void*)scrollview; +} + +/* ------ common functions ------ */ + +void ui_tableview_update(UiList *list, int i) { + NSTableView *tableview = (__bridge NSTableView*)list->obj; + if(i < 0) { + [tableview reloadData]; + } else { + [tableview reloadData]; // TODO: optimize + } +} + +UiListSelection ui_tableview_getselection(UiList *list) { + NSTableView *tableview = (__bridge NSTableView*)list->obj; + return ui_tableview_selection(tableview); +} + +void ui_tableview_setselection(UiList *list, UiListSelection selection) { + NSTableView *tableview = (__bridge NSTableView*)list->obj; + NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; + for(int i=0;ivalue; + if(index >= 0) { + eventdata = list->get(list, index); + } + } else { + NSString *str = _combobox.objectValueOfSelectedItem; + if(str) { + eventdata = (void*)str.UTF8String; + } + } + + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = eventdata; + event.eventdatatype = UI_EVENT_DATA_LIST_ELM; + event.intval = index; + + if(_onselection) { + _onselection(&event, _onselectiondata); + } + + if(_onactivate) { + _onactivate(&event, _onactivatedata); + } +} + +@end + +UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) { + NSComboBox *dropdown = [[NSComboBox alloc] init]; + dropdown.editable = NO; + + UiDropDown *uidropdown = [[UiDropDown alloc] init:obj]; + objc_setAssociatedObject(dropdown, "ui_dropdown", uidropdown, OBJC_ASSOCIATION_RETAIN); + uidropdown.onactivate = args->onactivate; + uidropdown.onactivatedata = args->onactivatedata; + uidropdown.onselection = args->onselection; + uidropdown.onselectiondata = args->onselectiondata; + uidropdown.combobox = dropdown; + + if(!args->getvalue2) { + if(args->getvalue) { + args->getvalue2 = getvalue_wrapper; + args->getvalue2data = (void*)args->getvalue; + } else { + args->getvalue2 = str_getvalue; + } + } + uidropdown.getvalue = args->getvalue2; + uidropdown.getvaluedata = args->getvalue2data; + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, dropdown, &layout); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + list->obj = (__bridge void*)dropdown; + list->update = ui_dropdown_update; + list->getselection = ui_dropdown_getselection; + list->setselection = ui_dropdown_setselection; + ui_dropdown_update(list, -1); + } else { + for(int i=0;istatic_nelm;i++) { + char *str = args->static_elements[i]; + NSString *item = [[NSString alloc] initWithUTF8String:str]; + [dropdown addItemWithObjectValue:item]; + } + } + + uidropdown.var = var; + + return (__bridge void*)dropdown; +} + +void ui_dropdown_update(UiList *list, int i) { + NSComboBox *combobox = (__bridge NSComboBox*)list->obj; + UiDropDown *dropdown = objc_getAssociatedObject(combobox, "ui_dropdown"); + if(dropdown) { + [combobox removeAllItems]; + + ui_getvaluefunc2 getvalue = dropdown.getvalue; + void *getvaluedata = dropdown.getvaluedata; + + int index = 0; + void *elm = list->first(list); + while(elm) { + UiBool freeResult = FALSE; + char *str = getvalue(list, elm, index, 0, getvaluedata, &freeResult); + if(str) { + NSString *item = [[NSString alloc] initWithUTF8String:str]; + [combobox addItemWithObjectValue:item]; + } + if(freeResult) { + free(str); + } + elm = list->next(list); + index++; + } + } else { + fprintf(stderr, "Error: obj is not a dropdown\n"); + } +} + +UiListSelection ui_dropdown_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + NSComboBox *combobox = (__bridge NSComboBox*)list->obj; + NSInteger index = combobox.indexOfSelectedItem; + if(index >= 0) { + sel.rows = malloc(sizeof(int)); + sel.count = 1; + sel.rows[0] = (int)index; + } + return sel; +} + +void ui_dropdown_setselection(UiList *list, UiListSelection selection) { + NSComboBox *combobox = (__bridge NSComboBox*)list->obj; + if(selection.count > 0) { + [combobox selectItemAtIndex:selection.rows[0]]; + } else { + [combobox selectItemAtIndex: -1]; + } +} diff --git a/ui/cocoa/menu.h b/ui/cocoa/menu.h index be58c9f..d1cd280 100644 --- a/ui/cocoa/menu.h +++ b/ui/cocoa/menu.h @@ -33,26 +33,38 @@ @interface MenuItem : NSObject +@property (weak) NSMenuItem *menuItem; @property (strong) NSString *itemId; -@property UiMenuCheckItem *checkItem; -@property UiMenuRadioItem *radioItem; -@property ui_callback callback; -@property void *userdata; +@property UiMenuCheckItem *checkItem; +@property UiMenuRadioItem *radioItem; +@property ui_callback callback; +@property void *userdata; +@property (strong) NSString *varname; +@property UiObject *obj; +@property UiVar *var; +@property BOOL state; - (MenuItem*)init:(int)itId; +- (void)handleToggleEvent:(id)sender; + @end void ui_menu_init(void); -typedef void(*ui_menu_add_f)(NSMenu*, int, UiMenuItemI*); +typedef void(*ui_menu_add_f)(UiObject*, NSMenu*, int, UiMenuItemI*); + +void add_menu_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); +void add_menuitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); +void add_menuseparator_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); +void add_checkitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); +void add_radioitem_widget(UiObject *obj, NSMenu *parent, int index, UiMenuItemI *item); +void add_checkitemnv_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); +void add_menuitem_list_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item); -void add_menu_widget(NSMenu *parent, int i, UiMenuItemI *item); -void add_menuitem_widget(NSMenu *parent, int i, UiMenuItemI *item); -void add_menuseparator_widget(NSMenu *parent, int i, UiMenuItemI *item); -void add_checkitem_widget(NSMenu *parent, int i, UiMenuItemI *item); -void add_radioitem_widget(NSMenu *parent, int index, UiMenuItemI *item); -void add_checkitemnv_widget(NSMenu *parent, int i, UiMenuItemI *item); -void add_menuitem_list_widget(NSMenu *parent, int i, UiMenuItemI *item); +void ui_add_menu_items(UiObject *obj, NSMenu *parent, int i, UiMenu *menu); NSArray* ui_get_binding_items(void); + +int64_t ui_menu_toggleitem_get(UiInteger *i); +void ui_menu_toggleitem_set(UiInteger *i, int64_t value); diff --git a/ui/cocoa/menu.m b/ui/cocoa/menu.m index 5395d0a..eae8a50 100644 --- a/ui/cocoa/menu.m +++ b/ui/cocoa/menu.m @@ -50,6 +50,28 @@ static NSMutableArray *bindingItems; return self; } +- (void)handleToggleEvent:(id)sender { + NSMenuItem *item = (NSMenuItem*)sender; + if(!_state) { + item.state = NSControlStateValueOn; + } else { + item.state = NSControlStateValueOff; + } + _state = !_state; + + if(_callback) { + UiEvent event; + event.obj = _obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = _state; + event.set = ui_get_setop(); + _callback(&event, _userdata); + } +} + @end static ui_menu_add_f createMenuItem[] = { @@ -63,61 +85,84 @@ static ui_menu_add_f createMenuItem[] = { /* UI_MENU_SEPARATOR */ add_menuseparator_widget }; -static void add_menu_items(NSMenu *parent, int i, UiMenu *menu) { +void ui_add_menu_items(UiObject *obj, NSMenu *parent, int i, UiMenu *menu) { UiMenuItemI *it = menu->items_begin; int index = 0; while(it) { - createMenuItem[it->type](parent, index, it); + createMenuItem[it->type](obj, parent, index, it); it = it->next; index++; } } -void add_menu_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_menu_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { UiMenu *it = (UiMenu*)item; NSString *str = [[NSString alloc] initWithUTF8String:it->label]; NSMenu *menu = [[NSMenu alloc] initWithTitle: str]; NSMenuItem *menuItem = [parent addItemWithTitle:str action:nil keyEquivalent:@""]; [parent setSubmenu:menu forItem:menuItem]; - add_menu_items(menu, i, it); + ui_add_menu_items(obj, menu, i, it); } -void add_menuitem_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_menuitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { UiMenuItem *it = (UiMenuItem*)item; NSString *str = [[NSString alloc] initWithUTF8String:it->label]; - NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuItemAction:) keyEquivalent:@""]; + NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuItemAction) keyEquivalent:@""]; if(it->callback) { EventData *event = [[EventData alloc] init:it->callback userdata:it->userdata]; + if(obj) { + event.obj = obj; + menuItem.target = event; + menuItem.action = @selector(handleEvent:); + } objc_setAssociatedObject(menuItem, "eventdata", event, OBJC_ASSOCIATION_RETAIN); } } -void add_menuseparator_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_menuseparator_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { NSMenuItem *menuItem = [NSMenuItem separatorItem]; [parent addItem:menuItem]; } static int nItem = 0; -void add_checkitem_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_checkitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { UiMenuCheckItem *it = (UiMenuCheckItem*)item; NSString *str = [[NSString alloc] initWithUTF8String:it->label]; NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuCheckItemAction:) keyEquivalent:@""]; MenuItem *mItem = [[MenuItem alloc] init:nItem++]; + mItem.menuItem = menuItem; + mItem.obj = obj; mItem.callback = it->callback; mItem.userdata = it->userdata; mItem.checkItem = it; + if(it->varname) { + mItem.varname = [[NSString alloc] initWithUTF8String:it->varname]; + } objc_setAssociatedObject(menuItem, "menuitem", mItem, OBJC_ASSOCIATION_RETAIN); - [bindingItems addObject:mItem]; + + if(!obj) { + [bindingItems addObject:mItem]; + } else { + mItem.var = uic_widget_var(obj->ctx, obj->ctx, NULL, it->varname, UI_VAR_INTEGER); + if(mItem.var) { + UiInteger *i = mItem.var->value; + i->obj = (__bridge void*)mItem; + i->get = ui_menu_toggleitem_get; + i->set = ui_menu_toggleitem_set; + } + menuItem.target = mItem; + menuItem.action = @selector(handleToggleEvent:); + } } -void add_radioitem_widget(NSMenu *parent, int index, UiMenuItemI *item) { +void add_radioitem_widget(UiObject *obj, NSMenu *parent, int index, UiMenuItemI *item) { UiMenuRadioItem *it = (UiMenuRadioItem*)item; NSString *str = [[NSString alloc] initWithUTF8String:it->label]; @@ -132,11 +177,11 @@ void add_radioitem_widget(NSMenu *parent, int index, UiMenuItemI *item) { [bindingItems addObject:mItem]; } -void add_checkitemnv_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_checkitemnv_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { } -void add_menuitem_list_widget(NSMenu *parent, int i, UiMenuItemI *item) { +void add_menuitem_list_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) { } @@ -154,7 +199,7 @@ void ui_menu_init(void) { NSMenuItem *menuItem = [[NSApp mainMenu] insertItemWithTitle:str action:nil keyEquivalent:@"" atIndex:index]; [[NSApp mainMenu] setSubmenu:menu forItem:menuItem]; - add_menu_items(menu, 0, ls); + ui_add_menu_items(NULL, menu, 0, ls); } ls = (UiMenu*)ls->item.next; index++; @@ -164,3 +209,20 @@ void ui_menu_init(void) { NSArray* ui_get_binding_items(void) { return bindingItems; } + + +int64_t ui_menu_toggleitem_get(UiInteger *i) { + MenuItem *item = (__bridge MenuItem*)i->obj; + i->value = item.state; + return i->value; +} + +void ui_menu_toggleitem_set(UiInteger *i, int64_t value) { + MenuItem *item = (__bridge MenuItem*)i->obj; + i->value = value; + if(value != 0) { + item.menuItem.state = NSControlStateValueOn; + } else { + item.menuItem.state = NSControlStateValueOff; + } +} diff --git a/ui/cocoa/objs.mk b/ui/cocoa/objs.mk index f3f20c7..a9a0b4b 100644 --- a/ui/cocoa/objs.mk +++ b/ui/cocoa/objs.mk @@ -41,6 +41,7 @@ COCOAOBJ += Container.o COCOAOBJ += button.o COCOAOBJ += text.o COCOAOBJ += menu.o +COCOAOBJ += Toolbar.o TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%) TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m) diff --git a/ui/cocoa/text.m b/ui/cocoa/text.m index 1fe0ce3..366f7a3 100644 --- a/ui/cocoa/text.m +++ b/ui/cocoa/text.m @@ -42,7 +42,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { scrollview.documentView = textview; UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, scrollview, &layout, TRUE); + ui_container_add(obj, scrollview, &layout); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT); @@ -85,7 +85,6 @@ void ui_textarea_restore(UiText *text) { NSTextStorage *textStorage; if(text->data1) { textStorage = (__bridge NSTextStorage*)text->data1; - } else { textStorage = [[NSTextStorage alloc] init]; } @@ -94,43 +93,83 @@ void ui_textarea_restore(UiText *text) { } void ui_textarea_set(UiText *text, const char *str) { - + NSTextView *textview = (__bridge NSTextView*)text->obj; + if(text->value.free) { + text->value.free(text->value.ptr); + } + text->value.ptr = strdup(str); + text->value.free = free; + textview.string = [[NSString alloc] initWithUTF8String:str]; } char* ui_textarea_get(UiText *text) { - return NULL; + NSTextView *textview = (__bridge NSTextView*)text->obj; + if(text->value.free) { + text->value.free(text->value.ptr); + } + text->value.ptr = strdup(textview.string.UTF8String); + text->value.free = free; + return text->value.ptr; } char* ui_textarea_getsubstr(UiText *text, int begin, int end) { - return NULL; + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSString *str = textview.string; + NSRange range = NSMakeRange(begin, end-begin); + NSString *sub = [str substringWithRange:range]; + return strdup(sub.UTF8String); } void ui_textarea_insert(UiText *text, int pos, char *str) { - + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSString *s = [[NSString alloc] initWithUTF8String:str]; + NSAttributedString *attributedStr = [[NSAttributedString alloc] initWithString:s]; + [textview.textStorage insertAttributedString:attributedStr atIndex:pos]; } void ui_textarea_setposition(UiText *text, int pos) { - + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSRange range = NSMakeRange(pos, 0); + [textview setSelectedRange:range]; } int ui_textarea_position(UiText *text) { - return 0; + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSRange range = textview.selectedRange; + return (int)range.location; } void ui_textarea_setselection(UiText *text, int begin, int end) { - + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSRange range = NSMakeRange(begin, end-begin); + [textview setSelectedRange:range]; } void ui_textarea_selection(UiText *text, int *begin, int *end) { - + NSTextView *textview = (__bridge NSTextView*)text->obj; + NSRange range = textview.selectedRange; + if(begin) { + *begin = (int)range.location; + } + if(end) { + *end = (int)(range.location+range.length); + } } int ui_textarea_length(UiText *text) { - return 0; + NSTextView *textview = (__bridge NSTextView*)text->obj; + return (int)textview.string.length; } void ui_textarea_remove(UiText *text, int begin, int end) { + NSTextView *textview = (__bridge NSTextView*)text->obj; + + if (begin < 0 || end < begin || end > textview.string.length) { + return; + } + NSRange range = NSMakeRange(begin, end - begin); + [[textview textStorage] deleteCharactersInRange:range]; } @@ -150,7 +189,7 @@ static UIWIDGET textfield_create(UiObject *obj, UiTextFieldArgs *args, BOOL pass } UiLayout layout = UI_INIT_LAYOUT(args); - ui_container_add(obj, textfield, &layout, FALSE); + ui_container_add(obj, textfield, &layout); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if(var) { diff --git a/ui/cocoa/toolkit.h b/ui/cocoa/toolkit.h index b44ba19..fd00333 100644 --- a/ui/cocoa/toolkit.h +++ b/ui/cocoa/toolkit.h @@ -31,6 +31,19 @@ #include "../common/context.h" #include "../common/object.h" +@interface UiAppCallback : NSObject { + ui_threadfunc callback; + void *userdata; +} + +- (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata; + +- (void) callMainThread; + +- (void) mainThread:(id)n; + +@end + void ui_cocoa_onstartup(void); void ui_cocoa_onopen(const char *file); void ui_cocoa_onexit(void); diff --git a/ui/cocoa/toolkit.m b/ui/cocoa/toolkit.m index 2501a3f..4df0790 100644 --- a/ui/cocoa/toolkit.m +++ b/ui/cocoa/toolkit.m @@ -34,7 +34,10 @@ #include "../common/toolbar.h" #include "../common/threadpool.h" +#import "image.h" #import "menu.h" +#import "Toolbar.h" +#import "UiThread.h" #import "AppDelegate.h" @@ -46,9 +49,11 @@ static const char **app_argv; static ui_callback startup_func; static void *startup_data; static ui_callback open_func; -void *open_data; +static void *open_data; static ui_callback exit_func; -void *exit_data; +static void *exit_data; + +static UiBool exit_on_shutdown; /* ------------------- App Init / Event Loop functions ------------------- */ @@ -68,6 +73,10 @@ void ui_init(const char *appname, int argc, char **argv) { [NSApplication sharedApplication]; //[NSBundle loadNibNamed:@"MainMenu" owner:NSApp ]; //[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&topLevelObjects]; + + ui_icon_init(); + ui_toolbar_init(); + } const char* ui_appname() { @@ -89,6 +98,10 @@ void ui_onexit(ui_callback f, void *userdata) { exit_data = userdata; } +void ui_app_exit_on_shutdown(UiBool exitapp) { + exit_on_shutdown = exitapp; +} + void ui_cocoa_onstartup(void) { UiEvent e; e.obj = NULL; @@ -127,6 +140,9 @@ void ui_cocoa_onexit(void) { void ui_main(void) { NSApplicationMain(app_argc, app_argv); + if(exit_on_shutdown) { + exit(0); + } } /* ------------------- Window Visibility functions ------------------- */ @@ -145,9 +161,33 @@ void ui_close(UiObject *obj) { /* ------------------- Job Control / Threadpool functions ------------------- */ void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { + UiThread *thread = [[UiThread alloc]init:obj jobfunc:tf jobdata:td]; + thread.finish_callback = f; + thread.finish_userdata = fd; + [thread start]; +} +@implementation UiAppCallback + +- (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata { + self->callback = func; + self->userdata = userdata; + return self; } -void ui_call_mainthread(ui_threadfunc tf, void* td) { +- (void) callMainThread { + [self performSelectorOnMainThread:@selector(mainThread:) + withObject:nil + waitUntilDone:NO]; +} +- (void) mainThread:(id)n { + callback(userdata); +} + +@end + +void ui_call_mainthread(ui_threadfunc tf, void* td) { + UiAppCallback *cb = [[UiAppCallback alloc]initWithCallback:tf userdata:td]; + [cb callMainThread]; } diff --git a/ui/common/args.c b/ui/common/args.c index e799889..3ef591f 100644 --- a/ui/common/args.c +++ b/ui/common/args.c @@ -33,6 +33,156 @@ #include "../ui/container.h" + +/* ---------------------------- UiDialogArgs ---------------------------- */ + +UiDialogArgs* ui_dialog_args_new(void) { + UiDialogArgs *args = malloc(sizeof(UiDialogArgs)); + memset(args, 0, sizeof(UiDialogArgs)); + return args; +} + +void ui_dialog_args_set_title(UiDialogArgs *args, const char *title) { + args->title = strdup(title); +} + +void ui_dialog_args_set_content(UiDialogArgs *args, const char *str) { + args->content = strdup(str); +} + +void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label) { + args->button1_label = strdup(label); +} + +void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label) { + args->button2_label = strdup(label); +} + +void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label) { + args->closebutton_label = strdup(label); +} + +void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value) { + args->input_value = strdup(value); +} + +void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input) { + args->input = input; +} + +void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password) { + args->password = password; +} + +void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb) { + args->result = cb; +} + +void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata) { + args->resultdata = userdata; +} + +void ui_dialog_args_free(UiDialogArgs *args) { + free((void*)args->title); + free((void*)args->button1_label); + free((void*)args->button2_label); + free((void*)args->content); + free((void*)args->closebutton_label); + free((void*)args->input_value); + free(args); +} + + +/* -------------------------- UiDialogWindowArgs -------------------------- */ + +UiDialogWindowArgs* ui_dialogwindow_args_new(void) { + UiDialogWindowArgs *args = malloc(sizeof(UiDialogWindowArgs)); + memset(args, 0, sizeof(UiDialogWindowArgs)); + return args; +} + +void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value) { + args->modal = value; +} + +void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value) { + args->titlebar_buttons = value; +} + +void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value) { + args->show_closebutton = value; +} + +void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title) { + args->title = strdup(title); +} + +void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label) { + args->lbutton1 = strdup(label); +} + +void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label) { + args->lbutton2 = strdup(label); +} + +void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label) { + args->rbutton3 = strdup(label); +} + +void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label) { + args->rbutton4 = strdup(label); +} + +void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button) { + args->default_button = button; +} + +void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width) { + args->width = width; +} + +void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height) { + args->height = height; +} + +void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb) { + args->onclick = cb; +} + +void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata) { + args->onclickdata = userdata; +} + +void ui_dialogwindow_args_free(UiDialogWindowArgs *args) { + free((void*)args->title); + free((void*)args->lbutton1); + free((void*)args->lbutton2); + free((void*)args->rbutton3); + free((void*)args->rbutton4); + free((void*)args->lbutton1_groups); + free((void*)args->lbutton2_groups); + free((void*)args->rbutton3_groups); + free((void*)args->rbutton4_groups); + free(args); +} + + /* ---------------------------- UiMenuItemArgs ---------------------------- */ UiMenuItemArgs* ui_menuitem_args_new(void) { @@ -608,6 +758,71 @@ void ui_splitpane_args_free(UiSplitPaneArgs *args) { } +/* ------------------------- UiWidgetArgs ----------------------------*/ + +UiWidgetArgs* ui_widget_args_new(void) { + UiWidgetArgs *args = malloc(sizeof(UiWidgetArgs)); + memset(args, 0, sizeof(UiWidgetArgs)); + return args; +} + + +void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill) { + args->fill = fill ? UI_ON : UI_OFF; +} + + +void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value) { + args->hexpand = value; +} + + +void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value) { + args->vexpand = value; +} + + +void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value) { + args->hfill = value; +} + + +void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value) { + args->vfill = value; +} + + +void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value) { + args->override_defaults = value; +} + + +void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan) { + args->colspan = colspan; +} + + +void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan) { + args->rowspan = rowspan; +} + + +void ui_widget_args_set_name(UiWidgetArgs *args, const char *name) { + args->name = strdup(name); +} + + +void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_widget_args_free(UiWidgetArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free(args); +} + + /* ------------------------- UiLabelArgs ----------------------------*/ @@ -1059,6 +1274,111 @@ void ui_toggle_args_free(UiToggleArgs *args) { free(args); } +/* ------------------------- UiLinkButtonArgs ----------------------------*/ + + +UiLinkButtonArgs* ui_linkbutton_args_new(void) { + UiLinkButtonArgs *args = malloc(sizeof(UiLinkButtonArgs)); + memset(args, 0, sizeof(UiLinkButtonArgs)); + return args; +} + + +void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill) { + args->fill = fill ? UI_ON : UI_OFF; +} + + +void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value) { + args->hexpand = value; +} + + +void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value) { + args->vexpand = value; +} + + +void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value) { + args->hfill = value; +} + + +void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value) { + args->vfill = value; +} + + +void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value) { + args->override_defaults = value; +} + + +void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan) { + args->colspan = colspan; +} + + +void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan) { + args->rowspan = rowspan; +} + + +void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name) { + args->name = strdup(name); +} + + +void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label){ + args->label = strdup(label); +} + +void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri) { + args->uri = strdup(uri); +} + +void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback) { + args->onclick = callback; +} + +void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata) { + args->onclickdata = userdata; +} + +void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value) { + args->nofollow = value; +} + +void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type) { + args->type = type; +} + +void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname) { + args->varname = strdup(varname); +} + +void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) { + args->value = value; +} + +void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups){ + // TODO +} + +void ui_linkbutton_args_free(UiLinkButtonArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free((void*)args->label); + free((void*)args->uri); + free((void*)args->varname); + free((void*)args->groups); + free(args); +} + /* ------------------------- UiListArgs ----------------------------*/ diff --git a/ui/common/args.h b/ui/common/args.h index 04e7063..a4bebe8 100644 --- a/ui/common/args.h +++ b/ui/common/args.h @@ -29,6 +29,7 @@ #ifndef UIC_ARGS_H #define UIC_ARGS_H +#include "../ui/window.h" #include "../ui/container.h" #include "../ui/display.h" #include "../ui/button.h" @@ -37,11 +38,44 @@ #include "../ui/tree.h" #include "../ui/text.h" #include "../ui/webview.h" +#include "../ui/widget.h" #ifdef __cplusplus extern "C" { #endif +UIEXPORT UiDialogArgs* ui_dialog_args_new(void); +UIEXPORT void ui_dialog_args_set_title(UiDialogArgs *args, const char *title); +UIEXPORT void ui_dialog_args_set_content(UiDialogArgs *args, const char *str); +UIEXPORT void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value); +UIEXPORT void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input); +UIEXPORT void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password); +UIEXPORT void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb); +UIEXPORT void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata); +UIEXPORT void ui_dialog_args_free(UiDialogArgs *args); + +UIEXPORT UiDialogWindowArgs* ui_dialogwindow_args_new(void); +UIEXPORT void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title); +UIEXPORT void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button); +UIEXPORT void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width); +UIEXPORT void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height); +UIEXPORT void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb); +UIEXPORT void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata); +UIEXPORT void ui_dialogwindow_args_free(UiDialogWindowArgs *args); UIEXPORT UiMenuItemArgs* ui_menuitem_args_new(void); UIEXPORT void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label); @@ -162,6 +196,19 @@ UIEXPORT void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *valu UIEXPORT void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max); UIEXPORT void ui_splitpane_args_free(UiSplitPaneArgs *args); +UIEXPORT UiWidgetArgs* ui_widget_args_new(void); +UIEXPORT void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill); +UIEXPORT void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan); +UIEXPORT void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan); +UIEXPORT void ui_widget_args_set_name(UiWidgetArgs *args, const char *name); +UIEXPORT void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname); +UIEXPORT void ui_widget_args_free(UiWidgetArgs *args); + UIEXPORT UiLabelArgs* ui_label_args_new(void); UIEXPORT void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill); UIEXPORT void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value); @@ -255,6 +302,28 @@ UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group); UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups); UIEXPORT void ui_toggle_args_free(UiToggleArgs *args); +UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void); +UIEXPORT void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill); +UIEXPORT void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan); +UIEXPORT void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan); +UIEXPORT void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name); +UIEXPORT void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname); +UIEXPORT void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname); +UIEXPORT void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value); +UIEXPORT void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label); +UIEXPORT void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri); +UIEXPORT void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback); +UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata); +UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type); +UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups); +UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args); + UIEXPORT UiListArgs* ui_list_args_new(void); UIEXPORT void ui_list_args_set_fill(UiListArgs *args, UiBool fill); UIEXPORT void ui_list_args_set_hexpand(UiListArgs *args, UiBool value); diff --git a/ui/common/properties.c b/ui/common/properties.c index 629112f..0866923 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -191,6 +191,11 @@ int uic_store_app_properties() { return ret; } +// public +int ui_app_save_settings(void) { + return uic_store_app_properties(); +} + const char* ui_get_property(const char *name) { return cxMapGet(application_properties, name); diff --git a/ui/common/types.c b/ui/common/types.c index b3f55d1..5f5685f 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -165,12 +165,26 @@ void ui_list_clear(UiList *list) { cxListClear(list->data); } -UIEXPORT void ui_list_update(UiList *list) { +void ui_list_update(UiList *list) { if(list->update) { list->update(list, -1); } } +void ui_list_update_row(UiList *list, int row) { + if(list->update) { + list->update(list, row); + } +} + +UiListSelection ui_list_get_selection(UiList *list) { + if(list->getselection) { + return list->getselection(list); + } else { + return (UiListSelection){0, NULL}; + } +} + void ui_list_addobsv(UiList *list, ui_callback f, void *data) { list->observers = ui_add_observer(list->observers, f, data); } diff --git a/ui/common/wrapper.c b/ui/common/wrapper.c index ea88553..6d5be81 100644 --- a/ui/common/wrapper.c +++ b/ui/common/wrapper.c @@ -218,3 +218,38 @@ void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge) { void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata) { item->eventdata = NULL; } + +/* ---------------------------- UiListSelection ---------------------------- */ + +UiListSelection* ui_list_get_selection_allocated(UiList *list) { + UiListSelection *sel = malloc(sizeof(UiListSelection)); + *sel = ui_list_get_selection(list); + return sel; + +} + +int ui_list_selection_get_count(UiListSelection *sel) { + return sel->count; +} + +int* ui_list_selection_get_rows(UiListSelection *sel) { + return sel->rows; +} + +void ui_list_selection_free(UiListSelection *sel) { + ui_listselection_free(*sel); + free(sel); +} + +/* ---------------------------- UiFileList ---------------------------- */ + +int ui_filelist_count(UiFileList *flist) { + return flist->nfiles; +} + +char* ui_filelist_get(UiFileList *flist, int index) { + if(index >= 0 && index < flist->nfiles) { + return flist->files[index]; + } + return NULL; +} diff --git a/ui/common/wrapper.h b/ui/common/wrapper.h index d190fc8..ec8d2d9 100644 --- a/ui/common/wrapper.h +++ b/ui/common/wrapper.h @@ -74,6 +74,13 @@ UIEXPORT int ui_event_get_eventdatatype(UiEvent *event); UIEXPORT int ui_event_get_int(UiEvent *event); UIEXPORT int ui_event_get_set(UiEvent *event); +UIEXPORT UiListSelection* ui_list_get_selection_allocated(UiList *list); +UIEXPORT int ui_list_selection_get_count(UiListSelection *sel); +UIEXPORT int* ui_list_selection_get_rows(UiListSelection *sel); +UIEXPORT void ui_list_selection_free(UiListSelection *sel); + +UIEXPORT int ui_filelist_count(UiFileList *flist); +UIEXPORT char* ui_filelist_get(UiFileList *flist, int index); #ifdef __cplusplus diff --git a/ui/gtk/button.c b/ui/gtk/button.c index 6f6ad0b..84d9608 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -32,6 +32,8 @@ #include "button.h" #include "container.h" #include +#include +#include #include "../common/context.h" #include "../common/object.h" @@ -120,6 +122,14 @@ void ui_button_clicked(GtkWidget *widget, UiEventData *event) { event->callback(&e, event->userdata); } +void ui_button_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_button_set_icon(UIWIDGET button, const char *icon) { + ui_button_set_icon_name(button, icon); +} + int64_t ui_toggle_button_get(UiInteger *integer) { GtkToggleButton *button = integer->obj; integer->value = (int)gtk_toggle_button_get_active(button); @@ -594,3 +604,280 @@ void ui_radiobutton_set(UiInteger *value, int64_t i) { value->value = i; } #endif + + +static void ui_destroy_linkbutton(GtkWidget *widget, UiLinkButton *data) { + free(data->link); + free(data); +} + +static const char* linkbutton_get_uri(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return link->link; + } else { + return gtk_link_button_get_uri(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_uri(UiLinkButton *link, const char *uri) { + if(link->type == UI_LINK_BUTTON) { + free(link->link); + link->link = uri ? strdup(uri) : NULL; + } else { + gtk_link_button_set_uri(GTK_LINK_BUTTON(link->widget), uri); + } +} + +static gboolean linkbutton_get_visited(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return FALSE; + } else { + gtk_link_button_get_visited(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_visited(UiLinkButton *link, gboolean visited) { + if(link->type != UI_LINK_BUTTON) { + gtk_link_button_set_visited(GTK_LINK_BUTTON(link->widget), visited); + } +} + +/* + * Apply linkbutton settings from json. Expects jsonvalue to be a valid + * json object. + * + * { + * "label": "label text", + * "uri": "http://example.com", + * "visited": true + * } + * + */ +static void linkbutton_apply_value(UiLinkButton *link, const char *jsonvalue) { + CxJson json; + cxJsonInit(&json, NULL); + cxJsonFill(&json, jsonvalue); + + 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) { + gtk_button_set_label(GTK_BUTTON(link->widget), cxJsonIsString(label) ? cxJsonAsString(label) : NULL); + } + if(uri) { + linkbutton_set_uri(link, cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL); + + } + if(visited) { + linkbutton_set_visited(link, cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE); + } + } + cxJsonValueFree(value); + } + cxJsonDestroy(&json); +} + +static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean 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; +} + +static char* linkbutton_get_value(UiLinkButton *link) { + const char *label = gtk_button_get_label(GTK_BUTTON(link->widget)); + const char *uri = linkbutton_get_uri(link); + gboolean visited = linkbutton_get_visited(link); + return create_linkbutton_jsonvalue(label, uri, TRUE, visited, TRUE); +} + +static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) { + if(data->onclick) { + UiEvent e; + e.obj = data->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = (char*)linkbutton_get_uri(data); + e.eventdatatype = UI_EVENT_DATA_STRING; + e.intval = 0; + e.set = ui_get_setop(); + data->onclick(&e, data->onclickdata); + } +} + +static gboolean linkbutton_activate_link(GtkLinkButton *self, UiLinkButton *data) { + linkbutton_clicked(data->widget, data); + return data->nofollow; +} + +UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) { + UiLinkButton *data = malloc(sizeof(UiLinkButton)); + memset(data, 0, sizeof(UiLinkButton)); + data->obj = obj; + data->type = args->type; + data->nofollow = args->nofollow; + data->onclick = args->onclick; + data->onclickdata = args->onclickdata; + + GtkWidget *button; + if(args->type == UI_LINK_BUTTON) { + button = gtk_button_new(); + g_signal_connect( + button, + "clicked", + G_CALLBACK(linkbutton_clicked), + data); + } else { + button = gtk_link_button_new("file:///"); + g_signal_connect( + button, + "activate-link", + G_CALLBACK(linkbutton_activate_link), + data); + } + gtk_button_set_label(GTK_BUTTON(button), args->label); + g_object_set_data(G_OBJECT(button), "ui_linkbutton", data); + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_destroy_linkbutton), + data); + + data->widget = button; + + UiObject* current = uic_current_obj(obj); + UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *str = var->value; + char *current_value = ui_get(str); + if(current_value) { + linkbutton_apply_value(data, current_value); + } + + str->obj = data; + str->get = ui_linkbutton_get; + str->set = ui_linkbutton_set; + } + + ui_set_name_and_style(button, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, button, args->groups); + UI_APPLY_LAYOUT2(current, args); + current->container->add(current->container, button); + + return button; +} + +char* ui_linkbutton_get(UiString *s) { + UiLinkButton *link = s->obj; + if(s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = linkbutton_get_value(link); + s->value.free = free; + return s->value.ptr; +} + +void ui_linkbutton_set(UiString *s, const char *str) { + linkbutton_apply_value(s->obj, str); + if(s->value.free) { + s->value.free(s->value.ptr); + } +} + + +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); +} + + +void ui_linkbutton_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_linkbutton_set_uri(UIWIDGET button, const char *label) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_uri(link, label); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_visited(link, visited); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +char* ui_linkbutton_get_label(UIWIDGET button) { + const char *label = gtk_button_get_label(GTK_BUTTON(button)); + return label ? strdup(label) : NULL; +} + +char* ui_linkbutton_get_uri(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + const char *uri = linkbutton_get_uri(link); + return uri ? strdup(uri) : NULL; + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return NULL; +} + +UiBool ui_linkbutton_get_visited(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + return linkbutton_get_visited(link); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return FALSE; +} diff --git a/ui/gtk/button.h b/ui/gtk/button.h index c855b35..4b60f49 100644 --- a/ui/gtk/button.h +++ b/ui/gtk/button.h @@ -37,6 +37,16 @@ extern "C" { #endif +typedef struct UiLinkButton { + UiObject *obj; + GtkWidget *widget; + UiLinkType type; + UiBool nofollow; + char *link; + ui_callback onclick; + void *onclickdata; +} UiLinkButton; + void ui_button_set_icon_name(GtkWidget *button, const char *icon_name); typedef void (*ui_toggled_func)(void*, void*); @@ -89,6 +99,9 @@ void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event); int64_t ui_radiobutton_get(UiInteger *value); void ui_radiobutton_set(UiInteger *value, int64_t i); +char* ui_linkbutton_get(UiString *s); +void ui_linkbutton_set(UiString *s, const char *str); + #ifdef __cplusplus } #endif diff --git a/ui/gtk/display.c b/ui/gtk/display.c index 40fcde1..32c4afd 100644 --- a/ui/gtk/display.c +++ b/ui/gtk/display.c @@ -101,7 +101,7 @@ UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) { case UI_ALIGN_DEFAULT: break; case UI_ALIGN_LEFT: set_alignment(widget, 0, .5); break; case UI_ALIGN_RIGHT: set_alignment(widget, 1, .5); break; - case UI_ALIGN_CENTER: break; // TODO + case UI_ALIGN_CENTER: set_alignment(widget, .5, .5); break; } diff --git a/ui/gtk/headerbar.c b/ui/gtk/headerbar.c index e1b80b5..f53dbd5 100644 --- a/ui/gtk/headerbar.c +++ b/ui/gtk/headerbar.c @@ -162,9 +162,10 @@ void ui_add_headerbar_menu( gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu)); #else GtkWidget *menubutton = gtk_menu_button_new(); - - // TODO - + GtkWidget *menu = gtk_menu_new(); + ui_add_menu_items(menu, 0, &item->menu, obj); + gtk_widget_show_all(menu); + gtk_menu_button_set_popup(GTK_MENU_BUTTON(menubutton), menu); #endif diff --git a/ui/gtk/list.c b/ui/gtk/list.c index be9c00a..41ed5d0 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -39,21 +39,22 @@ #include #include "list.h" +#include "button.h" #include "icon.h" #include "menu.h" #include "dnd.h" -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; +static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; + return getvalue(elm, col); } -static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) { - if(model->getvalue2) { - return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult); - } else if(model->getvalue) { - return model->getvalue(elm, col); - } +static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return elm; +} + +static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { return NULL; } @@ -74,6 +75,37 @@ static void listview_copy_static_elements(UiListView *listview, char **elm, size } } +static UiListView* create_listview(UiObject *obj, UiListArgs *args) { + UiListView *tableview = malloc(sizeof(UiListView)); + memset(tableview, 0, sizeof(UiListView)); + tableview->obj = obj; + tableview->model = args->model; + tableview->onactivate = args->onactivate; + tableview->onactivatedata = args->onactivatedata; + tableview->onselection = args->onselection; + tableview->onselectiondata = args->onselectiondata; + tableview->ondragstart = args->ondragstart; + tableview->ondragstartdata = args->ondragstartdata; + tableview->ondragcomplete = args->ondragcomplete; + tableview->ondragcompletedata = args->ondragcompletedata; + tableview->ondrop = args->ondrop; + tableview->ondropdata = args->ondropdata; + tableview->selection.count = 0; + tableview->selection.rows = NULL; + + if(args->getvalue2) { + tableview->getvalue = args->getvalue2; + tableview->getvaluedata = args->getvalue2data; + } else if(args->getvalue) { + tableview->getvalue = getvalue_wrapper; + tableview->getvaluedata = (void*)args->getvalue; + } else { + tableview->getvalue = null_getvalue; + } + + return tableview; +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -131,16 +163,17 @@ static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, } } -static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { +static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; UiList *list = col->listview->var ? col->listview->var->value : NULL; + UiListView *listview = col->listview; ObjWrapper *obj = gtk_list_item_get_item(item); UiModel *model = col->listview->model; UiModelType type = model->types[col->model_column]; UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, obj->data, obj->i, col->data_column, &freevalue); + void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue); GtkWidget *child = gtk_list_item_get_child(item); switch(type) { @@ -172,7 +205,7 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, } case UI_ICON_TEXT_FREE: { - void *data2 = model_getvalue(model, list, obj->data, obj->i, col->data_column+1, &freevalue); + void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue); if(type == UI_ICON_TEXT_FREE) { freevalue = TRUE; } @@ -199,55 +232,32 @@ static GtkSelectionModel* create_selection_model(UiListView *listview, GListStor selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore))); } else { selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore))); + gtk_single_selection_set_can_unselect(GTK_SINGLE_SELECTION(selection_model), TRUE); + gtk_single_selection_set_autoselect(GTK_SINGLE_SELECTION(selection_model), FALSE); } g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview); return selection_model; } -static UiListView* create_listview(UiObject *obj, UiListArgs *args) { - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; - tableview->model = args->model; - tableview->onactivate = args->onactivate; - tableview->onactivatedata = args->onactivatedata; - tableview->onselection = args->onselection; - tableview->onselectiondata = args->onselectiondata; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; - return tableview; -} - UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); // to simplify things and share code with ui_table_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; listview->columns->model_column = 0; - + GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); @@ -280,7 +290,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -320,19 +330,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { // to simplify things and share code with ui_tableview_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } + listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; @@ -370,7 +376,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -626,14 +632,20 @@ void ui_listview_update2(UiList *list, int i) { void *value = list->get(list, i); if(value) { ObjWrapper *obj = obj_wrapper_new(value, i); + UiListSelection sel = list->getselection(list); // TODO: if index i is selected, the selection is lost // is it possible to update the item without removing it? + // workaround: save selection and reapply it int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore)); if(count <= i) { g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1); } else { g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1); } + if(sel.count > 0) { + list->setselection(list, sel); + } + ui_listselection_free(sel); } } } @@ -695,12 +707,13 @@ void ui_combobox_setselection(UiList *list, UiListSelection selection) { #else -static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, UiList *list, void *elm, int row) { +static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) { + UiModel *model = listview->model; // set column values int c = 0; for(int i=0;icolumns;i++,c++) { UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, elm, row, c, &freevalue); + void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); GValue value = G_VALUE_INIT; switch(model->types[i]) { @@ -765,7 +778,7 @@ static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *mod c++; freevalue = FALSE; - char *str = model_getvalue(model, list, elm, row, c, &freevalue); + char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, str); if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) { @@ -779,7 +792,8 @@ static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *mod } } -static GtkListStore* create_list_store(UiList *list, UiModel *model) { +static GtkListStore* create_list_store(UiListView *listview, UiList *list) { + UiModel *model = listview->model; int columns = model->columns; GType types[2*columns]; int c = 0; @@ -807,7 +821,7 @@ static GtkListStore* create_list_store(UiList *list, UiModel *model) { GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - update_list_row(store, &iter, model, list, elm, i++); + update_list_row(listview, store, &iter, list, elm, i++); // next row elm = list->next(list); @@ -841,36 +855,29 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { #endif UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); - - UiListView *listview = malloc(sizeof(UiListView)); - memset(listview, 0, sizeof(UiListView)); - listview->obj = obj; - listview->widget = view; - listview->var = var; + UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } listview->model = model; - listview->selection.count = 0; - listview->selection.rows = NULL; g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), listview); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + + // init listview + listview->widget = view; + listview->var = var; + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(listview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -921,7 +928,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { SCROLLEDWINDOW_SET_CHILD(scroll_area, view); UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); + current->container->add(current->container, scroll_area); // ct->current should point to view, not scroll_area, to make it possible // to add a context menu @@ -1006,36 +1013,23 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); - //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); // add TreeView as observer to the UiList to update the TreeView if the // data changes - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; - tableview->widget = view; - tableview->var = var; - tableview->model = model; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; + UiListView *tableview = create_listview(obj, args); g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), tableview); + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(tableview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -1098,7 +1092,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { } UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); + current->container->add(current->container, scroll_area); // ct->current should point to view, not scroll_area, to make it possible // to add a context menu @@ -1112,7 +1106,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { void ui_listview_update(UiList *list, int i) { UiListView *view = list->obj; if(i < 0) { - GtkListStore *store = create_list_store(list, view->model); + GtkListStore *store = create_list_store(view, list); gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); } else { @@ -1120,7 +1114,7 @@ void ui_listview_update(UiList *list, int i) { GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget)); GtkTreeIter iter; if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) { - update_list_row(GTK_LIST_STORE(store), &iter, view->model, list, elm, i); + update_list_row(view, GTK_LIST_STORE(store), &iter, list, elm, i); } } } @@ -1150,46 +1144,41 @@ void ui_listview_setselection(UiList *list, UiListSelection selection) { UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } - - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + GtkWidget *combobox = gtk_combo_box_new(); - GtkWidget *combobox = ui_create_combobox(obj, model, var, args->static_elements, args->static_nelm, args->onactivate, args->onactivatedata); ui_set_name_and_style(combobox, args->name, args->style_class); ui_set_widget_groups(obj->ctx, combobox, args->groups); UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, combobox, FALSE); + current->container->add(current->container, combobox); current->container->current = combobox; - return combobox; -} - -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata) { - GtkWidget *combobox = gtk_combo_box_new(); - - UiListView *uicbox = malloc(sizeof(UiListView)); - memset(uicbox, 0, sizeof(UiListView)); - uicbox->obj = obj; - uicbox->widget = combobox; - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); + UiListView *listview = create_listview(obj, args); + listview->widget = combobox; + listview->model = ui_model(obj->ctx, UI_STRING, "", -1); + g_signal_connect( + combobox, + "destroy", + G_CALLBACK(ui_listview_destroy), + listview); - if(!list && elm && nelm > 0) { - listview_copy_static_elements(uicbox, elm, nelm); - for(int i=0;ictx, current->ctx, args->list, args->varname, UI_VAR_LIST); + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(listview, list); + if(var) { + listview->var = var; + list->update = ui_combobox_modelupdate; + list->getselection = ui_combobox_getselection; + list->setselection = ui_combobox_setselection; + list->obj = listview; + list->update(list, -1); + } else if(args->static_nelm > 0) { + listview_copy_static_elements(listview, args->static_elements, args->static_nelm); + for(int i=0;istatic_nelm;i++) { GtkTreeIter iter; GValue value = G_VALUE_INIT; gtk_list_store_insert(listmodel, &iter, -1); g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, uicbox->elements[i]); + g_value_set_string(&value, listview->elements[i]); gtk_list_store_set_value(listmodel, &iter, 0, &value); } } @@ -1199,23 +1188,6 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char ** g_object_unref(listmodel); } - uicbox->var = var; - uicbox->model = model; - - g_signal_connect( - combobox, - "destroy", - G_CALLBACK(ui_combobox_destroy), - uicbox); - - // bind var - if(list) { - list->update = ui_combobox_modelupdate; - list->getselection = ui_combobox_getselection; - list->setselection = ui_combobox_setselection; - list->obj = uicbox; - } - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); gtk_cell_layout_set_attributes( @@ -1227,13 +1199,13 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char ** gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); // add callback - if(f) { + if(args->onactivate) { UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); event->obj = obj; - event->userdata = udata; - event->callback = f; + event->userdata = args->onactivatedata; + event->callback = args->onactivate; event->value = 0; - event->customdata = uicbox; + event->customdata = listview; g_signal_connect( combobox, @@ -1268,7 +1240,7 @@ void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { void ui_combobox_modelupdate(UiList *list, int i) { UiListView *view = list->obj; - GtkListStore *store = create_list_store(view->var->value, view->model); + GtkListStore *store = create_list_store(view, list); gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store)); g_object_unref(store); } @@ -1709,19 +1681,6 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { free(v); } -void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - if(v->var) { - ui_destroy_boundvar(v->obj->ctx, v->var); - } - if(v->elements) { - for(int i=0;inelm;i++) { - free(v->elements[i]); - } - free(v->elements); - } - free(v); -} - /* ------------------------------ Source List ------------------------------ */ @@ -1801,16 +1760,16 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli uisublist.listbox = uilistbox; uisublist.userdata = sublist->userdata; uisublist.index = cxListSize(sublists); - + uisublist.startpos = 0; + cxListAdd(sublists, &uisublist); + // bind UiList UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1); - UiList *list = uisublist.var->value; - if(list) { + if(uisublist.var && uisublist.var->value) { + UiList *list = uisublist.var->value; list->obj = sublist_ptr; list->update = ui_listbox_list_update; } - - cxListAdd(sublists, &uisublist); } UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { @@ -1883,6 +1842,11 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox); + ui_widget_set_contextmenu(listbox, menu); + } + // signals g_signal_connect( listbox, @@ -1952,11 +1916,37 @@ void ui_listbox_update(UiListBox *listbox, int from, int to) { } // reload sublist + sublist->startpos = pos; ui_listbox_update_sublist(listbox, sublist, pos); pos += sublist->numitems; } } +static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) { + UiListBoxSubList *sublist = data->customdata0; + + UiSubListEventData eventdata; + eventdata.list = sublist->var->value; + eventdata.sublist_index = sublist->index; + eventdata.row_index = data->value0; + eventdata.sublist_userdata = sublist->userdata; + eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); + eventdata.event_data = data->customdata2; + + UiEvent event; + event.obj = data->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &eventdata; + event.eventdatatype = UI_EVENT_DATA_SUBLIST; + event.intval = data->value0; + event.set = ui_get_setop(); + + if(data->callback2) { + data->callback2(&event, data->userdata2); + } +} + static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); if(item->icon) { @@ -1966,7 +1956,9 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli GtkWidget *label = gtk_label_new(item->label); gtk_widget_set_halign(label, GTK_ALIGN_START); BOX_ADD_EXPAND(hbox, label); - // TODO: badge, button + if(item->badge) { + + } GtkWidget *row = gtk_list_box_row_new(); LISTBOX_ROW_SET_CHILD(row, hbox); @@ -1991,6 +1983,34 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); + // badge + if(item->badge) { + GtkWidget *badge = gtk_label_new(item->badge); + WIDGET_ADD_CSS_CLASS(badge, "ui-badge"); +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_widget_set_valign(badge, GTK_ALIGN_CENTER); + BOX_ADD(hbox, badge); +#else + GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0); + gtk_container_add(GTK_CONTAINER(align), badge); + BOX_ADD(hbox, align); +#endif + } + // button + if(item->button_icon || item->button_label) { + GtkWidget *button = gtk_button_new(); + gtk_button_set_label(GTK_BUTTON(button), item->button_label); + ui_button_set_icon_name(button, item->button_icon); + WIDGET_ADD_CSS_CLASS(button, "flat"); + BOX_ADD(hbox, button); + g_signal_connect( + button, + "clicked", + G_CALLBACK(listbox_button_clicked), + event + ); + } + return row; } @@ -2005,6 +2025,9 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si sublist->numitems = 0; // create items for each UiList element + if(!sublist->var) { + return; + } UiList *list = sublist->var->value; if(!list) { return; @@ -2012,6 +2035,20 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si size_t index = 0; void *elm = list->first(list); + + if(!elm && sublist->header) { + // empty row for header + GtkWidget *row = gtk_list_box_row_new(); + cxListAdd(sublist->widgets, row); + g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); + g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); + intptr_t rowindex = listbox_insert_index + index; + g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + sublist->numitems = 1; + return; + } + while(elm) { UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; if(listbox->getvalue) { @@ -2055,6 +2092,14 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si void ui_listbox_list_update(UiList *list, int i) { UiListBoxSubList *sublist = list->obj; + ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos); + size_t pos = 0; + CxIterator it = cxListIterator(sublist->listbox->sublists); + cx_foreach(UiListBoxSubList *, ls, it) { + ls->startpos = pos; + pos += sublist->numitems; + } + } void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { diff --git a/ui/gtk/list.h b/ui/gtk/list.h index bd490ab..7878b4f 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -45,6 +45,8 @@ typedef struct UiListView { GtkWidget *widget; UiVar *var; UiModel *model; + ui_getvaluefunc2 getvalue; + void *getvaluedata; char **elements; size_t nelm; #if GTK_CHECK_VERSION(4, 10, 0) @@ -91,6 +93,7 @@ typedef struct UiListBoxSubList { UiListBox *listbox; void *userdata; size_t index; + size_t startpos; } UiListBoxSubList; struct UiListBox { diff --git a/ui/gtk/text.c b/ui/gtk/text.c index 9ea0c4c..ef07daf 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -1118,7 +1118,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { pathtf); UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, eventbox, FALSE); + current->container->add(current->container, eventbox); // hbox as parent for the GtkEntry and GtkButtonBox GtkWidget *hbox = ui_gtk_hbox_new(0); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 7a26a70..d64246c 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -66,6 +66,8 @@ static UiObject *active_window; static int scale_factor = 1; +static UiBool exit_on_shutdown; + UIEXPORT void ui_init(const char *appname, int argc, char **argv) { application_name = appname; uic_init_global_context(); @@ -110,8 +112,11 @@ void ui_onexit(ui_callback f, void *userdata) { exit_data = userdata; } +void ui_app_exit_on_shutdown(UiBool exitapp) { + exit_on_shutdown = exitapp; +} -#ifndef UI_GTK2 +#ifdef UI_APPLICATION static void app_startup(GtkApplication* app, gpointer userdata) { if(startup_func) { startup_func(NULL, startup_data); @@ -119,8 +124,16 @@ static void app_startup(GtkApplication* app, gpointer userdata) { } static void app_activate(GtkApplication* app, gpointer userdata) { - printf("activate\n"); + //printf("activate\n"); +} + +static void app_shutdown(GtkApplication *app, gpointer userdata) { + if(exit_func) { + exit_func(NULL, exit_data); + } + ui_app_save_settings(); } + #endif void ui_main() { @@ -130,6 +143,7 @@ void ui_main() { application_name ? application_name : "application1"); app = UI_APPLICATION_NEW(appid.ptr); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); + g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_application_run(G_APPLICATION (app), 0, NULL); g_object_unref (app); @@ -140,11 +154,14 @@ void ui_main() { startup_func(NULL, startup_data); } gtk_main(); -#endif if(exit_func) { exit_func(NULL, exit_data); } - uic_store_app_properties(); + ui_app_save_settings(); +#endif + if(exit_on_shutdown) { + exit(0); + } } #ifndef UI_GTK2 @@ -378,6 +395,15 @@ static const char *ui_gtk_css = " margin-top: 4px;\n" " margin-bottom: 10px;\n" "}\n" +".ui-badge {\n" +" background-color: #e53935;\n" +" color: white;\n" +" border-radius: 9999px;\n" +" padding: 0px 10px 0px 10px;\n" +" font-weight: bold;\n" +" margin-left: 4px;" +" margin-right: 4px;" +"}\n" ; #elif GTK_MAJOR_VERSION == 3 @@ -411,6 +437,15 @@ static const char *ui_gtk_css = " margin-top: 4px;\n" " margin-bottom: 10px;\n" "}\n" +".ui-badge {\n" +" background-color: #e53935;\n" +" color: white;\n" +" border-radius: 9999px;\n" +" padding: 0px 10px 0px 10px;\n" +" font-weight: bold;\n" +" margin-left: 4px;" +" margin-right: 4px;" +"}\n" ; #endif diff --git a/ui/gtk/webview.c b/ui/gtk/webview.c index 3b9a946..b9b93c7 100644 --- a/ui/gtk/webview.c +++ b/ui/gtk/webview.c @@ -55,7 +55,7 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { value->set = ui_webview_set; value->obj = data; if(value->value && value->type && !strcmp(value->type, UI_WEBVIEW_OBJECT_TYPE)) { - // TODO + ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE); } } diff --git a/ui/gtk/window.c b/ui/gtk/window.c index cdafae3..d0cabe3 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -313,7 +313,6 @@ static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData * evt.window = evt.obj->window; evt.eventdata = NULL; evt.eventdatatype = 0; - evt.eventdatatype = 0; evt.intval = 0; if(!strcmp(response, "btn1")) { @@ -333,35 +332,35 @@ static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData * } } -void ui_dialog_create(UiObject *parent, UiDialogArgs args) { - AdwDialog *dialog = adw_alert_dialog_new(args.title, args.content); +void ui_dialog_create(UiObject *parent, UiDialogArgs *args) { + AdwDialog *dialog = adw_alert_dialog_new(args->title, args->content); UiEventData *event = malloc(sizeof(UiEventData)); - event->callback = args.result; - event->userdata = args.resultdata; + event->callback = args->result; + event->userdata = args->resultdata; event->customdata = NULL; event->customint = 0; event->value = 0; event->obj = parent; - if(args.button1_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args.button1_label); + if(args->button1_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args->button1_label); } - if(args.button2_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args.button2_label); + if(args->button2_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args->button2_label); } - if(args.closebutton_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args.closebutton_label); + if(args->closebutton_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args->closebutton_label); adw_alert_dialog_set_close_response(ADW_ALERT_DIALOG(dialog), "close"); } GtkWidget *entry = NULL; - if(args.input || args.password) { + if(args->input || args->password) { entry = gtk_entry_new(); - if(args.password) { + if(args->password) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); } - if(args.input_value) { - ENTRY_SET_TEXT(entry, args.input_value); + if(args->input_value) { + ENTRY_SET_TEXT(entry, args->input_value); } adw_alert_dialog_set_extra_child(ADW_ALERT_DIALOG(dialog), entry); event->customdata = entry; @@ -410,47 +409,47 @@ static void ui_dialog_response (GtkDialog* self, gint response_id, gpointer user WINDOW_DESTROY(GTK_WIDGET(self)); } -void ui_dialog_create(UiObject *parent, UiDialogArgs args) { +void ui_dialog_create(UiObject *parent, UiDialogArgs *args) { GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new()); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); GtkWidget *dialog_w = GTK_WIDGET(dialog); - if(args.title) { - gtk_window_set_title(GTK_WINDOW(dialog), args.title); + if(args->title) { + gtk_window_set_title(GTK_WINDOW(dialog), args->title); } - if(args.button1_label) { - gtk_dialog_add_button(dialog, args.button1_label, 1); + if(args->button1_label) { + gtk_dialog_add_button(dialog, args->button1_label, 1); } - if(args.button2_label) { - gtk_dialog_add_button(dialog, args.button2_label, 2); + if(args->button2_label) { + gtk_dialog_add_button(dialog, args->button2_label, 2); } - if(args.closebutton_label) { - gtk_dialog_add_button(dialog, args.closebutton_label, 0); + if(args->closebutton_label) { + gtk_dialog_add_button(dialog, args->closebutton_label, 0); } GtkWidget *content_area = gtk_dialog_get_content_area(dialog); - if(args.content) { - GtkWidget *label = gtk_label_new(args.content); + if(args->content) { + GtkWidget *label = gtk_label_new(args->content); BOX_ADD(content_area, label); } GtkWidget *textfield = NULL; - if(args.input || args.password) { + if(args->input || args->password) { textfield = gtk_entry_new(); - if(args.password) { + if(args->password) { gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE); } - if(args.input_value) { - ENTRY_SET_TEXT(textfield, args.input_value); + if(args->input_value) { + ENTRY_SET_TEXT(textfield, args->input_value); } BOX_ADD(content_area, textfield); } UiEventData *event = malloc(sizeof(UiEventData)); event->obj = parent; - event->callback = args.result; - event->userdata = args.resultdata; + event->callback = args->result; + event->userdata = args->resultdata; event->value = 0; event->customdata = textfield; @@ -749,18 +748,18 @@ static void ui_dialogwindow_response(GtkDialog* self, gint response_id, gpointer -UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { +UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) { GtkWidget *dialog = DIALOG_NEW(); - if(args.width > 0 || args.height > 0) { + if(args->width > 0 || args->height > 0) { gtk_window_set_default_size( GTK_WINDOW(dialog), - args.width, - args.height); + args->width, + args->height); } gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); - if(args.modal != UI_OFF) { + if(args->modal != UI_OFF) { gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); } @@ -770,15 +769,15 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { obj->destroy = ui_window_widget_destroy; nwindows++; - if(args.title != NULL) { - gtk_window_set_title(GTK_WINDOW(dialog), args.title); + if(args->title != NULL) { + gtk_window_set_title(GTK_WINDOW(dialog), args->title); } #if ! GTK_CHECK_VERSION(4, 10, 0) UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; - event->userdata = args.onclickdata; - event->callback = args.onclick; + event->userdata = args->onclickdata; + event->callback = args->onclick; event->value = 0; event->customdata = NULL; @@ -816,44 +815,44 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { GtkWidget *content_vbox = ui_gtk_vbox_new(0); obj->container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX); - if(args.lbutton1 || args.lbutton2 || args.rbutton3 || args.rbutton4) { + if(args->lbutton1 || args->lbutton2 || args->rbutton3 || args->rbutton4) { #if GTK_CHECK_VERSION(3, 10, 0) - if(args.titlebar_buttons != UI_OFF) { + if(args->titlebar_buttons != UI_OFF) { GtkWidget *headerbar = gtk_header_bar_new(); gtk_window_set_titlebar(GTK_WINDOW(dialog), headerbar); - if(args.show_closebutton == UI_OFF) { + if(args->show_closebutton == UI_OFF) { HEADERBAR_SHOW_CLOSEBUTTON(headerbar, FALSE); } - if(args.lbutton1) { - GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + if(args->lbutton1) { + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 1) { + if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.lbutton2) { - GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + if(args->lbutton2) { + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 2) { + if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton4) { - GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + if(args->rbutton4) { + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 4) { + if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton3) { - GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + if(args->rbutton3) { + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 3) { + if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } @@ -871,18 +870,18 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { GtkWidget *widget = ui_box_set_margin(grid, 16); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); - if(args.lbutton1) { - GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + if(args->lbutton1) { + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1); - if(args.default_button == 1) { + if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.lbutton2) { - GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + if(args->lbutton2) { + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1); - if(args.default_button == 2) { + if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } @@ -890,18 +889,18 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { GtkWidget *space = gtk_label_new(NULL); gtk_widget_set_hexpand(space, TRUE); gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1); - if(args.rbutton3) { - GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + if(args->rbutton3) { + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1); - if(args.default_button == 3) { + if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton4) { - GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + if(args->rbutton4) { + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1); - if(args.default_button == 4) { + if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } diff --git a/ui/motif/toolkit.c b/ui/motif/toolkit.c index 25ea546..b6ffc84 100644 --- a/ui/motif/toolkit.c +++ b/ui/motif/toolkit.c @@ -52,17 +52,18 @@ static const char *application_name; static ui_callback startup_func; static void *startup_data; static ui_callback open_func; -void *open_data; +static void *open_data; static ui_callback exit_func; -void *exit_data; +static void *exit_data; static ui_callback appclose_fnc; static void *appclose_udata; static int is_toplevel_realized = 0; -int event_pipe[2]; +static int event_pipe[2]; +static UiBool exit_on_shutdown; static String fallback[] = { //"*fontList: -dt-interface system-medium-r-normal-s*utf*:", @@ -139,6 +140,10 @@ void ui_onexit(ui_callback f, void *userdata) { exit_data = userdata; } +void ui_app_exit_on_shutdown(UiBool exitapp) { + exit_on_shutdown = exitapp; +} + void ui_main() { if(startup_func) { startup_func(NULL, startup_data); @@ -148,6 +153,9 @@ void ui_main() { exit_func(NULL, exit_data); } uic_store_app_properties(); + if(exit_on_shutdown) { + exit(0); + } } void ui_exit_mainloop() { diff --git a/ui/qt/button.cpp b/ui/qt/button.cpp index 5e04f27..6036cd6 100644 --- a/ui/qt/button.cpp +++ b/ui/qt/button.cpp @@ -48,6 +48,16 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { return button; } +void ui_button_set_label(UIWIDGET button, const char *label) { + QString str = QString::fromUtf8(label); + QAbstractButton *b = (QAbstractButton*)button; + b->setText(str); +} + +void ui_button_set_icon(UIWIDGET button, const char *icon) { + // TODO +} + static void togglebutton_event(UiEvent *event, UiEventWrapper *wrapper) { QPushButton *button = (QPushButton*)wrapper->customdata1; event->intval = button->isChecked(); diff --git a/ui/qt/container.cpp b/ui/qt/container.cpp index 07ac46a..1313ab6 100644 --- a/ui/qt/container.cpp +++ b/ui/qt/container.cpp @@ -35,6 +35,7 @@ #include #include #include +#include static void delete_container(UiContainerPrivate *ct) { delete ct; @@ -228,7 +229,7 @@ void UiGridContainer::end() { } } -UIEXPORT UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); @@ -251,6 +252,25 @@ UIEXPORT UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { } +/* ---------------------------- UiSidebar ---------------------------- */ + +UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) { + QVariant v = obj->widget->property("ui_sidebar"); + QDockWidget *dock = (QDockWidget*)v.value(); + if(!dock) { + fprintf(stderr, "Error: window is not configured for sidebar\n"); + return nullptr; + } + + QWidget *widget = new QWidget(); + QBoxLayout *box = new QBoxLayout(QBoxLayout::TopToBottom); + widget->setLayout(box); + dock->setWidget(widget); + + ui_container_add(obj, new UiBoxContainer(box)); + + return dock; +} /* -------------------- Container Helper Functions -------------------- */ diff --git a/ui/qt/list.cpp b/ui/qt/list.cpp index a44a93d..6ce56e1 100644 --- a/ui/qt/list.cpp +++ b/ui/qt/list.cpp @@ -42,6 +42,10 @@ static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *u return getvalue(elm, col); } +static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return NULL; +} + UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) { UiContainerPrivate *ctn = ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); @@ -102,7 +106,17 @@ UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) { UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); - TableModel *model = new TableModel(obj, view, var, args->model); + ui_getvaluefunc2 getvalue = args->getvalue2; + void *getvaluedata = args->getvalue2data; + if(!getvalue) { + if(args->getvalue) { + getvalue = getvalue_wrapper; + getvaluedata = (void*)args->getvalue; + } else { + getvalue = null_getvalue; + } + } + TableModel *model = new TableModel(obj, view, var, args->model, getvalue, getvaluedata); view->setModel(model); if(var) { diff --git a/ui/qt/model.cpp b/ui/qt/model.cpp index e4846b8..7b99134 100644 --- a/ui/qt/model.cpp +++ b/ui/qt/model.cpp @@ -28,14 +28,6 @@ #include "model.h" -static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) { - if(model->getvalue2) { - return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult); - } else if(model->getvalue) { - return model->getvalue(elm, col); - } - return NULL; -} ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata){ this->obj = obj; @@ -116,11 +108,13 @@ void ListModel::selectionChanged(const QItemSelection& selected, const QItemSele -TableModel::TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model){ +TableModel::TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model, ui_getvaluefunc2 getvalue, void *getvaluedata){ this->obj = obj; this->view = view; this->var = var; this->model = model; + this->getvalue = getvalue; + this->getvaluedata = getvaluedata; this->onactivate = nullptr; this->onactivatedata = nullptr; this->onselection = nullptr; @@ -162,7 +156,7 @@ QVariant TableModel::data(const QModelIndex &index, int role) const { if(rowData) { int col = index.column(); UiBool freeResult = false; - void *value = model_getvalue(model, ls, rowData, index.row(), col, &freeResult); + void *value = getvalue(ls, rowData, index.row(), col, getvaluedata, &freeResult); if(value) { UiModelType type = model->types[col]; switch(type) { diff --git a/ui/qt/model.h b/ui/qt/model.h index faec52a..8fa513e 100644 --- a/ui/qt/model.h +++ b/ui/qt/model.h @@ -45,7 +45,7 @@ class ListModel : public QAbstractListModel { Q_OBJECT ui_getvaluefunc2 getvalue; - void *getvaluedata; + void *getvaluedata; ui_callback onactivate; void *onactivatedata; ui_callback onselection; @@ -75,18 +75,20 @@ public slots: class TableModel : public QAbstractListModel { Q_OBJECT - UiModel *model; - ui_callback onactivate; - void *onactivatedata; - ui_callback onselection; - void *onselectiondata; + UiModel *model; + ui_getvaluefunc2 getvalue; + void *getvaluedata; + ui_callback onactivate; + void *onactivatedata; + ui_callback onselection; + void *onselectiondata; public: - UiObject *obj; - UiVar *var; - QTreeView *view; + UiObject *obj; + UiVar *var; + QTreeView *view; - TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model); + TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model, ui_getvaluefunc2 getvalue, void *getvaluedata); void setActivationCallback(ui_callback f, void *userdata); void setSelectionCallback(ui_callback f, void *userdata); diff --git a/ui/qt/qt4.pro b/ui/qt/qt4.pro deleted file mode 100644 index efc299d..0000000 --- a/ui/qt/qt4.pro +++ /dev/null @@ -1,63 +0,0 @@ -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. -# -# Copyright 2014 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. -# - -TARGET = uitk -TEMPLATE = lib -CONFIG += staticlib warn_off debug -DESTDIR = ../build/lib -MOC_DIR = ../build/ui/qt -OBJECTS_DIR = ../build/ui/qt - -DEFINES += UI_QT4 - -SOURCES += toolkit.cpp -SOURCES += window.cpp -SOURCES += menu.cpp -SOURCES += toolbar.cpp -SOURCES += stock.cpp -SOURCES += container.cpp -SOURCES += text.cpp -SOURCES += model.cpp -SOURCES += tree.cpp -SOURCES += button.cpp -SOURCES += label.cpp -SOURCES += graphics.cpp - -HEADERS += toolkit.h -HEADERS += window.h -HEADERS += menu.h -HEADERS += toolbar.h -HEADERS += stock.h -HEADERS += container.h -HEADERS += text.h -HEADERS += model.h -HEADERS += tree.h -HEADERS += button.h -HEADERS += label.h -HEADERS += graphics.h - diff --git a/ui/qt/toolkit.cpp b/ui/qt/toolkit.cpp index 1d1a15e..2dd98b8 100644 --- a/ui/qt/toolkit.cpp +++ b/ui/qt/toolkit.cpp @@ -43,15 +43,17 @@ static const char *application_name; static ui_callback startup_func; static void *startup_data; static ui_callback open_func; -void *open_data; +static void *open_data; static ui_callback exit_func; -void *exit_data; +static void *exit_data; static int is_toplevel_realized = 0; -int app_argc; -char **app_argv; -QApplication *application = NULL; +static int app_argc; +static char **app_argv; +static QApplication *application = NULL; + +static UiBool exit_on_shutdown; void ui_init(const char *appname, int argc, char **argv) { application_name = appname; @@ -87,6 +89,10 @@ void ui_onexit(ui_callback f, void *userdata) { exit_data = userdata; } +void ui_app_exit_on_shutdown(UiBool exitapp) { + exit_on_shutdown = exitapp; +} + void ui_main() { if(startup_func) { startup_func(NULL, startup_data); @@ -98,6 +104,10 @@ void ui_main() { uic_store_app_properties(); delete application; + + if(exit_on_shutdown) { + exit(0); + } } void ui_show(UiObject *obj) { diff --git a/ui/qt/window.cpp b/ui/qt/window.cpp index 5a9f189..bdc8b4e 100644 --- a/ui/qt/window.cpp +++ b/ui/qt/window.cpp @@ -38,8 +38,10 @@ #include #include #include +#include +#include -static UiObject* create_window(const char *title, void *window_data, bool simple) { +static UiObject* create_window(const char *title, void *window_data, bool simple, bool sidebar = false) { UiObject *obj = uic_object_new_toplevel(); obj->window = window_data; obj->next = NULL; @@ -61,19 +63,70 @@ static UiObject* create_window(const char *title, void *window_data, bool simple boxWidget->setLayout(box); window->setCentralWidget(boxWidget); ui_container_add(obj, new UiBoxContainer(box)); + if(sidebar) { + QDockWidget *dock = new QDockWidget(); + window->addDockWidget(Qt::LeftDockWidgetArea, dock); + window->setProperty("ui_sidebar", QVariant::fromValue((void*)dock)); + } obj->widget = window; return obj; } UiObject* ui_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE); + return create_window(title, window_data, false); } UiObject* ui_simplewindow(char *title, void *window_data) { - return create_window(title, window_data, TRUE); + return create_window(title, window_data, true); +} + +UiObject *ui_sidebar_window(const char *title, void *window_data) { + return create_window(title, window_data, false, true); } +void ui_dialog_create(UiObject *parent, UiDialogArgs *args) { + if(args->input || args->password) { + // TODO: QInputDialog + } else { + QMessageBox msgBox; + if(args->title) { + msgBox.setWindowTitle(args->title); + } + if(args->content) { + msgBox.setText(args->content); + } + QPushButton *btn1; + QPushButton *btn2; + if(args->button1_label) { + btn1 = msgBox.addButton(args->button1_label, QMessageBox::ActionRole); + } + if(args->button2_label) { + btn2 = msgBox.addButton(args->button2_label, QMessageBox::ActionRole); + } + if(args->closebutton_label) { + msgBox.addButton(args->closebutton_label, QMessageBox::DestructiveRole); + } + + msgBox.exec(); + + UiEvent evt; + evt.obj = parent; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.eventdata = NULL; + evt.eventdatatype = 0; + evt.intval = 0; + if(msgBox.clickedButton() == btn1) { + evt.intval = 1; + } else if(msgBox.clickedButton() == btn2) { + evt.intval = 2; + } + if(args->result) { + args->result(&evt, args->resultdata); + } + } +} char* ui_openfiledialog(UiObject *obj) { QString fileName = QFileDialog::getOpenFileName(obj->widget); diff --git a/ui/ui/button.h b/ui/ui/button.h index fb578f7..fff9383 100644 --- a/ui/ui/button.h +++ b/ui/ui/button.h @@ -35,6 +35,12 @@ extern "C" { #endif +enum UiLinkType { + UI_LINK_TEXT = 0, + UI_LINK_BUTTON +}; +typedef enum UiLinkType UiLinkType; + typedef struct UiButtonArgs { UiBool fill; UiBool hexpand; @@ -81,21 +87,59 @@ typedef struct UiToggleArgs { const int* groups; } UiToggleArgs; + +typedef struct UiLinkButtonArgs { + UiBool fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const char *label; + const char *uri; + UiString *value; + const char *varname; + ui_callback onclick; + void *onclickdata; + UiBool nofollow; + UiLinkType type; + + const int* groups; +} UiLinkButtonArgs; #define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } ) #define ui_togglebutton(obj, ...) ui_togglebutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_checkbox(obj, ...) ui_checkbox_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_switch(obj, ...) ui_switch_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_radiobutton(obj, ...) ui_radiobutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) +#define ui_linkbutton(obj, ...) ui_linkbutton_create(obj, &(UiLinkButtonArgs){ __VA_ARGS__ }) -UIEXPORT UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args); -UIEXPORT UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args); +UIEXPORT UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args); +UIEXPORT void ui_button_set_label(UIWIDGET button, const char *label); +UIEXPORT void ui_button_set_icon(UIWIDGET button, const char *icon); +UIEXPORT void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri); +UIEXPORT void ui_linkbutton_value_set_label(UiString *str, const char *label); +UIEXPORT void ui_linkbutton_value_set_uri(UiString *str, const char *uri); +UIEXPORT void ui_linkbutton_value_set_visited(UiString *str, UiBool visited); +UIEXPORT void ui_linkbutton_set_label(UIWIDGET button, const char *label); +UIEXPORT void ui_linkbutton_set_uri(UIWIDGET button, const char *label); +UIEXPORT void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited); +UIEXPORT char* ui_linkbutton_get_label(UIWIDGET button); +UIEXPORT char* ui_linkbutton_get_uri(UIWIDGET button); +UIEXPORT UiBool ui_linkbutton_get_visited(UIWIDGET button); #ifdef __cplusplus } diff --git a/ui/ui/display.h b/ui/ui/display.h index 3cb3d65..3e05222 100644 --- a/ui/ui/display.h +++ b/ui/ui/display.h @@ -38,15 +38,7 @@ #ifdef __cplusplus extern "C" { #endif - -enum UiAlignment { - UI_ALIGN_DEFAULT = 0, - UI_ALIGN_LEFT, - UI_ALIGN_RIGHT, - UI_ALIGN_CENTER -}; - -typedef enum UiAlignment UiAlignment; + enum UiLabelStyle { UI_LABEL_STYLE_DEFAULT = 0, diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 7b54742..d412156 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -237,7 +237,16 @@ typedef enum UiDnDAction { UI_DND_ACTION_LINK, UI_DND_ACTION_CUSTOM } UiDnDAction; - + +enum UiAlignment { + UI_ALIGN_DEFAULT = 0, + UI_ALIGN_LEFT, + UI_ALIGN_RIGHT, + UI_ALIGN_CENTER +}; + +typedef enum UiAlignment UiAlignment; + typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */ typedef void*(*ui_getvaluefunc)(void *elm, int col); @@ -514,6 +523,9 @@ UIEXPORT void ui_onstartup(ui_callback f, void *userdata); UIEXPORT void ui_onopen(ui_callback f, void *userdata); UIEXPORT void ui_onexit(ui_callback f, void *userdata); +UIEXPORT int ui_app_save_settings(void); +UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp); + UIEXPORT void ui_main(void); UIEXPORT void ui_show(UiObject *obj); UIEXPORT void ui_close(UiObject *obj); @@ -608,6 +620,8 @@ UIEXPORT void ui_list_prepend(UiList *list, void *data); UIEXPORT void ui_list_remove(UiList *list, int i); UIEXPORT void ui_list_clear(UiList *list); UIEXPORT void ui_list_update(UiList *list); +UIEXPORT void ui_list_update_row(UiList *list, int row); +UIEXPORT UiListSelection ui_list_get_selection(UiList *list); UIEXPORT void ui_list_addobsv(UiList *list, ui_callback f, void *data); UIEXPORT void ui_list_notify(UiList *list); diff --git a/ui/ui/tree.h b/ui/ui/tree.h index af51d08..4a1e590 100644 --- a/ui/ui/tree.h +++ b/ui/ui/tree.h @@ -76,25 +76,6 @@ struct UiModel { * array of column size hints */ int *columnsize; - - /* - * void*(*ui_getvaluefunc)(void *elm, int col); - * - * function for translating model data to view data - * first argument is the pointer returned by UiList first|next|get - * second argument is the column index - * TODO: return - */ - ui_getvaluefunc getvalue; - - /* - * void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata) - * - * alternative for getvalue - */ - ui_getvaluefunc2 getvalue2; - - void *getvalue2data; }; struct UiListCallbacks { @@ -248,12 +229,23 @@ struct UiSourceListArgs { */ ui_callback onbuttonclick; void *onbuttonclickdata; + + UiMenuBuilder *contextmenu; }; #define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ } #define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} } +/* + * Creates an UiModel, that specifies columns for a table widget. + * + * For each column a column type (UiModelType) and a title string + * (char*) must be specified. The column list must be terminated + * with -1. + * + * UiModel *model = ui_model(ctx, UI_STRING, "Column 1", UI_STRING, "Column 2", -1); + */ UIEXPORT UiModel* ui_model(UiContext *ctx, ...); UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model); UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi); diff --git a/ui/ui/window.h b/ui/ui/window.h index 352abb7..78703c2 100644 --- a/ui/ui/window.h +++ b/ui/ui/window.h @@ -75,16 +75,16 @@ typedef struct UiDialogWindowArgs { UIEXPORT UiObject *ui_window(const char *title, void *window_data); UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data); UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data); -UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args); +UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args); -#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, (UiDialogWindowArgs){ __VA_ARGS__ }); -#define ui_dialog_window0(parent) ui_dialog_window_create(parent, (UiDialogWindowArgs){ 0 }); +#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ }); +#define ui_dialog_window0(parent) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ 0 }); UIEXPORT void ui_window_size(UiObject *obj, int width, int height); -#define ui_dialog(parent, ...) ui_dialog_create(parent, (UiDialogArgs){ __VA_ARGS__ } ) +#define ui_dialog(parent, ...) ui_dialog_create(parent, &(UiDialogArgs){ __VA_ARGS__ } ) -UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs args); +UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs *args); UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata); UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata); -- 2.47.3