# HG changeset patch # User Olaf Wintermann # Date 1760098432 -7200 # Node ID a952337ae325440d804d7884cdf22dc5ccb8a985 # Parent eae5b817aa47b300c5b4f3dc035e79c061fe75ee add sourcelist data source (Cocoa) diff -r eae5b817aa47 -r a952337ae325 make/xcode/toolkit/toolkit/main.m --- a/make/xcode/toolkit/toolkit/main.m Fri Oct 10 09:06:06 2025 +0200 +++ b/make/xcode/toolkit/toolkit/main.m Fri Oct 10 14:13:52 2025 +0200 @@ -39,6 +39,7 @@ UiDouble *number; UiList *list1; UiList *list2; + UiList *sidebar_list; } MyDocument; MyDocument* create_doc(void) { @@ -60,6 +61,14 @@ ui_list_append(doc->list2, "Option 5"); ui_list_append(doc->list2, "Option 6"); + doc->sidebar_list = ui_list_new(ctx, "sidebar_list"); + ui_list_append(doc->sidebar_list, "Item 1"); + ui_list_append(doc->sidebar_list, "Item 2"); + ui_list_append(doc->sidebar_list, "Item 3"); + ui_list_append(doc->sidebar_list, "Item 4"); + ui_list_append(doc->sidebar_list, "Item 5"); + ui_list_append(doc->sidebar_list, "Item 6"); + return doc; } @@ -105,12 +114,11 @@ MyDocument *doc = create_doc(); ui_attach_document(obj->ctx, doc); - + UiSubList sublist = {0}; + sublist.header = "Test"; + sublist.value = doc->sidebar_list; ui_sidebar(obj) { - ui_vbox(obj, .margin = 0, .fill = TRUE) { - ui_button(obj, .label = "Button"); - //ui_textarea(obj, .varname = "text", .fill = TRUE); - } + ui_sourcelist(obj, .fill = TRUE, .sublists = &sublist, .numsublists = 1); } diff -r eae5b817aa47 -r a952337ae325 ui/cocoa/container.h --- a/ui/cocoa/container.h Fri Oct 10 09:06:06 2025 +0200 +++ b/ui/cocoa/container.h Fri Oct 10 14:13:52 2025 +0200 @@ -34,13 +34,7 @@ #define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) typedef struct UiLayout UiLayout; -typedef enum UiLayoutBool UiLayoutBool; -enum UiLayoutBool { - UI_LAYOUT_UNDEFINED = 0, - UI_LAYOUT_TRUE, - UI_LAYOUT_FALSE, -}; #define UI_INIT_LAYOUT(args) (UiLayout) {\ .fill = args->fill, \ diff -r eae5b817aa47 -r a952337ae325 ui/cocoa/list.h --- a/ui/cocoa/list.h Fri Oct 10 09:06:06 2025 +0200 +++ b/ui/cocoa/list.h Fri Oct 10 14:13:52 2025 +0200 @@ -55,3 +55,50 @@ void ui_dropdown_update(UiList *list, int i); UiListSelection ui_dropdown_getselection(UiList *list); void ui_dropdown_setselection(UiList *list, UiListSelection selection); + +@class UiSourceList; + +@interface UiSourceListItem : NSObject +@property (weak) UiSourceList *sourcelist; +@property (strong) NSString *label; +@property (strong) NSString *badge; + +@property (strong) NSMutableArray *items; +@property UiVar *var; +@property UiSubList *sublist; + +/* + * Initialize a section item + */ +- (id)init:(UiSubListItem*)item; +/* + * Initialize a child item + */ +- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist; +- (BOOL)isSection; +- (void)update:(int)row; + +@end + + +@interface UiSourceList : NSObject + +@property UiObject *obj; +@property CxList *sublists; +@property UiVar *dynamic_sublists; +@property ui_sublist_getvalue_func getvalue; +@property void *getvaluedata; +@property ui_callback onactivate; +@property void *onactivatedata; +@property ui_callback onbuttonclick; +@property void *onbuttonclickdata; + +@property (strong) NSMutableArray *sections; + +- (id)init:(UiObject*)obj; + +- (void)update:(int)row; + +@end + +void ui_sourcelist_update(UiList *list, int row); diff -r eae5b817aa47 -r a952337ae325 ui/cocoa/list.m --- a/ui/cocoa/list.m Fri Oct 10 09:06:06 2025 +0200 +++ b/ui/cocoa/list.m Fri Oct 10 14:13:52 2025 +0200 @@ -30,6 +30,11 @@ #import "ListDelegate.h" #import +#import +#import + +#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); @@ -354,3 +359,221 @@ [combobox selectItemAtIndex: -1]; } } + + +/* --------------------------- SourceList --------------------------- */ + +static void sublist_free(const CxAllocator *a, UiSubList *sl) { + cxFree(a, (char*)sl->varname); + cxFree(a, (char*)sl->header); +} + +static UiSubList copy_sublist(const CxAllocator *a, UiSubList *sl) { + UiSubList new_sl; + new_sl.value = sl->value; + new_sl.varname = sl->varname ? cx_strdup_a(a, cx_str(sl->varname)).ptr : NULL; + new_sl.header = sl->header ? cx_strdup_a(a, cx_str(sl->header)).ptr : NULL; + new_sl.separator = sl->separator; + new_sl.userdata = sl->userdata; + return new_sl; +} + +static CxList* copy_sublists(const CxAllocator *a, UiSourceListArgs *args) { + if(args->sublists) { + size_t max = args->numsublists; + if(max == 0) { + max = INT_MAX; + } + + CxList *sublists = cxArrayListCreate(a, NULL, sizeof(UiSubList), args->numsublists); + sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_free; + + for(int i=0;isublists[i]; + if(sl->value == NULL && sl->varname == NULL) { + break; + } + + UiSubList new_sl = copy_sublist(a, sl); + cxListAdd(sublists, &new_sl); + } + + return sublists; + } + return NULL; +} + +UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { + // create views + NSScrollView *scrollview = [[NSScrollView alloc] init]; + scrollview.autoresizingMask = NSViewWidthSizable; + + NSOutlineView *outline = [[NSOutlineView alloc]init]; + NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"label"]; + [outline addTableColumn:column]; + outline.outlineTableColumn = column; + outline.headerView = NULL; + + outline.style = NSTableViewStyleSourceList; + + // Make background transparent so vibrancy shows through + outline.backgroundColor = [NSColor clearColor]; + scrollview.drawsBackground = NO; + + scrollview.documentView = outline; + + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, scrollview, &layout); + + // datasource and delegate + UiSourceList *data = [[UiSourceList alloc] init:obj]; + data.sublists = copy_sublists(obj->ctx->allocator, args); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + list->obj = (__bridge void*)data; + list->update = ui_sourcelist_update; + } + data.dynamic_sublists = var; + data.getvalue = args->getvalue; + data.getvaluedata = args->getvaluedata; + data.onactivate = args->onactivate; + data.onactivatedata = args->onactivatedata; + data.onbuttonclick = args->onbuttonclick; + data.onactivatedata = args->onbuttonclickdata; + [data update:-1]; + + outline.dataSource = data; + //outline.delegate = data; + + objc_setAssociatedObject(outline, "ui_datasource", data, OBJC_ASSOCIATION_RETAIN); + + return (__bridge void*)scrollview; +} + +void ui_sourcelist_update(UiList *list, int row) { + UiSourceList *sourcelist = (__bridge UiSourceList*)list->obj; + [sourcelist update:row]; +} + + +@implementation UiSourceList + +- (id)init:(UiObject*)obj { + _obj = obj; + _sections = [[NSMutableArray alloc] initWithCapacity:16]; + return self; +} + +- (void)dealloc { + cxListFree(_sublists); +} + +- (void)update:(int)row { + // TODO: check row + + [_sections removeAllObjects]; + + CxIterator i = cxListIterator(_sublists); + cx_foreach(UiSubList *, sl, i) { + UiSourceListItem *section = [[UiSourceListItem alloc] init:self sublist:sl]; + [section update:-1]; + [_sections addObject:section]; + } +} + +// NSOutlineViewDataSource implementation + +- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { + if(item == nil) { + return _sections.count; + } else { + UiSourceListItem *i = item; + return i.items.count; + } +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { + UiSourceListItem *i = item; + return [i isSection]; +} + +- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { + UiSourceListItem *i = item; + if(i) { + return [i.items objectAtIndex:index]; + } + return [_sections objectAtIndex:index]; +} + +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { + UiSourceListItem *i = item; + if([[tableColumn identifier] isEqualToString:@"label"]) { + return i.label; + } + return nil; +} + +- (void)outlineView:(NSOutlineView *)outlineView + setObjectValue:(id)object + forTableColumn:(NSTableColumn *)tableColumn + byItem:(id)item { + +} + +@end + +@implementation UiSourceListItem + +- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist { + _sourcelist = sourcelist; + _sublist = sublist; + _items = [[NSMutableArray alloc]initWithCapacity:16]; + if(sublist->header) { + _label = [[NSString alloc]initWithUTF8String:sublist->header]; + } + UiVar *var = uic_widget_var(sourcelist.obj->ctx, + sourcelist.obj->ctx, + sublist->value, + sublist->varname, + UI_VAR_LIST); + _var = var; + return self; +} + +- (id)init:(UiSubListItem*)item { + if(item->label) { + _label = [[NSString alloc]initWithUTF8String:item->label]; + } + return self; +} + +- (BOOL)isSection { + return _sublist != NULL; +} + +- (void)update:(int)row { + [_items removeAllObjects]; + if(_var == NULL) { + return; + } + UiList *list = _var->value; + void *elm = list->first(list); + int index = 0; + while(elm) { + UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; + if(_sourcelist.getvalue) { + _sourcelist.getvalue(list, _sublist->userdata, elm, index, &item, _sourcelist.getvaluedata); + } else { + item.label = strdup(elm); + } + + [_items addObject:[[UiSourceListItem alloc] init:&item]]; + + elm = list->next(list); + index++; + } +} + +@end +