ui/cocoa/list.m

changeset 109
c3dfcb8f0be7
child 112
c3f2f16fa4b8
equal deleted inserted replaced
108:77254bd6dccb 109:c3dfcb8f0be7
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 }

mercurial