diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 15:06:52 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 15:07:38 +0300 |
commit | d773b499e595a43b9b1ae449262dcf13cabf2d02 (patch) | |
tree | b1baeb12025a04f8316636b5d0ab18e30ceedb2c /src/ui/util.c |
Initial commit
Borrowing the app skeleton from Bitwise Harmony.
Diffstat (limited to 'src/ui/util.c')
-rw-r--r-- | src/ui/util.c | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/src/ui/util.c b/src/ui/util.c new file mode 100644 index 00000000..9487e004 --- /dev/null +++ b/src/ui/util.c | |||
@@ -0,0 +1,604 @@ | |||
1 | #include "util.h" | ||
2 | |||
3 | #include "app.h" | ||
4 | #include "color.h" | ||
5 | #include "command.h" | ||
6 | #include "labelwidget.h" | ||
7 | #include "inputwidget.h" | ||
8 | #include "widget.h" | ||
9 | #include "text.h" | ||
10 | #include "window.h" | ||
11 | |||
12 | #include <the_Foundation/math.h> | ||
13 | #include <the_Foundation/path.h> | ||
14 | |||
15 | iBool isCommand_UserEvent(const SDL_Event *d, const char *cmd) { | ||
16 | return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode && | ||
17 | equal_Command(d->user.data1, cmd); | ||
18 | } | ||
19 | |||
20 | const char *command_UserEvent(const SDL_Event *d) { | ||
21 | if (d->type == SDL_USEREVENT && d->user.code == command_UserEventCode) { | ||
22 | return d->user.data1; | ||
23 | } | ||
24 | return ""; | ||
25 | } | ||
26 | |||
27 | int keyMods_Sym(int kmods) { | ||
28 | kmods &= (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL | KMOD_GUI); | ||
29 | /* Don't treat left/right modifiers differently. */ | ||
30 | if (kmods & KMOD_SHIFT) kmods |= KMOD_SHIFT; | ||
31 | if (kmods & KMOD_ALT) kmods |= KMOD_ALT; | ||
32 | if (kmods & KMOD_CTRL) kmods |= KMOD_CTRL; | ||
33 | if (kmods & KMOD_GUI) kmods |= KMOD_GUI; | ||
34 | return kmods; | ||
35 | } | ||
36 | |||
37 | /*-----------------------------------------------------------------------------------------------*/ | ||
38 | |||
39 | void init_Click(iClick *d, iAnyObject *widget, int button) { | ||
40 | d->isActive = iFalse; | ||
41 | d->button = button; | ||
42 | d->bounds = as_Widget(widget); | ||
43 | d->startPos = zero_I2(); | ||
44 | d->pos = zero_I2(); | ||
45 | } | ||
46 | |||
47 | enum iClickResult processEvent_Click(iClick *d, const SDL_Event *event) { | ||
48 | if (event->type == SDL_MOUSEMOTION) { | ||
49 | const iInt2 pos = init_I2(event->motion.x, event->motion.y); | ||
50 | if (d->isActive) { | ||
51 | d->pos = pos; | ||
52 | return drag_ClickResult; | ||
53 | } | ||
54 | } | ||
55 | if (event->type != SDL_MOUSEBUTTONDOWN && event->type != SDL_MOUSEBUTTONUP) { | ||
56 | return none_ClickResult; | ||
57 | } | ||
58 | const SDL_MouseButtonEvent *mb = &event->button; | ||
59 | if (mb->button != d->button) { | ||
60 | return none_ClickResult; | ||
61 | } | ||
62 | const iInt2 pos = init_I2(mb->x, mb->y); | ||
63 | if (event->type == SDL_MOUSEBUTTONDOWN && mb->clicks == 2) { | ||
64 | if (contains_Widget(d->bounds, pos)) { | ||
65 | d->pos = pos; | ||
66 | setMouseGrab_Widget(NULL); | ||
67 | return double_ClickResult; | ||
68 | } | ||
69 | } | ||
70 | if (!d->isActive) { | ||
71 | if (mb->state == SDL_PRESSED) { | ||
72 | if (contains_Widget(d->bounds, pos)) { | ||
73 | d->isActive = iTrue; | ||
74 | d->startPos = d->pos = pos; | ||
75 | //setFlags_Widget(d->bounds, hover_WidgetFlag, iFalse); | ||
76 | setMouseGrab_Widget(d->bounds); | ||
77 | return started_ClickResult; | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | else { /* Active. */ | ||
82 | if (mb->state == SDL_RELEASED) { | ||
83 | enum iClickResult result = contains_Widget(d->bounds, pos) | ||
84 | ? finished_ClickResult | ||
85 | : aborted_ClickResult; | ||
86 | d->isActive = iFalse; | ||
87 | d->pos = pos; | ||
88 | setMouseGrab_Widget(NULL); | ||
89 | return result; | ||
90 | } | ||
91 | } | ||
92 | return none_ClickResult; | ||
93 | } | ||
94 | |||
95 | void cancel_Click(iClick *d) { | ||
96 | if (d->isActive) { | ||
97 | d->isActive = iFalse; | ||
98 | setMouseGrab_Widget(NULL); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | iBool isMoved_Click(const iClick *d) { | ||
103 | return dist_I2(d->startPos, d->pos) > 2; | ||
104 | } | ||
105 | |||
106 | iInt2 pos_Click(const iClick *d) { | ||
107 | return d->pos; | ||
108 | } | ||
109 | |||
110 | iRect rect_Click(const iClick *d) { | ||
111 | return initCorners_Rect(min_I2(d->startPos, d->pos), max_I2(d->startPos, d->pos)); | ||
112 | } | ||
113 | |||
114 | iInt2 delta_Click(const iClick *d) { | ||
115 | return sub_I2(d->pos, d->startPos); | ||
116 | } | ||
117 | |||
118 | /*-----------------------------------------------------------------------------------------------*/ | ||
119 | |||
120 | iWidget *makePadding_Widget(int size) { | ||
121 | iWidget *pad = new_Widget(); | ||
122 | setSize_Widget(pad, init1_I2(size)); | ||
123 | return pad; | ||
124 | } | ||
125 | |||
126 | iLabelWidget *makeHeading_Widget(const char *text) { | ||
127 | iLabelWidget *heading = new_LabelWidget(text, 0, 0, NULL); | ||
128 | setFlags_Widget(as_Widget(heading), frameless_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
129 | return heading; | ||
130 | } | ||
131 | |||
132 | iWidget *makeVDiv_Widget(void) { | ||
133 | iWidget *div = new_Widget(); | ||
134 | setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeVertical_WidgetFlag, iTrue); | ||
135 | return div; | ||
136 | } | ||
137 | |||
138 | iWidget *makeHDiv_Widget(void) { | ||
139 | iWidget *div = new_Widget(); | ||
140 | setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); | ||
141 | return div; | ||
142 | } | ||
143 | |||
144 | iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *command) { | ||
145 | iLabelWidget *action = new_LabelWidget("", key, kmods, command); | ||
146 | setSize_Widget(as_Widget(action), zero_I2()); | ||
147 | addChildFlags_Widget(parent, iClob(action), hidden_WidgetFlag); | ||
148 | return as_Widget(action); | ||
149 | } | ||
150 | |||
151 | /*-----------------------------------------------------------------------------------------------*/ | ||
152 | |||
153 | static iBool menuHandler_(iWidget *menu, const char *cmd) { | ||
154 | if (isVisible_Widget(menu)) { | ||
155 | if (equal_Command(cmd, "menu.open") && pointer_Command(cmd) == menu->parent) { | ||
156 | /* Don't reopen self; instead, root will close the menu. */ | ||
157 | return iFalse; | ||
158 | } | ||
159 | if (!equal_Command(cmd, "window.resized")) { | ||
160 | closeMenu_Widget(menu); | ||
161 | } | ||
162 | } | ||
163 | return iFalse; | ||
164 | } | ||
165 | |||
166 | iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | ||
167 | iWidget *menu = new_Widget(); | ||
168 | setBackgroundColor_Widget(menu, gray25_ColorId); | ||
169 | setFlags_Widget(menu, | ||
170 | keepOnTop_WidgetFlag | hidden_WidgetFlag | arrangeVertical_WidgetFlag | | ||
171 | arrangeSize_WidgetFlag | resizeChildrenToWidestChild_WidgetFlag, | ||
172 | iTrue); | ||
173 | for (size_t i = 0; i < n; ++i) { | ||
174 | const iMenuItem *item = &items[i]; | ||
175 | if (equal_CStr(item->label, "---")) { | ||
176 | iWidget *sep = addChild_Widget(menu, iClob(new_Widget())); | ||
177 | setBackgroundColor_Widget(sep, gray50_ColorId); | ||
178 | sep->rect.size.y = gap_UI / 3; | ||
179 | setFlags_Widget(sep, hover_WidgetFlag | fixedHeight_WidgetFlag, iTrue); | ||
180 | } | ||
181 | else { | ||
182 | iLabelWidget *label = addChildFlags_Widget( | ||
183 | menu, | ||
184 | iClob(new_LabelWidget(item->label, item->key, item->kmods, item->command)), | ||
185 | frameless_WidgetFlag | alignLeft_WidgetFlag | drawKey_WidgetFlag); | ||
186 | updateSize_LabelWidget(label); /* drawKey was set */ | ||
187 | } | ||
188 | } | ||
189 | addChild_Widget(parent, iClob(menu)); | ||
190 | setCommandHandler_Widget(menu, menuHandler_); | ||
191 | addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel"); | ||
192 | return menu; | ||
193 | } | ||
194 | |||
195 | void openMenu_Widget(iWidget *d, iInt2 coord) { | ||
196 | /* Menu closes when commands are emitted, so handle any pending ones beforehand. */ | ||
197 | processEvents_App(); | ||
198 | setFlags_Widget(d, hidden_WidgetFlag, iFalse); | ||
199 | arrange_Widget(d); | ||
200 | d->rect.pos = coord; | ||
201 | /* Ensure the full menu is visible. */ | ||
202 | const iInt2 rootSize = rootSize_Window(get_Window()); | ||
203 | const int bottomExcess = bottom_Rect(bounds_Widget(d)) - rootSize.y; | ||
204 | if (bottomExcess > 0) { | ||
205 | d->rect.pos.y -= bottomExcess; | ||
206 | } | ||
207 | if (top_Rect(d->rect) < 0) { | ||
208 | d->rect.pos.y += -top_Rect(d->rect); | ||
209 | } | ||
210 | if (right_Rect(bounds_Widget(d)) > rootSize.x) { | ||
211 | d->rect.pos.x = coord.x - d->rect.size.x; | ||
212 | } | ||
213 | if (left_Rect(d->rect) < 0) { | ||
214 | d->rect.pos.x = 0; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | void closeMenu_Widget(iWidget *d) { | ||
219 | setFlags_Widget(d, hidden_WidgetFlag, iTrue); | ||
220 | } | ||
221 | |||
222 | iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) { | ||
223 | iLabelWidget *button = new_LabelWidget(label, 0, 0, "menu.open"); | ||
224 | iWidget *menu = makeMenu_Widget(as_Widget(button), items, n); | ||
225 | setId_Widget(menu, "menu"); | ||
226 | return button; | ||
227 | } | ||
228 | |||
229 | /*-----------------------------------------------------------------------------------------------*/ | ||
230 | |||
231 | static iBool isTabPage_Widget_(const iWidget *tabs, const iWidget *page) { | ||
232 | return page->parent == findChild_Widget(tabs, "tabs.pages"); | ||
233 | } | ||
234 | |||
235 | static iBool tabSwitcher_(iWidget *tabs, const char *cmd) { | ||
236 | if (equal_Command(cmd, "tabs.switch")) { | ||
237 | iWidget *target = pointerLabel_Command(cmd, "page"); | ||
238 | if (!target) { | ||
239 | const iString *id = string_Command(cmd, "id"); | ||
240 | target = findChild_Widget(tabs, cstr_String(id)); | ||
241 | } | ||
242 | if (!target) return iFalse; | ||
243 | if (flags_Widget(target) & focusable_WidgetFlag) { | ||
244 | setFocus_Widget(target); | ||
245 | } | ||
246 | if (isTabPage_Widget_(tabs, target)) { | ||
247 | showTabPage_Widget(tabs, target); | ||
248 | return iTrue; | ||
249 | } | ||
250 | else if (hasParent_Widget(target, tabs)) { | ||
251 | /* Some widget on a page. */ | ||
252 | while (!isTabPage_Widget_(tabs, target)) { | ||
253 | target = target->parent; | ||
254 | } | ||
255 | showTabPage_Widget(tabs, target); | ||
256 | return iTrue; | ||
257 | } | ||
258 | } | ||
259 | else if (equal_Command(cmd, "tabs.next") || equal_Command(cmd, "tabs.prev")) { | ||
260 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
261 | int tabIndex = 0; | ||
262 | iConstForEach(ObjectList, i, pages->children) { | ||
263 | const iWidget *child = constAs_Widget(i.object); | ||
264 | if (isVisible_Widget(child)) break; | ||
265 | tabIndex++; | ||
266 | } | ||
267 | tabIndex += (equal_Command(cmd, "tabs.next") ? +1 : -1); | ||
268 | showTabPage_Widget(tabs, child_Widget(pages, iWrap(tabIndex, 0, childCount_Widget(pages)))); | ||
269 | return iTrue; | ||
270 | } | ||
271 | return iFalse; | ||
272 | } | ||
273 | |||
274 | iWidget *makeTabs_Widget(iWidget *parent) { | ||
275 | iWidget *tabs = makeVDiv_Widget(); | ||
276 | iWidget *buttons = addChild_Widget(tabs, iClob(new_Widget())); | ||
277 | setFlags_Widget(buttons, arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag, iTrue); | ||
278 | setId_Widget(buttons, "tabs.buttons"); | ||
279 | iWidget *pages = addChildFlags_Widget( | ||
280 | tabs, iClob(new_Widget()), expand_WidgetFlag | resizeChildren_WidgetFlag); | ||
281 | setId_Widget(pages, "tabs.pages"); | ||
282 | addChild_Widget(parent, iClob(tabs)); | ||
283 | setCommandHandler_Widget(tabs, tabSwitcher_); | ||
284 | return tabs; | ||
285 | } | ||
286 | |||
287 | static void addTabPage_Widget_(iWidget *tabs, enum iWidgetAddPos addPos, iWidget *page, | ||
288 | const char *label, int key, int kmods) { | ||
289 | iWidget * pages = findChild_Widget(tabs, "tabs.pages"); | ||
290 | const iBool isSel = childCount_Widget(pages) == 0; | ||
291 | iWidget * button = addChildPos_Widget( | ||
292 | findChild_Widget(tabs, "tabs.buttons"), | ||
293 | iClob(new_LabelWidget(label, key, kmods, cstrFormat_String("tabs.switch page:%p", page))), | ||
294 | addPos); | ||
295 | setFlags_Widget(button, selected_WidgetFlag, isSel); | ||
296 | addChildPos_Widget(pages, page, addPos); | ||
297 | setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, !isSel); | ||
298 | } | ||
299 | |||
300 | void appendTabPage_Widget(iWidget *tabs, iWidget *page, const char *label, int key, int kmods) { | ||
301 | addTabPage_Widget_(tabs, back_WidgetAddPos, page, label, key, kmods); | ||
302 | } | ||
303 | |||
304 | void prependTabPage_Widget(iWidget *tabs, iWidget *page, const char *label, int key, int kmods) { | ||
305 | addTabPage_Widget_(tabs, front_WidgetAddPos, page, label, key, kmods); | ||
306 | } | ||
307 | |||
308 | iWidget *tabPage_Widget(iWidget *tabs, size_t index) { | ||
309 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
310 | return child_Widget(pages, index); | ||
311 | } | ||
312 | |||
313 | iWidget *removeTabPage_Widget(iWidget *tabs, size_t index) { | ||
314 | iWidget *buttons = findChild_Widget(tabs, "tabs.buttons"); | ||
315 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
316 | iWidget *button = removeChild_Widget(buttons, child_Widget(buttons, index)); | ||
317 | iRelease(button); | ||
318 | iWidget *page = child_Widget(pages, index); | ||
319 | ref_Object(page); | ||
320 | setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, iFalse); | ||
321 | removeChild_Widget(pages, page); | ||
322 | return page; | ||
323 | } | ||
324 | |||
325 | void showTabPage_Widget(iWidget *tabs, const iWidget *page) { | ||
326 | /* Select the corresponding button. */ { | ||
327 | iWidget *buttons = findChild_Widget(tabs, "tabs.buttons"); | ||
328 | iForEach(ObjectList, i, buttons->children) { | ||
329 | iAssert(isInstance_Object(i.object, &Class_LabelWidget)); | ||
330 | iAny *label = i.object; | ||
331 | const iBool isSel = | ||
332 | (pointerLabel_Command(cstr_String(command_LabelWidget(label)), "page") == page); | ||
333 | setFlags_Widget(label, selected_WidgetFlag, isSel); | ||
334 | } | ||
335 | } | ||
336 | /* Show/hide pages. */ { | ||
337 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
338 | iForEach(ObjectList, i, pages->children) { | ||
339 | iWidget *child = as_Widget(i.object); | ||
340 | setFlags_Widget(child, hidden_WidgetFlag | disabled_WidgetFlag, child != page); | ||
341 | } | ||
342 | } | ||
343 | /* Notify. */ | ||
344 | if (!isEmpty_String(id_Widget(page))) { | ||
345 | postCommandf_App("tabs.changed id:%s", cstr_String(id_Widget(page))); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | const iWidget *currentTabPage_Widget(const iWidget *tabs) { | ||
350 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
351 | iConstForEach(ObjectList, i, pages->children) { | ||
352 | if (isVisible_Widget(constAs_Widget(i.object))) { | ||
353 | return constAs_Widget(i.object); | ||
354 | } | ||
355 | } | ||
356 | return NULL; | ||
357 | } | ||
358 | |||
359 | size_t tabCount_Widget(const iWidget *tabs) { | ||
360 | return childCount_Widget(findChild_Widget(tabs, "tabs.buttons")); | ||
361 | } | ||
362 | |||
363 | /*-----------------------------------------------------------------------------------------------*/ | ||
364 | |||
365 | static void acceptFilePath_(iWidget *dlg) { | ||
366 | iInputWidget *input = findChild_Widget(dlg, "input"); | ||
367 | iString *path = makeAbsolute_Path(text_InputWidget(input)); | ||
368 | postCommandf_App("%s path:%s", cstr_String(id_Widget(dlg)), cstr_String(path)); | ||
369 | destroy_Widget(dlg); | ||
370 | delete_String(path); | ||
371 | } | ||
372 | |||
373 | iBool filePathHandler_(iWidget *dlg, const char *cmd) { | ||
374 | iWidget *ptr = as_Widget(pointer_Command(cmd)); | ||
375 | if (equal_Command(cmd, "input.ended")) { | ||
376 | if (hasParent_Widget(ptr, dlg)) { | ||
377 | if (arg_Command(cmd)) { | ||
378 | acceptFilePath_(dlg); | ||
379 | } | ||
380 | else { | ||
381 | destroy_Widget(dlg); | ||
382 | } | ||
383 | return iTrue; | ||
384 | } | ||
385 | return iFalse; | ||
386 | } | ||
387 | else if (ptr && !hasParent_Widget(ptr, dlg)) { | ||
388 | /* Command from outside the dialog, so dismiss the dialog. */ | ||
389 | if (!equal_Command(cmd, "focus.lost")) { | ||
390 | destroy_Widget(dlg); | ||
391 | } | ||
392 | return iFalse; | ||
393 | } | ||
394 | else if (equal_Command(cmd, "filepath.cancel")) { | ||
395 | end_InputWidget(findChild_Widget(dlg, "input"), iFalse); | ||
396 | destroy_Widget(dlg); | ||
397 | return iTrue; | ||
398 | } | ||
399 | else if (equal_Command(cmd, "filepath.accept")) { | ||
400 | acceptFilePath_(dlg); | ||
401 | return iTrue; | ||
402 | } | ||
403 | return iFalse; | ||
404 | } | ||
405 | |||
406 | iWidget *makeSheet_Widget(const char *id) { | ||
407 | iWidget *sheet = new_Widget(); | ||
408 | setId_Widget(sheet, id); | ||
409 | setBackgroundColor_Widget(sheet, gray25_ColorId); | ||
410 | setFlags_Widget(sheet, | ||
411 | keepOnTop_WidgetFlag | arrangeVertical_WidgetFlag | | ||
412 | arrangeHeight_WidgetFlag, | ||
413 | iTrue); | ||
414 | const iInt2 rootSize = rootSize_Window(get_Window()); | ||
415 | setSize_Widget(sheet, init_I2(rootSize.x / 2, 0)); | ||
416 | setFlags_Widget(sheet, fixedHeight_WidgetFlag, iFalse); | ||
417 | return sheet; | ||
418 | } | ||
419 | |||
420 | void centerSheet_Widget(iWidget *sheet) { | ||
421 | arrange_Widget(sheet); | ||
422 | const iInt2 rootSize = rootSize_Window(get_Window()); | ||
423 | sheet->rect.pos.x = rootSize.x / 2 - sheet->rect.size.x / 2; | ||
424 | } | ||
425 | |||
426 | void makeFilePath_Widget(iWidget * parent, | ||
427 | const iString *initialPath, | ||
428 | const char * title, | ||
429 | const char * acceptLabel, | ||
430 | const char * command) { | ||
431 | setFocus_Widget(NULL); | ||
432 | processEvents_App(); | ||
433 | iWidget *dlg = makeSheet_Widget(command); | ||
434 | setCommandHandler_Widget(dlg, filePathHandler_); | ||
435 | addChild_Widget(parent, iClob(dlg)); | ||
436 | addChildFlags_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL)), frameless_WidgetFlag); | ||
437 | iInputWidget *input = addChild_Widget(dlg, iClob(new_InputWidget(0))); | ||
438 | if (initialPath) { | ||
439 | setText_InputWidget(input, collect_String(makeRelative_Path(initialPath))); | ||
440 | } | ||
441 | setId_Widget(as_Widget(input), "input"); | ||
442 | as_Widget(input)->rect.size.x = dlg->rect.size.x; | ||
443 | addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); | ||
444 | iWidget *div = new_Widget(); { | ||
445 | setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | ||
446 | addChild_Widget(div, iClob(new_LabelWidget("Cancel", SDLK_ESCAPE, 0, "filepath.cancel"))); | ||
447 | addChild_Widget(div, iClob(new_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept"))); | ||
448 | } | ||
449 | addChild_Widget(dlg, iClob(div)); | ||
450 | centerSheet_Widget(dlg); | ||
451 | setFocus_Widget(as_Widget(input)); | ||
452 | } | ||
453 | |||
454 | static void acceptValueInput_(iWidget *dlg) { | ||
455 | const iInputWidget *input = findChild_Widget(dlg, "input"); | ||
456 | const iString *val = text_InputWidget(input); | ||
457 | postCommandf_App("%s arg:%d value:%s", | ||
458 | cstr_String(id_Widget(dlg)), | ||
459 | toInt_String(val), | ||
460 | cstr_String(val)); | ||
461 | } | ||
462 | |||
463 | iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | ||
464 | iWidget *ptr = as_Widget(pointer_Command(cmd)); | ||
465 | if (equal_Command(cmd, "input.ended")) { | ||
466 | if (hasParent_Widget(ptr, dlg)) { | ||
467 | if (arg_Command(cmd)) { | ||
468 | acceptValueInput_(dlg); | ||
469 | } | ||
470 | destroy_Widget(dlg); | ||
471 | return iTrue; | ||
472 | } | ||
473 | return iFalse; | ||
474 | } | ||
475 | else if (equal_Command(cmd, "cancel")) { | ||
476 | destroy_Widget(dlg); | ||
477 | return iTrue; | ||
478 | } | ||
479 | else if (equal_Command(cmd, "valueinput.accept")) { | ||
480 | acceptValueInput_(dlg); | ||
481 | destroy_Widget(dlg); | ||
482 | return iTrue; | ||
483 | } | ||
484 | return iFalse; | ||
485 | } | ||
486 | |||
487 | iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, const char *title, | ||
488 | const char *prompt, const char *command) { | ||
489 | setFocus_Widget(NULL); | ||
490 | processEvents_App(); | ||
491 | iWidget *dlg = makeSheet_Widget(command); | ||
492 | setCommandHandler_Widget(dlg, valueInputHandler_); | ||
493 | addChild_Widget(parent, iClob(dlg)); | ||
494 | addChild_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL))); | ||
495 | addChild_Widget(dlg, iClob(new_LabelWidget(prompt, 0, 0, NULL))); | ||
496 | iInputWidget *input = addChild_Widget(dlg, iClob(new_InputWidget(0))); | ||
497 | if (initialValue) { | ||
498 | setText_InputWidget(input, initialValue); | ||
499 | } | ||
500 | setId_Widget(as_Widget(input), "input"); | ||
501 | as_Widget(input)->rect.size.x = dlg->rect.size.x; | ||
502 | addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); | ||
503 | iWidget *div = new_Widget(); { | ||
504 | setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | ||
505 | addChild_Widget(div, iClob(new_LabelWidget("Cancel", SDLK_ESCAPE, 0, "cancel"))); | ||
506 | addChild_Widget(div, iClob(new_LabelWidget(cyan_ColorEscape "OK", SDLK_RETURN, 0, "valueinput.accept"))); | ||
507 | } | ||
508 | addChild_Widget(dlg, iClob(div)); | ||
509 | centerSheet_Widget(dlg); | ||
510 | setFocus_Widget(as_Widget(input)); | ||
511 | return dlg; | ||
512 | } | ||
513 | |||
514 | static iBool messageHandler_(iWidget *msg, const char *cmd) { | ||
515 | /* Any command dismisses the sheet. */ | ||
516 | iUnused(cmd); | ||
517 | destroy_Widget(msg); | ||
518 | return iFalse; | ||
519 | } | ||
520 | |||
521 | void makeMessage_Widget(const char *title, const char *msg) { | ||
522 | iWidget *dlg = makeQuestion_Widget( | ||
523 | title, msg, (const char *[]){ "Continue" }, (const char *[]){ "message.ok" }, 1); | ||
524 | addAction_Widget(dlg, SDLK_ESCAPE, 0, "message.ok"); | ||
525 | addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); | ||
526 | } | ||
527 | |||
528 | iWidget *makeQuestion_Widget(const char *title, | ||
529 | const char *msg, | ||
530 | const char *labels[], | ||
531 | const char *commands[], | ||
532 | size_t count) { | ||
533 | processEvents_App(); | ||
534 | iWidget *dlg = makeSheet_Widget(""); | ||
535 | setCommandHandler_Widget(dlg, messageHandler_); | ||
536 | addChild_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL))); | ||
537 | addChild_Widget(dlg, iClob(new_LabelWidget(msg, 0, 0, NULL))); | ||
538 | addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); | ||
539 | iWidget *div = new_Widget(); { | ||
540 | setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | ||
541 | for (size_t i = 0; i < count; ++i) { | ||
542 | /* The last one is the default option. */ | ||
543 | const int key = (i == count - 1 ? SDLK_RETURN : 0); | ||
544 | addChild_Widget(div, iClob(new_LabelWidget(labels[i], key, 0, commands[i]))); | ||
545 | } | ||
546 | } | ||
547 | addChild_Widget(dlg, iClob(div)); | ||
548 | addChild_Widget(get_Window()->root, iClob(dlg)); | ||
549 | centerSheet_Widget(dlg); | ||
550 | return dlg; | ||
551 | } | ||
552 | |||
553 | void setToggle_Widget(iWidget *d, iBool active) { | ||
554 | setFlags_Widget(d, selected_WidgetFlag, active); | ||
555 | updateText_LabelWidget( | ||
556 | (iLabelWidget *) d, | ||
557 | collectNewFormat_String( | ||
558 | "%s", isSelected_Widget(d) ? "YES" : "NO")); | ||
559 | } | ||
560 | |||
561 | static iBool toggleHandler_(iWidget *d, const char *cmd) { | ||
562 | if (equal_Command(cmd, "toggle") && pointer_Command(cmd) == d) { | ||
563 | setToggle_Widget(d, (flags_Widget(d) & selected_WidgetFlag) == 0); | ||
564 | postCommand_Widget(d, | ||
565 | cstrFormat_String("%s.changed arg:%d", | ||
566 | cstr_String(id_Widget(d)), | ||
567 | isSelected_Widget(d) ? 1 : 0)); | ||
568 | return iTrue; | ||
569 | } | ||
570 | return iFalse; | ||
571 | } | ||
572 | |||
573 | iWidget *makeToggle_Widget(const char *id) { | ||
574 | iWidget *toggle = as_Widget(new_LabelWidget("YES", 0, 0, "toggle")); | ||
575 | setId_Widget(toggle, id); | ||
576 | setCommandHandler_Widget(toggle, toggleHandler_); | ||
577 | return toggle; | ||
578 | } | ||
579 | |||
580 | iWidget *makePreferences_Widget(void) { | ||
581 | iWidget *dlg = makeSheet_Widget("prefs"); | ||
582 | addChild_Widget(dlg, iClob(new_LabelWidget(cyan_ColorEscape "PREFERENCES", 0, 0, NULL))); | ||
583 | iWidget *page = new_Widget(); | ||
584 | addChild_Widget(dlg, iClob(page)); | ||
585 | setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | ||
586 | iWidget *headings = addChildFlags_Widget( | ||
587 | page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); | ||
588 | iWidget *values = addChildFlags_Widget( | ||
589 | page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); | ||
590 | addChild_Widget(headings, iClob(makeHeading_Widget("Retain window size:"))); | ||
591 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.retainwindow"))); | ||
592 | addChild_Widget(headings, iClob(makeHeading_Widget("UI scale factor:"))); | ||
593 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale"); | ||
594 | arrange_Widget(dlg); | ||
595 | // as_Widget(songDir)->rect.size.x = dlg->rect.size.x - headings->rect.size.x; | ||
596 | iWidget *div = new_Widget(); { | ||
597 | setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | ||
598 | addChild_Widget(div, iClob(new_LabelWidget("Dismiss", SDLK_ESCAPE, 0, "prefs.dismiss"))); | ||
599 | } | ||
600 | addChild_Widget(dlg, iClob(div)); | ||
601 | addChild_Widget(get_Window()->root, iClob(dlg)); | ||
602 | centerSheet_Widget(dlg); | ||
603 | return dlg; | ||
604 | } | ||