diff options
Diffstat (limited to 'src/ui/sidebarwidget.c')
-rw-r--r-- | src/ui/sidebarwidget.c | 876 |
1 files changed, 456 insertions, 420 deletions
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 2219eba9..409d749c 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include "app.h" | 25 | #include "app.h" |
26 | #include "defs.h" | 26 | #include "defs.h" |
27 | #include "bookmarks.h" | 27 | #include "bookmarks.h" |
28 | #include "certlistwidget.h" | ||
28 | #include "command.h" | 29 | #include "command.h" |
29 | #include "documentwidget.h" | 30 | #include "documentwidget.h" |
30 | #include "feeds.h" | 31 | #include "feeds.h" |
@@ -34,10 +35,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
34 | #include "inputwidget.h" | 35 | #include "inputwidget.h" |
35 | #include "labelwidget.h" | 36 | #include "labelwidget.h" |
36 | #include "listwidget.h" | 37 | #include "listwidget.h" |
38 | #include "mobile.h" | ||
37 | #include "keys.h" | 39 | #include "keys.h" |
38 | #include "paint.h" | 40 | #include "paint.h" |
39 | #include "root.h" | 41 | #include "root.h" |
40 | #include "scrollwidget.h" | 42 | #include "scrollwidget.h" |
43 | #include "touch.h" | ||
41 | #include "util.h" | 44 | #include "util.h" |
42 | #include "visited.h" | 45 | #include "visited.h" |
43 | 46 | ||
@@ -88,6 +91,22 @@ iDefineObjectConstruction(SidebarItem) | |||
88 | 91 | ||
89 | /*----------------------------------------------------------------------------------------------*/ | 92 | /*----------------------------------------------------------------------------------------------*/ |
90 | 93 | ||
94 | static const char *normalModeLabels_[max_SidebarMode] = { | ||
95 | book_Icon " ${sidebar.bookmarks}", | ||
96 | star_Icon " ${sidebar.feeds}", | ||
97 | clock_Icon " ${sidebar.history}", | ||
98 | person_Icon " ${sidebar.identities}", | ||
99 | page_Icon " ${sidebar.outline}", | ||
100 | }; | ||
101 | |||
102 | static const char *tightModeLabels_[max_SidebarMode] = { | ||
103 | book_Icon, | ||
104 | star_Icon, | ||
105 | clock_Icon, | ||
106 | person_Icon, | ||
107 | page_Icon, | ||
108 | }; | ||
109 | |||
91 | struct Impl_SidebarWidget { | 110 | struct Impl_SidebarWidget { |
92 | iWidget widget; | 111 | iWidget widget; |
93 | enum iSidebarSide side; | 112 | enum iSidebarSide side; |
@@ -96,7 +115,10 @@ struct Impl_SidebarWidget { | |||
96 | iString cmdPrefix; | 115 | iString cmdPrefix; |
97 | iWidget * blank; | 116 | iWidget * blank; |
98 | iListWidget * list; | 117 | iListWidget * list; |
118 | iCertListWidget * certList; | ||
99 | iWidget * actions; /* below the list, area for buttons */ | 119 | iWidget * actions; /* below the list, area for buttons */ |
120 | int midHeight; /* on portrait phone, the height for the middle state */ | ||
121 | iBool isEditing; /* mobile edit mode */ | ||
100 | int modeScroll[max_SidebarMode]; | 122 | int modeScroll[max_SidebarMode]; |
101 | iLabelWidget * modeButtons[max_SidebarMode]; | 123 | iLabelWidget * modeButtons[max_SidebarMode]; |
102 | int maxButtonLabelWidth; | 124 | int maxButtonLabelWidth; |
@@ -114,6 +136,10 @@ struct Impl_SidebarWidget { | |||
114 | 136 | ||
115 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) | 137 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) |
116 | 138 | ||
139 | iLocalDef iListWidget *list_SidebarWidget_(iSidebarWidget *d) { | ||
140 | return d->mode == identities_SidebarMode ? (iListWidget *) d->certList : d->list; | ||
141 | } | ||
142 | |||
117 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { | 143 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { |
118 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; | 144 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; |
119 | } | 145 | } |
@@ -181,77 +207,31 @@ int cmpTree_Bookmark(const iBookmark **a, const iBookmark **b) { | |||
181 | return cmpStringCase_String(&bm1->title, &bm2->title); | 207 | return cmpStringCase_String(&bm1->title, &bm2->title); |
182 | } | 208 | } |
183 | 209 | ||
210 | static enum iFontId actionButtonFont_SidebarWidget_(const iSidebarWidget *d) { | ||
211 | switch (deviceType_App()) { | ||
212 | default: | ||
213 | break; | ||
214 | case phone_AppDeviceType: | ||
215 | return isPortrait_App() ? uiLabelBig_FontId : uiLabelMedium_FontId; | ||
216 | case tablet_AppDeviceType: | ||
217 | return uiLabelMedium_FontId; | ||
218 | } | ||
219 | return d->buttonFont; | ||
220 | } | ||
221 | |||
184 | static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, | 222 | static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, |
185 | const char *command, int64_t flags) { | 223 | const char *command, int64_t flags) { |
186 | iLabelWidget *btn = addChildFlags_Widget(d->actions, | 224 | iLabelWidget *btn = addChildFlags_Widget(d->actions, |
187 | iClob(new_LabelWidget(label, command)), | 225 | iClob(new_LabelWidget(label, command)), |
188 | //(deviceType_App() != desktop_AppDeviceType ? | ||
189 | // extraPadding_WidgetFlag : 0) | | ||
190 | flags); | 226 | flags); |
191 | setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide | 227 | setFont_LabelWidget(btn, actionButtonFont_SidebarWidget_(d)); |
192 | ? uiLabelBig_FontId | ||
193 | : d->buttonFont); | ||
194 | checkIcon_LabelWidget(btn); | 228 | checkIcon_LabelWidget(btn); |
195 | return btn; | 229 | if (deviceType_App() != desktop_AppDeviceType) { |
196 | } | 230 | setFlags_Widget(as_Widget(btn), frameless_WidgetFlag, iTrue); |
197 | 231 | setTextColor_LabelWidget(btn, uiTextAction_ColorId); | |
198 | static iGmIdentity *menuIdentity_SidebarWidget_(const iSidebarWidget *d) { | 232 | setBackgroundColor_Widget(as_Widget(btn), uiBackground_ColorId); |
199 | if (d->mode == identities_SidebarMode) { | ||
200 | if (d->contextItem) { | ||
201 | return identity_GmCerts(certs_App(), d->contextItem->id); | ||
202 | } | ||
203 | } | ||
204 | return NULL; | ||
205 | } | ||
206 | |||
207 | static void updateContextMenu_SidebarWidget_(iSidebarWidget *d) { | ||
208 | if (d->mode != identities_SidebarMode) { | ||
209 | return; | ||
210 | } | 233 | } |
211 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | 234 | return btn; |
212 | pushBackN_Array(items, (iMenuItem[]){ | ||
213 | { person_Icon " ${ident.use}", 0, 0, "ident.use arg:1" }, | ||
214 | { close_Icon " ${ident.stopuse}", 0, 0, "ident.use arg:0" }, | ||
215 | { close_Icon " ${ident.stopuse.all}", 0, 0, "ident.use arg:0 clear:1" }, | ||
216 | { "---", 0, 0, NULL }, | ||
217 | { edit_Icon " ${menu.edit.notes}", 0, 0, "ident.edit" }, | ||
218 | { "${ident.fingerprint}", 0, 0, "ident.fingerprint" }, | ||
219 | { export_Icon " ${ident.export}", 0, 0, "ident.export" }, | ||
220 | { "---", 0, 0, NULL }, | ||
221 | { delete_Icon " " uiTextCaution_ColorEscape "${ident.delete}", 0, 0, "ident.delete confirm:1" }, | ||
222 | }, 9); | ||
223 | /* Used URLs. */ | ||
224 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
225 | if (ident) { | ||
226 | size_t insertPos = 3; | ||
227 | if (!isEmpty_StringSet(ident->useUrls)) { | ||
228 | insert_Array(items, insertPos++, &(iMenuItem){ "---", 0, 0, NULL }); | ||
229 | } | ||
230 | const iString *docUrl = url_DocumentWidget(document_App()); | ||
231 | iBool usedOnCurrentPage = iFalse; | ||
232 | iConstForEach(StringSet, i, ident->useUrls) { | ||
233 | const iString *url = i.value; | ||
234 | usedOnCurrentPage |= equalCase_String(docUrl, url); | ||
235 | iRangecc urlStr = range_String(url); | ||
236 | if (startsWith_Rangecc(urlStr, "gemini://")) { | ||
237 | urlStr.start += 9; /* omit the default scheme */ | ||
238 | } | ||
239 | if (endsWith_Rangecc(urlStr, "/")) { | ||
240 | urlStr.end--; /* looks cleaner */ | ||
241 | } | ||
242 | insert_Array(items, | ||
243 | insertPos++, | ||
244 | &(iMenuItem){ format_CStr(globe_Icon " %s", cstr_Rangecc(urlStr)), | ||
245 | 0, | ||
246 | 0, | ||
247 | format_CStr("!open url:%s", cstr_String(url)) }); | ||
248 | } | ||
249 | if (!usedOnCurrentPage) { | ||
250 | remove_Array(items, 1); | ||
251 | } | ||
252 | } | ||
253 | destroy_Widget(d->menu); | ||
254 | d->menu = makeMenu_Widget(as_Widget(d), data_Array(items), size_Array(items)); | ||
255 | } | 235 | } |
256 | 236 | ||
257 | static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { | 237 | static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { |
@@ -264,7 +244,25 @@ static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBoo | |||
264 | return iFalse; | 244 | return iFalse; |
265 | } | 245 | } |
266 | 246 | ||
247 | static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) { | ||
248 | return isPortraitPhone_App(); | ||
249 | } | ||
250 | |||
251 | static void setMobileEditMode_SidebarWidget_(iSidebarWidget *d, iBool editing) { | ||
252 | iWidget *w = as_Widget(d); | ||
253 | d->isEditing = editing; | ||
254 | if (d->actions) { | ||
255 | setFlags_Widget(findChild_Widget(w, "sidebar.close"), hidden_WidgetFlag, editing); | ||
256 | setFlags_Widget(child_Widget(d->actions, 0), hidden_WidgetFlag, !editing); | ||
257 | setTextCStr_LabelWidget(child_Widget(as_Widget(d->actions), 2), | ||
258 | editing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}"); | ||
259 | setDragHandleWidth_ListWidget(d->list, editing ? itemHeight_ListWidget(d->list) * 3 / 2 : 0); | ||
260 | arrange_Widget(d->actions); | ||
261 | } | ||
262 | } | ||
263 | |||
267 | static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { | 264 | static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { |
265 | const iBool isMobile = (deviceType_App() != desktop_AppDeviceType); | ||
268 | clear_ListWidget(d->list); | 266 | clear_ListWidget(d->list); |
269 | releaseChildren_Widget(d->blank); | 267 | releaseChildren_Widget(d->blank); |
270 | if (!keepActions) { | 268 | if (!keepActions) { |
@@ -301,12 +299,12 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
301 | if (secondsSince_Time(&now, &entry->discovered) > maxAge_Visited) { | 299 | if (secondsSince_Time(&now, &entry->discovered) > maxAge_Visited) { |
302 | break; /* the rest are even older */ | 300 | break; /* the rest are even older */ |
303 | } | 301 | } |
304 | isEmpty = iFalse; | ||
305 | const iBool isOpen = equal_String(docUrl, &entry->url); | 302 | const iBool isOpen = equal_String(docUrl, &entry->url); |
306 | const iBool isUnread = isUnread_FeedEntry(entry); | 303 | const iBool isUnread = isUnread_FeedEntry(entry); |
307 | if (d->feedsMode == unread_FeedsMode && !isUnread && !isOpen) { | 304 | if (d->feedsMode == unread_FeedsMode && !isUnread && !isOpen) { |
308 | continue; | 305 | continue; |
309 | } | 306 | } |
307 | isEmpty = iFalse; | ||
310 | /* Insert date separators. */ { | 308 | /* Insert date separators. */ { |
311 | iDate entryDate; | 309 | iDate entryDate; |
312 | init_Date(&entryDate, &entry->posted); | 310 | init_Date(&entryDate, &entry->posted); |
@@ -351,16 +349,18 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
351 | } | 349 | } |
352 | } | 350 | } |
353 | /* Actions. */ | 351 | /* Actions. */ |
352 | if (!isMobile) { | ||
354 | if (!keepActions) { | 353 | if (!keepActions) { |
355 | addActionButton_SidebarWidget_( | 354 | addActionButton_SidebarWidget_(d, |
356 | d, check_Icon " ${sidebar.action.feeds.markallread}", "feeds.markallread", expand_WidgetFlag | | 355 | check_Icon " ${sidebar.action.feeds.markallread}", |
357 | tight_WidgetFlag); | 356 | "feeds.markallread", |
357 | expand_WidgetFlag | tight_WidgetFlag); | ||
358 | updateSize_LabelWidget(addChildFlags_Widget(d->actions, | 358 | updateSize_LabelWidget(addChildFlags_Widget(d->actions, |
359 | iClob(new_LabelWidget("${sidebar.action.show}", NULL)), | 359 | iClob(new_LabelWidget("${sidebar.action.show}", NULL)), |
360 | frameless_WidgetFlag | tight_WidgetFlag)); | 360 | frameless_WidgetFlag | tight_WidgetFlag)); |
361 | const iMenuItem items[] = { | 361 | const iMenuItem items[] = { |
362 | { "${sidebar.action.feeds.showall}", SDLK_u, KMOD_SHIFT, "feeds.mode arg:0" }, | 362 | { page_Icon " ${sidebar.action.feeds.showall}", SDLK_u, KMOD_SHIFT, "feeds.mode arg:0" }, |
363 | { "${sidebar.action.feeds.showunread}", SDLK_u, 0, "feeds.mode arg:1" }, | 363 | { circle_Icon " ${sidebar.action.feeds.showunread}", SDLK_u, 0, "feeds.mode arg:1" }, |
364 | }; | 364 | }; |
365 | iWidget *dropButton = addChild_Widget( | 365 | iWidget *dropButton = addChild_Widget( |
366 | d->actions, | 366 | d->actions, |
@@ -369,17 +369,38 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
369 | checkIcon_LabelWidget((iLabelWidget *) dropButton); | 369 | checkIcon_LabelWidget((iLabelWidget *) dropButton); |
370 | setFixedSize_Widget( | 370 | setFixedSize_Widget( |
371 | dropButton, | 371 | dropButton, |
372 | init_I2(iMaxi(20 * gap_UI, measure_Text( | 372 | init_I2(iMaxi(20 * gap_UI, |
373 | default_FontId, | 373 | measure_Text(default_FontId, |
374 | translateCStr_Lang(items[findWidestLabel_MenuItem(items, 2)].label)) | 374 | translateCStr_Lang( |
375 | items[findWidestLabel_MenuItem(items, 2)].label)) | ||
375 | .advance.x + | 376 | .advance.x + |
376 | 6 * gap_UI), | 377 | 13 * gap_UI), |
377 | -1)); | 378 | -1)); |
378 | } | 379 | } |
379 | else { | 380 | else { |
380 | updateDropdownSelection_LabelWidget(findChild_Widget(d->actions, "feeds.modebutton"), | 381 | updateDropdownSelection_LabelWidget(findChild_Widget(d->actions, "feeds.modebutton"), |
381 | format_CStr(" arg:%d", d->feedsMode)); | 382 | format_CStr(" arg:%d", d->feedsMode)); |
382 | } | 383 | } |
384 | } | ||
385 | else { | ||
386 | if (!keepActions) { | ||
387 | iLabelWidget *readAll = addActionButton_SidebarWidget_(d, | ||
388 | check_Icon, | ||
389 | "feeds.markallread confirm:1", | ||
390 | 0); | ||
391 | setTextColor_LabelWidget(readAll, uiTextCaution_ColorId); | ||
392 | addActionButton_SidebarWidget_(d, | ||
393 | page_Icon, | ||
394 | "feeds.mode arg:0", | ||
395 | 0); | ||
396 | addActionButton_SidebarWidget_(d, | ||
397 | circle_Icon, | ||
398 | "feeds.mode arg:1", | ||
399 | 0); | ||
400 | } | ||
401 | setOutline_LabelWidget(child_Widget(d->actions, 1), d->feedsMode != all_FeedsMode); | ||
402 | setOutline_LabelWidget(child_Widget(d->actions, 2), d->feedsMode != unread_FeedsMode); | ||
403 | } | ||
383 | d->menu = makeMenu_Widget( | 404 | d->menu = makeMenu_Widget( |
384 | as_Widget(d), | 405 | as_Widget(d), |
385 | (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" }, | 406 | (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" }, |
@@ -416,11 +437,6 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
416 | break; | 437 | break; |
417 | } | 438 | } |
418 | case bookmarks_SidebarMode: { | 439 | case bookmarks_SidebarMode: { |
419 | iRegExp *homeTag = iClob(new_RegExp("\\b" homepage_BookmarkTag "\\b", caseSensitive_RegExpOption)); | ||
420 | iRegExp *subTag = iClob(new_RegExp("\\b" subscribed_BookmarkTag "\\b", caseSensitive_RegExpOption)); | ||
421 | iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption)); | ||
422 | iRegExp *remoteTag = iClob(new_RegExp("\\b" remote_BookmarkTag "\\b", caseSensitive_RegExpOption)); | ||
423 | iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption)); | ||
424 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark, NULL, NULL)) { | 440 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark, NULL, NULL)) { |
425 | const iBookmark *bm = i.ptr; | 441 | const iBookmark *bm = i.ptr; |
426 | if (isBookmarkFolded_SidebarWidget_(d, bm)) { | 442 | if (isBookmarkFolded_SidebarWidget_(d, bm)) { |
@@ -439,27 +455,21 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
439 | } | 455 | } |
440 | set_String(&item->url, &bm->url); | 456 | set_String(&item->url, &bm->url); |
441 | set_String(&item->label, &bm->title); | 457 | set_String(&item->label, &bm->title); |
442 | /* Icons for special tags. */ { | 458 | /* Icons for special behaviors. */ { |
443 | iRegExpMatch m; | 459 | if (bm->flags & subscribed_BookmarkFlag) { |
444 | init_RegExpMatch(&m); | ||
445 | if (matchString_RegExp(subTag, &bm->tags, &m)) { | ||
446 | appendChar_String(&item->meta, 0x2605); | 460 | appendChar_String(&item->meta, 0x2605); |
447 | } | 461 | } |
448 | init_RegExpMatch(&m); | 462 | if (bm->flags & homepage_BookmarkFlag) { |
449 | if (matchString_RegExp(homeTag, &bm->tags, &m)) { | ||
450 | appendChar_String(&item->meta, 0x1f3e0); | 463 | appendChar_String(&item->meta, 0x1f3e0); |
451 | } | 464 | } |
452 | init_RegExpMatch(&m); | 465 | if (bm->flags & remote_BookmarkFlag) { |
453 | if (matchString_RegExp(remoteTag, &bm->tags, &m)) { | ||
454 | item->listItem.isDraggable = iFalse; | 466 | item->listItem.isDraggable = iFalse; |
455 | } | 467 | } |
456 | init_RegExpMatch(&m); | 468 | if (bm->flags & remoteSource_BookmarkFlag) { |
457 | if (matchString_RegExp(remoteSourceTag, &bm->tags, &m)) { | ||
458 | appendChar_String(&item->meta, 0x2913); | 469 | appendChar_String(&item->meta, 0x2913); |
459 | item->isBold = iTrue; | 470 | item->isBold = iTrue; |
460 | } | 471 | } |
461 | init_RegExpMatch(&m); | 472 | if (bm->flags & linkSplit_BookmarkFlag) { |
462 | if (matchString_RegExp(linkSplitTag, &bm->tags, &m)) { | ||
463 | appendChar_String(&item->meta, 0x25e7); | 473 | appendChar_String(&item->meta, 0x25e7); |
464 | } | 474 | } |
465 | } | 475 | } |
@@ -495,6 +505,14 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
495 | { "---", 0, 0, NULL }, | 505 | { "---", 0, 0, NULL }, |
496 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, | 506 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, |
497 | 6); | 507 | 6); |
508 | if (isMobile) { | ||
509 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", | ||
510 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); | ||
511 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | ||
512 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, | ||
513 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", | ||
514 | "sidebar.bookmarks.edit", 0); | ||
515 | } | ||
498 | break; | 516 | break; |
499 | } | 517 | } |
500 | case history_SidebarMode: { | 518 | case history_SidebarMode: { |
@@ -554,49 +572,15 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
554 | (iMenuItem[]){ | 572 | (iMenuItem[]){ |
555 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, | 573 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, |
556 | }, 1); | 574 | }, 1); |
575 | if (isMobile) { | ||
576 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | ||
577 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, "${sidebar.action.history.clear}", | ||
578 | "history.clear confirm:1", 0); | ||
579 | } | ||
557 | break; | 580 | break; |
558 | } | 581 | } |
559 | case identities_SidebarMode: { | 582 | case identities_SidebarMode: { |
560 | const iString *tabUrl = url_DocumentWidget(document_App()); | 583 | isEmpty = !updateItems_CertListWidget(d->certList); |
561 | const iRangecc tabHost = urlHost_String(tabUrl); | ||
562 | isEmpty = iTrue; | ||
563 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { | ||
564 | const iGmIdentity *ident = i.ptr; | ||
565 | iSidebarItem *item = new_SidebarItem(); | ||
566 | item->id = (uint32_t) index_PtrArrayConstIterator(&i); | ||
567 | item->icon = 0x1f464; /* person */ | ||
568 | set_String(&item->label, name_GmIdentity(ident)); | ||
569 | iDate until; | ||
570 | validUntil_TlsCertificate(ident->cert, &until); | ||
571 | const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); | ||
572 | format_String(&item->meta, | ||
573 | "%s", | ||
574 | isActive ? cstr_Lang("ident.using") | ||
575 | : isUsed_GmIdentity(ident) | ||
576 | ? formatCStrs_Lang("ident.usedonurls.n", size_StringSet(ident->useUrls)) | ||
577 | : cstr_Lang("ident.notused")); | ||
578 | const char *expiry = | ||
579 | ident->flags & temporary_GmIdentityFlag | ||
580 | ? cstr_Lang("ident.temporary") | ||
581 | : cstrCollect_String(format_Date(&until, cstr_Lang("ident.expiry"))); | ||
582 | if (isEmpty_String(&ident->notes)) { | ||
583 | appendFormat_String(&item->meta, "\n%s", expiry); | ||
584 | } | ||
585 | else { | ||
586 | appendFormat_String(&item->meta, | ||
587 | " \u2014 %s\n%s%s", | ||
588 | expiry, | ||
589 | escape_Color(uiHeading_ColorId), | ||
590 | cstr_String(&ident->notes)); | ||
591 | } | ||
592 | item->listItem.isSelected = isActive; | ||
593 | if (isUsedOnDomain_GmIdentity(ident, tabHost)) { | ||
594 | item->indent = 1; /* will be highlighted */ | ||
595 | } | ||
596 | addItem_ListWidget(d->list, item); | ||
597 | iRelease(item); | ||
598 | isEmpty = iFalse; | ||
599 | } | ||
600 | /* Actions. */ | 584 | /* Actions. */ |
601 | if (!isEmpty) { | 585 | if (!isEmpty) { |
602 | addActionButton_SidebarWidget_(d, add_Icon " ${sidebar.action.ident.new}", "ident.new", 0); | 586 | addActionButton_SidebarWidget_(d, add_Icon " ${sidebar.action.ident.new}", "ident.new", 0); |
@@ -607,16 +591,27 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
607 | default: | 591 | default: |
608 | break; | 592 | break; |
609 | } | 593 | } |
610 | scrollOffset_ListWidget(d->list, 0); | 594 | setFlags_Widget(as_Widget(d->list), hidden_WidgetFlag, d->mode == identities_SidebarMode); |
611 | updateVisible_ListWidget(d->list); | 595 | setFlags_Widget(as_Widget(d->certList), hidden_WidgetFlag, d->mode != identities_SidebarMode); |
612 | invalidate_ListWidget(d->list); | 596 | scrollOffset_ListWidget(list_SidebarWidget_(d), 0); |
597 | updateVisible_ListWidget(list_SidebarWidget_(d)); | ||
598 | invalidate_ListWidget(list_SidebarWidget_(d)); | ||
613 | /* Content for a blank tab. */ | 599 | /* Content for a blank tab. */ |
614 | if (isEmpty) { | 600 | if (isEmpty) { |
615 | if (d->mode == feeds_SidebarMode) { | 601 | if (d->mode == feeds_SidebarMode) { |
616 | iWidget *div = makeVDiv_Widget(); | 602 | iWidget *div = makeVDiv_Widget(); |
617 | setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI); | 603 | //setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI); |
604 | arrange_Widget(d->actions); | ||
605 | setPadding_Widget(div, 0, 0, 0, height_Widget(d->actions)); | ||
618 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ | 606 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ |
607 | if (d->feedsMode == all_FeedsMode) { | ||
619 | addChild_Widget(div, iClob(new_LabelWidget("${menu.feeds.refresh}", "feeds.refresh"))); | 608 | addChild_Widget(div, iClob(new_LabelWidget("${menu.feeds.refresh}", "feeds.refresh"))); |
609 | } | ||
610 | else { | ||
611 | iLabelWidget *msg = addChildFlags_Widget(div, iClob(new_LabelWidget("${sidebar.empty.unread}", NULL)), | ||
612 | frameless_WidgetFlag); | ||
613 | setFont_LabelWidget(msg, uiLabelLarge_FontId); | ||
614 | } | ||
620 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ | 615 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ |
621 | addChild_Widget(d->blank, iClob(div)); | 616 | addChild_Widget(d->blank, iClob(div)); |
622 | } | 617 | } |
@@ -659,7 +654,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
659 | #endif | 654 | #endif |
660 | arrange_Widget(d->actions); | 655 | arrange_Widget(d->actions); |
661 | arrange_Widget(as_Widget(d)); | 656 | arrange_Widget(as_Widget(d)); |
662 | updateMouseHover_ListWidget(d->list); | 657 | updateMouseHover_ListWidget(list_SidebarWidget_(d)); |
663 | } | 658 | } |
664 | 659 | ||
665 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | 660 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { |
@@ -678,35 +673,59 @@ static size_t findItem_SidebarWidget_(const iSidebarWidget *d, int id) { | |||
678 | } | 673 | } |
679 | 674 | ||
680 | static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { | 675 | static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { |
676 | const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f }; | ||
681 | if (d->list) { | 677 | if (d->list) { |
682 | const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f }; | ||
683 | setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0])); | 678 | setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0])); |
684 | } | 679 | } |
680 | if (d->certList) { | ||
681 | updateItemHeight_CertListWidget(d->certList); | ||
682 | } | ||
685 | } | 683 | } |
686 | 684 | ||
687 | iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | 685 | iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { |
688 | if (d->mode == mode) { | 686 | if (d->mode == mode) { |
689 | return iFalse; | 687 | return iFalse; |
690 | } | 688 | } |
689 | if (mode == identities_SidebarMode && deviceType_App() != desktop_AppDeviceType) { | ||
690 | return iFalse; /* Identities are in Settings. */ | ||
691 | } | ||
691 | if (d->mode >= 0 && d->mode < max_SidebarMode) { | 692 | if (d->mode >= 0 && d->mode < max_SidebarMode) { |
692 | d->modeScroll[d->mode] = scrollPos_ListWidget(d->list); /* saved for later */ | 693 | d->modeScroll[d->mode] = scrollPos_ListWidget(list_SidebarWidget_(d)); /* saved for later */ |
693 | } | 694 | } |
694 | d->mode = mode; | 695 | d->mode = mode; |
695 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | 696 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { |
696 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | 697 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); |
697 | } | 698 | } |
698 | setBackgroundColor_Widget(as_Widget(d->list), | 699 | setBackgroundColor_Widget(as_Widget(list_SidebarWidget_(d)), |
699 | d->mode == documentOutline_SidebarMode ? tmBannerBackground_ColorId | 700 | d->mode == documentOutline_SidebarMode ? tmBannerBackground_ColorId |
700 | : uiBackgroundSidebar_ColorId); | 701 | : uiBackgroundSidebar_ColorId); |
701 | updateItemHeight_SidebarWidget_(d); | 702 | updateItemHeight_SidebarWidget_(d); |
703 | if (deviceType_App() != desktop_AppDeviceType && mode != bookmarks_SidebarMode) { | ||
704 | setMobileEditMode_SidebarWidget_(d, iFalse); | ||
705 | } | ||
702 | /* Restore previous scroll position. */ | 706 | /* Restore previous scroll position. */ |
703 | setScrollPos_ListWidget(d->list, d->modeScroll[mode]); | 707 | setScrollPos_ListWidget(list_SidebarWidget_(d), d->modeScroll[mode]); |
708 | /* Title of the mobile sliding sheet. */ | ||
709 | iLabelWidget *sheetTitle = findChild_Widget(&d->widget, "sidebar.title"); | ||
710 | if (sheetTitle) { | ||
711 | iString title; | ||
712 | initCStr_String(&title, normalModeLabels_[d->mode]); | ||
713 | removeIconPrefix_String(&title); | ||
714 | setText_LabelWidget(sheetTitle, &title); | ||
715 | deinit_String(&title); | ||
716 | } | ||
704 | return iTrue; | 717 | return iTrue; |
705 | } | 718 | } |
706 | 719 | ||
707 | void setClosedFolders_SidebarWidget(iSidebarWidget *d, const iIntSet *closedFolders) { | 720 | void setClosedFolders_SidebarWidget(iSidebarWidget *d, const iIntSet *closedFolders) { |
721 | if (d) { | ||
708 | delete_IntSet(d->closedFolders); | 722 | delete_IntSet(d->closedFolders); |
709 | d->closedFolders = copy_IntSet(closedFolders); | 723 | d->closedFolders = copy_IntSet(closedFolders); |
724 | } | ||
725 | } | ||
726 | |||
727 | void setMidHeight_SidebarWidget(iSidebarWidget *d, int midHeight) { | ||
728 | d->midHeight = midHeight; | ||
710 | } | 729 | } |
711 | 730 | ||
712 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { | 731 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { |
@@ -722,25 +741,9 @@ float width_SidebarWidget(const iSidebarWidget *d) { | |||
722 | } | 741 | } |
723 | 742 | ||
724 | const iIntSet *closedFolders_SidebarWidget(const iSidebarWidget *d) { | 743 | const iIntSet *closedFolders_SidebarWidget(const iSidebarWidget *d) { |
725 | return d->closedFolders; | 744 | return d ? d->closedFolders : collect_IntSet(new_IntSet()); |
726 | } | 745 | } |
727 | 746 | ||
728 | static const char *normalModeLabels_[max_SidebarMode] = { | ||
729 | book_Icon " ${sidebar.bookmarks}", | ||
730 | star_Icon " ${sidebar.feeds}", | ||
731 | clock_Icon " ${sidebar.history}", | ||
732 | person_Icon " ${sidebar.identities}", | ||
733 | page_Icon " ${sidebar.outline}", | ||
734 | }; | ||
735 | |||
736 | static const char *tightModeLabels_[max_SidebarMode] = { | ||
737 | book_Icon, | ||
738 | star_Icon, | ||
739 | clock_Icon, | ||
740 | person_Icon, | ||
741 | page_Icon, | ||
742 | }; | ||
743 | |||
744 | const char *icon_SidebarMode(enum iSidebarMode mode) { | 747 | const char *icon_SidebarMode(enum iSidebarMode mode) { |
745 | return tightModeLabels_[mode]; | 748 | return tightModeLabels_[mode]; |
746 | } | 749 | } |
@@ -762,6 +765,20 @@ static void updateMetrics_SidebarWidget_(iSidebarWidget *d) { | |||
762 | updateItemHeight_SidebarWidget_(d); | 765 | updateItemHeight_SidebarWidget_(d); |
763 | } | 766 | } |
764 | 767 | ||
768 | static void updateSlidingSheetHeight_SidebarWidget_(iSidebarWidget *sidebar, iRoot *root) { | ||
769 | if (!isPortraitPhone_App() || !isVisible_Widget(sidebar)) return; | ||
770 | iWidget *d = as_Widget(sidebar); | ||
771 | const int oldSize = d->rect.size.y; | ||
772 | const int newSize = bottom_Rect(safeRect_Root(d->root)) - top_Rect(bounds_Widget(d)); | ||
773 | if (oldSize != newSize) { | ||
774 | d->rect.size.y = newSize; | ||
775 | arrange_Widget(d); | ||
776 | } | ||
777 | // printf("[%p] %u: %d animating %d\n", d, window_Widget(d)->frameTime, | ||
778 | // (flags_Widget(d) & visualOffset_WidgetFlag) != 0, | ||
779 | // newSize); | ||
780 | } | ||
781 | |||
765 | void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | 782 | void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { |
766 | iWidget *w = as_Widget(d); | 783 | iWidget *w = as_Widget(d); |
767 | init_Widget(w); | 784 | init_Widget(w); |
@@ -778,6 +795,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
778 | d->side = side; | 795 | d->side = side; |
779 | d->mode = -1; | 796 | d->mode = -1; |
780 | d->feedsMode = all_FeedsMode; | 797 | d->feedsMode = all_FeedsMode; |
798 | d->midHeight = 0; | ||
799 | d->isEditing = iFalse; | ||
781 | d->numUnreadEntries = 0; | 800 | d->numUnreadEntries = 0; |
782 | d->buttonFont = uiLabel_FontId; /* wiil be changed later */ | 801 | d->buttonFont = uiLabel_FontId; /* wiil be changed later */ |
783 | d->itemFonts[0] = uiContent_FontId; | 802 | d->itemFonts[0] = uiContent_FontId; |
@@ -795,18 +814,38 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
795 | iWidget *vdiv = makeVDiv_Widget(); | 814 | iWidget *vdiv = makeVDiv_Widget(); |
796 | addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag); | 815 | addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag); |
797 | iZap(d->modeButtons); | 816 | iZap(d->modeButtons); |
798 | d->resizer = NULL; | 817 | d->resizer = NULL; |
799 | d->list = NULL; | 818 | d->list = NULL; |
800 | d->actions = NULL; | 819 | d->certList = NULL; |
820 | d->actions = NULL; | ||
801 | d->closedFolders = new_IntSet(); | 821 | d->closedFolders = new_IntSet(); |
802 | /* On a phone, the right sidebar is used exclusively for Identities. */ | 822 | /* On a phone, the right sidebar is not used. */ |
803 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; | 823 | const iBool isPhone = (deviceType_App() == phone_AppDeviceType); |
804 | if (!isPhone || d->side == left_SidebarSide) { | 824 | if (isPhone) { |
825 | iLabelWidget *sheetTitle = | ||
826 | addChildFlags_Widget(vdiv, | ||
827 | iClob(new_LabelWidget("", NULL)), | ||
828 | collapse_WidgetFlag | | ||
829 | extraPadding_WidgetFlag | frameless_WidgetFlag); | ||
830 | setBackgroundColor_Widget(as_Widget(sheetTitle), uiBackground_ColorId); | ||
831 | iLabelWidget *closeButton = addChildFlags_Widget(as_Widget(sheetTitle), | ||
832 | iClob(new_LabelWidget(uiTextAction_ColorEscape "${sidebar.close}", "sidebar.toggle")), | ||
833 | extraPadding_WidgetFlag | frameless_WidgetFlag | | ||
834 | alignRight_WidgetFlag | moveToParentRightEdge_WidgetFlag); | ||
835 | as_Widget(sheetTitle)->flags2 |= slidingSheetDraggable_WidgetFlag2; /* phone */ | ||
836 | as_Widget(closeButton)->flags2 |= slidingSheetDraggable_WidgetFlag2; /* phone */ | ||
837 | setId_Widget(as_Widget(sheetTitle), "sidebar.title"); | ||
838 | setId_Widget(as_Widget(closeButton), "sidebar.close"); | ||
839 | setFont_LabelWidget(sheetTitle, uiLabelBig_FontId); | ||
840 | setFont_LabelWidget(closeButton, uiLabelBigBold_FontId); | ||
841 | iConnect(Root, get_Root(), visualOffsetsChanged, d, updateSlidingSheetHeight_SidebarWidget_); | ||
842 | } | ||
805 | iWidget *buttons = new_Widget(); | 843 | iWidget *buttons = new_Widget(); |
806 | setId_Widget(buttons, "buttons"); | 844 | setId_Widget(buttons, "buttons"); |
807 | setDrawBufferEnabled_Widget(buttons, iTrue); | 845 | setDrawBufferEnabled_Widget(buttons, iTrue); |
808 | for (int i = 0; i < max_SidebarMode; i++) { | 846 | for (int i = 0; i < max_SidebarMode; i++) { |
809 | if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) { | 847 | if (i == identities_SidebarMode && deviceType_App() != desktop_AppDeviceType) { |
848 | /* On mobile, identities are managed via Settings. */ | ||
810 | continue; | 849 | continue; |
811 | } | 850 | } |
812 | d->modeButtons[i] = addChildFlags_Widget( | 851 | d->modeButtons[i] = addChildFlags_Widget( |
@@ -815,51 +854,54 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
815 | tightModeLabels_[i], | 854 | tightModeLabels_[i], |
816 | format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), | 855 | format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), |
817 | frameless_WidgetFlag | noBackground_WidgetFlag); | 856 | frameless_WidgetFlag | noBackground_WidgetFlag); |
857 | as_Widget(d->modeButtons[i])->flags2 |= slidingSheetDraggable_WidgetFlag2; /* phone */ | ||
818 | } | 858 | } |
819 | setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId); | 859 | setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId); |
820 | addChildFlags_Widget(vdiv, | 860 | addChildFlags_Widget(vdiv, |
821 | iClob(buttons), | 861 | iClob(buttons), |
822 | arrangeHorizontal_WidgetFlag | | 862 | arrangeHorizontal_WidgetFlag | resizeWidthOfChildren_WidgetFlag | |
823 | resizeWidthOfChildren_WidgetFlag | | 863 | arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag); |
824 | arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag); // | | ||
825 | // drawBackgroundToHorizontalSafeArea_WidgetFlag); | ||
826 | setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); | 864 | setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); |
827 | } | ||
828 | else { | ||
829 | iLabelWidget *heading = new_LabelWidget(person_Icon " ${sidebar.identities}", NULL); | ||
830 | checkIcon_LabelWidget(heading); | ||
831 | setBackgroundColor_Widget(as_Widget(heading), uiBackgroundSidebar_ColorId); | ||
832 | setTextColor_LabelWidget(heading, uiTextSelected_ColorId); | ||
833 | setFont_LabelWidget(addChildFlags_Widget(vdiv, iClob(heading), borderTop_WidgetFlag | | ||
834 | alignLeft_WidgetFlag | frameless_WidgetFlag | | ||
835 | drawBackgroundToHorizontalSafeArea_WidgetFlag), | ||
836 | uiLabelLargeBold_FontId); | ||
837 | } | ||
838 | iWidget *content = new_Widget(); | 865 | iWidget *content = new_Widget(); |
839 | setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); | 866 | setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); |
840 | iWidget *listAndActions = makeVDiv_Widget(); | 867 | iWidget *listAndActions = makeVDiv_Widget(); |
841 | addChild_Widget(content, iClob(listAndActions)); | 868 | addChild_Widget(content, iClob(listAndActions)); |
869 | iWidget *listArea = new_Widget(); | ||
870 | setFlags_Widget(listArea, resizeChildren_WidgetFlag, iTrue); | ||
842 | d->list = new_ListWidget(); | 871 | d->list = new_ListWidget(); |
843 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); | 872 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); |
873 | addChild_Widget(listArea, iClob(d->list)); | ||
874 | if (!isPhone) { | ||
875 | d->certList = new_CertListWidget(); | ||
876 | setPadding_Widget(as_Widget(d->certList), 0, gap_UI, 0, gap_UI); | ||
877 | addChild_Widget(listArea, iClob(d->certList)); | ||
878 | } | ||
844 | addChildFlags_Widget(listAndActions, | 879 | addChildFlags_Widget(listAndActions, |
845 | iClob(d->list), | 880 | iClob(listArea), |
846 | expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); | 881 | expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); |
847 | setId_Widget(addChildPosFlags_Widget(listAndActions, | 882 | setId_Widget(addChildPosFlags_Widget(listAndActions, |
848 | iClob(d->actions = new_Widget()), | 883 | iClob(d->actions = new_Widget()), |
849 | isPhone ? front_WidgetAddPos : back_WidgetAddPos, | 884 | /*isPhone ? front_WidgetAddPos :*/ back_WidgetAddPos, |
850 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | | 885 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | |
851 | resizeWidthOfChildren_WidgetFlag), // | | 886 | resizeWidthOfChildren_WidgetFlag), // | |
852 | // drawBackgroundToHorizontalSafeArea_WidgetFlag), | 887 | // drawBackgroundToHorizontalSafeArea_WidgetFlag), |
853 | "actions"); | 888 | "actions"); |
889 | if (deviceType_App() != desktop_AppDeviceType) { | ||
890 | setFlags_Widget(findChild_Widget(w, "sidebar.title"), borderTop_WidgetFlag, iTrue); | ||
891 | setFlags_Widget(d->actions, drawBackgroundToBottom_WidgetFlag, iTrue); | ||
892 | setBackgroundColor_Widget(d->actions, uiBackground_ColorId); | ||
893 | } | ||
894 | else { | ||
854 | setBackgroundColor_Widget(d->actions, uiBackgroundSidebar_ColorId); | 895 | setBackgroundColor_Widget(d->actions, uiBackgroundSidebar_ColorId); |
896 | } | ||
855 | d->contextItem = NULL; | 897 | d->contextItem = NULL; |
856 | d->contextIndex = iInvalidPos; | 898 | d->contextIndex = iInvalidPos; |
857 | d->blank = new_Widget(); | 899 | d->blank = new_Widget(); |
858 | addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); | 900 | addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); |
859 | addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); | 901 | addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); |
860 | setMode_SidebarWidget(d, | 902 | setMode_SidebarWidget(d, |
861 | deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide ? | 903 | /*deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide ? |
862 | identities_SidebarMode : bookmarks_SidebarMode); | 904 | identities_SidebarMode :*/ bookmarks_SidebarMode); |
863 | d->resizer = | 905 | d->resizer = |
864 | addChildFlags_Widget(w, | 906 | addChildFlags_Widget(w, |
865 | iClob(new_Widget()), | 907 | iClob(new_Widget()), |
@@ -867,7 +909,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
867 | resizeToParentHeight_WidgetFlag | | 909 | resizeToParentHeight_WidgetFlag | |
868 | (side == left_SidebarSide ? moveToParentRightEdge_WidgetFlag | 910 | (side == left_SidebarSide ? moveToParentRightEdge_WidgetFlag |
869 | : moveToParentLeftEdge_WidgetFlag)); | 911 | : moveToParentLeftEdge_WidgetFlag)); |
870 | if (deviceType_App() == phone_AppDeviceType) { | 912 | if (deviceType_App() != desktop_AppDeviceType) { |
871 | setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); | 913 | setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); |
872 | } | 914 | } |
873 | setId_Widget(d->resizer, side == left_SidebarSide ? "sidebar.grab" : "sidebar2.grab"); | 915 | setId_Widget(d->resizer, side == left_SidebarSide ? "sidebar.grab" : "sidebar2.grab"); |
@@ -902,16 +944,16 @@ iBool setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { | |||
902 | 944 | ||
903 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | 945 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { |
904 | if (d->mode == identities_SidebarMode) { | 946 | if (d->mode == identities_SidebarMode) { |
905 | const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list); | 947 | return constHoverIdentity_CertListWidget(d->certList); |
906 | if (hoverItem) { | ||
907 | return identity_GmCerts(certs_App(), hoverItem->id); | ||
908 | } | 948 | } |
909 | } | ||
910 | return NULL; | 949 | return NULL; |
911 | } | 950 | } |
912 | 951 | ||
913 | static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | 952 | static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { |
914 | return iConstCast(iGmIdentity *, constHoverIdentity_SidebarWidget_(d)); | 953 | if (d->mode == identities_SidebarMode) { |
954 | return hoverIdentity_CertListWidget(d->certList); | ||
955 | } | ||
956 | return NULL; | ||
915 | } | 957 | } |
916 | 958 | ||
917 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, size_t itemIndex) { | 959 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, size_t itemIndex) { |
@@ -923,7 +965,6 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
923 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); | 965 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); |
924 | postCommandf_App("document.goto loc:%p", head->text.start); | 966 | postCommandf_App("document.goto loc:%p", head->text.start); |
925 | dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); | 967 | dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); |
926 | setOpenedFromSidebar_DocumentWidget(document_App(), iTrue); | ||
927 | } | 968 | } |
928 | break; | 969 | break; |
929 | } | 970 | } |
@@ -945,6 +986,12 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
945 | updateItems_SidebarWidget_(d); | 986 | updateItems_SidebarWidget_(d); |
946 | break; | 987 | break; |
947 | } | 988 | } |
989 | if (d->isEditing) { | ||
990 | d->contextItem = item; | ||
991 | d->contextIndex = itemIndex; | ||
992 | postCommand_Widget(d, "bookmark.edit"); | ||
993 | break; | ||
994 | } | ||
948 | /* fall through */ | 995 | /* fall through */ |
949 | case history_SidebarMode: { | 996 | case history_SidebarMode: { |
950 | if (!isEmpty_String(&item->url)) { | 997 | if (!isEmpty_String(&item->url)) { |
@@ -954,23 +1001,6 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
954 | } | 1001 | } |
955 | break; | 1002 | break; |
956 | } | 1003 | } |
957 | case identities_SidebarMode: { | ||
958 | d->contextItem = item; | ||
959 | if (d->contextIndex != iInvalidPos) { | ||
960 | invalidateItem_ListWidget(d->list, d->contextIndex); | ||
961 | } | ||
962 | d->contextIndex = itemIndex; | ||
963 | if (itemIndex < numItems_ListWidget(d->list)) { | ||
964 | updateContextMenu_SidebarWidget_(d); | ||
965 | arrange_Widget(d->menu); | ||
966 | openMenu_Widget(d->menu, | ||
967 | d->side == left_SidebarSide | ||
968 | ? topRight_Rect(itemRect_ListWidget(d->list, itemIndex)) | ||
969 | : addX_I2(topLeft_Rect(itemRect_ListWidget(d->list, itemIndex)), | ||
970 | -width_Widget(d->menu))); | ||
971 | } | ||
972 | break; | ||
973 | } | ||
974 | default: | 1004 | default: |
975 | break; | 1005 | break; |
976 | } | 1006 | } |
@@ -990,7 +1020,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { | |||
990 | // updateMetrics_SidebarWidget_(d); | 1020 | // updateMetrics_SidebarWidget_(d); |
991 | updateItemHeight_SidebarWidget_(d); | 1021 | updateItemHeight_SidebarWidget_(d); |
992 | } | 1022 | } |
993 | setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelBig_FontId : uiLabel_FontId); | 1023 | setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelMedium_FontId : uiLabel_FontId); |
994 | } | 1024 | } |
995 | const iBool isTight = | 1025 | const iBool isTight = |
996 | (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); | 1026 | (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); |
@@ -1018,6 +1048,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { | |||
1018 | } | 1048 | } |
1019 | 1049 | ||
1020 | void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { | 1050 | void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { |
1051 | if (!d) return; | ||
1021 | iWidget *w = as_Widget(d); | 1052 | iWidget *w = as_Widget(d); |
1022 | const iBool isFixedWidth = deviceType_App() == phone_AppDeviceType; | 1053 | const iBool isFixedWidth = deviceType_App() == phone_AppDeviceType; |
1023 | int width = widthAsGaps * gap_UI; /* in pixels */ | 1054 | int width = widthAsGaps * gap_UI; /* in pixels */ |
@@ -1056,19 +1087,16 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c | |||
1056 | set_String(&bm->url, url); | 1087 | set_String(&bm->url, url); |
1057 | set_String(&bm->tags, tags); | 1088 | set_String(&bm->tags, tags); |
1058 | if (isEmpty_String(icon)) { | 1089 | if (isEmpty_String(icon)) { |
1059 | removeTag_Bookmark(bm, userIcon_BookmarkTag); | 1090 | bm->flags &= ~userIcon_BookmarkFlag; |
1060 | bm->icon = 0; | 1091 | bm->icon = 0; |
1061 | } | 1092 | } |
1062 | else { | 1093 | else { |
1063 | addTagIfMissing_Bookmark(bm, userIcon_BookmarkTag); | 1094 | bm->flags |= userIcon_BookmarkFlag; |
1064 | bm->icon = first_String(icon); | 1095 | bm->icon = first_String(icon); |
1065 | } | 1096 | } |
1066 | addOrRemoveTag_Bookmark(bm, homepage_BookmarkTag, | 1097 | iChangeFlags(bm->flags, homepage_BookmarkFlag, isSelected_Widget(findChild_Widget(editor, "bmed.tag.home"))); |
1067 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.home"))); | 1098 | iChangeFlags(bm->flags, remoteSource_BookmarkFlag, isSelected_Widget(findChild_Widget(editor, "bmed.tag.remote"))); |
1068 | addOrRemoveTag_Bookmark(bm, remoteSource_BookmarkTag, | 1099 | iChangeFlags(bm->flags, linkSplit_BookmarkFlag, isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))); |
1069 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.remote"))); | ||
1070 | addOrRemoveTag_Bookmark(bm, linkSplit_BookmarkTag, | ||
1071 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))); | ||
1072 | } | 1100 | } |
1073 | const iBookmark *folder = userData_Object(findChild_Widget(editor, "bmed.folder")); | 1101 | const iBookmark *folder = userData_Object(findChild_Widget(editor, "bmed.folder")); |
1074 | if (!folder || !hasParent_Bookmark(folder, id_Bookmark(bm))) { | 1102 | if (!folder || !hasParent_Bookmark(folder, id_Bookmark(bm))) { |
@@ -1083,6 +1111,36 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c | |||
1083 | return iFalse; | 1111 | return iFalse; |
1084 | } | 1112 | } |
1085 | 1113 | ||
1114 | enum iSlidingSheetPos { | ||
1115 | top_SlidingSheetPos, | ||
1116 | middle_SlidingSheetPos, | ||
1117 | bottom_SlidingSheetPos, | ||
1118 | }; | ||
1119 | |||
1120 | static void setSlidingSheetPos_SidebarWidget_(iSidebarWidget *d, enum iSlidingSheetPos slide) { | ||
1121 | iWidget *w = as_Widget(d); | ||
1122 | const int pos = w->rect.pos.y; | ||
1123 | const iRect safeRect = safeRect_Root(w->root); | ||
1124 | if (slide == top_SlidingSheetPos) { | ||
1125 | w->rect.pos.y = top_Rect(safeRect); | ||
1126 | w->rect.size.y = height_Rect(safeRect); | ||
1127 | setVisualOffset_Widget(w, pos - w->rect.pos.y, 0, 0); | ||
1128 | setVisualOffset_Widget(w, 0, 200, easeOut_AnimFlag | softer_AnimFlag); | ||
1129 | setScrollMode_ListWidget(d->list, disabledAtTopUpwards_ScrollMode); | ||
1130 | } | ||
1131 | else if (slide == bottom_SlidingSheetPos) { | ||
1132 | postCommand_Widget(w, "sidebar.toggle"); | ||
1133 | } | ||
1134 | else { | ||
1135 | w->rect.size.y = d->midHeight; | ||
1136 | w->rect.pos.y = height_Rect(safeRect) - w->rect.size.y; | ||
1137 | setVisualOffset_Widget(w, pos - w->rect.pos.y, 0, 0); | ||
1138 | setVisualOffset_Widget(w, 0, 200, easeOut_AnimFlag | softer_AnimFlag); | ||
1139 | setScrollMode_ListWidget(d->list, disabledAtTopBothDirections_ScrollMode); | ||
1140 | } | ||
1141 | // animateSlidingSheetHeight_SidebarWidget_(d); | ||
1142 | } | ||
1143 | |||
1086 | static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *cmd) { | 1144 | static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *cmd) { |
1087 | iWidget *w = as_Widget(d); | 1145 | iWidget *w = as_Widget(d); |
1088 | if (equal_Command(cmd, "width")) { | 1146 | if (equal_Command(cmd, "width")) { |
@@ -1112,13 +1170,18 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1112 | argLabel_Command(cmd, "noanim") == 0 && | 1170 | argLabel_Command(cmd, "noanim") == 0 && |
1113 | (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType); | 1171 | (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType); |
1114 | int visX = 0; | 1172 | int visX = 0; |
1173 | int visY = 0; | ||
1115 | if (isVisible_Widget(w)) { | 1174 | if (isVisible_Widget(w)) { |
1116 | visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); | 1175 | visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); |
1176 | visY = top_Rect(bounds_Widget(w)) - top_Rect(w->root->widget->rect); | ||
1117 | } | 1177 | } |
1118 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); | 1178 | const iBool isHiding = isVisible_Widget(w); |
1179 | setFlags_Widget(w, hidden_WidgetFlag, isHiding); | ||
1119 | /* Safe area inset for mobile. */ | 1180 | /* Safe area inset for mobile. */ |
1120 | const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0); | 1181 | const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0); |
1121 | if (isVisible_Widget(w)) { | 1182 | const int animFlags = easeOut_AnimFlag | softer_AnimFlag; |
1183 | if (!isPortraitPhone_App()) { | ||
1184 | if (!isHiding) { | ||
1122 | setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); | 1185 | setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); |
1123 | w->rect.size.x = d->widthAsGaps * gap_UI; | 1186 | w->rect.size.x = d->widthAsGaps * gap_UI; |
1124 | invalidate_ListWidget(d->list); | 1187 | invalidate_ListWidget(d->list); |
@@ -1126,7 +1189,7 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1126 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); | 1189 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); |
1127 | setVisualOffset_Widget( | 1190 | setVisualOffset_Widget( |
1128 | w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0); | 1191 | w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0); |
1129 | setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); | 1192 | setVisualOffset_Widget(w, 0, 300, animFlags); |
1130 | } | 1193 | } |
1131 | } | 1194 | } |
1132 | else if (isAnimated) { | 1195 | else if (isAnimated) { |
@@ -1134,19 +1197,42 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1134 | if (d->side == right_SidebarSide) { | 1197 | if (d->side == right_SidebarSide) { |
1135 | setVisualOffset_Widget(w, visX, 0, 0); | 1198 | setVisualOffset_Widget(w, visX, 0, 0); |
1136 | setVisualOffset_Widget( | 1199 | setVisualOffset_Widget( |
1137 | w, visX + w->rect.size.x + safePad, 300, easeOut_AnimFlag | softer_AnimFlag); | 1200 | w, visX + w->rect.size.x + safePad, 300, animFlags); |
1138 | } | 1201 | } |
1139 | else { | 1202 | else { |
1140 | setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); | 1203 | setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); |
1141 | setVisualOffset_Widget( | 1204 | setVisualOffset_Widget( |
1142 | w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag); | 1205 | w, -w->rect.size.x - safePad, 300, animFlags); |
1206 | } | ||
1143 | } | 1207 | } |
1208 | setScrollMode_ListWidget(d->list, normal_ScrollMode); | ||
1209 | } | ||
1210 | else { | ||
1211 | /* Portrait phone sidebar works differently: it slides up from the bottom. */ | ||
1212 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iFalse); | ||
1213 | if (!isHiding) { | ||
1214 | invalidate_ListWidget(d->list); | ||
1215 | w->rect.pos.y = height_Rect(safeRect_Root(w->root)) - d->midHeight; | ||
1216 | setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 0, 0); | ||
1217 | setVisualOffset_Widget(w, 0, 300, animFlags); | ||
1218 | //animateSlidingSheetHeight_SidebarWidget_(d); | ||
1219 | setScrollMode_ListWidget(d->list, disabledAtTopBothDirections_ScrollMode); | ||
1220 | } | ||
1221 | else { | ||
1222 | setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 300, animFlags); | ||
1223 | if (d->isEditing) { | ||
1224 | setMobileEditMode_SidebarWidget_(d, iFalse); | ||
1225 | } | ||
1226 | } | ||
1227 | showToolbar_Root(w->root, isHiding); | ||
1144 | } | 1228 | } |
1145 | updateToolbarColors_Root(w->root); | 1229 | updateToolbarColors_Root(w->root); |
1146 | arrange_Widget(w->parent); | 1230 | arrange_Widget(w->parent); |
1147 | /* BUG: Rearranging because the arrange above didn't fully resolve the height. */ | 1231 | /* BUG: Rearranging because the arrange above didn't fully resolve the height. */ |
1148 | arrange_Widget(w); | 1232 | arrange_Widget(w); |
1233 | if (!isPortraitPhone_App()) { | ||
1149 | updateSize_DocumentWidget(document_App()); | 1234 | updateSize_DocumentWidget(document_App()); |
1235 | } | ||
1150 | if (isVisible_Widget(w)) { | 1236 | if (isVisible_Widget(w)) { |
1151 | updateItems_SidebarWidget_(d); | 1237 | updateItems_SidebarWidget_(d); |
1152 | scrollOffset_ListWidget(d->list, 0); | 1238 | scrollOffset_ListWidget(d->list, 0); |
@@ -1154,6 +1240,10 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1154 | refresh_Widget(w->parent); | 1240 | refresh_Widget(w->parent); |
1155 | return iTrue; | 1241 | return iTrue; |
1156 | } | 1242 | } |
1243 | else if (equal_Command(cmd, "bookmarks.edit")) { | ||
1244 | setMobileEditMode_SidebarWidget_(d, !d->isEditing); | ||
1245 | invalidate_ListWidget(d->list); | ||
1246 | } | ||
1157 | return iFalse; | 1247 | return iFalse; |
1158 | } | 1248 | } |
1159 | 1249 | ||
@@ -1166,7 +1256,7 @@ static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t | |||
1166 | : dstIndex); | 1256 | : dstIndex); |
1167 | if (isLast && isBefore) isBefore = iFalse; | 1257 | if (isLast && isBefore) isBefore = iFalse; |
1168 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); | 1258 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); |
1169 | if (hasParent_Bookmark(dst, movingItem->id) || hasTag_Bookmark(dst, remote_BookmarkTag)) { | 1259 | if (hasParent_Bookmark(dst, movingItem->id) || dst->flags & remote_BookmarkFlag) { |
1170 | /* Can't move a folder inside itself, and remote bookmarks cannot be reordered. */ | 1260 | /* Can't move a folder inside itself, and remote bookmarks cannot be reordered. */ |
1171 | return; | 1261 | return; |
1172 | } | 1262 | } |
@@ -1190,20 +1280,41 @@ static void bookmarkMovedOntoFolder_SidebarWidget_(iSidebarWidget *d, size_t ind | |||
1190 | static size_t numBookmarks_(const iPtrArray *bmList) { | 1280 | static size_t numBookmarks_(const iPtrArray *bmList) { |
1191 | size_t num = 0; | 1281 | size_t num = 0; |
1192 | iConstForEach(PtrArray, i, bmList) { | 1282 | iConstForEach(PtrArray, i, bmList) { |
1193 | if (!isFolder_Bookmark(i.ptr) && !hasTag_Bookmark(i.ptr, remote_BookmarkTag)) { | 1283 | const iBookmark *bm = i.ptr; |
1284 | if (!isFolder_Bookmark(bm) && ~bm->flags & remote_BookmarkFlag) { | ||
1194 | num++; | 1285 | num++; |
1195 | } | 1286 | } |
1196 | } | 1287 | } |
1197 | return num; | 1288 | return num; |
1198 | } | 1289 | } |
1199 | 1290 | ||
1291 | static iRangei SlidingSheetMiddleRegion_SidebarWidget_(const iSidebarWidget *d) { | ||
1292 | const iWidget *w = constAs_Widget(d); | ||
1293 | const iRect safeRect = safeRect_Root(w->root); | ||
1294 | const int midY = bottom_Rect(safeRect) - d->midHeight; | ||
1295 | const int topHalf = (top_Rect(safeRect) + midY) / 2; | ||
1296 | const int bottomHalf = (bottom_Rect(safeRect) + midY * 2) / 3; | ||
1297 | return (iRangei){ topHalf, bottomHalf }; | ||
1298 | } | ||
1299 | |||
1300 | static void gotoNearestSlidingSheetPos_SidebarWidget_(iSidebarWidget *d) { | ||
1301 | const iRangei midRegion = SlidingSheetMiddleRegion_SidebarWidget_(d); | ||
1302 | const int pos = top_Rect(d->widget.rect); | ||
1303 | setSlidingSheetPos_SidebarWidget_(d, pos < midRegion.start | ||
1304 | ? top_SlidingSheetPos | ||
1305 | : pos > midRegion.end ? bottom_SlidingSheetPos | ||
1306 | : middle_SlidingSheetPos); | ||
1307 | } | ||
1308 | |||
1200 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { | 1309 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { |
1201 | iWidget *w = as_Widget(d); | 1310 | iWidget *w = as_Widget(d); |
1202 | /* Handle commands. */ | 1311 | /* Handle commands. */ |
1203 | if (isResize_UserEvent(ev)) { | 1312 | if (isResize_UserEvent(ev)) { |
1204 | checkModeButtonLayout_SidebarWidget_(d); | 1313 | checkModeButtonLayout_SidebarWidget_(d); |
1205 | if (deviceType_App() == phone_AppDeviceType && d->side == left_SidebarSide) { | 1314 | if (deviceType_App() == phone_AppDeviceType) { |
1206 | setFlags_Widget(w, rightEdgeDraggable_WidgetFlag, isPortrait_App()); | 1315 | setPadding_Widget(d->actions, 0, 0, 0, 0); |
1316 | setFlags_Widget(findChild_Widget(w, "sidebar.title"), hidden_WidgetFlag, isLandscape_App()); | ||
1317 | setFlags_Widget(findChild_Widget(w, "sidebar.close"), hidden_WidgetFlag, isLandscape_App()); | ||
1207 | /* In landscape, visibility of the toolbar is controlled separately. */ | 1318 | /* In landscape, visibility of the toolbar is controlled separately. */ |
1208 | if (isVisible_Widget(w)) { | 1319 | if (isVisible_Widget(w)) { |
1209 | postCommand_Widget(w, "sidebar.toggle"); | 1320 | postCommand_Widget(w, "sidebar.toggle"); |
@@ -1217,8 +1328,16 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1217 | setFlags_Widget(as_Widget(d->list), | 1328 | setFlags_Widget(as_Widget(d->list), |
1218 | drawBackgroundToHorizontalSafeArea_WidgetFlag, | 1329 | drawBackgroundToHorizontalSafeArea_WidgetFlag, |
1219 | isLandscape_App()); | 1330 | isLandscape_App()); |
1220 | return iFalse; | 1331 | setFlags_Widget(w, |
1332 | drawBackgroundToBottom_WidgetFlag, | ||
1333 | isPortrait_App()); | ||
1334 | setBackgroundColor_Widget(w, isPortrait_App() ? uiBackgroundSidebar_ColorId : none_ColorId); | ||
1335 | } | ||
1336 | if (!isPortraitPhone_App()) { | ||
1337 | /* In sliding sheet mode, sidebar is resized to fit in the safe area. */ | ||
1338 | setPadding_Widget(d->actions, 0, 0, 0, bottomSafeInset_Mobile()); | ||
1221 | } | 1339 | } |
1340 | return iFalse; | ||
1222 | } | 1341 | } |
1223 | else if (isMetricsChange_UserEvent(ev)) { | 1342 | else if (isMetricsChange_UserEvent(ev)) { |
1224 | if (isVisible_Widget(w)) { | 1343 | if (isVisible_Widget(w)) { |
@@ -1230,7 +1349,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1230 | } | 1349 | } |
1231 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | 1350 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { |
1232 | const char *cmd = command_UserEvent(ev); | 1351 | const char *cmd = command_UserEvent(ev); |
1233 | if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { | 1352 | if (startsWith_CStr(cmd, "tabs.changed id:doc") || equal_Command(cmd, "document.changed")) { |
1234 | updateItems_SidebarWidget_(d); | 1353 | updateItems_SidebarWidget_(d); |
1235 | scrollOffset_ListWidget(d->list, 0); | 1354 | scrollOffset_ListWidget(d->list, 0); |
1236 | } | 1355 | } |
@@ -1257,13 +1376,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1257 | } | 1376 | } |
1258 | } | 1377 | } |
1259 | } | 1378 | } |
1260 | else if (equal_Command(cmd, "idents.changed") && d->mode == identities_SidebarMode) { | ||
1261 | updateItems_SidebarWidget_(d); | ||
1262 | } | ||
1263 | else if (deviceType_App() == tablet_AppDeviceType && equal_Command(cmd, "toolbar.showident")) { | ||
1264 | postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); | ||
1265 | return iTrue; | ||
1266 | } | ||
1267 | else if (isPortraitPhone_App() && isVisible_Widget(w) && d->side == left_SidebarSide && | 1379 | else if (isPortraitPhone_App() && isVisible_Widget(w) && d->side == left_SidebarSide && |
1268 | equal_Command(cmd, "swipe.forward")) { | 1380 | equal_Command(cmd, "swipe.forward")) { |
1269 | postCommand_App("sidebar.toggle"); | 1381 | postCommand_App("sidebar.toggle"); |
@@ -1328,9 +1440,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1328 | } | 1440 | } |
1329 | return iTrue; | 1441 | return iTrue; |
1330 | } | 1442 | } |
1331 | // else if (isCommand_Widget(w, ev, "menu.closed")) { | ||
1332 | // invalidateItem_ListWidget(d->list, d->contextIndex); | ||
1333 | // } | ||
1334 | else if (isCommand_Widget(w, ev, "bookmark.open")) { | 1443 | else if (isCommand_Widget(w, ev, "bookmark.open")) { |
1335 | const iSidebarItem *item = d->contextItem; | 1444 | const iSidebarItem *item = d->contextItem; |
1336 | if (d->mode == bookmarks_SidebarMode && item) { | 1445 | if (d->mode == bookmarks_SidebarMode && item) { |
@@ -1363,13 +1472,13 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1363 | if (!isFolder_Bookmark(bm)) { | 1472 | if (!isFolder_Bookmark(bm)) { |
1364 | setText_InputWidget(urlInput, &bm->url); | 1473 | setText_InputWidget(urlInput, &bm->url); |
1365 | setText_InputWidget(tagsInput, &bm->tags); | 1474 | setText_InputWidget(tagsInput, &bm->tags); |
1366 | if (hasTag_Bookmark(bm, userIcon_BookmarkTag)) { | 1475 | if (bm->flags & userIcon_BookmarkFlag) { |
1367 | setText_InputWidget(iconInput, | 1476 | setText_InputWidget(iconInput, |
1368 | collect_String(newUnicodeN_String(&bm->icon, 1))); | 1477 | collect_String(newUnicodeN_String(&bm->icon, 1))); |
1369 | } | 1478 | } |
1370 | setToggle_Widget(homeTag, hasTag_Bookmark(bm, homepage_BookmarkTag)); | 1479 | setToggle_Widget(homeTag, bm->flags & homepage_BookmarkFlag); |
1371 | setToggle_Widget(remoteSourceTag, hasTag_Bookmark(bm, remoteSource_BookmarkTag)); | 1480 | setToggle_Widget(remoteSourceTag, bm->flags & remoteSource_BookmarkFlag); |
1372 | setToggle_Widget(linkSplitTag, hasTag_Bookmark(bm, linkSplit_BookmarkTag)); | 1481 | setToggle_Widget(linkSplitTag, bm->flags & linkSplit_BookmarkFlag); |
1373 | } | 1482 | } |
1374 | else { | 1483 | else { |
1375 | setFlags_Widget(findChild_Widget(dlg, "bmed.special"), | 1484 | setFlags_Widget(findChild_Widget(dlg, "bmed.special"), |
@@ -1390,7 +1499,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1390 | const iSidebarItem *item = d->contextItem; | 1499 | const iSidebarItem *item = d->contextItem; |
1391 | if (d->mode == bookmarks_SidebarMode && item) { | 1500 | if (d->mode == bookmarks_SidebarMode && item) { |
1392 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); | 1501 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); |
1393 | const iBool isRemote = hasTag_Bookmark(bm, remote_BookmarkTag); | 1502 | const iBool isRemote = (bm->flags & remote_BookmarkFlag) != 0; |
1394 | iChar icon = isRemote ? 0x1f588 : bm->icon; | 1503 | iChar icon = isRemote ? 0x1f588 : bm->icon; |
1395 | iWidget *dlg = makeBookmarkCreation_Widget(&bm->url, &bm->title, icon); | 1504 | iWidget *dlg = makeBookmarkCreation_Widget(&bm->url, &bm->title, icon); |
1396 | setId_Widget(dlg, format_CStr("bmed.%s", cstr_String(id_Widget(w)))); | 1505 | setId_Widget(dlg, format_CStr("bmed.%s", cstr_String(id_Widget(w)))); |
@@ -1404,17 +1513,16 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1404 | else if (isCommand_Widget(w, ev, "bookmark.tag")) { | 1513 | else if (isCommand_Widget(w, ev, "bookmark.tag")) { |
1405 | const iSidebarItem *item = d->contextItem; | 1514 | const iSidebarItem *item = d->contextItem; |
1406 | if (d->mode == bookmarks_SidebarMode && item) { | 1515 | if (d->mode == bookmarks_SidebarMode && item) { |
1407 | const char *tag = cstr_String(string_Command(cmd, "tag")); | 1516 | const iRangecc tag = range_Command(cmd, "tag"); |
1517 | const int flag = | ||
1518 | (equal_Rangecc(tag, "homepage") ? homepage_BookmarkFlag : 0) | | ||
1519 | (equal_Rangecc(tag, "subscribed") ? subscribed_BookmarkFlag : 0) | | ||
1520 | (equal_Rangecc(tag, "remotesource") ? remoteSource_BookmarkFlag : 0); | ||
1408 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); | 1521 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); |
1409 | if (hasTag_Bookmark(bm, tag)) { | 1522 | if (flag == subscribed_BookmarkFlag && (bm->flags & flag)) { |
1410 | removeTag_Bookmark(bm, tag); | 1523 | removeEntries_Feeds(item->id); /* get rid of unsubscribed entries */ |
1411 | if (!iCmpStr(tag, subscribed_BookmarkTag)) { | 1524 | bm->flags ^= flag; |
1412 | removeEntries_Feeds(item->id); | ||
1413 | } | 1525 | } |
1414 | } | ||
1415 | else { | ||
1416 | addTag_Bookmark(bm, tag); | ||
1417 | } | ||
1418 | postCommand_App("bookmarks.changed"); | 1526 | postCommand_App("bookmarks.changed"); |
1419 | } | 1527 | } |
1420 | return iTrue; | 1528 | return iTrue; |
@@ -1491,6 +1599,15 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1491 | return iTrue; | 1599 | return iTrue; |
1492 | } | 1600 | } |
1493 | else if (equal_Command(cmd, "feeds.markallread") && d->mode == feeds_SidebarMode) { | 1601 | else if (equal_Command(cmd, "feeds.markallread") && d->mode == feeds_SidebarMode) { |
1602 | if (argLabel_Command(cmd, "confirm")) { | ||
1603 | /* This is used on mobile. */ | ||
1604 | iWidget *menu = makeMenu_Widget(w->root->widget, (iMenuItem[]){ | ||
1605 | check_Icon " " uiTextCaution_ColorEscape "${feeds.markallread}", 0, 0, | ||
1606 | "feeds.markallread" | ||
1607 | }, 1); | ||
1608 | openMenu_Widget(menu, topLeft_Rect(bounds_Widget(d->actions))); | ||
1609 | return iTrue; | ||
1610 | } | ||
1494 | iConstForEach(PtrArray, i, listEntries_Feeds()) { | 1611 | iConstForEach(PtrArray, i, listEntries_Feeds()) { |
1495 | const iFeedEntry *entry = i.ptr; | 1612 | const iFeedEntry *entry = i.ptr; |
1496 | const iString *url = url_FeedEntry(entry); | 1613 | const iString *url = url_FeedEntry(entry); |
@@ -1539,7 +1656,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1539 | } | 1656 | } |
1540 | if (isCommand_Widget(w, ev, "feed.entry.unsubscribe")) { | 1657 | if (isCommand_Widget(w, ev, "feed.entry.unsubscribe")) { |
1541 | if (arg_Command(cmd)) { | 1658 | if (arg_Command(cmd)) { |
1542 | removeTag_Bookmark(feedBookmark, subscribed_BookmarkTag); | 1659 | feedBookmark->flags &= ~subscribed_BookmarkFlag; |
1543 | removeEntries_Feeds(id_Bookmark(feedBookmark)); | 1660 | removeEntries_Feeds(id_Bookmark(feedBookmark)); |
1544 | updateItems_SidebarWidget_(d); | 1661 | updateItems_SidebarWidget_(d); |
1545 | } | 1662 | } |
@@ -1555,108 +1672,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1555 | 0, | 1672 | 0, |
1556 | format_CStr("!feed.entry.unsubscribe arg:1 ptr:%p", d) } }, | 1673 | format_CStr("!feed.entry.unsubscribe arg:1 ptr:%p", d) } }, |
1557 | 2); | 1674 | 2); |
1558 | } | ||
1559 | return iTrue; | ||
1560 | } | ||
1561 | } | ||
1562 | } | ||
1563 | } | ||
1564 | else if (isCommand_Widget(w, ev, "ident.use")) { | ||
1565 | iGmIdentity * ident = menuIdentity_SidebarWidget_(d); | ||
1566 | const iString *tabUrl = url_DocumentWidget(document_App()); | ||
1567 | if (ident) { | ||
1568 | if (argLabel_Command(cmd, "clear")) { | ||
1569 | clearUse_GmIdentity(ident); | ||
1570 | } | ||
1571 | else if (arg_Command(cmd)) { | ||
1572 | signIn_GmCerts(certs_App(), ident, tabUrl); | ||
1573 | postCommand_App("navigate.reload"); | ||
1574 | } | ||
1575 | else { | ||
1576 | signOut_GmCerts(certs_App(), tabUrl); | ||
1577 | postCommand_App("navigate.reload"); | ||
1578 | } | ||
1579 | saveIdentities_GmCerts(certs_App()); | ||
1580 | updateItems_SidebarWidget_(d); | ||
1581 | } | ||
1582 | return iTrue; | ||
1583 | } | ||
1584 | else if (isCommand_Widget(w, ev, "ident.edit")) { | ||
1585 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1586 | if (ident) { | ||
1587 | makeValueInput_Widget(get_Root()->widget, | ||
1588 | &ident->notes, | ||
1589 | uiHeading_ColorEscape "${heading.ident.notes}", | ||
1590 | format_CStr(cstr_Lang("dlg.ident.notes"), cstr_String(name_GmIdentity(ident))), | ||
1591 | uiTextAction_ColorEscape "${dlg.default}", | ||
1592 | format_CStr("!ident.setnotes ident:%p ptr:%p", ident, d)); | ||
1593 | } | ||
1594 | return iTrue; | ||
1595 | } | ||
1596 | else if (isCommand_Widget(w, ev, "ident.fingerprint")) { | ||
1597 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1598 | if (ident) { | ||
1599 | const iString *fps = collect_String( | ||
1600 | hexEncode_Block(collect_Block(fingerprint_TlsCertificate(ident->cert)))); | ||
1601 | SDL_SetClipboardText(cstr_String(fps)); | ||
1602 | } | 1675 | } |
1603 | return iTrue; | 1676 | return iTrue; |
1604 | } | 1677 | } |
1605 | else if (isCommand_Widget(w, ev, "ident.export")) { | ||
1606 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1607 | if (ident) { | ||
1608 | iString *pem = collect_String(pem_TlsCertificate(ident->cert)); | ||
1609 | append_String(pem, collect_String(privateKeyPem_TlsCertificate(ident->cert))); | ||
1610 | iDocumentWidget *expTab = newTab_App(NULL, iTrue); | ||
1611 | setUrlAndSource_DocumentWidget( | ||
1612 | expTab, | ||
1613 | collectNewFormat_String("file:%s.pem", cstr_String(name_GmIdentity(ident))), | ||
1614 | collectNewCStr_String("text/plain"), | ||
1615 | utf8_String(pem)); | ||
1616 | } | 1678 | } |
1617 | return iTrue; | ||
1618 | } | ||
1619 | else if (isCommand_Widget(w, ev, "ident.setnotes")) { | ||
1620 | iGmIdentity *ident = pointerLabel_Command(cmd, "ident"); | ||
1621 | if (ident) { | ||
1622 | setCStr_String(&ident->notes, suffixPtr_Command(cmd, "value")); | ||
1623 | updateItems_SidebarWidget_(d); | ||
1624 | } | ||
1625 | return iTrue; | ||
1626 | } | ||
1627 | else if (isCommand_Widget(w, ev, "ident.pickicon")) { | ||
1628 | return iTrue; | ||
1629 | } | ||
1630 | else if (isCommand_Widget(w, ev, "ident.reveal")) { | ||
1631 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1632 | if (ident) { | ||
1633 | const iString *crtPath = certificatePath_GmCerts(certs_App(), ident); | ||
1634 | if (crtPath) { | ||
1635 | revealPath_App(crtPath); | ||
1636 | } | ||
1637 | } | 1679 | } |
1638 | return iTrue; | ||
1639 | } | ||
1640 | else if (isCommand_Widget(w, ev, "ident.delete")) { | ||
1641 | iSidebarItem *item = d->contextItem; | ||
1642 | if (argLabel_Command(cmd, "confirm")) { | ||
1643 | makeQuestion_Widget( | ||
1644 | uiTextCaution_ColorEscape "${heading.ident.delete}", | ||
1645 | format_CStr(cstr_Lang("dlg.confirm.ident.delete"), | ||
1646 | uiTextAction_ColorEscape, | ||
1647 | cstr_String(&item->label), | ||
1648 | uiText_ColorEscape), | ||
1649 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, | ||
1650 | { uiTextCaution_ColorEscape "${dlg.ident.delete}", | ||
1651 | 0, | ||
1652 | 0, | ||
1653 | format_CStr("!ident.delete confirm:0 ptr:%p", d) } }, | ||
1654 | 2); | ||
1655 | return iTrue; | ||
1656 | } | ||
1657 | deleteIdentity_GmCerts(certs_App(), menuIdentity_SidebarWidget_(d)); | ||
1658 | postCommand_App("idents.changed"); | ||
1659 | return iTrue; | ||
1660 | } | 1680 | } |
1661 | else if (isCommand_Widget(w, ev, "history.delete")) { | 1681 | else if (isCommand_Widget(w, ev, "history.delete")) { |
1662 | if (d->contextItem && !isEmpty_String(&d->contextItem->url)) { | 1682 | if (d->contextItem && !isEmpty_String(&d->contextItem->url)) { |
@@ -1711,14 +1731,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1711 | /* Update cursor. */ | 1731 | /* Update cursor. */ |
1712 | else if (contains_Widget(w, mouse)) { | 1732 | else if (contains_Widget(w, mouse)) { |
1713 | const iSidebarItem *item = constHoverItem_ListWidget(d->list); | 1733 | const iSidebarItem *item = constHoverItem_ListWidget(d->list); |
1714 | if (item && d->mode != identities_SidebarMode) { | ||
1715 | setCursor_Window(get_Window(), | 1734 | setCursor_Window(get_Window(), |
1716 | item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW | 1735 | item ? (item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW |
1717 | : SDL_SYSTEM_CURSOR_HAND); | 1736 | : SDL_SYSTEM_CURSOR_HAND) |
1718 | } | 1737 | : SDL_SYSTEM_CURSOR_ARROW); |
1719 | else { | ||
1720 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | ||
1721 | } | ||
1722 | } | 1738 | } |
1723 | if (d->contextIndex != iInvalidPos) { | 1739 | if (d->contextIndex != iInvalidPos) { |
1724 | invalidateItem_ListWidget(d->list, d->contextIndex); | 1740 | invalidateItem_ListWidget(d->list, d->contextIndex); |
@@ -1726,7 +1742,14 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1726 | } | 1742 | } |
1727 | } | 1743 | } |
1728 | /* Update context menu items. */ | 1744 | /* Update context menu items. */ |
1729 | if ((d->menu || d->mode == identities_SidebarMode) && ev->type == SDL_MOUSEBUTTONDOWN) { | 1745 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { |
1746 | if (isSlidingSheet_SidebarWidget_(d) && | ||
1747 | ev->button.button == SDL_BUTTON_LEFT && | ||
1748 | isVisible_Widget(d) && | ||
1749 | !contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { | ||
1750 | setSlidingSheetPos_SidebarWidget_(d, bottom_SlidingSheetPos); | ||
1751 | return iTrue; | ||
1752 | } | ||
1730 | if (ev->button.button == SDL_BUTTON_RIGHT) { | 1753 | if (ev->button.button == SDL_BUTTON_RIGHT) { |
1731 | d->contextItem = NULL; | 1754 | d->contextItem = NULL; |
1732 | if (!isVisible_Widget(d->menu)) { | 1755 | if (!isVisible_Widget(d->menu)) { |
@@ -1739,7 +1762,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1739 | invalidateItem_ListWidget(d->list, d->contextIndex); | 1762 | invalidateItem_ListWidget(d->list, d->contextIndex); |
1740 | } | 1763 | } |
1741 | d->contextIndex = hoverItemIndex_ListWidget(d->list); | 1764 | d->contextIndex = hoverItemIndex_ListWidget(d->list); |
1742 | updateContextMenu_SidebarWidget_(d); | ||
1743 | /* TODO: Some callback-based mechanism would be nice for updating menus right | 1765 | /* TODO: Some callback-based mechanism would be nice for updating menus right |
1744 | before they open? At least move these to `updateContextMenu_ */ | 1766 | before they open? At least move these to `updateContextMenu_ */ |
1745 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { | 1767 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { |
@@ -1747,17 +1769,17 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1747 | if (bm) { | 1769 | if (bm) { |
1748 | setMenuItemLabel_Widget(d->menu, | 1770 | setMenuItemLabel_Widget(d->menu, |
1749 | "bookmark.tag tag:homepage", | 1771 | "bookmark.tag tag:homepage", |
1750 | hasTag_Bookmark(bm, homepage_BookmarkTag) | 1772 | bm->flags & homepage_BookmarkFlag |
1751 | ? home_Icon " ${bookmark.untag.home}" | 1773 | ? home_Icon " ${bookmark.untag.home}" |
1752 | : home_Icon " ${bookmark.tag.home}"); | 1774 | : home_Icon " ${bookmark.tag.home}"); |
1753 | setMenuItemLabel_Widget(d->menu, | 1775 | setMenuItemLabel_Widget(d->menu, |
1754 | "bookmark.tag tag:subscribed", | 1776 | "bookmark.tag tag:subscribed", |
1755 | hasTag_Bookmark(bm, subscribed_BookmarkTag) | 1777 | bm->flags & subscribed_BookmarkFlag |
1756 | ? star_Icon " ${bookmark.untag.sub}" | 1778 | ? star_Icon " ${bookmark.untag.sub}" |
1757 | : star_Icon " ${bookmark.tag.sub}"); | 1779 | : star_Icon " ${bookmark.tag.sub}"); |
1758 | setMenuItemLabel_Widget(d->menu, | 1780 | setMenuItemLabel_Widget(d->menu, |
1759 | "bookmark.tag tag:remotesource", | 1781 | "bookmark.tag tag:remotesource", |
1760 | hasTag_Bookmark(bm, remoteSource_BookmarkTag) | 1782 | bm->flags & remoteSource_BookmarkFlag |
1761 | ? downArrowBar_Icon " ${bookmark.untag.remote}" | 1783 | ? downArrowBar_Icon " ${bookmark.untag.remote}" |
1762 | : downArrowBar_Icon " ${bookmark.tag.remote}"); | 1784 | : downArrowBar_Icon " ${bookmark.tag.remote}"); |
1763 | } | 1785 | } |
@@ -1769,29 +1791,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1769 | isRead ? circle_Icon " ${feeds.entry.markunread}" | 1791 | isRead ? circle_Icon " ${feeds.entry.markunread}" |
1770 | : circleWhite_Icon " ${feeds.entry.markread}"); | 1792 | : circleWhite_Icon " ${feeds.entry.markread}"); |
1771 | } | 1793 | } |
1772 | else if (d->mode == identities_SidebarMode) { | ||
1773 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); | ||
1774 | const iString * docUrl = url_DocumentWidget(document_App()); | ||
1775 | iForEach(ObjectList, i, children_Widget(d->menu)) { | ||
1776 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | ||
1777 | iLabelWidget *menuItem = i.object; | ||
1778 | const char * cmdItem = cstr_String(command_LabelWidget(menuItem)); | ||
1779 | if (equal_Command(cmdItem, "ident.use")) { | ||
1780 | const iBool cmdUse = arg_Command(cmdItem) != 0; | ||
1781 | const iBool cmdClear = argLabel_Command(cmdItem, "clear") != 0; | ||
1782 | setFlags_Widget( | ||
1783 | as_Widget(menuItem), | ||
1784 | disabled_WidgetFlag, | ||
1785 | (cmdClear && !isUsed_GmIdentity(ident)) || | ||
1786 | (!cmdClear && cmdUse && isUsedOn_GmIdentity(ident, docUrl)) || | ||
1787 | (!cmdClear && !cmdUse && !isUsedOn_GmIdentity(ident, docUrl))); | ||
1788 | } | 1794 | } |
1789 | } | 1795 | } |
1790 | } | 1796 | } |
1791 | } | ||
1792 | } | ||
1793 | } | ||
1794 | } | ||
1795 | if (ev->type == SDL_KEYDOWN) { | 1797 | if (ev->type == SDL_KEYDOWN) { |
1796 | const int key = ev->key.keysym.sym; | 1798 | const int key = ev->key.keysym.sym; |
1797 | const int kmods = keyMods_Sym(ev->key.keysym.mod); | 1799 | const int kmods = keyMods_Sym(ev->key.keysym.mod); |
@@ -1801,6 +1803,61 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1801 | return iTrue; | 1803 | return iTrue; |
1802 | } | 1804 | } |
1803 | } | 1805 | } |
1806 | if (isSlidingSheet_SidebarWidget_(d)) { | ||
1807 | if (ev->type == SDL_MOUSEWHEEL) { | ||
1808 | enum iWidgetTouchMode touchMode = widgetMode_Touch(w); | ||
1809 | if (touchMode == momentum_WidgetTouchMode) { | ||
1810 | /* We don't do momentum. */ | ||
1811 | float swipe = stopWidgetMomentum_Touch(w) / gap_UI; | ||
1812 | // printf("swipe: %f\n", swipe); | ||
1813 | const iRangei midRegion = SlidingSheetMiddleRegion_SidebarWidget_(d); | ||
1814 | const int pos = top_Rect(w->rect); | ||
1815 | if (swipe < 170) { | ||
1816 | gotoNearestSlidingSheetPos_SidebarWidget_(d); | ||
1817 | } | ||
1818 | else if (swipe > 500 && ev->wheel.y > 0) { | ||
1819 | /* Fast swipe down will dismiss. */ | ||
1820 | setSlidingSheetPos_SidebarWidget_(d, bottom_SlidingSheetPos); | ||
1821 | } | ||
1822 | else if (ev->wheel.y < 0) { | ||
1823 | setSlidingSheetPos_SidebarWidget_(d, top_SlidingSheetPos); | ||
1824 | } | ||
1825 | else if (pos < (midRegion.start + midRegion.end) / 2) { | ||
1826 | setSlidingSheetPos_SidebarWidget_(d, middle_SlidingSheetPos); | ||
1827 | } | ||
1828 | else { | ||
1829 | setSlidingSheetPos_SidebarWidget_(d, bottom_SlidingSheetPos); | ||
1830 | } | ||
1831 | } | ||
1832 | else if (touchMode == touch_WidgetTouchMode) { | ||
1833 | /* Move with the finger. */ | ||
1834 | adjustEdges_Rect(&w->rect, ev->wheel.y, 0, 0, 0); | ||
1835 | /* Upon reaching the top, scrolling is switched back to the list. */ | ||
1836 | const iRect rootRect = safeRect_Root(w->root); | ||
1837 | const int top = top_Rect(rootRect); | ||
1838 | if (w->rect.pos.y < top) { | ||
1839 | setScrollMode_ListWidget(d->list, disabledAtTopUpwards_ScrollMode); | ||
1840 | setScrollPos_ListWidget(d->list, top - w->rect.pos.y); | ||
1841 | transferAffinity_Touch(w, as_Widget(d->list)); | ||
1842 | w->rect.pos.y = top; | ||
1843 | w->rect.size.y = height_Rect(rootRect); | ||
1844 | } | ||
1845 | else { | ||
1846 | setScrollMode_ListWidget(d->list, disabled_ScrollMode); | ||
1847 | } | ||
1848 | arrange_Widget(w); | ||
1849 | refresh_Widget(w); | ||
1850 | } | ||
1851 | else { | ||
1852 | return iFalse; | ||
1853 | } | ||
1854 | return iTrue; | ||
1855 | } | ||
1856 | if (ev->type == SDL_USEREVENT && ev->user.code == widgetTouchEnds_UserEventCode) { | ||
1857 | gotoNearestSlidingSheetPos_SidebarWidget_(d); | ||
1858 | return iTrue; | ||
1859 | } | ||
1860 | } | ||
1804 | if (ev->type == SDL_MOUSEBUTTONDOWN && | 1861 | if (ev->type == SDL_MOUSEBUTTONDOWN && |
1805 | contains_Widget(as_Widget(d->list), init_I2(ev->button.x, ev->button.y))) { | 1862 | contains_Widget(as_Widget(d->list), init_I2(ev->button.x, ev->button.y))) { |
1806 | if (hoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) { | 1863 | if (hoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) { |
@@ -1811,13 +1868,12 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1811 | const iSidebarItem *hoverItem = hoverItem_ListWidget(d->list); | 1868 | const iSidebarItem *hoverItem = hoverItem_ListWidget(d->list); |
1812 | iAssert(hoverItem); | 1869 | iAssert(hoverItem); |
1813 | const iBookmark * bm = get_Bookmarks(bookmarks_App(), hoverItem->id); | 1870 | const iBookmark * bm = get_Bookmarks(bookmarks_App(), hoverItem->id); |
1814 | const iBool isRemote = hasTag_Bookmark(bm, remote_BookmarkTag); | 1871 | const iBool isRemote = (bm->flags & remote_BookmarkFlag) != 0; |
1815 | static const char *localOnlyCmds[] = { "bookmark.edit", | 1872 | static const char *localOnlyCmds[] = { "bookmark.edit", |
1816 | "bookmark.delete", | 1873 | "bookmark.delete", |
1817 | "bookmark.tag tag:" subscribed_BookmarkTag, | 1874 | "bookmark.tag tag:subscribed", |
1818 | "bookmark.tag tag:" homepage_BookmarkTag, | 1875 | "bookmark.tag tag:homepage", |
1819 | "bookmark.tag tag:" remoteSource_BookmarkTag, | 1876 | "bookmark.tag tag:remotesource" }; |
1820 | "bookmark.tag tag:" subscribed_BookmarkTag }; | ||
1821 | iForIndices(i, localOnlyCmds) { | 1877 | iForIndices(i, localOnlyCmds) { |
1822 | setFlags_Widget(as_Widget(findMenuItem_Widget(d->menu, localOnlyCmds[i])), | 1878 | setFlags_Widget(as_Widget(findMenuItem_Widget(d->menu, localOnlyCmds[i])), |
1823 | disabled_WidgetFlag, | 1879 | disabled_WidgetFlag, |
@@ -1838,6 +1894,9 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
1838 | const iRect bounds = bounds_Widget(w); | 1894 | const iRect bounds = bounds_Widget(w); |
1839 | iPaint p; | 1895 | iPaint p; |
1840 | init_Paint(&p); | 1896 | init_Paint(&p); |
1897 | if (d->mode == documentOutline_SidebarMode) { | ||
1898 | makePaletteGlobal_GmDocument(document_DocumentWidget(document_App())); | ||
1899 | } | ||
1841 | if (!isPortraitPhone_App()) { /* this would erase page contents during transition on the phone */ | 1900 | if (!isPortraitPhone_App()) { /* this would erase page contents during transition on the phone */ |
1842 | if (flags_Widget(w) & visualOffset_WidgetFlag && | 1901 | if (flags_Widget(w) & visualOffset_WidgetFlag && |
1843 | flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { | 1902 | flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { |
@@ -1860,6 +1919,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1860 | &Class_SidebarWidget); | 1919 | &Class_SidebarWidget); |
1861 | const iBool isMenuVisible = isVisible_Widget(sidebar->menu); | 1920 | const iBool isMenuVisible = isVisible_Widget(sidebar->menu); |
1862 | const iBool isDragging = constDragItem_ListWidget(list) == d; | 1921 | const iBool isDragging = constDragItem_ListWidget(list) == d; |
1922 | const iBool isEditing = sidebar->isEditing; /* only on mobile */ | ||
1863 | const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging; | 1923 | const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging; |
1864 | const iBool isHover = | 1924 | const iBool isHover = |
1865 | (!isMenuVisible && | 1925 | (!isMenuVisible && |
@@ -2013,6 +2073,16 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2013 | drawRange_Text(font, textPos, fg, range_String(&d->label)); | 2073 | drawRange_Text(font, textPos, fg, range_String(&d->label)); |
2014 | const int metaFont = uiLabel_FontId; | 2074 | const int metaFont = uiLabel_FontId; |
2015 | const int metaIconWidth = 4.5f * gap_UI; | 2075 | const int metaIconWidth = 4.5f * gap_UI; |
2076 | if (isEditing) { | ||
2077 | iRect dragRect = { | ||
2078 | addX_I2(topRight_Rect(itemRect), -itemHeight * 3 / 2), | ||
2079 | init_I2(itemHeight * 3 / 2, itemHeight) | ||
2080 | }; | ||
2081 | fillRect_Paint(p, dragRect, bg); | ||
2082 | drawVLine_Paint(p, topLeft_Rect(dragRect), height_Rect(dragRect), uiSeparator_ColorId); | ||
2083 | drawCentered_Text(uiContent_FontId, dragRect, iTrue, uiAnnotation_ColorId, menu_Icon); | ||
2084 | adjustEdges_Rect(&itemRect, 0, -width_Rect(dragRect), 0, 0); | ||
2085 | } | ||
2016 | const iInt2 metaPos = | 2086 | const iInt2 metaPos = |
2017 | init_I2(right_Rect(itemRect) - | 2087 | init_I2(right_Rect(itemRect) - |
2018 | length_String(&d->meta) * | 2088 | length_String(&d->meta) * |
@@ -2091,40 +2161,6 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2091 | } | 2161 | } |
2092 | iEndCollect(); | 2162 | iEndCollect(); |
2093 | } | 2163 | } |
2094 | else if (sidebar->mode == identities_SidebarMode) { | ||
2095 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
2096 | : uiTextStrong_ColorId; | ||
2097 | const iBool isUsedOnDomain = (d->indent != 0); | ||
2098 | iString icon; | ||
2099 | initUnicodeN_String(&icon, &d->icon, 1); | ||
2100 | iInt2 cPos = topLeft_Rect(itemRect); | ||
2101 | const int indent = 1.4f * lineHeight_Text(font); | ||
2102 | addv_I2(&cPos, | ||
2103 | init_I2(3 * gap_UI, | ||
2104 | (itemHeight - lineHeight_Text(uiLabel_FontId) * 2 - lineHeight_Text(font)) / | ||
2105 | 2)); | ||
2106 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId | ||
2107 | : uiTextFramelessHover_ColorId) | ||
2108 | : uiTextDim_ColorId; | ||
2109 | if (!d->listItem.isSelected && !isUsedOnDomain) { | ||
2110 | drawOutline_Text(font, cPos, metaFg, none_ColorId, range_String(&icon)); | ||
2111 | } | ||
2112 | drawRange_Text(font, | ||
2113 | cPos, | ||
2114 | d->listItem.isSelected ? iconColor | ||
2115 | : isUsedOnDomain ? altIconColor | ||
2116 | : uiBackgroundSidebar_ColorId, | ||
2117 | range_String(&icon)); | ||
2118 | deinit_String(&icon); | ||
2119 | drawRange_Text(d->listItem.isSelected ? sidebar->itemFonts[1] : font, | ||
2120 | add_I2(cPos, init_I2(indent, 0)), | ||
2121 | fg, | ||
2122 | range_String(&d->label)); | ||
2123 | drawRange_Text(uiLabel_FontId, | ||
2124 | add_I2(cPos, init_I2(indent, lineHeight_Text(font))), | ||
2125 | metaFg, | ||
2126 | range_String(&d->meta)); | ||
2127 | } | ||
2128 | } | 2164 | } |
2129 | 2165 | ||
2130 | iBeginDefineSubclass(SidebarWidget, Widget) | 2166 | iBeginDefineSubclass(SidebarWidget, Widget) |