summaryrefslogtreecommitdiff
path: root/src/ui/util.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-21 15:06:52 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-21 15:07:38 +0300
commitd773b499e595a43b9b1ae449262dcf13cabf2d02 (patch)
treeb1baeb12025a04f8316636b5d0ab18e30ceedb2c /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.c604
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
15iBool 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
20const 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
27int 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
39void 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
47enum 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
95void cancel_Click(iClick *d) {
96 if (d->isActive) {
97 d->isActive = iFalse;
98 setMouseGrab_Widget(NULL);
99 }
100}
101
102iBool isMoved_Click(const iClick *d) {
103 return dist_I2(d->startPos, d->pos) > 2;
104}
105
106iInt2 pos_Click(const iClick *d) {
107 return d->pos;
108}
109
110iRect rect_Click(const iClick *d) {
111 return initCorners_Rect(min_I2(d->startPos, d->pos), max_I2(d->startPos, d->pos));
112}
113
114iInt2 delta_Click(const iClick *d) {
115 return sub_I2(d->pos, d->startPos);
116}
117
118/*-----------------------------------------------------------------------------------------------*/
119
120iWidget *makePadding_Widget(int size) {
121 iWidget *pad = new_Widget();
122 setSize_Widget(pad, init1_I2(size));
123 return pad;
124}
125
126iLabelWidget *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
132iWidget *makeVDiv_Widget(void) {
133 iWidget *div = new_Widget();
134 setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeVertical_WidgetFlag, iTrue);
135 return div;
136}
137
138iWidget *makeHDiv_Widget(void) {
139 iWidget *div = new_Widget();
140 setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue);
141 return div;
142}
143
144iWidget *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
153static 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
166iWidget *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
195void 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
218void closeMenu_Widget(iWidget *d) {
219 setFlags_Widget(d, hidden_WidgetFlag, iTrue);
220}
221
222iLabelWidget *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
231static iBool isTabPage_Widget_(const iWidget *tabs, const iWidget *page) {
232 return page->parent == findChild_Widget(tabs, "tabs.pages");
233}
234
235static 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
274iWidget *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
287static 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
300void 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
304void 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
308iWidget *tabPage_Widget(iWidget *tabs, size_t index) {
309 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
310 return child_Widget(pages, index);
311}
312
313iWidget *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
325void 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
349const 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
359size_t tabCount_Widget(const iWidget *tabs) {
360 return childCount_Widget(findChild_Widget(tabs, "tabs.buttons"));
361}
362
363/*-----------------------------------------------------------------------------------------------*/
364
365static 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
373iBool 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
406iWidget *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
420void 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
426void 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
454static 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
463iBool 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
487iWidget *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
514static 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
521void 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
528iWidget *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
553void 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
561static 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
573iWidget *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
580iWidget *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}