summaryrefslogtreecommitdiff
path: root/src/ui/sidebarwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/sidebarwidget.c')
-rw-r--r--src/ui/sidebarwidget.c876
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
94static 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
102static const char *tightModeLabels_[max_SidebarMode] = {
103 book_Icon,
104 star_Icon,
105 clock_Icon,
106 person_Icon,
107 page_Icon,
108};
109
91struct Impl_SidebarWidget { 110struct 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
115iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) 137iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side)
116 138
139iLocalDef iListWidget *list_SidebarWidget_(iSidebarWidget *d) {
140 return d->mode == identities_SidebarMode ? (iListWidget *) d->certList : d->list;
141}
142
117static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { 143static 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
210static 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
184static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const char *label, 222static 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);
198static 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
207static 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
257static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { 237static 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
247static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) {
248 return isPortraitPhone_App();
249}
250
251static 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
267static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { 264static 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
665static void updateItems_SidebarWidget_(iSidebarWidget *d) { 660static void updateItems_SidebarWidget_(iSidebarWidget *d) {
@@ -678,35 +673,59 @@ static size_t findItem_SidebarWidget_(const iSidebarWidget *d, int id) {
678} 673}
679 674
680static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { 675static 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
687iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { 685iBool 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
707void setClosedFolders_SidebarWidget(iSidebarWidget *d, const iIntSet *closedFolders) { 720void 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
727void setMidHeight_SidebarWidget(iSidebarWidget *d, int midHeight) {
728 d->midHeight = midHeight;
710} 729}
711 730
712enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { 731enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) {
@@ -722,25 +741,9 @@ float width_SidebarWidget(const iSidebarWidget *d) {
722} 741}
723 742
724const iIntSet *closedFolders_SidebarWidget(const iSidebarWidget *d) { 743const iIntSet *closedFolders_SidebarWidget(const iSidebarWidget *d) {
725 return d->closedFolders; 744 return d ? d->closedFolders : collect_IntSet(new_IntSet());
726} 745}
727 746
728static 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
736static const char *tightModeLabels_[max_SidebarMode] = {
737 book_Icon,
738 star_Icon,
739 clock_Icon,
740 person_Icon,
741 page_Icon,
742};
743
744const char *icon_SidebarMode(enum iSidebarMode mode) { 747const 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
768static 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
765void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { 782void 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
903static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { 945static 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
913static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { 952static 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
917static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, size_t itemIndex) { 959static 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
1020void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { 1050void 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
1114enum iSlidingSheetPos {
1115 top_SlidingSheetPos,
1116 middle_SlidingSheetPos,
1117 bottom_SlidingSheetPos,
1118};
1119
1120static 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
1086static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *cmd) { 1144static 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
1190static size_t numBookmarks_(const iPtrArray *bmList) { 1280static 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
1291static 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
1300static 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
1200static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { 1309static 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
2130iBeginDefineSubclass(SidebarWidget, Widget) 2166iBeginDefineSubclass(SidebarWidget, Widget)