diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-07 22:10:29 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-07 22:10:29 +0300 |
commit | 4ce9a07bbd4a4a82ff0b310d9c9e0768febe8d61 (patch) | |
tree | ca8c41fa1d792fdc764c787cb343ddbb6cfbef5a /src/ui/mobile.c | |
parent | c10fc7a058c05edcedeb23bdcc9faae635f1780d (diff) |
Mobile: Redoing Preferences
Contents of the Preferences split panel view are created based on arrays of MenuItems. This removes the confusing indirection of trying to modify the desktop widget tree to fit mobile.
Diffstat (limited to 'src/ui/mobile.c')
-rw-r--r-- | src/ui/mobile.c | 205 |
1 files changed, 202 insertions, 3 deletions
diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 168a92b8..4ccbb0cb 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c | |||
@@ -57,7 +57,6 @@ static enum iFontId labelBoldFont_(void) { | |||
57 | 57 | ||
58 | static void updatePanelSheetMetrics_(iWidget *sheet) { | 58 | static void updatePanelSheetMetrics_(iWidget *sheet) { |
59 | iWidget *navi = findChild_Widget(sheet, "panel.navi"); | 59 | iWidget *navi = findChild_Widget(sheet, "panel.navi"); |
60 | // iWidget *naviPad = child_Widget(navi, 0); | ||
61 | int naviHeight = lineHeight_Text(labelFont_()) + 4 * gap_UI; | 60 | int naviHeight = lineHeight_Text(labelFont_()) + 4 * gap_UI; |
62 | #if defined (iPlatformMobile) | 61 | #if defined (iPlatformMobile) |
63 | float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f; | 62 | float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f; |
@@ -339,7 +338,7 @@ static iWidget *makeValuePaddingWithHeading_(iLabelWidget *heading, iWidget *val | |||
339 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); | 338 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); |
340 | addChild_Widget(div, iClob(value)); | 339 | addChild_Widget(div, iClob(value)); |
341 | } | 340 | } |
342 | printTree_Widget(div); | 341 | // printTree_Widget(div); |
343 | return div; | 342 | return div; |
344 | } | 343 | } |
345 | 344 | ||
@@ -369,6 +368,205 @@ static iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton, | |||
369 | } | 368 | } |
370 | 369 | ||
371 | void finalizeSheet_Mobile(iWidget *sheet) { | 370 | void finalizeSheet_Mobile(iWidget *sheet) { |
371 | arrange_Widget(sheet); | ||
372 | // postRefresh_App(); | ||
373 | } | ||
374 | |||
375 | static size_t countItems_(const iMenuItem *itemsNullTerminated) { | ||
376 | size_t num = 0; | ||
377 | for (; itemsNullTerminated->label; num++, itemsNullTerminated++) {} | ||
378 | return num; | ||
379 | } | ||
380 | |||
381 | void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) { | ||
382 | const char * spec = item->label; | ||
383 | const char * id = cstr_Rangecc(range_Command(spec, "id")); | ||
384 | const char * label = format_CStr("${%s}", id); | ||
385 | iWidget * widget = NULL; | ||
386 | iLabelWidget *heading = NULL; | ||
387 | if (hasLabel_Command(spec, "device") && deviceType_App() != argLabel_Command(spec, "device")) { | ||
388 | return; | ||
389 | } | ||
390 | if (equal_Command(spec, "title")) { | ||
391 | iLabelWidget *title = addChildFlags_Widget(panel, | ||
392 | iClob(new_LabelWidget(label, NULL)), | ||
393 | alignLeft_WidgetFlag | frameless_WidgetFlag); | ||
394 | setFont_LabelWidget(title, uiLabelLargeBold_FontId); | ||
395 | setTextColor_LabelWidget(title, uiHeading_ColorId); | ||
396 | setAllCaps_LabelWidget(title, iTrue); | ||
397 | } | ||
398 | else if (equal_Command(spec, "heading")) { | ||
399 | addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | ||
400 | heading = makeHeading_Widget(label); | ||
401 | setAllCaps_LabelWidget(heading, iTrue); | ||
402 | setRemoveTrailingColon_LabelWidget(heading, iTrue); | ||
403 | addChild_Widget(panel, iClob(heading)); | ||
404 | } | ||
405 | else if (equal_Command(spec, "toggle")) { | ||
406 | iLabelWidget *toggle = (iLabelWidget *) makeToggle_Widget(id); | ||
407 | setFont_LabelWidget(toggle, labelFont_()); | ||
408 | widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label), | ||
409 | as_Widget(toggle)); | ||
410 | } | ||
411 | else if (equal_Command(spec, "dropdown")) { | ||
412 | const iMenuItem *dropItems = item->data; | ||
413 | iLabelWidget *drop = makeMenuButton_LabelWidget("", dropItems, countItems_(dropItems)); | ||
414 | setFont_LabelWidget(drop, labelFont_()); | ||
415 | setFlags_Widget(as_Widget(drop), | ||
416 | alignRight_WidgetFlag | noBackground_WidgetFlag | | ||
417 | frameless_WidgetFlag, iTrue); | ||
418 | setId_Widget(as_Widget(drop), id); | ||
419 | widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label), as_Widget(drop)); | ||
420 | } | ||
421 | else if (equal_Command(spec, "radio")) { | ||
422 | addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | ||
423 | iLabelWidget *head = makeHeading_Widget(label); | ||
424 | setAllCaps_LabelWidget(head, iTrue); | ||
425 | setRemoveTrailingColon_LabelWidget(head, iTrue); | ||
426 | addChild_Widget(panel, iClob(head)); | ||
427 | widget = new_Widget(); | ||
428 | setBackgroundColor_Widget(widget, uiBackgroundSidebar_ColorId); | ||
429 | setPadding_Widget(widget, 4 * gap_UI, 2 * gap_UI, 4 * gap_UI, 2 * gap_UI); | ||
430 | // setFlags_Widget(widget, arrangeWidth_WidgetFlag, iFalse); | ||
431 | setFlags_Widget(widget, | ||
432 | borderBottom_WidgetFlag | | ||
433 | arrangeHorizontal_WidgetFlag | | ||
434 | arrangeHeight_WidgetFlag | | ||
435 | resizeToParentWidth_WidgetFlag | | ||
436 | resizeWidthOfChildren_WidgetFlag, | ||
437 | iTrue); | ||
438 | setId_Widget(widget, id); | ||
439 | for (const iMenuItem *radioItem = item->data; radioItem->label; radioItem++) { | ||
440 | const char * radId = cstr_Rangecc(range_Command(radioItem->label, "id")); | ||
441 | const char * radLabel = hasLabel_Command(radioItem->label, "label") | ||
442 | ? format_CStr("${%s}", cstr_Rangecc(range_Command(radioItem->label, "label"))) | ||
443 | : suffixPtr_Command(radioItem->label, "text"); | ||
444 | iLabelWidget *radButton = new_LabelWidget(radLabel, radioItem->command); | ||
445 | setId_Widget(as_Widget(radButton), radId); | ||
446 | setFont_LabelWidget(radButton, defaultMedium_FontId); | ||
447 | addChildFlags_Widget(widget, iClob(radButton), radio_WidgetFlag | noBackground_WidgetFlag); | ||
448 | } | ||
449 | } | ||
450 | else if (equal_Command(spec, "input")) { | ||
451 | iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen")); | ||
452 | setId_Widget(as_Widget(input), id); | ||
453 | setFont_InputWidget(input, labelFont_()); | ||
454 | setContentPadding_InputWidget(input, 3 * gap_UI, 0); | ||
455 | setUrlContent_InputWidget(input, argLabel_Command(spec, "url")); | ||
456 | widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label), | ||
457 | as_Widget(input)); | ||
458 | } | ||
459 | else if (equal_Command(spec, "padding")) { | ||
460 | widget = makePadding_Widget(lineHeight_Text(labelFont_()) * 1.5f); | ||
461 | } | ||
462 | if (heading) { | ||
463 | setRemoveTrailingColon_LabelWidget(heading, iTrue); | ||
464 | const iChar icon = toInt_String(string_Command(item->label, "icon")); | ||
465 | if (icon) { | ||
466 | setIcon_LabelWidget(heading, icon); | ||
467 | } | ||
468 | } | ||
469 | if (widget) { | ||
470 | addChild_Widget(panel, iClob(widget)); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | void makePanelItems_Mobile(iWidget *panel, const iMenuItem *itemsNullTerminated) { | ||
475 | for (const iMenuItem *item = itemsNullTerminated; item->label; item++) { | ||
476 | makePanelItem_Mobile(panel, item); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | iWidget *makeSplitMultiPanel_Mobile(const iMenuItem *itemsNullTerminated) { | ||
481 | /* A multipanel widget has a top panel and one or more detail panels. In a horizontal layout, | ||
482 | the detail panels slide in from the right and cover the top panel. In a landscape layout, | ||
483 | the detail panels are always visible on the side. */ | ||
484 | iWidget *sheet = new_Widget(); | ||
485 | setBackgroundColor_Widget(sheet, uiBackground_ColorId); | ||
486 | setFlags_Widget(sheet, | ||
487 | resizeToParentWidth_WidgetFlag | | ||
488 | resizeToParentHeight_WidgetFlag | | ||
489 | frameless_WidgetFlag | focusRoot_WidgetFlag | commandOnClick_WidgetFlag | | ||
490 | overflowScrollable_WidgetFlag | leftEdgeDraggable_WidgetFlag, | ||
491 | iTrue); | ||
492 | /* The top-level split between main and detail panels. */ | ||
493 | iWidget *mainDetailSplit = makeHDiv_Widget(); { | ||
494 | setCommandHandler_Widget(mainDetailSplit, mainDetailSplitHandler_); | ||
495 | setFlags_Widget(mainDetailSplit, resizeHeightOfChildren_WidgetFlag, iFalse); | ||
496 | setId_Widget(mainDetailSplit, "mdsplit"); | ||
497 | addChild_Widget(sheet, iClob(mainDetailSplit)); | ||
498 | } | ||
499 | /* The panel roots. */ | ||
500 | iWidget *topPanel = new_Widget(); { | ||
501 | setId_Widget(topPanel, "panel.top"); | ||
502 | setCommandHandler_Widget(topPanel, topPanelHandler_); | ||
503 | setFlags_Widget(topPanel, | ||
504 | arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag | | ||
505 | arrangeHeight_WidgetFlag | overflowScrollable_WidgetFlag | | ||
506 | commandOnClick_WidgetFlag, | ||
507 | iTrue); | ||
508 | addChild_Widget(mainDetailSplit, iClob(topPanel)); | ||
509 | setId_Widget(addChild_Widget(topPanel, iClob(makePadding_Widget(0))), "panel.toppad"); | ||
510 | } | ||
511 | iWidget *detailStack = new_Widget(); { | ||
512 | setId_Widget(detailStack, "detailstack"); | ||
513 | setFlags_Widget(detailStack, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag, iTrue); | ||
514 | addChild_Widget(mainDetailSplit, iClob(detailStack)); | ||
515 | } | ||
516 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | ||
517 | /* Slide top panel with detail panels. */ { | ||
518 | setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); | ||
519 | topPanel->offsetRef = detailStack; | ||
520 | } | ||
521 | /* Navigation bar at the top. */ | ||
522 | iWidget *navi = new_Widget(); { | ||
523 | setId_Widget(navi, "panel.navi"); | ||
524 | setBackgroundColor_Widget(navi, uiBackground_ColorId); | ||
525 | addChild_Widget(navi, iClob(makePadding_Widget(0))); | ||
526 | iLabelWidget *back = addChildFlags_Widget( | ||
527 | navi, | ||
528 | iClob(new_LabelWidget(leftAngle_Icon " ${panel.back}", "panel.close")), | ||
529 | noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | | ||
530 | extraPadding_WidgetFlag); | ||
531 | checkIcon_LabelWidget(back); | ||
532 | setId_Widget(as_Widget(back), "panel.back"); | ||
533 | setFont_LabelWidget(back, labelFont_()); | ||
534 | addChildFlags_Widget(sheet, iClob(navi), | ||
535 | drawBackgroundToVerticalSafeArea_WidgetFlag | | ||
536 | arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag | | ||
537 | resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag); | ||
538 | } | ||
539 | /* Create panel contents based on provided items. */ | ||
540 | for (size_t i = 0; itemsNullTerminated[i].label; i++) { | ||
541 | const iMenuItem *item = &itemsNullTerminated[i]; | ||
542 | if (equal_Command(item->label, "panel")) { | ||
543 | const char *id = cstr_Rangecc(range_Command(item->label, "id")); | ||
544 | const iString *label = collectNewFormat_String("${%s}", id); | ||
545 | iLabelWidget *button = | ||
546 | addChildFlags_Widget(topPanel, | ||
547 | iClob(makePanelButton_(cstr_String(label), "panel.open")), | ||
548 | chevron_WidgetFlag | borderTop_WidgetFlag); | ||
549 | const iChar icon = toInt_String(string_Command(item->label, "icon")); | ||
550 | if (icon) { | ||
551 | setIcon_LabelWidget(button, icon); | ||
552 | } | ||
553 | iWidget *panel = addChildPanel_(detailStack, button, NULL); | ||
554 | makePanelItems_Mobile(panel, item->data); | ||
555 | } | ||
556 | else { | ||
557 | makePanelItem_Mobile(topPanel, item); | ||
558 | } | ||
559 | } | ||
560 | /* Finalize the layout. */ | ||
561 | addChild_Widget(sheet->root->widget, iClob(sheet)); | ||
562 | mainDetailSplitHandler_(mainDetailSplit, "window.resized"); /* make it resize the split */ | ||
563 | updatePanelSheetMetrics_(sheet); | ||
564 | arrange_Widget(sheet); | ||
565 | postCommand_App("widget.overflow"); /* with the correct dimensions */ | ||
566 | return sheet; | ||
567 | } | ||
568 | |||
569 | #if 0 | ||
372 | /* The sheet contents are completely rearranged and restyled on a phone. | 570 | /* The sheet contents are completely rearranged and restyled on a phone. |
373 | We'll set up a linear fullscreen arrangement of the widgets. Sheets are already | 571 | We'll set up a linear fullscreen arrangement of the widgets. Sheets are already |
374 | scrollable so they can be taller than the display. In hindsight, it may have been | 572 | scrollable so they can be taller than the display. In hindsight, it may have been |
@@ -399,7 +597,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
399 | │ │ └┤ ││ │ │└┤ ││ | 597 | │ │ └┤ ││ │ │└┤ ││ |
400 | │ │ └───────────────────┘│ │ │ └──────┘ | 598 | │ │ └───────────────────┘│ │ │ └──────┘ |
401 | └─────────┴───────────────────────┘ └─────────┴ ─ ─ ─ ─ ┘ | 599 | └─────────┴───────────────────────┘ └─────────┴ ─ ─ ─ ─ ┘ |
402 | offscreen | 600 | underneath |
403 | */ | 601 | */ |
404 | /* Modify the top sheet to act as a fullscreen background. */ | 602 | /* Modify the top sheet to act as a fullscreen background. */ |
405 | setPadding1_Widget(sheet, 0); | 603 | setPadding1_Widget(sheet, 0); |
@@ -774,6 +972,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
774 | } | 972 | } |
775 | postRefresh_App(); | 973 | postRefresh_App(); |
776 | } | 974 | } |
975 | #endif | ||
777 | 976 | ||
778 | void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) { | 977 | void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) { |
779 | if (!useMobileSheetLayout_()) { | 978 | if (!useMobileSheetLayout_()) { |