summaryrefslogtreecommitdiff
path: root/src/ui/mobile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/mobile.c')
-rw-r--r--src/ui/mobile.c547
1 files changed, 516 insertions, 31 deletions
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 0ff3fe85..3cb6e631 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
@@ -57,11 +57,12 @@ static enum iFontId labelBoldFont_(void) {
57 57
58static void updatePanelSheetMetrics_(iWidget *sheet) { 58static 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;
61#if defined (iPlatformMobile)
62 float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f;
62#if defined (iPlatformAppleMobile) 63#if defined (iPlatformAppleMobile)
63 float left, right, top, bottom;
64 safeAreaInsets_iOS(&left, &top, &right, &bottom); 64 safeAreaInsets_iOS(&left, &top, &right, &bottom);
65#endif
65 setPadding_Widget(sheet, left, 0, right, 0); 66 setPadding_Widget(sheet, left, 0, right, 0);
66 navi->rect.pos = init_I2(left, top); 67 navi->rect.pos = init_I2(left, top);
67 iConstForEach(PtrArray, i, findChildren_Widget(sheet, "panel.toppad")) { 68 iConstForEach(PtrArray, i, findChildren_Widget(sheet, "panel.toppad")) {
@@ -87,17 +88,28 @@ static void unselectAllPanelButtons_(iWidget *topPanel) {
87 } 88 }
88} 89}
89 90
91static iWidget *findTitleLabel_(iWidget *panel) {
92 iForEach(ObjectList, i, children_Widget(panel)) {
93 iWidget *child = i.object;
94 if (flags_Widget(child) & collapse_WidgetFlag &&
95 isInstance_Object(child, &Class_LabelWidget)) {
96 return child;
97 }
98 }
99 return NULL;
100}
101
90static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd) { 102static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd) {
91 if (equal_Command(cmd, "window.resized")) { 103 if (equal_Command(cmd, "window.resized")) {
92 const iBool isPortrait = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); 104 const iBool isPortrait = (deviceType_App() == phone_AppDeviceType && isPortrait_App());
93 const iRect safeRoot = safeRect_Root(mainDetailSplit->root); 105 const iRect safeRoot = safeRect_Root(mainDetailSplit->root);
94 setPos_Widget(mainDetailSplit, topLeft_Rect(safeRoot));
95 setFixedSize_Widget(mainDetailSplit, safeRoot.size);
96 iWidget * sheet = parent_Widget(mainDetailSplit); 106 iWidget * sheet = parent_Widget(mainDetailSplit);
97 iWidget * navi = findChild_Widget(sheet, "panel.navi"); 107 iWidget * navi = findChild_Widget(sheet, "panel.navi");
98 iWidget * detailStack = findChild_Widget(mainDetailSplit, "detailstack"); 108 iWidget * detailStack = findChild_Widget(mainDetailSplit, "detailstack");
99 const size_t numPanels = childCount_Widget(detailStack); 109 const size_t numPanels = childCount_Widget(detailStack);
100 const iBool isSideBySide = isSideBySideLayout_() && numPanels > 0; 110 const iBool isSideBySide = isSideBySideLayout_() && numPanels > 0;
111 setPos_Widget(mainDetailSplit, topLeft_Rect(safeRoot));
112 setFixedSize_Widget(mainDetailSplit, safeRoot.size);
101 setFlags_Widget(mainDetailSplit, arrangeHorizontal_WidgetFlag, isSideBySide); 113 setFlags_Widget(mainDetailSplit, arrangeHorizontal_WidgetFlag, isSideBySide);
102 setFlags_Widget(detailStack, expand_WidgetFlag, isSideBySide); 114 setFlags_Widget(detailStack, expand_WidgetFlag, isSideBySide);
103 setFlags_Widget(detailStack, hidden_WidgetFlag, numPanels == 0); 115 setFlags_Widget(detailStack, hidden_WidgetFlag, numPanels == 0);
@@ -107,7 +119,7 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd)
107 iAssert(topPanel); 119 iAssert(topPanel);
108 topPanel->rect.size.x = (deviceType_App() == phone_AppDeviceType ? 120 topPanel->rect.size.x = (deviceType_App() == phone_AppDeviceType ?
109 safeRoot.size.x * 2 / 5 : (safeRoot.size.x / 3)); 121 safeRoot.size.x * 2 / 5 : (safeRoot.size.x / 3));
110 } 122 }
111 if (deviceType_App() == tablet_AppDeviceType) { 123 if (deviceType_App() == tablet_AppDeviceType) {
112 setPadding_Widget(topPanel, pad, 0, pad, pad); 124 setPadding_Widget(topPanel, pad, 0, pad, pad);
113 if (numPanels == 0) { 125 if (numPanels == 0) {
@@ -118,8 +130,15 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd)
118 setFixedSize_Widget(navi, init_I2(sheetWidth, -1)); 130 setFixedSize_Widget(navi, init_I2(sheetWidth, -1));
119 } 131 }
120 } 132 }
133 iWidget *detailTitle = findChild_Widget(navi, "detailtitle"); {
134 setPos_Widget(detailTitle, init_I2(width_Widget(topPanel), 0));
135 setFixedSize_Widget(detailTitle,
136 init_I2(width_Widget(detailStack), height_Widget(navi)));
137 setFlags_Widget(detailTitle, hidden_WidgetFlag, !isSideBySide);
138 }
121 iForEach(ObjectList, i, children_Widget(detailStack)) { 139 iForEach(ObjectList, i, children_Widget(detailStack)) {
122 iWidget *panel = i.object; 140 iWidget *panel = i.object;
141 setFlags_Widget(findTitleLabel_(panel), hidden_WidgetFlag, isSideBySide);
123 setFlags_Widget(panel, leftEdgeDraggable_WidgetFlag, !isSideBySide); 142 setFlags_Widget(panel, leftEdgeDraggable_WidgetFlag, !isSideBySide);
124 if (isSideBySide) { 143 if (isSideBySide) {
125 setVisualOffset_Widget(panel, 0, 0, 0); 144 setVisualOffset_Widget(panel, 0, 0, 0);
@@ -128,9 +147,27 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd)
128 } 147 }
129 arrange_Widget(mainDetailSplit); 148 arrange_Widget(mainDetailSplit);
130 } 149 }
150 else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) {
151 if (focus_Widget() && class_Widget(focus_Widget()) == &Class_InputWidget) {
152 setFocus_Widget(NULL);
153 return iTrue;
154 }
155 }
131 return iFalse; 156 return iFalse;
132} 157}
133 158
159size_t currentPanelIndex_Mobile(const iWidget *panels) {
160 size_t index = 0;
161 iConstForEach(ObjectList, i, children_Widget(findChild_Widget(panels, "detailstack"))) {
162 const iWidget *child = i.object;
163 if (isVisible_Widget(child)) {
164 return index;
165 }
166 index++;
167 }
168 return iInvalidPos;
169}
170
134static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) { 171static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) {
135 const iBool isPortrait = !isSideBySideLayout_(); 172 const iBool isPortrait = !isSideBySideLayout_();
136 if (equal_Command(cmd, "panel.open")) { 173 if (equal_Command(cmd, "panel.open")) {
@@ -147,6 +184,12 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) {
147 setupSheetTransition_Mobile(panel, iTrue); 184 setupSheetTransition_Mobile(panel, iTrue);
148 } 185 }
149 } 186 }
187 iLabelWidget *detailTitle =
188 findChild_Widget(parent_Widget(parent_Widget(topPanel)), "detailtitle");
189// setFlags_Widget(as_Widget(detailTitle), hidden_WidgetFlag, !isSideBySideLayout_());
190 setFont_LabelWidget(detailTitle, uiLabelLargeBold_FontId);
191 setTextColor_LabelWidget(detailTitle, uiHeading_ColorId);
192 setText_LabelWidget(detailTitle, text_LabelWidget((iLabelWidget *) findTitleLabel_(panel)));
150 setFlags_Widget(button, selected_WidgetFlag, iTrue); 193 setFlags_Widget(button, selected_WidgetFlag, iTrue);
151 return iTrue; 194 return iTrue;
152 } 195 }
@@ -171,7 +214,22 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) {
171 } 214 }
172 unselectAllPanelButtons_(topPanel); 215 unselectAllPanelButtons_(topPanel);
173 if (!wasClosed) { 216 if (!wasClosed) {
174 postCommand_App("prefs.dismiss"); 217 /* TODO: Should come up with a more general-purpose approach here. */
218 if (findWidget_App("prefs")) {
219 postCommand_App("prefs.dismiss");
220 }
221 else if (findWidget_App("upload")) {
222 postCommand_App("upload.cancel");
223 }
224 else if (findWidget_App("ident")) {
225 postCommand_Widget(topPanel, "ident.cancel");
226 }
227 else if (findWidget_App("xlt")) {
228 postCommand_Widget(topPanel, "translation.cancel");
229 }
230 else {
231 postCommand_Widget(topPanel, "cancel");
232 }
175 } 233 }
176 return iTrue; 234 return iTrue;
177 } 235 }
@@ -186,6 +244,7 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) {
186 return iFalse; 244 return iFalse;
187} 245}
188 246
247#if 0
189static iBool isTwoColumnPage_(iWidget *d) { 248static iBool isTwoColumnPage_(iWidget *d) {
190 if (cmp_String(id_Widget(d), "dialogbuttons") == 0 || 249 if (cmp_String(id_Widget(d), "dialogbuttons") == 0 ||
191 cmp_String(id_Widget(d), "prefs.tabs") == 0) { 250 cmp_String(id_Widget(d), "prefs.tabs") == 0) {
@@ -273,12 +332,13 @@ static void stripTrailingColon_(iLabelWidget *label) {
273 delete_String(mod); 332 delete_String(mod);
274 } 333 }
275} 334}
335#endif
276 336
277static iLabelWidget *makePanelButton_(const char *text, const char *command) { 337static iLabelWidget *makePanelButton_(const char *text, const char *command) {
278 iLabelWidget *btn = new_LabelWidget(text, command); 338 iLabelWidget *btn = new_LabelWidget(text, command);
279 setFlags_Widget(as_Widget(btn), 339 setFlags_Widget(as_Widget(btn),
280 borderBottom_WidgetFlag | alignLeft_WidgetFlag | 340 borderTop_WidgetFlag | borderBottom_WidgetFlag | alignLeft_WidgetFlag |
281 frameless_WidgetFlag | extraPadding_WidgetFlag, 341 frameless_WidgetFlag | extraPadding_WidgetFlag,
282 iTrue); 342 iTrue);
283 checkIcon_LabelWidget(btn); 343 checkIcon_LabelWidget(btn);
284 setFont_LabelWidget(btn, labelFont_()); 344 setFont_LabelWidget(btn, labelFont_());
@@ -298,11 +358,9 @@ static iWidget *makeValuePadding_(iWidget *value) {
298 setPadding_Widget(pad, 0, 1 * gap_UI, 0, 1 * gap_UI); 358 setPadding_Widget(pad, 0, 1 * gap_UI, 0, 1 * gap_UI);
299 addChild_Widget(pad, iClob(value)); 359 addChild_Widget(pad, iClob(value));
300 setFlags_Widget(pad, 360 setFlags_Widget(pad,
301 borderBottom_WidgetFlag | 361 borderTop_WidgetFlag | borderBottom_WidgetFlag | arrangeVertical_WidgetFlag |
302 arrangeVertical_WidgetFlag | 362 resizeToParentWidth_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
303 resizeToParentWidth_WidgetFlag | 363 arrangeHeight_WidgetFlag,
304 resizeWidthOfChildren_WidgetFlag |
305 arrangeHeight_WidgetFlag,
306 iTrue); 364 iTrue);
307 return pad; 365 return pad;
308} 366}
@@ -311,7 +369,7 @@ static iWidget *makeValuePaddingWithHeading_(iLabelWidget *heading, iWidget *val
311 const iBool isInput = isInstance_Object(value, &Class_InputWidget); 369 const iBool isInput = isInstance_Object(value, &Class_InputWidget);
312 iWidget *div = new_Widget(); 370 iWidget *div = new_Widget();
313 setFlags_Widget(div, 371 setFlags_Widget(div,
314 borderBottom_WidgetFlag | arrangeHeight_WidgetFlag | 372 borderTop_WidgetFlag | borderBottom_WidgetFlag | arrangeHeight_WidgetFlag |
315 resizeWidthOfChildren_WidgetFlag | 373 resizeWidthOfChildren_WidgetFlag |
316 arrangeHorizontal_WidgetFlag, iTrue); 374 arrangeHorizontal_WidgetFlag, iTrue);
317 setBackgroundColor_Widget(div, uiBackgroundSidebar_ColorId); 375 setBackgroundColor_Widget(div, uiBackgroundSidebar_ColorId);
@@ -321,7 +379,7 @@ static iWidget *makeValuePaddingWithHeading_(iLabelWidget *heading, iWidget *val
321 //setFixedSize_Widget(as_Widget(heading), init_I2(-1, height_Widget(value))); 379 //setFixedSize_Widget(as_Widget(heading), init_I2(-1, height_Widget(value)));
322 setFont_LabelWidget(heading, labelFont_()); 380 setFont_LabelWidget(heading, labelFont_());
323 setTextColor_LabelWidget(heading, uiTextStrong_ColorId); 381 setTextColor_LabelWidget(heading, uiTextStrong_ColorId);
324 if (isInput) { 382 if (isInput && ~value->flags & fixedWidth_WidgetFlag) {
325 addChildFlags_Widget(div, iClob(value), expand_WidgetFlag); 383 addChildFlags_Widget(div, iClob(value), expand_WidgetFlag);
326 } 384 }
327 else if (isInstance_Object(value, &Class_LabelWidget) && 385 else if (isInstance_Object(value, &Class_LabelWidget) &&
@@ -337,7 +395,7 @@ static iWidget *makeValuePaddingWithHeading_(iLabelWidget *heading, iWidget *val
337 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); 395 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag);
338 addChild_Widget(div, iClob(value)); 396 addChild_Widget(div, iClob(value));
339 } 397 }
340 printTree_Widget(div); 398// printTree_Widget(div);
341 return div; 399 return div;
342} 400}
343 401
@@ -347,6 +405,7 @@ static iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton,
347 setId_Widget(panel, "panel"); 405 setId_Widget(panel, "panel");
348 setUserData_Object(panelButton, panel); 406 setUserData_Object(panelButton, panel);
349 setBackgroundColor_Widget(panel, uiBackground_ColorId); 407 setBackgroundColor_Widget(panel, uiBackground_ColorId);
408 setDrawBufferEnabled_Widget(panel, iTrue);
350 setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad"); 409 setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad");
351 if (titleText) { 410 if (titleText) {
352 iLabelWidget *title = 411 iLabelWidget *title =
@@ -366,7 +425,396 @@ static iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton,
366 return panel; 425 return panel;
367} 426}
368 427
369void finalizeSheet_Mobile(iWidget *sheet) { 428//void finalizeSheet_Mobile(iWidget *sheet) {
429// arrange_Widget(sheet);
430// postRefresh_App();
431//}
432
433static size_t countItems_(const iMenuItem *itemsNullTerminated) {
434 size_t num = 0;
435 for (; itemsNullTerminated->label; num++, itemsNullTerminated++) {}
436 return num;
437}
438
439static iBool dropdownHeadingHandler_(iWidget *d, const char *cmd) {
440 if (isVisible_Widget(d) &&
441 equal_Command(cmd, "mouse.clicked") && contains_Widget(d, coord_Command(cmd)) &&
442 arg_Command(cmd)) {
443 postCommand_Widget(userData_Object(d),
444 cstr_String(command_LabelWidget(userData_Object(d))));
445 return iTrue;
446 }
447 return iFalse;
448}
449
450static iBool inputHeadingHandler_(iWidget *d, const char *cmd) {
451 if (isVisible_Widget(d) &&
452 equal_Command(cmd, "mouse.clicked") && contains_Widget(d, coord_Command(cmd)) &&
453 arg_Command(cmd)) {
454 setFocus_Widget(userData_Object(d));
455 return iTrue;
456 }
457 return iFalse;
458}
459
460void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
461 iWidget * widget = NULL;
462 iLabelWidget *heading = NULL;
463 iWidget * value = NULL;
464 const char * spec = item->label;
465 const char * id = cstr_Rangecc(range_Command(spec, "id"));
466 const char * label = hasLabel_Command(spec, "text")
467 ? suffixPtr_Command(spec, "text")
468 : format_CStr("${%s}", id);
469 if (hasLabel_Command(spec, "device") && deviceType_App() != argLabel_Command(spec, "device")) {
470 return;
471 }
472 if (equal_Command(spec, "title")) {
473 iLabelWidget *title = addChildFlags_Widget(panel,
474 iClob(new_LabelWidget(label, NULL)),
475 alignLeft_WidgetFlag | frameless_WidgetFlag |
476 collapse_WidgetFlag);
477 setFont_LabelWidget(title, uiLabelLargeBold_FontId);
478 setTextColor_LabelWidget(title, uiHeading_ColorId);
479 setAllCaps_LabelWidget(title, iTrue);
480 setId_Widget(as_Widget(title), id);
481 }
482 else if (equal_Command(spec, "heading")) {
483 addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_()))));
484 heading = makeHeading_Widget(label);
485 setAllCaps_LabelWidget(heading, iTrue);
486 setRemoveTrailingColon_LabelWidget(heading, iTrue);
487 addChild_Widget(panel, iClob(heading));
488 setId_Widget(as_Widget(heading), id);
489 }
490 else if (equal_Command(spec, "toggle")) {
491 iLabelWidget *toggle = (iLabelWidget *) makeToggle_Widget(id);
492 setFont_LabelWidget(toggle, labelFont_());
493 widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label),
494 as_Widget(toggle));
495 }
496 else if (equal_Command(spec, "dropdown")) {
497 const iMenuItem *dropItems = item->data;
498 iLabelWidget *drop = makeMenuButton_LabelWidget(dropItems[0].label,
499 dropItems, countItems_(dropItems));
500 value = as_Widget(drop);
501 setFont_LabelWidget(drop, labelFont_());
502 setFlags_Widget(as_Widget(drop),
503 alignRight_WidgetFlag | noBackground_WidgetFlag |
504 frameless_WidgetFlag, iTrue);
505 setId_Widget(as_Widget(drop), id);
506 widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label), as_Widget(drop));
507 setCommandHandler_Widget(widget, dropdownHeadingHandler_);
508 setUserData_Object(widget, drop);
509 }
510 else if (equal_Command(spec, "radio") || equal_Command(spec, "buttons")) {
511 const iBool isRadio = equal_Command(spec, "radio");
512 addChild_Widget(panel, iClob(makePadding_Widget(lineHeight_Text(labelFont_()))));
513 iLabelWidget *head = makeHeading_Widget(label);
514 setAllCaps_LabelWidget(head, iTrue);
515 setRemoveTrailingColon_LabelWidget(head, iTrue);
516 addChild_Widget(panel, iClob(head));
517 widget = new_Widget();
518 setBackgroundColor_Widget(widget, uiBackgroundSidebar_ColorId);
519 setPadding_Widget(widget, 4 * gap_UI, 2 * gap_UI, 4 * gap_UI, 2 * gap_UI);
520 setFlags_Widget(widget,
521 borderTop_WidgetFlag |
522 borderBottom_WidgetFlag |
523 arrangeHorizontal_WidgetFlag |
524 arrangeHeight_WidgetFlag |
525 resizeToParentWidth_WidgetFlag |
526 resizeWidthOfChildren_WidgetFlag,
527 iTrue);
528 setId_Widget(widget, id);
529 for (const iMenuItem *radioItem = item->data; radioItem->label; radioItem++) {
530 const char * radId = cstr_Rangecc(range_Command(radioItem->label, "id"));
531 int64_t flags = noBackground_WidgetFlag;
532 iLabelWidget *button;
533 if (isRadio) {
534 const char *radLabel =
535 hasLabel_Command(radioItem->label, "label")
536 ? format_CStr("${%s}",
537 cstr_Rangecc(range_Command(radioItem->label, "label")))
538 : suffixPtr_Command(radioItem->label, "text");
539 button = new_LabelWidget(radLabel, radioItem->command);
540 flags |= radio_WidgetFlag;
541 }
542 else {
543 button = (iLabelWidget *) makeToggle_Widget(radId);
544 setTextCStr_LabelWidget(button, format_CStr("${%s}", radId));
545 setFlags_Widget(as_Widget(button), fixedWidth_WidgetFlag, iFalse);
546 updateSize_LabelWidget(button);
547 }
548 setId_Widget(as_Widget(button), radId);
549 setFont_LabelWidget(button, defaultMedium_FontId);
550 addChildFlags_Widget(widget, iClob(button), flags);
551 }
552 }
553 else if (equal_Command(spec, "input")) {
554 iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen"));
555 if (hasLabel_Command(spec, "hint")) {
556 setHint_InputWidget(input, cstr_Lang(cstr_Rangecc(range_Command(spec, "hint"))));
557 }
558 setId_Widget(as_Widget(input), id);
559 setUrlContent_InputWidget(input, argLabel_Command(spec, "url"));
560 setSelectAllOnFocus_InputWidget(input, argLabel_Command(spec, "selectall"));
561 setFont_InputWidget(input, labelFont_());
562 if (argLabel_Command(spec, "noheading")) {
563 widget = makeValuePadding_(as_Widget(input));
564 setFlags_Widget(widget, expand_WidgetFlag, iTrue);
565 }
566 else {
567 setContentPadding_InputWidget(input, 3 * gap_UI, 0);
568 if (hasLabel_Command(spec, "unit")) {
569 iWidget *unit = addChildFlags_Widget(
570 as_Widget(input),
571 iClob(new_LabelWidget(
572 format_CStr("${%s}", cstr_Rangecc(range_Command(spec, "unit"))), NULL)),
573 frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag |
574 resizeToParentHeight_WidgetFlag);
575 setContentPadding_InputWidget(input, -1, width_Widget(unit) - 4 * gap_UI);
576 }
577 widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label),
578 as_Widget(input));
579 setCommandHandler_Widget(widget, inputHeadingHandler_);
580 setUserData_Object(widget, input);
581 }
582 }
583 else if (equal_Command(spec, "button")) {
584 widget = as_Widget(heading = makePanelButton_(label, item->command));
585 setFlags_Widget(widget, selected_WidgetFlag, argLabel_Command(spec, "selected") != 0);
586 }
587 else if (equal_Command(spec, "label")) {
588 iLabelWidget *lab = new_LabelWidget(label, NULL);
589 widget = as_Widget(lab);
590 setId_Widget(widget, id);
591 setWrap_LabelWidget(lab, !argLabel_Command(spec, "nowrap"));
592 setFlags_Widget(widget,
593 fixedHeight_WidgetFlag |
594 (!argLabel_Command(spec, "frame") ? frameless_WidgetFlag : 0),
595 iTrue);
596 }
597 else if (equal_Command(spec, "padding")) {
598 float height = 1.5f;
599 if (hasLabel_Command(spec, "arg")) {
600 height *= argfLabel_Command(spec, "arg");
601 }
602 widget = makePadding_Widget(lineHeight_Text(labelFont_()) * height);
603 }
604 /* Apply common styling to the heading. */
605 if (heading) {
606 setRemoveTrailingColon_LabelWidget(heading, iTrue);
607 const iChar icon = toInt_String(string_Command(item->label, "icon"));
608 if (icon) {
609 setIcon_LabelWidget(heading, icon);
610 }
611 if (value && as_Widget(heading) != value) {
612 as_Widget(heading)->sizeRef = value; /* heading height matches value widget */
613 }
614 }
615 if (widget) {
616 setFlags_Widget(widget,
617 collapse_WidgetFlag | hidden_WidgetFlag,
618 argLabel_Command(spec, "collapse") != 0);
619 addChild_Widget(panel, iClob(widget));
620 }
621}
622
623void makePanelItems_Mobile(iWidget *panel, const iMenuItem *itemsNullTerminated) {
624 for (const iMenuItem *item = itemsNullTerminated; item->label; item++) {
625 makePanelItem_Mobile(panel, item);
626 }
627}
628
629static const iMenuItem *findDialogCancelAction_(const iMenuItem *items, size_t n) {
630 if (n <= 1) {
631 return NULL;
632 }
633 for (size_t i = 0; i < n; i++) {
634 if (!iCmpStr(items[i].label, "${cancel}") || !iCmpStr(items[i].label, "${close}")) {
635 return &items[i];
636 }
637 }
638 return NULL;
639}
640
641iWidget *makePanels_Mobile(const char *id,
642 const iMenuItem *itemsNullTerminated,
643 const iMenuItem *actions, size_t numActions) {
644 return makePanelsParent_Mobile(get_Root()->widget, id, itemsNullTerminated, actions, numActions);
645}
646
647iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
648 const char *id,
649 const iMenuItem *itemsNullTerminated,
650 const iMenuItem *actions, size_t numActions) {
651 iWidget *panels = new_Widget();
652 setId_Widget(panels, id);
653 initPanels_Mobile(panels, parentWidget, itemsNullTerminated, actions, numActions);
654 return panels;
655}
656
657void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
658 const iMenuItem *itemsNullTerminated,
659 const iMenuItem *actions, size_t numActions) {
660 /* A multipanel widget has a top panel and one or more detail panels. In a horizontal layout,
661 the detail panels slide in from the right and cover the top panel. In a landscape layout,
662 the detail panels are always visible on the side. */
663 setBackgroundColor_Widget(panels, uiBackground_ColorId);
664 setFlags_Widget(panels,
665 resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag |
666 frameless_WidgetFlag | focusRoot_WidgetFlag | commandOnClick_WidgetFlag |
667 /*overflowScrollable_WidgetFlag |*/ leftEdgeDraggable_WidgetFlag,
668 iTrue);
669 setFlags_Widget(panels, overflowScrollable_WidgetFlag, iFalse);
670 /* The top-level split between main and detail panels. */
671 iWidget *mainDetailSplit = makeHDiv_Widget(); {
672 setCommandHandler_Widget(mainDetailSplit, mainDetailSplitHandler_);
673 setFlags_Widget(mainDetailSplit, resizeHeightOfChildren_WidgetFlag, iFalse);
674 setId_Widget(mainDetailSplit, "mdsplit");
675 addChild_Widget(panels, iClob(mainDetailSplit));
676 }
677 /* The panel roots. */
678 iWidget *topPanel = new_Widget(); {
679 setId_Widget(topPanel, "panel.top");
680 setDrawBufferEnabled_Widget(topPanel, iTrue);
681 setCommandHandler_Widget(topPanel, topPanelHandler_);
682 setFlags_Widget(topPanel,
683 arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
684 arrangeHeight_WidgetFlag | overflowScrollable_WidgetFlag |
685 commandOnClick_WidgetFlag,
686 iTrue);
687 addChild_Widget(mainDetailSplit, iClob(topPanel));
688 setId_Widget(addChild_Widget(topPanel, iClob(makePadding_Widget(0))), "panel.toppad");
689 }
690 iWidget *detailStack = new_Widget(); {
691 setId_Widget(detailStack, "detailstack");
692 setFlags_Widget(detailStack, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag, iTrue);
693 addChild_Widget(mainDetailSplit, iClob(detailStack));
694 }
695 /* Slide top panel with detail panels. */ {
696 setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue);
697 topPanel->offsetRef = detailStack;
698 }
699 /* Navigation bar at the top. */
700 iLabelWidget *naviBack;
701 iWidget *navi = new_Widget(); {
702 setId_Widget(navi, "panel.navi");
703 setBackgroundColor_Widget(navi, uiBackground_ColorId);
704 setId_Widget(addChildFlags_Widget(navi,
705 iClob(new_LabelWidget("", NULL)),
706 alignLeft_WidgetFlag | fixedPosition_WidgetFlag |
707 fixedSize_WidgetFlag | hidden_WidgetFlag |
708 frameless_WidgetFlag),
709 "detailtitle");
710 naviBack = addChildFlags_Widget(
711 navi,
712 iClob(newKeyMods_LabelWidget(
713 leftAngle_Icon " ${panel.back}", SDLK_ESCAPE, 0, "panel.close")),
714 noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag |
715 extraPadding_WidgetFlag);
716 checkIcon_LabelWidget(naviBack);
717 setId_Widget(as_Widget(naviBack), "panel.back");
718 setFont_LabelWidget(naviBack, labelFont_());
719 addChildFlags_Widget(panels, iClob(navi),
720 drawBackgroundToVerticalSafeArea_WidgetFlag |
721 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
722 resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag);
723 }
724 iBool haveDetailPanels = iFalse;
725 /* Create panel contents based on provided items. */
726 for (size_t i = 0; itemsNullTerminated[i].label; i++) {
727 const iMenuItem *item = &itemsNullTerminated[i];
728 if (equal_Command(item->label, "panel")) {
729 haveDetailPanels = iTrue;
730 const char *id = cstr_Rangecc(range_Command(item->label, "id"));
731 const iString *label = hasLabel_Command(item->label, "text")
732 ? collect_String(suffix_Command(item->label, "text"))
733 : collectNewFormat_String("${%s}", id);
734 iLabelWidget * button =
735 addChildFlags_Widget(topPanel,
736 iClob(makePanelButton_(cstr_String(label), "panel.open")),
737 chevron_WidgetFlag | borderTop_WidgetFlag);
738 const iChar icon = toInt_String(string_Command(item->label, "icon"));
739 if (icon) {
740 setIcon_LabelWidget(button, icon);
741 }
742 iWidget *panel = addChildPanel_(detailStack, button, NULL);
743 makePanelItems_Mobile(panel, item->data);
744 }
745 else {
746 makePanelItem_Mobile(topPanel, item);
747 }
748 }
749 /* Actions. */
750 if (numActions) {
751 /* Some actions go in the navigation bar and some go on the top panel. */
752 const iMenuItem *cancelItem = findDialogCancelAction_(actions, numActions);
753 const iMenuItem *defaultItem = &actions[numActions - 1];
754 iAssert(defaultItem);
755 if (defaultItem && !cancelItem) {
756 updateTextCStr_LabelWidget(naviBack, defaultItem->label);
757 setCommand_LabelWidget(naviBack, collectNewCStr_String(defaultItem->command));
758 setFlags_Widget(as_Widget(naviBack), alignLeft_WidgetFlag, iFalse);
759 setFlags_Widget(as_Widget(naviBack), alignRight_WidgetFlag, iTrue);
760 setIcon_LabelWidget(naviBack, 0);
761 setFont_LabelWidget(naviBack, labelBoldFont_());
762 }
763 else if (defaultItem && defaultItem != cancelItem) {
764 if (!haveDetailPanels) {
765 updateTextCStr_LabelWidget(naviBack, cancelItem->label);
766 setCommand_LabelWidget(naviBack, collectNewCStr_String(cancelItem->command
767 ? cancelItem->command
768 : "cancel"));
769 }
770 iLabelWidget *defaultButton = new_LabelWidget(defaultItem->label, defaultItem->command);
771 setFont_LabelWidget(defaultButton, labelBoldFont_());
772 setFlags_Widget(as_Widget(defaultButton),
773 frameless_WidgetFlag | extraPadding_WidgetFlag |
774 noBackground_WidgetFlag,
775 iTrue);
776 addChildFlags_Widget(as_Widget(naviBack), iClob(defaultButton),
777 moveToParentRightEdge_WidgetFlag);
778 updateSize_LabelWidget(defaultButton);
779 }
780 /* All other actions are added as buttons. */
781 iBool needPadding = iTrue;
782 for (size_t i = 0; i < numActions; i++) {
783 const iMenuItem *act = &actions[i];
784 if (act == cancelItem || act == defaultItem) {
785 continue;
786 }
787 const char *label = act->label;
788 if (*label == '*' || *label == '&') {
789 continue; /* Special value selection items for a Question dialog. */
790 }
791 if (!iCmpStr(label, "---")) {
792 continue; /* Separator. */
793 }
794 if (needPadding) {
795 makePanelItem_Mobile(topPanel, &(iMenuItem){ "padding" });
796 needPadding = iFalse;
797 }
798 makePanelItem_Mobile(
799 topPanel,
800 &(iMenuItem){ format_CStr("button text:" uiTextAction_ColorEscape "%s", act->label),
801 0,
802 0,
803 act->command });
804 }
805 }
806 /* Finalize the layout. */
807 if (parentWidget) {
808 addChild_Widget(parentWidget, iClob(panels));
809 }
810 mainDetailSplitHandler_(mainDetailSplit, "window.resized"); /* make it resize the split */
811 updatePanelSheetMetrics_(panels);
812 arrange_Widget(panels);
813 postCommand_App("widget.overflow"); /* with the correct dimensions */
814// printTree_Widget(panels);
815}
816
817#if 0
370 /* The sheet contents are completely rearranged and restyled on a phone. 818 /* The sheet contents are completely rearranged and restyled on a phone.
371 We'll set up a linear fullscreen arrangement of the widgets. Sheets are already 819 We'll set up a linear fullscreen arrangement of the widgets. Sheets are already
372 scrollable so they can be taller than the display. In hindsight, it may have been 820 scrollable so they can be taller than the display. In hindsight, it may have been
@@ -397,7 +845,7 @@ void finalizeSheet_Mobile(iWidget *sheet) {
397 │ │ └┤ ││ │ │└┤ ││ 845 │ │ └┤ ││ │ │└┤ ││
398 │ │ └───────────────────┘│ │ │ └──────┘ 846 │ │ └───────────────────┘│ │ │ └──────┘
399 └─────────┴───────────────────────┘ └─────────┴ ─ ─ ─ ─ ┘ 847 └─────────┴───────────────────────┘ └─────────┴ ─ ─ ─ ─ ┘
400 offscreen 848 underneath
401 */ 849 */
402 /* Modify the top sheet to act as a fullscreen background. */ 850 /* Modify the top sheet to act as a fullscreen background. */
403 setPadding1_Widget(sheet, 0); 851 setPadding1_Widget(sheet, 0);
@@ -772,9 +1220,10 @@ void finalizeSheet_Mobile(iWidget *sheet) {
772 } 1220 }
773 postRefresh_App(); 1221 postRefresh_App();
774} 1222}
1223#endif
775 1224
776void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) { 1225void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) {
777 if (!useMobileSheetLayout_()) { 1226 if (!isUsingPanelLayout_Mobile()) {
778 return; 1227 return;
779 } 1228 }
780 const iBool isSlidePanel = (flags_Widget(sheet) & horizontalOffset_WidgetFlag) != 0; 1229 const iBool isSlidePanel = (flags_Widget(sheet) & horizontalOffset_WidgetFlag) != 0;
@@ -794,8 +1243,10 @@ void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) {
794 } 1243 }
795} 1244}
796 1245
797void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) { 1246void setupSheetTransition_Mobile(iWidget *sheet, int flags) {
798 if (!useMobileSheetLayout_()) { 1247 const iBool isIncoming = (flags & incoming_TransitionFlag) != 0;
1248 const int dir = flags & dirMask_TransitionFlag;
1249 if (!isUsingPanelLayout_Mobile()) {
799 if (prefs_App()->uiAnimations) { 1250 if (prefs_App()->uiAnimations) {
800 setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iFalse); 1251 setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iFalse);
801 if (isIncoming) { 1252 if (isIncoming) {
@@ -808,17 +1259,51 @@ void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) {
808 } 1259 }
809 return; 1260 return;
810 } 1261 }
811 if(isSideBySideLayout_()) { 1262 if (isSideBySideLayout_()) {
1263 /* TODO: Landscape transitions? */
812 return; 1264 return;
813 } 1265 }
814 setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iTrue); 1266 setFlags_Widget(sheet,
1267 horizontalOffset_WidgetFlag,
1268 dir == right_TransitionDir || dir == left_TransitionDir);
815 if (isIncoming) { 1269 if (isIncoming) {
816 setVisualOffset_Widget(sheet, size_Root(sheet->root).x, 0, 0); 1270 switch (dir) {
1271 case right_TransitionDir:
1272 setVisualOffset_Widget(sheet, size_Root(sheet->root).x, 0, 0);
1273 break;
1274 case left_TransitionDir:
1275 setVisualOffset_Widget(sheet, -size_Root(sheet->root).x, 0, 0);
1276 break;
1277 case top_TransitionDir:
1278 setVisualOffset_Widget(
1279 sheet, -bottom_Rect(boundsWithoutVisualOffset_Widget(sheet)), 0, 0);
1280 break;
1281 case bottom_TransitionDir:
1282 setVisualOffset_Widget(sheet, height_Widget(sheet), 0, 0);
1283 break;
1284 }
817 setVisualOffset_Widget(sheet, 0, 200, easeOut_AnimFlag); 1285 setVisualOffset_Widget(sheet, 0, 200, easeOut_AnimFlag);
818 } 1286 }
819 else { 1287 else {
820 const iBool wasDragged = iAbs(value_Anim(&sheet->visualOffset)) > 0; 1288 switch (dir) {
821 setVisualOffset_Widget(sheet, size_Root(sheet->root).x, wasDragged ? 100 : 200, 1289 case right_TransitionDir: {
822 wasDragged ? 0 : easeIn_AnimFlag); 1290 const iBool wasDragged = iAbs(value_Anim(&sheet->visualOffset)) > 0;
1291 setVisualOffset_Widget(sheet, size_Root(sheet->root).x, wasDragged ? 100 : 200,
1292 wasDragged ? 0 : easeIn_AnimFlag);
1293 break;
1294 }
1295 case left_TransitionDir:
1296 setVisualOffset_Widget(sheet, -size_Root(sheet->root).x, 200, easeIn_AnimFlag);
1297 break;
1298 case top_TransitionDir:
1299 setVisualOffset_Widget(sheet,
1300 -bottom_Rect(boundsWithoutVisualOffset_Widget(sheet)),
1301 200,
1302 easeIn_AnimFlag);
1303 break;
1304 case bottom_TransitionDir:
1305 setVisualOffset_Widget(sheet, height_Widget(sheet), 200, easeIn_AnimFlag);
1306 break;
1307 }
823 } 1308 }
824} 1309}