diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 553 | ||||
-rw-r--r-- | src/app.h | 13 | ||||
-rw-r--r-- | src/defs.h | 12 | ||||
-rw-r--r-- | src/fontpack.c | 120 | ||||
-rw-r--r-- | src/fontpack.h | 4 | ||||
-rw-r--r-- | src/gmdocument.c | 61 | ||||
-rw-r--r-- | src/gmdocument.h | 4 | ||||
-rw-r--r-- | src/gmutil.c | 87 | ||||
-rw-r--r-- | src/gmutil.h | 9 | ||||
-rw-r--r-- | src/macos.m | 51 | ||||
-rw-r--r-- | src/periodic.c | 19 | ||||
-rw-r--r-- | src/prefs.c | 12 | ||||
-rw-r--r-- | src/prefs.h | 7 | ||||
-rw-r--r-- | src/sitespec.c | 32 | ||||
-rw-r--r-- | src/sitespec.h | 1 | ||||
-rw-r--r-- | src/ui/banner.c | 3 | ||||
-rw-r--r-- | src/ui/color.c | 4 | ||||
-rw-r--r-- | src/ui/color.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 135 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 5 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 | ||||
-rw-r--r-- | src/ui/keys.c | 2 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 12 | ||||
-rw-r--r-- | src/ui/linkinfo.c | 4 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 2 | ||||
-rw-r--r-- | src/ui/root.c | 16 | ||||
-rw-r--r-- | src/ui/root.h | 1 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 187 | ||||
-rw-r--r-- | src/ui/text.c | 60 | ||||
-rw-r--r-- | src/ui/text.h | 2 | ||||
-rw-r--r-- | src/ui/touch.c | 3 | ||||
-rw-r--r-- | src/ui/util.c | 200 | ||||
-rw-r--r-- | src/ui/util.h | 14 | ||||
-rw-r--r-- | src/ui/widget.c | 3 | ||||
-rw-r--r-- | src/ui/window.c | 75 | ||||
-rw-r--r-- | src/ui/window.h | 3 |
36 files changed, 1265 insertions, 454 deletions
@@ -125,7 +125,8 @@ struct Impl_App { | |||
125 | iGmCerts * certs; | 125 | iGmCerts * certs; |
126 | iVisited * visited; | 126 | iVisited * visited; |
127 | iBookmarks * bookmarks; | 127 | iBookmarks * bookmarks; |
128 | iMainWindow *window; | 128 | iMainWindow *window; /* currently active MainWindow */ |
129 | iPtrArray mainWindows; | ||
129 | iPtrArray popupWindows; | 130 | iPtrArray popupWindows; |
130 | iSortedArray tickers; /* per-frame callbacks, used for animations */ | 131 | iSortedArray tickers; /* per-frame callbacks, used for animations */ |
131 | uint32_t lastTickerTime; | 132 | uint32_t lastTickerTime; |
@@ -153,7 +154,8 @@ struct Impl_App { | |||
153 | /* Preferences: */ | 154 | /* Preferences: */ |
154 | iBool commandEcho; /* --echo */ | 155 | iBool commandEcho; /* --echo */ |
155 | iBool forceSoftwareRender; /* --sw */ | 156 | iBool forceSoftwareRender; /* --sw */ |
156 | iRect initialWindowRect; | 157 | //iRect initialWindowRect; |
158 | iArray initialWindowRects; /* one per window */ | ||
157 | iPrefs prefs; | 159 | iPrefs prefs; |
158 | }; | 160 | }; |
159 | 161 | ||
@@ -199,33 +201,43 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
199 | appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize); | 201 | appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize); |
200 | if (d->prefs.retainWindowSize) { | 202 | if (d->prefs.retainWindowSize) { |
201 | int w, h, x, y; | 203 | int w, h, x, y; |
202 | x = d->window->place.normalRect.pos.x; | 204 | iConstForEach(PtrArray, i, &d->mainWindows) { |
203 | y = d->window->place.normalRect.pos.y; | 205 | const iMainWindow *win = i.ptr; |
204 | w = d->window->place.normalRect.size.x; | 206 | const size_t winIndex = index_PtrArrayConstIterator(&i); |
205 | h = d->window->place.normalRect.size.y; | 207 | x = win->place.normalRect.pos.x; |
206 | appendFormat_String(str, "window.setrect width:%d height:%d coord:%d %d\n", w, h, x, y); | 208 | y = win->place.normalRect.pos.y; |
207 | /* On macOS, maximization should be applied at creation time or the window will take | 209 | w = win->place.normalRect.size.x; |
208 | a moment to animate to its maximized size. */ | 210 | h = win->place.normalRect.size.y; |
211 | appendFormat_String(str, | ||
212 | "window.setrect index:%zu width:%d height:%d coord:%d %d\n", | ||
213 | winIndex, | ||
214 | w, | ||
215 | h, | ||
216 | x, | ||
217 | y); | ||
218 | /* On macOS, maximization should be applied at creation time or the window will take | ||
219 | a moment to animate to its maximized size. */ | ||
209 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 220 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
210 | if (snap_MainWindow(d->window)) { | 221 | if (snap_MainWindow(win)) { |
211 | if (snap_MainWindow(d->window) == maximized_WindowSnap) { | 222 | if (snap_MainWindow(win) == maximized_WindowSnap) { |
212 | appendFormat_String(str, "~window.maximize\n"); | 223 | appendFormat_String(str, "~window.maximize index:%zu\n", winIndex); |
213 | } | 224 | } |
214 | else if (~SDL_GetWindowFlags(d->window->base.win) & SDL_WINDOW_MINIMIZED) { | 225 | else if (~SDL_GetWindowFlags(win->base.win) & SDL_WINDOW_MINIMIZED) { |
215 | /* Save the actual visible window position, too, because snapped windows may | 226 | /* Save the actual visible window position, too, because snapped windows may |
216 | still be resized/moved without affecting normalRect. */ | 227 | still be resized/moved without affecting normalRect. */ |
217 | SDL_GetWindowPosition(d->window->base.win, &x, &y); | 228 | SDL_GetWindowPosition(win->base.win, &x, &y); |
218 | SDL_GetWindowSize(d->window->base.win, &w, &h); | 229 | SDL_GetWindowSize(win->base.win, &w, &h); |
219 | appendFormat_String( | 230 | appendFormat_String( |
220 | str, "~window.setrect snap:%d width:%d height:%d coord:%d %d\n", | 231 | str, "~window.setrect index:%zu snap:%d width:%d height:%d coord:%d %d\n", |
221 | snap_MainWindow(d->window), w, h, x, y); | 232 | winIndex, snap_MainWindow(d->window), w, h, x, y); |
233 | } | ||
222 | } | 234 | } |
223 | } | ||
224 | #elif !defined (iPlatformApple) | 235 | #elif !defined (iPlatformApple) |
225 | if (snap_MainWindow(d->window) == maximized_WindowSnap) { | 236 | if (snap_MainWindow(win) == maximized_WindowSnap) { |
226 | appendFormat_String(str, "~window.maximize\n"); | 237 | appendFormat_String(str, "~window.maximize index:%zu\n", winIndex); |
227 | } | 238 | } |
228 | #endif | 239 | #endif |
240 | } | ||
229 | } | 241 | } |
230 | appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.strings[uiLanguage_PrefsString])); | 242 | appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.strings[uiLanguage_PrefsString])); |
231 | appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window))); | 243 | appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window))); |
@@ -244,6 +256,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
244 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); | 256 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); |
245 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); | 257 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); |
246 | appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); | 258 | appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); |
259 | appendFormat_String(str, "urlsize.set arg:%d\n", d->prefs.maxUrlSize); | ||
247 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); | 260 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); |
248 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); | 261 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); |
249 | appendFormat_String(str, "linespacing.set arg:%f\n", d->prefs.lineSpacing); | 262 | appendFormat_String(str, "linespacing.set arg:%f\n", d->prefs.lineSpacing); |
@@ -279,6 +292,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
279 | { "prefs.collapsepreonload", &d->prefs.collapsePreOnLoad }, | 292 | { "prefs.collapsepreonload", &d->prefs.collapsePreOnLoad }, |
280 | { "prefs.hoverlink", &d->prefs.hoverLink }, | 293 | { "prefs.hoverlink", &d->prefs.hoverLink }, |
281 | { "prefs.bookmarks.addbottom", &d->prefs.addBookmarksToBottom }, | 294 | { "prefs.bookmarks.addbottom", &d->prefs.addBookmarksToBottom }, |
295 | { "prefs.dataurl.openimages", &d->prefs.openDataUrlImagesOnLoad }, | ||
282 | { "prefs.archive.openindex", &d->prefs.openArchiveIndexPages }, | 296 | { "prefs.archive.openindex", &d->prefs.openArchiveIndexPages }, |
283 | { "prefs.font.warnmissing", &d->prefs.warnAboutMissingGlyphs }, | 297 | { "prefs.font.warnmissing", &d->prefs.warnAboutMissingGlyphs }, |
284 | { "prefs.blink", &d->prefs.blinkingCursor }, | 298 | { "prefs.blink", &d->prefs.blinkingCursor }, |
@@ -401,9 +415,14 @@ static void loadPrefs_App_(iApp *d) { | |||
401 | d->prefs.customFrame = arg_Command(cmd); | 415 | d->prefs.customFrame = arg_Command(cmd); |
402 | } | 416 | } |
403 | else if (equal_Command(cmd, "window.setrect") && !argLabel_Command(cmd, "snap")) { | 417 | else if (equal_Command(cmd, "window.setrect") && !argLabel_Command(cmd, "snap")) { |
404 | const iInt2 pos = coord_Command(cmd); | 418 | const int index = argLabel_Command(cmd, "index"); |
405 | d->initialWindowRect = init_Rect( | 419 | const iInt2 pos = coord_Command(cmd); |
420 | iRect winRect = init_Rect( | ||
406 | pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height")); | 421 | pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height")); |
422 | if (index >= 0 && index < 100) { | ||
423 | resize_Array(&d->initialWindowRects, index + 1); | ||
424 | set_Array(&d->initialWindowRects, index, &winRect); | ||
425 | } | ||
407 | } | 426 | } |
408 | else if (equal_Command(cmd, "fontpack.disable")) { | 427 | else if (equal_Command(cmd, "fontpack.disable")) { |
409 | insert_StringSet(d->prefs.disabledFontPacks, | 428 | insert_StringSet(d->prefs.disabledFontPacks, |
@@ -439,6 +458,7 @@ static void loadPrefs_App_(iApp *d) { | |||
439 | } | 458 | } |
440 | iRelease(f); | 459 | iRelease(f); |
441 | /* Upgrade checks. */ | 460 | /* Upgrade checks. */ |
461 | #if 0 /* disabled in v1.11 (font library search) */ | ||
442 | if (cmp_Version(&upgradedFromAppVersion, &(iVersion){ 1, 8, 0 }) < 0) { | 462 | if (cmp_Version(&upgradedFromAppVersion, &(iVersion){ 1, 8, 0 }) < 0) { |
443 | #if !defined (iPlatformAppleMobile) && !defined (iPlatformAndroidMobile) | 463 | #if !defined (iPlatformAppleMobile) && !defined (iPlatformAndroidMobile) |
444 | /* When upgrading to v1.8.0, the old hardcoded font library is gone and that means | 464 | /* When upgrading to v1.8.0, the old hardcoded font library is gone and that means |
@@ -448,6 +468,7 @@ static void loadPrefs_App_(iApp *d) { | |||
448 | postCommand_App("~fontpack.suggest.classic"); | 468 | postCommand_App("~fontpack.suggest.classic"); |
449 | #endif | 469 | #endif |
450 | } | 470 | } |
471 | #endif | ||
451 | #if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 472 | #if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
452 | d->prefs.customFrame = iFalse; | 473 | d->prefs.customFrame = iFalse; |
453 | #endif | 474 | #endif |
@@ -470,10 +491,36 @@ static const char *magicTabDocument_App_ = "tabd"; | |||
470 | static const char *magicSidebar_App_ = "side"; | 491 | static const char *magicSidebar_App_ = "side"; |
471 | 492 | ||
472 | enum iDocumentStateFlag { | 493 | enum iDocumentStateFlag { |
473 | current_DocumentStateFlag = iBit(1), | 494 | current_DocumentStateFlag = iBit(1), |
474 | rootIndex1_DocumentStateFlag = iBit(2) | 495 | rootIndex1_DocumentStateFlag = iBit(2), |
496 | }; | ||
497 | |||
498 | enum iWindowStateFlag { | ||
499 | current_WindowStateFlag = iBit(9), | ||
475 | }; | 500 | }; |
476 | 501 | ||
502 | static iRect initialWindowRect_App_(const iApp *d, size_t windowIndex) { | ||
503 | if (windowIndex < size_Array(&d->initialWindowRects)) { | ||
504 | return constValue_Array(&d->initialWindowRects, windowIndex, iRect); | ||
505 | } | ||
506 | /* The default window rectangle. */ | ||
507 | iRect rect = init_Rect(-1, -1, 900, 560); | ||
508 | #if defined (iPlatformMsys) | ||
509 | /* Must scale by UI scaling factor. */ | ||
510 | mulfv_I2(&rect.size, desktopDPI_Win32()); | ||
511 | #endif | ||
512 | #if defined (iPlatformLinux) && !defined (iPlatformAndroid) | ||
513 | /* Scale by the primary (?) monitor DPI. */ | ||
514 | if (isRunningUnderWindowSystem_App()) { | ||
515 | float vdpi; | ||
516 | SDL_GetDisplayDPI(0, NULL, NULL, &vdpi); | ||
517 | const float factor = vdpi / 96.0f; | ||
518 | mulfv_I2(&rect.size, iMax(factor, 1.0f)); | ||
519 | } | ||
520 | #endif | ||
521 | return rect; | ||
522 | } | ||
523 | |||
477 | static iBool loadState_App_(iApp *d) { | 524 | static iBool loadState_App_(iApp *d) { |
478 | iUnused(d); | 525 | iUnused(d); |
479 | const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_); | 526 | const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_); |
@@ -494,19 +541,49 @@ static iBool loadState_App_(iApp *d) { | |||
494 | } | 541 | } |
495 | setVersion_Stream(stream_File(f), version); | 542 | setVersion_Stream(stream_File(f), version); |
496 | /* Window state. */ | 543 | /* Window state. */ |
497 | iDocumentWidget *doc = NULL; | 544 | iDeclareType(CurrentTabs); |
498 | iDocumentWidget *current[2] = { NULL, NULL }; | 545 | struct Impl_CurrentTabs { |
499 | iBool isFirstTab[2] = { iTrue, iTrue }; | 546 | iDocumentWidget *currentTab[2]; /* for each root */ |
547 | }; | ||
548 | int numWins = 0; | ||
549 | iMainWindow * win = NULL; | ||
550 | iMainWindow * currentWin = d->window; | ||
551 | iArray * currentTabs; /* two per window (per root per window) */ | ||
552 | iBool isFirstTab[2]; | ||
553 | currentTabs = collectNew_Array(sizeof(iCurrentTabs)); | ||
500 | while (!atEnd_File(f)) { | 554 | while (!atEnd_File(f)) { |
501 | readData_File(f, 4, magic); | 555 | readData_File(f, 4, magic); |
502 | if (!memcmp(magic, magicWindow_App_, 4)) { | 556 | if (!memcmp(magic, magicWindow_App_, 4)) { |
503 | const int splitMode = read32_File(f); | 557 | numWins++; |
504 | const int keyRoot = read32_File(f); | 558 | const int splitMode = read32_File(f); |
505 | d->window->pendingSplitMode = splitMode; | 559 | const int winState = read32_File(f); |
506 | setSplitMode_MainWindow(d->window, splitMode | noEvents_WindowSplit); | 560 | const int keyRoot = (winState & 1); |
507 | d->window->base.keyRoot = d->window->base.roots[keyRoot]; | 561 | const iBool isCurrent = (winState & current_WindowStateFlag) != 0; |
562 | // printf("[State] '%.4s' split:%d state:%x\n", magic, splitMode, winState); | ||
563 | if (numWins == 1) { | ||
564 | win = d->window; | ||
565 | } | ||
566 | else { | ||
567 | win = new_MainWindow(initialWindowRect_App_(d, numWins - 1)); | ||
568 | addWindow_App(win); | ||
569 | } | ||
570 | pushBack_Array(currentTabs, &(iCurrentTabs){ { NULL, NULL } }); | ||
571 | isFirstTab[0] = isFirstTab[1] = iTrue; | ||
572 | if (isCurrent) { | ||
573 | currentWin = win; | ||
574 | } | ||
575 | setCurrent_Window(win); | ||
576 | setCurrent_Root(NULL); | ||
577 | win->pendingSplitMode = splitMode; | ||
578 | setSplitMode_MainWindow(win, splitMode | noEvents_WindowSplit); | ||
579 | win->base.keyRoot = win->base.roots[keyRoot]; | ||
508 | } | 580 | } |
509 | else if (!memcmp(magic, magicSidebar_App_, 4)) { | 581 | else if (!memcmp(magic, magicSidebar_App_, 4)) { |
582 | if (!win) { | ||
583 | printf("%s: missing window\n", cstr_String(path_File(f))); | ||
584 | setCurrent_Root(NULL); | ||
585 | return iFalse; | ||
586 | } | ||
510 | const uint16_t bits = readU16_File(f); | 587 | const uint16_t bits = readU16_File(f); |
511 | const uint8_t modes = readU8_File(f); | 588 | const uint8_t modes = readU8_File(f); |
512 | const float widths[2] = { | 589 | const float widths[2] = { |
@@ -523,7 +600,7 @@ static iBool loadState_App_(iApp *d) { | |||
523 | } | 600 | } |
524 | const uint8_t rootIndex = bits & 0xff; | 601 | const uint8_t rootIndex = bits & 0xff; |
525 | const uint8_t flags = bits >> 8; | 602 | const uint8_t flags = bits >> 8; |
526 | iRoot *root = d->window->base.roots[rootIndex]; | 603 | iRoot *root = win->base.roots[rootIndex]; |
527 | if (root) { | 604 | if (root) { |
528 | iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); | 605 | iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); |
529 | iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); | 606 | iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); |
@@ -546,12 +623,18 @@ static iBool loadState_App_(iApp *d) { | |||
546 | } | 623 | } |
547 | } | 624 | } |
548 | else if (!memcmp(magic, magicTabDocument_App_, 4)) { | 625 | else if (!memcmp(magic, magicTabDocument_App_, 4)) { |
626 | if (!win) { | ||
627 | printf("%s: missing window\n", cstr_String(path_File(f))); | ||
628 | setCurrent_Root(NULL); | ||
629 | return iFalse; | ||
630 | } | ||
549 | const int8_t flags = read8_File(f); | 631 | const int8_t flags = read8_File(f); |
550 | int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; | 632 | int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; |
551 | if (rootIndex > numRoots_Window(as_Window(d->window)) - 1) { | 633 | if (rootIndex > numRoots_Window(as_Window(win)) - 1) { |
552 | rootIndex = 0; | 634 | rootIndex = 0; |
553 | } | 635 | } |
554 | setCurrent_Root(d->window->base.roots[rootIndex]); | 636 | setCurrent_Root(win->base.roots[rootIndex]); |
637 | iDocumentWidget *doc; | ||
555 | if (isFirstTab[rootIndex]) { | 638 | if (isFirstTab[rootIndex]) { |
556 | isFirstTab[rootIndex] = iFalse; | 639 | isFirstTab[rootIndex] = iFalse; |
557 | /* There is one pre-created tab in each root. */ | 640 | /* There is one pre-created tab in each root. */ |
@@ -561,7 +644,7 @@ static iBool loadState_App_(iApp *d) { | |||
561 | doc = newTab_App(NULL, iFalse /* no switching */); | 644 | doc = newTab_App(NULL, iFalse /* no switching */); |
562 | } | 645 | } |
563 | if (flags & current_DocumentStateFlag) { | 646 | if (flags & current_DocumentStateFlag) { |
564 | current[rootIndex] = doc; | 647 | value_Array(currentTabs, numWins - 1, iCurrentTabs).currentTab[rootIndex] = doc; |
565 | } | 648 | } |
566 | deserializeState_DocumentWidget(doc, stream_File(f)); | 649 | deserializeState_DocumentWidget(doc, stream_File(f)); |
567 | doc = NULL; | 650 | doc = NULL; |
@@ -572,12 +655,23 @@ static iBool loadState_App_(iApp *d) { | |||
572 | return iFalse; | 655 | return iFalse; |
573 | } | 656 | } |
574 | } | 657 | } |
575 | if (d->window->splitMode) { | 658 | iForEach(Array, i, currentTabs) { |
576 | /* Update root placement. */ | 659 | const iCurrentTabs *cur = i.value; |
577 | resize_MainWindow(d->window, -1, -1); | 660 | win = at_PtrArray(&d->mainWindows, index_ArrayIterator(&i)); |
661 | for (size_t j = 0; j < 2; ++j) { | ||
662 | postCommandf_Root(win->base.roots[j], "tabs.switch page:%p", cur->currentTab[j]); | ||
663 | } | ||
664 | if (win->splitMode) { | ||
665 | /* Update root placement. */ | ||
666 | resize_MainWindow(win, -1, -1); | ||
667 | } | ||
668 | // postCommand_Root(win->base.roots[0], "window.unfreeze"); | ||
669 | win->isDrawFrozen = iFalse; | ||
670 | SDL_ShowWindow(win->base.win); | ||
578 | } | 671 | } |
579 | iForIndices(i, current) { | 672 | if (numWindows_App() > 1) { |
580 | postCommandf_Root(NULL, "tabs.switch page:%p", current[i]); | 673 | SDL_RaiseWindow(currentWin->base.win); |
674 | setActiveWindow_App(currentWin); | ||
581 | } | 675 | } |
582 | setCurrent_Root(NULL); | 676 | setCurrent_Root(NULL); |
583 | return iTrue; | 677 | return iTrue; |
@@ -588,7 +682,6 @@ static iBool loadState_App_(iApp *d) { | |||
588 | static void saveState_App_(const iApp *d) { | 682 | static void saveState_App_(const iApp *d) { |
589 | iUnused(d); | 683 | iUnused(d); |
590 | trimCache_App(); | 684 | trimCache_App(); |
591 | iMainWindow *win = d->window; | ||
592 | /* UI state is saved in binary because it is quite complex (e.g., | 685 | /* UI state is saved in binary because it is quite complex (e.g., |
593 | navigation history, cached content) and depends closely on the widget | 686 | navigation history, cached content) and depends closely on the widget |
594 | tree. The data is largely not reorderable and should not be modified | 687 | tree. The data is largely not reorderable and should not be modified |
@@ -597,43 +690,48 @@ static void saveState_App_(const iApp *d) { | |||
597 | if (open_File(f, writeOnly_FileMode)) { | 690 | if (open_File(f, writeOnly_FileMode)) { |
598 | writeData_File(f, magicState_App_, 4); | 691 | writeData_File(f, magicState_App_, 4); |
599 | writeU32_File(f, latest_FileVersion); /* version */ | 692 | writeU32_File(f, latest_FileVersion); /* version */ |
600 | /* Begin with window state. */ { | 693 | iConstForEach(PtrArray, winIter, &d->mainWindows) { |
601 | writeData_File(f, magicWindow_App_, 4); | 694 | const iMainWindow *win = winIter.ptr; |
602 | writeU32_File(f, win->splitMode); | 695 | setCurrent_Window(winIter.ptr); |
603 | writeU32_File(f, win->base.keyRoot == win->base.roots[0] ? 0 : 1); | 696 | /* Window state. */ { |
604 | } | 697 | writeData_File(f, magicWindow_App_, 4); |
605 | /* State of UI elements. */ { | 698 | writeU32_File(f, win->splitMode); |
606 | iForIndices(i, win->base.roots) { | 699 | writeU32_File(f, (win->base.keyRoot == win->base.roots[0] ? 0 : 1) | |
607 | const iRoot *root = win->base.roots[i]; | 700 | (win == d->window ? current_WindowStateFlag : 0)); |
608 | if (root) { | 701 | } |
609 | writeData_File(f, magicSidebar_App_, 4); | 702 | /* State of UI elements. */ { |
610 | const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); | 703 | iForIndices(i, win->base.roots) { |
611 | const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); | 704 | const iRoot *root = win->base.roots[i]; |
612 | writeU16_File(f, i | | 705 | if (root) { |
613 | (isVisible_Widget(sidebar) ? 0x100 : 0) | | 706 | writeData_File(f, magicSidebar_App_, 4); |
614 | (isVisible_Widget(sidebar2) ? 0x200 : 0) | | 707 | const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); |
615 | (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) | | 708 | const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); |
616 | (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0)); | 709 | writeU16_File(f, i | |
617 | writeU8_File(f, | 710 | (isVisible_Widget(sidebar) ? 0x100 : 0) | |
618 | mode_SidebarWidget(sidebar) | | 711 | (isVisible_Widget(sidebar2) ? 0x200 : 0) | |
619 | (mode_SidebarWidget(sidebar2) << 4)); | 712 | (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) | |
620 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar)); | 713 | (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0)); |
621 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar2)); | 714 | writeU8_File(f, |
622 | serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f)); | 715 | mode_SidebarWidget(sidebar) | |
623 | serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f)); | 716 | (mode_SidebarWidget(sidebar2) << 4)); |
717 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar)); | ||
718 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar2)); | ||
719 | serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f)); | ||
720 | serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f)); | ||
721 | } | ||
624 | } | 722 | } |
625 | } | 723 | } |
626 | } | 724 | iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { |
627 | iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | 725 | iAssert(isInstance_Object(i.object, &Class_DocumentWidget)); |
628 | iAssert(isInstance_Object(i.object, &Class_DocumentWidget)); | 726 | const iWidget *widget = constAs_Widget(i.object); |
629 | const iWidget *widget = constAs_Widget(i.object); | 727 | writeData_File(f, magicTabDocument_App_, 4); |
630 | writeData_File(f, magicTabDocument_App_, 4); | 728 | int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); |
631 | int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); | 729 | if (widget->root == win->base.roots[1]) { |
632 | if (widget->root == win->base.roots[1]) { | 730 | flags |= rootIndex1_DocumentStateFlag; |
633 | flags |= rootIndex1_DocumentStateFlag; | 731 | } |
732 | write8_File(f, flags); | ||
733 | serializeState_DocumentWidget(i.object, stream_File(f)); | ||
634 | } | 734 | } |
635 | write8_File(f, flags); | ||
636 | serializeState_DocumentWidget(i.object, stream_File(f)); | ||
637 | } | 735 | } |
638 | iRelease(f); | 736 | iRelease(f); |
639 | } | 737 | } |
@@ -760,6 +858,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
760 | d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */ | 858 | d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */ |
761 | d->isSuspended = iFalse; | 859 | d->isSuspended = iFalse; |
762 | d->tempFilesPendingDeletion = new_StringSet(); | 860 | d->tempFilesPendingDeletion = new_StringSet(); |
861 | init_Array(&d->initialWindowRects, sizeof(iRect)); | ||
763 | init_CommandLine(&d->args, argc, argv); | 862 | init_CommandLine(&d->args, argc, argv); |
764 | /* Where was the app started from? We ask SDL first because the command line alone | 863 | /* Where was the app started from? We ask SDL first because the command line alone |
765 | cannot be relied on (behavior differs depending on OS). */ { | 864 | cannot be relied on (behavior differs depending on OS). */ { |
@@ -894,20 +993,6 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
894 | d->elapsedSinceLastTicker = 0; | 993 | d->elapsedSinceLastTicker = 0; |
895 | d->commandEcho = iClob(checkArgument_CommandLine(&d->args, "echo;E")) != NULL; | 994 | d->commandEcho = iClob(checkArgument_CommandLine(&d->args, "echo;E")) != NULL; |
896 | d->forceSoftwareRender = iClob(checkArgument_CommandLine(&d->args, "sw")) != NULL; | 995 | d->forceSoftwareRender = iClob(checkArgument_CommandLine(&d->args, "sw")) != NULL; |
897 | d->initialWindowRect = init_Rect(-1, -1, 900, 560); | ||
898 | #if defined (iPlatformMsys) | ||
899 | /* Must scale by UI scaling factor. */ | ||
900 | mulfv_I2(&d->initialWindowRect.size, desktopDPI_Win32()); | ||
901 | #endif | ||
902 | #if defined (iPlatformLinux) && !defined (iPlatformAndroid) | ||
903 | /* Scale by the primary (?) monitor DPI. */ | ||
904 | if (isRunningUnderWindowSystem_App()) { | ||
905 | float vdpi; | ||
906 | SDL_GetDisplayDPI(0, NULL, NULL, &vdpi); | ||
907 | const float factor = vdpi / 96.0f; | ||
908 | mulfv_I2(&d->initialWindowRect.size, iMax(factor, 1.0f)); | ||
909 | } | ||
910 | #endif | ||
911 | init_Prefs(&d->prefs); | 996 | init_Prefs(&d->prefs); |
912 | init_SiteSpec(dataDir_App_()); | 997 | init_SiteSpec(dataDir_App_()); |
913 | setCStr_String(&d->prefs.strings[downloadDir_PrefsString], downloadDir_App_()); | 998 | setCStr_String(&d->prefs.strings[downloadDir_PrefsString], downloadDir_App_()); |
@@ -929,21 +1014,30 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
929 | init_Fonts(dataDir_App_()); | 1014 | init_Fonts(dataDir_App_()); |
930 | loadPalette_Color(dataDir_App_()); | 1015 | loadPalette_Color(dataDir_App_()); |
931 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ | 1016 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ |
932 | loadPrefs_App_(d); | 1017 | /* Initial window rectangle of the first window. */ { |
1018 | iAssert(isEmpty_Array(&d->initialWindowRects)); | ||
1019 | const iRect winRect = initialWindowRect_App_(d, 0); /* calculated */ | ||
1020 | resize_Array(&d->initialWindowRects, 1); | ||
1021 | set_Array(&d->initialWindowRects, 0, &winRect); | ||
1022 | } | ||
1023 | loadPrefs_App_(d); | ||
933 | updateActive_Fonts(); | 1024 | updateActive_Fonts(); |
934 | load_Keys(dataDir_App_()); | 1025 | load_Keys(dataDir_App_()); |
1026 | iRect *winRect0 = at_Array(&d->initialWindowRects, 0); | ||
935 | /* See if the user wants to override the window size. */ { | 1027 | /* See if the user wants to override the window size. */ { |
936 | iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption)); | 1028 | iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption)); |
937 | if (arg) { | 1029 | if (arg) { |
938 | d->initialWindowRect.size.x = toInt_String(value_CommandLineArg(arg, 0)); | 1030 | winRect0->size.x = toInt_String(value_CommandLineArg(arg, 0)); |
939 | } | 1031 | } |
940 | arg = iClob(checkArgument_CommandLine(&d->args, windowHeight_CommandLineOption)); | 1032 | arg = iClob(checkArgument_CommandLine(&d->args, windowHeight_CommandLineOption)); |
941 | if (arg) { | 1033 | if (arg) { |
942 | d->initialWindowRect.size.y = toInt_String(value_CommandLineArg(arg, 0)); | 1034 | winRect0->size.y = toInt_String(value_CommandLineArg(arg, 0)); |
943 | } | 1035 | } |
944 | } | 1036 | } |
1037 | init_PtrArray(&d->mainWindows); | ||
945 | init_PtrArray(&d->popupWindows); | 1038 | init_PtrArray(&d->popupWindows); |
946 | d->window = new_MainWindow(d->initialWindowRect); | 1039 | d->window = new_MainWindow(*winRect0); /* first window is always created */ |
1040 | addWindow_App(d->window); | ||
947 | load_Visited(d->visited, dataDir_App_()); | 1041 | load_Visited(d->visited, dataDir_App_()); |
948 | load_Bookmarks(d->bookmarks, dataDir_App_()); | 1042 | load_Bookmarks(d->bookmarks, dataDir_App_()); |
949 | load_MimeHooks(d->mimehooks, dataDir_App_()); | 1043 | load_MimeHooks(d->mimehooks, dataDir_App_()); |
@@ -968,8 +1062,8 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
968 | } | 1062 | } |
969 | postCommand_App("~navbar.actions.changed"); | 1063 | postCommand_App("~navbar.actions.changed"); |
970 | postCommand_App("~toolbar.actions.changed"); | 1064 | postCommand_App("~toolbar.actions.changed"); |
971 | postCommand_Root(NULL, "~window.unfreeze"); | 1065 | postCommand_App("~window.unfreeze"); |
972 | postCommand_Root(NULL, "font.reset"); | 1066 | postCommand_App("font.reset"); |
973 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); | 1067 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); |
974 | postCommand_Root(NULL, "document.autoreload"); | 1068 | postCommand_Root(NULL, "document.autoreload"); |
975 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) | 1069 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) |
@@ -1008,7 +1102,11 @@ static void deinit_App(iApp *d) { | |||
1008 | SDL_RemoveTimer(d->autoReloadTimer); | 1102 | SDL_RemoveTimer(d->autoReloadTimer); |
1009 | saveState_App_(d); | 1103 | saveState_App_(d); |
1010 | savePrefs_App_(d); | 1104 | savePrefs_App_(d); |
1011 | delete_MainWindow(d->window); | 1105 | iReverseForEach(PtrArray, j, &d->mainWindows) { |
1106 | delete_MainWindow(j.ptr); | ||
1107 | } | ||
1108 | iAssert(isEmpty_PtrArray(&d->mainWindows)); | ||
1109 | deinit_PtrArray(&d->mainWindows); | ||
1012 | d->window = NULL; | 1110 | d->window = NULL; |
1013 | deinit_Feeds(); | 1111 | deinit_Feeds(); |
1014 | save_Keys(dataDir_App_()); | 1112 | save_Keys(dataDir_App_()); |
@@ -1023,7 +1121,6 @@ static void deinit_App(iApp *d) { | |||
1023 | delete_GmCerts(d->certs); | 1121 | delete_GmCerts(d->certs); |
1024 | save_MimeHooks(d->mimehooks); | 1122 | save_MimeHooks(d->mimehooks); |
1025 | delete_MimeHooks(d->mimehooks); | 1123 | delete_MimeHooks(d->mimehooks); |
1026 | d->window = NULL; | ||
1027 | deinit_CommandLine(&d->args); | 1124 | deinit_CommandLine(&d->args); |
1028 | iRelease(d->launchCommands); | 1125 | iRelease(d->launchCommands); |
1029 | delete_String(d->execPath); | 1126 | delete_String(d->execPath); |
@@ -1037,7 +1134,8 @@ static void deinit_App(iApp *d) { | |||
1037 | /* Delete all temporary files created while running. */ | 1134 | /* Delete all temporary files created while running. */ |
1038 | iConstForEach(StringSet, tmp, d->tempFilesPendingDeletion) { | 1135 | iConstForEach(StringSet, tmp, d->tempFilesPendingDeletion) { |
1039 | remove(cstr_String(tmp.value)); | 1136 | remove(cstr_String(tmp.value)); |
1040 | } | 1137 | } |
1138 | deinit_Array(&d->initialWindowRects); | ||
1041 | iRelease(d->tempFilesPendingDeletion); | 1139 | iRelease(d->tempFilesPendingDeletion); |
1042 | } | 1140 | } |
1043 | 1141 | ||
@@ -1282,10 +1380,23 @@ static iPtrArray *listWindows_App_(const iApp *d, iPtrArray *windows) { | |||
1282 | iReverseConstForEach(PtrArray, i, &d->popupWindows) { | 1380 | iReverseConstForEach(PtrArray, i, &d->popupWindows) { |
1283 | pushBack_PtrArray(windows, i.ptr); | 1381 | pushBack_PtrArray(windows, i.ptr); |
1284 | } | 1382 | } |
1285 | pushBack_PtrArray(windows, d->window); | 1383 | if (d->window) { |
1384 | pushBack_PtrArray(windows, d->window); | ||
1385 | } | ||
1386 | iConstForEach(PtrArray, j, &d->mainWindows) { | ||
1387 | if (j.ptr != d->window) { | ||
1388 | pushBack_PtrArray(windows, j.ptr); | ||
1389 | } | ||
1390 | } | ||
1286 | return windows; | 1391 | return windows; |
1287 | } | 1392 | } |
1288 | 1393 | ||
1394 | iPtrArray *listWindows_App(void) { | ||
1395 | iPtrArray *wins = new_PtrArray(); | ||
1396 | listWindows_App_(&app_, wins); | ||
1397 | return wins; | ||
1398 | } | ||
1399 | |||
1289 | void processEvents_App(enum iAppEventMode eventMode) { | 1400 | void processEvents_App(enum iAppEventMode eventMode) { |
1290 | iApp *d = &app_; | 1401 | iApp *d = &app_; |
1291 | iRoot *oldCurrentRoot = current_Root(); /* restored afterwards */ | 1402 | iRoot *oldCurrentRoot = current_Root(); /* restored afterwards */ |
@@ -1365,6 +1476,10 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1365 | dispatchCommands_Periodic(&d->periodic); | 1476 | dispatchCommands_Periodic(&d->periodic); |
1366 | continue; | 1477 | continue; |
1367 | } | 1478 | } |
1479 | if (ev.type == SDL_USEREVENT && ev.user.code == releaseObject_UserEventCode) { | ||
1480 | iRelease(ev.user.data1); | ||
1481 | continue; | ||
1482 | } | ||
1368 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { | 1483 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { |
1369 | gotRefresh = iTrue; | 1484 | gotRefresh = iTrue; |
1370 | continue; | 1485 | continue; |
@@ -1470,6 +1585,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1470 | window->lastHover = window->hover; | 1585 | window->lastHover = window->hover; |
1471 | wasUsed = processEvent_Window(window, &ev); | 1586 | wasUsed = processEvent_Window(window, &ev); |
1472 | if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) { | 1587 | if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) { |
1588 | /* Only offered to the frontmost window. */ | ||
1473 | break; | 1589 | break; |
1474 | } | 1590 | } |
1475 | if (wasUsed) break; | 1591 | if (wasUsed) break; |
@@ -1563,6 +1679,9 @@ static void runTickers_App_(iApp *d) { | |||
1563 | iConstForEach(Array, i, &pending->values) { | 1679 | iConstForEach(Array, i, &pending->values) { |
1564 | const iTicker *ticker = i.value; | 1680 | const iTicker *ticker = i.value; |
1565 | if (ticker->callback) { | 1681 | if (ticker->callback) { |
1682 | if (ticker->root) { | ||
1683 | setCurrent_Window(ticker->root->window); | ||
1684 | } | ||
1566 | setCurrent_Root(ticker->root); /* root might be NULL */ | 1685 | setCurrent_Root(ticker->root); /* root might be NULL */ |
1567 | ticker->callback(ticker->context); | 1686 | ticker->callback(ticker->context); |
1568 | } | 1687 | } |
@@ -1755,11 +1874,11 @@ void postCommand_Root(iRoot *d, const char *command) { | |||
1755 | } | 1874 | } |
1756 | SDL_Event ev = { .type = SDL_USEREVENT }; | 1875 | SDL_Event ev = { .type = SDL_USEREVENT }; |
1757 | ev.user.code = command_UserEventCode; | 1876 | ev.user.code = command_UserEventCode; |
1758 | /*ev.user.windowID = id_Window(get_Window());*/ | ||
1759 | ev.user.data1 = strdup(command); | 1877 | ev.user.data1 = strdup(command); |
1760 | ev.user.data2 = d; /* all events are root-specific */ | 1878 | ev.user.data2 = d; /* all events are root-specific */ |
1879 | ev.user.windowID = d ? id_Window(d->window) : 0; /* root-specific means window-specific */ | ||
1761 | SDL_PushEvent(&ev); | 1880 | SDL_PushEvent(&ev); |
1762 | iWindow *win = get_Window(); | 1881 | iWindow *win = d ? d->window : NULL; |
1763 | #if defined (iPlatformAndroid) | 1882 | #if defined (iPlatformAndroid) |
1764 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s[command] {%d} %s", | 1883 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s[command] {%d} %s", |
1765 | app_.isLoadingPrefs ? "[Prefs] " : "", | 1884 | app_.isLoadingPrefs ? "[Prefs] " : "", |
@@ -1767,10 +1886,17 @@ void postCommand_Root(iRoot *d, const char *command) { | |||
1767 | command); | 1886 | command); |
1768 | #else | 1887 | #else |
1769 | if (app_.commandEcho) { | 1888 | if (app_.commandEcho) { |
1770 | printf("%s[command] {%d} %s\n", | 1889 | const int windowIndex = |
1771 | app_.isLoadingPrefs ? "[Prefs] " : "", | 1890 | win && type_Window(win) == main_WindowType ? windowIndex_App(as_MainWindow(win)) + 1 : 0; |
1772 | (d == NULL || win == NULL ? 0 : d == win->roots[0] ? 1 : 2), | 1891 | printf("%s%s[command] {%d:%d} %s\n", |
1773 | command); fflush(stdout); | 1892 | !app_.isFinishedLaunching ? "<Ln> " : "", |
1893 | app_.isLoadingPrefs ? "<Pr> " : "", | ||
1894 | windowIndex, | ||
1895 | (d == NULL || win == NULL ? 0 | ||
1896 | : d == win->roots[0] ? 1 | ||
1897 | : 2), | ||
1898 | command); | ||
1899 | fflush(stdout); | ||
1774 | } | 1900 | } |
1775 | #endif | 1901 | #endif |
1776 | } | 1902 | } |
@@ -1835,6 +1961,41 @@ void removeTicker_App(iTickerFunc ticker, iAny *context) { | |||
1835 | remove_SortedArray(&d->tickers, &(iTicker){ context, NULL, ticker }); | 1961 | remove_SortedArray(&d->tickers, &(iTicker){ context, NULL, ticker }); |
1836 | } | 1962 | } |
1837 | 1963 | ||
1964 | void addWindow_App(iMainWindow *win) { | ||
1965 | iApp *d = &app_; | ||
1966 | pushBack_PtrArray(&d->mainWindows, win); | ||
1967 | } | ||
1968 | |||
1969 | void removeWindow_App(iMainWindow *win) { | ||
1970 | iApp *d = &app_; | ||
1971 | removeOne_PtrArray(&d->mainWindows, win); | ||
1972 | } | ||
1973 | |||
1974 | size_t numWindows_App(void) { | ||
1975 | return size_PtrArray(&app_.mainWindows); | ||
1976 | } | ||
1977 | |||
1978 | size_t windowIndex_App(const iMainWindow *win) { | ||
1979 | return indexOf_PtrArray(&app_.mainWindows, win); | ||
1980 | } | ||
1981 | |||
1982 | iMainWindow *newMainWindow_App(void) { | ||
1983 | iApp *d = &app_; | ||
1984 | iMainWindow *win = new_MainWindow(initialWindowRect_App_(d, size_PtrArray(&d->mainWindows))); | ||
1985 | addWindow_App(win); | ||
1986 | return win; | ||
1987 | } | ||
1988 | |||
1989 | const iPtrArray *mainWindows_App(void) { | ||
1990 | return &app_.mainWindows; | ||
1991 | } | ||
1992 | |||
1993 | void setActiveWindow_App(iMainWindow *win) { | ||
1994 | iApp *d = &app_; | ||
1995 | d->window = win; | ||
1996 | printf("Active window: %p\n", win); fflush(stdout); | ||
1997 | } | ||
1998 | |||
1838 | void addPopup_App(iWindow *popup) { | 1999 | void addPopup_App(iWindow *popup) { |
1839 | iApp *d = &app_; | 2000 | iApp *d = &app_; |
1840 | pushBack_PtrArray(&d->popupWindows, popup); | 2001 | pushBack_PtrArray(&d->popupWindows, popup); |
@@ -1970,6 +2131,8 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
1970 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); | 2131 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); |
1971 | postCommandf_App("memorysize.set arg:%d", | 2132 | postCommandf_App("memorysize.set arg:%d", |
1972 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); | 2133 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); |
2134 | postCommandf_App("urlsize.set arg:%d", | ||
2135 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.urlsize")))); | ||
1973 | postCommandf_App("ca.file path:%s", | 2136 | postCommandf_App("ca.file path:%s", |
1974 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); | 2137 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); |
1975 | postCommandf_App("ca.path path:%s", | 2138 | postCommandf_App("ca.path path:%s", |
@@ -2111,6 +2274,22 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe | |||
2111 | return doc; | 2274 | return doc; |
2112 | } | 2275 | } |
2113 | 2276 | ||
2277 | void closeWindow_App(iMainWindow *win) { | ||
2278 | iApp *d = &app_; | ||
2279 | delete_MainWindow(win); | ||
2280 | if (d->window == win) { | ||
2281 | /* Activate another window. */ | ||
2282 | iForEach(PtrArray, i, &d->mainWindows) { | ||
2283 | if (i.ptr != d->window) { | ||
2284 | SDL_RaiseWindow(i.ptr); | ||
2285 | setActiveWindow_App(i.ptr); | ||
2286 | setCurrent_Window(i.ptr); | ||
2287 | break; | ||
2288 | } | ||
2289 | } | ||
2290 | } | ||
2291 | } | ||
2292 | |||
2114 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { | 2293 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { |
2115 | iApp *d = &app_; | 2294 | iApp *d = &app_; |
2116 | if (equal_Command(cmd, "ident.showmore")) { | 2295 | if (equal_Command(cmd, "ident.showmore")) { |
@@ -2257,6 +2436,13 @@ void resetFonts_App(void) { | |||
2257 | } | 2436 | } |
2258 | } | 2437 | } |
2259 | 2438 | ||
2439 | void availableFontsChanged_App(void) { | ||
2440 | iApp *d = &app_; | ||
2441 | iConstForEach(PtrArray, win, listWindows_App_(d, collectNew_PtrArray())) { | ||
2442 | resetMissing_Text(text_Window(win.ptr)); | ||
2443 | } | ||
2444 | } | ||
2445 | |||
2260 | static void invalidateCachedDocuments_App_(void) { | 2446 | static void invalidateCachedDocuments_App_(void) { |
2261 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | 2447 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { |
2262 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); | 2448 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); |
@@ -2274,6 +2460,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2274 | suffixPtr_Command(cmd, "where"))); | 2460 | suffixPtr_Command(cmd, "where"))); |
2275 | return iTrue; | 2461 | return iTrue; |
2276 | } | 2462 | } |
2463 | #if 0 /* disabled in v1.11 */ | ||
2277 | else if (equal_Command(cmd, "fontpack.suggest.classic")) { | 2464 | else if (equal_Command(cmd, "fontpack.suggest.classic")) { |
2278 | /* TODO: Don't use this when system fonts are accessible. */ | 2465 | /* TODO: Don't use this when system fonts are accessible. */ |
2279 | if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) { | 2466 | if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) { |
@@ -2290,6 +2477,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2290 | } | 2477 | } |
2291 | return iTrue; | 2478 | return iTrue; |
2292 | } | 2479 | } |
2480 | #endif | ||
2293 | else if (equal_Command(cmd, "prefs.changed")) { | 2481 | else if (equal_Command(cmd, "prefs.changed")) { |
2294 | savePrefs_App_(d); | 2482 | savePrefs_App_(d); |
2295 | return iTrue; | 2483 | return iTrue; |
@@ -2360,12 +2548,16 @@ iBool handleCommand_App(const char *cmd) { | |||
2360 | return iTrue; | 2548 | return iTrue; |
2361 | } | 2549 | } |
2362 | else if (equal_Command(cmd, "window.maximize")) { | 2550 | else if (equal_Command(cmd, "window.maximize")) { |
2363 | if (!argLabel_Command(cmd, "toggle")) { | 2551 | const size_t winIndex = argU32Label_Command(cmd, "index"); |
2364 | setSnap_MainWindow(d->window, maximized_WindowSnap); | 2552 | if (winIndex < size_PtrArray(&d->mainWindows)) { |
2365 | } | 2553 | iMainWindow *win = at_PtrArray(&d->mainWindows, winIndex); |
2366 | else { | 2554 | if (!argLabel_Command(cmd, "toggle")) { |
2367 | setSnap_MainWindow(d->window, snap_MainWindow(d->window) == maximized_WindowSnap ? 0 : | 2555 | setSnap_MainWindow(win, maximized_WindowSnap); |
2368 | maximized_WindowSnap); | 2556 | } |
2557 | else { | ||
2558 | setSnap_MainWindow( | ||
2559 | win, snap_MainWindow(win) == maximized_WindowSnap ? 0 : maximized_WindowSnap); | ||
2560 | } | ||
2369 | } | 2561 | } |
2370 | return iTrue; | 2562 | return iTrue; |
2371 | } | 2563 | } |
@@ -2383,24 +2575,49 @@ iBool handleCommand_App(const char *cmd) { | |||
2383 | reload_Fonts(); /* also does font cache reset, window invalidation */ | 2575 | reload_Fonts(); /* also does font cache reset, window invalidation */ |
2384 | return iTrue; | 2576 | return iTrue; |
2385 | } | 2577 | } |
2386 | #if 0 | 2578 | else if (equal_Command(cmd, "font.find")) { |
2387 | else if (equal_Command(cmd, "font.user")) { | 2579 | searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars")); |
2388 | const char *path = suffixPtr_Command(cmd, "path"); | 2580 | return iTrue; |
2389 | if (cmp_String(&d->prefs.symbolFontPath, path)) { | 2581 | } |
2390 | if (!isFrozen) { | 2582 | else if (equal_Command(cmd, "font.found")) { |
2391 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2583 | if (hasLabel_Command(cmd, "error")) { |
2392 | } | 2584 | makeSimpleMessage_Widget("${heading.glyphfinder}", |
2393 | setCStr_String(&d->prefs.symbolFontPath, path); | 2585 | format_CStr("%d %s", |
2394 | loadUserFonts_Text(); | 2586 | argLabel_Command(cmd, "error"), |
2395 | resetFonts_App(d); | 2587 | suffixPtr_Command(cmd, "msg"))); |
2396 | if (!isFrozen) { | 2588 | return iTrue; |
2397 | postCommand_App("font.changed"); | ||
2398 | postCommand_App("window.unfreeze"); | ||
2399 | } | ||
2400 | } | 2589 | } |
2590 | iString *src = collectNew_String(); | ||
2591 | setCStr_String(src, "# ${heading.glyphfinder.results}\n\n"); | ||
2592 | iRangecc path = iNullRange; | ||
2593 | iBool isFirst = iTrue; | ||
2594 | while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) { | ||
2595 | if (isFirst) { | ||
2596 | appendCStr_String(src, "${glyphfinder.results}\n\n"); | ||
2597 | } | ||
2598 | iRangecc fpath = path; | ||
2599 | iRangecc fsize = path; | ||
2600 | fpath.end = strchr(fpath.start, ';'); | ||
2601 | fsize.start = fpath.end + 1; | ||
2602 | const uint32_t size = strtoul(fsize.start, NULL, 10); | ||
2603 | appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n", | ||
2604 | cstr_Rangecc(fpath), | ||
2605 | cstr_Rangecc(fpath), | ||
2606 | (double) size / 1.0e6); | ||
2607 | isFirst = iFalse; | ||
2608 | } | ||
2609 | if (isFirst) { | ||
2610 | appendFormat_String(src, "${glyphfinder.results.empty}\n"); | ||
2611 | } | ||
2612 | appendCStr_String(src, "\n=> about:fonts ${menu.fonts}"); | ||
2613 | iDocumentWidget *page = newTab_App(NULL, iTrue); | ||
2614 | translate_Lang(src); | ||
2615 | setUrlAndSource_DocumentWidget(page, | ||
2616 | collectNewCStr_String(""), | ||
2617 | collectNewCStr_String("text/gemini"), | ||
2618 | utf8_String(src)); | ||
2401 | return iTrue; | 2619 | return iTrue; |
2402 | } | 2620 | } |
2403 | #endif | ||
2404 | else if (equal_Command(cmd, "font.set")) { | 2621 | else if (equal_Command(cmd, "font.set")) { |
2405 | if (!isFrozen) { | 2622 | if (!isFrozen) { |
2406 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2623 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); |
@@ -2690,6 +2907,10 @@ iBool handleCommand_App(const char *cmd) { | |||
2690 | postRefresh_App(); | 2907 | postRefresh_App(); |
2691 | return iTrue; | 2908 | return iTrue; |
2692 | } | 2909 | } |
2910 | else if (equal_Command(cmd, "prefs.dataurl.openimages.changed")) { | ||
2911 | d->prefs.openDataUrlImagesOnLoad = arg_Command(cmd) != 0; | ||
2912 | return iTrue; | ||
2913 | } | ||
2693 | else if (equal_Command(cmd, "prefs.archive.openindex.changed")) { | 2914 | else if (equal_Command(cmd, "prefs.archive.openindex.changed")) { |
2694 | d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0; | 2915 | d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0; |
2695 | return iTrue; | 2916 | return iTrue; |
@@ -2735,6 +2956,13 @@ iBool handleCommand_App(const char *cmd) { | |||
2735 | } | 2956 | } |
2736 | return iTrue; | 2957 | return iTrue; |
2737 | } | 2958 | } |
2959 | else if (equal_Command(cmd, "urlsize.set")) { | ||
2960 | d->prefs.maxUrlSize = arg_Command(cmd); | ||
2961 | if (d->prefs.maxUrlSize < 1024) { | ||
2962 | d->prefs.maxUrlSize = 1024; /* Gemini protocol requirement */ | ||
2963 | } | ||
2964 | return iTrue; | ||
2965 | } | ||
2738 | else if (equal_Command(cmd, "searchurl")) { | 2966 | else if (equal_Command(cmd, "searchurl")) { |
2739 | iString *url = &d->prefs.strings[searchUrl_PrefsString]; | 2967 | iString *url = &d->prefs.strings[searchUrl_PrefsString]; |
2740 | setCStr_String(url, suffixPtr_Command(cmd, "address")); | 2968 | setCStr_String(url, suffixPtr_Command(cmd, "address")); |
@@ -2817,7 +3045,18 @@ iBool handleCommand_App(const char *cmd) { | |||
2817 | return iTrue; /* invalid command */ | 3045 | return iTrue; /* invalid command */ |
2818 | } | 3046 | } |
2819 | if (findWidget_App("prefs")) { | 3047 | if (findWidget_App("prefs")) { |
2820 | postCommand_App("prefs.dismiss"); | 3048 | postCommand_App("prefs.dismiss"); |
3049 | } | ||
3050 | if (argLabel_Command(cmd, "newwindow")) { | ||
3051 | const iRangecc gotoheading = range_Command(cmd, "gotoheading"); | ||
3052 | const iRangecc gotourlheading = range_Command(cmd, "gotourlheading"); | ||
3053 | postCommandf_Root(get_Root(), "window.new%s%s%s%s url:%s", | ||
3054 | isEmpty_Range(&gotoheading) ? "" : " gotoheading:", | ||
3055 | isEmpty_Range(&gotoheading) ? "" : cstr_Rangecc(gotoheading), | ||
3056 | isEmpty_Range(&gotourlheading) ? "" : " gotourlheading:", | ||
3057 | isEmpty_Range(&gotourlheading) ? "" : cstr_Rangecc(gotourlheading), | ||
3058 | urlArg); | ||
3059 | return iTrue; | ||
2821 | } | 3060 | } |
2822 | iString *url = collectNewCStr_String(urlArg); | 3061 | iString *url = collectNewCStr_String(urlArg); |
2823 | const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; | 3062 | const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; |
@@ -2961,6 +3200,20 @@ iBool handleCommand_App(const char *cmd) { | |||
2961 | #endif | 3200 | #endif |
2962 | return iFalse; | 3201 | return iFalse; |
2963 | } | 3202 | } |
3203 | else if (equal_Command(cmd, "window.new")) { | ||
3204 | iMainWindow *newWin = new_MainWindow(initialWindowRect_App_(d, numWindows_App())); | ||
3205 | addWindow_App(newWin); /* takes ownership */ | ||
3206 | SDL_ShowWindow(newWin->base.win); | ||
3207 | setCurrent_Window(newWin); | ||
3208 | if (hasLabel_Command(cmd, "url")) { | ||
3209 | postCommandf_Root(newWin->base.roots[0], "~open %s", cmd + 11 /* all arguments passed on */); | ||
3210 | } | ||
3211 | else { | ||
3212 | postCommand_Root(newWin->base.roots[0], "~navigate.home"); | ||
3213 | } | ||
3214 | postCommand_Root(newWin->base.roots[0], "~window.unfreeze"); | ||
3215 | return iTrue; | ||
3216 | } | ||
2964 | else if (equal_Command(cmd, "tabs.new")) { | 3217 | else if (equal_Command(cmd, "tabs.new")) { |
2965 | const iBool isDuplicate = argLabel_Command(cmd, "duplicate") != 0; | 3218 | const iBool isDuplicate = argLabel_Command(cmd, "duplicate") != 0; |
2966 | newTab_App(isDuplicate ? document_App() : NULL, iTrue); | 3219 | newTab_App(isDuplicate ? document_App() : NULL, iTrue); |
@@ -3021,6 +3274,9 @@ iBool handleCommand_App(const char *cmd) { | |||
3021 | } | 3274 | } |
3022 | } | 3275 | } |
3023 | } | 3276 | } |
3277 | else if (numWindows_App() > 1) { | ||
3278 | closeWindow_App(d->window); | ||
3279 | } | ||
3024 | else { | 3280 | else { |
3025 | postCommand_App("quit"); | 3281 | postCommand_App("quit"); |
3026 | } | 3282 | } |
@@ -3050,6 +3306,7 @@ iBool handleCommand_App(const char *cmd) { | |||
3050 | setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); | 3306 | setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); |
3051 | setToggle_Widget(findChild_Widget(dlg, "prefs.bookmarks.addbottom"), d->prefs.addBookmarksToBottom); | 3307 | setToggle_Widget(findChild_Widget(dlg, "prefs.bookmarks.addbottom"), d->prefs.addBookmarksToBottom); |
3052 | setToggle_Widget(findChild_Widget(dlg, "prefs.font.warnmissing"), d->prefs.warnAboutMissingGlyphs); | 3308 | setToggle_Widget(findChild_Widget(dlg, "prefs.font.warnmissing"), d->prefs.warnAboutMissingGlyphs); |
3309 | setToggle_Widget(findChild_Widget(dlg, "prefs.dataurl.openimages"), d->prefs.openDataUrlImagesOnLoad); | ||
3053 | setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); | 3310 | setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); |
3054 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); | 3311 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); |
3055 | setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); | 3312 | setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); |
@@ -3122,6 +3379,8 @@ iBool handleCommand_App(const char *cmd) { | |||
3122 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); | 3379 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); |
3123 | setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), | 3380 | setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), |
3124 | collectNewFormat_String("%d", d->prefs.maxMemorySize)); | 3381 | collectNewFormat_String("%d", d->prefs.maxMemorySize)); |
3382 | setText_InputWidget(findChild_Widget(dlg, "prefs.urlsize"), | ||
3383 | collectNewFormat_String("%d", d->prefs.maxUrlSize)); | ||
3125 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); | 3384 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); |
3126 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.strings[searchUrl_PrefsString]); | 3385 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.strings[searchUrl_PrefsString]); |
3127 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.strings[caFile_PrefsString]); | 3386 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.strings[caFile_PrefsString]); |
@@ -3509,21 +3768,7 @@ void revealPath_App(const iString *path) { | |||
3509 | } | 3768 | } |
3510 | 3769 | ||
3511 | iObjectList *listDocuments_App(const iRoot *rootOrNull) { | 3770 | iObjectList *listDocuments_App(const iRoot *rootOrNull) { |
3512 | iWindow *win = get_Window(); | 3771 | return listDocuments_MainWindow(get_MainWindow(), rootOrNull); |
3513 | iObjectList *docs = new_ObjectList(); | ||
3514 | iForIndices(i, win->roots) { | ||
3515 | iRoot *root = win->roots[i]; | ||
3516 | if (!root) continue; | ||
3517 | if (!rootOrNull || root == rootOrNull) { | ||
3518 | const iWidget *tabs = findChild_Widget(root->widget, "doctabs"); | ||
3519 | iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
3520 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
3521 | pushBack_ObjectList(docs, i.object); | ||
3522 | } | ||
3523 | } | ||
3524 | } | ||
3525 | } | ||
3526 | return docs; | ||
3527 | } | 3772 | } |
3528 | 3773 | ||
3529 | iStringSet *listOpenURLs_App(void) { | 3774 | iStringSet *listOpenURLs_App(void) { |
@@ -3540,12 +3785,16 @@ iMainWindow *mainWindow_App(void) { | |||
3540 | return app_.window; | 3785 | return app_.window; |
3541 | } | 3786 | } |
3542 | 3787 | ||
3543 | void closePopups_App(void) { | 3788 | void closePopups_App(iBool doForce) { |
3544 | iApp *d = &app_; | 3789 | iApp *d = &app_; |
3545 | const uint32_t now = SDL_GetTicks(); | 3790 | const uint32_t now = SDL_GetTicks(); |
3546 | iConstForEach(PtrArray, i, &d->popupWindows) { | 3791 | iForEach(PtrArray, i, &d->popupWindows) { |
3547 | const iWindow *win = i.ptr; | 3792 | iWindow *win = i.ptr; |
3548 | if (now - win->focusGainedAt > 200) { | 3793 | // if (doForce) { |
3794 | // collect_Garbage(win, (iDeleteFunc) delete_Window); | ||
3795 | // } | ||
3796 | // else | ||
3797 | if (now - win->focusGainedAt > 200) { | ||
3549 | postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel"); | 3798 | postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel"); |
3550 | } | 3799 | } |
3551 | } | 3800 | } |
@@ -67,6 +67,7 @@ enum iUserEventCode { | |||
67 | take, it could turn into a tap-and-hold for example. */ | 67 | take, it could turn into a tap-and-hold for example. */ |
68 | widgetTapBegins_UserEventCode, | 68 | widgetTapBegins_UserEventCode, |
69 | widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */ | 69 | widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */ |
70 | releaseObject_UserEventCode, /* object that needs releasing in the main thread */ | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | const iString *execPath_App (void); | 73 | const iString *execPath_App (void); |
@@ -97,6 +98,7 @@ iPeriodic * periodic_App (void); | |||
97 | iDocumentWidget * document_App (void); | 98 | iDocumentWidget * document_App (void); |
98 | iObjectList * listDocuments_App (const iRoot *rootOrNull); /* NULL for all roots */ | 99 | iObjectList * listDocuments_App (const iRoot *rootOrNull); /* NULL for all roots */ |
99 | iStringSet * listOpenURLs_App (void); /* all tabs */ | 100 | iStringSet * listOpenURLs_App (void); /* all tabs */ |
101 | iPtrArray * listWindows_App (void); | ||
100 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); | 102 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); |
101 | void trimCache_App (void); | 103 | void trimCache_App (void); |
102 | void trimMemory_App (void); | 104 | void trimMemory_App (void); |
@@ -121,6 +123,14 @@ iAny * findWidget_App (const char *id); | |||
121 | void addTicker_App (iTickerFunc ticker, iAny *context); | 123 | void addTicker_App (iTickerFunc ticker, iAny *context); |
122 | void addTickerRoot_App (iTickerFunc ticker, iRoot *root, iAny *context); | 124 | void addTickerRoot_App (iTickerFunc ticker, iRoot *root, iAny *context); |
123 | void removeTicker_App (iTickerFunc ticker, iAny *context); | 125 | void removeTicker_App (iTickerFunc ticker, iAny *context); |
126 | void addWindow_App (iMainWindow *win); | ||
127 | void removeWindow_App (iMainWindow *win); | ||
128 | void setActiveWindow_App (iMainWindow *win); | ||
129 | void closeWindow_App (iMainWindow *win); | ||
130 | size_t numWindows_App (void); | ||
131 | size_t windowIndex_App (const iMainWindow *win); | ||
132 | iMainWindow *newMainWindow_App (void); | ||
133 | const iPtrArray *mainWindows_App(void); | ||
124 | void addPopup_App (iWindow *popup); | 134 | void addPopup_App (iWindow *popup); |
125 | void removePopup_App (iWindow *popup); | 135 | void removePopup_App (iWindow *popup); |
126 | void postRefresh_App (void); | 136 | void postRefresh_App (void); |
@@ -142,6 +152,7 @@ iDocumentWidget * document_Command (const char *cmd); | |||
142 | void openInDefaultBrowser_App(const iString *url); | 152 | void openInDefaultBrowser_App(const iString *url); |
143 | void revealPath_App (const iString *path); | 153 | void revealPath_App (const iString *path); |
144 | void resetFonts_App (void); | 154 | void resetFonts_App (void); |
155 | void availableFontsChanged_App(void); | ||
145 | 156 | ||
146 | iMainWindow * mainWindow_App (void); | 157 | iMainWindow * mainWindow_App (void); |
147 | void closePopups_App (void); | 158 | void closePopups_App (iBool doForce); |
@@ -38,9 +38,10 @@ enum iFileVersion { | |||
38 | serializedSidebarState_FileVersion = 3, | 38 | serializedSidebarState_FileVersion = 3, |
39 | addedRecentUrlFlags_FileVersion = 4, | 39 | addedRecentUrlFlags_FileVersion = 4, |
40 | bookmarkFolderState_FileVersion = 5, | 40 | bookmarkFolderState_FileVersion = 5, |
41 | multipleWindows_FileVersion = 6, | ||
41 | /* meta */ | 42 | /* meta */ |
42 | idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ | 43 | latest_FileVersion = 6, /* used by state.lgr */ |
43 | latest_FileVersion = 5, | 44 | idents_FileVersion = 1, /* used by GmCerts/idents.lgr */ |
44 | }; | 45 | }; |
45 | 46 | ||
46 | enum iImageStyle { | 47 | enum iImageStyle { |
@@ -156,8 +157,9 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) { | |||
156 | #define bookmark_Icon "\U0001f516" | 157 | #define bookmark_Icon "\U0001f516" |
157 | #define folder_Icon "\U0001f4c1" | 158 | #define folder_Icon "\U0001f4c1" |
158 | #define file_Icon "\U0001f5ce" | 159 | #define file_Icon "\U0001f5ce" |
159 | #define openTab_Icon "\u2750" | 160 | #define openWindow_Icon "\u2b1a" //"\U0001F5d4" |
160 | #define openTabBg_Icon "\u2b1a" | 161 | #define openTab_Icon add_Icon |
162 | #define openTabBg_Icon "\u2750" //"\u2b1a" | ||
161 | #define openExt_Icon "\u27a0" | 163 | #define openExt_Icon "\u27a0" |
162 | #define add_Icon "\u2795" | 164 | #define add_Icon "\u2795" |
163 | #define page_Icon "\U00010117" | 165 | #define page_Icon "\U00010117" |
@@ -186,6 +188,8 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) { | |||
186 | #define downAngle_Icon "\ufe40" | 188 | #define downAngle_Icon "\ufe40" |
187 | #define photo_Icon "\U0001f5bc" | 189 | #define photo_Icon "\U0001f5bc" |
188 | #define fontpack_Icon "\U0001f520" | 190 | #define fontpack_Icon "\U0001f520" |
191 | #define package_Icon "\U0001f4e6" | ||
192 | #define paperclip_Icon "\U0001f4ce" | ||
189 | 193 | ||
190 | #if defined (iPlatformApple) | 194 | #if defined (iPlatformApple) |
191 | # define shift_Icon "\u21e7" | 195 | # define shift_Icon "\u21e7" |
diff --git a/src/fontpack.c b/src/fontpack.c index a440234e..96006226 100644 --- a/src/fontpack.c +++ b/src/fontpack.c | |||
@@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
23 | #include "fontpack.h" | 23 | #include "fontpack.h" |
24 | #include "resources.h" | 24 | #include "resources.h" |
25 | #include "ui/window.h" | 25 | #include "ui/window.h" |
26 | #include "gmrequest.h" | ||
26 | #include "app.h" | 27 | #include "app.h" |
27 | 28 | ||
28 | #include <the_Foundation/archive.h> | 29 | #include <the_Foundation/archive.h> |
@@ -748,7 +749,7 @@ const iPtrArray *listSpecsByPriority_Fonts(void) { | |||
748 | return &fonts_.specOrder; | 749 | return &fonts_.specOrder; |
749 | } | 750 | } |
750 | 751 | ||
751 | iString *infoText_FontPack(const iFontPack *d) { | 752 | iString *infoText_FontPack(const iFontPack *d, iBool isFull) { |
752 | const iFontPack *installed = pack_Fonts(cstr_String(&d->id)); | 753 | const iFontPack *installed = pack_Fonts(cstr_String(&d->id)); |
753 | const iBool isInstalled = (installed != NULL); | 754 | const iBool isInstalled = (installed != NULL); |
754 | const int installedVersion = installed ? installed->version : 0; | 755 | const int installedVersion = installed ? installed->version : 0; |
@@ -757,9 +758,17 @@ iString *infoText_FontPack(const iFontPack *d) { | |||
757 | size_t sizeInBytes = 0; | 758 | size_t sizeInBytes = 0; |
758 | iPtrSet *uniqueFiles = new_PtrSet(); | 759 | iPtrSet *uniqueFiles = new_PtrSet(); |
759 | iStringList *names = new_StringList(); | 760 | iStringList *names = new_StringList(); |
761 | size_t numNames = 0; | ||
762 | iBool isAbbreviated = iFalse; | ||
760 | iConstForEach(PtrArray, i, listSpecs_FontPack(d)) { | 763 | iConstForEach(PtrArray, i, listSpecs_FontPack(d)) { |
761 | const iFontSpec *spec = i.ptr; | 764 | const iFontSpec *spec = i.ptr; |
762 | pushBack_StringList(names, &spec->name); | 765 | numNames++; |
766 | if (isFull || size_StringList(names) < 20) { | ||
767 | pushBack_StringList(names, &spec->name); | ||
768 | } | ||
769 | else { | ||
770 | isAbbreviated = iTrue; | ||
771 | } | ||
763 | iForIndices(j, spec->styles) { | 772 | iForIndices(j, spec->styles) { |
764 | insert_PtrSet(uniqueFiles, spec->styles[j]->sourceData.i); | 773 | insert_PtrSet(uniqueFiles, spec->styles[j]->sourceData.i); |
765 | } | 774 | } |
@@ -777,11 +786,12 @@ iString *infoText_FontPack(const iFontPack *d) { | |||
777 | if (!endsWith_String(str, "(")) { | 786 | if (!endsWith_String(str, "(")) { |
778 | appendCStr_String(str, ", "); | 787 | appendCStr_String(str, ", "); |
779 | } | 788 | } |
780 | appendCStr_String(str, formatCStrs_Lang("num.fonts.n", size_StringList(names))); | 789 | appendCStr_String(str, formatCStrs_Lang("num.fonts.n", numNames)); |
781 | } | 790 | } |
782 | appendFormat_String(str, ")"); | 791 | appendFormat_String(str, ")"); |
783 | } | 792 | } |
784 | appendFormat_String(str, " \u2014 %s\n", cstrCollect_String(joinCStr_StringList(names, ", "))); | 793 | appendFormat_String(str, " \u2014 %s%s\n", cstrCollect_String(joinCStr_StringList(names, ", ")), |
794 | isAbbreviated ? ", ..." : ""); | ||
785 | if (isInstalled && installedVersion != d->version) { | 795 | if (isInstalled && installedVersion != d->version) { |
786 | appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version)); | 796 | appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version)); |
787 | } | 797 | } |
@@ -945,7 +955,7 @@ const iString *infoPage_Fonts(iRangecc query) { | |||
945 | appendFormat_String(str, "### %s\n", | 955 | appendFormat_String(str, "### %s\n", |
946 | isEmpty_String(packId) ? "fonts.ini" : | 956 | isEmpty_String(packId) ? "fonts.ini" : |
947 | cstr_String(packId)); | 957 | cstr_String(packId)); |
948 | append_String(str, collect_String(infoText_FontPack(pack))); | 958 | append_String(str, collect_String(infoText_FontPack(pack, iFalse))); |
949 | appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n", | 959 | appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n", |
950 | cstrCollect_String(makeFileUrl_String(&spec->sourcePath))); | 960 | cstrCollect_String(makeFileUrl_String(&spec->sourcePath))); |
951 | if (pack->isStandalone) { | 961 | if (pack->isStandalone) { |
@@ -1018,6 +1028,7 @@ void install_Fonts(const iString *packId, const iBlock *data) { | |||
1018 | iRelease(f); | 1028 | iRelease(f); |
1019 | /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ | 1029 | /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ |
1020 | reload_Fonts(); | 1030 | reload_Fonts(); |
1031 | availableFontsChanged_App(); | ||
1021 | } | 1032 | } |
1022 | 1033 | ||
1023 | void installFontFile_Fonts(const iString *fileName, const iBlock *data) { | 1034 | void installFontFile_Fonts(const iString *fileName, const iBlock *data) { |
@@ -1028,6 +1039,7 @@ void installFontFile_Fonts(const iString *fileName, const iBlock *data) { | |||
1028 | } | 1039 | } |
1029 | iRelease(f); | 1040 | iRelease(f); |
1030 | reload_Fonts(); | 1041 | reload_Fonts(); |
1042 | availableFontsChanged_App(); | ||
1031 | } | 1043 | } |
1032 | 1044 | ||
1033 | void enablePack_Fonts(const iString *packId, iBool enable) { | 1045 | void enablePack_Fonts(const iString *packId, iBool enable) { |
@@ -1040,6 +1052,7 @@ void enablePack_Fonts(const iString *packId, iBool enable) { | |||
1040 | } | 1052 | } |
1041 | updateActive_Fonts(); | 1053 | updateActive_Fonts(); |
1042 | resetFonts_App(); | 1054 | resetFonts_App(); |
1055 | availableFontsChanged_App(); | ||
1043 | invalidate_Window(get_MainWindow()); | 1056 | invalidate_Window(get_MainWindow()); |
1044 | } | 1057 | } |
1045 | 1058 | ||
@@ -1047,5 +1060,100 @@ void updateActive_Fonts(void) { | |||
1047 | sortSpecs_Fonts_(&fonts_); | 1060 | sortSpecs_Fonts_(&fonts_); |
1048 | } | 1061 | } |
1049 | 1062 | ||
1050 | iDefineClass(FontFile) | 1063 | static void findCharactersInCMap_(iGmRequest *d, iGmRequest *req) { |
1064 | /* Note: Called in background thread. */ | ||
1065 | iUnused(req); | ||
1066 | const iString *missingChars = userData_Object(d); | ||
1067 | if (isSuccess_GmStatusCode(status_GmRequest(d))) { | ||
1068 | iStringList *matchingPacks = new_StringList(); | ||
1069 | iChar needed[20]; | ||
1070 | iChar minChar = UINT32_MAX, maxChar = 0; | ||
1071 | size_t numNeeded = 0; | ||
1072 | iConstForEach(String, ch, missingChars) { | ||
1073 | needed[numNeeded++] = ch.value; | ||
1074 | minChar = iMin(minChar, ch.value); | ||
1075 | maxChar = iMax(maxChar, ch.value); | ||
1076 | if (numNeeded == iElemCount(needed)) { | ||
1077 | /* Shouldn't be that many. */ | ||
1078 | break; | ||
1079 | } | ||
1080 | } | ||
1081 | iBlock *data = decompressGzip_Block(body_GmRequest(d)); | ||
1082 | iRangecc line = iNullRange; | ||
1083 | while (nextSplit_Rangecc(range_Block(data), "\n", &line)) { | ||
1084 | iRangecc fontpackPath = iNullRange; | ||
1085 | for (const char *pos = line.start; pos < line.end; pos++) { | ||
1086 | if (*pos == ':') { | ||
1087 | fontpackPath.start = line.start; | ||
1088 | fontpackPath.end = pos; | ||
1089 | line.start = pos + 1; | ||
1090 | trimStart_Rangecc(&line); | ||
1091 | break; | ||
1092 | } | ||
1093 | } | ||
1094 | if (fontpackPath.start) { | ||
1095 | /* Parse the character ranges and see if any match what we need. */ | ||
1096 | const char *pos = line.start; | ||
1097 | while (pos < line.end) { | ||
1098 | char *endp; | ||
1099 | uint32_t first = strtoul(pos, &endp, 10); | ||
1100 | uint32_t last = first; | ||
1101 | if (*endp == '-') { | ||
1102 | last = strtoul(endp + 1, &endp, 10); | ||
1103 | } | ||
1104 | if (maxChar < first) { | ||
1105 | break; /* The rest are even higher. */ | ||
1106 | } | ||
1107 | if (minChar <= last) { | ||
1108 | for (size_t i = 0; i < numNeeded; i++) { | ||
1109 | if (needed[i] >= first && needed[i] <= last) { | ||
1110 | /* Got it. */ | ||
1111 | pushBackRange_StringList(matchingPacks, fontpackPath); | ||
1112 | break; | ||
1113 | } | ||
1114 | } | ||
1115 | } | ||
1116 | pos = endp + 1; | ||
1117 | } | ||
1118 | } | ||
1119 | } | ||
1120 | delete_Block(data); | ||
1121 | iString result; | ||
1122 | init_String(&result); | ||
1123 | format_String(&result, "font.found chars:%s packs:", cstr_String(missingChars)); | ||
1124 | iConstForEach(StringList, s, matchingPacks) { | ||
1125 | if (s.pos != 0) { | ||
1126 | appendCStr_String(&result, ","); | ||
1127 | } | ||
1128 | append_String(&result, s.value); | ||
1129 | } | ||
1130 | postCommandString_Root(NULL, &result); | ||
1131 | deinit_String(&result); | ||
1132 | iRelease(matchingPacks); | ||
1133 | } | ||
1134 | else { | ||
1135 | /* Report error. */ | ||
1136 | postCommandf_Root(NULL, | ||
1137 | "font.found chars:%s error:%d msg:\x1b[1m%s\x1b[0m\n%s", | ||
1138 | cstr_String(missingChars), | ||
1139 | status_GmRequest(d), | ||
1140 | cstr_String(meta_GmRequest(d)), | ||
1141 | cstr_String(url_GmRequest(d))); | ||
1142 | } | ||
1143 | // fflush(stdout); | ||
1144 | delete_String(userData_Object(d)); | ||
1145 | /* We can't delete ourselves; threads must be joined from another thread. */ | ||
1146 | SDL_PushEvent((SDL_Event *) &(SDL_UserEvent){ | ||
1147 | .type = SDL_USEREVENT, .code = releaseObject_UserEventCode, .data1 = d }); | ||
1148 | } | ||
1051 | 1149 | ||
1150 | void searchOnlineLibraryForCharacters_Fonts(const iString *chars) { | ||
1151 | /* Fetch the character map from skyjake.fi. */ | ||
1152 | iGmRequest *req = new_GmRequest(certs_App()); | ||
1153 | setUrl_GmRequest(req, collectNewCStr_String("gemini://skyjake.fi/fonts/cmap.txt.gz")); | ||
1154 | setUserData_Object(req, copy_String(chars)); | ||
1155 | iConnect(GmRequest, req, finished, req, findCharactersInCMap_); | ||
1156 | submit_GmRequest(req); | ||
1157 | } | ||
1158 | |||
1159 | iDefineClass(FontFile) | ||
diff --git a/src/fontpack.h b/src/fontpack.h index aa6f2b9f..f6d4d483 100644 --- a/src/fontpack.h +++ b/src/fontpack.h | |||
@@ -160,7 +160,7 @@ const iString * loadPath_FontPack (const iFontPack *); /* may return N | |||
160 | iBool isDisabled_FontPack (const iFontPack *); | 160 | iBool isDisabled_FontPack (const iFontPack *); |
161 | iBool isReadOnly_FontPack (const iFontPack *); | 161 | iBool isReadOnly_FontPack (const iFontPack *); |
162 | const iPtrArray * listSpecs_FontPack (const iFontPack *); | 162 | const iPtrArray * listSpecs_FontPack (const iFontPack *); |
163 | iString * infoText_FontPack (const iFontPack *); | 163 | iString * infoText_FontPack (const iFontPack *, iBool isFull); |
164 | const iArray * actions_FontPack (const iFontPack *, iBool showInstalled); | 164 | const iArray * actions_FontPack (const iFontPack *, iBool showInstalled); |
165 | 165 | ||
166 | const iString * idFromUrl_FontPack (const iString *url); | 166 | const iString * idFromUrl_FontPack (const iString *url); |
@@ -186,3 +186,5 @@ void reload_Fonts (void); | |||
186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { | 186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { |
187 | return pack_Fonts(packId) != NULL; | 187 | return pack_Fonts(packId) != NULL; |
188 | } | 188 | } |
189 | |||
190 | void searchOnlineLibraryForCharacters_Fonts (const iString *chars); | ||
diff --git a/src/gmdocument.c b/src/gmdocument.c index 5ff8c72f..a85e4b71 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -333,13 +333,14 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
333 | link->urlRange = capturedRange_RegExpMatch(&m, 1); | 333 | link->urlRange = capturedRange_RegExpMatch(&m, 1); |
334 | setRange_String(&link->url, link->urlRange); | 334 | setRange_String(&link->url, link->urlRange); |
335 | set_String(&link->url, canonicalUrl_String(absoluteUrl_String(&d->url, &link->url))); | 335 | set_String(&link->url, canonicalUrl_String(absoluteUrl_String(&d->url, &link->url))); |
336 | if (startsWithCase_String(&link->url, "about:command")) { | 336 | /* If invalid, disregard the link. */ |
337 | /* This is a special internal page that allows submitting UI events. */ | 337 | if ((d->format == gemini_SourceFormat && size_String(&link->url) > prefs_App()->maxUrlSize) || |
338 | if (!d->enableCommandLinks) { | 338 | (startsWithCase_String(&link->url, "about:command") |
339 | delete_GmLink(link); | 339 | /* this is a special internal page that allows submitting UI events */ |
340 | *linkId = 0; | 340 | && !d->enableCommandLinks)) { |
341 | return line; | 341 | delete_GmLink(link); |
342 | } | 342 | *linkId = 0; |
343 | return line; | ||
343 | } | 344 | } |
344 | /* Check the URL. */ { | 345 | /* Check the URL. */ { |
345 | iUrl parts; | 346 | iUrl parts; |
@@ -370,6 +371,13 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
370 | } | 371 | } |
371 | else if (equalCase_Rangecc(parts.scheme, "data")) { | 372 | else if (equalCase_Rangecc(parts.scheme, "data")) { |
372 | setScheme_GmLink_(link, data_GmLinkScheme); | 373 | setScheme_GmLink_(link, data_GmLinkScheme); |
374 | if (startsWith_Rangecc(parts.path, "image/png") || | ||
375 | startsWith_Rangecc(parts.path, "image/jpg") || | ||
376 | startsWith_Rangecc(parts.path, "image/jpeg") || | ||
377 | startsWith_Rangecc(parts.path, "image/webp") || | ||
378 | startsWith_Rangecc(parts.path, "image/gif")) { | ||
379 | link->flags |= imageFileExtension_GmLinkFlag; | ||
380 | } | ||
373 | } | 381 | } |
374 | else if (equalCase_Rangecc(parts.scheme, "about")) { | 382 | else if (equalCase_Rangecc(parts.scheme, "about")) { |
375 | setScheme_GmLink_(link, about_GmLinkScheme); | 383 | setScheme_GmLink_(link, about_GmLinkScheme); |
@@ -382,7 +390,7 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
382 | iString *path = newRange_String(parts.path); | 390 | iString *path = newRange_String(parts.path); |
383 | if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") || | 391 | if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") || |
384 | endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") || | 392 | endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") || |
385 | endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") || | 393 | endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") || |
386 | #if defined (LAGRANGE_ENABLE_WEBP) | 394 | #if defined (LAGRANGE_ENABLE_WEBP) |
387 | endsWithCase_String(path, ".webp") || | 395 | endsWithCase_String(path, ".webp") || |
388 | #endif | 396 | #endif |
@@ -483,8 +491,7 @@ static iBool isNormalized_GmDocument_(const iGmDocument *d) { | |||
483 | } | 491 | } |
484 | 492 | ||
485 | static enum iGmDocumentTheme currentTheme_(void) { | 493 | static enum iGmDocumentTheme currentTheme_(void) { |
486 | return (isDark_ColorTheme(colorTheme_App()) ? prefs_App()->docThemeDark | 494 | return docTheme_Prefs(prefs_App()); |
487 | : prefs_App()->docThemeLight); | ||
488 | } | 495 | } |
489 | 496 | ||
490 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { | 497 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { |
@@ -904,6 +911,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
904 | : scheme == titan_GmLinkScheme ? uploadArrow | 911 | : scheme == titan_GmLinkScheme ? uploadArrow |
905 | : scheme == finger_GmLinkScheme ? pointingFinger | 912 | : scheme == finger_GmLinkScheme ? pointingFinger |
906 | : scheme == mailto_GmLinkScheme ? envelope | 913 | : scheme == mailto_GmLinkScheme ? envelope |
914 | : scheme == data_GmLinkScheme ? paperclip_Icon | ||
907 | : link->flags & remote_GmLinkFlag ? globe | 915 | : link->flags & remote_GmLinkFlag ? globe |
908 | : link->flags & imageFileExtension_GmLinkFlag ? image | 916 | : link->flags & imageFileExtension_GmLinkFlag ? image |
909 | : link->flags & fontpackFileExtension_GmLinkFlag ? fontpack_Icon | 917 | : link->flags & fontpackFileExtension_GmLinkFlag ? fontpack_Icon |
@@ -1283,7 +1291,7 @@ static void updateIconBasedOnUrl_GmDocument_(iGmDocument *d) { | |||
1283 | } | 1291 | } |
1284 | } | 1292 | } |
1285 | 1293 | ||
1286 | void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | 1294 | void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *paletteSeed, const iBlock *iconSeed) { |
1287 | const iPrefs * prefs = prefs_App(); | 1295 | const iPrefs * prefs = prefs_App(); |
1288 | enum iGmDocumentTheme theme = currentTheme_(); | 1296 | enum iGmDocumentTheme theme = currentTheme_(); |
1289 | static const iChar siteIcons[] = { | 1297 | static const iChar siteIcons[] = { |
@@ -1294,6 +1302,16 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1294 | 0x1f306, 0x1f308, 0x1f30a, 0x1f319, 0x1f31f, 0x1f320, 0x1f340, 0x1f4cd, 0x1f4e1, 0x1f531, | 1302 | 0x1f306, 0x1f308, 0x1f30a, 0x1f319, 0x1f31f, 0x1f320, 0x1f340, 0x1f4cd, 0x1f4e1, 0x1f531, |
1295 | 0x1f533, 0x1f657, 0x1f659, 0x1f665, 0x1f668, 0x1f66b, 0x1f78b, 0x1f796, 0x1f79c, | 1303 | 0x1f533, 0x1f657, 0x1f659, 0x1f665, 0x1f668, 0x1f66b, 0x1f78b, 0x1f796, 0x1f79c, |
1296 | }; | 1304 | }; |
1305 | if (!iconSeed) { | ||
1306 | iconSeed = paletteSeed; | ||
1307 | } | ||
1308 | if (iconSeed && !isEmpty_Block(iconSeed)) { | ||
1309 | const uint32_t seedHash = themeHash_(iconSeed); | ||
1310 | d->siteIcon = siteIcons[(seedHash >> 7) % iElemCount(siteIcons)]; | ||
1311 | } | ||
1312 | else { | ||
1313 | d->siteIcon = 0; | ||
1314 | } | ||
1297 | /* Default colors. These are used on "about:" pages and local files, for example. */ { | 1315 | /* Default colors. These are used on "about:" pages and local files, for example. */ { |
1298 | /* Link colors are generally the same in all themes. */ | 1316 | /* Link colors are generally the same in all themes. */ |
1299 | set_Color(tmBadLink_ColorId, get_Color(red_ColorId)); | 1317 | set_Color(tmBadLink_ColorId, get_Color(red_ColorId)); |
@@ -1495,13 +1513,11 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1495 | } | 1513 | } |
1496 | } | 1514 | } |
1497 | } | 1515 | } |
1498 | if (seed && !isEmpty_Block(seed)) { | 1516 | if (paletteSeed && !isEmpty_Block(paletteSeed)) { |
1499 | d->themeSeed = themeHash_(seed); | 1517 | d->themeSeed = themeHash_(paletteSeed); |
1500 | d->siteIcon = siteIcons[(d->themeSeed >> 7) % iElemCount(siteIcons)]; | ||
1501 | } | 1518 | } |
1502 | else { | 1519 | else { |
1503 | d->themeSeed = 0; | 1520 | d->themeSeed = 0; |
1504 | d->siteIcon = 0; | ||
1505 | } | 1521 | } |
1506 | /* Set up colors. */ | 1522 | /* Set up colors. */ |
1507 | if (d->themeSeed) { | 1523 | if (d->themeSeed) { |
@@ -1552,7 +1568,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1552 | 1568 | ||
1553 | if (theme == colorfulDark_GmDocumentTheme) { | 1569 | if (theme == colorfulDark_GmDocumentTheme) { |
1554 | iHSLColor base = { hues[primIndex], | 1570 | iHSLColor base = { hues[primIndex], |
1555 | 0.8f * (d->themeSeed >> 24) / 255.0f, | 1571 | 0.8f * (d->themeSeed >> 24) / 255.0f + minSat_HSLColor, |
1556 | 0.06f + 0.09f * ((d->themeSeed >> 5) & 0x7) / 7.0f, | 1572 | 0.06f + 0.09f * ((d->themeSeed >> 5) & 0x7) / 7.0f, |
1557 | 1.0f }; | 1573 | 1.0f }; |
1558 | iHSLColor altBase = { altHue, base.sat, base.lum, 1 }; | 1574 | iHSLColor altBase = { altHue, base.sat, base.lum, 1 }; |
@@ -1562,13 +1578,13 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1562 | setHsl_Color(tmBannerBackground_ColorId, addSatLum_HSLColor(base, 0.1f, 0.04f * (isBannerLighter ? 1 : -1))); | 1578 | setHsl_Color(tmBannerBackground_ColorId, addSatLum_HSLColor(base, 0.1f, 0.04f * (isBannerLighter ? 1 : -1))); |
1563 | setHsl_Color(tmBannerTitle_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.1f, 0), 0.55f)); | 1579 | setHsl_Color(tmBannerTitle_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.1f, 0), 0.55f)); |
1564 | setHsl_Color(tmBannerIcon_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.35f, 0), 0.65f)); | 1580 | setHsl_Color(tmBannerIcon_ColorId, setLum_HSLColor(addSatLum_HSLColor(base, 0.35f, 0), 0.65f)); |
1565 | 1581 | ||
1566 | // printf("primHue: %zu alts: %d %d isDarkBgSat: %d\n", | 1582 | // printf("primHue: %zu alts: %d %d isDarkBgSat: %d\n", |
1567 | // primIndex, | 1583 | // primIndex, |
1568 | // altHues[primIndex].index[altIndex[0]], | 1584 | // altHues[primIndex].index[altIndex[0]], |
1569 | // altHues[primIndex].index[altIndex[1]], | 1585 | // altHues[primIndex].index[altIndex[1]], |
1570 | // isDarkBgSat); | 1586 | // isDarkBgSat); |
1571 | 1587 | ||
1572 | const float titleLum = 0.2f * ((d->themeSeed >> 17) & 0x7) / 7.0f; | 1588 | const float titleLum = 0.2f * ((d->themeSeed >> 17) & 0x7) / 7.0f; |
1573 | setHsl_Color(tmHeading1_ColorId, setLum_HSLColor(altBase, titleLum + 0.80f)); | 1589 | setHsl_Color(tmHeading1_ColorId, setLum_HSLColor(altBase, titleLum + 0.80f)); |
1574 | setHsl_Color(tmHeading2_ColorId, setLum_HSLColor(altBase, titleLum + 0.70f)); | 1590 | setHsl_Color(tmHeading2_ColorId, setLum_HSLColor(altBase, titleLum + 0.70f)); |
@@ -1730,8 +1746,8 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1730 | /* Derived colors. */ | 1746 | /* Derived colors. */ |
1731 | setDerivedThemeColors_(theme); | 1747 | setDerivedThemeColors_(theme); |
1732 | /* Special exceptions. */ | 1748 | /* Special exceptions. */ |
1733 | if (seed) { | 1749 | if (iconSeed) { |
1734 | if (equal_CStr(cstr_Block(seed), "gemini.circumlunar.space")) { | 1750 | if (equal_CStr(cstr_Block(iconSeed), "gemini.circumlunar.space")) { |
1735 | d->siteIcon = 0x264a; /* gemini symbol */ | 1751 | d->siteIcon = 0x264a; /* gemini symbol */ |
1736 | } | 1752 | } |
1737 | updateIconBasedOnUrl_GmDocument_(d); | 1753 | updateIconBasedOnUrl_GmDocument_(d); |
@@ -1752,7 +1768,8 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1752 | void makePaletteGlobal_GmDocument(const iGmDocument *d) { | 1768 | void makePaletteGlobal_GmDocument(const iGmDocument *d) { |
1753 | if (!d->isPaletteValid) { | 1769 | if (!d->isPaletteValid) { |
1754 | /* Recompute the palette since it's needed now. */ | 1770 | /* Recompute the palette since it's needed now. */ |
1755 | setThemeSeed_GmDocument((iGmDocument *) d, urlThemeSeed_String(&d->url)); | 1771 | setThemeSeed_GmDocument( |
1772 | (iGmDocument *) d, urlPaletteSeed_String(&d->url), urlThemeSeed_String(&d->url)); | ||
1756 | } | 1773 | } |
1757 | iAssert(d->isPaletteValid); | 1774 | iAssert(d->isPaletteValid); |
1758 | memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); | 1775 | memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); |
@@ -1929,7 +1946,7 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1929 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { | 1946 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { |
1930 | url = canonicalUrl_String(url); | 1947 | url = canonicalUrl_String(url); |
1931 | set_String(&d->url, url); | 1948 | set_String(&d->url, url); |
1932 | setThemeSeed_GmDocument(d, urlThemeSeed_String(url)); | 1949 | setThemeSeed_GmDocument(d, urlPaletteSeed_String(url), urlThemeSeed_String(url)); |
1933 | iUrl parts; | 1950 | iUrl parts; |
1934 | init_Url(&parts, url); | 1951 | init_Url(&parts, url); |
1935 | setRange_String(&d->localHost, parts.host); | 1952 | setRange_String(&d->localHost, parts.host); |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 0969794c..6c25dd6f 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -181,7 +181,9 @@ enum iGmDocumentUpdate { | |||
181 | final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ | 181 | final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ |
182 | }; | 182 | }; |
183 | 183 | ||
184 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | 184 | void setThemeSeed_GmDocument (iGmDocument *, |
185 | const iBlock *paletteSeed, | ||
186 | const iBlock *iconSeed); /* seeds may be NULL; NULL iconSeed will use paletteSeed instead */ | ||
185 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); | 187 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); |
186 | void setWidth_GmDocument (iGmDocument *, int width, int canvasWidth); | 188 | void setWidth_GmDocument (iGmDocument *, int width, int canvasWidth); |
187 | iBool updateWidth_GmDocument (iGmDocument *, int width, int canvasWidth); | 189 | iBool updateWidth_GmDocument (iGmDocument *, int width, int canvasWidth); |
diff --git a/src/gmutil.c b/src/gmutil.c index ecfe2128..b32722ac 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -22,6 +22,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "gmutil.h" | 23 | #include "gmutil.h" |
24 | #include "fontpack.h" | 24 | #include "fontpack.h" |
25 | #include "lang.h" | ||
26 | #include "sitespec.h" | ||
27 | #include "ui/color.h" | ||
25 | 28 | ||
26 | #include <the_Foundation/file.h> | 29 | #include <the_Foundation/file.h> |
27 | #include <the_Foundation/fileinfo.h> | 30 | #include <the_Foundation/fileinfo.h> |
@@ -277,6 +280,19 @@ const iBlock *urlThemeSeed_String(const iString *url) { | |||
277 | return collect_Block(newRange_Block(user)); | 280 | return collect_Block(newRange_Block(user)); |
278 | } | 281 | } |
279 | 282 | ||
283 | const iBlock *urlPaletteSeed_String(const iString *url) { | ||
284 | if (equalCase_Rangecc(urlScheme_String(url), "file")) { | ||
285 | return urlThemeSeed_String(url); | ||
286 | } | ||
287 | /* Check for a site-specific setting. */ | ||
288 | const iString *seed = | ||
289 | valueString_SiteSpec(collectNewRange_String(urlRoot_String(url)), paletteSeed_SiteSpecKey); | ||
290 | if (!isEmpty_String(seed)) { | ||
291 | return utf8_String(seed); | ||
292 | } | ||
293 | return urlThemeSeed_String(url); | ||
294 | } | ||
295 | |||
280 | static iBool isAbsolutePath_(iRangecc path) { | 296 | static iBool isAbsolutePath_(iRangecc path) { |
281 | return isAbsolute_Path(collect_String(urlDecode_String(collect_String(newRange_String(path))))); | 297 | return isAbsolute_Path(collect_String(urlDecode_String(collect_String(newRange_String(path))))); |
282 | } | 298 | } |
@@ -735,13 +751,36 @@ const iString *canonicalUrl_String(const iString *d) { | |||
735 | return canon ? collect_String(canon) : d; | 751 | return canon ? collect_String(canon) : d; |
736 | } | 752 | } |
737 | 753 | ||
754 | const iString *prettyDataUrl_String(const iString *d, int contentColor) { | ||
755 | iUrl url; | ||
756 | init_Url(&url, d); | ||
757 | if (!equalCase_Rangecc(url.scheme, "data")) { | ||
758 | return d; | ||
759 | } | ||
760 | iString *pretty = new_String(); | ||
761 | const char *comma = strchr(url.path.start, ','); | ||
762 | if (!comma) { | ||
763 | comma = iMin(constEnd_String(d), constBegin_String(d) + 256); | ||
764 | } | ||
765 | appendRange_String(pretty, (iRangecc){ constBegin_String(d), comma }); | ||
766 | if (size_Range(&url.path)) { | ||
767 | if (contentColor != none_ColorId) { | ||
768 | appendCStr_String(pretty, escape_Color(contentColor)); | ||
769 | } | ||
770 | appendCStr_String(pretty, " ("); | ||
771 | appendCStr_String(pretty, formatCStrs_Lang("num.bytes.n", size_Range(&url.path))); | ||
772 | appendCStr_String(pretty, ")"); | ||
773 | } | ||
774 | return collect_String(pretty); | ||
775 | } | ||
776 | |||
738 | iRangecc mediaTypeWithoutParameters_Rangecc(iRangecc mime) { | 777 | iRangecc mediaTypeWithoutParameters_Rangecc(iRangecc mime) { |
739 | iRangecc part = iNullRange; | 778 | iRangecc part = iNullRange; |
740 | nextSplit_Rangecc(mime, ";", &part); | 779 | nextSplit_Rangecc(mime, ";", &part); |
741 | return part; | 780 | return part; |
742 | } | 781 | } |
743 | 782 | ||
744 | const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { | 783 | const iString *feedEntryOpenCommand_String(const iString *url, int newTab, int newWindow) { |
745 | if (!isEmpty_String(url)) { | 784 | if (!isEmpty_String(url)) { |
746 | iString *cmd = collectNew_String(); | 785 | iString *cmd = collectNew_String(); |
747 | const size_t fragPos = indexOf_String(url, '#'); | 786 | const size_t fragPos = indexOf_String(url, '#'); |
@@ -749,15 +788,20 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { | |||
749 | iString *head = newRange_String( | 788 | iString *head = newRange_String( |
750 | (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); | 789 | (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); |
751 | format_String(cmd, | 790 | format_String(cmd, |
752 | "open fromsidebar:1 newtab:%d gotourlheading:%s url:%s", | 791 | "open fromsidebar:1 newtab:%d newwindow:%d gotourlheading:%s url:%s", |
753 | newTab, | 792 | newTab, |
793 | newWindow, | ||
754 | cstr_String(head), | 794 | cstr_String(head), |
755 | cstr_Rangecc((iRangecc){ constBegin_String(url), | 795 | cstr_Rangecc((iRangecc){ constBegin_String(url), |
756 | constBegin_String(url) + fragPos })); | 796 | constBegin_String(url) + fragPos })); |
757 | delete_String(head); | 797 | delete_String(head); |
758 | } | 798 | } |
759 | else { | 799 | else { |
760 | format_String(cmd, "open fromsidebar:1 newtab:%d url:%s", newTab, cstr_String(url)); | 800 | format_String(cmd, |
801 | "open fromsidebar:1 newtab:%d newwindow:%d url:%s", | ||
802 | newTab, | ||
803 | newWindow, | ||
804 | cstr_String(url)); | ||
761 | } | 805 | } |
762 | return cmd; | 806 | return cmd; |
763 | } | 807 | } |
@@ -901,40 +945,3 @@ const iGmError *get_GmError(enum iGmStatusCode code) { | |||
901 | return &errors_[0].err; /* unknown */ | 945 | return &errors_[0].err; /* unknown */ |
902 | } | 946 | } |
903 | 947 | ||
904 | int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement, | ||
905 | void (*matchHandler)(void *, const iRegExpMatch *), | ||
906 | void *context) { | ||
907 | iRegExpMatch m; | ||
908 | iString result; | ||
909 | int numMatches = 0; | ||
910 | const char *pos = constBegin_String(d); | ||
911 | init_RegExpMatch(&m); | ||
912 | init_String(&result); | ||
913 | while (matchString_RegExp(regexp, d, &m)) { | ||
914 | appendRange_String(&result, (iRangecc){ pos, begin_RegExpMatch(&m) }); | ||
915 | /* Replace any capture group back-references. */ | ||
916 | for (const char *ch = replacement; *ch; ch++) { | ||
917 | if (*ch == '\\') { | ||
918 | ch++; | ||
919 | if (*ch == '\\') { | ||
920 | appendCStr_String(&result, "\\"); | ||
921 | } | ||
922 | else if (*ch >= '0' && *ch <= '9') { | ||
923 | appendRange_String(&result, capturedRange_RegExpMatch(&m, *ch - '0')); | ||
924 | } | ||
925 | } | ||
926 | else { | ||
927 | appendData_Block(&result.chars, ch, 1); | ||
928 | } | ||
929 | } | ||
930 | if (matchHandler) { | ||
931 | matchHandler(context, &m); | ||
932 | } | ||
933 | pos = end_RegExpMatch(&m); | ||
934 | numMatches++; | ||
935 | } | ||
936 | appendRange_String(&result, (iRangecc){ pos, constEnd_String(d) }); | ||
937 | set_String(d, &result); | ||
938 | deinit_String(&result); | ||
939 | return numMatches; | ||
940 | } | ||
diff --git a/src/gmutil.h b/src/gmutil.h index 1594afc4..e4284cfd 100644 --- a/src/gmutil.h +++ b/src/gmutil.h | |||
@@ -120,6 +120,7 @@ uint16_t urlPort_String (const iString *); | |||
120 | iRangecc urlUser_String (const iString *); | 120 | iRangecc urlUser_String (const iString *); |
121 | iRangecc urlRoot_String (const iString *); | 121 | iRangecc urlRoot_String (const iString *); |
122 | const iBlock * urlThemeSeed_String (const iString *); | 122 | const iBlock * urlThemeSeed_String (const iString *); |
123 | const iBlock * urlPaletteSeed_String (const iString *); | ||
123 | 124 | ||
124 | const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); | 125 | const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); |
125 | iBool isLikelyUrl_String (const iString *); | 126 | iBool isLikelyUrl_String (const iString *); |
@@ -141,6 +142,7 @@ void urlEncodeSpaces_String (iString *); | |||
141 | const iString * withSpacesEncoded_String(const iString *); | 142 | const iString * withSpacesEncoded_String(const iString *); |
142 | const iString * withScheme_String (const iString *, const char *scheme); /* replace URI scheme */ | 143 | const iString * withScheme_String (const iString *, const char *scheme); /* replace URI scheme */ |
143 | const iString * canonicalUrl_String (const iString *); | 144 | const iString * canonicalUrl_String (const iString *); |
145 | const iString * prettyDataUrl_String (const iString *, int contentColor); | ||
144 | 146 | ||
145 | const char * mediaType_Path (const iString *path); | 147 | const char * mediaType_Path (const iString *path); |
146 | const char * mediaTypeFromFileExtension_String (const iString *); | 148 | const char * mediaTypeFromFileExtension_String (const iString *); |
@@ -149,9 +151,4 @@ iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime); | |||
149 | const iString * findContainerArchive_Path (const iString *path); | 151 | const iString * findContainerArchive_Path (const iString *path); |
150 | 152 | ||
151 | 153 | ||
152 | const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */ | 154 | const iString * feedEntryOpenCommand_String (const iString *url, int newTab, int newWindow); /* checks fragment */ |
153 | |||
154 | /* TODO: Consider adding this to the_Foundation. */ | ||
155 | int replaceRegExp_String (iString *, const iRegExp *regexp, const char *replacement, | ||
156 | void (*matchHandler)(void *, const iRegExpMatch *), | ||
157 | void *context); | ||
diff --git a/src/macos.m b/src/macos.m index 7b248c3b..5f376874 100644 --- a/src/macos.m +++ b/src/macos.m | |||
@@ -83,7 +83,9 @@ static void ignoreImmediateKeyDownEvents_(void) { | |||
83 | However, we shouldn't double-activate menu items when a shortcut key is used in our | 83 | However, we shouldn't double-activate menu items when a shortcut key is used in our |
84 | widgets. Quite a kludge: take advantage of Window's focus-acquisition threshold to | 84 | widgets. Quite a kludge: take advantage of Window's focus-acquisition threshold to |
85 | ignore the immediately following key down events. */ | 85 | ignore the immediately following key down events. */ |
86 | get_Window()->focusGainedAt = SDL_GetTicks(); | 86 | iForEach(PtrArray, w, collect_PtrArray(listWindows_App())) { |
87 | as_Window(w.ptr)->focusGainedAt = SDL_GetTicks(); | ||
88 | } | ||
87 | } | 89 | } |
88 | 90 | ||
89 | /*----------------------------------------------------------------------------------------------*/ | 91 | /*----------------------------------------------------------------------------------------------*/ |
@@ -435,9 +437,17 @@ static iBool processScrollWheelEvent_(NSEvent *event) { | |||
435 | const iBool isPerPixel = (event.hasPreciseScrollingDeltas != 0); | 437 | const iBool isPerPixel = (event.hasPreciseScrollingDeltas != 0); |
436 | const iBool isInertia = (event.momentumPhase & (NSEventPhaseBegan | NSEventPhaseChanged)) != 0; | 438 | const iBool isInertia = (event.momentumPhase & (NSEventPhaseBegan | NSEventPhaseChanged)) != 0; |
437 | const iBool isEnded = event.scrollingDeltaX == 0.0f && event.scrollingDeltaY == 0.0f && !isInertia; | 439 | const iBool isEnded = event.scrollingDeltaX == 0.0f && event.scrollingDeltaY == 0.0f && !isInertia; |
438 | const iWindow *win = &get_MainWindow()->base; | 440 | const iWindow *win = NULL; //&get_MainWindow()->base; |
439 | if (event.window != nsWindow_(win->win)) { | 441 | /* If this event belongs to one of the MainWindows, handle it and mark it for that window. |
440 | /* Not the main window. */ | 442 | If it's for an auxiliary window, let the system handle it. */ |
443 | iConstForEach(PtrArray, i, mainWindows_App()) { | ||
444 | if (event.window == nsWindow_(as_Window(i.ptr)->win)) { | ||
445 | win = i.ptr; | ||
446 | break; | ||
447 | } | ||
448 | } | ||
449 | if (!win) { //event.window != nsWindow_(win->win)) { | ||
450 | /* Not a main window. */ | ||
441 | return iFalse; | 451 | return iFalse; |
442 | } | 452 | } |
443 | if (isPerPixel) { | 453 | if (isPerPixel) { |
@@ -478,16 +488,18 @@ static iBool processScrollWheelEvent_(NSEvent *event) { | |||
478 | else { | 488 | else { |
479 | SDL_MouseWheelEvent e = { .type = SDL_MOUSEWHEEL }; | 489 | SDL_MouseWheelEvent e = { .type = SDL_MOUSEWHEEL }; |
480 | e.timestamp = SDL_GetTicks(); | 490 | e.timestamp = SDL_GetTicks(); |
491 | e.windowID = id_Window(win); | ||
481 | e.which = 1; /* Distinction between trackpad and regular mouse. */ | 492 | e.which = 1; /* Distinction between trackpad and regular mouse. */ |
482 | /* Disregard any wheel acceleration. */ | 493 | /* Disregard any wheel acceleration. */ |
483 | e.x = event.scrollingDeltaX > 0 ? 1 : event.scrollingDeltaX < 0 ? -1 : 0; | 494 | e.x = event.scrollingDeltaX > 0 ? 1 : event.scrollingDeltaX < 0 ? -1 : 0; |
484 | e.y = event.scrollingDeltaY > 0 ? 1 : event.scrollingDeltaY < 0 ? -1 : 0; | 495 | e.y = event.scrollingDeltaY > 0 ? 1 : event.scrollingDeltaY < 0 ? -1 : 0; |
485 | SDL_PushEvent((SDL_Event *) &e); | 496 | SDL_PushEvent((SDL_Event *) &e); |
486 | return iTrue; | 497 | return iTrue; |
487 | } | 498 | } |
488 | /* Post corresponding MOUSEWHEEL events. */ | 499 | /* Post corresponding MOUSEWHEEL events. */ |
489 | SDL_MouseWheelEvent e = { .type = SDL_MOUSEWHEEL }; | 500 | SDL_MouseWheelEvent e = { .type = SDL_MOUSEWHEEL }; |
490 | e.timestamp = SDL_GetTicks(); | 501 | e.timestamp = SDL_GetTicks(); |
502 | e.windowID = id_Window(win); | ||
491 | e.which = isPerPixel ? 0 : 1; /* Distinction between trackpad and regular mouse. */ | 503 | e.which = isPerPixel ? 0 : 1; /* Distinction between trackpad and regular mouse. */ |
492 | setPerPixel_MouseWheelEvent(&e, isPerPixel); | 504 | setPerPixel_MouseWheelEvent(&e, isPerPixel); |
493 | if (isPerPixel) { | 505 | if (isPerPixel) { |
@@ -517,28 +529,6 @@ static iBool processScrollWheelEvent_(NSEvent *event) { | |||
517 | // printf("#### [%d] dx:%d dy:%d phase:%ld inertia:%d end:%d\n", preventTapGlitch_, e.x, e.y, (long) event.momentumPhase, | 529 | // printf("#### [%d] dx:%d dy:%d phase:%ld inertia:%d end:%d\n", preventTapGlitch_, e.x, e.y, (long) event.momentumPhase, |
518 | // isInertia, isEnded); fflush(stdout); | 530 | // isInertia, isEnded); fflush(stdout); |
519 | SDL_PushEvent((SDL_Event *) &e); | 531 | SDL_PushEvent((SDL_Event *) &e); |
520 | #if 0 | ||
521 | /* On macOS, we handle both trackpad and mouse events. We expect SDL to identify | ||
522 | which device is sending the event. */ | ||
523 | if (ev.wheel.which == 0) { | ||
524 | /* Trackpad with precise scrolling w/inertia (points). */ | ||
525 | setPerPixel_MouseWheelEvent(&ev.wheel, iTrue); | ||
526 | ev.wheel.x *= -d->window->base.pixelRatio; | ||
527 | ev.wheel.y *= d->window->base.pixelRatio; | ||
528 | /* Only scroll on one axis at a time. */ | ||
529 | if (iAbs(ev.wheel.x) > iAbs(ev.wheel.y)) { | ||
530 | ev.wheel.y = 0; | ||
531 | } | ||
532 | else { | ||
533 | ev.wheel.x = 0; | ||
534 | } | ||
535 | } | ||
536 | else { | ||
537 | /* Disregard wheel acceleration applied by the OS. */ | ||
538 | ev.wheel.x = -ev.wheel.x; | ||
539 | ev.wheel.y = iSign(ev.wheel.y); | ||
540 | } | ||
541 | #endif | ||
542 | return iTrue; | 532 | return iTrue; |
543 | } | 533 | } |
544 | 534 | ||
@@ -565,13 +555,6 @@ void setupApplication_MacOS(void) { | |||
565 | windowCloseItem.action = @selector(closeTab); | 555 | windowCloseItem.action = @selector(closeTab); |
566 | [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel | 556 | [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel |
567 | handler:^NSEvent*(NSEvent *event){ | 557 | handler:^NSEvent*(NSEvent *event){ |
568 | // printf("event type: %lu\n", (unsigned long) event.type); | ||
569 | // fflush(stdout); | ||
570 | // if (event.type == NSEventTypeGesture) { | ||
571 | // trackSwipe_(event); | ||
572 | // printf("GESTURE phase:%lu\n", (unsigned long) event.phase); | ||
573 | //fflush(stdout); | ||
574 | // } | ||
575 | if (event.type == NSEventTypeScrollWheel && | 558 | if (event.type == NSEventTypeScrollWheel && |
576 | processScrollWheelEvent_(event)) { | 559 | processScrollWheelEvent_(event)) { |
577 | return nil; /* was eaten */ | 560 | return nil; /* was eaten */ |
diff --git a/src/periodic.c b/src/periodic.c index b4f51ed3..0558ed50 100644 --- a/src/periodic.c +++ b/src/periodic.c | |||
@@ -107,14 +107,17 @@ iBool dispatchCommands_Periodic(iPeriodic *d) { | |||
107 | iConstForEach(Array, i, &d->commands.values) { | 107 | iConstForEach(Array, i, &d->commands.values) { |
108 | const iPeriodicCommand *pc = i.value; | 108 | const iPeriodicCommand *pc = i.value; |
109 | iAssert(isInstance_Object(pc->context, &Class_Widget)); | 109 | iAssert(isInstance_Object(pc->context, &Class_Widget)); |
110 | const SDL_UserEvent ev = { | 110 | iRoot *root = constAs_Widget(pc->context)->root; |
111 | .type = SDL_USEREVENT, | 111 | if (root) { |
112 | .code = command_UserEventCode, | 112 | const SDL_UserEvent ev = { |
113 | .data1 = (void *) cstr_String(&pc->command), | 113 | .type = SDL_USEREVENT, |
114 | .data2 = findRoot_Window(get_Window(), pc->context) | 114 | .code = command_UserEventCode, |
115 | }; | 115 | .data1 = (void *) cstr_String(&pc->command), |
116 | if (ev.data2) { | 116 | .data2 = root, |
117 | setCurrent_Root(ev.data2); | 117 | .windowID = id_Window(root->window), |
118 | }; | ||
119 | setCurrent_Window(root->window); | ||
120 | setCurrent_Root(root); | ||
118 | dispatchEvent_Widget(pc->context, (const SDL_Event *) &ev); | 121 | dispatchEvent_Widget(pc->context, (const SDL_Event *) &ev); |
119 | wasPosted = iTrue; | 122 | wasPosted = iTrue; |
120 | } | 123 | } |
diff --git a/src/prefs.c b/src/prefs.c index 13a1dab7..08355a6a 100644 --- a/src/prefs.c +++ b/src/prefs.c | |||
@@ -66,13 +66,15 @@ void init_Prefs(iPrefs *d) { | |||
66 | d->smoothScrollSpeed[keyboard_ScrollType] = 13; | 66 | d->smoothScrollSpeed[keyboard_ScrollType] = 13; |
67 | d->smoothScrollSpeed[mouse_ScrollType] = 13; | 67 | d->smoothScrollSpeed[mouse_ScrollType] = 13; |
68 | d->loadImageInsteadOfScrolling = iFalse; | 68 | d->loadImageInsteadOfScrolling = iFalse; |
69 | d->collapsePreOnLoad = iFalse; | 69 | d->openDataUrlImagesOnLoad = iFalse; |
70 | d->openArchiveIndexPages = iTrue; | 70 | d->collapsePreOnLoad = iFalse; |
71 | d->addBookmarksToBottom = iTrue; | 71 | d->openArchiveIndexPages = iTrue; |
72 | d->warnAboutMissingGlyphs = iTrue; | 72 | d->addBookmarksToBottom = iTrue; |
73 | d->decodeUserVisibleURLs = iTrue; | 73 | d->warnAboutMissingGlyphs = iTrue; |
74 | d->decodeUserVisibleURLs = iTrue; | ||
74 | d->maxCacheSize = 10; | 75 | d->maxCacheSize = 10; |
75 | d->maxMemorySize = 200; | 76 | d->maxMemorySize = 200; |
77 | d->maxUrlSize = 8192; | ||
76 | setCStr_String(&d->strings[uiFont_PrefsString], "default"); | 78 | setCStr_String(&d->strings[uiFont_PrefsString], "default"); |
77 | setCStr_String(&d->strings[headingFont_PrefsString], "default"); | 79 | setCStr_String(&d->strings[headingFont_PrefsString], "default"); |
78 | setCStr_String(&d->strings[bodyFont_PrefsString], "default"); | 80 | setCStr_String(&d->strings[bodyFont_PrefsString], "default"); |
diff --git a/src/prefs.h b/src/prefs.h index ea864f51..d988399e 100644 --- a/src/prefs.h +++ b/src/prefs.h | |||
@@ -75,6 +75,7 @@ enum iPrefsBool { | |||
75 | hoverLink_PrefsBool, | 75 | hoverLink_PrefsBool, |
76 | smoothScrolling_PrefsBool, | 76 | smoothScrolling_PrefsBool, |
77 | loadImageInsteadOfScrolling_PrefsBool, | 77 | loadImageInsteadOfScrolling_PrefsBool, |
78 | openDataUrlImagesOnLoad_PrefsBool, | ||
78 | collapsePreOnLoad_PrefsBool, | 79 | collapsePreOnLoad_PrefsBool, |
79 | openArchiveIndexPages_PrefsBool, | 80 | openArchiveIndexPages_PrefsBool, |
80 | 81 | ||
@@ -128,6 +129,7 @@ struct Impl_Prefs { | |||
128 | iBool hoverLink; | 129 | iBool hoverLink; |
129 | iBool smoothScrolling; | 130 | iBool smoothScrolling; |
130 | iBool loadImageInsteadOfScrolling; | 131 | iBool loadImageInsteadOfScrolling; |
132 | iBool openDataUrlImagesOnLoad; | ||
131 | iBool collapsePreOnLoad; | 133 | iBool collapsePreOnLoad; |
132 | iBool openArchiveIndexPages; | 134 | iBool openArchiveIndexPages; |
133 | 135 | ||
@@ -172,6 +174,7 @@ struct Impl_Prefs { | |||
172 | /* Network */ | 174 | /* Network */ |
173 | int maxCacheSize; /* MB */ | 175 | int maxCacheSize; /* MB */ |
174 | int maxMemorySize; /* MB */ | 176 | int maxMemorySize; /* MB */ |
177 | int maxUrlSize; /* bytes; longer ones will be disregarded */ | ||
175 | /* Style */ | 178 | /* Style */ |
176 | iStringSet * disabledFontPacks; | 179 | iStringSet * disabledFontPacks; |
177 | int gemtextAnsiEscapes; | 180 | int gemtextAnsiEscapes; |
@@ -190,3 +193,7 @@ iLocalDef float scrollSpeedFactor_Prefs(const iPrefs *d, enum iScrollType type) | |||
190 | iAssert(type >= 0 && type < max_ScrollType); | 193 | iAssert(type >= 0 && type < max_ScrollType); |
191 | return 10.0f / iMax(1, d->smoothScrollSpeed[type]) * (type == mouse_ScrollType ? 0.5f : 1.0f); | 194 | return 10.0f / iMax(1, d->smoothScrollSpeed[type]) * (type == mouse_ScrollType ? 0.5f : 1.0f); |
192 | } | 195 | } |
196 | |||
197 | iLocalDef enum iGmDocumentTheme docTheme_Prefs(const iPrefs *d) { | ||
198 | return isDark_ColorTheme(d->theme) ? d->docThemeDark : d->docThemeLight; | ||
199 | } | ||
diff --git a/src/sitespec.c b/src/sitespec.c index fe80ad13..5133abe5 100644 --- a/src/sitespec.c +++ b/src/sitespec.c | |||
@@ -37,7 +37,8 @@ struct Impl_SiteParams { | |||
37 | iString titanIdentity; /* fingerprint */ | 37 | iString titanIdentity; /* fingerprint */ |
38 | int dismissWarnings; | 38 | int dismissWarnings; |
39 | iStringArray usedIdentities; /* fingerprints; latest ones at the end */ | 39 | iStringArray usedIdentities; /* fingerprints; latest ones at the end */ |
40 | /* TODO: theme seed, style settings */ | 40 | iString paletteSeed; |
41 | /* TODO: style settings */ | ||
41 | }; | 42 | }; |
42 | 43 | ||
43 | void init_SiteParams(iSiteParams *d) { | 44 | void init_SiteParams(iSiteParams *d) { |
@@ -45,9 +46,11 @@ void init_SiteParams(iSiteParams *d) { | |||
45 | init_String(&d->titanIdentity); | 46 | init_String(&d->titanIdentity); |
46 | d->dismissWarnings = 0; | 47 | d->dismissWarnings = 0; |
47 | init_StringArray(&d->usedIdentities); | 48 | init_StringArray(&d->usedIdentities); |
49 | init_String(&d->paletteSeed); | ||
48 | } | 50 | } |
49 | 51 | ||
50 | void deinit_SiteParams(iSiteParams *d) { | 52 | void deinit_SiteParams(iSiteParams *d) { |
53 | deinit_String(&d->paletteSeed); | ||
51 | deinit_StringArray(&d->usedIdentities); | 54 | deinit_StringArray(&d->usedIdentities); |
52 | deinit_String(&d->titanIdentity); | 55 | deinit_String(&d->titanIdentity); |
53 | } | 56 | } |
@@ -149,6 +152,9 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con | |||
149 | pushBack_StringArray(&d->loadParams->usedIdentities, collectNewRange_String(seg)); | 152 | pushBack_StringArray(&d->loadParams->usedIdentities, collectNewRange_String(seg)); |
150 | } | 153 | } |
151 | } | 154 | } |
155 | else if (!cmp_String(key, "paletteSeed") && value->type == string_TomlType) { | ||
156 | set_String(&d->loadParams->paletteSeed, value->value.string); | ||
157 | } | ||
152 | } | 158 | } |
153 | 159 | ||
154 | static iBool load_SiteSpec_(iSiteSpec *d) { | 160 | static iBool load_SiteSpec_(iSiteSpec *d) { |
@@ -173,7 +179,7 @@ static void save_SiteSpec_(iSiteSpec *d) { | |||
173 | iBeginCollect(); | 179 | iBeginCollect(); |
174 | const iBlock * key = &i.value->keyBlock; | 180 | const iBlock * key = &i.value->keyBlock; |
175 | const iSiteParams *params = i.value->object; | 181 | const iSiteParams *params = i.value->object; |
176 | format_String(buf, "[%s]\n", cstr_Block(key)); | 182 | clear_String(buf); |
177 | if (params->titanPort) { | 183 | if (params->titanPort) { |
178 | appendFormat_String(buf, "titanPort = %u\n", params->titanPort); | 184 | appendFormat_String(buf, "titanPort = %u\n", params->titanPort); |
179 | } | 185 | } |
@@ -190,8 +196,18 @@ static void save_SiteSpec_(iSiteSpec *d) { | |||
190 | "usedIdentities = \"%s\"\n", | 196 | "usedIdentities = \"%s\"\n", |
191 | cstrCollect_String(joinCStr_StringArray(¶ms->usedIdentities, " "))); | 197 | cstrCollect_String(joinCStr_StringArray(¶ms->usedIdentities, " "))); |
192 | } | 198 | } |
193 | appendCStr_String(buf, "\n"); | 199 | if (!isEmpty_String(¶ms->paletteSeed)) { |
194 | write_File(f, utf8_String(buf)); | 200 | appendCStr_String(buf, "paletteSeed = \""); |
201 | append_String(buf, collect_String(quote_String(¶ms->paletteSeed, iFalse))); | ||
202 | appendCStr_String(buf, "\"\n"); | ||
203 | } | ||
204 | if (!isEmpty_String(buf)) { | ||
205 | writeData_File(f, "[", 1); | ||
206 | writeData_File(f, constData_Block(key), size_Block(key)); | ||
207 | writeData_File(f, "]\n", 2); | ||
208 | appendCStr_String(buf, "\n"); | ||
209 | write_File(f, utf8_String(buf)); | ||
210 | } | ||
195 | iEndCollect(); | 211 | iEndCollect(); |
196 | } | 212 | } |
197 | delete_String(buf); | 213 | delete_String(buf); |
@@ -257,6 +273,12 @@ void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const i | |||
257 | set_String(¶ms->titanIdentity, value); | 273 | set_String(¶ms->titanIdentity, value); |
258 | } | 274 | } |
259 | break; | 275 | break; |
276 | case paletteSeed_SiteSpecKey: | ||
277 | if (!equal_String(¶ms->paletteSeed, value)) { | ||
278 | needSave = iTrue; | ||
279 | set_String(¶ms->paletteSeed, value); | ||
280 | } | ||
281 | break; | ||
260 | default: | 282 | default: |
261 | break; | 283 | break; |
262 | } | 284 | } |
@@ -328,6 +350,8 @@ const iString *valueString_SiteSpec(const iString *site, enum iSiteSpecKey key) | |||
328 | switch (key) { | 350 | switch (key) { |
329 | case titanIdentity_SiteSpecKey: | 351 | case titanIdentity_SiteSpecKey: |
330 | return ¶ms->titanIdentity; | 352 | return ¶ms->titanIdentity; |
353 | case paletteSeed_SiteSpecKey: | ||
354 | return ¶ms->paletteSeed; | ||
331 | default: | 355 | default: |
332 | return collectNew_String(); | 356 | return collectNew_String(); |
333 | } | 357 | } |
diff --git a/src/sitespec.h b/src/sitespec.h index 11c40e3c..372e021e 100644 --- a/src/sitespec.h +++ b/src/sitespec.h | |||
@@ -31,6 +31,7 @@ enum iSiteSpecKey { | |||
31 | titanIdentity_SiteSpecKey, /* String */ | 31 | titanIdentity_SiteSpecKey, /* String */ |
32 | dismissWarnings_SiteSpecKey, /* int */ | 32 | dismissWarnings_SiteSpecKey, /* int */ |
33 | usedIdentities_SiteSpecKey, /* StringArray */ | 33 | usedIdentities_SiteSpecKey, /* StringArray */ |
34 | paletteSeed_SiteSpecKey, /* String */ | ||
34 | }; | 35 | }; |
35 | 36 | ||
36 | void init_SiteSpec (const char *saveDir); | 37 | void init_SiteSpec (const char *saveDir); |
diff --git a/src/ui/banner.c b/src/ui/banner.c index 11ae1574..79d70039 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c | |||
@@ -327,7 +327,8 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | |||
327 | else { | 327 | else { |
328 | switch (item->code) { | 328 | switch (item->code) { |
329 | case missingGlyphs_GmStatusCode: | 329 | case missingGlyphs_GmStatusCode: |
330 | postCommandf_App("open newtab:1 url:about:fonts"); | 330 | //postCommandf_App("open newtab:1 url:about:fonts"); |
331 | makeGlyphFinder_Widget(); | ||
331 | break; | 332 | break; |
332 | case ansiEscapes_GmStatusCode: | 333 | case ansiEscapes_GmStatusCode: |
333 | makeQuestion_Widget( | 334 | makeQuestion_Widget( |
diff --git a/src/ui/color.c b/src/ui/color.c index 824342ae..9cba322d 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -522,8 +522,8 @@ iHSLColor setLum_HSLColor(iHSLColor d, float lum) { | |||
522 | } | 522 | } |
523 | 523 | ||
524 | iHSLColor addSatLum_HSLColor(iHSLColor d, float sat, float lum) { | 524 | iHSLColor addSatLum_HSLColor(iHSLColor d, float sat, float lum) { |
525 | d.sat = iClamp(d.sat + sat, 0, 1); | 525 | d.sat = iClamp(d.sat + sat, minSat_HSLColor, 1); |
526 | d.lum = iClamp(d.lum + lum, 0, 1); | 526 | d.lum = iClamp(d.lum + lum, minSat_HSLColor, 1); |
527 | return d; | 527 | return d; |
528 | } | 528 | } |
529 | 529 | ||
diff --git a/src/ui/color.h b/src/ui/color.h index 24f9e713..f46976d7 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -231,6 +231,8 @@ struct Impl_HSLColor { | |||
231 | float hue, sat, lum, a; | 231 | float hue, sat, lum, a; |
232 | }; | 232 | }; |
233 | 233 | ||
234 | #define minSat_HSLColor 0.013f /* Conversion to 8-bit RGB may result in saturation dropping to zero. */ | ||
235 | |||
234 | iHSLColor hsl_Color (iColor); | 236 | iHSLColor hsl_Color (iColor); |
235 | iColor rgb_HSLColor (iHSLColor); | 237 | iColor rgb_HSLColor (iHSLColor); |
236 | float luma_Color (iColor); | 238 | float luma_Color (iColor); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a52e99af..fdc0dd75 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -358,6 +358,7 @@ static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); | |||
358 | static void prerender_DocumentWidget_ (iAny *); | 358 | static void prerender_DocumentWidget_ (iAny *); |
359 | static void scrollBegan_DocumentWidget_ (iAnyObject *, int, uint32_t); | 359 | static void scrollBegan_DocumentWidget_ (iAnyObject *, int, uint32_t); |
360 | static void refreshWhileScrolling_DocumentWidget_ (iAny *); | 360 | static void refreshWhileScrolling_DocumentWidget_ (iAny *); |
361 | static iBool requestMedia_DocumentWidget_ (iDocumentWidget *d, iGmLinkId linkId, iBool enableFilters); | ||
361 | 362 | ||
362 | /* TODO: The following methods are called from DocumentView, which goes the wrong way. */ | 363 | /* TODO: The following methods are called from DocumentView, which goes the wrong way. */ |
363 | 364 | ||
@@ -1824,7 +1825,7 @@ static void draw_DocumentView_(const iDocumentView *d) { | |||
1824 | } | 1825 | } |
1825 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { | 1826 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { |
1826 | updateSideIconBuf_DocumentView_(d); | 1827 | updateSideIconBuf_DocumentView_(d); |
1827 | } | 1828 | } |
1828 | const iRect docBounds = documentBounds_DocumentView_(d); | 1829 | const iRect docBounds = documentBounds_DocumentView_(d); |
1829 | const iRangei vis = visibleRange_DocumentView_(d); | 1830 | const iRangei vis = visibleRange_DocumentView_(d); |
1830 | iDrawContext ctx = { | 1831 | iDrawContext ctx = { |
@@ -2410,6 +2411,20 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
2410 | 2411 | ||
2411 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { | 2412 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { |
2412 | iWidget *w = as_Widget(d); | 2413 | iWidget *w = as_Widget(d); |
2414 | /* Embedded images in data links can be shown immediately as they are already fetched | ||
2415 | data that is part of the document. */ | ||
2416 | if (prefs_App()->openDataUrlImagesOnLoad) { | ||
2417 | iGmDocument *doc = d->view.doc; | ||
2418 | for (size_t linkId = 1; ; linkId++) { | ||
2419 | const int linkFlags = linkFlags_GmDocument(doc, linkId); | ||
2420 | const iString *linkUrl = linkUrl_GmDocument(doc, linkId); | ||
2421 | if (!linkUrl) break; | ||
2422 | if (scheme_GmLinkFlag(linkFlags) == data_GmLinkScheme && | ||
2423 | (linkFlags & imageFileExtension_GmLinkFlag)) { | ||
2424 | requestMedia_DocumentWidget_(d, linkId, 0); | ||
2425 | } | ||
2426 | } | ||
2427 | } | ||
2413 | /* Gempub page behavior and footer actions. */ { | 2428 | /* Gempub page behavior and footer actions. */ { |
2414 | /* TODO: move this to gempub.c */ | 2429 | /* TODO: move this to gempub.c */ |
2415 | delete_Gempub(d->sourceGempub); | 2430 | delete_Gempub(d->sourceGempub); |
@@ -2681,7 +2696,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
2681 | if (loadArchive_FontPack(fp, zip)) { | 2696 | if (loadArchive_FontPack(fp, zip)) { |
2682 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", | 2697 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", |
2683 | cstr_String(id_FontPack(fp).id), | 2698 | cstr_String(id_FontPack(fp).id), |
2684 | cstrCollect_String(infoText_FontPack(fp))); | 2699 | cstrCollect_String(infoText_FontPack(fp, iTrue))); |
2685 | } | 2700 | } |
2686 | appendCStr_String(&str, "\n"); | 2701 | appendCStr_String(&str, "\n"); |
2687 | appendCStr_String(&str, cstr_Lang("fontpack.help")); | 2702 | appendCStr_String(&str, cstr_Lang("fontpack.help")); |
@@ -3258,9 +3273,11 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
3258 | setTextColor_LabelWidget(menu, uiTextAction_ColorId); | 3273 | setTextColor_LabelWidget(menu, uiTextAction_ColorId); |
3259 | } | 3274 | } |
3260 | } | 3275 | } |
3261 | setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d); | 3276 | iInputWidget *input = findChild_Widget(dlg, "input"); |
3262 | setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), | 3277 | setValidator_InputWidget(input, inputQueryValidator_, d); |
3263 | statusCode == sensitiveInput_GmStatusCode); | 3278 | setBackupFileName_InputWidget(input, "inputbackup.txt"); |
3279 | setSelectAllOnFocus_InputWidget(input, iTrue); | ||
3280 | setSensitiveContent_InputWidget(input, statusCode == sensitiveInput_GmStatusCode); | ||
3264 | if (document_App() != d) { | 3281 | if (document_App() != d) { |
3265 | postCommandf_App("tabs.switch page:%p", d); | 3282 | postCommandf_App("tabs.switch page:%p", d); |
3266 | } | 3283 | } |
@@ -3921,12 +3938,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3921 | const char *unchecked = red_ColorEscape "\u2610"; | 3938 | const char *unchecked = red_ColorEscape "\u2610"; |
3922 | const char *checked = green_ColorEscape "\u2611"; | 3939 | const char *checked = green_ColorEscape "\u2611"; |
3923 | const iBool haveFingerprint = (d->certFlags & haveFingerprint_GmCertFlag) != 0; | 3940 | const iBool haveFingerprint = (d->certFlags & haveFingerprint_GmCertFlag) != 0; |
3924 | const int requiredForTrust = (available_GmCertFlag | haveFingerprint_GmCertFlag | | 3941 | const int requiredForTrust = |
3925 | timeVerified_GmCertFlag); | 3942 | (available_GmCertFlag | haveFingerprint_GmCertFlag | timeVerified_GmCertFlag); |
3926 | const iBool canTrust = ~d->certFlags & trusted_GmCertFlag && | 3943 | const iBool canTrust = ~d->certFlags & trusted_GmCertFlag && |
3927 | ((d->certFlags & requiredForTrust) == requiredForTrust); | 3944 | ((d->certFlags & requiredForTrust) == requiredForTrust); |
3928 | const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history); | 3945 | const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history); |
3929 | const iString *meta = &d->sourceMime; | 3946 | const iString *meta = &d->sourceMime; |
3930 | if (recent && recent->cachedResponse) { | 3947 | if (recent && recent->cachedResponse) { |
3931 | meta = &recent->cachedResponse->meta; | 3948 | meta = &recent->cachedResponse->meta; |
3932 | } | 3949 | } |
@@ -3991,6 +4008,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3991 | if (haveFingerprint) { | 4008 | if (haveFingerprint) { |
3992 | pushBack_Array(items, &(iMenuItem){ "${dlg.cert.fingerprint}", 0, 0, "server.copycert" }); | 4009 | pushBack_Array(items, &(iMenuItem){ "${dlg.cert.fingerprint}", 0, 0, "server.copycert" }); |
3993 | } | 4010 | } |
4011 | const iRangecc root = urlRoot_String(d->mod.url); | ||
4012 | if (!isEmpty_Range(&root)) { | ||
4013 | pushBack_Array(items, &(iMenuItem){ "${pageinfo.settings}", 0, 0, "document.sitespec" }); | ||
4014 | } | ||
3994 | if (!isEmpty_Array(items)) { | 4015 | if (!isEmpty_Array(items)) { |
3995 | pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 }); | 4016 | pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 }); |
3996 | } | 4017 | } |
@@ -4014,6 +4035,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
4014 | addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); | 4035 | addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); |
4015 | return iTrue; | 4036 | return iTrue; |
4016 | } | 4037 | } |
4038 | else if (equal_Command(cmd, "document.sitespec") && d == document_App()) { | ||
4039 | if (!findWidget_App("sitespec.palette")) { | ||
4040 | makeSiteSpecificSettings_Widget(d->mod.url); | ||
4041 | } | ||
4042 | return iTrue; | ||
4043 | } | ||
4017 | else if (equal_Command(cmd, "server.unexpire") && document_App() == d) { | 4044 | else if (equal_Command(cmd, "server.unexpire") && document_App() == d) { |
4018 | const iRangecc host = urlHost_String(d->mod.url); | 4045 | const iRangecc host = urlHost_String(d->mod.url); |
4019 | const uint16_t port = urlPort_String(d->mod.url); | 4046 | const uint16_t port = urlPort_String(d->mod.url); |
@@ -4922,7 +4949,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4922 | for (size_t i = 0; i < 64; ++i) { | 4949 | for (size_t i = 0; i < 64; ++i) { |
4923 | setByte_Block(seed, i, iRandom(0, 256)); | 4950 | setByte_Block(seed, i, iRandom(0, 256)); |
4924 | } | 4951 | } |
4925 | setThemeSeed_GmDocument(view->doc, seed); | 4952 | setThemeSeed_GmDocument(view->doc, seed, NULL); |
4926 | delete_Block(seed); | 4953 | delete_Block(seed); |
4927 | invalidate_DocumentWidget_(d); | 4954 | invalidate_DocumentWidget_(d); |
4928 | refresh_Widget(w); | 4955 | refresh_Widget(w); |
@@ -5044,10 +5071,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5044 | iArray items; | 5071 | iArray items; |
5045 | init_Array(&items, sizeof(iMenuItem)); | 5072 | init_Array(&items, sizeof(iMenuItem)); |
5046 | if (d->contextLink) { | 5073 | if (d->contextLink) { |
5047 | /* Context menu for a link. */ | 5074 | /* Construct the link context menu, depending on what kind of link was clicked. */ |
5048 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ | 5075 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ |
5049 | const iString *linkUrl = linkUrl_GmDocument(view->doc, d->contextLink->linkId); | 5076 | const iString *linkUrl = linkUrl_GmDocument(view->doc, d->contextLink->linkId); |
5050 | // const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); | ||
5051 | const iRangecc scheme = urlScheme_String(linkUrl); | 5077 | const iRangecc scheme = urlScheme_String(linkUrl); |
5052 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); | 5078 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); |
5053 | iBool isNative = iFalse; | 5079 | iBool isNative = iFalse; |
@@ -5059,41 +5085,55 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5059 | format_CStr("```%s", cstr_String(infoText)), | 5085 | format_CStr("```%s", cstr_String(infoText)), |
5060 | 0, 0, NULL }); | 5086 | 0, 0, NULL }); |
5061 | } | 5087 | } |
5062 | if (willUseProxy_App(scheme) || isGemini || | 5088 | if (isGemini || |
5089 | willUseProxy_App(scheme) || | ||
5090 | equalCase_Rangecc(scheme, "data") || | ||
5063 | equalCase_Rangecc(scheme, "file") || | 5091 | equalCase_Rangecc(scheme, "file") || |
5064 | equalCase_Rangecc(scheme, "finger") || | 5092 | equalCase_Rangecc(scheme, "finger") || |
5065 | equalCase_Rangecc(scheme, "gopher")) { | 5093 | equalCase_Rangecc(scheme, "gopher")) { |
5066 | isNative = iTrue; | 5094 | isNative = iTrue; |
5067 | /* Regular links that we can open. */ | 5095 | /* Regular links that we can open. */ |
5068 | pushBackN_Array( | 5096 | pushBackN_Array(&items, |
5069 | &items, | 5097 | (iMenuItem[]){ |
5070 | (iMenuItem[]){ { openTab_Icon " ${link.newtab}", | 5098 | { openTab_Icon " ${link.newtab}", |
5071 | 0, | 5099 | 0, |
5072 | 0, | 5100 | 0, |
5073 | format_CStr("!open newtab:1 origin:%s url:%s", | 5101 | format_CStr("!open newtab:1 origin:%s url:%s", |
5074 | cstr_String(id_Widget(w)), | 5102 | cstr_String(id_Widget(w)), |
5075 | cstr_String(linkUrl)) }, | 5103 | cstr_String(linkUrl)) }, |
5076 | { openTabBg_Icon " ${link.newtab.background}", | 5104 | { openTabBg_Icon " ${link.newtab.background}", |
5077 | 0, | 5105 | 0, |
5078 | 0, | 5106 | 0, |
5079 | format_CStr("!open newtab:2 origin:%s url:%s", | 5107 | format_CStr("!open newtab:2 origin:%s url:%s", |
5080 | cstr_String(id_Widget(w)), | 5108 | cstr_String(id_Widget(w)), |
5081 | cstr_String(linkUrl)) }, | 5109 | cstr_String(linkUrl)) }, |
5082 | { "${link.side}", | 5110 | { openWindow_Icon " ${link.newwindow}", |
5083 | 0, | 5111 | 0, |
5084 | 0, | 5112 | 0, |
5085 | format_CStr("!open newtab:4 origin:%s url:%s", | 5113 | format_CStr("!open newwindow:1 origin:%s url:%s", |
5086 | cstr_String(id_Widget(w)), | 5114 | cstr_String(id_Widget(w)), |
5087 | cstr_String(linkUrl)) }, | 5115 | cstr_String(linkUrl)) }, |
5088 | { "${link.side.newtab}", | 5116 | { "${link.side}", |
5089 | 0, | 5117 | 0, |
5090 | 0, | 5118 | 0, |
5091 | format_CStr("!open newtab:5 origin:%s url:%s", | 5119 | format_CStr("!open newtab:4 origin:%s url:%s", |
5092 | cstr_String(id_Widget(w)), | 5120 | cstr_String(id_Widget(w)), |
5093 | cstr_String(linkUrl)) } }, | 5121 | cstr_String(linkUrl)) }, |
5094 | 4); | 5122 | { "${link.side.newtab}", |
5123 | 0, | ||
5124 | 0, | ||
5125 | format_CStr("!open newtab:5 origin:%s url:%s", | ||
5126 | cstr_String(id_Widget(w)), | ||
5127 | cstr_String(linkUrl)) }, | ||
5128 | }, | ||
5129 | 5); | ||
5095 | if (deviceType_App() == phone_AppDeviceType) { | 5130 | if (deviceType_App() == phone_AppDeviceType) { |
5096 | removeN_Array(&items, size_Array(&items) - 2, iInvalidSize); | 5131 | /* Phones don't do windows or splits. */ |
5132 | removeN_Array(&items, size_Array(&items) - 3, iInvalidSize); | ||
5133 | } | ||
5134 | else if (deviceType_App() == tablet_AppDeviceType) { | ||
5135 | /* Tablets only do splits. */ | ||
5136 | removeN_Array(&items, size_Array(&items) - 3, 1); | ||
5097 | } | 5137 | } |
5098 | if (equalCase_Rangecc(scheme, "file")) { | 5138 | if (equalCase_Rangecc(scheme, "file")) { |
5099 | pushBack_Array(&items, &(iMenuItem){ "---" }); | 5139 | pushBack_Array(&items, &(iMenuItem){ "---" }); |
@@ -5248,6 +5288,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5248 | "document.upload", | 5288 | "document.upload", |
5249 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && | 5289 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && |
5250 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); | 5290 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); |
5291 | setMenuItemDisabled_Widget( | ||
5292 | d->menu, | ||
5293 | "document.upload copy:1", | ||
5294 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && | ||
5295 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); | ||
5251 | } | 5296 | } |
5252 | processContextMenuEvent_Widget(d->menu, ev, {}); | 5297 | processContextMenuEvent_Widget(d->menu, ev, {}); |
5253 | } | 5298 | } |
@@ -5545,12 +5590,12 @@ static void prerender_DocumentWidget_(iAny *context) { | |||
5545 | } | 5590 | } |
5546 | const iDocumentWidget *d = context; | 5591 | const iDocumentWidget *d = context; |
5547 | iDrawContext ctx = { | 5592 | iDrawContext ctx = { |
5548 | .view = &d->view, | 5593 | .view = &d->view, |
5549 | .docBounds = documentBounds_DocumentView_(&d->view), | 5594 | .docBounds = documentBounds_DocumentView_(&d->view), |
5550 | .vis = visibleRange_DocumentView_(&d->view), | 5595 | .vis = visibleRange_DocumentView_(&d->view), |
5551 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 | 5596 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 |
5552 | }; | 5597 | }; |
5553 | // printf("%u prerendering\n", SDL_GetTicks()); | 5598 | // printf("%u prerendering\n", SDL_GetTicks()); |
5554 | if (d->view.visBuf->buffers[0].texture) { | 5599 | if (d->view.visBuf->buffers[0].texture) { |
5555 | makePaletteGlobal_GmDocument(d->view.doc); | 5600 | makePaletteGlobal_GmDocument(d->view.doc); |
5556 | if (render_DocumentView_(&d->view, &ctx, iTrue /* just fill up progressively */)) { | 5601 | if (render_DocumentView_(&d->view, &ctx, iTrue /* just fill up progressively */)) { |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 6a8d428a..1b68ff57 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -1201,6 +1201,11 @@ void selectAll_InputWidget(iInputWidget *d) { | |||
1201 | #endif | 1201 | #endif |
1202 | } | 1202 | } |
1203 | 1203 | ||
1204 | void deselect_InputWidget(iInputWidget *d) { | ||
1205 | iZap(d->mark); | ||
1206 | refresh_Widget(as_Widget(d)); | ||
1207 | } | ||
1208 | |||
1204 | void validate_InputWidget(iInputWidget *d) { | 1209 | void validate_InputWidget(iInputWidget *d) { |
1205 | if (d->validator) { | 1210 | if (d->validator) { |
1206 | d->validator(d, d->validatorContext); /* this may change the contents */ | 1211 | d->validator(d, d->validatorContext); /* this may change the contents */ |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index 000fa4b7..832f7853 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -59,6 +59,7 @@ void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); | |||
59 | void begin_InputWidget (iInputWidget *); | 59 | void begin_InputWidget (iInputWidget *); |
60 | void end_InputWidget (iInputWidget *, iBool accept); | 60 | void end_InputWidget (iInputWidget *, iBool accept); |
61 | void selectAll_InputWidget (iInputWidget *); | 61 | void selectAll_InputWidget (iInputWidget *); |
62 | void deselect_InputWidget (iInputWidget *); | ||
62 | void validate_InputWidget (iInputWidget *); | 63 | void validate_InputWidget (iInputWidget *); |
63 | 64 | ||
64 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); | 65 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 26a286bc..88efa98b 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -243,6 +243,8 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = | |||
243 | { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, | 243 | { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, |
244 | { 120,{ "${keys.upload}", SDLK_u, KMOD_PRIMARY, "document.upload" }, 0 }, | 244 | { 120,{ "${keys.upload}", SDLK_u, KMOD_PRIMARY, "document.upload" }, 0 }, |
245 | { 121,{ "${keys.upload.edit}", SDLK_e, KMOD_PRIMARY, "document.upload copy:1" }, 0 }, | 245 | { 121,{ "${keys.upload.edit}", SDLK_e, KMOD_PRIMARY, "document.upload copy:1" }, 0 }, |
246 | { 125,{ "${keys.pageinfo}", SDLK_i, KMOD_PRIMARY, "document.info" }, 0 }, | ||
247 | { 126,{ "${keys.sitespec}", ',', KMOD_PRIMARY | KMOD_SHIFT, "document.sitespec" }, 0 }, | ||
246 | { 130,{ "${keys.input.precedingline}", SDLK_v, KMOD_PRIMARY | KMOD_SHIFT, "input.precedingline" }, 0 }, | 248 | { 130,{ "${keys.input.precedingline}", SDLK_v, KMOD_PRIMARY | KMOD_SHIFT, "input.precedingline" }, 0 }, |
247 | /* The following cannot currently be changed (built-in duplicates). */ | 249 | /* The following cannot currently be changed (built-in duplicates). */ |
248 | #if defined (iPlatformApple) | 250 | #if defined (iPlatformApple) |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 3454014a..75cbbf3a 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -231,7 +231,17 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int | |||
231 | *bg = uiBackgroundUnfocusedSelection_ColorId; | 231 | *bg = uiBackgroundUnfocusedSelection_ColorId; |
232 | } | 232 | } |
233 | else { | 233 | else { |
234 | *bg = uiBackgroundSelected_ColorId; | 234 | const enum iGmDocumentTheme docTheme = docTheme_Prefs(prefs_App()); |
235 | if ((docTheme == colorfulLight_GmDocumentTheme || docTheme == sepia_GmDocumentTheme) && | ||
236 | !cmp_String(&d->widget.parent->id, "tabs.buttons")) { | ||
237 | *bg = (docTheme == sepia_GmDocumentTheme && | ||
238 | colorTheme_App() == pureWhite_ColorTheme | ||
239 | ? tmBackground_ColorId | ||
240 | : tmBannerBackground_ColorId); | ||
241 | } | ||
242 | else { | ||
243 | *bg = uiBackgroundSelected_ColorId; | ||
244 | } | ||
235 | } | 245 | } |
236 | if (!isKeyRoot) { | 246 | if (!isKeyRoot) { |
237 | *bg = isDark_ColorTheme(colorTheme_App()) ? uiBackgroundUnfocusedSelection_ColorId | 247 | *bg = isDark_ColorTheme(colorTheme_App()) ? uiBackgroundUnfocusedSelection_ColorId |
diff --git a/src/ui/linkinfo.c b/src/ui/linkinfo.c index 36ab00c8..15aea16e 100644 --- a/src/ui/linkinfo.c +++ b/src/ui/linkinfo.c | |||
@@ -91,6 +91,10 @@ void infoText_LinkInfo(const iGmDocument *doc, iGmLinkId linkId, iString *text_o | |||
91 | appendCStr_String(text_out, "\x1b[0m"); | 91 | appendCStr_String(text_out, "\x1b[0m"); |
92 | appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) }); | 92 | appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) }); |
93 | } | 93 | } |
94 | else if (scheme == data_GmLinkScheme) { | ||
95 | appendCStr_String(text_out, paperclip_Icon " "); | ||
96 | append_String(text_out, prettyDataUrl_String(url, none_ColorId)); | ||
97 | } | ||
94 | else if (scheme != gemini_GmLinkScheme) { | 98 | else if (scheme != gemini_GmLinkScheme) { |
95 | const size_t maxDispLen = 300; | 99 | const size_t maxDispLen = 300; |
96 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); | 100 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index f14170ad..dc3264a2 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -568,7 +568,7 @@ static void presentResults_LookupWidget_(iLookupWidget *d) { | |||
568 | cstr_String(&res->label), | 568 | cstr_String(&res->label), |
569 | uiText_ColorEscape, | 569 | uiText_ColorEscape, |
570 | cstr_String(&res->meta)); | 570 | cstr_String(&res->meta)); |
571 | const iString *cmd = feedEntryOpenCommand_String(&res->url, 0); | 571 | const iString *cmd = feedEntryOpenCommand_String(&res->url, 0, 0); |
572 | if (cmd) { | 572 | if (cmd) { |
573 | set_String(&item->command, cmd); | 573 | set_String(&item->command, cmd); |
574 | } | 574 | } |
diff --git a/src/ui/root.c b/src/ui/root.c index 6e187313..9dee50ae 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -56,7 +56,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
56 | #if defined (iPlatformPcDesktop) | 56 | #if defined (iPlatformPcDesktop) |
57 | /* TODO: Submenus wouldn't hurt here. */ | 57 | /* TODO: Submenus wouldn't hurt here. */ |
58 | static const iMenuItem navMenuItems_[] = { | 58 | static const iMenuItem navMenuItems_[] = { |
59 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, | 59 | { openWindow_Icon " ${menu.newwindow}", SDLK_n, KMOD_PRIMARY, "window.new" }, |
60 | { add_Icon " ${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, | ||
60 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, | 61 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, |
61 | { "---" }, | 62 | { "---" }, |
62 | { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, | 63 | { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, |
@@ -468,6 +469,10 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
468 | return iFalse; | 469 | return iFalse; |
469 | } | 470 | } |
470 | else if (equal_Command(cmd, "window.setrect")) { | 471 | else if (equal_Command(cmd, "window.setrect")) { |
472 | if (hasLabel_Command(cmd, "index") && | ||
473 | argU32Label_Command(cmd, "index") != windowIndex_Root(root->root)) { | ||
474 | return iFalse; | ||
475 | } | ||
471 | const int snap = argLabel_Command(cmd, "snap"); | 476 | const int snap = argLabel_Command(cmd, "snap"); |
472 | if (snap) { | 477 | if (snap) { |
473 | iMainWindow *window = get_MainWindow(); | 478 | iMainWindow *window = get_MainWindow(); |
@@ -1059,6 +1064,8 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
1059 | updateNavBarIdentity_(navBar); | 1064 | updateNavBarIdentity_(navBar); |
1060 | } | 1065 | } |
1061 | setFocus_Widget(NULL); | 1066 | setFocus_Widget(NULL); |
1067 | makePaletteGlobal_GmDocument(document_DocumentWidget(doc)); | ||
1068 | refresh_Widget(findWidget_Root("doctabs")); | ||
1062 | } | 1069 | } |
1063 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { | 1070 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { |
1064 | iWidget *widget = pointer_Command(cmd); | 1071 | iWidget *widget = pointer_Command(cmd); |
@@ -1774,6 +1781,13 @@ void showToolbar_Root(iRoot *d, iBool show) { | |||
1774 | } | 1781 | } |
1775 | } | 1782 | } |
1776 | 1783 | ||
1784 | size_t windowIndex_Root(const iRoot *d) { | ||
1785 | if (type_Window(d->window) == main_WindowType) { | ||
1786 | return windowIndex_App(as_MainWindow(d->window)); | ||
1787 | } | ||
1788 | return iInvalidPos; | ||
1789 | } | ||
1790 | |||
1777 | iInt2 size_Root(const iRoot *d) { | 1791 | iInt2 size_Root(const iRoot *d) { |
1778 | return d && d->widget ? d->widget->rect.size : zero_I2(); | 1792 | return d && d->widget ? d->widget->rect.size : zero_I2(); |
1779 | } | 1793 | } |
diff --git a/src/ui/root.h b/src/ui/root.h index a81ebdf7..3b053c9e 100644 --- a/src/ui/root.h +++ b/src/ui/root.h | |||
@@ -47,6 +47,7 @@ void showOrHideNewTabButton_Root (iRoot *); | |||
47 | 47 | ||
48 | void notifyVisualOffsetChange_Root (iRoot *); | 48 | void notifyVisualOffsetChange_Root (iRoot *); |
49 | 49 | ||
50 | size_t windowIndex_Root (const iRoot *); | ||
50 | iInt2 size_Root (const iRoot *); | 51 | iInt2 size_Root (const iRoot *); |
51 | iRect rect_Root (const iRoot *); | 52 | iRect rect_Root (const iRoot *); |
52 | iRect safeRect_Root (const iRoot *); | 53 | iRect safeRect_Root (const iRoot *); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index da377ac2..8a96961a 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -412,19 +412,25 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
412 | setOutline_LabelWidget(child_Widget(d->actions, 1), d->feedsMode != all_FeedsMode); | 412 | setOutline_LabelWidget(child_Widget(d->actions, 1), d->feedsMode != all_FeedsMode); |
413 | setOutline_LabelWidget(child_Widget(d->actions, 2), d->feedsMode != unread_FeedsMode); | 413 | setOutline_LabelWidget(child_Widget(d->actions, 2), d->feedsMode != unread_FeedsMode); |
414 | } | 414 | } |
415 | d->menu = makeMenu_Widget( | 415 | const iMenuItem menuItems[] = { |
416 | as_Widget(d), | 416 | { openTab_Icon " ${menu.opentab}", 0, 0, "feed.entry.open newtab:1" }, |
417 | (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" }, | 417 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "feed.entry.open newtab:2" }, |
418 | { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" }, | 418 | #if defined (iPlatformDesktop) |
419 | { bookmark_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" }, | 419 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "feed.entry.open newwindow:1" }, |
420 | { "---", 0, 0, NULL }, | 420 | #endif |
421 | { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" }, | 421 | { "---", 0, 0, NULL }, |
422 | { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" }, | 422 | { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" }, |
423 | { whiteStar_Icon " " uiTextCaution_ColorEscape "${feeds.unsubscribe}", 0, 0, "feed.entry.unsubscribe" }, | 423 | { bookmark_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" }, |
424 | { "---", 0, 0, NULL }, | 424 | { "${menu.copyurl}", 0, 0, "feed.entry.copy" }, |
425 | { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" }, | 425 | { "---", 0, 0, NULL }, |
426 | { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } }, | 426 | { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" }, |
427 | 10); | 427 | { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" }, |
428 | { whiteStar_Icon " " uiTextCaution_ColorEscape "${feeds.unsubscribe}", 0, 0, "feed.entry.unsubscribe" }, | ||
429 | { "---", 0, 0, NULL }, | ||
430 | { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" }, | ||
431 | { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } | ||
432 | }; | ||
433 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
428 | d->modeMenu = makeMenu_Widget( | 434 | d->modeMenu = makeMenu_Widget( |
429 | as_Widget(d), | 435 | as_Widget(d), |
430 | (iMenuItem[]){ | 436 | (iMenuItem[]){ |
@@ -487,26 +493,29 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
487 | addItem_ListWidget(d->list, item); | 493 | addItem_ListWidget(d->list, item); |
488 | iRelease(item); | 494 | iRelease(item); |
489 | } | 495 | } |
490 | d->menu = makeMenu_Widget( | 496 | const iMenuItem menuItems[] = { |
491 | as_Widget(d), | 497 | { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" }, |
492 | (iMenuItem[]){ { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" }, | 498 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" }, |
493 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" }, | 499 | #if defined (iPlatformDesktop) |
494 | { "---", 0, 0, NULL }, | 500 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "bookmark.open newwindow:1" }, |
495 | { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" }, | 501 | #endif |
496 | { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" }, | 502 | { "---", 0, 0, NULL }, |
497 | { "${menu.copyurl}", 0, 0, "bookmark.copy" }, | 503 | { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" }, |
498 | { "---", 0, 0, NULL }, | 504 | { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" }, |
499 | { "", 0, 0, "bookmark.tag tag:subscribed" }, | 505 | { "${menu.copyurl}", 0, 0, "bookmark.copy" }, |
500 | { "", 0, 0, "bookmark.tag tag:homepage" }, | 506 | { "---", 0, 0, NULL }, |
501 | { "", 0, 0, "bookmark.tag tag:remotesource" }, | 507 | { "", 0, 0, "bookmark.tag tag:subscribed" }, |
502 | { "---", 0, 0, NULL }, | 508 | { "", 0, 0, "bookmark.tag tag:homepage" }, |
503 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, | 509 | { "", 0, 0, "bookmark.tag tag:remotesource" }, |
504 | { "---", 0, 0, NULL }, | 510 | { "---", 0, 0, NULL }, |
505 | { add_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" }, | 511 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, |
506 | { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" }, | 512 | { "---", 0, 0, NULL }, |
507 | { "---", 0, 0, NULL }, | 513 | { folder_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" }, |
508 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, | 514 | { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" }, |
509 | 17); | 515 | { "---", 0, 0, NULL }, |
516 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } | ||
517 | }; | ||
518 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
510 | d->modeMenu = makeMenu_Widget( | 519 | d->modeMenu = makeMenu_Widget( |
511 | as_Widget(d), | 520 | as_Widget(d), |
512 | (iMenuItem[]){ { bookmark_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 521 | (iMenuItem[]){ { bookmark_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
@@ -520,7 +529,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
520 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", | 529 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", |
521 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); | 530 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); |
522 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | 531 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); |
523 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, | 532 | addActionButton_SidebarWidget_(d, |
524 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", | 533 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", |
525 | "sidebar.bookmarks.edit", 0); | 534 | "sidebar.bookmarks.edit", 0); |
526 | } | 535 | } |
@@ -568,16 +577,21 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
568 | addItem_ListWidget(d->list, item); | 577 | addItem_ListWidget(d->list, item); |
569 | iRelease(item); | 578 | iRelease(item); |
570 | } | 579 | } |
571 | d->menu = makeMenu_Widget( | 580 | const iMenuItem menuItems[] = { |
572 | as_Widget(d), | 581 | { openTab_Icon " ${menu.opentab}", 0, 0, "history.open newtab:1" }, |
573 | (iMenuItem[]){ | 582 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "history.open newtab:2" }, |
574 | { "${menu.copyurl}", 0, 0, "history.copy" }, | 583 | #if defined (iPlatformDesktop) |
575 | { bookmark_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" }, | 584 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "history.open newwindow:1" }, |
576 | { "---", 0, 0, NULL }, | 585 | #endif |
577 | { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" }, | 586 | { "---" }, |
578 | { "---", 0, 0, NULL }, | 587 | { bookmark_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" }, |
579 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, | 588 | { "${menu.copyurl}", 0, 0, "history.copy" }, |
580 | }, 6); | 589 | { "---", 0, 0, NULL }, |
590 | { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" }, | ||
591 | { "---", 0, 0, NULL }, | ||
592 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, | ||
593 | }; | ||
594 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
581 | d->modeMenu = makeMenu_Widget( | 595 | d->modeMenu = makeMenu_Widget( |
582 | as_Widget(d), | 596 | as_Widget(d), |
583 | (iMenuItem[]){ | 597 | (iMenuItem[]){ |
@@ -981,7 +995,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
981 | } | 995 | } |
982 | case feeds_SidebarMode: { | 996 | case feeds_SidebarMode: { |
983 | postCommandString_Root(get_Root(), | 997 | postCommandString_Root(get_Root(), |
984 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()))); | 998 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()), 0)); |
985 | break; | 999 | break; |
986 | } | 1000 | } |
987 | case bookmarks_SidebarMode: | 1001 | case bookmarks_SidebarMode: |
@@ -1641,11 +1655,20 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1641 | else if (startsWith_CStr(cmd, "feed.entry.") && d->mode == feeds_SidebarMode) { | 1655 | else if (startsWith_CStr(cmd, "feed.entry.") && d->mode == feeds_SidebarMode) { |
1642 | const iSidebarItem *item = d->contextItem; | 1656 | const iSidebarItem *item = d->contextItem; |
1643 | if (item) { | 1657 | if (item) { |
1644 | if (isCommand_Widget(w, ev, "feed.entry.opentab")) { | 1658 | if (isCommand_Widget(w, ev, "feed.entry.open")) { |
1645 | postCommandString_Root(get_Root(), feedEntryOpenCommand_String(&item->url, 1)); | 1659 | const char *cmd = command_UserEvent(ev); |
1660 | postCommandString_Root( | ||
1661 | get_Root(), | ||
1662 | feedEntryOpenCommand_String(&item->url, | ||
1663 | argLabel_Command(cmd, "newtab"), | ||
1664 | argLabel_Command(cmd, "newwindow"))); | ||
1646 | return iTrue; | 1665 | return iTrue; |
1647 | } | 1666 | } |
1648 | if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { | 1667 | else if (isCommand_Widget(w, ev, "feed.entry.copy")) { |
1668 | SDL_SetClipboardText(cstr_String(&item->url)); | ||
1669 | return iTrue; | ||
1670 | } | ||
1671 | else if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { | ||
1649 | iVisited *vis = visited_App(); | 1672 | iVisited *vis = visited_App(); |
1650 | const iString *url = urlFragmentStripped_String(&item->url); | 1673 | const iString *url = urlFragmentStripped_String(&item->url); |
1651 | if (containsUrl_Visited(vis, url)) { | 1674 | if (containsUrl_Visited(vis, url)) { |
@@ -1657,7 +1680,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1657 | postCommand_App("visited.changed"); | 1680 | postCommand_App("visited.changed"); |
1658 | return iTrue; | 1681 | return iTrue; |
1659 | } | 1682 | } |
1660 | if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { | 1683 | else if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { |
1661 | makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); | 1684 | makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); |
1662 | if (deviceType_App() == desktop_AppDeviceType) { | 1685 | if (deviceType_App() == desktop_AppDeviceType) { |
1663 | postCommand_App("focus.set id:bmed.title"); | 1686 | postCommand_App("focus.set id:bmed.title"); |
@@ -1706,6 +1729,18 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1706 | } | 1729 | } |
1707 | return iTrue; | 1730 | return iTrue; |
1708 | } | 1731 | } |
1732 | else if (isCommand_Widget(w, ev, "history.open")) { | ||
1733 | const iSidebarItem *item = d->contextItem; | ||
1734 | if (item && !isEmpty_String(&item->url)) { | ||
1735 | const char *cmd = command_UserEvent(ev); | ||
1736 | postCommand_Widget(d, | ||
1737 | "!open newtab:%d newwindow:%d url:%s", | ||
1738 | argLabel_Command(cmd, "newtab"), | ||
1739 | argLabel_Command(cmd, "newwindow"), | ||
1740 | cstr_String(&item->url)); | ||
1741 | } | ||
1742 | return iTrue; | ||
1743 | } | ||
1709 | else if (isCommand_Widget(w, ev, "history.copy")) { | 1744 | else if (isCommand_Widget(w, ev, "history.copy")) { |
1710 | const iSidebarItem *item = d->contextItem; | 1745 | const iSidebarItem *item = d->contextItem; |
1711 | if (item && !isEmpty_String(&item->url)) { | 1746 | if (item && !isEmpty_String(&item->url)) { |
@@ -2156,28 +2191,38 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2156 | : uiTextDim_ColorId; | 2191 | : uiTextDim_ColorId; |
2157 | iUrl parts; | 2192 | iUrl parts; |
2158 | init_Url(&parts, &d->label); | 2193 | init_Url(&parts, &d->label); |
2159 | const iBool isAbout = equalCase_Rangecc(parts.scheme, "about"); | 2194 | const iBool isAbout = equalCase_Rangecc(parts.scheme, "about"); |
2160 | const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); | 2195 | const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); |
2161 | draw_Text(font, | 2196 | const iBool isData = equalCase_Rangecc(parts.scheme, "data"); |
2162 | add_I2(topLeft_Rect(itemRect), | 2197 | const int queryColor = isPressing ? uiTextPressed_ColorId |
2163 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), | 2198 | : isHover ? uiText_ColorId |
2164 | fg, | 2199 | : uiAnnotation_ColorId; |
2165 | "%s%s%s%s%s%s%s%s", | 2200 | const iInt2 textPos = |
2166 | isGemini ? "" : cstr_Rangecc(parts.scheme), | 2201 | add_I2(topLeft_Rect(itemRect), |
2167 | isGemini ? "" | 2202 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)); |
2168 | : isAbout ? ":" | 2203 | if (isData) { |
2169 | : "://", | 2204 | drawRange_Text( |
2170 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | 2205 | font, textPos, fg, range_String(prettyDataUrl_String(&d->label, queryColor))); |
2171 | : uiTextFramelessHover_ColorId) | 2206 | } |
2172 | : uiTextStrong_ColorId), | 2207 | else { |
2173 | cstr_Rangecc(parts.host), | 2208 | draw_Text( |
2174 | escape_Color(fg), | 2209 | font, |
2175 | cstr_Rangecc(parts.path), | 2210 | textPos, |
2176 | !isEmpty_Range(&parts.query) ? escape_Color(isPressing ? uiTextPressed_ColorId | 2211 | fg, |
2177 | : isHover ? uiText_ColorId | 2212 | "%s%s%s%s%s%s%s%s", |
2178 | : uiAnnotation_ColorId) | 2213 | isGemini ? "" : cstr_Rangecc(parts.scheme), |
2179 | : "", | 2214 | isGemini ? "" |
2180 | !isEmpty_Range(&parts.query) ? cstr_Rangecc(parts.query) : ""); | 2215 | : isAbout ? ":" |
2216 | : "://", | ||
2217 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | ||
2218 | : uiTextFramelessHover_ColorId) | ||
2219 | : uiTextStrong_ColorId), | ||
2220 | cstr_Rangecc(parts.host), | ||
2221 | escape_Color(fg), | ||
2222 | cstr_Rangecc(parts.path), | ||
2223 | !isEmpty_Range(&parts.query) ? escape_Color(queryColor) : "", | ||
2224 | !isEmpty_Range(&parts.query) ? cstr_Rangecc(parts.query) : ""); | ||
2225 | } | ||
2181 | } | 2226 | } |
2182 | iEndCollect(); | 2227 | iEndCollect(); |
2183 | } | 2228 | } |
diff --git a/src/ui/text.c b/src/ui/text.c index c19aed2f..83e87d0c 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -258,8 +258,6 @@ static int cmp_PrioMapItem_(const void *a, const void *b) { | |||
258 | } | 258 | } |
259 | 259 | ||
260 | struct Impl_Text { | 260 | struct Impl_Text { |
261 | // enum iTextFont contentFont; | ||
262 | // enum iTextFont headingFont; | ||
263 | float contentFontSize; | 261 | float contentFontSize; |
264 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ | 262 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
265 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | 263 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ |
@@ -276,7 +274,8 @@ struct Impl_Text { | |||
276 | int ansiFlags; | 274 | int ansiFlags; |
277 | int baseFontId; /* base attributes (for restoring via escapes) */ | 275 | int baseFontId; /* base attributes (for restoring via escapes) */ |
278 | int baseFgColorId; | 276 | int baseFgColorId; |
279 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | 277 | iBool missingGlyphs; /* true if a glyph couldn't be found */ |
278 | iChar missingChars[20]; /* rotating buffer of the latest missing characters */ | ||
280 | }; | 279 | }; |
281 | 280 | ||
282 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 281 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
@@ -296,6 +295,7 @@ static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) | |||
296 | /* This is the highest priority override font. */ | 295 | /* This is the highest priority override font. */ |
297 | d->overrideFontId = baseId; | 296 | d->overrideFontId = baseId; |
298 | } | 297 | } |
298 | iAssert(activeText_ == d); | ||
299 | pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); | 299 | pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); |
300 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { | 300 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { |
301 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { | 301 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { |
@@ -357,6 +357,8 @@ static void initFonts_Text_(iText *d) { | |||
357 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); | 357 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); |
358 | #endif | 358 | #endif |
359 | gap_Text = iRound(gap_UI * d->contentFontSize); | 359 | gap_Text = iRound(gap_UI * d->contentFontSize); |
360 | // d->missingGlyphs = iFalse; | ||
361 | // iZap(d->missingChars); | ||
360 | } | 362 | } |
361 | 363 | ||
362 | static void deinitFonts_Text_(iText *d) { | 364 | static void deinitFonts_Text_(iText *d) { |
@@ -424,6 +426,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
424 | d->baseFontId = -1; | 426 | d->baseFontId = -1; |
425 | d->baseFgColorId = -1; | 427 | d->baseFgColorId = -1; |
426 | d->missingGlyphs = iFalse; | 428 | d->missingGlyphs = iFalse; |
429 | iZap(d->missingChars); | ||
427 | d->render = render; | 430 | d->render = render; |
428 | /* A grayscale palette for rasterized glyphs. */ { | 431 | /* A grayscale palette for rasterized glyphs. */ { |
429 | SDL_Color colors[256]; | 432 | SDL_Color colors[256]; |
@@ -497,10 +500,13 @@ static void resetCache_Text_(iText *d) { | |||
497 | } | 500 | } |
498 | 501 | ||
499 | void resetFonts_Text(iText *d) { | 502 | void resetFonts_Text(iText *d) { |
503 | iText *oldActive = activeText_; | ||
504 | setCurrent_Text(d); /* some routines rely on the global `activeText_` pointer */ | ||
500 | deinitFonts_Text_(d); | 505 | deinitFonts_Text_(d); |
501 | deinitCache_Text_(d); | 506 | deinitCache_Text_(d); |
502 | initCache_Text_(d); | 507 | initCache_Text_(d); |
503 | initFonts_Text_(d); | 508 | initFonts_Text_(d); |
509 | setCurrent_Text(oldActive); | ||
504 | } | 510 | } |
505 | 511 | ||
506 | static SDL_Palette *glyphPalette_(void) { | 512 | static SDL_Palette *glyphPalette_(void) { |
@@ -610,8 +616,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
610 | } | 616 | } |
611 | } | 617 | } |
612 | if (!*glyphIndex) { | 618 | if (!*glyphIndex) { |
613 | activeText_->missingGlyphs = iTrue; | 619 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int) ch); fflush(stderr); |
614 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 620 | iText *tx = activeText_; |
621 | tx->missingGlyphs = iTrue; | ||
622 | /* Remember a few of the latest missing characters. */ | ||
623 | iBool gotIt = iFalse; | ||
624 | for (size_t i = 0; i < iElemCount(tx->missingChars); i++) { | ||
625 | if (tx->missingChars[i] == ch) { | ||
626 | gotIt = iTrue; | ||
627 | break; | ||
628 | } | ||
629 | } | ||
630 | if (!gotIt) { | ||
631 | memmove(tx->missingChars + 1, | ||
632 | tx->missingChars, | ||
633 | sizeof(tx->missingChars) - sizeof(tx->missingChars[0])); | ||
634 | tx->missingChars[0] = ch; | ||
635 | } | ||
615 | } | 636 | } |
616 | return d; | 637 | return d; |
617 | } | 638 | } |
@@ -1459,14 +1480,14 @@ static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont) | |||
1459 | } | 1480 | } |
1460 | 1481 | ||
1461 | static iRect run_Font_(iFont *d, const iRunArgs *args) { | 1482 | static iRect run_Font_(iFont *d, const iRunArgs *args) { |
1462 | const int mode = args->mode; | 1483 | const int mode = args->mode; |
1463 | const iInt2 orig = args->pos; | 1484 | const iInt2 orig = args->pos; |
1464 | iRect bounds = { orig, init_I2(0, d->height) }; | 1485 | iRect bounds = { orig, init_I2(0, d->height) }; |
1465 | float xCursor = 0.0f; | 1486 | float xCursor = 0.0f; |
1466 | float yCursor = 0.0f; | 1487 | float yCursor = 0.0f; |
1467 | float xCursorMax = 0.0f; | 1488 | float xCursorMax = 0.0f; |
1468 | const iBool isMonospaced = isMonospaced_Font(d); | 1489 | const iBool isMonospaced = isMonospaced_Font(d); |
1469 | iWrapText *wrap = args->wrap; | 1490 | iWrapText *wrap = args->wrap; |
1470 | iAssert(args->text.end >= args->text.start); | 1491 | iAssert(args->text.end >= args->text.start); |
1471 | /* Split the text into a number of attributed runs that specify exactly which | 1492 | /* Split the text into a number of attributed runs that specify exactly which |
1472 | font is used and other attributes such as color. (HarfBuzz shaping is done | 1493 | font is used and other attributes such as color. (HarfBuzz shaping is done |
@@ -2250,6 +2271,19 @@ iBool checkMissing_Text(void) { | |||
2250 | return missing; | 2271 | return missing; |
2251 | } | 2272 | } |
2252 | 2273 | ||
2274 | iChar missing_Text(size_t index) { | ||
2275 | const iText *d = activeText_; | ||
2276 | if (index >= iElemCount(d->missingChars)) { | ||
2277 | return 0; | ||
2278 | } | ||
2279 | return d->missingChars[index]; | ||
2280 | } | ||
2281 | |||
2282 | void resetMissing_Text(iText *d) { | ||
2283 | d->missingGlyphs = iFalse; | ||
2284 | iZap(d->missingChars); | ||
2285 | } | ||
2286 | |||
2253 | SDL_Texture *glyphCache_Text(void) { | 2287 | SDL_Texture *glyphCache_Text(void) { |
2254 | return activeText_->cache; | 2288 | return activeText_->cache; |
2255 | } | 2289 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index b952df84..e741880d 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -227,6 +227,8 @@ struct Impl_WrapText { | |||
227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); | 227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); |
228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | 228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); |
229 | 229 | ||
230 | iChar missing_Text (size_t index); | ||
231 | void resetMissing_Text (iText *); | ||
230 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ | 232 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ |
231 | SDL_Texture * glyphCache_Text (void); | 233 | SDL_Texture * glyphCache_Text (void); |
232 | 234 | ||
diff --git a/src/ui/touch.c b/src/ui/touch.c index a178a913..21a92b80 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -244,7 +244,8 @@ static void dispatchNotification_Touch_(const iTouch *d, int code) { | |||
244 | .timestamp = SDL_GetTicks(), | 244 | .timestamp = SDL_GetTicks(), |
245 | .code = code, | 245 | .code = code, |
246 | .data1 = d->affinity, | 246 | .data1 = d->affinity, |
247 | .data2 = d->affinity->root | 247 | .data2 = d->affinity->root, |
248 | .windowID = id_Window(window_Widget(d->affinity)), | ||
248 | }); | 249 | }); |
249 | setCurrent_Root(oldRoot); | 250 | setCurrent_Root(oldRoot); |
250 | } | 251 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index 5dd8a0bd..4f5de7f9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
35 | #include "keys.h" | 35 | #include "keys.h" |
36 | #include "labelwidget.h" | 36 | #include "labelwidget.h" |
37 | #include "root.h" | 37 | #include "root.h" |
38 | #include "sitespec.h" | ||
38 | #include "text.h" | 39 | #include "text.h" |
39 | #include "touch.h" | 40 | #include "touch.h" |
40 | #include "widget.h" | 41 | #include "widget.h" |
@@ -903,6 +904,7 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
903 | #else | 904 | #else |
904 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ | 905 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ |
905 | setDrawBufferEnabled_Widget(menu, iTrue); | 906 | setDrawBufferEnabled_Widget(menu, iTrue); |
907 | setFrameColor_Widget(menu, uiSeparator_ColorId); | ||
906 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); | 908 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); |
907 | if (deviceType_App() != desktop_AppDeviceType) { | 909 | if (deviceType_App() != desktop_AppDeviceType) { |
908 | setPadding1_Widget(menu, 2 * gap_UI); | 910 | setPadding1_Widget(menu, 2 * gap_UI); |
@@ -1084,12 +1086,12 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { | |||
1084 | setFlags_Widget(d, hidden_WidgetFlag, iFalse); | 1086 | setFlags_Widget(d, hidden_WidgetFlag, iFalse); |
1085 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); | 1087 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); |
1086 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); | 1088 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); |
1087 | if (!isPortraitPhone) { | 1089 | // if (!isPortraitPhone) { |
1088 | setFrameColor_Widget(d, uiBackgroundSelected_ColorId); | 1090 | // setFrameColor_Widget(d, uiSeparator_ColorId); |
1089 | } | 1091 | // } |
1090 | else { | 1092 | // else { |
1091 | setFrameColor_Widget(d, none_ColorId); | 1093 | // setFrameColor_Widget(d, none_ColorId); |
1092 | } | 1094 | // } |
1093 | arrange_Widget(d); /* need to know the height */ | 1095 | arrange_Widget(d); /* need to know the height */ |
1094 | iBool allowOverflow = iFalse; | 1096 | iBool allowOverflow = iFalse; |
1095 | /* A vertical offset determined by a possible selected label in the menu. */ | 1097 | /* A vertical offset determined by a possible selected label in the menu. */ |
@@ -1327,6 +1329,7 @@ int checkContextMenu_Widget(iWidget *menu, const SDL_Event *ev) { | |||
1327 | iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) { | 1329 | iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) { |
1328 | iLabelWidget *button = new_LabelWidget(label, "menu.open"); | 1330 | iLabelWidget *button = new_LabelWidget(label, "menu.open"); |
1329 | iWidget *menu = makeMenu_Widget(as_Widget(button), items, n); | 1331 | iWidget *menu = makeMenu_Widget(as_Widget(button), items, n); |
1332 | setFrameColor_Widget(menu, uiBackgroundSelected_ColorId); | ||
1330 | setId_Widget(menu, "menu"); | 1333 | setId_Widget(menu, "menu"); |
1331 | return button; | 1334 | return button; |
1332 | } | 1335 | } |
@@ -1383,6 +1386,9 @@ void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *s | |||
1383 | updateText_LabelWidget(dropButton, | 1386 | updateText_LabelWidget(dropButton, |
1384 | replaceNewlinesWithDash_(text_LabelWidget(item))); | 1387 | replaceNewlinesWithDash_(text_LabelWidget(item))); |
1385 | checkIcon_LabelWidget(dropButton); | 1388 | checkIcon_LabelWidget(dropButton); |
1389 | if (!icon_LabelWidget(dropButton)) { | ||
1390 | setIcon_LabelWidget(dropButton, icon_LabelWidget(item)); | ||
1391 | } | ||
1386 | } | 1392 | } |
1387 | } | 1393 | } |
1388 | } | 1394 | } |
@@ -1709,13 +1715,14 @@ iLabelWidget *addDialogTitle_Widget(iWidget *dlg, const char *text, const char * | |||
1709 | } | 1715 | } |
1710 | 1716 | ||
1711 | static void acceptValueInput_(iWidget *dlg) { | 1717 | static void acceptValueInput_(iWidget *dlg) { |
1712 | const iInputWidget *input = findChild_Widget(dlg, "input"); | 1718 | iInputWidget *input = findChild_Widget(dlg, "input"); |
1713 | if (!isEmpty_String(id_Widget(dlg))) { | 1719 | if (!isEmpty_String(id_Widget(dlg))) { |
1714 | const iString *val = text_InputWidget(input); | 1720 | const iString *val = text_InputWidget(input); |
1715 | postCommandf_App("%s arg:%d value:%s", | 1721 | postCommandf_App("%s arg:%d value:%s", |
1716 | cstr_String(id_Widget(dlg)), | 1722 | cstr_String(id_Widget(dlg)), |
1717 | toInt_String(val), | 1723 | toInt_String(val), |
1718 | cstr_String(val)); | 1724 | cstr_String(val)); |
1725 | setBackupFileName_InputWidget(input, NULL); | ||
1719 | } | 1726 | } |
1720 | } | 1727 | } |
1721 | 1728 | ||
@@ -1779,6 +1786,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | |||
1779 | else if (equal_Command(cmd, "valueinput.set")) { | 1786 | else if (equal_Command(cmd, "valueinput.set")) { |
1780 | iInputWidget *input = findChild_Widget(dlg, "input"); | 1787 | iInputWidget *input = findChild_Widget(dlg, "input"); |
1781 | setTextUndoableCStr_InputWidget(input, suffixPtr_Command(cmd, "text"), iTrue); | 1788 | setTextUndoableCStr_InputWidget(input, suffixPtr_Command(cmd, "text"), iTrue); |
1789 | deselect_InputWidget(input); | ||
1782 | validate_InputWidget(input); | 1790 | validate_InputWidget(input); |
1783 | return iTrue; | 1791 | return iTrue; |
1784 | } | 1792 | } |
@@ -2495,6 +2503,7 @@ iWidget *makePreferences_Widget(void) { | |||
2495 | { "input id:prefs.searchurl url:1 noheading:1" }, | 2503 | { "input id:prefs.searchurl url:1 noheading:1" }, |
2496 | { "padding" }, | 2504 | { "padding" }, |
2497 | { "toggle id:prefs.bookmarks.addbottom" }, | 2505 | { "toggle id:prefs.bookmarks.addbottom" }, |
2506 | { "toggle id:prefs.dataurl.openimages" }, | ||
2498 | { "toggle id:prefs.archive.openindex" }, | 2507 | { "toggle id:prefs.archive.openindex" }, |
2499 | { "radio device:1 id:prefs.pinsplit", 0, 0, (const void *) pinSplitItems }, | 2508 | { "radio device:1 id:prefs.pinsplit", 0, 0, (const void *) pinSplitItems }, |
2500 | { "padding" }, | 2509 | { "padding" }, |
@@ -2567,6 +2576,7 @@ iWidget *makePreferences_Widget(void) { | |||
2567 | const iMenuItem networkPanelItems[] = { | 2576 | const iMenuItem networkPanelItems[] = { |
2568 | { "title id:heading.prefs.network" }, | 2577 | { "title id:heading.prefs.network" }, |
2569 | { "toggle id:prefs.decodeurls" }, | 2578 | { "toggle id:prefs.decodeurls" }, |
2579 | { "input id:prefs.urlsize maxlen:10 selectall:1" }, | ||
2570 | { "padding" }, | 2580 | { "padding" }, |
2571 | { "input id:prefs.cachesize maxlen:4 selectall:1 unit:mb" }, | 2581 | { "input id:prefs.cachesize maxlen:4 selectall:1 unit:mb" }, |
2572 | { "input id:prefs.memorysize maxlen:4 selectall:1 unit:mb" }, | 2582 | { "input id:prefs.memorysize maxlen:4 selectall:1 unit:mb" }, |
@@ -2640,8 +2650,9 @@ iWidget *makePreferences_Widget(void) { | |||
2640 | setUrlContent_InputWidget(searchUrl, iTrue); | 2650 | setUrlContent_InputWidget(searchUrl, iTrue); |
2641 | addDialogPadding_(headings, values); | 2651 | addDialogPadding_(headings, values); |
2642 | addDialogToggle_(headings, values, "${prefs.hoverlink}", "prefs.hoverlink"); | 2652 | addDialogToggle_(headings, values, "${prefs.hoverlink}", "prefs.hoverlink"); |
2643 | addDialogToggle_(headings, values, "${prefs.bookmarks.addbottom}", "prefs.bookmarks.addbottom"); | 2653 | addDialogToggle_(headings, values, "${prefs.dataurl.openimages}", "prefs.dataurl.openimages"); |
2644 | addDialogToggle_(headings, values, "${prefs.archive.openindex}", "prefs.archive.openindex"); | 2654 | addDialogToggle_(headings, values, "${prefs.archive.openindex}", "prefs.archive.openindex"); |
2655 | addDialogToggle_(headings, values, "${prefs.bookmarks.addbottom}", "prefs.bookmarks.addbottom"); | ||
2645 | if (deviceType_App() != phone_AppDeviceType) { | 2656 | if (deviceType_App() != phone_AppDeviceType) { |
2646 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.pinsplit}"))); | 2657 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.pinsplit}"))); |
2647 | iWidget *pinSplit = new_Widget(); | 2658 | iWidget *pinSplit = new_Widget(); |
@@ -2900,6 +2911,7 @@ iWidget *makePreferences_Widget(void) { | |||
2900 | appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.network}", '6', &headings, &values); | 2911 | appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.network}", '6', &headings, &values); |
2901 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); | 2912 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); |
2902 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | 2913 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); |
2914 | addPrefsInputWithHeading_(headings, values, "prefs.urlsize", iClob(new_InputWidget(10))); | ||
2903 | /* Cache size. */ { | 2915 | /* Cache size. */ { |
2904 | iInputWidget *cache = new_InputWidget(4); | 2916 | iInputWidget *cache = new_InputWidget(4); |
2905 | setSelectAllOnFocus_InputWidget(cache, iTrue); | 2917 | setSelectAllOnFocus_InputWidget(cache, iTrue); |
@@ -3120,7 +3132,7 @@ iWidget *makeBookmarkCreation_Widget(const iString *url, const iString *title, i | |||
3120 | 3132 | ||
3121 | static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { | 3133 | static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { |
3122 | if (equal_Command(cmd, "cancel")) { | 3134 | if (equal_Command(cmd, "cancel")) { |
3123 | setupSheetTransition_Mobile(dlg, iFalse); | 3135 | setupSheetTransition_Mobile(dlg, 0); |
3124 | destroy_Widget(dlg); | 3136 | destroy_Widget(dlg); |
3125 | return iTrue; | 3137 | return iTrue; |
3126 | } | 3138 | } |
@@ -3163,15 +3175,14 @@ static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { | |||
3163 | } | 3175 | } |
3164 | 3176 | ||
3165 | iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { | 3177 | iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { |
3166 | const char *headingText = bookmarkId ? uiHeading_ColorEscape "${heading.feedcfg}" | 3178 | iWidget *dlg; |
3167 | : uiHeading_ColorEscape "${heading.subscribe}"; | 3179 | const char *headingText = bookmarkId ? "${heading.feedcfg}" : "${heading.subscribe}"; |
3168 | const iMenuItem actions[] = { { "${cancel}" }, | 3180 | const iMenuItem actions[] = { { "${cancel}" }, |
3169 | { bookmarkId ? uiTextCaution_ColorEscape "${dlg.feed.save}" | 3181 | { bookmarkId ? uiTextCaution_ColorEscape "${dlg.feed.save}" |
3170 | : uiTextCaution_ColorEscape "${dlg.feed.sub}", | 3182 | : uiTextCaution_ColorEscape "${dlg.feed.sub}", |
3171 | SDLK_RETURN, | 3183 | SDLK_RETURN, |
3172 | KMOD_PRIMARY, | 3184 | KMOD_PRIMARY, |
3173 | format_CStr("feedcfg.accept bmid:%d", bookmarkId) } }; | 3185 | format_CStr("feedcfg.accept bmid:%d", bookmarkId) } }; |
3174 | iWidget *dlg; | ||
3175 | if (isUsingPanelLayout_Mobile()) { | 3186 | if (isUsingPanelLayout_Mobile()) { |
3176 | const iMenuItem typeItems[] = { | 3187 | const iMenuItem typeItems[] = { |
3177 | { "button id:feedcfg.type.gemini label:dlg.feed.type.gemini", 0, 0, "feedcfg.type arg:0" }, | 3188 | { "button id:feedcfg.type.gemini label:dlg.feed.type.gemini", 0, 0, "feedcfg.type arg:0" }, |
@@ -3228,6 +3239,111 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { | |||
3228 | return dlg; | 3239 | return dlg; |
3229 | } | 3240 | } |
3230 | 3241 | ||
3242 | /*----------------------------------------------------------------------------------------------*/ | ||
3243 | |||
3244 | static void siteSpecificThemeChanged_(const iWidget *dlg) { | ||
3245 | iDocumentWidget *doc = document_App(); | ||
3246 | setThemeSeed_GmDocument((iGmDocument *) document_DocumentWidget(doc), | ||
3247 | urlPaletteSeed_String(url_DocumentWidget(doc)), | ||
3248 | urlThemeSeed_String(url_DocumentWidget(doc))); | ||
3249 | postCommand_App("theme.changed"); | ||
3250 | } | ||
3251 | |||
3252 | static const iString *siteSpecificRoot_(const iWidget *dlg) { | ||
3253 | return collect_String(suffix_Command(cstr_String(id_Widget(dlg)), "site")); | ||
3254 | } | ||
3255 | |||
3256 | static void updateSiteSpecificTheme_(iInputWidget *palSeed, void *context) { | ||
3257 | iWidget *dlg = context; | ||
3258 | const iString *siteRoot = siteSpecificRoot_(dlg); | ||
3259 | setValueString_SiteSpec(siteRoot, paletteSeed_SiteSpecKey, text_InputWidget(palSeed)); | ||
3260 | siteSpecificThemeChanged_(dlg); | ||
3261 | /* Allow seeing the new theme. */ | ||
3262 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, iTrue); | ||
3263 | } | ||
3264 | |||
3265 | static void closeSiteSpecific_(iWidget *dlg) { | ||
3266 | setupSheetTransition_Mobile(dlg, 0); | ||
3267 | delete_String(userData_Object(dlg)); /* saved original palette seed */ | ||
3268 | destroy_Widget(dlg); | ||
3269 | } | ||
3270 | |||
3271 | static iBool siteSpecificSettingsHandler_(iWidget *dlg, const char *cmd) { | ||
3272 | if (equal_Command(cmd, "cancel")) { | ||
3273 | const iBool wasNoFade = (flags_Widget(dlg) & noFadeBackground_WidgetFlag) != 0; | ||
3274 | iInputWidget *palSeed = findChild_Widget(dlg, "sitespec.palette"); | ||
3275 | setText_InputWidget(palSeed, userData_Object(dlg)); | ||
3276 | updateSiteSpecificTheme_(palSeed, dlg); | ||
3277 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, wasNoFade); | ||
3278 | closeSiteSpecific_(dlg); | ||
3279 | return iTrue; | ||
3280 | } | ||
3281 | if (startsWith_CStr(cmd, "input.ended id:sitespec.palette")) { | ||
3282 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, iFalse); | ||
3283 | refresh_Widget(dlg); | ||
3284 | siteSpecificThemeChanged_(dlg); | ||
3285 | return iTrue; | ||
3286 | } | ||
3287 | if (equal_Command(cmd, "sitespec.accept")) { | ||
3288 | const iInputWidget *palSeed = findChild_Widget(dlg, "sitespec.palette"); | ||
3289 | const iBool warnAnsi = isSelected_Widget(findChild_Widget(dlg, "sitespec.ansi")); | ||
3290 | const iString *siteRoot = siteSpecificRoot_(dlg); | ||
3291 | int dismissed = value_SiteSpec(siteRoot, dismissWarnings_SiteSpecKey); | ||
3292 | iChangeFlags(dismissed, ansiEscapes_GmDocumentWarning, !warnAnsi); | ||
3293 | setValue_SiteSpec(siteRoot, dismissWarnings_SiteSpecKey, dismissed); | ||
3294 | setValueString_SiteSpec(siteRoot, paletteSeed_SiteSpecKey, text_InputWidget(palSeed)); | ||
3295 | siteSpecificThemeChanged_(dlg); | ||
3296 | /* Note: The active DocumentWidget may actually be different than when opening the dialog. */ | ||
3297 | closeSiteSpecific_(dlg); | ||
3298 | return iTrue; | ||
3299 | } | ||
3300 | return iFalse; | ||
3301 | } | ||
3302 | |||
3303 | iWidget *makeSiteSpecificSettings_Widget(const iString *url) { | ||
3304 | iWidget *dlg; | ||
3305 | const iMenuItem actions[] = { | ||
3306 | { "${cancel}" }, | ||
3307 | { "${sitespec.accept}", SDLK_RETURN, KMOD_PRIMARY, "sitespec.accept" } | ||
3308 | }; | ||
3309 | if (isUsingPanelLayout_Mobile()) { | ||
3310 | iAssert(iFalse); | ||
3311 | } | ||
3312 | else { | ||
3313 | iWidget *headings, *values; | ||
3314 | dlg = makeSheet_Widget(format_CStr("sitespec site:%s", cstr_Rangecc(urlRoot_String(url)))); | ||
3315 | addDialogTitle_(dlg, "${heading.sitespec}", "heading.sitespec"); | ||
3316 | addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values))); | ||
3317 | iInputWidget *palSeed = new_InputWidget(0); | ||
3318 | setHint_InputWidget(palSeed, cstr_Block(urlThemeSeed_String(url))); | ||
3319 | addPrefsInputWithHeading_(headings, values, "sitespec.palette", iClob(palSeed)); | ||
3320 | addDialogToggle_(headings, values, "${sitespec.ansi}", "sitespec.ansi"); | ||
3321 | addChild_Widget(dlg, iClob(makeDialogButtons_Widget(actions, iElemCount(actions)))); | ||
3322 | addChild_Widget(get_Root()->widget, iClob(dlg)); | ||
3323 | as_Widget(palSeed)->rect.size.x = 60 * gap_UI; | ||
3324 | arrange_Widget(dlg); | ||
3325 | } | ||
3326 | /* Initialize. */ { | ||
3327 | const iString *site = collectNewRange_String(urlRoot_String(url)); | ||
3328 | setToggle_Widget(findChild_Widget(dlg, "sitespec.ansi"), | ||
3329 | ~value_SiteSpec(site, dismissWarnings_SiteSpecKey) & ansiEscapes_GmDocumentWarning); | ||
3330 | setText_InputWidget(findChild_Widget(dlg, "sitespec.palette"), | ||
3331 | valueString_SiteSpec(site, paletteSeed_SiteSpecKey)); | ||
3332 | /* Keep a copy of the original palette seed for restoring on cancel. */ | ||
3333 | setUserData_Object(dlg, copy_String(valueString_SiteSpec(site, paletteSeed_SiteSpecKey))); | ||
3334 | if (!isUsingPanelLayout_Mobile()) { | ||
3335 | setValidator_InputWidget(findChild_Widget(dlg, "sitespec.palette"), | ||
3336 | updateSiteSpecificTheme_, dlg); | ||
3337 | } | ||
3338 | } | ||
3339 | setCommandHandler_Widget(dlg, siteSpecificSettingsHandler_); | ||
3340 | setupSheetTransition_Mobile(dlg, incoming_TransitionFlag); | ||
3341 | setFocus_Widget(findChild_Widget(dlg, "sitespec.palette")); | ||
3342 | return dlg; | ||
3343 | } | ||
3344 | |||
3345 | /*----------------------------------------------------------------------------------------------*/ | ||
3346 | |||
3231 | iWidget *makeIdentityCreation_Widget(void) { | 3347 | iWidget *makeIdentityCreation_Widget(void) { |
3232 | const iMenuItem actions[] = { { "${dlg.newident.more}", 0, 0, "ident.showmore" }, | 3348 | const iMenuItem actions[] = { { "${dlg.newident.more}", 0, 0, "ident.showmore" }, |
3233 | { "---" }, | 3349 | { "---" }, |
@@ -3451,6 +3567,54 @@ iWidget *makeTranslation_Widget(iWidget *parent) { | |||
3451 | return dlg; | 3567 | return dlg; |
3452 | } | 3568 | } |
3453 | 3569 | ||
3570 | iWidget *makeGlyphFinder_Widget(void) { | ||
3571 | iString msg; | ||
3572 | iString command; | ||
3573 | init_String(&msg); | ||
3574 | initCStr_String(&command, "!font.find chars:"); | ||
3575 | for (size_t i = 0; ; i++) { | ||
3576 | iChar ch = missing_Text(i); | ||
3577 | if (!ch) break; | ||
3578 | appendFormat_String(&msg, " U+%04X", ch); | ||
3579 | appendChar_String(&command, ch); | ||
3580 | } | ||
3581 | iArray items; | ||
3582 | init_Array(&items, sizeof(iMenuItem)); | ||
3583 | if (!isEmpty_String(&msg)) { | ||
3584 | prependCStr_String(&msg, "${dlg.glyphfinder.missing} "); | ||
3585 | appendCStr_String(&msg, "\n\n${dlg.glyphfinder.help}"); | ||
3586 | pushBackN_Array( | ||
3587 | &items, | ||
3588 | (iMenuItem[]){ | ||
3589 | { "${menu.fonts}", 0, 0, "!open newtab:1 url:about:fonts" }, | ||
3590 | { "${dlg.glyphfinder.disable}", 0, 0, "prefs.font.warnmissing.changed arg:0" }, | ||
3591 | { "---" }, | ||
3592 | { uiTextCaution_ColorEscape magnifyingGlass_Icon " ${dlg.glyphfinder.search}", | ||
3593 | 0, | ||
3594 | 0, | ||
3595 | cstr_String(&command) }, | ||
3596 | { "${close}", 0, 0, "cancel" } }, | ||
3597 | 5); | ||
3598 | } | ||
3599 | else { | ||
3600 | setCStr_String(&msg, "${dlg.glyphfinder.help.empty}"); | ||
3601 | pushBackN_Array(&items, | ||
3602 | (iMenuItem[]){ { "${menu.reload}", 0, 0, "navigate.reload" }, | ||
3603 | { "${close}", 0, 0, "cancel" } }, | ||
3604 | 2); | ||
3605 | } | ||
3606 | iWidget *dlg = makeQuestion_Widget("${heading.glyphfinder}", cstr_String(&msg), | ||
3607 | constData_Array(&items), | ||
3608 | size_Array(&items)); | ||
3609 | arrange_Widget(dlg); | ||
3610 | deinit_Array(&items); | ||
3611 | deinit_String(&command); | ||
3612 | deinit_String(&msg); | ||
3613 | return dlg; | ||
3614 | } | ||
3615 | |||
3616 | /*----------------------------------------------------------------------------------------------*/ | ||
3617 | |||
3454 | void init_PerfTimer(iPerfTimer *d) { | 3618 | void init_PerfTimer(iPerfTimer *d) { |
3455 | d->ticks = SDL_GetPerformanceCounter(); | 3619 | d->ticks = SDL_GetPerformanceCounter(); |
3456 | } | 3620 | } |
diff --git a/src/ui/util.h b/src/ui/util.h index 98ce784c..31c8cedc 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -336,12 +336,14 @@ iWidget * makeQuestion_Widget (const char *title, const char *msg, | |||
336 | iWidget * makePreferences_Widget (void); | 336 | iWidget * makePreferences_Widget (void); |
337 | void updatePreferencesLayout_Widget (iWidget *prefs); | 337 | void updatePreferencesLayout_Widget (iWidget *prefs); |
338 | 338 | ||
339 | iWidget * makeBookmarkEditor_Widget (void); | 339 | iWidget * makeBookmarkEditor_Widget (void); |
340 | void setBookmarkEditorFolder_Widget(iWidget *editor, uint32_t folderId); | 340 | void setBookmarkEditorFolder_Widget (iWidget *editor, uint32_t folderId); |
341 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); | 341 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); |
342 | iWidget * makeIdentityCreation_Widget (void); | 342 | iWidget * makeIdentityCreation_Widget (void); |
343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); | 343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); |
344 | iWidget * makeTranslation_Widget (iWidget *parent); | 344 | iWidget * makeSiteSpecificSettings_Widget (const iString *url); |
345 | iWidget * makeTranslation_Widget (iWidget *parent); | ||
346 | iWidget * makeGlyphFinder_Widget (void); | ||
345 | 347 | ||
346 | const char * languageId_String (const iString *menuItemLabel); | 348 | const char * languageId_String (const iString *menuItemLabel); |
347 | int languageIndex_CStr (const char *langId); | 349 | int languageIndex_CStr (const char *langId); |
diff --git a/src/ui/widget.c b/src/ui/widget.c index fc754b7a..2e878878 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -168,7 +168,8 @@ void deinit_Widget(iWidget *d) { | |||
168 | if (d->flags & visualOffset_WidgetFlag) { | 168 | if (d->flags & visualOffset_WidgetFlag) { |
169 | removeTicker_App(visualOffsetAnimation_Widget_, d); | 169 | removeTicker_App(visualOffsetAnimation_Widget_, d); |
170 | } | 170 | } |
171 | iWindow *win = get_Window(); | 171 | iWindow *win = d->root->window; |
172 | iAssert(win); | ||
172 | if (win->lastHover == d) { | 173 | if (win->lastHover == d) { |
173 | win->lastHover = NULL; | 174 | win->lastHover = NULL; |
174 | } | 175 | } |
diff --git a/src/ui/window.c b/src/ui/window.c index 47abf878..b0de0557 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -80,6 +80,7 @@ iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect) | |||
80 | #if defined (iHaveNativeMenus) | 80 | #if defined (iHaveNativeMenus) |
81 | /* Using native menus. */ | 81 | /* Using native menus. */ |
82 | static const iMenuItem fileMenuItems_[] = { | 82 | static const iMenuItem fileMenuItems_[] = { |
83 | { "${menu.newwindow}", SDLK_n, KMOD_PRIMARY, "window.new" }, | ||
83 | { "${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, | 84 | { "${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, |
84 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, | 85 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, |
85 | { "---", 0, 0, NULL }, | 86 | { "---", 0, 0, NULL }, |
@@ -210,7 +211,9 @@ static void windowSizeChanged_MainWindow_(iMainWindow *d) { | |||
210 | 211 | ||
211 | static void setupUserInterface_MainWindow(iMainWindow *d) { | 212 | static void setupUserInterface_MainWindow(iMainWindow *d) { |
212 | #if defined (iHaveNativeMenus) | 213 | #if defined (iHaveNativeMenus) |
213 | insertMacMenus_(); | 214 | if (numWindows_App() == 0) { |
215 | insertMacMenus_(); /* TODO: Shouldn't this be in the App? */ | ||
216 | } | ||
214 | #endif | 217 | #endif |
215 | /* One root is created by default. */ | 218 | /* One root is created by default. */ |
216 | d->base.roots[0] = new_Root(); | 219 | d->base.roots[0] = new_Root(); |
@@ -246,6 +249,7 @@ static void updateSize_MainWindow_(iMainWindow *d, iBool notifyAlways) { | |||
246 | 249 | ||
247 | void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) { | 250 | void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) { |
248 | if (!isDrawing_) { | 251 | if (!isDrawing_) { |
252 | setCurrent_Window(d); | ||
249 | draw_MainWindow(d); | 253 | draw_MainWindow(d); |
250 | } | 254 | } |
251 | } | 255 | } |
@@ -647,6 +651,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
647 | } | 651 | } |
648 | 652 | ||
649 | void deinit_MainWindow(iMainWindow *d) { | 653 | void deinit_MainWindow(iMainWindow *d) { |
654 | removeWindow_App(d); | ||
650 | if (d->backBuf) { | 655 | if (d->backBuf) { |
651 | SDL_DestroyTexture(d->backBuf); | 656 | SDL_DestroyTexture(d->backBuf); |
652 | } | 657 | } |
@@ -677,6 +682,7 @@ iBool isFullscreen_MainWindow(const iMainWindow *d) { | |||
677 | } | 682 | } |
678 | 683 | ||
679 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { | 684 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { |
685 | |||
680 | while (widget->parent) { | 686 | while (widget->parent) { |
681 | widget = widget->parent; | 687 | widget = widget->parent; |
682 | } | 688 | } |
@@ -830,6 +836,9 @@ static void savePlace_MainWindow_(iAny *mainWindow) { | |||
830 | } | 836 | } |
831 | 837 | ||
832 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { | 838 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { |
839 | if (ev->windowID != SDL_GetWindowID(d->base.win)) { | ||
840 | return iFalse; | ||
841 | } | ||
833 | switch (ev->event) { | 842 | switch (ev->event) { |
834 | #if defined(iPlatformDesktop) | 843 | #if defined(iPlatformDesktop) |
835 | case SDL_WINDOWEVENT_EXPOSED: | 844 | case SDL_WINDOWEVENT_EXPOSED: |
@@ -857,7 +866,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
857 | if (d->base.isMinimized) { | 866 | if (d->base.isMinimized) { |
858 | return iFalse; | 867 | return iFalse; |
859 | } | 868 | } |
860 | closePopups_App(); | 869 | closePopups_App(iFalse); |
861 | checkPixelRatioChange_Window_(as_Window(d)); | 870 | checkPixelRatioChange_Window_(as_Window(d)); |
862 | const iInt2 newPos = init_I2(ev->data1, ev->data2); | 871 | const iInt2 newPos = init_I2(ev->data1, ev->data2); |
863 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ | 872 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ |
@@ -907,7 +916,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
907 | // updateSize_Window_(d, iTrue); | 916 | // updateSize_Window_(d, iTrue); |
908 | return iTrue; | 917 | return iTrue; |
909 | } | 918 | } |
910 | closePopups_App(); | 919 | closePopups_App(iFalse); |
911 | if (unsnap_MainWindow_(d, NULL)) { | 920 | if (unsnap_MainWindow_(d, NULL)) { |
912 | return iTrue; | 921 | return iTrue; |
913 | } | 922 | } |
@@ -929,7 +938,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
929 | return iTrue; | 938 | return iTrue; |
930 | case SDL_WINDOWEVENT_MINIMIZED: | 939 | case SDL_WINDOWEVENT_MINIMIZED: |
931 | d->base.isMinimized = iTrue; | 940 | d->base.isMinimized = iTrue; |
932 | closePopups_App(); | 941 | closePopups_App(iTrue); |
933 | return iTrue; | 942 | return iTrue; |
934 | #else /* if defined (!iPlatformDesktop) */ | 943 | #else /* if defined (!iPlatformDesktop) */ |
935 | case SDL_WINDOWEVENT_RESIZED: | 944 | case SDL_WINDOWEVENT_RESIZED: |
@@ -953,6 +962,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
953 | setCapsLockDown_Keys(iFalse); | 962 | setCapsLockDown_Keys(iFalse); |
954 | postCommand_App("window.focus.gained"); | 963 | postCommand_App("window.focus.gained"); |
955 | d->base.isExposed = iTrue; | 964 | d->base.isExposed = iTrue; |
965 | setActiveWindow_App(d); | ||
956 | #if !defined (iPlatformDesktop) | 966 | #if !defined (iPlatformDesktop) |
957 | /* Returned to foreground, may have lost buffered content. */ | 967 | /* Returned to foreground, may have lost buffered content. */ |
958 | invalidate_MainWindow_(d, iTrue); | 968 | invalidate_MainWindow_(d, iTrue); |
@@ -964,12 +974,17 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
964 | #if !defined (iPlatformDesktop) | 974 | #if !defined (iPlatformDesktop) |
965 | setFreezeDraw_MainWindow(d, iTrue); | 975 | setFreezeDraw_MainWindow(d, iTrue); |
966 | #endif | 976 | #endif |
967 | closePopups_App(); | 977 | closePopups_App(iTrue); |
968 | return iFalse; | 978 | return iFalse; |
969 | case SDL_WINDOWEVENT_TAKE_FOCUS: | 979 | case SDL_WINDOWEVENT_TAKE_FOCUS: |
970 | SDL_SetWindowInputFocus(d->base.win); | 980 | SDL_SetWindowInputFocus(d->base.win); |
971 | postRefresh_App(); | 981 | postRefresh_App(); |
972 | return iTrue; | 982 | return iTrue; |
983 | case SDL_WINDOWEVENT_CLOSE: | ||
984 | if (numWindows_App() > 1) { | ||
985 | closeWindow_App(d); | ||
986 | } | ||
987 | return iTrue; | ||
973 | default: | 988 | default: |
974 | break; | 989 | break; |
975 | } | 990 | } |
@@ -1009,7 +1024,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
1009 | } | 1024 | } |
1010 | } | 1025 | } |
1011 | case SDL_RENDER_TARGETS_RESET: | 1026 | case SDL_RENDER_TARGETS_RESET: |
1012 | case SDL_RENDER_DEVICE_RESET: { | 1027 | case SDL_RENDER_DEVICE_RESET: { |
1013 | if (mw) { | 1028 | if (mw) { |
1014 | invalidate_MainWindow_(mw, iTrue /* force full reset */); | 1029 | invalidate_MainWindow_(mw, iTrue /* force full reset */); |
1015 | } | 1030 | } |
@@ -1095,7 +1110,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
1095 | event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { | 1110 | event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { |
1096 | if (mouseGrab_Widget()) { | 1111 | if (mouseGrab_Widget()) { |
1097 | iWidget *grabbed = mouseGrab_Widget(); | 1112 | iWidget *grabbed = mouseGrab_Widget(); |
1098 | setCurrent_Root(findRoot_Window(d, grabbed)); | 1113 | setCurrent_Root(grabbed->root /* findRoot_Window(d, grabbed)*/); |
1099 | wasUsed = dispatchEvent_Widget(grabbed, &event); | 1114 | wasUsed = dispatchEvent_Widget(grabbed, &event); |
1100 | } | 1115 | } |
1101 | } | 1116 | } |
@@ -1164,7 +1179,33 @@ iLocalDef iBool isEscapeKeypress_(const SDL_Event *ev) { | |||
1164 | return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; | 1179 | return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; |
1165 | } | 1180 | } |
1166 | 1181 | ||
1182 | static uint32_t windowId_SDLEvent_(const SDL_Event *ev) { | ||
1183 | switch (ev->type) { | ||
1184 | case SDL_MOUSEBUTTONDOWN: | ||
1185 | case SDL_MOUSEBUTTONUP: | ||
1186 | return ev->button.windowID; | ||
1187 | case SDL_MOUSEMOTION: | ||
1188 | return ev->motion.windowID; | ||
1189 | case SDL_MOUSEWHEEL: | ||
1190 | return ev->wheel.windowID; | ||
1191 | case SDL_KEYDOWN: | ||
1192 | case SDL_KEYUP: | ||
1193 | return ev->key.windowID; | ||
1194 | case SDL_TEXTINPUT: | ||
1195 | return ev->text.windowID; | ||
1196 | case SDL_USEREVENT: | ||
1197 | return ev->user.windowID; | ||
1198 | default: | ||
1199 | return 0; | ||
1200 | } | ||
1201 | } | ||
1202 | |||
1167 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { | 1203 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { |
1204 | /* For the right window? */ | ||
1205 | const uint32_t evWin = windowId_SDLEvent_(ev); | ||
1206 | if (evWin && evWin != id_Window(d)) { | ||
1207 | return iFalse; /* Meant for a different window. */ | ||
1208 | } | ||
1168 | if (ev->type == SDL_MOUSEMOTION) { | 1209 | if (ev->type == SDL_MOUSEMOTION) { |
1169 | /* Hover widget may change. */ | 1210 | /* Hover widget may change. */ |
1170 | setHover_Widget(NULL); | 1211 | setHover_Widget(NULL); |
@@ -1526,6 +1567,23 @@ void setKeyboardHeight_MainWindow(iMainWindow *d, int height) { | |||
1526 | } | 1567 | } |
1527 | } | 1568 | } |
1528 | 1569 | ||
1570 | iObjectList *listDocuments_MainWindow(iMainWindow *d, const iRoot *rootOrNull) { | ||
1571 | iObjectList *docs = new_ObjectList(); | ||
1572 | iForIndices(i, d->base.roots) { | ||
1573 | iRoot *root = d->base.roots[i]; | ||
1574 | if (!root) continue; | ||
1575 | if (!rootOrNull || root == rootOrNull) { | ||
1576 | const iWidget *tabs = findChild_Widget(root->widget, "doctabs"); | ||
1577 | iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
1578 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
1579 | pushBack_ObjectList(docs, i.object); | ||
1580 | } | ||
1581 | } | ||
1582 | } | ||
1583 | } | ||
1584 | return docs; | ||
1585 | } | ||
1586 | |||
1529 | void checkPendingSplit_MainWindow(iMainWindow *d) { | 1587 | void checkPendingSplit_MainWindow(iMainWindow *d) { |
1530 | if (d->splitMode != d->pendingSplitMode) { | 1588 | if (d->splitMode != d->pendingSplitMode) { |
1531 | setSplitMode_MainWindow(d, d->pendingSplitMode); | 1589 | setSplitMode_MainWindow(d, d->pendingSplitMode); |
@@ -1549,6 +1607,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) { | |||
1549 | } | 1607 | } |
1550 | iWindow *w = as_Window(d); | 1608 | iWindow *w = as_Window(d); |
1551 | iAssert(current_Root() == NULL); | 1609 | iAssert(current_Root() == NULL); |
1610 | setCurrent_Window(w); | ||
1552 | if (d->splitMode != splitMode) { | 1611 | if (d->splitMode != splitMode) { |
1553 | int oldCount = numRoots_Window(w); | 1612 | int oldCount = numRoots_Window(w); |
1554 | setFreezeDraw_MainWindow(d, iTrue); | 1613 | setFreezeDraw_MainWindow(d, iTrue); |
@@ -1577,8 +1636,8 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) { | |||
1577 | /* The last child is the [+] button for adding a tab. */ | 1636 | /* The last child is the [+] button for adding a tab. */ |
1578 | moveTabButtonToEnd_Widget(findChild_Widget(docTabs, "newtab")); | 1637 | moveTabButtonToEnd_Widget(findChild_Widget(docTabs, "newtab")); |
1579 | setFlags_Widget(findWidget_Root("navbar.unsplit"), hidden_WidgetFlag, iTrue); | 1638 | setFlags_Widget(findWidget_Root("navbar.unsplit"), hidden_WidgetFlag, iTrue); |
1580 | iRelease(tabs); | ||
1581 | postCommandf_App("tabs.switch id:%s", cstr_String(id_Widget(constAs_Widget(curPage)))); | 1639 | postCommandf_App("tabs.switch id:%s", cstr_String(id_Widget(constAs_Widget(curPage)))); |
1640 | iRelease(tabs); | ||
1582 | } | 1641 | } |
1583 | else if (oldCount == 1 && splitMode) { | 1642 | else if (oldCount == 1 && splitMode) { |
1584 | /* Add a second root. */ | 1643 | /* Add a second root. */ |
diff --git a/src/ui/window.h b/src/ui/window.h index 5abf23eb..c3c34e1b 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -139,7 +139,7 @@ iAnyObject * hitChild_Window (const iWindow *, iInt2 coord); | |||
139 | uint32_t frameTime_Window (const iWindow *); | 139 | uint32_t frameTime_Window (const iWindow *); |
140 | SDL_Renderer * renderer_Window (const iWindow *); | 140 | SDL_Renderer * renderer_Window (const iWindow *); |
141 | int numRoots_Window (const iWindow *); | 141 | int numRoots_Window (const iWindow *); |
142 | iRoot * findRoot_Window (const iWindow *, const iWidget *widget); | 142 | //iRoot * findRoot_Window (const iWindow *, const iWidget *widget); |
143 | iRoot * otherRoot_Window (const iWindow *, iRoot *root); | 143 | iRoot * otherRoot_Window (const iWindow *, iRoot *root); |
144 | 144 | ||
145 | iBool processEvent_Window (iWindow *, const SDL_Event *); | 145 | iBool processEvent_Window (iWindow *, const SDL_Event *); |
@@ -187,6 +187,7 @@ void setTitle_MainWindow (iMainWindow *, const iString *title | |||
187 | void setSnap_MainWindow (iMainWindow *, int snapMode); | 187 | void setSnap_MainWindow (iMainWindow *, int snapMode); |
188 | void setFreezeDraw_MainWindow (iMainWindow *, iBool freezeDraw); | 188 | void setFreezeDraw_MainWindow (iMainWindow *, iBool freezeDraw); |
189 | void setKeyboardHeight_MainWindow (iMainWindow *, int height); | 189 | void setKeyboardHeight_MainWindow (iMainWindow *, int height); |
190 | iObjectList *listDocuments_MainWindow (iMainWindow *, const iRoot *rootOrNull); | ||
190 | void setSplitMode_MainWindow (iMainWindow *, int splitMode); | 191 | void setSplitMode_MainWindow (iMainWindow *, int splitMode); |
191 | void checkPendingSplit_MainWindow (iMainWindow *); | 192 | void checkPendingSplit_MainWindow (iMainWindow *); |
192 | void swapRoots_MainWindow (iMainWindow *); | 193 | void swapRoots_MainWindow (iMainWindow *); |