summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-21 15:13:30 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-21 15:13:30 +0200
commitf58a551096aa82986d1270c52dd385110ee8c506 (patch)
tree9fe7d451c44615a039a55d0053d3a3a7b4710887 /src/ui
parentf5f3383efa8fa6533936c042aedcc1cddc3985d6 (diff)
Animated widget offsets; phone sidebars
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/inputwidget.c1
-rw-r--r--src/ui/sidebarwidget.c76
-rw-r--r--src/ui/sidebarwidget.h11
-rw-r--r--src/ui/touch.c2
-rw-r--r--src/ui/util.c20
-rw-r--r--src/ui/util.h3
-rw-r--r--src/ui/widget.c51
-rw-r--r--src/ui/widget.h4
-rw-r--r--src/ui/window.c153
9 files changed, 264 insertions, 57 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 9b36b057..e51ec64b 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -133,6 +133,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
133 133
134void deinit_InputWidget(iInputWidget *d) { 134void deinit_InputWidget(iInputWidget *d) {
135 if (isSelected_Widget(d)) { 135 if (isSelected_Widget(d)) {
136 SDL_StopTextInput();
136 enableEditorKeysInMenus_(iTrue); 137 enableEditorKeysInMenus_(iTrue);
137 } 138 }
138 delete_TextBuf(d->buffered); 139 delete_TextBuf(d->buffered);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index da76c4d4..b1dfc378 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -451,6 +451,10 @@ static const char *tightModeLabels_[max_SidebarMode] = {
451 "\U0001f5b9", 451 "\U0001f5b9",
452}; 452};
453 453
454const char *icon_SidebarMode(enum iSidebarMode mode) {
455 return tightModeLabels_[mode];
456}
457
454void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { 458void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
455 iWidget *w = as_Widget(d); 459 iWidget *w = as_Widget(d);
456 init_Widget(w); 460 init_Widget(w);
@@ -470,28 +474,43 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
470 d->width = 73 * gap_UI; 474 d->width = 73 * gap_UI;
471#endif 475#endif
472 setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); 476 setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue);
473 d->maxButtonLabelWidth = 0;
474 iWidget *vdiv = makeVDiv_Widget(); 477 iWidget *vdiv = makeVDiv_Widget();
475 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag); 478 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag);
476 iWidget *buttons = new_Widget(); 479 if (deviceType_App() == phone_AppDeviceType && d->side == left_SideBarSide) {
477 for (int i = 0; i < max_SidebarMode; i++) {
478 d->modeButtons[i] = addChildFlags_Widget(
479 buttons,
480 iClob(new_LabelWidget(
481 tightModeLabels_[i],
482 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))),
483 frameless_WidgetFlag);
484 d->maxButtonLabelWidth =
485 iMaxi(d->maxButtonLabelWidth,
486 3 * gap_UI + measure_Text(uiLabel_FontId, normalModeLabels_[i]).x);
487 } 480 }
488 addChildFlags_Widget(vdiv, 481 iZap(d->modeButtons);
489 iClob(buttons), 482 /* On a phone, the right sidebar is used exclusively for Identities. */
490 arrangeHorizontal_WidgetFlag | resizeWidthOfChildren_WidgetFlag | 483 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
484 if (!isPhone || d->side == left_SideBarSide) {
485 iWidget *buttons = new_Widget();
486 setId_Widget(buttons, "buttons");
487 for (int i = 0; i < max_SidebarMode; i++) {
488 if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) {
489 continue;
490 }
491 d->modeButtons[i] = addChildFlags_Widget(
492 buttons,
493 iClob(new_LabelWidget(
494 tightModeLabels_[i],
495 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))),
496 frameless_WidgetFlag | (isPhone ? noBackground_WidgetFlag : 0));
497 }
498 setButtonFont_SidebarWidget(d, isPhone ? uiLabelLarge_FontId : uiLabel_FontId);
499 addChildFlags_Widget(vdiv,
500 iClob(buttons),
501 arrangeHorizontal_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
491 arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag | 502 arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag |
492 drawBackgroundToHorizontalSafeArea_WidgetFlag); 503 drawBackgroundToHorizontalSafeArea_WidgetFlag);
493 if (deviceType_App() == phone_AppDeviceType) { 504 if (deviceType_App() == phone_AppDeviceType) {
494 setBackgroundColor_Widget(buttons, uiBackground_ColorId); 505 setBackgroundColor_Widget(buttons, uiBackground_ColorId);
506 }
507 }
508 else {
509 iLabelWidget *heading = new_LabelWidget("Identities", NULL);
510 setBackgroundColor_Widget(as_Widget(heading), uiBackgroundUnfocusedSelection_ColorId);
511 setTextColor_LabelWidget(heading, uiIcon_ColorId);
512 setFont_LabelWidget(addChild_Widget(vdiv, iClob(heading)),
513 uiLabelLarge_FontId);
495 } 514 }
496 iWidget *content = new_Widget(); 515 iWidget *content = new_Widget();
497 setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); 516 setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue);
@@ -502,7 +521,9 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
502 d->blank = new_Widget(); 521 d->blank = new_Widget();
503 addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); 522 addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag);
504 addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); 523 addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag);
505 setMode_SidebarWidget(d, bookmarks_SidebarMode); 524 setMode_SidebarWidget(d,
525 deviceType_App() == phone_AppDeviceType && d->side == right_SideBarSide ?
526 identities_SidebarMode : bookmarks_SidebarMode);
506 d->resizer = 527 d->resizer =
507 addChildFlags_Widget(w, 528 addChildFlags_Widget(w,
508 iClob(new_Widget()), 529 iClob(new_Widget()),
@@ -524,6 +545,18 @@ void deinit_SidebarWidget(iSidebarWidget *d) {
524 deinit_String(&d->cmdPrefix); 545 deinit_String(&d->cmdPrefix);
525} 546}
526 547
548void setButtonFont_SidebarWidget(iSidebarWidget *d, int font) {
549 d->maxButtonLabelWidth = 0;
550 for (int i = 0; i < max_SidebarMode; i++) {
551 if (d->modeButtons[i]) {
552 setFont_LabelWidget(d->modeButtons[i], font);
553 d->maxButtonLabelWidth =
554 iMaxi(d->maxButtonLabelWidth,
555 3 * gap_UI + measure_Text(font, normalModeLabels_[i]).x);
556 }
557 }
558}
559
527static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { 560static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) {
528 if (d->mode == identities_SidebarMode) { 561 if (d->mode == identities_SidebarMode) {
529 const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list); 562 const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list);
@@ -591,9 +624,11 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, const iSidebarItem *it
591} 624}
592 625
593static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { 626static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) {
627 if (!d->modeButtons[0]) return;
594 const iBool isTight = 628 const iBool isTight =
595 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); 629 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth);
596 for (int i = 0; i < max_SidebarMode; i++) { 630 for (int i = 0; i < max_SidebarMode; i++) {
631 if (!d->modeButtons[i]) continue;
597 if (isTight && ~flags_Widget(as_Widget(d->modeButtons[i])) & tight_WidgetFlag) { 632 if (isTight && ~flags_Widget(as_Widget(d->modeButtons[i])) & tight_WidgetFlag) {
598 setFlags_Widget(as_Widget(d->modeButtons[i]), tight_WidgetFlag, iTrue); 633 setFlags_Widget(as_Widget(d->modeButtons[i]), tight_WidgetFlag, iTrue);
599 updateTextCStr_LabelWidget(d->modeButtons[i], tightModeLabels_[i]); 634 updateTextCStr_LabelWidget(d->modeButtons[i], tightModeLabels_[i]);
@@ -662,6 +697,9 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *
662 postCommandf_App("%s.toggle", cstr_String(id_Widget(w))); 697 postCommandf_App("%s.toggle", cstr_String(id_Widget(w)));
663 } 698 }
664 scrollOffset_ListWidget(d->list, 0); 699 scrollOffset_ListWidget(d->list, 0);
700 if (wasChanged) {
701 postCommandf_App("%s.mode.changed arg:%d", cstr_String(id_Widget(w)), d->mode);
702 }
665 return iTrue; 703 return iTrue;
666 } 704 }
667 else if (equal_Command(cmd, "toggle")) { 705 else if (equal_Command(cmd, "toggle")) {
diff --git a/src/ui/sidebarwidget.h b/src/ui/sidebarwidget.h
index fa74e049..d7029b0f 100644
--- a/src/ui/sidebarwidget.h
+++ b/src/ui/sidebarwidget.h
@@ -33,6 +33,8 @@ enum iSidebarMode {
33 max_SidebarMode 33 max_SidebarMode
34}; 34};
35 35
36const char * icon_SidebarMode (enum iSidebarMode mode);
37
36enum iSidebarSide { 38enum iSidebarSide {
37 left_SideBarSide, 39 left_SideBarSide,
38 right_SideBarSide, 40 right_SideBarSide,
@@ -41,8 +43,9 @@ enum iSidebarSide {
41iDeclareWidgetClass(SidebarWidget) 43iDeclareWidgetClass(SidebarWidget)
42iDeclareObjectConstructionArgs(SidebarWidget, enum iSidebarSide side) 44iDeclareObjectConstructionArgs(SidebarWidget, enum iSidebarSide side)
43 45
44iBool setMode_SidebarWidget (iSidebarWidget *, enum iSidebarMode mode); 46iBool setMode_SidebarWidget (iSidebarWidget *, enum iSidebarMode mode);
47void setButtonFont_SidebarWidget (iSidebarWidget *, int font);
45 48
46enum iSidebarMode mode_SidebarWidget (const iSidebarWidget *); 49enum iSidebarMode mode_SidebarWidget (const iSidebarWidget *);
47int width_SidebarWidget (const iSidebarWidget *); 50int width_SidebarWidget (const iSidebarWidget *);
48void setWidth_SidebarWidget (iSidebarWidget *, int width); 51void setWidth_SidebarWidget (iSidebarWidget *, int width);
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 505b85f9..eb5f4f7f 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -94,7 +94,7 @@ static iTouch *find_TouchState_(iTouchState *d, SDL_FingerID id) {
94 return NULL; 94 return NULL;
95} 95}
96 96
97static const uint32_t longPressSpanMs_ = 500; 97static const uint32_t longPressSpanMs_ = 425;
98static const int tapRadiusPt_ = 15; 98static const int tapRadiusPt_ = 15;
99 99
100static iBool isStationary_Touch_(const iTouch *d) { 100static iBool isStationary_Touch_(const iTouch *d) {
diff --git a/src/ui/util.c b/src/ui/util.c
index db9e4507..c55b2c36 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -198,14 +198,18 @@ static float valueAt_Anim_(const iAnim *d, const uint32_t now) {
198 return d->from; 198 return d->from;
199 } 199 }
200 float t = pos_Anim_(d, now); 200 float t = pos_Anim_(d, now);
201 const iBool isSoft = (d->flags & softer_AnimFlag) != 0;
201 if ((d->flags & easeBoth_AnimFlag) == easeBoth_AnimFlag) { 202 if ((d->flags & easeBoth_AnimFlag) == easeBoth_AnimFlag) {
202 t = easeBoth_(t); 203 t = easeBoth_(t);
204 if (isSoft) t = easeBoth_(t);
203 } 205 }
204 else if (d->flags & easeIn_AnimFlag) { 206 else if (d->flags & easeIn_AnimFlag) {
205 t = easeIn_(t); 207 t = easeIn_(t);
208 if (isSoft) t = easeIn_(t);
206 } 209 }
207 else if (d->flags & easeOut_AnimFlag) { 210 else if (d->flags & easeOut_AnimFlag) {
208 t = easeOut_(t); 211 t = easeOut_(t);
212 if (isSoft) t = easeOut_(t);
209 } 213 }
210 return d->from * (1.0f - t) + d->to * t; 214 return d->from * (1.0f - t) + d->to * t;
211} 215}
@@ -428,6 +432,9 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
428 iWidget *menu = new_Widget(); 432 iWidget *menu = new_Widget();
429 setFrameColor_Widget(menu, uiSeparator_ColorId); 433 setFrameColor_Widget(menu, uiSeparator_ColorId);
430 setBackgroundColor_Widget(menu, uiBackground_ColorId); 434 setBackgroundColor_Widget(menu, uiBackground_ColorId);
435 if (deviceType_App() != desktop_AppDeviceType) {
436 setPadding1_Widget(menu, gap_UI);
437 }
431 setFlags_Widget(menu, 438 setFlags_Widget(menu,
432 keepOnTop_WidgetFlag | collapse_WidgetFlag | hidden_WidgetFlag | 439 keepOnTop_WidgetFlag | collapse_WidgetFlag | hidden_WidgetFlag |
433 arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag | 440 arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag |
@@ -439,6 +446,9 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
439 iWidget *sep = addChild_Widget(menu, iClob(new_Widget())); 446 iWidget *sep = addChild_Widget(menu, iClob(new_Widget()));
440 setBackgroundColor_Widget(sep, uiSeparator_ColorId); 447 setBackgroundColor_Widget(sep, uiSeparator_ColorId);
441 sep->rect.size.y = gap_UI / 3; 448 sep->rect.size.y = gap_UI / 3;
449 if (deviceType_App() != desktop_AppDeviceType) {
450 sep->rect.size.y = gap_UI / 2;
451 }
442 setFlags_Widget(sep, hover_WidgetFlag | fixedHeight_WidgetFlag, iTrue); 452 setFlags_Widget(sep, hover_WidgetFlag | fixedHeight_WidgetFlag, iTrue);
443 } 453 }
444 else { 454 else {
@@ -447,6 +457,9 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
447 iClob(newKeyMods_LabelWidget(item->label, item->key, item->kmods, item->command)), 457 iClob(newKeyMods_LabelWidget(item->label, item->key, item->kmods, item->command)),
448 frameless_WidgetFlag | alignLeft_WidgetFlag | drawKey_WidgetFlag); 458 frameless_WidgetFlag | alignLeft_WidgetFlag | drawKey_WidgetFlag);
449 updateSize_LabelWidget(label); /* drawKey was set */ 459 updateSize_LabelWidget(label); /* drawKey was set */
460 if (deviceType_App() != desktop_AppDeviceType) {
461 setFont_LabelWidget(label, uiContent_FontId);
462 }
450 } 463 }
451 } 464 }
452 addChild_Widget(parent, iClob(menu)); 465 addChild_Widget(parent, iClob(menu));
@@ -468,13 +481,6 @@ void openMenu_Widget(iWidget *d, iInt2 coord) {
468 d->rect.pos = coord; 481 d->rect.pos = coord;
469 /* Ensure the full menu is visible. */ 482 /* Ensure the full menu is visible. */
470 const iInt2 rootSize = rootSize_Window(get_Window()); 483 const iInt2 rootSize = rootSize_Window(get_Window());
471#if defined (iPlatformAppleMobile)
472 /* Move out from under the user's hand/finger. */
473 if (!parentMenuButton_(d)) {
474 const float normX = (float) left_Rect(bounds_Widget(d)) / rootSize.x;
475 subv_I2(&d->rect.pos, init_I2(normX * width_Rect(d->rect), height_Rect(d->rect)));
476 }
477#endif
478 const iRect bounds = bounds_Widget(d); 484 const iRect bounds = bounds_Widget(d);
479 int leftExcess = -left_Rect(bounds); 485 int leftExcess = -left_Rect(bounds);
480 int rightExcess = right_Rect(bounds) - rootSize.x; 486 int rightExcess = right_Rect(bounds) - rootSize.x;
diff --git a/src/ui/util.h b/src/ui/util.h
index a280fedb..829a9c74 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -75,7 +75,8 @@ enum iAnimFlag {
75 indefinite_AnimFlag = iBit(1), /* does not end; must be linear */ 75 indefinite_AnimFlag = iBit(1), /* does not end; must be linear */
76 easeIn_AnimFlag = iBit(2), 76 easeIn_AnimFlag = iBit(2),
77 easeOut_AnimFlag = iBit(3), 77 easeOut_AnimFlag = iBit(3),
78 easeBoth_AnimFlag = easeIn_AnimFlag | easeOut_AnimFlag, 78 easeBoth_AnimFlag = easeIn_AnimFlag | easeOut_AnimFlag,
79 softer_AnimFlag = iBit(4),
79}; 80};
80 81
81struct Impl_Anim { 82struct Impl_Anim {
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 9320659c..31544cf8 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -77,6 +77,7 @@ void init_Widget(iWidget *d) {
77 d->rect = zero_Rect(); 77 d->rect = zero_Rect();
78 d->bgColor = none_ColorId; 78 d->bgColor = none_ColorId;
79 d->frameColor = none_ColorId; 79 d->frameColor = none_ColorId;
80 init_Anim(&d->visualOffset, 0.0f);
80 d->children = NULL; 81 d->children = NULL;
81 d->parent = NULL; 82 d->parent = NULL;
82 d->commandHandler = NULL; 83 d->commandHandler = NULL;
@@ -86,6 +87,10 @@ void init_Widget(iWidget *d) {
86void deinit_Widget(iWidget *d) { 87void deinit_Widget(iWidget *d) {
87 releaseChildren_Widget(d); 88 releaseChildren_Widget(d);
88 deinit_String(&d->id); 89 deinit_String(&d->id);
90// printf("widget %p deleted (on top:%d)\n", d, d->flags & keepOnTop_WidgetFlag ? 1 : 0);
91 if (d->flags & keepOnTop_WidgetFlag) {
92 removeAll_PtrArray(onTop_RootData_(), d);
93 }
89} 94}
90 95
91static void aboutToBeDestroyed_Widget_(iWidget *d) { 96static void aboutToBeDestroyed_Widget_(iWidget *d) {
@@ -166,8 +171,33 @@ void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) {
166 d->padding[3] = bottom; 171 d->padding[3] = bottom;
167} 172}
168 173
174static void visualOffsetAnimation_Widget_(void *ptr) {
175 iWidget *d = ptr;
176 postRefresh_App();
177 if (!isFinished_Anim(&d->visualOffset)) {
178 addTicker_App(visualOffsetAnimation_Widget_, ptr);
179 }
180 else {
181 setFlags_Widget(d, visualOffset_WidgetFlag, iFalse);
182 }
183}
184
185void setVisualOffset_Widget(iWidget *d, int value, uint32_t span, int animFlags) {
186 setFlags_Widget(d, visualOffset_WidgetFlag, iTrue);
187 if (span == 0) {
188 init_Anim(&d->visualOffset, value);
189 }
190 else {
191 setValue_Anim(&d->visualOffset, value, span);
192 d->visualOffset.flags = animFlags;
193 addTicker_App(visualOffsetAnimation_Widget_, d);
194 }
195}
196
169void setBackgroundColor_Widget(iWidget *d, int bgColor) { 197void setBackgroundColor_Widget(iWidget *d, int bgColor) {
170 d->bgColor = bgColor; 198 if (d) {
199 d->bgColor = bgColor;
200 }
171} 201}
172 202
173void setFrameColor_Widget(iWidget *d, int frameColor) { 203void setFrameColor_Widget(iWidget *d, int frameColor) {
@@ -456,8 +486,15 @@ void arrange_Widget(iWidget *d) {
456 486
457iRect bounds_Widget(const iWidget *d) { 487iRect bounds_Widget(const iWidget *d) {
458 iRect bounds = d->rect; 488 iRect bounds = d->rect;
489 if (d->flags & visualOffset_WidgetFlag) {
490 bounds.pos.y += iRound(value_Anim(&d->visualOffset));
491 }
459 for (const iWidget *w = d->parent; w; w = w->parent) { 492 for (const iWidget *w = d->parent; w; w = w->parent) {
460 addv_I2(&bounds.pos, w->rect.pos); 493 iInt2 pos = w->rect.pos;
494 if (w->flags & visualOffset_WidgetFlag) {
495 pos.y += iRound(value_Anim(&w->visualOffset));
496 }
497 addv_I2(&bounds.pos, pos);
461 } 498 }
462 return bounds; 499 return bounds;
463} 500}
@@ -677,11 +714,17 @@ void drawBackground_Widget(const iWidget *d) {
677 } 714 }
678} 715}
679 716
717iLocalDef iBool isDrawn_Widget_(const iWidget *d) {
718 return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;
719}
720
680void drawChildren_Widget(const iWidget *d) { 721void drawChildren_Widget(const iWidget *d) {
681 if (d->flags & hidden_WidgetFlag) return; 722 if (!isDrawn_Widget_(d)) {
723 return;
724 }
682 iConstForEach(ObjectList, i, d->children) { 725 iConstForEach(ObjectList, i, d->children) {
683 const iWidget *child = constAs_Widget(i.object); 726 const iWidget *child = constAs_Widget(i.object);
684 if (~child->flags & keepOnTop_WidgetFlag && ~child->flags & hidden_WidgetFlag) { 727 if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) {
685 class_Widget(child)->draw(child); 728 class_Widget(child)->draw(child);
686 } 729 }
687 } 730 }
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 66fe222b..02ee091f 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25/* Base class for UI widgets. */ 25/* Base class for UI widgets. */
26 26
27#include "metrics.h" 27#include "metrics.h"
28#include "util.h"
28 29
29#include <the_Foundation/object.h> 30#include <the_Foundation/object.h>
30#include <the_Foundation/objectlist.h> 31#include <the_Foundation/objectlist.h>
@@ -96,6 +97,7 @@ enum iWidgetFlag {
96#define noBackground_WidgetFlag iBit64(42) 97#define noBackground_WidgetFlag iBit64(42)
97#define drawBackgroundToHorizontalSafeArea_WidgetFlag iBit64(43) 98#define drawBackgroundToHorizontalSafeArea_WidgetFlag iBit64(43)
98#define drawBackgroundToVerticalSafeArea_WidgetFlag iBit64(44) 99#define drawBackgroundToVerticalSafeArea_WidgetFlag iBit64(44)
100#define visualOffset_WidgetFlag iBit64(45)
99 101
100enum iWidgetAddPos { 102enum iWidgetAddPos {
101 back_WidgetAddPos, 103 back_WidgetAddPos,
@@ -113,6 +115,7 @@ struct Impl_Widget {
113 int64_t flags; 115 int64_t flags;
114 iRect rect; 116 iRect rect;
115 int padding[4]; /* left, top, right, bottom */ 117 int padding[4]; /* left, top, right, bottom */
118 iAnim visualOffset;
116 int bgColor; 119 int bgColor;
117 int frameColor; 120 int frameColor;
118 iObjectList *children; 121 iObjectList *children;
@@ -200,6 +203,7 @@ void setPos_Widget (iWidget *, iInt2 pos);
200void setSize_Widget (iWidget *, iInt2 size); 203void setSize_Widget (iWidget *, iInt2 size);
201void setPadding_Widget (iWidget *, int left, int top, int right, int bottom); 204void setPadding_Widget (iWidget *, int left, int top, int right, int bottom);
202iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); } 205iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); }
206void setVisualOffset_Widget (iWidget *d, int value, uint32_t span, int animFlags);
203void setBackgroundColor_Widget (iWidget *, int bgColor); 207void setBackgroundColor_Widget (iWidget *, int bgColor);
204void setFrameColor_Widget (iWidget *, int frameColor); 208void setFrameColor_Widget (iWidget *, int frameColor);
205void setCommandHandler_Widget (iWidget *, iBool (*handler)(iWidget *, const char *)); 209void setCommandHandler_Widget (iWidget *, iBool (*handler)(iWidget *, const char *));
diff --git a/src/ui/window.c b/src/ui/window.c
index 4d6a197a..3c77b18b 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -129,9 +129,16 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
129 /* Place the sidebar next to or under doctabs depending on orientation. */ 129 /* Place the sidebar next to or under doctabs depending on orientation. */
130 iSidebarWidget *sidebar = findChild_Widget(root, "sidebar"); 130 iSidebarWidget *sidebar = findChild_Widget(root, "sidebar");
131 removeChild_Widget(parent_Widget(sidebar), sidebar); 131 removeChild_Widget(parent_Widget(sidebar), sidebar);
132 setButtonFont_SidebarWidget(sidebar, isLandscape_App() ? uiLabel_FontId : uiLabelLarge_FontId);
133 setBackgroundColor_Widget(findChild_Widget(as_Widget(sidebar), "buttons"),
134 isPortrait_App() ? uiBackgroundUnfocusedSelection_ColorId
135 : uiBackground_ColorId);
132 if (isLandscape_App()) { 136 if (isLandscape_App()) {
133 addChildPos_Widget(findChild_Widget(root, "tabs.content"), iClob(sidebar), front_WidgetAddPos); 137 addChildPos_Widget(findChild_Widget(root, "tabs.content"), iClob(sidebar), front_WidgetAddPos);
134 setWidth_SidebarWidget(sidebar, 73 * gap_UI); 138 setWidth_SidebarWidget(sidebar, 73 * gap_UI);
139 if (isVisible_Widget(findWidget_App("sidebar2"))) {
140 postCommand_App("sidebar2.toggle");
141 }
135 } 142 }
136 else { 143 else {
137 addChildPos_Widget(findChild_Widget(root, "stack"), iClob(sidebar), back_WidgetAddPos); 144 addChildPos_Widget(findChild_Widget(root, "stack"), iClob(sidebar), back_WidgetAddPos);
@@ -184,9 +191,7 @@ static const iMenuItem navMenuItems_[] = {
184 { "Close Tab", 'w', KMOD_PRIMARY, "tabs.close" }, 191 { "Close Tab", 'w', KMOD_PRIMARY, "tabs.close" },
185 { "---", 0, 0, NULL }, 192 { "---", 0, 0, NULL },
186 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, 193 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" },
187 { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" },
188 { "---", 0, 0, NULL }, 194 { "---", 0, 0, NULL },
189 { "Toggle Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" },
190 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 195 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" },
191 { "Zoom Out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" }, 196 { "Zoom Out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" },
192 { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, 197 { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" },
@@ -266,20 +271,31 @@ static const iMenuItem helpMenuItems_[] = {
266}; 271};
267#endif 272#endif
268 273
274#if defined (iPlatformAppleMobile)
269static const iMenuItem identityButtonMenuItems_[] = { 275static const iMenuItem identityButtonMenuItems_[] = {
270 { "No Active Identity", 0, 0, "ident.showactive" }, 276 { "No Active Identity", 0, 0, "ident.showactive" },
271 { "---", 0, 0, NULL }, 277 { "---", 0, 0, NULL },
272#if !defined (iHaveNativeMenus) 278 { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" },
279 { "Import...", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" },
280 { "---", 0, 0, NULL },
281 { "Show Identities", 0, 0, "toolbar.showident" },
282};
283#else /* desktop */
284static const iMenuItem identityButtonMenuItems_[] = {
285 { "No Active Identity", 0, 0, "ident.showactive" },
286 { "---", 0, 0, NULL },
287# if !defined (iHaveNativeMenus)
273 { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, 288 { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" },
274 { "Import...", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, 289 { "Import...", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" },
275 { "---", 0, 0, NULL }, 290 { "---", 0, 0, NULL },
276 { "Show Identities", '4', KMOD_PRIMARY, "sidebar.mode arg:3 show:1" }, 291 { "Show Identities", '4', KMOD_PRIMARY, "sidebar.mode arg:3 show:1" },
277#else 292# else
278 { "New Identity...", 0, 0, "ident.new" }, 293 { "New Identity...", 0, 0, "ident.new" },
279 { "---", 0, 0, NULL }, 294 { "---", 0, 0, NULL },
280 { "Show Identities", 0, 0, "sidebar.mode arg:3 show:1" }, 295 { "Show Identities", 0, 0, "sidebar.mode arg:3 show:1" },
281#endif 296#endif
282}; 297};
298#endif
283 299
284static const char *reloadCStr_ = "\U0001f503"; 300static const char *reloadCStr_ = "\U0001f503";
285 301
@@ -418,14 +434,26 @@ static void updatePadding_Window_(iWindow *d) {
418 safeAreaInsets_iOS(&left, &top, &right, &bottom); 434 safeAreaInsets_iOS(&left, &top, &right, &bottom);
419 setPadding_Widget(findChild_Widget(d->root, "navdiv"), left, top, right, 0); 435 setPadding_Widget(findChild_Widget(d->root, "navdiv"), left, top, right, 0);
420 iWidget *toolBar = findChild_Widget(d->root, "toolbar"); 436 iWidget *toolBar = findChild_Widget(d->root, "toolbar");
421 if (isPortrait_App()) { 437 setPadding_Widget(toolBar, left, 0, right, bottom);
422 setPadding_Widget(toolBar, left, 0, right, bottom); 438 }
439#endif
440}
441
442static void dismissPortraitPhoneSidebars_(void) {
443 if (deviceType_App() == phone_AppDeviceType && isPortrait_App()) {
444 iWidget *sidebar = findWidget_App("sidebar");
445 iWidget *sidebar2 = findWidget_App("sidebar2");
446 if (isVisible_Widget(sidebar)) {
447 postCommand_App("sidebar.toggle");
448 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag);
423 } 449 }
424 else { 450 if (isVisible_Widget(sidebar2)) {
425 setPadding1_Widget(toolBar, 0); 451 postCommand_App("sidebar2.toggle");
452 setVisualOffset_Widget(sidebar2, height_Widget(sidebar2), 250, easeIn_AnimFlag);
426 } 453 }
454 setFlags_Widget(findWidget_App("toolbar.ident"), noBackground_WidgetFlag, iTrue);
455 setFlags_Widget(findWidget_App("toolbar.view"), noBackground_WidgetFlag, iTrue);
427 } 456 }
428#endif
429} 457}
430 458
431static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { 459static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
@@ -521,12 +549,11 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
521 postCommand_App("visited.changed"); /* sidebar will update */ 549 postCommand_App("visited.changed"); /* sidebar will update */
522 setText_InputWidget(url, urlStr); 550 setText_InputWidget(url, urlStr);
523 checkLoadAnimation_Window_(get_Window()); 551 checkLoadAnimation_Window_(get_Window());
552 dismissPortraitPhoneSidebars_();
524 updateNavBarIdentity_(navBar); 553 updateNavBarIdentity_(navBar);
525 /* Icon updates should be limited to automatically chosen icons if the user 554 /* Icon updates should be limited to automatically chosen icons if the user
526 is allowed to pick their own in the future. */ 555 is allowed to pick their own in the future. */
527 if (updateBookmarkIcon_Bookmarks( 556 if (updateBookmarkIcon_Bookmarks(bookmarks_App(), urlStr,
528 bookmarks_App(),
529 urlStr,
530 siteIcon_GmDocument(document_DocumentWidget(document_App())))) { 557 siteIcon_GmDocument(document_DocumentWidget(document_App())))) {
531 postCommand_App("bookmarks.changed"); 558 postCommand_App("bookmarks.changed");
532 } 559 }
@@ -540,11 +567,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
540 iInputWidget *url = findChild_Widget(navBar, "url"); 567 iInputWidget *url = findChild_Widget(navBar, "url");
541 setTextCStr_InputWidget(url, suffixPtr_Command(cmd, "url")); 568 setTextCStr_InputWidget(url, suffixPtr_Command(cmd, "url"));
542 checkLoadAnimation_Window_(get_Window()); 569 checkLoadAnimation_Window_(get_Window());
543 if (deviceType_App() == phone_AppDeviceType && isPortrait_App()) { 570 dismissPortraitPhoneSidebars_();
544 if (isVisible_Widget(findWidget_App("sidebar"))) {
545 postCommand_App("sidebar.toggle");
546 }
547 }
548 return iFalse; 571 return iFalse;
549 } 572 }
550 } 573 }
@@ -627,6 +650,77 @@ static iBool handleSearchBarCommands_(iWidget *searchBar, const char *cmd) {
627 return iFalse; 650 return iFalse;
628} 651}
629 652
653#if defined (iPlatformAppleMobile)
654static void dismissSidebar_(iWidget *sidebar, const char *toolButtonId) {
655 if (isVisible_Widget(sidebar)) {
656 postCommandf_App("%s.toggle", cstr_String(id_Widget(sidebar)));
657 if (toolButtonId) {
658 setFlags_Widget(findWidget_App(toolButtonId), noBackground_WidgetFlag, iTrue);
659 }
660 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag);
661 }
662}
663
664static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) {
665 if (equalWidget_Command(cmd, toolBar, "mouse.clicked") && arg_Command(cmd) &&
666 argLabel_Command(cmd, "button") == SDL_BUTTON_RIGHT) {
667 iWidget *menu = findChild_Widget(toolBar, "toolbar.menu");
668 arrange_Widget(menu);
669 openMenu_Widget(menu, init_I2(0, -height_Widget(menu)));
670 return iTrue;
671 }
672 else if (equal_Command(cmd, "toolbar.showview")) {
673 iWidget *sidebar = findWidget_App("sidebar");
674 iWidget *sidebar2 = findWidget_App("sidebar2");
675 dismissSidebar_(sidebar2, "toolbar.ident");
676 const iBool isVisible = isVisible_Widget(sidebar);
677 setFlags_Widget(findChild_Widget(toolBar, "toolbar.view"), noBackground_WidgetFlag,
678 isVisible);
679 if (arg_Command(cmd) >= 0) {
680 postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd));
681 if (!isVisible) {
682 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 0, 0);
683 setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
684 }
685 }
686 else {
687 postCommandf_App("sidebar.toggle");
688 if (isVisible) {
689 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag);
690 }
691 else {
692 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 0, 0);
693 setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
694 }
695 }
696 return iTrue;
697 }
698 else if (equal_Command(cmd, "toolbar.showident")) {
699 iWidget *sidebar = findWidget_App("sidebar");
700 iWidget *sidebar2 = findWidget_App("sidebar2");
701 dismissSidebar_(sidebar, "toolbar.view");
702 const iBool isVisible = isVisible_Widget(sidebar2);
703 setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag,
704 isVisible);
705 if (isVisible) {
706 dismissSidebar_(sidebar2, NULL);
707 }
708 else {
709 postCommand_App("sidebar2.mode arg:3 show:1");
710 setVisualOffset_Widget(sidebar2, height_Widget(sidebar2), 0, 0);
711 setVisualOffset_Widget(sidebar2, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
712 }
713 return iTrue;
714 }
715 else if (equal_Command(cmd, "sidebar.mode.changed")) {
716 iLabelWidget *viewTool = findChild_Widget(toolBar, "toolbar.view");
717 updateTextCStr_LabelWidget(viewTool, icon_SidebarMode(arg_Command(cmd)));
718 return iFalse;
719 }
720 return iFalse;
721}
722#endif /* defined (iPlatformAppleMobile) */
723
630static iLabelWidget *newLargeIcon_LabelWidget(const char *text, const char *cmd) { 724static iLabelWidget *newLargeIcon_LabelWidget(const char *text, const char *cmd) {
631 iLabelWidget *lab = newIcon_LabelWidget(text, 0, 0, cmd); 725 iLabelWidget *lab = newIcon_LabelWidget(text, 0, 0, cmd);
632 setFont_LabelWidget(lab, uiLabelLarge_FontId); 726 setFont_LabelWidget(lab, uiLabelLarge_FontId);
@@ -804,10 +898,15 @@ static void setupUserInterface_Window(iWindow *d) {
804 iWidget *content = findChild_Widget(d->root, "tabs.content"); 898 iWidget *content = findChild_Widget(d->root, "tabs.content");
805 iSidebarWidget *sidebar1 = new_SidebarWidget(left_SideBarSide); 899 iSidebarWidget *sidebar1 = new_SidebarWidget(left_SideBarSide);
806 addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos); 900 addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos);
901 iSidebarWidget *sidebar2 = new_SidebarWidget(right_SideBarSide);
807 if (deviceType_App() != phone_AppDeviceType) { 902 if (deviceType_App() != phone_AppDeviceType) {
808 iSidebarWidget *sidebar2 = new_SidebarWidget(right_SideBarSide);
809 addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos); 903 addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos);
810 } 904 }
905 else {
906 /* The identities sidebar is always in the main area. */
907 addChild_Widget(findChild_Widget(d->root, "stack"), iClob(sidebar2));
908 setFlags_Widget(as_Widget(sidebar2), hidden_WidgetFlag, iTrue);
909 }
811 } 910 }
812 /* Lookup results. */ { 911 /* Lookup results. */ {
813 iLookupWidget *lookup = new_LookupWidget(); 912 iLookupWidget *lookup = new_LookupWidget();
@@ -841,21 +940,33 @@ static void setupUserInterface_Window(iWindow *d) {
841 iWidget *toolBar = new_Widget(); 940 iWidget *toolBar = new_Widget();
842 addChild_Widget(div, iClob(toolBar)); 941 addChild_Widget(div, iClob(toolBar));
843 setId_Widget(toolBar, "toolbar"); 942 setId_Widget(toolBar, "toolbar");
943 setCommandHandler_Widget(toolBar, handleToolBarCommands_);
844 setFlags_Widget(toolBar, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag | 944 setFlags_Widget(toolBar, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
845 arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); 945 arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue);
846 setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); 946 setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId);
847 addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), frameless_WidgetFlag); 947 addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), frameless_WidgetFlag);
848 addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f872", "navigate.forward")), frameless_WidgetFlag); 948 addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f872", "navigate.forward")), frameless_WidgetFlag);
849 setId_Widget(addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f464", "sidebar.mode arg:3 toggle:1")), frameless_WidgetFlag), "toolbar.ident"); 949 setId_Widget(addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f464", "toolbar.showident")), frameless_WidgetFlag), "toolbar.ident");
850 addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f588", "sidebar.mode arg:0 toggle:1")), frameless_WidgetFlag); 950 setId_Widget(addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f588", "toolbar.showview arg:-1")),
951 frameless_WidgetFlag | commandOnClick_WidgetFlag), "toolbar.view");
851 iLabelWidget *menuButton = makeMenuButton_LabelWidget("\U0001d362", navMenuItems_, iElemCount(navMenuItems_)); 952 iLabelWidget *menuButton = makeMenuButton_LabelWidget("\U0001d362", navMenuItems_, iElemCount(navMenuItems_));
852 setFont_LabelWidget(menuButton, uiLabelLarge_FontId); 953 setFont_LabelWidget(menuButton, uiLabelLarge_FontId);
853 addChildFlags_Widget(toolBar, iClob(menuButton), frameless_WidgetFlag); 954 addChildFlags_Widget(toolBar, iClob(menuButton), frameless_WidgetFlag);
854 iForEach(ObjectList, i, children_Widget(toolBar)) { 955 iForEach(ObjectList, i, children_Widget(toolBar)) {
855 iLabelWidget *btn = i.object; 956 iLabelWidget *btn = i.object;
856 setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue); 957 setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue);
857 setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId); 958 setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId);
959 setBackgroundColor_Widget(i.object, tmBannerSideTitle_ColorId);
858 } 960 }
961 const iMenuItem items[] = {
962 { "Bookmarks", 0, 0, "toolbar.showview arg:0" },
963 { "Feeds", 0, 0, "toolbar.showview arg:1" },
964 { "History", 0, 0, "toolbar.showview arg:2" },
965 { "Page Outline", 0, 0, "toolbar.showview arg:4" },
966 };
967 iWidget *menu = makeMenu_Widget(findChild_Widget(toolBar, "toolbar.view"),
968 items, iElemCount(items));
969 setId_Widget(menu, "toolbar.menu");
859 } 970 }
860#endif 971#endif
861 updatePadding_Window_(d); 972 updatePadding_Window_(d);