diff options
Diffstat (limited to 'src/ui/window.c')
-rw-r--r-- | src/ui/window.c | 133 |
1 files changed, 113 insertions, 20 deletions
diff --git a/src/ui/window.c b/src/ui/window.c index 3d9d98d1..650bc9ee 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1,17 +1,39 @@ | |||
1 | /* Copyright 2020 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 | |||
1 | #include "window.h" | 23 | #include "window.h" |
2 | 24 | ||
25 | #include "labelwidget.h" | ||
26 | #include "inputwidget.h" | ||
27 | #include "documentwidget.h" | ||
28 | #include "sidebarwidget.h" | ||
3 | #include "embedded.h" | 29 | #include "embedded.h" |
4 | #include "app.h" | ||
5 | #include "command.h" | 30 | #include "command.h" |
6 | #include "paint.h" | 31 | #include "paint.h" |
7 | #include "text.h" | ||
8 | #include "util.h" | 32 | #include "util.h" |
33 | #include "../app.h" | ||
9 | #include "../visited.h" | 34 | #include "../visited.h" |
10 | #include "labelwidget.h" | 35 | #include "../gmcerts.h" |
11 | #include "inputwidget.h" | 36 | #include "../gmutil.h" |
12 | #include "documentwidget.h" | ||
13 | #include "sidebarwidget.h" | ||
14 | #include "gmutil.h" | ||
15 | #if defined (iPlatformMsys) | 37 | #if defined (iPlatformMsys) |
16 | # include "../win32.h" | 38 | # include "../win32.h" |
17 | #endif | 39 | #endif |
@@ -80,6 +102,8 @@ static const iMenuItem navMenuItems[] = { | |||
80 | { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, | 102 | { "Reset Zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, |
81 | { "---", 0, 0, NULL }, | 103 | { "---", 0, 0, NULL }, |
82 | { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, | 104 | { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, |
105 | { "Help", 0, 0, "!open url:about:help" }, | ||
106 | { "Release Notes", 0, 0, "!open url:about:version" }, | ||
83 | { "---", 0, 0, NULL }, | 107 | { "---", 0, 0, NULL }, |
84 | { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" } | 108 | { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" } |
85 | }; | 109 | }; |
@@ -94,8 +118,13 @@ static const iMenuItem fileMenuItems[] = { | |||
94 | 118 | ||
95 | static const iMenuItem editMenuItems[] = { | 119 | static const iMenuItem editMenuItems[] = { |
96 | { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" }, | 120 | { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" }, |
121 | { "Copy Link to Page", SDLK_c, KMOD_PRIMARY | KMOD_SHIFT, "document.copylink" }, | ||
97 | { "---", 0, 0, NULL }, | 122 | { "---", 0, 0, NULL }, |
98 | { "Bookmark This Page", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 123 | { "Bookmark This Page...", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
124 | }; | ||
125 | |||
126 | static const iMenuItem identityMenuItems[] = { | ||
127 | { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | ||
99 | }; | 128 | }; |
100 | 129 | ||
101 | static const iMenuItem viewMenuItems[] = { | 130 | static const iMenuItem viewMenuItems[] = { |
@@ -115,14 +144,43 @@ static const iMenuItem viewMenuItems[] = { | |||
115 | }; | 144 | }; |
116 | 145 | ||
117 | static const iMenuItem helpMenuItems[] = { | 146 | static const iMenuItem helpMenuItems[] = { |
118 | { "Help", 0, 0, "open url:about:help" }, | 147 | { "Help", 0, 0, "!open url:about:help" }, |
119 | { "Release Notes", 0, 0, "open url:about:version" }, | 148 | { "Release Notes", 0, 0, "!open url:about:version" }, |
120 | }; | 149 | }; |
121 | #endif | 150 | #endif |
122 | 151 | ||
152 | static const iMenuItem identityButtonMenuItems[] = { | ||
153 | { "No Active Identity", 0, 0, "ident.showactive" }, | ||
154 | { "---", 0, 0, NULL }, | ||
155 | #if !defined (iHaveNativeMenus) | ||
156 | { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | ||
157 | { "---", 0, 0, NULL }, | ||
158 | { "Show Identities", '3', KMOD_PRIMARY, "sidebar.mode arg:2 show:1" }, | ||
159 | #else | ||
160 | { "New Identity...", 0, 0, "ident.new" }, | ||
161 | { "---", 0, 0, NULL }, | ||
162 | { "Show Identities", 0, 0, "sidebar.mode arg:2 show:1" }, | ||
163 | #endif | ||
164 | }; | ||
165 | |||
123 | static const char *reloadCStr_ = "\U0001f503"; | 166 | static const char *reloadCStr_ = "\U0001f503"; |
124 | static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310"; | 167 | static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310"; |
125 | 168 | ||
169 | static void updateNavBarIdentity_(iWidget *navBar) { | ||
170 | const iGmIdentity *ident = | ||
171 | identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App())); | ||
172 | iWidget *button = findChild_Widget(navBar, "navbar.ident"); | ||
173 | setFlags_Widget(button, selected_WidgetFlag, ident != NULL); | ||
174 | /* Update menu. */ | ||
175 | iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); | ||
176 | setTextCStr_LabelWidget( | ||
177 | idItem, | ||
178 | ident ? format_CStr(uiTextAction_ColorEscape "%s", | ||
179 | cstrCollect_String(subject_TlsCertificate(ident->cert))) | ||
180 | : "No Active Identity"); | ||
181 | setFlags_Widget(as_Widget(idItem), disabled_WidgetFlag, !ident); | ||
182 | } | ||
183 | |||
126 | static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | 184 | static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { |
127 | if (equal_Command(cmd, "window.resized")) { | 185 | if (equal_Command(cmd, "window.resized")) { |
128 | const iBool isNarrow = width_Rect(bounds_Widget(navBar)) / gap_UI < 140; | 186 | const iBool isNarrow = width_Rect(bounds_Widget(navBar)) / gap_UI < 140; |
@@ -160,6 +218,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
160 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); | 218 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); |
161 | setText_InputWidget(url, urlStr); | 219 | setText_InputWidget(url, urlStr); |
162 | updateTextCStr_LabelWidget(reloadButton, reloadCStr_); | 220 | updateTextCStr_LabelWidget(reloadButton, reloadCStr_); |
221 | updateNavBarIdentity_(navBar); | ||
163 | return iFalse; | 222 | return iFalse; |
164 | } | 223 | } |
165 | else if (equal_Command(cmd, "document.request.cancelled")) { | 224 | else if (equal_Command(cmd, "document.request.cancelled")) { |
@@ -184,12 +243,13 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
184 | setText_InputWidget(findChild_Widget(navBar, "url"), url_DocumentWidget(doc)); | 243 | setText_InputWidget(findChild_Widget(navBar, "url"), url_DocumentWidget(doc)); |
185 | updateTextCStr_LabelWidget(findChild_Widget(navBar, "reload"), | 244 | updateTextCStr_LabelWidget(findChild_Widget(navBar, "reload"), |
186 | isRequestOngoing_DocumentWidget(doc) ? stopCStr_ : reloadCStr_); | 245 | isRequestOngoing_DocumentWidget(doc) ? stopCStr_ : reloadCStr_); |
246 | updateNavBarIdentity_(navBar); | ||
187 | } | 247 | } |
188 | } | 248 | } |
189 | else if (equal_Command(cmd, "mouse.clicked")) { | 249 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { |
190 | iWidget *widget = pointer_Command(cmd); | 250 | iWidget *widget = pointer_Command(cmd); |
191 | iWidget *menu = findWidget_App("doctabs.menu"); | 251 | iWidget *menu = findWidget_App("doctabs.menu"); |
192 | if (isTabButton_Widget(widget)) { | 252 | if (isTabButton_Widget(widget) && !isVisible_Widget(menu)) { |
193 | iWidget *tabs = findWidget_App("doctabs"); | 253 | iWidget *tabs = findWidget_App("doctabs"); |
194 | showTabPage_Widget(tabs, | 254 | showTabPage_Widget(tabs, |
195 | tabPage_Widget(tabs, childIndex_Widget(widget->parent, widget))); | 255 | tabPage_Widget(tabs, childIndex_Widget(widget->parent, widget))); |
@@ -261,6 +321,7 @@ static void setupUserInterface_Window(iWindow *d) { | |||
261 | /* Navigation bar. */ { | 321 | /* Navigation bar. */ { |
262 | iWidget *navBar = new_Widget(); | 322 | iWidget *navBar = new_Widget(); |
263 | setId_Widget(navBar, "navbar"); | 323 | setId_Widget(navBar, "navbar"); |
324 | setPadding_Widget(navBar, gap_UI / 2, 0, gap_UI / 2, 0); | ||
264 | setFlags_Widget(navBar, | 325 | setFlags_Widget(navBar, |
265 | arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag | | 326 | arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag | |
266 | arrangeHorizontal_WidgetFlag, | 327 | arrangeHorizontal_WidgetFlag, |
@@ -270,7 +331,11 @@ static void setupUserInterface_Window(iWindow *d) { | |||
270 | setCommandHandler_Widget(navBar, handleNavBarCommands_); | 331 | setCommandHandler_Widget(navBar, handleNavBarCommands_); |
271 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f850", 0, 0, "navigate.back"))); | 332 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f850", 0, 0, "navigate.back"))); |
272 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f852", 0, 0, "navigate.forward"))); | 333 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f852", 0, 0, "navigate.forward"))); |
273 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f3e0", 0, 0, "navigate.home"))); | 334 | iLabelWidget *idMenu = |
335 | makeMenuButton_LabelWidget("\U0001f464", identityButtonMenuItems, iElemCount(identityButtonMenuItems)); | ||
336 | setAlignVisually_LabelWidget(idMenu, iTrue); | ||
337 | addChild_Widget(navBar, iClob(idMenu)); | ||
338 | setId_Widget(as_Widget(idMenu), "navbar.ident"); | ||
274 | iLabelWidget *lock = | 339 | iLabelWidget *lock = |
275 | addChildFlags_Widget(navBar, | 340 | addChildFlags_Widget(navBar, |
276 | iClob(newIcon_LabelWidget("\U0001f513", 0, 0, "server.showcert")), | 341 | iClob(newIcon_LabelWidget("\U0001f513", 0, 0, "server.showcert")), |
@@ -279,14 +344,16 @@ static void setupUserInterface_Window(iWindow *d) { | |||
279 | setFont_LabelWidget(lock, defaultSymbols_FontId); | 344 | setFont_LabelWidget(lock, defaultSymbols_FontId); |
280 | updateTextCStr_LabelWidget(lock, "\U0001f512"); | 345 | updateTextCStr_LabelWidget(lock, "\U0001f512"); |
281 | iInputWidget *url = new_InputWidget(0); | 346 | iInputWidget *url = new_InputWidget(0); |
347 | setSelectAllOnFocus_InputWidget(url, iTrue); | ||
282 | setId_Widget(as_Widget(url), "url"); | 348 | setId_Widget(as_Widget(url), "url"); |
283 | setTextCStr_InputWidget(url, "gemini://"); | 349 | setTextCStr_InputWidget(url, "gemini://"); |
284 | addChildFlags_Widget(navBar, iClob(url), expand_WidgetFlag); | 350 | addChildFlags_Widget(navBar, iClob(url), expand_WidgetFlag); |
285 | setId_Widget( | 351 | setId_Widget(addChild_Widget( |
286 | addChild_Widget(navBar, iClob(newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"))), | 352 | navBar, iClob(newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"))), |
287 | "reload"); | 353 | "reload"); |
288 | addChild_Widget(navBar, iClob(newIcon_LabelWidget("\U0001f464", 0, 0, "cert.client"))); | 354 | addChild_Widget(navBar, |
289 | 355 | iClob(newIcon_LabelWidget( | |
356 | "\U0001f3e0", SDLK_h, KMOD_PRIMARY | KMOD_SHIFT, "navigate.home"))); | ||
290 | #if !defined (iHaveNativeMenus) | 357 | #if !defined (iHaveNativeMenus) |
291 | iLabelWidget *navMenu = | 358 | iLabelWidget *navMenu = |
292 | makeMenuButton_LabelWidget("\U0001d362", navMenuItems, iElemCount(navMenuItems)); | 359 | makeMenuButton_LabelWidget("\U0001d362", navMenuItems, iElemCount(navMenuItems)); |
@@ -296,7 +363,8 @@ static void setupUserInterface_Window(iWindow *d) { | |||
296 | insertMenuItems_MacOS("File", 1, fileMenuItems, iElemCount(fileMenuItems)); | 363 | insertMenuItems_MacOS("File", 1, fileMenuItems, iElemCount(fileMenuItems)); |
297 | insertMenuItems_MacOS("Edit", 2, editMenuItems, iElemCount(editMenuItems)); | 364 | insertMenuItems_MacOS("Edit", 2, editMenuItems, iElemCount(editMenuItems)); |
298 | insertMenuItems_MacOS("View", 3, viewMenuItems, iElemCount(viewMenuItems)); | 365 | insertMenuItems_MacOS("View", 3, viewMenuItems, iElemCount(viewMenuItems)); |
299 | insertMenuItems_MacOS("Help", 5, helpMenuItems, iElemCount(helpMenuItems)); | 366 | insertMenuItems_MacOS("Identity", 4, identityMenuItems, iElemCount(identityMenuItems)); |
367 | insertMenuItems_MacOS("Help", 6, helpMenuItems, iElemCount(helpMenuItems)); | ||
300 | #endif | 368 | #endif |
301 | } | 369 | } |
302 | /* Tab bar. */ { | 370 | /* Tab bar. */ { |
@@ -380,6 +448,8 @@ static void drawBlank_Window_(iWindow *d) { | |||
380 | 448 | ||
381 | void init_Window(iWindow *d, iRect rect) { | 449 | void init_Window(iWindow *d, iRect rect) { |
382 | theWindow_ = d; | 450 | theWindow_ = d; |
451 | iZap(d->cursors); | ||
452 | d->pendingCursor = NULL; | ||
383 | d->isDrawFrozen = iTrue; | 453 | d->isDrawFrozen = iTrue; |
384 | uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; | 454 | uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; |
385 | #if defined (iPlatformApple) | 455 | #if defined (iPlatformApple) |
@@ -396,7 +466,7 @@ void init_Window(iWindow *d, iRect rect) { | |||
396 | if (left_Rect(rect) >= 0) { | 466 | if (left_Rect(rect) >= 0) { |
397 | SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); | 467 | SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); |
398 | } | 468 | } |
399 | SDL_SetWindowMinimumSize(d->win, 400, 200); | 469 | SDL_SetWindowMinimumSize(d->win, 400, 250); |
400 | SDL_SetWindowTitle(d->win, "Lagrange"); | 470 | SDL_SetWindowTitle(d->win, "Lagrange"); |
401 | /* Some info. */ { | 471 | /* Some info. */ { |
402 | SDL_RendererInfo info; | 472 | SDL_RendererInfo info; |
@@ -442,6 +512,11 @@ void deinit_Window(iWindow *d) { | |||
442 | if (theWindow_ == d) { | 512 | if (theWindow_ == d) { |
443 | theWindow_ = NULL; | 513 | theWindow_ = NULL; |
444 | } | 514 | } |
515 | iForIndices(i, d->cursors) { | ||
516 | if (d->cursors[i]) { | ||
517 | SDL_FreeCursor(d->cursors[i]); | ||
518 | } | ||
519 | } | ||
445 | iReleasePtr(&d->root); | 520 | iReleasePtr(&d->root); |
446 | deinit_Text(); | 521 | deinit_Text(); |
447 | SDL_DestroyRenderer(d->render); | 522 | SDL_DestroyRenderer(d->render); |
@@ -470,6 +545,13 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | |||
470 | return iFalse; | 545 | return iFalse; |
471 | } | 546 | } |
472 | 547 | ||
548 | static void applyCursor_Window_(iWindow *d) { | ||
549 | if (d->pendingCursor) { | ||
550 | SDL_SetCursor(d->pendingCursor); | ||
551 | d->pendingCursor = NULL; | ||
552 | } | ||
553 | } | ||
554 | |||
473 | iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | 555 | iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { |
474 | switch (ev->type) { | 556 | switch (ev->type) { |
475 | case SDL_WINDOWEVENT: { | 557 | case SDL_WINDOWEVENT: { |
@@ -484,6 +566,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
484 | } | 566 | } |
485 | /* Map mouse pointer coordinate to our coordinate system. */ | 567 | /* Map mouse pointer coordinate to our coordinate system. */ |
486 | if (event.type == SDL_MOUSEMOTION) { | 568 | if (event.type == SDL_MOUSEMOTION) { |
569 | setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */ | ||
487 | const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y); | 570 | const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y); |
488 | event.motion.x = pos.x; | 571 | event.motion.x = pos.x; |
489 | event.motion.y = pos.y; | 572 | event.motion.y = pos.y; |
@@ -506,6 +589,9 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
506 | if (oldHover != hover_Widget()) { | 589 | if (oldHover != hover_Widget()) { |
507 | postRefresh_App(); | 590 | postRefresh_App(); |
508 | } | 591 | } |
592 | if (event.type == SDL_MOUSEMOTION) { | ||
593 | applyCursor_Window_(d); | ||
594 | } | ||
509 | return wasUsed; | 595 | return wasUsed; |
510 | } | 596 | } |
511 | } | 597 | } |
@@ -564,6 +650,13 @@ void setFreezeDraw_Window(iWindow *d, iBool freezeDraw) { | |||
564 | d->isDrawFrozen = freezeDraw; | 650 | d->isDrawFrozen = freezeDraw; |
565 | } | 651 | } |
566 | 652 | ||
653 | void setCursor_Window(iWindow *d, int cursor) { | ||
654 | if (!d->cursors[cursor]) { | ||
655 | d->cursors[cursor] = SDL_CreateSystemCursor(cursor); | ||
656 | } | ||
657 | d->pendingCursor = d->cursors[cursor]; | ||
658 | } | ||
659 | |||
567 | iInt2 rootSize_Window(const iWindow *d) { | 660 | iInt2 rootSize_Window(const iWindow *d) { |
568 | return d->root->rect.size; | 661 | return d->root->rect.size; |
569 | } | 662 | } |