summaryrefslogtreecommitdiff
path: root/src/ui/mobile.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-09 20:22:33 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-09 20:22:33 +0300
commitb85471a5b84f0837611fb47be35ee713139f702f (patch)
treee2d2ae695279f14c2e559595a3129151e574fbe2 /src/ui/mobile.c
parent55cd303c52a8fe524e15bd618bfcb02b8ff7dcb7 (diff)
Mobile: Working on dialogs
The new panels maker offers a declarative solution for creating consistent UIs.
Diffstat (limited to 'src/ui/mobile.c')
-rw-r--r--src/ui/mobile.c106
1 files changed, 96 insertions, 10 deletions
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 7e359a84..f3e23e06 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36# include "ios.h" 36# include "ios.h"
37#endif 37#endif
38 38
39static iBool useMobileSheetLayout_(void) { 39iBool isUsingPanelLayout_Mobile(void) {
40 return deviceType_App() != desktop_AppDeviceType; 40 return deviceType_App() != desktop_AppDeviceType;
41} 41}
42 42
@@ -381,6 +381,7 @@ static size_t countItems_(const iMenuItem *itemsNullTerminated) {
381void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) { 381void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
382 iWidget * widget = NULL; 382 iWidget * widget = NULL;
383 iLabelWidget *heading = NULL; 383 iLabelWidget *heading = NULL;
384 iWidget * value = NULL;
384 const char * spec = item->label; 385 const char * spec = item->label;
385 const char * id = cstr_Rangecc(range_Command(spec, "id")); 386 const char * id = cstr_Rangecc(range_Command(spec, "id"));
386 const char * label = hasLabel_Command(spec, "text") 387 const char * label = hasLabel_Command(spec, "text")
@@ -396,6 +397,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
396 setFont_LabelWidget(title, uiLabelLargeBold_FontId); 397 setFont_LabelWidget(title, uiLabelLargeBold_FontId);
397 setTextColor_LabelWidget(title, uiHeading_ColorId); 398 setTextColor_LabelWidget(title, uiHeading_ColorId);
398 setAllCaps_LabelWidget(title, iTrue); 399 setAllCaps_LabelWidget(title, iTrue);
400 setId_Widget(as_Widget(title), id);
399 } 401 }
400 else if (equal_Command(spec, "heading")) { 402 else if (equal_Command(spec, "heading")) {
401 addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); 403 addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_()))));
@@ -403,6 +405,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
403 setAllCaps_LabelWidget(heading, iTrue); 405 setAllCaps_LabelWidget(heading, iTrue);
404 setRemoveTrailingColon_LabelWidget(heading, iTrue); 406 setRemoveTrailingColon_LabelWidget(heading, iTrue);
405 addChild_Widget(panel, iClob(heading)); 407 addChild_Widget(panel, iClob(heading));
408 setId_Widget(as_Widget(heading), id);
406 } 409 }
407 else if (equal_Command(spec, "toggle")) { 410 else if (equal_Command(spec, "toggle")) {
408 iLabelWidget *toggle = (iLabelWidget *) makeToggle_Widget(id); 411 iLabelWidget *toggle = (iLabelWidget *) makeToggle_Widget(id);
@@ -412,7 +415,9 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
412 } 415 }
413 else if (equal_Command(spec, "dropdown")) { 416 else if (equal_Command(spec, "dropdown")) {
414 const iMenuItem *dropItems = item->data; 417 const iMenuItem *dropItems = item->data;
415 iLabelWidget *drop = makeMenuButton_LabelWidget("", dropItems, countItems_(dropItems)); 418 iLabelWidget *drop = makeMenuButton_LabelWidget(dropItems[0].label,
419 dropItems, countItems_(dropItems));
420 value = as_Widget(drop);
416 setFont_LabelWidget(drop, labelFont_()); 421 setFont_LabelWidget(drop, labelFont_());
417 setFlags_Widget(as_Widget(drop), 422 setFlags_Widget(as_Widget(drop),
418 alignRight_WidgetFlag | noBackground_WidgetFlag | 423 alignRight_WidgetFlag | noBackground_WidgetFlag |
@@ -465,6 +470,9 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
465 } 470 }
466 else if (equal_Command(spec, "input")) { 471 else if (equal_Command(spec, "input")) {
467 iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen")); 472 iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen"));
473 if (hasLabel_Command(spec, "hint")) {
474 setHint_InputWidget(input, cstr_Lang(cstr_Rangecc(range_Command(spec, "hint"))));
475 }
468 setId_Widget(as_Widget(input), id); 476 setId_Widget(as_Widget(input), id);
469 setUrlContent_InputWidget(input, argLabel_Command(spec, "url")); 477 setUrlContent_InputWidget(input, argLabel_Command(spec, "url"));
470 setSelectAllOnFocus_InputWidget(input, argLabel_Command(spec, "selectall")); 478 setSelectAllOnFocus_InputWidget(input, argLabel_Command(spec, "selectall"));
@@ -491,6 +499,12 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
491 else if (equal_Command(spec, "button")) { 499 else if (equal_Command(spec, "button")) {
492 widget = as_Widget(heading = makePanelButton_(label, item->command)); 500 widget = as_Widget(heading = makePanelButton_(label, item->command));
493 } 501 }
502 else if (equal_Command(spec, "label")) {
503 iLabelWidget *lab = new_LabelWidget(label, NULL);
504 widget = as_Widget(lab);
505 setWrap_LabelWidget(lab, iTrue);
506 setFlags_Widget(widget, frameless_WidgetFlag, iTrue);
507 }
494 else if (equal_Command(spec, "padding")) { 508 else if (equal_Command(spec, "padding")) {
495 widget = makePadding_Widget(lineHeight_Text(labelFont_()) * 1.5f); 509 widget = makePadding_Widget(lineHeight_Text(labelFont_()) * 1.5f);
496 } 510 }
@@ -500,8 +514,14 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
500 if (icon) { 514 if (icon) {
501 setIcon_LabelWidget(heading, icon); 515 setIcon_LabelWidget(heading, icon);
502 } 516 }
517 if (value && as_Widget(heading) != value) {
518 as_Widget(heading)->sizeRef = value; /* heading height matches value widget */
519 }
503 } 520 }
504 if (widget) { 521 if (widget) {
522 setFlags_Widget(widget,
523 collapse_WidgetFlag | hidden_WidgetFlag,
524 argLabel_Command(spec, "collapse") != 0);
505 addChild_Widget(panel, iClob(widget)); 525 addChild_Widget(panel, iClob(widget));
506 } 526 }
507} 527}
@@ -512,11 +532,26 @@ void makePanelItems_Mobile(iWidget *panel, const iMenuItem *itemsNullTerminated)
512 } 532 }
513} 533}
514 534
515iWidget *makePanels_Mobile(const iMenuItem *itemsNullTerminated) { 535static const iMenuItem *findDialogCancelAction_(const iMenuItem *items, size_t n) {
536 if (n <= 1) {
537 return NULL;
538 }
539 for (size_t i = 0; i < n - 1; i++) {
540 if (!iCmpStr(items[i].label, "${cancel}")) {
541 return &items[i];
542 }
543 }
544 return NULL;
545}
546
547iWidget *makePanels_Mobile(const char *id,
548 const iMenuItem *itemsNullTerminated,
549 const iMenuItem *actions, size_t numActions) {
516 /* A multipanel widget has a top panel and one or more detail panels. In a horizontal layout, 550 /* A multipanel widget has a top panel and one or more detail panels. In a horizontal layout,
517 the detail panels slide in from the right and cover the top panel. In a landscape layout, 551 the detail panels slide in from the right and cover the top panel. In a landscape layout,
518 the detail panels are always visible on the side. */ 552 the detail panels are always visible on the side. */
519 iWidget *sheet = new_Widget(); 553 iWidget *sheet = new_Widget();
554 setId_Widget(sheet, id);
520 setBackgroundColor_Widget(sheet, uiBackground_ColorId); 555 setBackgroundColor_Widget(sheet, uiBackground_ColorId);
521 setFlags_Widget(sheet, 556 setFlags_Widget(sheet,
522 resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag | 557 resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag |
@@ -553,18 +588,21 @@ iWidget *makePanels_Mobile(const iMenuItem *itemsNullTerminated) {
553 topPanel->offsetRef = detailStack; 588 topPanel->offsetRef = detailStack;
554 } 589 }
555 /* Navigation bar at the top. */ 590 /* Navigation bar at the top. */
591 iLabelWidget *naviBack;
556 iWidget *navi = new_Widget(); { 592 iWidget *navi = new_Widget(); {
557 setId_Widget(navi, "panel.navi"); 593 setId_Widget(navi, "panel.navi");
558 setBackgroundColor_Widget(navi, uiBackground_ColorId); 594 setBackgroundColor_Widget(navi, uiBackground_ColorId);
559 addChild_Widget(navi, iClob(makePadding_Widget(0))); 595 addChild_Widget(navi, iClob(makePadding_Widget(0)));
560 iLabelWidget *back = addChildFlags_Widget( 596 naviBack = addChildFlags_Widget(
561 navi, 597 navi,
562 iClob(new_LabelWidget(leftAngle_Icon " ${panel.back}", "panel.close")), 598 iClob(newKeyMods_LabelWidget(leftAngle_Icon " ${panel.back}",
599 SDLK_ESCAPE, 0,
600 "panel.close")),
563 noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | 601 noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag |
564 extraPadding_WidgetFlag); 602 extraPadding_WidgetFlag);
565 checkIcon_LabelWidget(back); 603 checkIcon_LabelWidget(naviBack);
566 setId_Widget(as_Widget(back), "panel.back"); 604 setId_Widget(as_Widget(naviBack), "panel.back");
567 setFont_LabelWidget(back, labelFont_()); 605 setFont_LabelWidget(naviBack, labelFont_());
568 addChildFlags_Widget(sheet, iClob(navi), 606 addChildFlags_Widget(sheet, iClob(navi),
569 drawBackgroundToVerticalSafeArea_WidgetFlag | 607 drawBackgroundToVerticalSafeArea_WidgetFlag |
570 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag | 608 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
@@ -593,6 +631,54 @@ iWidget *makePanels_Mobile(const iMenuItem *itemsNullTerminated) {
593 makePanelItem_Mobile(topPanel, item); 631 makePanelItem_Mobile(topPanel, item);
594 } 632 }
595 } 633 }
634 /* Actions. */
635 if (numActions) {
636 /* Some actions go in the navigation bar and some go on the top panel. */
637 const iMenuItem *cancelItem = findDialogCancelAction_(actions, numActions);
638 const iMenuItem *defaultItem = &actions[numActions - 1];
639 iAssert(defaultItem);
640 if (!cancelItem) {
641 updateTextCStr_LabelWidget(naviBack, defaultItem->label);
642 setCommand_LabelWidget(naviBack, collectNewCStr_String(defaultItem->command));
643 setFlags_Widget(as_Widget(naviBack), alignLeft_WidgetFlag, iFalse);
644 setFlags_Widget(as_Widget(naviBack), alignRight_WidgetFlag, iTrue);
645 setIcon_LabelWidget(naviBack, 0);
646 setFont_LabelWidget(naviBack, labelBoldFont_());
647 }
648 else {
649 updateTextCStr_LabelWidget(naviBack, cancelItem->label);
650 setCommand_LabelWidget(naviBack, collectNewCStr_String(cancelItem->command
651 ? cancelItem->command
652 : "cancel"));
653 iLabelWidget *defaultButton = new_LabelWidget(defaultItem->label, defaultItem->command);
654 setFont_LabelWidget(defaultButton, labelBoldFont_());
655 setFlags_Widget(as_Widget(defaultButton),
656 frameless_WidgetFlag | extraPadding_WidgetFlag |
657 noBackground_WidgetFlag,
658 iTrue);
659 addChildFlags_Widget(as_Widget(naviBack), iClob(defaultButton),
660 moveToParentRightEdge_WidgetFlag);
661 updateSize_LabelWidget(defaultButton);
662 }
663 /* All other actions are added as buttons. */
664 iBool needPadding = iTrue;
665 for (size_t i = 0; i < numActions; i++) {
666 const iMenuItem *act = &actions[i];
667 if (act == cancelItem || act == defaultItem) {
668 continue;
669 }
670 if (!iCmpStr(act->label, "---")) {
671 continue;
672 }
673 if (needPadding) {
674 makePanelItem_Mobile(topPanel, &(iMenuItem){ "padding" });
675 needPadding = iFalse;
676 }
677 makePanelItem_Mobile(
678 topPanel,
679 &(iMenuItem){ format_CStr("button text:%s", act->label), 0, 0, act->command });
680 }
681 }
596 /* Finalize the layout. */ 682 /* Finalize the layout. */
597 addChild_Widget(sheet->root->widget, iClob(sheet)); 683 addChild_Widget(sheet->root->widget, iClob(sheet));
598 mainDetailSplitHandler_(mainDetailSplit, "window.resized"); /* make it resize the split */ 684 mainDetailSplitHandler_(mainDetailSplit, "window.resized"); /* make it resize the split */
@@ -1011,7 +1097,7 @@ iWidget *makePanels_Mobile(const iMenuItem *itemsNullTerminated) {
1011#endif 1097#endif
1012 1098
1013void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) { 1099void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) {
1014 if (!useMobileSheetLayout_()) { 1100 if (!isUsingPanelLayout_Mobile()) {
1015 return; 1101 return;
1016 } 1102 }
1017 const iBool isSlidePanel = (flags_Widget(sheet) & horizontalOffset_WidgetFlag) != 0; 1103 const iBool isSlidePanel = (flags_Widget(sheet) & horizontalOffset_WidgetFlag) != 0;
@@ -1032,7 +1118,7 @@ void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) {
1032} 1118}
1033 1119
1034void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) { 1120void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) {
1035 if (!useMobileSheetLayout_()) { 1121 if (!isUsingPanelLayout_Mobile()) {
1036 if (prefs_App()->uiAnimations) { 1122 if (prefs_App()->uiAnimations) {
1037 setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iFalse); 1123 setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iFalse);
1038 if (isIncoming) { 1124 if (isIncoming) {