UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2025 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #import "list.h" 30 #import "ListDelegate.h" 31 #import <objc/runtime.h> 32 33 static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { 34 ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; 35 return getvalue(elm, col); 36 } 37 38 static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { 39 return elm; 40 } 41 42 /* --------------------------- ListView --------------------------- */ 43 44 /* 45 * adds a NSTableViewDelegate that handles all events and calls 46 * callbacks specified in the UiListArgs 47 */ 48 static void add_listdelegate(UiObject *obj, NSTableView *tableview, UiListArgs *args) { 49 ListDelegate *delegate = [[ListDelegate alloc] init:tableview obj:obj]; 50 delegate.onactivate = args->onactivate; 51 delegate.onactivatedata = args->onactivatedata; 52 delegate.onselection = args->onselection; 53 delegate.onselectiondata = args->onselectiondata; 54 tableview.delegate = delegate; 55 objc_setAssociatedObject(tableview, "ui_listdelegate", delegate, OBJC_ASSOCIATION_RETAIN); 56 tableview.doubleAction = @selector(activateEvent:); 57 tableview.target = delegate; 58 } 59 60 static void bind_list_to_tableview(UiList *list, NSTableView *tableview) { 61 list->obj = (__bridge void*)tableview; 62 list->update = ui_tableview_update; 63 list->getselection = ui_tableview_getselection; 64 list->setselection = ui_tableview_setselection; 65 } 66 67 UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) { 68 NSScrollView *scrollview = [[NSScrollView alloc] init]; 69 70 NSTableView *tableview = [[NSTableView alloc] init]; 71 tableview.autoresizingMask = NSViewWidthSizable; 72 tableview.headerView = nil; 73 74 if(args->multiselection) { 75 tableview.allowsMultipleSelection = YES; 76 } 77 78 scrollview.documentView = tableview; 79 80 UiLayout layout = UI_INIT_LAYOUT(args); 81 ui_container_add(obj, scrollview, &layout); 82 83 add_listdelegate(obj, tableview, args); 84 85 UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); 86 if(var) { 87 UiList *list = var->value; 88 bind_list_to_tableview(list, tableview); 89 90 ui_getvaluefunc2 getvalue = args->getvalue2; 91 void *getvaluedata = args->getvalue2data; 92 if(!getvalue) { 93 if(args->getvalue) { 94 getvalue = getvalue_wrapper; 95 getvaluedata = (void*)args->getvalue; 96 } else { 97 getvalue = str_getvalue; // by default list values are interpreted as strings 98 } 99 } 100 101 NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"column"]; 102 [tableview addTableColumn:column]; 103 104 ListDataSource *dataSource = [[ListDataSource alloc] init:tableview.tableColumns var:var getvalue:getvalue getvaluedata:getvaluedata]; 105 106 tableview.dataSource = dataSource; 107 [tableview reloadData]; 108 109 objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN); 110 } 111 112 return (__bridge void*)scrollview; 113 } 114 115 /* --------------------------- TableView --------------------------- */ 116 117 UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) { 118 NSScrollView *scrollview = [[NSScrollView alloc] init]; 119 120 NSTableView *tableview = [[NSTableView alloc] init]; 121 tableview.autoresizingMask = NSViewWidthSizable; 122 tableview.columnAutoresizingStyle = NSTableViewSequentialColumnAutoresizingStyle; 123 124 if(args->multiselection) { 125 tableview.allowsMultipleSelection = YES; 126 } 127 128 UiLayout layout = UI_INIT_LAYOUT(args); 129 ui_container_add(obj, scrollview, &layout); 130 131 add_listdelegate(obj, tableview, args); 132 133 // convert model 134 NSMutableArray<NSTableColumn*> *cols = [[NSMutableArray alloc] init]; 135 UiModel *model = args->model; 136 if(model) { 137 for(int i=0;i<model->columns;i++) { 138 char *title = model->titles[i]; 139 UiModelType type = model->types[i]; 140 int width = model->columnsize[i]; 141 NSString *identifier = [[NSString alloc] initWithUTF8String:title]; 142 NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:identifier]; 143 column.title = identifier; 144 column.resizingMask = NSTableColumnUserResizingMask; 145 if(width > 0) { 146 column.width = width; 147 } else if(width < 0) { 148 column.resizingMask = NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask; 149 } 150 if(type >= UI_ICON) { 151 // TODO 152 } 153 [tableview addTableColumn:column]; 154 [cols addObject:column]; 155 } 156 } 157 158 UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); 159 if(var) { 160 UiList *list = var->value; 161 bind_list_to_tableview(list, tableview); 162 163 ui_getvaluefunc2 getvalue = args->getvalue2; 164 void *getvaluedata = args->getvalue2data; 165 if(!getvalue) { 166 if(args->getvalue) { 167 getvalue = getvalue_wrapper; 168 getvaluedata = (void*)args->getvalue; 169 } else { 170 fprintf(stderr, "Error: tableview requires getvalue or getvalue2 func\n"); 171 return (__bridge void*)scrollview; 172 } 173 } 174 175 ListDataSource *dataSource = [[ListDataSource alloc] init:cols var:var getvalue:getvalue getvaluedata:getvaluedata]; 176 if(model) { 177 dataSource.model = ui_model_copy(obj->ctx, model); 178 } 179 180 tableview.dataSource = dataSource; 181 [tableview reloadData]; 182 183 objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN); 184 } 185 186 scrollview.documentView = tableview; 187 188 return (__bridge void*)scrollview; 189 } 190 191 /* ------ common functions ------ */ 192 193 void ui_tableview_update(UiList *list, int i) { 194 NSTableView *tableview = (__bridge NSTableView*)list->obj; 195 if(i < 0) { 196 [tableview reloadData]; 197 } else { 198 [tableview reloadData]; // TODO: optimize 199 } 200 } 201 202 UiListSelection ui_tableview_getselection(UiList *list) { 203 NSTableView *tableview = (__bridge NSTableView*)list->obj; 204 return ui_tableview_selection(tableview); 205 } 206 207 void ui_tableview_setselection(UiList *list, UiListSelection selection) { 208 NSTableView *tableview = (__bridge NSTableView*)list->obj; 209 NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; 210 for(int i=0;i<selection.count;i++) { 211 [indexSet addIndex:selection.rows[i]]; 212 } 213 [tableview selectRowIndexes:indexSet byExtendingSelection:NO]; 214 } 215 216 217 /* --------------------------- DropDown --------------------------- */ 218 219 @implementation UiDropDown 220 221 - (id)init:(UiObject*)obj { 222 _obj = obj; 223 return self; 224 } 225 226 - (void) comboBoxSelectionDidChange:(NSNotification *) notification { 227 int index = (int)_combobox.indexOfSelectedItem; 228 229 void *eventdata = NULL; 230 if(_var) { 231 UiList *list = _var->value; 232 if(index >= 0) { 233 eventdata = list->get(list, index); 234 } 235 } else { 236 NSString *str = _combobox.objectValueOfSelectedItem; 237 if(str) { 238 eventdata = (void*)str.UTF8String; 239 } 240 } 241 242 UiEvent event; 243 event.obj = _obj; 244 event.window = event.obj->window; 245 event.document = event.obj->ctx->document; 246 event.eventdata = eventdata; 247 event.eventdatatype = UI_EVENT_DATA_LIST_ELM; 248 event.intval = index; 249 250 if(_onselection) { 251 _onselection(&event, _onselectiondata); 252 } 253 254 if(_onactivate) { 255 _onactivate(&event, _onactivatedata); 256 } 257 } 258 259 @end 260 261 UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) { 262 NSComboBox *dropdown = [[NSComboBox alloc] init]; 263 dropdown.editable = NO; 264 265 UiDropDown *uidropdown = [[UiDropDown alloc] init:obj]; 266 objc_setAssociatedObject(dropdown, "ui_dropdown", uidropdown, OBJC_ASSOCIATION_RETAIN); 267 uidropdown.onactivate = args->onactivate; 268 uidropdown.onactivatedata = args->onactivatedata; 269 uidropdown.onselection = args->onselection; 270 uidropdown.onselectiondata = args->onselectiondata; 271 uidropdown.combobox = dropdown; 272 273 if(!args->getvalue2) { 274 if(args->getvalue) { 275 args->getvalue2 = getvalue_wrapper; 276 args->getvalue2data = (void*)args->getvalue; 277 } else { 278 args->getvalue2 = str_getvalue; 279 } 280 } 281 uidropdown.getvalue = args->getvalue2; 282 uidropdown.getvaluedata = args->getvalue2data; 283 284 UiLayout layout = UI_INIT_LAYOUT(args); 285 ui_container_add(obj, dropdown, &layout); 286 287 UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); 288 if(var) { 289 UiList *list = var->value; 290 list->obj = (__bridge void*)dropdown; 291 list->update = ui_dropdown_update; 292 list->getselection = ui_dropdown_getselection; 293 list->setselection = ui_dropdown_setselection; 294 ui_dropdown_update(list, -1); 295 } else { 296 for(int i=0;i<args->static_nelm;i++) { 297 char *str = args->static_elements[i]; 298 NSString *item = [[NSString alloc] initWithUTF8String:str]; 299 [dropdown addItemWithObjectValue:item]; 300 } 301 } 302 303 uidropdown.var = var; 304 305 return (__bridge void*)dropdown; 306 } 307 308 void ui_dropdown_update(UiList *list, int i) { 309 NSComboBox *combobox = (__bridge NSComboBox*)list->obj; 310 UiDropDown *dropdown = objc_getAssociatedObject(combobox, "ui_dropdown"); 311 if(dropdown) { 312 [combobox removeAllItems]; 313 314 ui_getvaluefunc2 getvalue = dropdown.getvalue; 315 void *getvaluedata = dropdown.getvaluedata; 316 317 int index = 0; 318 void *elm = list->first(list); 319 while(elm) { 320 UiBool freeResult = FALSE; 321 char *str = getvalue(list, elm, index, 0, getvaluedata, &freeResult); 322 if(str) { 323 NSString *item = [[NSString alloc] initWithUTF8String:str]; 324 [combobox addItemWithObjectValue:item]; 325 } 326 if(freeResult) { 327 free(str); 328 } 329 elm = list->next(list); 330 index++; 331 } 332 } else { 333 fprintf(stderr, "Error: obj is not a dropdown\n"); 334 } 335 } 336 337 UiListSelection ui_dropdown_getselection(UiList *list) { 338 UiListSelection sel = { 0, NULL }; 339 NSComboBox *combobox = (__bridge NSComboBox*)list->obj; 340 NSInteger index = combobox.indexOfSelectedItem; 341 if(index >= 0) { 342 sel.rows = malloc(sizeof(int)); 343 sel.count = 1; 344 sel.rows[0] = (int)index; 345 } 346 return sel; 347 } 348 349 void ui_dropdown_setselection(UiList *list, UiListSelection selection) { 350 NSComboBox *combobox = (__bridge NSComboBox*)list->obj; 351 if(selection.count > 0) { 352 [combobox selectItemAtIndex:selection.rows[0]]; 353 } else { 354 [combobox selectItemAtIndex: -1]; 355 } 356 } 357