summaryrefslogtreecommitdiff
path: root/src/ui/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/window.c')
-rw-r--r--src/ui/window.c133
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
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are met:
5
61. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
82. 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
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY 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
21SOFTWARE, 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
95static const iMenuItem editMenuItems[] = { 119static 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
126static const iMenuItem identityMenuItems[] = {
127 { "New Identity...", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" },
99}; 128};
100 129
101static const iMenuItem viewMenuItems[] = { 130static const iMenuItem viewMenuItems[] = {
@@ -115,14 +144,43 @@ static const iMenuItem viewMenuItems[] = {
115}; 144};
116 145
117static const iMenuItem helpMenuItems[] = { 146static 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
152static 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
123static const char *reloadCStr_ = "\U0001f503"; 166static const char *reloadCStr_ = "\U0001f503";
124static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310"; 167static const char *stopCStr_ = uiTextCaution_ColorEscape "\U0001f310";
125 168
169static 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
126static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { 184static 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
381void init_Window(iWindow *d, iRect rect) { 449void 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
548static void applyCursor_Window_(iWindow *d) {
549 if (d->pendingCursor) {
550 SDL_SetCursor(d->pendingCursor);
551 d->pendingCursor = NULL;
552 }
553}
554
473iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { 555iBool 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
653void 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
567iInt2 rootSize_Window(const iWindow *d) { 660iInt2 rootSize_Window(const iWindow *d) {
568 return d->root->rect.size; 661 return d->root->rect.size;
569} 662}