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 +