diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-02 10:02:16 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-02 10:02:16 +0200 |
commit | b9cdb34c59dc133b549deed5a4f3b9bb95197cca (patch) | |
tree | 1ed42fabfff17fe5d7d2c0ed4c8687f471df345d | |
parent | f4942e1b4da6dc1334dcdb4f2daae670bfa1f813 (diff) |
Refactored CertListWidget out of the sidebar
The identity list is needed elsewhere outside of the sidebar, so moved it into a specialized ListWidget class.
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | res/about/version.gmi | 2 | ||||
-rw-r--r-- | src/ui/certlistwidget.c | 474 | ||||
-rw-r--r-- | src/ui/certlistwidget.h | 40 | ||||
-rw-r--r-- | src/ui/listwidget.c | 15 | ||||
-rw-r--r-- | src/ui/listwidget.h | 20 | ||||
-rw-r--r-- | src/ui/root.c | 18 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 335 |
8 files changed, 586 insertions, 320 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c225c9b..4200c7c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -168,6 +168,8 @@ set (SOURCES | |||
168 | src/ui/bindingswidget.h | 168 | src/ui/bindingswidget.h |
169 | src/ui/certimportwidget.c | 169 | src/ui/certimportwidget.c |
170 | src/ui/certimportwidget.h | 170 | src/ui/certimportwidget.h |
171 | src/ui/certlistwidget.c | ||
172 | src/ui/certlistwidget.h | ||
171 | src/ui/color.c | 173 | src/ui/color.c |
172 | src/ui/color.h | 174 | src/ui/color.h |
173 | src/ui/command.c | 175 | src/ui/command.c |
diff --git a/res/about/version.gmi b/res/about/version.gmi index 7a14e79b..506e3ae0 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -8,7 +8,7 @@ | |||
8 | 8 | ||
9 | ## 1.10 | 9 | ## 1.10 |
10 | New features: | 10 | New features: |
11 | * Identity toolbar menu can be used to switch between alternate identities that have been used on the site. If you have multiple accounts on a site, this makes it more convenient to switch between them. | 11 | * Identity toolbar menu can be used to switch between alternate identities. If you have used multiple identities on one site, this makes it more convenient to switch between them. |
12 | 12 | ||
13 | ## 1.9.2 | 13 | ## 1.9.2 |
14 | * Windows: Use the correct version number for update checks. | 14 | * Windows: Use the correct version number for update checks. |
diff --git a/src/ui/certlistwidget.c b/src/ui/certlistwidget.c new file mode 100644 index 00000000..4d939ae2 --- /dev/null +++ b/src/ui/certlistwidget.c | |||
@@ -0,0 +1,474 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "certlistwidget.h" | ||
24 | |||
25 | #include "documentwidget.h" | ||
26 | #include "command.h" | ||
27 | #include "labelwidget.h" | ||
28 | #include "listwidget.h" | ||
29 | #include "../gmcerts.h" | ||
30 | #include "../app.h" | ||
31 | |||
32 | #include <SDL_clipboard.h> | ||
33 | |||
34 | iDeclareType(CertItem) | ||
35 | typedef iListItemClass iCertItemClass; | ||
36 | |||
37 | struct Impl_CertItem { | ||
38 | iListItem listItem; | ||
39 | uint32_t id; | ||
40 | int indent; | ||
41 | iChar icon; | ||
42 | iBool isBold; | ||
43 | iString label; | ||
44 | iString meta; | ||
45 | // iString url; | ||
46 | }; | ||
47 | |||
48 | void init_CertItem(iCertItem *d) { | ||
49 | init_ListItem(&d->listItem); | ||
50 | d->id = 0; | ||
51 | d->indent = 0; | ||
52 | d->icon = 0; | ||
53 | d->isBold = iFalse; | ||
54 | init_String(&d->label); | ||
55 | init_String(&d->meta); | ||
56 | // init_String(&d->url); | ||
57 | } | ||
58 | |||
59 | void deinit_CertItem(iCertItem *d) { | ||
60 | // deinit_String(&d->url); | ||
61 | deinit_String(&d->meta); | ||
62 | deinit_String(&d->label); | ||
63 | } | ||
64 | |||
65 | static void draw_CertItem_(const iCertItem *d, iPaint *p, iRect itemRect, const iListWidget *list); | ||
66 | |||
67 | iBeginDefineSubclass(CertItem, ListItem) | ||
68 | .draw = (iAny *) draw_CertItem_, | ||
69 | iEndDefineSubclass(CertItem) | ||
70 | |||
71 | iDefineObjectConstruction(CertItem) | ||
72 | |||
73 | /*----------------------------------------------------------------------------------------------*/ | ||
74 | |||
75 | struct Impl_CertListWidget { | ||
76 | iListWidget list; | ||
77 | int itemFonts[2]; | ||
78 | iWidget *menu; /* context menu for an item */ | ||
79 | iCertItem *contextItem; /* list item accessed in the context menu */ | ||
80 | size_t contextIndex; /* index of list item accessed in the context menu */ | ||
81 | }; | ||
82 | |||
83 | iDefineObjectConstruction(CertListWidget) | ||
84 | |||
85 | static iGmIdentity *menuIdentity_CertListWidget_(const iCertListWidget *d) { | ||
86 | if (d->contextItem) { | ||
87 | return identity_GmCerts(certs_App(), d->contextItem->id); | ||
88 | } | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | static void updateContextMenu_CertListWidget_(iCertListWidget *d) { | ||
93 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | ||
94 | pushBackN_Array(items, (iMenuItem[]){ | ||
95 | { person_Icon " ${ident.use}", 0, 0, "ident.use arg:1" }, | ||
96 | { close_Icon " ${ident.stopuse}", 0, 0, "ident.use arg:0" }, | ||
97 | { close_Icon " ${ident.stopuse.all}", 0, 0, "ident.use arg:0 clear:1" }, | ||
98 | { "---", 0, 0, NULL }, | ||
99 | { edit_Icon " ${menu.edit.notes}", 0, 0, "ident.edit" }, | ||
100 | { "${ident.fingerprint}", 0, 0, "ident.fingerprint" }, | ||
101 | { export_Icon " ${ident.export}", 0, 0, "ident.export" }, | ||
102 | { "---", 0, 0, NULL }, | ||
103 | { delete_Icon " " uiTextCaution_ColorEscape "${ident.delete}", 0, 0, "ident.delete confirm:1" }, | ||
104 | }, 9); | ||
105 | /* Used URLs. */ | ||
106 | const iGmIdentity *ident = menuIdentity_CertListWidget_(d); | ||
107 | if (ident) { | ||
108 | size_t insertPos = 3; | ||
109 | if (!isEmpty_StringSet(ident->useUrls)) { | ||
110 | insert_Array(items, insertPos++, &(iMenuItem){ "---", 0, 0, NULL }); | ||
111 | } | ||
112 | const iString *docUrl = url_DocumentWidget(document_App()); | ||
113 | iBool usedOnCurrentPage = iFalse; | ||
114 | iConstForEach(StringSet, i, ident->useUrls) { | ||
115 | const iString *url = i.value; | ||
116 | usedOnCurrentPage |= equalCase_String(docUrl, url); | ||
117 | iRangecc urlStr = range_String(url); | ||
118 | if (startsWith_Rangecc(urlStr, "gemini://")) { | ||
119 | urlStr.start += 9; /* omit the default scheme */ | ||
120 | } | ||
121 | insert_Array(items, | ||
122 | insertPos++, | ||
123 | &(iMenuItem){ format_CStr(globe_Icon " %s", cstr_Rangecc(urlStr)), | ||
124 | 0, | ||
125 | 0, | ||
126 | format_CStr("!open url:%s", cstr_String(url)) }); | ||
127 | } | ||
128 | if (!usedOnCurrentPage) { | ||
129 | remove_Array(items, 1); | ||
130 | } | ||
131 | } | ||
132 | destroy_Widget(d->menu); | ||
133 | d->menu = makeMenu_Widget(as_Widget(d), data_Array(items), size_Array(items)); | ||
134 | } | ||
135 | |||
136 | static void itemClicked_CertListWidget_(iCertListWidget *d, iCertItem *item, size_t itemIndex) { | ||
137 | iWidget *w = as_Widget(d); | ||
138 | setFocus_Widget(NULL); | ||
139 | d->contextItem = item; | ||
140 | if (d->contextIndex != iInvalidPos) { | ||
141 | invalidateItem_ListWidget(&d->list, d->contextIndex); | ||
142 | } | ||
143 | d->contextIndex = itemIndex; | ||
144 | if (itemIndex < numItems_ListWidget(&d->list)) { | ||
145 | updateContextMenu_CertListWidget_(d); | ||
146 | arrange_Widget(d->menu); | ||
147 | openMenu_Widget(d->menu, | ||
148 | bounds_Widget(w).pos.x < mid_Rect(rect_Root(w->root)).x | ||
149 | ? topRight_Rect(itemRect_ListWidget(&d->list, itemIndex)) | ||
150 | : addX_I2(topLeft_Rect(itemRect_ListWidget(&d->list, itemIndex)), | ||
151 | -width_Widget(d->menu))); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static iBool processEvent_CertListWidget_(iCertListWidget *d, const SDL_Event *ev) { | ||
156 | iWidget *w = as_Widget(d); | ||
157 | /* Handle commands. */ | ||
158 | if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | ||
159 | const char *cmd = command_UserEvent(ev); | ||
160 | if (equal_Command(cmd, "idents.changed")) { | ||
161 | updateItems_CertListWidget(d); | ||
162 | } | ||
163 | else if (isCommand_Widget(w, ev, "list.clicked")) { | ||
164 | itemClicked_CertListWidget_( | ||
165 | d, pointerLabel_Command(cmd, "item"), argU32Label_Command(cmd, "arg")); | ||
166 | return iTrue; | ||
167 | } | ||
168 | else if (isCommand_Widget(w, ev, "ident.use")) { | ||
169 | iGmIdentity * ident = menuIdentity_CertListWidget_(d); | ||
170 | const iString *tabUrl = url_DocumentWidget(document_App()); | ||
171 | if (ident) { | ||
172 | if (argLabel_Command(cmd, "clear")) { | ||
173 | clearUse_GmIdentity(ident); | ||
174 | } | ||
175 | else if (arg_Command(cmd)) { | ||
176 | signIn_GmCerts(certs_App(), ident, tabUrl); | ||
177 | postCommand_App("navigate.reload"); | ||
178 | } | ||
179 | else { | ||
180 | signOut_GmCerts(certs_App(), tabUrl); | ||
181 | postCommand_App("navigate.reload"); | ||
182 | } | ||
183 | saveIdentities_GmCerts(certs_App()); | ||
184 | updateItems_CertListWidget(d); | ||
185 | } | ||
186 | return iTrue; | ||
187 | } | ||
188 | else if (isCommand_Widget(w, ev, "ident.edit")) { | ||
189 | const iGmIdentity *ident = menuIdentity_CertListWidget_(d); | ||
190 | if (ident) { | ||
191 | makeValueInput_Widget(get_Root()->widget, | ||
192 | &ident->notes, | ||
193 | uiHeading_ColorEscape "${heading.ident.notes}", | ||
194 | format_CStr(cstr_Lang("dlg.ident.notes"), cstr_String(name_GmIdentity(ident))), | ||
195 | uiTextAction_ColorEscape "${dlg.default}", | ||
196 | format_CStr("!ident.setnotes ident:%p ptr:%p", ident, d)); | ||
197 | } | ||
198 | return iTrue; | ||
199 | } | ||
200 | else if (isCommand_Widget(w, ev, "ident.fingerprint")) { | ||
201 | const iGmIdentity *ident = menuIdentity_CertListWidget_(d); | ||
202 | if (ident) { | ||
203 | const iString *fps = collect_String( | ||
204 | hexEncode_Block(collect_Block(fingerprint_TlsCertificate(ident->cert)))); | ||
205 | SDL_SetClipboardText(cstr_String(fps)); | ||
206 | } | ||
207 | return iTrue; | ||
208 | } | ||
209 | else if (isCommand_Widget(w, ev, "ident.export")) { | ||
210 | const iGmIdentity *ident = menuIdentity_CertListWidget_(d); | ||
211 | if (ident) { | ||
212 | iString *pem = collect_String(pem_TlsCertificate(ident->cert)); | ||
213 | append_String(pem, collect_String(privateKeyPem_TlsCertificate(ident->cert))); | ||
214 | iDocumentWidget *expTab = newTab_App(NULL, iTrue); | ||
215 | setUrlAndSource_DocumentWidget( | ||
216 | expTab, | ||
217 | collectNewFormat_String("file:%s.pem", cstr_String(name_GmIdentity(ident))), | ||
218 | collectNewCStr_String("text/plain"), | ||
219 | utf8_String(pem)); | ||
220 | } | ||
221 | return iTrue; | ||
222 | } | ||
223 | else if (isCommand_Widget(w, ev, "ident.setnotes")) { | ||
224 | iGmIdentity *ident = pointerLabel_Command(cmd, "ident"); | ||
225 | if (ident) { | ||
226 | setCStr_String(&ident->notes, suffixPtr_Command(cmd, "value")); | ||
227 | updateItems_CertListWidget(d); | ||
228 | } | ||
229 | return iTrue; | ||
230 | } | ||
231 | else if (isCommand_Widget(w, ev, "ident.pickicon")) { | ||
232 | return iTrue; | ||
233 | } | ||
234 | else if (isCommand_Widget(w, ev, "ident.reveal")) { | ||
235 | const iGmIdentity *ident = menuIdentity_CertListWidget_(d); | ||
236 | if (ident) { | ||
237 | const iString *crtPath = certificatePath_GmCerts(certs_App(), ident); | ||
238 | if (crtPath) { | ||
239 | revealPath_App(crtPath); | ||
240 | } | ||
241 | } | ||
242 | return iTrue; | ||
243 | } | ||
244 | else if (isCommand_Widget(w, ev, "ident.delete")) { | ||
245 | iCertItem *item = d->contextItem; | ||
246 | if (argLabel_Command(cmd, "confirm")) { | ||
247 | makeQuestion_Widget( | ||
248 | uiTextCaution_ColorEscape "${heading.ident.delete}", | ||
249 | format_CStr(cstr_Lang("dlg.confirm.ident.delete"), | ||
250 | uiTextAction_ColorEscape, | ||
251 | cstr_String(&item->label), | ||
252 | uiText_ColorEscape), | ||
253 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, | ||
254 | { uiTextCaution_ColorEscape "${dlg.ident.delete}", | ||
255 | 0, | ||
256 | 0, | ||
257 | format_CStr("!ident.delete confirm:0 ptr:%p", d) } }, | ||
258 | 2); | ||
259 | return iTrue; | ||
260 | } | ||
261 | deleteIdentity_GmCerts(certs_App(), menuIdentity_CertListWidget_(d)); | ||
262 | postCommand_App("idents.changed"); | ||
263 | return iTrue; | ||
264 | } | ||
265 | } | ||
266 | if (ev->type == SDL_MOUSEMOTION && !isVisible_Widget(d->menu)) { | ||
267 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); | ||
268 | /* Update cursor. */ | ||
269 | if (contains_Widget(w, mouse)) { | ||
270 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | ||
271 | } | ||
272 | else if (d->contextIndex != iInvalidPos) { | ||
273 | invalidateItem_ListWidget(&d->list, d->contextIndex); | ||
274 | d->contextIndex = iInvalidPos; | ||
275 | } | ||
276 | } | ||
277 | /* Update context menu items. */ | ||
278 | if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT) { | ||
279 | d->contextItem = NULL; | ||
280 | if (!isVisible_Widget(d->menu)) { | ||
281 | updateMouseHover_ListWidget(&d->list); | ||
282 | } | ||
283 | if (constHoverItem_ListWidget(&d->list) || isVisible_Widget(d->menu)) { | ||
284 | d->contextItem = hoverItem_ListWidget(&d->list); | ||
285 | /* Context is drawn in hover state. */ | ||
286 | if (d->contextIndex != iInvalidPos) { | ||
287 | invalidateItem_ListWidget(&d->list, d->contextIndex); | ||
288 | } | ||
289 | d->contextIndex = hoverItemIndex_ListWidget(&d->list); | ||
290 | updateContextMenu_CertListWidget_(d); | ||
291 | /* TODO: Some callback-based mechanism would be nice for updating menus right | ||
292 | before they open? At least move these to `updateContextMenu_ */ | ||
293 | const iGmIdentity *ident = constHoverIdentity_CertListWidget(d); | ||
294 | const iString * docUrl = url_DocumentWidget(document_App()); | ||
295 | iForEach(ObjectList, i, children_Widget(d->menu)) { | ||
296 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | ||
297 | iLabelWidget *menuItem = i.object; | ||
298 | const char * cmdItem = cstr_String(command_LabelWidget(menuItem)); | ||
299 | if (equal_Command(cmdItem, "ident.use")) { | ||
300 | const iBool cmdUse = arg_Command(cmdItem) != 0; | ||
301 | const iBool cmdClear = argLabel_Command(cmdItem, "clear") != 0; | ||
302 | setFlags_Widget( | ||
303 | as_Widget(menuItem), | ||
304 | disabled_WidgetFlag, | ||
305 | (cmdClear && !isUsed_GmIdentity(ident)) || | ||
306 | (!cmdClear && cmdUse && isUsedOn_GmIdentity(ident, docUrl)) || | ||
307 | (!cmdClear && !cmdUse && !isUsedOn_GmIdentity(ident, docUrl))); | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | if (hoverItem_ListWidget(&d->list) || isVisible_Widget(d->menu)) { | ||
313 | processContextMenuEvent_Widget(d->menu, ev, {}); | ||
314 | } | ||
315 | } | ||
316 | return ((iWidgetClass *) class_Widget(w)->super)->processEvent(w, ev); | ||
317 | } | ||
318 | |||
319 | static void draw_CertListWidget_(const iCertListWidget *d) { | ||
320 | const iWidget *w = constAs_Widget(d); | ||
321 | ((iWidgetClass *) class_Widget(w)->super)->draw(w); | ||
322 | } | ||
323 | |||
324 | static void draw_CertItem_(const iCertItem *d, iPaint *p, iRect itemRect, | ||
325 | const iListWidget *list) { | ||
326 | const iCertListWidget *certList = (const iCertListWidget *) list; | ||
327 | const iBool isMenuVisible = isVisible_Widget(certList->menu); | ||
328 | const iBool isDragging = constDragItem_ListWidget(list) == d; | ||
329 | const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging; | ||
330 | const iBool isHover = | ||
331 | (!isMenuVisible && | ||
332 | isHover_Widget(constAs_Widget(list)) && | ||
333 | constHoverItem_ListWidget(list) == d) || | ||
334 | (isMenuVisible && certList->contextItem == d) || | ||
335 | isDragging; | ||
336 | const int itemHeight = height_Rect(itemRect); | ||
337 | const int iconColor = isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) | ||
338 | : uiIcon_ColorId; | ||
339 | const int altIconColor = isPressing ? uiTextPressed_ColorId : uiTextCaution_ColorId; | ||
340 | const int font = certList->itemFonts[d->isBold ? 1 : 0]; | ||
341 | int bg = uiBackgroundSidebar_ColorId; | ||
342 | if (isHover) { | ||
343 | bg = isPressing ? uiBackgroundPressed_ColorId | ||
344 | : uiBackgroundFramelessHover_ColorId; | ||
345 | fillRect_Paint(p, itemRect, bg); | ||
346 | } | ||
347 | else if (d->listItem.isSelected) { | ||
348 | bg = uiBackgroundUnfocusedSelection_ColorId; | ||
349 | fillRect_Paint(p, itemRect, bg); | ||
350 | } | ||
351 | // iInt2 pos = itemRect.pos; | ||
352 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
353 | : uiTextStrong_ColorId; | ||
354 | const iBool isUsedOnDomain = (d->indent != 0); | ||
355 | iString icon; | ||
356 | initUnicodeN_String(&icon, &d->icon, 1); | ||
357 | iInt2 cPos = topLeft_Rect(itemRect); | ||
358 | const int indent = 1.4f * lineHeight_Text(font); | ||
359 | addv_I2(&cPos, | ||
360 | init_I2(3 * gap_UI, | ||
361 | (itemHeight - lineHeight_Text(uiLabel_FontId) * 2 - lineHeight_Text(font)) / | ||
362 | 2)); | ||
363 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId | ||
364 | : uiTextFramelessHover_ColorId) | ||
365 | : uiTextDim_ColorId; | ||
366 | if (!d->listItem.isSelected && !isUsedOnDomain) { | ||
367 | drawOutline_Text(font, cPos, metaFg, none_ColorId, range_String(&icon)); | ||
368 | } | ||
369 | drawRange_Text(font, | ||
370 | cPos, | ||
371 | d->listItem.isSelected ? iconColor | ||
372 | : isUsedOnDomain ? altIconColor | ||
373 | : uiBackgroundSidebar_ColorId, | ||
374 | range_String(&icon)); | ||
375 | deinit_String(&icon); | ||
376 | drawRange_Text(d->listItem.isSelected ? certList->itemFonts[1] : font, | ||
377 | add_I2(cPos, init_I2(indent, 0)), | ||
378 | fg, | ||
379 | range_String(&d->label)); | ||
380 | drawRange_Text(uiLabel_FontId, | ||
381 | add_I2(cPos, init_I2(indent, lineHeight_Text(font))), | ||
382 | metaFg, | ||
383 | range_String(&d->meta)); | ||
384 | } | ||
385 | |||
386 | void init_CertListWidget(iCertListWidget *d) { | ||
387 | iWidget *w = as_Widget(d); | ||
388 | init_ListWidget(&d->list); | ||
389 | setId_Widget(w, "certlist"); | ||
390 | setBackgroundColor_Widget(w, none_ColorId); | ||
391 | d->itemFonts[0] = uiContent_FontId; | ||
392 | d->itemFonts[1] = uiContentBold_FontId; | ||
393 | #if defined (iPlatformMobile) | ||
394 | if (deviceType_App() == phone_AppDeviceType) { | ||
395 | d->itemFonts[0] = uiLabelBig_FontId; | ||
396 | d->itemFonts[1] = uiLabelBigBold_FontId; | ||
397 | } | ||
398 | #endif | ||
399 | d->menu = NULL; | ||
400 | d->contextItem = NULL; | ||
401 | d->contextIndex = iInvalidPos; | ||
402 | } | ||
403 | |||
404 | void updateItemHeight_CertListWidget(iCertListWidget *d) { | ||
405 | setItemHeight_ListWidget(&d->list, 3.5f * lineHeight_Text(d->itemFonts[0])); | ||
406 | } | ||
407 | |||
408 | iBool updateItems_CertListWidget(iCertListWidget *d) { | ||
409 | clear_ListWidget(&d->list); | ||
410 | destroy_Widget(d->menu); | ||
411 | d->menu = NULL; | ||
412 | const iString *tabUrl = url_DocumentWidget(document_App()); | ||
413 | const iRangecc tabHost = urlHost_String(tabUrl); | ||
414 | iBool haveItems = iFalse; | ||
415 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { | ||
416 | const iGmIdentity *ident = i.ptr; | ||
417 | iCertItem *item = new_CertItem(); | ||
418 | item->id = (uint32_t) index_PtrArrayConstIterator(&i); | ||
419 | item->icon = 0x1f464; /* person */ | ||
420 | set_String(&item->label, name_GmIdentity(ident)); | ||
421 | iDate until; | ||
422 | validUntil_TlsCertificate(ident->cert, &until); | ||
423 | const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); | ||
424 | format_String(&item->meta, | ||
425 | "%s", | ||
426 | isActive ? cstr_Lang("ident.using") | ||
427 | : isUsed_GmIdentity(ident) | ||
428 | ? formatCStrs_Lang("ident.usedonurls.n", size_StringSet(ident->useUrls)) | ||
429 | : cstr_Lang("ident.notused")); | ||
430 | const char *expiry = | ||
431 | ident->flags & temporary_GmIdentityFlag | ||
432 | ? cstr_Lang("ident.temporary") | ||
433 | : cstrCollect_String(format_Date(&until, cstr_Lang("ident.expiry"))); | ||
434 | if (isEmpty_String(&ident->notes)) { | ||
435 | appendFormat_String(&item->meta, "\n%s", expiry); | ||
436 | } | ||
437 | else { | ||
438 | appendFormat_String(&item->meta, | ||
439 | " \u2014 %s\n%s%s", | ||
440 | expiry, | ||
441 | escape_Color(uiHeading_ColorId), | ||
442 | cstr_String(&ident->notes)); | ||
443 | } | ||
444 | item->listItem.isSelected = isActive; | ||
445 | if (isUsedOnDomain_GmIdentity(ident, tabHost)) { | ||
446 | item->indent = 1; /* will be highlighted */ | ||
447 | } | ||
448 | addItem_ListWidget(&d->list, item); | ||
449 | haveItems = iTrue; | ||
450 | iRelease(item); | ||
451 | } | ||
452 | return haveItems; | ||
453 | } | ||
454 | |||
455 | void deinit_CertListWidget(iCertListWidget *d) { | ||
456 | iUnused(d); | ||
457 | } | ||
458 | |||
459 | const iGmIdentity *constHoverIdentity_CertListWidget(const iCertListWidget *d) { | ||
460 | const iCertItem *hoverItem = constHoverItem_ListWidget(&d->list); | ||
461 | if (hoverItem) { | ||
462 | return identity_GmCerts(certs_App(), hoverItem->id); | ||
463 | } | ||
464 | return NULL; | ||
465 | } | ||
466 | |||
467 | iGmIdentity *hoverIdentity_CertListWidget(const iCertListWidget *d) { | ||
468 | return iConstCast(iGmIdentity *, constHoverIdentity_CertListWidget(d)); | ||
469 | } | ||
470 | |||
471 | iBeginDefineSubclass(CertListWidget, ListWidget) | ||
472 | .processEvent = (iAny *) processEvent_CertListWidget_, | ||
473 | .draw = (iAny *) draw_CertListWidget_, | ||
474 | iEndDefineSubclass(CertListWidget) | ||
diff --git a/src/ui/certlistwidget.h b/src/ui/certlistwidget.h new file mode 100644 index 00000000..2e5f6247 --- /dev/null +++ b/src/ui/certlistwidget.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #pragma once | ||
24 | |||
25 | #include "listwidget.h" | ||
26 | |||
27 | iDeclareType(CertListWidget) | ||
28 | |||
29 | typedef iListWidgetClass iCertListWidgetClass; | ||
30 | extern iCertListWidgetClass Class_CertListWidget; | ||
31 | |||
32 | iDeclareObjectConstruction(CertListWidget) | ||
33 | |||
34 | iDeclareType(GmIdentity) | ||
35 | |||
36 | const iGmIdentity * constHoverIdentity_CertListWidget(const iCertListWidget *); | ||
37 | iGmIdentity * hoverIdentity_CertListWidget (const iCertListWidget *); | ||
38 | |||
39 | iBool updateItems_CertListWidget (iCertListWidget *); /* returns False is empty */ | ||
40 | void updateItemHeight_CertListWidget (iCertListWidget *); | ||
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index c2ba5581..82e4e451 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c | |||
@@ -49,21 +49,6 @@ iDefineClass(ListItem) | |||
49 | 49 | ||
50 | iDefineObjectConstruction(ListWidget) | 50 | iDefineObjectConstruction(ListWidget) |
51 | 51 | ||
52 | struct Impl_ListWidget { | ||
53 | iWidget widget; | ||
54 | iScrollWidget *scroll; | ||
55 | iSmoothScroll scrollY; | ||
56 | int itemHeight; | ||
57 | iPtrArray items; | ||
58 | size_t hoverItem; | ||
59 | size_t dragItem; | ||
60 | iInt2 dragOrigin; /* offset from mouse to drag item's top-left corner */ | ||
61 | iClick click; | ||
62 | iIntSet invalidItems; | ||
63 | iVisBuf *visBuf; | ||
64 | iBool noHoverWhileScrolling; | ||
65 | }; | ||
66 | |||
67 | static void refreshWhileScrolling_ListWidget_(iAnyObject *any) { | 52 | static void refreshWhileScrolling_ListWidget_(iAnyObject *any) { |
68 | iListWidget *d = any; | 53 | iListWidget *d = any; |
69 | updateVisible_ListWidget(d); | 54 | updateVisible_ListWidget(d); |
diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h index 8adf6ac3..7e6624a0 100644 --- a/src/ui/listwidget.h +++ b/src/ui/listwidget.h | |||
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include "scrollwidget.h" | 25 | #include "scrollwidget.h" |
26 | #include "paint.h" | 26 | #include "paint.h" |
27 | 27 | ||
28 | #include <the_Foundation/intset.h> | ||
28 | #include <the_Foundation/ptrarray.h> | 29 | #include <the_Foundation/ptrarray.h> |
29 | 30 | ||
30 | iDeclareType(ListWidget) | 31 | iDeclareType(ListWidget) |
@@ -48,6 +49,25 @@ iDeclareObjectConstruction(ListItem) | |||
48 | iDeclareWidgetClass(ListWidget) | 49 | iDeclareWidgetClass(ListWidget) |
49 | iDeclareObjectConstruction(ListWidget) | 50 | iDeclareObjectConstruction(ListWidget) |
50 | 51 | ||
52 | iDeclareType(VisBuf) | ||
53 | |||
54 | struct Impl_ListWidget { | ||
55 | iWidget widget; | ||
56 | iScrollWidget *scroll; | ||
57 | iSmoothScroll scrollY; | ||
58 | int itemHeight; | ||
59 | iPtrArray items; | ||
60 | size_t hoverItem; | ||
61 | size_t dragItem; | ||
62 | iInt2 dragOrigin; /* offset from mouse to drag item's top-left corner */ | ||
63 | iClick click; | ||
64 | iIntSet invalidItems; | ||
65 | iVisBuf *visBuf; | ||
66 | iBool noHoverWhileScrolling; | ||
67 | }; | ||
68 | |||
69 | void init_ListWidget (iListWidget *); | ||
70 | |||
51 | void setItemHeight_ListWidget (iListWidget *, int itemHeight); | 71 | void setItemHeight_ListWidget (iListWidget *, int itemHeight); |
52 | 72 | ||
53 | void invalidate_ListWidget (iListWidget *); | 73 | void invalidate_ListWidget (iListWidget *); |
diff --git a/src/ui/root.c b/src/ui/root.c index f06ae842..5ed6b529 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -332,7 +332,8 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
332 | return iTrue; | 332 | return iTrue; |
333 | } | 333 | } |
334 | else if (equal_Command(cmd, "identmenu.open")) { | 334 | else if (equal_Command(cmd, "identmenu.open")) { |
335 | iWidget *button = findWidget_Root("navbar.ident"); | 335 | iWidget *toolBar = findWidget_Root("toolbar"); |
336 | iWidget *button = findWidget_Root(toolBar ? "toolbar.ident" : "navbar.ident"); | ||
336 | iArray items; | 337 | iArray items; |
337 | init_Array(&items, sizeof(iMenuItem)); | 338 | init_Array(&items, sizeof(iMenuItem)); |
338 | /* Current identity. */ | 339 | /* Current identity. */ |
@@ -375,16 +376,21 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
375 | (iMenuItem[]){ | 376 | (iMenuItem[]){ |
376 | { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" }, | 377 | { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" }, |
377 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, | 378 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, |
378 | { "---" }, | 379 | { "---" } }, 3); |
380 | if (deviceType_App() == desktop_AppDeviceType) { | ||
381 | pushBack_Array(&items, &(iMenuItem) | ||
379 | { isVisible_Widget(sidebar) && mode_SidebarWidget(sidebar) == identities_SidebarMode | 382 | { isVisible_Widget(sidebar) && mode_SidebarWidget(sidebar) == identities_SidebarMode |
380 | ? leftHalf_Icon " ${menu.hide.identities}" | 383 | ? leftHalf_Icon " ${menu.hide.identities}" |
381 | : leftHalf_Icon " ${menu.show.identities}", | 384 | : leftHalf_Icon " ${menu.show.identities}", |
382 | 0, | 385 | 0, |
383 | 0, | 386 | 0, |
384 | deviceType_App() == phone_AppDeviceType ? "toolbar.showident" | 387 | deviceType_App() == phone_AppDeviceType ? "toolbar.showident" |
385 | : "sidebar.mode arg:3 toggle:1" }, | 388 | : "sidebar.mode arg:3 toggle:1" }); |
386 | }, | 389 | } |
387 | 4); | 390 | else { |
391 | pushBack_Array(&items, &(iMenuItem){ gear_Icon " ${menu.identities}", 0, 0, | ||
392 | "toolbar.showident"}); | ||
393 | } | ||
388 | iWidget *menu = | 394 | iWidget *menu = |
389 | makeMenu_Widget(button, constData_Array(&items), size_Array(&items)); | 395 | makeMenu_Widget(button, constData_Array(&items), size_Array(&items)); |
390 | openMenu_Widget(menu, topLeft_Rect(bounds_Widget(button))); | 396 | openMenu_Widget(menu, topLeft_Rect(bounds_Widget(button))); |
@@ -1475,7 +1481,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1475 | frameless_WidgetFlag), | 1481 | frameless_WidgetFlag), |
1476 | "toolbar.forward"); | 1482 | "toolbar.forward"); |
1477 | setId_Widget(addChildFlags_Widget(toolBar, | 1483 | setId_Widget(addChildFlags_Widget(toolBar, |
1478 | iClob(newLargeIcon_LabelWidget("\U0001f464", "toolbar.showident")), | 1484 | iClob(newLargeIcon_LabelWidget("\U0001f464", "identmenu.open")), |
1479 | frameless_WidgetFlag), | 1485 | frameless_WidgetFlag), |
1480 | "toolbar.ident"); | 1486 | "toolbar.ident"); |
1481 | setId_Widget(addChildFlags_Widget(toolBar, | 1487 | setId_Widget(addChildFlags_Widget(toolBar, |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 9cfc507a..8440a597 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" |
@@ -96,6 +97,7 @@ struct Impl_SidebarWidget { | |||
96 | iString cmdPrefix; | 97 | iString cmdPrefix; |
97 | iWidget * blank; | 98 | iWidget * blank; |
98 | iListWidget * list; | 99 | iListWidget * list; |
100 | iCertListWidget * certList; | ||
99 | iWidget * actions; /* below the list, area for buttons */ | 101 | iWidget * actions; /* below the list, area for buttons */ |
100 | int modeScroll[max_SidebarMode]; | 102 | int modeScroll[max_SidebarMode]; |
101 | iLabelWidget * modeButtons[max_SidebarMode]; | 103 | iLabelWidget * modeButtons[max_SidebarMode]; |
@@ -114,6 +116,10 @@ struct Impl_SidebarWidget { | |||
114 | 116 | ||
115 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) | 117 | iDefineObjectConstructionArgs(SidebarWidget, (enum iSidebarSide side), side) |
116 | 118 | ||
119 | iLocalDef iListWidget *list_SidebarWidget_(iSidebarWidget *d) { | ||
120 | return d->mode == identities_SidebarMode ? (iListWidget *) d->certList : d->list; | ||
121 | } | ||
122 | |||
117 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { | 123 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { |
118 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; | 124 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; |
119 | } | 125 | } |
@@ -195,65 +201,6 @@ static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const cha | |||
195 | return btn; | 201 | return btn; |
196 | } | 202 | } |
197 | 203 | ||
198 | static iGmIdentity *menuIdentity_SidebarWidget_(const iSidebarWidget *d) { | ||
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 | } | ||
211 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | ||
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 | } | ||
256 | |||
257 | static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { | 204 | static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBookmark *bm) { |
258 | while (bm->parentId) { | 205 | while (bm->parentId) { |
259 | if (contains_IntSet(d->closedFolders, bm->parentId)) { | 206 | if (contains_IntSet(d->closedFolders, bm->parentId)) { |
@@ -547,46 +494,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
547 | break; | 494 | break; |
548 | } | 495 | } |
549 | case identities_SidebarMode: { | 496 | case identities_SidebarMode: { |
550 | const iString *tabUrl = url_DocumentWidget(document_App()); | 497 | isEmpty = !updateItems_CertListWidget(d->certList); |
551 | const iRangecc tabHost = urlHost_String(tabUrl); | ||
552 | isEmpty = iTrue; | ||
553 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { | ||
554 | const iGmIdentity *ident = i.ptr; | ||
555 | iSidebarItem *item = new_SidebarItem(); | ||
556 | item->id = (uint32_t) index_PtrArrayConstIterator(&i); | ||
557 | item->icon = 0x1f464; /* person */ | ||
558 | set_String(&item->label, name_GmIdentity(ident)); | ||
559 | iDate until; | ||
560 | validUntil_TlsCertificate(ident->cert, &until); | ||
561 | const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); | ||
562 | format_String(&item->meta, | ||
563 | "%s", | ||
564 | isActive ? cstr_Lang("ident.using") | ||
565 | : isUsed_GmIdentity(ident) | ||
566 | ? formatCStrs_Lang("ident.usedonurls.n", size_StringSet(ident->useUrls)) | ||
567 | : cstr_Lang("ident.notused")); | ||
568 | const char *expiry = | ||
569 | ident->flags & temporary_GmIdentityFlag | ||
570 | ? cstr_Lang("ident.temporary") | ||
571 | : cstrCollect_String(format_Date(&until, cstr_Lang("ident.expiry"))); | ||
572 | if (isEmpty_String(&ident->notes)) { | ||
573 | appendFormat_String(&item->meta, "\n%s", expiry); | ||
574 | } | ||
575 | else { | ||
576 | appendFormat_String(&item->meta, | ||
577 | " \u2014 %s\n%s%s", | ||
578 | expiry, | ||
579 | escape_Color(uiHeading_ColorId), | ||
580 | cstr_String(&ident->notes)); | ||
581 | } | ||
582 | item->listItem.isSelected = isActive; | ||
583 | if (isUsedOnDomain_GmIdentity(ident, tabHost)) { | ||
584 | item->indent = 1; /* will be highlighted */ | ||
585 | } | ||
586 | addItem_ListWidget(d->list, item); | ||
587 | iRelease(item); | ||
588 | isEmpty = iFalse; | ||
589 | } | ||
590 | /* Actions. */ | 498 | /* Actions. */ |
591 | if (!isEmpty) { | 499 | if (!isEmpty) { |
592 | addActionButton_SidebarWidget_(d, add_Icon " ${sidebar.action.ident.new}", "ident.new", 0); | 500 | addActionButton_SidebarWidget_(d, add_Icon " ${sidebar.action.ident.new}", "ident.new", 0); |
@@ -597,9 +505,11 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
597 | default: | 505 | default: |
598 | break; | 506 | break; |
599 | } | 507 | } |
600 | scrollOffset_ListWidget(d->list, 0); | 508 | setFlags_Widget(as_Widget(d->list), hidden_WidgetFlag, d->mode == identities_SidebarMode); |
601 | updateVisible_ListWidget(d->list); | 509 | setFlags_Widget(as_Widget(d->certList), hidden_WidgetFlag, d->mode != identities_SidebarMode); |
602 | invalidate_ListWidget(d->list); | 510 | scrollOffset_ListWidget(list_SidebarWidget_(d), 0); |
511 | updateVisible_ListWidget(list_SidebarWidget_(d)); | ||
512 | invalidate_ListWidget(list_SidebarWidget_(d)); | ||
603 | /* Content for a blank tab. */ | 513 | /* Content for a blank tab. */ |
604 | if (isEmpty) { | 514 | if (isEmpty) { |
605 | if (d->mode == feeds_SidebarMode) { | 515 | if (d->mode == feeds_SidebarMode) { |
@@ -649,7 +559,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
649 | #endif | 559 | #endif |
650 | arrange_Widget(d->actions); | 560 | arrange_Widget(d->actions); |
651 | arrange_Widget(as_Widget(d)); | 561 | arrange_Widget(as_Widget(d)); |
652 | updateMouseHover_ListWidget(d->list); | 562 | updateMouseHover_ListWidget(list_SidebarWidget_(d)); |
653 | } | 563 | } |
654 | 564 | ||
655 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | 565 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { |
@@ -668,10 +578,11 @@ static size_t findItem_SidebarWidget_(const iSidebarWidget *d, int id) { | |||
668 | } | 578 | } |
669 | 579 | ||
670 | static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { | 580 | static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { |
581 | const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f }; | ||
671 | if (d->list) { | 582 | if (d->list) { |
672 | const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f }; | ||
673 | setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0])); | 583 | setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0])); |
674 | } | 584 | } |
585 | updateItemHeight_CertListWidget(d->certList); | ||
675 | } | 586 | } |
676 | 587 | ||
677 | iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | 588 | iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { |
@@ -679,18 +590,18 @@ iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | |||
679 | return iFalse; | 590 | return iFalse; |
680 | } | 591 | } |
681 | if (d->mode >= 0 && d->mode < max_SidebarMode) { | 592 | if (d->mode >= 0 && d->mode < max_SidebarMode) { |
682 | d->modeScroll[d->mode] = scrollPos_ListWidget(d->list); /* saved for later */ | 593 | d->modeScroll[d->mode] = scrollPos_ListWidget(list_SidebarWidget_(d)); /* saved for later */ |
683 | } | 594 | } |
684 | d->mode = mode; | 595 | d->mode = mode; |
685 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | 596 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { |
686 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | 597 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); |
687 | } | 598 | } |
688 | setBackgroundColor_Widget(as_Widget(d->list), | 599 | setBackgroundColor_Widget(as_Widget(list_SidebarWidget_(d)), |
689 | d->mode == documentOutline_SidebarMode ? tmBannerBackground_ColorId | 600 | d->mode == documentOutline_SidebarMode ? tmBannerBackground_ColorId |
690 | : uiBackgroundSidebar_ColorId); | 601 | : uiBackgroundSidebar_ColorId); |
691 | updateItemHeight_SidebarWidget_(d); | 602 | updateItemHeight_SidebarWidget_(d); |
692 | /* Restore previous scroll position. */ | 603 | /* Restore previous scroll position. */ |
693 | setScrollPos_ListWidget(d->list, d->modeScroll[mode]); | 604 | setScrollPos_ListWidget(list_SidebarWidget_(d), d->modeScroll[mode]); |
694 | return iTrue; | 605 | return iTrue; |
695 | } | 606 | } |
696 | 607 | ||
@@ -787,6 +698,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
787 | iZap(d->modeButtons); | 698 | iZap(d->modeButtons); |
788 | d->resizer = NULL; | 699 | d->resizer = NULL; |
789 | d->list = NULL; | 700 | d->list = NULL; |
701 | d->certList = NULL; | ||
790 | d->actions = NULL; | 702 | d->actions = NULL; |
791 | d->closedFolders = new_IntSet(); | 703 | d->closedFolders = new_IntSet(); |
792 | /* On a phone, the right sidebar is used exclusively for Identities. */ | 704 | /* On a phone, the right sidebar is used exclusively for Identities. */ |
@@ -829,10 +741,16 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
829 | setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); | 741 | setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); |
830 | iWidget *listAndActions = makeVDiv_Widget(); | 742 | iWidget *listAndActions = makeVDiv_Widget(); |
831 | addChild_Widget(content, iClob(listAndActions)); | 743 | addChild_Widget(content, iClob(listAndActions)); |
832 | d->list = new_ListWidget(); | 744 | iWidget *listArea = new_Widget(); |
745 | setFlags_Widget(listArea, resizeChildren_WidgetFlag, iTrue); | ||
746 | d->list = new_ListWidget(); | ||
833 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); | 747 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); |
748 | addChild_Widget(listArea, iClob(d->list)); | ||
749 | d->certList = new_CertListWidget(); | ||
750 | setPadding_Widget(as_Widget(d->certList), 0, gap_UI, 0, gap_UI); | ||
751 | addChild_Widget(listArea, iClob(d->certList)); | ||
834 | addChildFlags_Widget(listAndActions, | 752 | addChildFlags_Widget(listAndActions, |
835 | iClob(d->list), | 753 | iClob(listArea), |
836 | expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); | 754 | expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); |
837 | setId_Widget(addChildPosFlags_Widget(listAndActions, | 755 | setId_Widget(addChildPosFlags_Widget(listAndActions, |
838 | iClob(d->actions = new_Widget()), | 756 | iClob(d->actions = new_Widget()), |
@@ -892,16 +810,16 @@ iBool setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { | |||
892 | 810 | ||
893 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | 811 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { |
894 | if (d->mode == identities_SidebarMode) { | 812 | if (d->mode == identities_SidebarMode) { |
895 | const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list); | 813 | return constHoverIdentity_CertListWidget(d->certList); |
896 | if (hoverItem) { | ||
897 | return identity_GmCerts(certs_App(), hoverItem->id); | ||
898 | } | ||
899 | } | 814 | } |
900 | return NULL; | 815 | return NULL; |
901 | } | 816 | } |
902 | 817 | ||
903 | static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | 818 | static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { |
904 | return iConstCast(iGmIdentity *, constHoverIdentity_SidebarWidget_(d)); | 819 | if (d->mode == identities_SidebarMode) { |
820 | return hoverIdentity_CertListWidget(d->certList); | ||
821 | } | ||
822 | return NULL; | ||
905 | } | 823 | } |
906 | 824 | ||
907 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, size_t itemIndex) { | 825 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, size_t itemIndex) { |
@@ -944,23 +862,6 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
944 | } | 862 | } |
945 | break; | 863 | break; |
946 | } | 864 | } |
947 | case identities_SidebarMode: { | ||
948 | d->contextItem = item; | ||
949 | if (d->contextIndex != iInvalidPos) { | ||
950 | invalidateItem_ListWidget(d->list, d->contextIndex); | ||
951 | } | ||
952 | d->contextIndex = itemIndex; | ||
953 | if (itemIndex < numItems_ListWidget(d->list)) { | ||
954 | updateContextMenu_SidebarWidget_(d); | ||
955 | arrange_Widget(d->menu); | ||
956 | openMenu_Widget(d->menu, | ||
957 | d->side == left_SidebarSide | ||
958 | ? topRight_Rect(itemRect_ListWidget(d->list, itemIndex)) | ||
959 | : addX_I2(topLeft_Rect(itemRect_ListWidget(d->list, itemIndex)), | ||
960 | -width_Widget(d->menu))); | ||
961 | } | ||
962 | break; | ||
963 | } | ||
964 | default: | 865 | default: |
965 | break; | 866 | break; |
966 | } | 867 | } |
@@ -1245,9 +1146,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1245 | } | 1146 | } |
1246 | } | 1147 | } |
1247 | } | 1148 | } |
1248 | else if (equal_Command(cmd, "idents.changed") && d->mode == identities_SidebarMode) { | ||
1249 | updateItems_SidebarWidget_(d); | ||
1250 | } | ||
1251 | else if (deviceType_App() == tablet_AppDeviceType && equal_Command(cmd, "toolbar.showident")) { | 1149 | else if (deviceType_App() == tablet_AppDeviceType && equal_Command(cmd, "toolbar.showident")) { |
1252 | postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); | 1150 | postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); |
1253 | return iTrue; | 1151 | return iTrue; |
@@ -1316,9 +1214,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1316 | } | 1214 | } |
1317 | return iTrue; | 1215 | return iTrue; |
1318 | } | 1216 | } |
1319 | // else if (isCommand_Widget(w, ev, "menu.closed")) { | ||
1320 | // invalidateItem_ListWidget(d->list, d->contextIndex); | ||
1321 | // } | ||
1322 | else if (isCommand_Widget(w, ev, "bookmark.open")) { | 1217 | else if (isCommand_Widget(w, ev, "bookmark.open")) { |
1323 | const iSidebarItem *item = d->contextItem; | 1218 | const iSidebarItem *item = d->contextItem; |
1324 | if (d->mode == bookmarks_SidebarMode && item) { | 1219 | if (d->mode == bookmarks_SidebarMode && item) { |
@@ -1548,103 +1443,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1548 | } | 1443 | } |
1549 | } | 1444 | } |
1550 | } | 1445 | } |
1551 | else if (isCommand_Widget(w, ev, "ident.use")) { | ||
1552 | iGmIdentity * ident = menuIdentity_SidebarWidget_(d); | ||
1553 | const iString *tabUrl = url_DocumentWidget(document_App()); | ||
1554 | if (ident) { | ||
1555 | if (argLabel_Command(cmd, "clear")) { | ||
1556 | clearUse_GmIdentity(ident); | ||
1557 | } | ||
1558 | else if (arg_Command(cmd)) { | ||
1559 | signIn_GmCerts(certs_App(), ident, tabUrl); | ||
1560 | postCommand_App("navigate.reload"); | ||
1561 | } | ||
1562 | else { | ||
1563 | signOut_GmCerts(certs_App(), tabUrl); | ||
1564 | postCommand_App("navigate.reload"); | ||
1565 | } | ||
1566 | saveIdentities_GmCerts(certs_App()); | ||
1567 | updateItems_SidebarWidget_(d); | ||
1568 | } | ||
1569 | return iTrue; | ||
1570 | } | ||
1571 | else if (isCommand_Widget(w, ev, "ident.edit")) { | ||
1572 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1573 | if (ident) { | ||
1574 | makeValueInput_Widget(get_Root()->widget, | ||
1575 | &ident->notes, | ||
1576 | uiHeading_ColorEscape "${heading.ident.notes}", | ||
1577 | format_CStr(cstr_Lang("dlg.ident.notes"), cstr_String(name_GmIdentity(ident))), | ||
1578 | uiTextAction_ColorEscape "${dlg.default}", | ||
1579 | format_CStr("!ident.setnotes ident:%p ptr:%p", ident, d)); | ||
1580 | } | ||
1581 | return iTrue; | ||
1582 | } | ||
1583 | else if (isCommand_Widget(w, ev, "ident.fingerprint")) { | ||
1584 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1585 | if (ident) { | ||
1586 | const iString *fps = collect_String( | ||
1587 | hexEncode_Block(collect_Block(fingerprint_TlsCertificate(ident->cert)))); | ||
1588 | SDL_SetClipboardText(cstr_String(fps)); | ||
1589 | } | ||
1590 | return iTrue; | ||
1591 | } | ||
1592 | else if (isCommand_Widget(w, ev, "ident.export")) { | ||
1593 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1594 | if (ident) { | ||
1595 | iString *pem = collect_String(pem_TlsCertificate(ident->cert)); | ||
1596 | append_String(pem, collect_String(privateKeyPem_TlsCertificate(ident->cert))); | ||
1597 | iDocumentWidget *expTab = newTab_App(NULL, iTrue); | ||
1598 | setUrlAndSource_DocumentWidget( | ||
1599 | expTab, | ||
1600 | collectNewFormat_String("file:%s.pem", cstr_String(name_GmIdentity(ident))), | ||
1601 | collectNewCStr_String("text/plain"), | ||
1602 | utf8_String(pem)); | ||
1603 | } | ||
1604 | return iTrue; | ||
1605 | } | ||
1606 | else if (isCommand_Widget(w, ev, "ident.setnotes")) { | ||
1607 | iGmIdentity *ident = pointerLabel_Command(cmd, "ident"); | ||
1608 | if (ident) { | ||
1609 | setCStr_String(&ident->notes, suffixPtr_Command(cmd, "value")); | ||
1610 | updateItems_SidebarWidget_(d); | ||
1611 | } | ||
1612 | return iTrue; | ||
1613 | } | ||
1614 | else if (isCommand_Widget(w, ev, "ident.pickicon")) { | ||
1615 | return iTrue; | ||
1616 | } | ||
1617 | else if (isCommand_Widget(w, ev, "ident.reveal")) { | ||
1618 | const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); | ||
1619 | if (ident) { | ||
1620 | const iString *crtPath = certificatePath_GmCerts(certs_App(), ident); | ||
1621 | if (crtPath) { | ||
1622 | revealPath_App(crtPath); | ||
1623 | } | ||
1624 | } | ||
1625 | return iTrue; | ||
1626 | } | ||
1627 | else if (isCommand_Widget(w, ev, "ident.delete")) { | ||
1628 | iSidebarItem *item = d->contextItem; | ||
1629 | if (argLabel_Command(cmd, "confirm")) { | ||
1630 | makeQuestion_Widget( | ||
1631 | uiTextCaution_ColorEscape "${heading.ident.delete}", | ||
1632 | format_CStr(cstr_Lang("dlg.confirm.ident.delete"), | ||
1633 | uiTextAction_ColorEscape, | ||
1634 | cstr_String(&item->label), | ||
1635 | uiText_ColorEscape), | ||
1636 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, | ||
1637 | { uiTextCaution_ColorEscape "${dlg.ident.delete}", | ||
1638 | 0, | ||
1639 | 0, | ||
1640 | format_CStr("!ident.delete confirm:0 ptr:%p", d) } }, | ||
1641 | 2); | ||
1642 | return iTrue; | ||
1643 | } | ||
1644 | deleteIdentity_GmCerts(certs_App(), menuIdentity_SidebarWidget_(d)); | ||
1645 | postCommand_App("idents.changed"); | ||
1646 | return iTrue; | ||
1647 | } | ||
1648 | else if (isCommand_Widget(w, ev, "history.delete")) { | 1446 | else if (isCommand_Widget(w, ev, "history.delete")) { |
1649 | if (d->contextItem && !isEmpty_String(&d->contextItem->url)) { | 1447 | if (d->contextItem && !isEmpty_String(&d->contextItem->url)) { |
1650 | removeUrl_Visited(visited_App(), &d->contextItem->url); | 1448 | removeUrl_Visited(visited_App(), &d->contextItem->url); |
@@ -1698,14 +1496,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1698 | /* Update cursor. */ | 1496 | /* Update cursor. */ |
1699 | else if (contains_Widget(w, mouse)) { | 1497 | else if (contains_Widget(w, mouse)) { |
1700 | const iSidebarItem *item = constHoverItem_ListWidget(d->list); | 1498 | const iSidebarItem *item = constHoverItem_ListWidget(d->list); |
1701 | if (item && d->mode != identities_SidebarMode) { | 1499 | setCursor_Window(get_Window(), |
1702 | setCursor_Window(get_Window(), | 1500 | item ? (item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW |
1703 | item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW | 1501 | : SDL_SYSTEM_CURSOR_HAND) |
1704 | : SDL_SYSTEM_CURSOR_HAND); | 1502 | : SDL_SYSTEM_CURSOR_ARROW); |
1705 | } | ||
1706 | else { | ||
1707 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | ||
1708 | } | ||
1709 | } | 1503 | } |
1710 | if (d->contextIndex != iInvalidPos) { | 1504 | if (d->contextIndex != iInvalidPos) { |
1711 | invalidateItem_ListWidget(d->list, d->contextIndex); | 1505 | invalidateItem_ListWidget(d->list, d->contextIndex); |
@@ -1713,7 +1507,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1713 | } | 1507 | } |
1714 | } | 1508 | } |
1715 | /* Update context menu items. */ | 1509 | /* Update context menu items. */ |
1716 | if ((d->menu || d->mode == identities_SidebarMode) && ev->type == SDL_MOUSEBUTTONDOWN) { | 1510 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { |
1717 | if (ev->button.button == SDL_BUTTON_RIGHT) { | 1511 | if (ev->button.button == SDL_BUTTON_RIGHT) { |
1718 | d->contextItem = NULL; | 1512 | d->contextItem = NULL; |
1719 | if (!isVisible_Widget(d->menu)) { | 1513 | if (!isVisible_Widget(d->menu)) { |
@@ -1726,7 +1520,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1726 | invalidateItem_ListWidget(d->list, d->contextIndex); | 1520 | invalidateItem_ListWidget(d->list, d->contextIndex); |
1727 | } | 1521 | } |
1728 | d->contextIndex = hoverItemIndex_ListWidget(d->list); | 1522 | d->contextIndex = hoverItemIndex_ListWidget(d->list); |
1729 | updateContextMenu_SidebarWidget_(d); | ||
1730 | /* TODO: Some callback-based mechanism would be nice for updating menus right | 1523 | /* TODO: Some callback-based mechanism would be nice for updating menus right |
1731 | before they open? At least move these to `updateContextMenu_ */ | 1524 | before they open? At least move these to `updateContextMenu_ */ |
1732 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { | 1525 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { |
@@ -1756,26 +1549,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1756 | isRead ? circle_Icon " ${feeds.entry.markunread}" | 1549 | isRead ? circle_Icon " ${feeds.entry.markunread}" |
1757 | : circleWhite_Icon " ${feeds.entry.markread}"); | 1550 | : circleWhite_Icon " ${feeds.entry.markread}"); |
1758 | } | 1551 | } |
1759 | else if (d->mode == identities_SidebarMode) { | ||
1760 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); | ||
1761 | const iString * docUrl = url_DocumentWidget(document_App()); | ||
1762 | iForEach(ObjectList, i, children_Widget(d->menu)) { | ||
1763 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | ||
1764 | iLabelWidget *menuItem = i.object; | ||
1765 | const char * cmdItem = cstr_String(command_LabelWidget(menuItem)); | ||
1766 | if (equal_Command(cmdItem, "ident.use")) { | ||
1767 | const iBool cmdUse = arg_Command(cmdItem) != 0; | ||
1768 | const iBool cmdClear = argLabel_Command(cmdItem, "clear") != 0; | ||
1769 | setFlags_Widget( | ||
1770 | as_Widget(menuItem), | ||
1771 | disabled_WidgetFlag, | ||
1772 | (cmdClear && !isUsed_GmIdentity(ident)) || | ||
1773 | (!cmdClear && cmdUse && isUsedOn_GmIdentity(ident, docUrl)) || | ||
1774 | (!cmdClear && !cmdUse && !isUsedOn_GmIdentity(ident, docUrl))); | ||
1775 | } | ||
1776 | } | ||
1777 | } | ||
1778 | } | ||
1779 | } | 1552 | } |
1780 | } | 1553 | } |
1781 | } | 1554 | } |
@@ -2077,40 +1850,6 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2077 | } | 1850 | } |
2078 | iEndCollect(); | 1851 | iEndCollect(); |
2079 | } | 1852 | } |
2080 | else if (sidebar->mode == identities_SidebarMode) { | ||
2081 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
2082 | : uiTextStrong_ColorId; | ||
2083 | const iBool isUsedOnDomain = (d->indent != 0); | ||
2084 | iString icon; | ||
2085 | initUnicodeN_String(&icon, &d->icon, 1); | ||
2086 | iInt2 cPos = topLeft_Rect(itemRect); | ||
2087 | const int indent = 1.4f * lineHeight_Text(font); | ||
2088 | addv_I2(&cPos, | ||
2089 | init_I2(3 * gap_UI, | ||
2090 | (itemHeight - lineHeight_Text(uiLabel_FontId) * 2 - lineHeight_Text(font)) / | ||
2091 | 2)); | ||
2092 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId | ||
2093 | : uiTextFramelessHover_ColorId) | ||
2094 | : uiTextDim_ColorId; | ||
2095 | if (!d->listItem.isSelected && !isUsedOnDomain) { | ||
2096 | drawOutline_Text(font, cPos, metaFg, none_ColorId, range_String(&icon)); | ||
2097 | } | ||
2098 | drawRange_Text(font, | ||
2099 | cPos, | ||
2100 | d->listItem.isSelected ? iconColor | ||
2101 | : isUsedOnDomain ? altIconColor | ||
2102 | : uiBackgroundSidebar_ColorId, | ||
2103 | range_String(&icon)); | ||
2104 | deinit_String(&icon); | ||
2105 | drawRange_Text(d->listItem.isSelected ? sidebar->itemFonts[1] : font, | ||
2106 | add_I2(cPos, init_I2(indent, 0)), | ||
2107 | fg, | ||
2108 | range_String(&d->label)); | ||
2109 | drawRange_Text(uiLabel_FontId, | ||
2110 | add_I2(cPos, init_I2(indent, lineHeight_Text(font))), | ||
2111 | metaFg, | ||
2112 | range_String(&d->meta)); | ||
2113 | } | ||
2114 | } | 1853 | } |
2115 | 1854 | ||
2116 | iBeginDefineSubclass(SidebarWidget, Widget) | 1855 | iBeginDefineSubclass(SidebarWidget, Widget) |