diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-06-25 16:26:53 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-06-25 16:26:53 +0300 |
commit | 5dbc85eaaa1bd0a0fc11dd76a75ece2efe763df5 (patch) | |
tree | 9721fb7aced603adb10b9bb3f3beb3f8d5fba973 | |
parent | 95c527db1484f7758a180c6de051d0182c3b2e81 (diff) | |
parent | f99a9111170f2ff28383fd3172fdaf4b9a1ba069 (diff) |
Merge branch 'work/v1.6' into work/serious-unicode
# Conflicts:
# res/fonts/SmolEmoji-Regular.ttf
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | Depends-iOS.cmake | 1 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | po/en.po | 33 | ||||
-rw-r--r-- | po/sgs.po | 150 | ||||
-rw-r--r-- | res/about/help.gmi | 57 | ||||
-rw-r--r-- | res/about/version.gmi | 8 | ||||
-rw-r--r-- | res/fi.skyjake.Lagrange.appdata.xml | 18 | ||||
-rw-r--r-- | res/fonts/SmolEmoji-Regular.ttf | bin | 50904 -> 57296 bytes | |||
-rw-r--r-- | res/iOSBundleInfo.plist.in | 259 | ||||
-rw-r--r-- | res/lang/de.bin | bin | 20699 -> 21008 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 19404 -> 19705 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 21778 -> 22087 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 21901 -> 22210 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 22311 -> 22620 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 21609 -> 21918 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 21167 -> 21476 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 22415 -> 22724 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 32646 -> 32955 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 32051 -> 32360 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 19792 -> 20101 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 18568 -> 18877 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 18753 -> 19062 bytes | |||
-rw-r--r-- | src/app.c | 156 | ||||
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/audio/player.c | 21 | ||||
-rw-r--r-- | src/audio/player.h | 3 | ||||
-rw-r--r-- | src/defs.h | 13 | ||||
-rw-r--r-- | src/gmdocument.c | 137 | ||||
-rw-r--r-- | src/gmdocument.h | 27 | ||||
-rw-r--r-- | src/gmtypesetter.c | 25 | ||||
-rw-r--r-- | src/gmtypesetter.h | 41 | ||||
-rw-r--r-- | src/gmutil.c | 4 | ||||
-rw-r--r-- | src/history.c | 183 | ||||
-rw-r--r-- | src/history.h | 25 | ||||
-rw-r--r-- | src/ios.h | 5 | ||||
-rw-r--r-- | src/ios.m | 158 | ||||
-rw-r--r-- | src/macos.m | 2 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/media.c | 35 | ||||
-rw-r--r-- | src/media.h | 3 | ||||
-rw-r--r-- | src/prefs.c | 1 | ||||
-rw-r--r-- | src/prefs.h | 1 | ||||
-rw-r--r-- | src/ui/certimportwidget.c | 8 | ||||
-rw-r--r-- | src/ui/color.c | 86 | ||||
-rw-r--r-- | src/ui/color.h | 42 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 374 | ||||
-rw-r--r-- | src/ui/documentwidget.h | 8 | ||||
-rw-r--r-- | src/ui/indicatorwidget.c | 2 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 41 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 27 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 57 | ||||
-rw-r--r-- | src/ui/labelwidget.h | 6 | ||||
-rw-r--r-- | src/ui/mediaui.c | 2 | ||||
-rw-r--r-- | src/ui/mobile.c | 20 | ||||
-rw-r--r-- | src/ui/root.c | 141 | ||||
-rw-r--r-- | src/ui/root.h | 3 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 91 | ||||
-rw-r--r-- | src/ui/sidebarwidget.h | 4 | ||||
-rw-r--r-- | src/ui/text.c | 79 | ||||
-rw-r--r-- | src/ui/text.h | 6 | ||||
-rw-r--r-- | src/ui/touch.c | 98 | ||||
-rw-r--r-- | src/ui/util.c | 104 | ||||
-rw-r--r-- | src/ui/widget.c | 110 | ||||
-rw-r--r-- | src/ui/widget.h | 17 | ||||
-rw-r--r-- | src/ui/window.c | 8 |
66 files changed, 2170 insertions, 546 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 64ba1c1d..47cd2442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -18,14 +18,14 @@ | |||
18 | cmake_minimum_required (VERSION 3.9) | 18 | cmake_minimum_required (VERSION 3.9) |
19 | 19 | ||
20 | project (Lagrange | 20 | project (Lagrange |
21 | VERSION 1.5.2 | 21 | VERSION 1.6.0 |
22 | DESCRIPTION "A Beautiful Gemini Client" | 22 | DESCRIPTION "A Beautiful Gemini Client" |
23 | LANGUAGES C | 23 | LANGUAGES C |
24 | ) | 24 | ) |
25 | set (COPYRIGHT_YEAR 2021) | 25 | set (COPYRIGHT_YEAR 2021) |
26 | if (IOS) | 26 | if (IOS) |
27 | set (PROJECT_VERSION 1.4) # pinned for TestFlight | 27 | set (PROJECT_VERSION 1.4) # pinned for TestFlight |
28 | set (IOS_BUNDLE_VERSION 8) # just increment this | 28 | set (IOS_BUNDLE_VERSION 21.6.15) |
29 | endif () | 29 | endif () |
30 | 30 | ||
31 | # Build configuration. | 31 | # Build configuration. |
@@ -126,6 +126,8 @@ set (SOURCES | |||
126 | src/gmdocument.h | 126 | src/gmdocument.h |
127 | src/gmrequest.c | 127 | src/gmrequest.c |
128 | src/gmrequest.h | 128 | src/gmrequest.h |
129 | src/gmtypesetter.c | ||
130 | src/gmtypesetter.h | ||
129 | src/gmutil.c | 131 | src/gmutil.c |
130 | src/gmutil.h | 132 | src/gmutil.h |
131 | src/gopher.c | 133 | src/gopher.c |
diff --git a/Depends-iOS.cmake b/Depends-iOS.cmake index c3747769..e0584c92 100644 --- a/Depends-iOS.cmake +++ b/Depends-iOS.cmake | |||
@@ -15,6 +15,7 @@ set (SDL2_LDFLAGS | |||
15 | "-framework Foundation" | 15 | "-framework Foundation" |
16 | "-framework Foundation" | 16 | "-framework Foundation" |
17 | "-framework GameController" | 17 | "-framework GameController" |
18 | "-framework MediaPlayer" | ||
18 | "-framework Metal" | 19 | "-framework Metal" |
19 | "-framework OpenGLES" | 20 | "-framework OpenGLES" |
20 | "-framework QuartzCore" | 21 | "-framework QuartzCore" |
@@ -21,11 +21,10 @@ Like Gemini, Lagrange has been designed with minimalism in mind. It depends on a | |||
21 | 21 | ||
22 | Prebuilt binaries for Windows, macOS and Linux can be found in [Releases][rel]. You can also find [Lagrange on Flathub for Linux](https://flathub.org/apps/details/fi.skyjake.Lagrange). | 22 | Prebuilt binaries for Windows, macOS and Linux can be found in [Releases][rel]. You can also find [Lagrange on Flathub for Linux](https://flathub.org/apps/details/fi.skyjake.Lagrange). |
23 | 23 | ||
24 | On macOS you can install and upgrade via a Homebrew tap: | 24 | On macOS you can install and upgrade via Homebrew: |
25 | 25 | ||
26 | ``` | 26 | ``` |
27 | $ brew tap skyjake/lagrange | 27 | brew install --cask lagrange |
28 | $ brew install lagrange | ||
29 | ``` | 28 | ``` |
30 | 29 | ||
31 | On openSUSE Tumbleweed: | 30 | On openSUSE Tumbleweed: |
@@ -392,6 +392,9 @@ msgstr "Find text on page" | |||
392 | msgid "status.query" | 392 | msgid "status.query" |
393 | msgstr "Search Query" | 393 | msgstr "Search Query" |
394 | 394 | ||
395 | msgid "status.query.tight" | ||
396 | msgstr "Query" | ||
397 | |||
395 | msgid "status.feeds" | 398 | msgid "status.feeds" |
396 | msgstr "Updating Feeds" | 399 | msgstr "Updating Feeds" |
397 | 400 | ||
@@ -465,6 +468,9 @@ msgstr "Open in New Tab" | |||
465 | msgid "menu.opentab.background" | 468 | msgid "menu.opentab.background" |
466 | msgstr "Open in Background Tab" | 469 | msgstr "Open in Background Tab" |
467 | 470 | ||
471 | msgid "menu.openfile" | ||
472 | msgstr "Open File…" | ||
473 | |||
468 | msgid "menu.edit" | 474 | msgid "menu.edit" |
469 | msgstr "Edit…" | 475 | msgstr "Edit…" |
470 | 476 | ||
@@ -739,6 +745,18 @@ msgstr "Bookmark Link…" | |||
739 | msgid "link.download" | 745 | msgid "link.download" |
740 | msgstr "Download Linked File" | 746 | msgstr "Download Linked File" |
741 | 747 | ||
748 | msgid "link.file.delete" | ||
749 | msgstr "Delete File" | ||
750 | |||
751 | msgid "heading.file.delete" | ||
752 | msgstr "DELETE FILE" | ||
753 | |||
754 | msgid "dlg.file.delete.confirm" | ||
755 | msgstr "Are you sure you want to delete this file?" | ||
756 | |||
757 | msgid "dlg.file.delete" | ||
758 | msgstr "Delete" | ||
759 | |||
742 | msgid "heading.openlink" | 760 | msgid "heading.openlink" |
743 | msgstr "OPEN LINK" | 761 | msgstr "OPEN LINK" |
744 | 762 | ||
@@ -1040,7 +1058,7 @@ msgid "heading.prefs.colors" | |||
1040 | msgstr "Colors" | 1058 | msgstr "Colors" |
1041 | 1059 | ||
1042 | msgid "heading.prefs.fonts" | 1060 | msgid "heading.prefs.fonts" |
1043 | msgstr "FONTS" | 1061 | msgstr "Fonts" |
1044 | 1062 | ||
1045 | # tab button | 1063 | # tab button |
1046 | msgid "heading.prefs.general" | 1064 | msgid "heading.prefs.general" |
@@ -1048,7 +1066,7 @@ msgstr "General" | |||
1048 | 1066 | ||
1049 | # tab button | 1067 | # tab button |
1050 | msgid "heading.prefs.interface" | 1068 | msgid "heading.prefs.interface" |
1051 | msgstr "Interface" | 1069 | msgstr "UI" |
1052 | 1070 | ||
1053 | # tab button | 1071 | # tab button |
1054 | msgid "heading.prefs.keys" | 1072 | msgid "heading.prefs.keys" |
@@ -1061,8 +1079,11 @@ msgstr "Network" | |||
1061 | msgid "heading.prefs.paragraph" | 1079 | msgid "heading.prefs.paragraph" |
1062 | msgstr "PARAGRAPH" | 1080 | msgstr "PARAGRAPH" |
1063 | 1081 | ||
1082 | msgid "heading.prefs.uitheme" | ||
1083 | msgstr "UI COLORS" | ||
1084 | |||
1064 | msgid "heading.prefs.pagecontent" | 1085 | msgid "heading.prefs.pagecontent" |
1065 | msgstr "PAGE CONTENT" | 1086 | msgstr "PAGE COLORS" |
1066 | 1087 | ||
1067 | msgid "heading.prefs.proxies" | 1088 | msgid "heading.prefs.proxies" |
1068 | msgstr "PROXIES" | 1089 | msgstr "PROXIES" |
@@ -1228,6 +1249,9 @@ msgstr "On Light" | |||
1228 | msgid "prefs.userfont" | 1249 | msgid "prefs.userfont" |
1229 | msgstr "Symbol font:" | 1250 | msgstr "Symbol font:" |
1230 | 1251 | ||
1252 | msgid "hint.prefs.userfont" | ||
1253 | msgstr "path of a TrueType font" | ||
1254 | |||
1231 | msgid "prefs.linewidth" | 1255 | msgid "prefs.linewidth" |
1232 | msgstr "Line width:" | 1256 | msgstr "Line width:" |
1233 | 1257 | ||
@@ -1258,6 +1282,9 @@ msgstr "Decode URLs:" | |||
1258 | msgid "prefs.cachesize" | 1282 | msgid "prefs.cachesize" |
1259 | msgstr "Cache size:" | 1283 | msgstr "Cache size:" |
1260 | 1284 | ||
1285 | msgid "prefs.memorysize" | ||
1286 | msgstr "Memory size:" | ||
1287 | |||
1261 | msgid "prefs.ca.file" | 1288 | msgid "prefs.ca.file" |
1262 | msgstr "CA file:" | 1289 | msgstr "CA file:" |
1263 | 1290 | ||
diff --git a/po/sgs.po b/po/sgs.po new file mode 100644 index 00000000..1de53b59 --- /dev/null +++ b/po/sgs.po | |||
@@ -0,0 +1,150 @@ | |||
1 | msgid "" | ||
2 | msgstr "" | ||
3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | ||
4 | "PO-Revision-Date: 2021-06-10 13:06+0000\n" | ||
5 | "Last-Translator: Arns Udovič <zordsdavini@arns.lt>\n" | ||
6 | "Language-Team: Samogitian <http://weblate.skyjake.fi/projects/lagrange/ui/" | ||
7 | "sgs/>\n" | ||
8 | "Language: sgs\n" | ||
9 | "MIME-Version: 1.0\n" | ||
10 | "Content-Type: text/plain; charset=UTF-8\n" | ||
11 | "Content-Transfer-Encoding: 8bit\n" | ||
12 | "Plural-Forms: nplurals=4; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : (n % " | ||
13 | "10 == 2 && n % 100 != 12) ? 1 : (n % 10 >= 3 && n % 10 <= 9 && (n % 100 < 11 " | ||
14 | "|| n % 100 > 19)) ? 2 : 3;\n" | ||
15 | "X-Generator: Weblate 4.5.1\n" | ||
16 | |||
17 | msgid "doc.archive.view" | ||
18 | msgstr "Veizietė arkīva vėdo" | ||
19 | |||
20 | # Inline download status message. | ||
21 | msgid "media.download.complete" | ||
22 | msgstr "Parsiuntėms bėngts." | ||
23 | |||
24 | # Used in inline audio player metadata popup. | ||
25 | msgid "audio.meta.artist" | ||
26 | msgstr "Atlėkies" | ||
27 | |||
28 | # Used in inline audio player metadata popup. | ||
29 | msgid "audio.meta.genre" | ||
30 | msgstr "Žanros" | ||
31 | |||
32 | # Used in inline audio player metadata popup. | ||
33 | msgid "audio.meta.date" | ||
34 | msgstr "Data" | ||
35 | |||
36 | # used as adjective, n is 8, 16, 24, 32, or 64 | ||
37 | #, c-format | ||
38 | msgid "n.bit" | ||
39 | msgstr "%d bitu" | ||
40 | |||
41 | # Hertz, unit for frequency values | ||
42 | msgid "hz" | ||
43 | msgstr "Hz" | ||
44 | |||
45 | #, c-format | ||
46 | msgid "feeds.list.entrycount" | ||
47 | msgid_plural "feeds.list.entrycount.n" | ||
48 | msgstr[0] "ėš vėsa %z īrašos" | ||
49 | msgstr[1] "ėš vėsa %z īrašo" | ||
50 | msgstr[2] "ėš vėsa %z īrašā" | ||
51 | msgstr[3] "ėš vėsa %z īrašu" | ||
52 | |||
53 | #, c-format | ||
54 | msgid "minutes.ago" | ||
55 | msgid_plural "minutes.ago.n" | ||
56 | msgstr[0] "prīš %d mėnotė" | ||
57 | msgstr[1] "prīš %d mėnotė" | ||
58 | msgstr[2] "prīš %d mėnotės" | ||
59 | msgstr[3] "prīš %d mėnoťiu" | ||
60 | |||
61 | #, c-format | ||
62 | msgid "hours.ago" | ||
63 | msgid_plural "hours.ago.n" | ||
64 | msgstr[0] "prīš %d adīna" | ||
65 | msgstr[1] "prīš %d adīnė" | ||
66 | msgstr[2] "prīš %d adīnas" | ||
67 | msgstr[3] "prīš %d adīnu" | ||
68 | |||
69 | #, c-format | ||
70 | msgid "days.ago" | ||
71 | msgid_plural "days.ago.n" | ||
72 | msgstr[0] "prīš %d dëna" | ||
73 | msgstr[1] "prīš %d dënė" | ||
74 | msgstr[2] "prīš %d dënas" | ||
75 | msgstr[3] "prīš %d dënu" | ||
76 | |||
77 | # Alt-text of the preformatted logo. | ||
78 | msgid "about.logo" | ||
79 | msgstr "ASCII dailė: žuodis „Lagrange“ stombio šrėfto" | ||
80 | |||
81 | msgid "about.tagline" | ||
82 | msgstr "Dailė Gemini naršīklė" | ||
83 | |||
84 | msgid "about.version" | ||
85 | msgstr "Versėjė" | ||
86 | |||
87 | msgid "cancel" | ||
88 | msgstr "Atšauktė" | ||
89 | |||
90 | msgid "close" | ||
91 | msgstr "Uždarītė" | ||
92 | |||
93 | msgid "dlg.message.ok" | ||
94 | msgstr "Tė̄stė" | ||
95 | |||
96 | msgid "dlg.default" | ||
97 | msgstr " Gerā " | ||
98 | |||
99 | msgid "toggle.yes" | ||
100 | msgstr "Tǡp" | ||
101 | |||
102 | msgid "toggle.no" | ||
103 | msgstr "Nē" | ||
104 | |||
105 | msgid "menu.title.file" | ||
106 | msgstr "Fails" | ||
107 | |||
108 | msgid "menu.title.edit" | ||
109 | msgstr "Redagůtė" | ||
110 | |||
111 | msgid "menu.title.help" | ||
112 | msgstr "Pagelba" | ||
113 | |||
114 | msgid "menu.split.horizontal" | ||
115 | msgstr "Golstē" | ||
116 | |||
117 | msgid "menu.split.vertical" | ||
118 | msgstr "Statē" | ||
119 | |||
120 | msgid "menu.openlocation" | ||
121 | msgstr "Atvertė vëta…" | ||
122 | |||
123 | msgid "menu.downloads" | ||
124 | msgstr "Ruodītė atsėsiuntėmus" | ||
125 | |||
126 | msgid "menu.find" | ||
127 | msgstr "Ëškuotė poslapie" | ||
128 | |||
129 | # Used on iOS. "Files" refers to Apple's iOS app where you can pick an iCloud folder. | ||
130 | msgid "menu.save.files" | ||
131 | msgstr "Ėšsauguotė failė" | ||
132 | |||
133 | msgid "menu.save.downloads.open" | ||
134 | msgstr "Ėšsauguotė atsisiuntėmūs ėr atvertė" | ||
135 | |||
136 | msgid "menu.zoom.in" | ||
137 | msgstr "Padėdintė" | ||
138 | |||
139 | msgid "menu.zoom.out" | ||
140 | msgstr "Somažintė" | ||
141 | |||
142 | msgid "menu.zoom.reset" | ||
143 | msgstr "Atstatītė priartėnėmus" | ||
144 | |||
145 | msgid "macos.menu.find" | ||
146 | msgstr "Ėškuotė" | ||
147 | |||
148 | # Used on desktop operating systems. "Downloads" refers to the user's configured downloads directory. | ||
149 | msgid "menu.save.downloads" | ||
150 | msgstr "Ėšsauguotė atsėsiuntėmu papkie" | ||
diff --git a/res/about/help.gmi b/res/about/help.gmi index d8a2749b..9ebf79ba 100644 --- a/res/about/help.gmi +++ b/res/about/help.gmi | |||
@@ -501,6 +501,63 @@ Translation table for keyboard modifiers. For example, one could swap the Alt an | |||
501 | ### mimehooks.txt | 501 | ### mimehooks.txt |
502 | See section 4. | 502 | See section 4. |
503 | 503 | ||
504 | ### palette.txt | ||
505 | Palette for UI color themes. This configuration file does not exist by default and must be created manually. | ||
506 | |||
507 | The actual colors used in the user interface are derived from the base palette. Therefore, you should not change the colors too drastically, but instead follow the expected pattern: a range of 5 intensities (black-white), followed by two accent color pairs (dim, bright). The last five colors are reserved for certain specific uses, e.g., the green is used for the navbar lock icon to indicate a trusted server certificate. The palettes defined here do not affect page color themes, only the UI. Note that link icons are considered part of the UI, so you may find that changing the palette will reduce visibility of the icons with some of the page color themes. | ||
508 | |||
509 | The file specifies two palettes: one for dark mode and one for light mode. The current palette is selected with the lines: | ||
510 | ``` | ||
511 | # Dark | ||
512 | ``` | ||
513 | and | ||
514 | ``` | ||
515 | # Light | ||
516 | ``` | ||
517 | |||
518 | The other lines specify colors: | ||
519 | > {label}: {rgb-hex} | ||
520 | > {label}: {red} {green} {blue} | ||
521 | * {rgb-hex} must use the form "#RRGGBB" | ||
522 | * {red}, {green}, {blue} are decimal numbers in the range 0...255 | ||
523 | |||
524 | The configuration below defines the built-in default palette: | ||
525 | ```palette.txt with the default UI palette | ||
526 | # Dark | ||
527 | |||
528 | black: #000000 | ||
529 | gray25: #282828 | ||
530 | gray50: #505050 | ||
531 | gray75: #A0A0A0 | ||
532 | white: #FFFFFF | ||
533 | brown: 106 80 0 | ||
534 | orange: 255 192 0 | ||
535 | teal: 0 96 128 | ||
536 | cyan: 0 192 255 | ||
537 | yellow: 255 255 32 | ||
538 | red: 255 64 64 | ||
539 | magenta: 255 0 255 | ||
540 | blue: 132 132 255 | ||
541 | green: 0 200 0 | ||
542 | |||
543 | # Light | ||
544 | |||
545 | black: 0 0 0 | ||
546 | gray25: 75 75 75 | ||
547 | gray50: 150 150 150 | ||
548 | gray75: 235 235 235 | ||
549 | white: 255 255 255 | ||
550 | brown: 210 120 10 | ||
551 | orange: 235 215 200 | ||
552 | teal: 10 110 130 | ||
553 | cyan: 170 215 220 | ||
554 | yellow: 255 255 32 | ||
555 | red: 255 64 64 | ||
556 | magenta: 255 0 255 | ||
557 | blue: 132 132 255 | ||
558 | green: 0 150 0 | ||
559 | ``` | ||
560 | |||
504 | ### prefs.cfg | 561 | ### prefs.cfg |
505 | Persistent configuration variables. The file is rewritten when the application is closed. Each line is interpreted as an internal UI event like those printed with the --echo command line option. | 562 | Persistent configuration variables. The file is rewritten when the application is closed. Each line is interpreted as an internal UI event like those printed with the --echo command line option. |
506 | 563 | ||
diff --git a/res/about/version.gmi b/res/about/version.gmi index c48a16d0..b213a24b 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -7,7 +7,13 @@ | |||
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 1.5.2 | 9 | ## 1.5.2 |
10 | * Normalize page contents (NFC) to avoid most common issues with diacritics. | 10 | * Fixed pasting a PEM-formatted certificate and/or private key via clipboard in Import Identity. |
11 | * Possible workaround for a visual glitch in the URL field. | ||
12 | * Specify `StartupWMClass` in .desktop file. | ||
13 | * Normalize page contents to avoid the most common issues with diacritics (Unicode NFC). | ||
14 | * Expanded the set of recognized custom link icons. | ||
15 | * Updated "Smol Emoji" font with new glyphs. | ||
16 | * Allow use of TLS cipher "DHE-RSA-AES256-GCM-SHA384". | ||
11 | 17 | ||
12 | ## 1.5.1 | 18 | ## 1.5.1 |
13 | * Updated UI translations. | 19 | * Updated UI translations. |
diff --git a/res/fi.skyjake.Lagrange.appdata.xml b/res/fi.skyjake.Lagrange.appdata.xml index 8f8ca7b3..b6344f45 100644 --- a/res/fi.skyjake.Lagrange.appdata.xml +++ b/res/fi.skyjake.Lagrange.appdata.xml | |||
@@ -45,6 +45,24 @@ | |||
45 | <update_contact>jaakko.keranen@iki.fi</update_contact> | 45 | <update_contact>jaakko.keranen@iki.fi</update_contact> |
46 | 46 | ||
47 | <releases> | 47 | <releases> |
48 | <release version="1.5.2" date="2021-06-15"> | ||
49 | <description> | ||
50 | <p>Bug fixes and tweaks:</p> | ||
51 | <ul> | ||
52 | <li>Fixed pasting a PEM-formatted certificate and/or | ||
53 | private key via clipboard in Import Identity.</li> | ||
54 | <li>Possible workaround for a visual glitch in the URL | ||
55 | field.</li> | ||
56 | <li>Specify `StartupWMClass` in .desktop file.</li> | ||
57 | <li>Normalize page contents to avoid the most common issues | ||
58 | with diacritics (Unicode NFC).</li> | ||
59 | <li>Expanded the set of recognized custom link icons.</li> | ||
60 | <li>Updated "Smol Emoji" font with new glyphs.</li> | ||
61 | <li>Allow use of TLS cipher "DHE-RSA-AES256-GCM-SHA384".</li> | ||
62 | </ul> | ||
63 | </description> | ||
64 | <url>https://github.com/skyjake/lagrange/releases/tag/v1.5.2</url> | ||
65 | </release> | ||
48 | <release version="1.5.1" date="2021-06-06"> | 66 | <release version="1.5.1" date="2021-06-06"> |
49 | <description> | 67 | <description> |
50 | <p>Resource update:</p> | 68 | <p>Resource update:</p> |
diff --git a/res/fonts/SmolEmoji-Regular.ttf b/res/fonts/SmolEmoji-Regular.ttf index 806766c2..5ef05e27 100644 --- a/res/fonts/SmolEmoji-Regular.ttf +++ b/res/fonts/SmolEmoji-Regular.ttf | |||
Binary files differ | |||
diff --git a/res/iOSBundleInfo.plist.in b/res/iOSBundleInfo.plist.in index 1cf0a303..09af4abf 100644 --- a/res/iOSBundleInfo.plist.in +++ b/res/iOSBundleInfo.plist.in | |||
@@ -1,116 +1,161 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
2 | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 2 | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
3 | <plist version="1.0"> | 3 | <plist version="1.0"> |
4 | <dict> | 4 | <dict> |
5 | <key>CFBundleDevelopmentRegion</key> | 5 | <key>CFBundleDevelopmentRegion</key> |
6 | <string>English</string> | 6 | <string>English</string> |
7 | <key>CFBundleExecutable</key> | 7 | <key>CFBundleExecutable</key> |
8 | <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> | 8 | <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> |
9 | <key>CFBundleGetInfoString</key> | 9 | <key>CFBundleGetInfoString</key> |
10 | <string>${MACOSX_BUNDLE_INFO_STRING}</string> | 10 | <string>${MACOSX_BUNDLE_INFO_STRING}</string> |
11 | <key>CFBundleIconName</key> | 11 | <key>CFBundleIconName</key> |
12 | <string>${MACOSX_BUNDLE_ICON_FILE}</string> | 12 | <string>${MACOSX_BUNDLE_ICON_FILE}</string> |
13 | <key>CFBundleIdentifier</key> | 13 | <key>CFBundleIdentifier</key> |
14 | <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> | 14 | <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> |
15 | <key>CFBundleInfoDictionaryVersion</key> | 15 | <key>CFBundleInfoDictionaryVersion</key> |
16 | <string>6.0</string> | 16 | <string>6.0</string> |
17 | <key>CFBundleName</key> | 17 | <key>CFBundleName</key> |
18 | <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> | 18 | <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> |
19 | <key>CFBundlePackageType</key> | 19 | <key>CFBundlePackageType</key> |
20 | <string>APPL</string> | 20 | <string>APPL</string> |
21 | <key>CFBundleVersion</key> | 21 | <key>CFBundleVersion</key> |
22 | <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> | 22 | <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> |
23 | <key>CFBundleShortVersionString</key> | 23 | <key>CFBundleShortVersionString</key> |
24 | <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> | 24 | <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> |
25 | <key>CFBundleSignature</key> | 25 | <key>CFBundleSignature</key> |
26 | <string>????</string> | 26 | <string>????</string> |
27 | <key>CSResourcesFileMapped</key> | 27 | <key>CSResourcesFileMapped</key> |
28 | <true/> | 28 | <true/> |
29 | <key>NSHumanReadableCopyright</key> | 29 | <key>NSHumanReadableCopyright</key> |
30 | <string>${MACOSX_BUNDLE_COPYRIGHT}</string> | 30 | <string>${MACOSX_BUNDLE_COPYRIGHT}</string> |
31 | <key>NSHighResolutionCapable</key> | 31 | <key>NSHighResolutionCapable</key> |
32 | <true/> | 32 | <true/> |
33 | <key>NSRequiresAquaSystemAppearance</key> | 33 | <key>NSRequiresAquaSystemAppearance</key> |
34 | <false/> | 34 | <false/> |
35 | <key>NSSupportsAutomaticGraphicsSwitching</key> | 35 | <key>NSSupportsAutomaticGraphicsSwitching</key> |
36 | <true/> | 36 | <true/> |
37 | <key>UISupportedInterfaceOrientations</key> | 37 | <key>UISupportedInterfaceOrientations</key> |
38 | <array> | 38 | <array> |
39 | <string>UIInterfaceOrientationPortrait</string> | 39 | <string>UIInterfaceOrientationPortrait</string> |
40 | <string>UIInterfaceOrientationLandscapeLeft</string> | 40 | <string>UIInterfaceOrientationLandscapeLeft</string> |
41 | <string>UIInterfaceOrientationLandscapeRight</string> | 41 | <string>UIInterfaceOrientationLandscapeRight</string> |
42 | </array> | 42 | </array> |
43 | <key>UISupportedInterfaceOrientations~ipad</key> | 43 | <key>UISupportedInterfaceOrientations~ipad</key> |
44 | <array> | 44 | <array> |
45 | <string>UIInterfaceOrientationPortrait</string> | 45 | <string>UIInterfaceOrientationPortrait</string> |
46 | <string>UIInterfaceOrientationPortraitUpsideDown</string> | 46 | <string>UIInterfaceOrientationPortraitUpsideDown</string> |
47 | <string>UIInterfaceOrientationLandscapeLeft</string> | 47 | <string>UIInterfaceOrientationLandscapeLeft</string> |
48 | <string>UIInterfaceOrientationLandscapeRight</string> | 48 | <string>UIInterfaceOrientationLandscapeRight</string> |
49 | </array> | 49 | </array> |
50 | <key>UILaunchStoryboardName</key> | 50 | <key>UILaunchStoryboardName</key> |
51 | <string>LaunchScreen</string> | 51 | <string>LaunchScreen</string> |
52 | <key>UIBackgroundModes</key> | 52 | <key>UIBackgroundModes</key> |
53 | <array> | 53 | <array> |
54 | <string>audio</string> | 54 | <string>audio</string> |
55 | </array> | 55 | </array> |
56 | <key>ITSAppUsesNonExemptEncryption</key> | 56 | <key>ITSAppUsesNonExemptEncryption</key> |
57 | <false/> | 57 | <false/> |
58 | <key>LSSupportsOpeningDocumentsInPlace</key> | 58 | <key>LSSupportsOpeningDocumentsInPlace</key> |
59 | <false/> | 59 | <false/> |
60 | <key>CFBundleDocumentTypes</key> | 60 | <key>CFBundleDocumentTypes</key> |
61 | <array> | 61 | <array> |
62 | <dict> | 62 | <dict> |
63 | <key>CFBundleTypeExtensions</key> | 63 | <key>CFBundleTypeName</key> |
64 | <array> | 64 | <string>Gemini Text File</string> |
65 | <string>gmi</string> | 65 | <key>LSHandlerRank</key> |
66 | <string>gemini</string> | 66 | <string>Default</string> |
67 | </array> | 67 | <key>LSItemContentTypes</key> |
68 | <key>CFBundleTypeIconFile</key> | 68 | <array> |
69 | <string>text-gemini.icns</string> | 69 | <string>fi.skyjake.lagrange.gemini</string> |
70 | <key>CFBundleTypeName</key> | 70 | </array> |
71 | <string>Gemini Text File</string> | 71 | </dict> |
72 | <key>CFBundleTypeRole</key> | 72 | <dict> |
73 | <string>Viewer</string> | 73 | <key>CFBundleTypeName</key> |
74 | <key>LSHandlerRank</key> | 74 | <string>Plain Text File</string> |
75 | <string>Default</string> | 75 | <key>LSHandlerRank</key> |
76 | <key>LSTypeIsPackage</key> | 76 | <string>Alternate</string> |
77 | <false/> | 77 | <key>LSItemContentTypes</key> |
78 | </dict> | 78 | <array> |
79 | <dict> | 79 | <string>public.plain-text</string> |
80 | <key>CFBundleTypeExtensions</key> | 80 | </array> |
81 | <array> | 81 | </dict> |
82 | <string>gpub</string> | 82 | <dict> |
83 | </array> | 83 | <key>CFBundleTypeExtensions</key> |
84 | <key>CFBundleTypeIconFile</key> | 84 | <array> |
85 | <string>gempub.icns</string> | 85 | <string>gmi</string> |
86 | <key>CFBundleTypeName</key> | 86 | <string>gemini</string> |
87 | <string>Gempub Book</string> | 87 | </array> |
88 | <key>CFBundleTypeRole</key> | 88 | <key>CFBundleTypeIconFile</key> |
89 | <string>Viewer</string> | 89 | <string>text-gemini.icns</string> |
90 | <key>LSHandlerRank</key> | 90 | <key>CFBundleTypeName</key> |
91 | <string>Default</string> | 91 | <string>Gemini Text File</string> |
92 | <key>LSTypeIsPackage</key> | 92 | <key>CFBundleTypeRole</key> |
93 | <false/> | 93 | <string>Viewer</string> |
94 | </dict> | 94 | <key>LSHandlerRank</key> |
95 | </array> | 95 | <string>Default</string> |
96 | <key>CFBundleURLTypes</key> | 96 | <key>LSTypeIsPackage</key> |
97 | <array> | 97 | <false/> |
98 | <dict> | 98 | </dict> |
99 | <key>CFBundleURLName</key> | 99 | <dict> |
100 | <string>Gemini</string> | 100 | <key>CFBundleTypeExtensions</key> |
101 | <key>CFBundleURLSchemes</key> | 101 | <array> |
102 | <array> | 102 | <string>gpub</string> |
103 | <string>gemini</string> | 103 | </array> |
104 | </array> | 104 | <key>CFBundleTypeIconFile</key> |
105 | </dict> | 105 | <string>gempub.icns</string> |
106 | <dict> | 106 | <key>CFBundleTypeName</key> |
107 | <key>CFBundleURLName</key> | 107 | <string>Gempub Book</string> |
108 | <string>Gopher</string> | 108 | <key>CFBundleTypeRole</key> |
109 | <key>CFBundleURLSchemes</key> | 109 | <string>Viewer</string> |
110 | <array> | 110 | <key>LSHandlerRank</key> |
111 | <string>gopher</string> | 111 | <string>Default</string> |
112 | </array> | 112 | <key>LSTypeIsPackage</key> |
113 | </dict> | 113 | <false/> |
114 | </array> | 114 | </dict> |
115 | </dict> | 115 | </array> |
116 | <key>CFBundleURLTypes</key> | ||
117 | <array> | ||
118 | <dict> | ||
119 | <key>CFBundleURLName</key> | ||
120 | <string>Gemini</string> | ||
121 | <key>CFBundleURLSchemes</key> | ||
122 | <array> | ||
123 | <string>gemini</string> | ||
124 | </array> | ||
125 | </dict> | ||
126 | <dict> | ||
127 | <key>CFBundleURLName</key> | ||
128 | <string>Gopher</string> | ||
129 | <key>CFBundleURLSchemes</key> | ||
130 | <array> | ||
131 | <string>gopher</string> | ||
132 | </array> | ||
133 | </dict> | ||
134 | </array> | ||
135 | <key>UTExportedTypeDeclarations</key> | ||
136 | <array> | ||
137 | <dict> | ||
138 | <key>UTTypeIdentifier</key> | ||
139 | <string>fi.skyjake.lagrange.gemini</string> | ||
140 | <key>UTTypeReferenceURL</key> | ||
141 | <string>https://gemini.circumlunar.space/docs/specification.gmi</string> | ||
142 | <key>UTTypeDescription</key> | ||
143 | <string>Gemini Text Document</string> | ||
144 | <key>UTTypeConformsTo</key> | ||
145 | <array> | ||
146 | <string>public.plain-text</string> | ||
147 | </array> | ||
148 | <key>UTTypeTagSpecification</key> | ||
149 | <dict> | ||
150 | <key>public.filename-extension</key> | ||
151 | <array> | ||
152 | <string>gmi</string> | ||
153 | <string>gemini</string> | ||
154 | </array> | ||
155 | <key>public.mime-type</key> | ||
156 | <string>text/gemini</string> | ||
157 | </dict> | ||
158 | </dict> | ||
159 | </array> | ||
160 | </dict> | ||
116 | </plist> | 161 | </plist> |
diff --git a/res/lang/de.bin b/res/lang/de.bin index b89c8b0f..2b2e2b3a 100644 --- a/res/lang/de.bin +++ b/res/lang/de.bin | |||
Binary files differ | |||
diff --git a/res/lang/en.bin b/res/lang/en.bin index 0ac42d88..54f384b0 100644 --- a/res/lang/en.bin +++ b/res/lang/en.bin | |||
Binary files differ | |||
diff --git a/res/lang/es.bin b/res/lang/es.bin index 8f3940f1..a69a21c5 100644 --- a/res/lang/es.bin +++ b/res/lang/es.bin | |||
Binary files differ | |||
diff --git a/res/lang/fi.bin b/res/lang/fi.bin index d19dc0e8..7f431a03 100644 --- a/res/lang/fi.bin +++ b/res/lang/fi.bin | |||
Binary files differ | |||
diff --git a/res/lang/fr.bin b/res/lang/fr.bin index 0b78f3b7..8040b95c 100644 --- a/res/lang/fr.bin +++ b/res/lang/fr.bin | |||
Binary files differ | |||
diff --git a/res/lang/ia.bin b/res/lang/ia.bin index e6590afc..2f8c6b21 100644 --- a/res/lang/ia.bin +++ b/res/lang/ia.bin | |||
Binary files differ | |||
diff --git a/res/lang/ie.bin b/res/lang/ie.bin index f852ba0e..d5d88d5b 100644 --- a/res/lang/ie.bin +++ b/res/lang/ie.bin | |||
Binary files differ | |||
diff --git a/res/lang/pl.bin b/res/lang/pl.bin index 9f42024c..a886bb65 100644 --- a/res/lang/pl.bin +++ b/res/lang/pl.bin | |||
Binary files differ | |||
diff --git a/res/lang/ru.bin b/res/lang/ru.bin index 5c9e3488..6f69ac6e 100644 --- a/res/lang/ru.bin +++ b/res/lang/ru.bin | |||
Binary files differ | |||
diff --git a/res/lang/sr.bin b/res/lang/sr.bin index f4239f1f..71c1daac 100644 --- a/res/lang/sr.bin +++ b/res/lang/sr.bin | |||
Binary files differ | |||
diff --git a/res/lang/tok.bin b/res/lang/tok.bin index 1a6b5baf..eb7da0c9 100644 --- a/res/lang/tok.bin +++ b/res/lang/tok.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin index b155036d..73dad7a7 100644 --- a/res/lang/zh_Hans.bin +++ b/res/lang/zh_Hans.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin index 57ce2d83..6b2ff359 100644 --- a/res/lang/zh_Hant.bin +++ b/res/lang/zh_Hant.bin | |||
Binary files differ | |||
@@ -59,6 +59,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
59 | #include <stdarg.h> | 59 | #include <stdarg.h> |
60 | #include <errno.h> | 60 | #include <errno.h> |
61 | 61 | ||
62 | //#define LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION 1 | ||
63 | |||
62 | #if defined (iPlatformAppleDesktop) | 64 | #if defined (iPlatformAppleDesktop) |
63 | # include "macos.h" | 65 | # include "macos.h" |
64 | #endif | 66 | #endif |
@@ -210,6 +212,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
210 | appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); | 212 | appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); |
211 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); | 213 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); |
212 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); | 214 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); |
215 | appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); | ||
213 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); | 216 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); |
214 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); | 217 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); |
215 | /* TODO: Set up an array of booleans in Prefs and do these in a loop. */ | 218 | /* TODO: Set up an array of booleans in Prefs and do these in a loop. */ |
@@ -760,6 +763,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
760 | setupApplication_iOS(); | 763 | setupApplication_iOS(); |
761 | #endif | 764 | #endif |
762 | init_Keys(); | 765 | init_Keys(); |
766 | loadPalette_Color(dataDir_App_()); | ||
763 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ | 767 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ |
764 | loadPrefs_App_(d); | 768 | loadPrefs_App_(d); |
765 | load_Keys(dataDir_App_()); | 769 | load_Keys(dataDir_App_()); |
@@ -918,13 +922,25 @@ const iString *debugInfo_App(void) { | |||
918 | extern char **environ; /* The environment variables. */ | 922 | extern char **environ; /* The environment variables. */ |
919 | iApp *d = &app_; | 923 | iApp *d = &app_; |
920 | iString *msg = collectNew_String(); | 924 | iString *msg = collectNew_String(); |
925 | iObjectList *docs = iClob(listDocuments_App(NULL)); | ||
921 | format_String(msg, "# Debug information\n"); | 926 | format_String(msg, "# Debug information\n"); |
927 | appendFormat_String(msg, "## Memory usage\n"); { | ||
928 | iMemInfo total = { 0, 0 }; | ||
929 | iForEach(ObjectList, i, docs) { | ||
930 | iDocumentWidget *doc = i.object; | ||
931 | iMemInfo usage = memoryUsage_History(history_DocumentWidget(doc)); | ||
932 | total.cacheSize += usage.cacheSize; | ||
933 | total.memorySize += usage.memorySize; | ||
934 | } | ||
935 | appendFormat_String(msg, "Total cache: %.3f MB\n", total.cacheSize / 1.0e6f); | ||
936 | appendFormat_String(msg, "Total memory: %.3f MB\n", total.memorySize / 1.0e6f); | ||
937 | } | ||
922 | appendFormat_String(msg, "## Documents\n"); | 938 | appendFormat_String(msg, "## Documents\n"); |
923 | iForEach(ObjectList, k, iClob(listDocuments_App(NULL))) { | 939 | iForEach(ObjectList, k, docs) { |
924 | iDocumentWidget *doc = k.object; | 940 | iDocumentWidget *doc = k.object; |
925 | appendFormat_String(msg, "### Tab %d.%zu: %s\n", | 941 | appendFormat_String(msg, "### Tab %d.%zu: %s\n", |
926 | constAs_Widget(doc)->root == get_Window()->roots[0] ? 0 : 1, | 942 | constAs_Widget(doc)->root == get_Window()->roots[0] ? 1 : 2, |
927 | childIndex_Widget(constAs_Widget(doc)->parent, k.object), | 943 | childIndex_Widget(constAs_Widget(doc)->parent, k.object) + 1, |
928 | cstr_String(bookmarkTitle_DocumentWidget(doc))); | 944 | cstr_String(bookmarkTitle_DocumentWidget(doc))); |
929 | append_String(msg, collect_String(debugInfo_History(history_DocumentWidget(doc)))); | 945 | append_String(msg, collect_String(debugInfo_History(history_DocumentWidget(doc)))); |
930 | } | 946 | } |
@@ -979,6 +995,33 @@ void trimCache_App(void) { | |||
979 | iRelease(docs); | 995 | iRelease(docs); |
980 | } | 996 | } |
981 | 997 | ||
998 | void trimMemory_App(void) { | ||
999 | iApp *d = &app_; | ||
1000 | size_t memorySize = 0; | ||
1001 | const size_t limit = d->prefs.maxMemorySize * 1000000; | ||
1002 | iObjectList *docs = listDocuments_App(NULL); | ||
1003 | iForEach(ObjectList, i, docs) { | ||
1004 | memorySize += memorySize_History(history_DocumentWidget(i.object)); | ||
1005 | } | ||
1006 | init_ObjectListIterator(&i, docs); | ||
1007 | iBool wasPruned = iFalse; | ||
1008 | while (memorySize > limit) { | ||
1009 | iDocumentWidget *doc = i.object; | ||
1010 | const size_t pruned = pruneLeastImportantMemory_History(history_DocumentWidget(doc)); | ||
1011 | if (pruned) { | ||
1012 | memorySize -= pruned; | ||
1013 | wasPruned = iTrue; | ||
1014 | } | ||
1015 | next_ObjectListIterator(&i); | ||
1016 | if (!i.value) { | ||
1017 | if (!wasPruned) break; | ||
1018 | wasPruned = iFalse; | ||
1019 | init_ObjectListIterator(&i, docs); | ||
1020 | } | ||
1021 | } | ||
1022 | iRelease(docs); | ||
1023 | } | ||
1024 | |||
982 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | 1025 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { |
983 | if (!isEmpty_Periodic(&d->periodic)) { | 1026 | if (!isEmpty_Periodic(&d->periodic)) { |
984 | return iFalse; | 1027 | return iFalse; |
@@ -1049,6 +1092,13 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1049 | postRefresh_App(); | 1092 | postRefresh_App(); |
1050 | break; | 1093 | break; |
1051 | case SDL_APP_WILLENTERBACKGROUND: | 1094 | case SDL_APP_WILLENTERBACKGROUND: |
1095 | #if defined (iPlatformAppleMobile) | ||
1096 | updateNowPlayingInfo_iOS(); | ||
1097 | #endif | ||
1098 | setFreezeDraw_Window(d->window, iTrue); | ||
1099 | savePrefs_App_(d); | ||
1100 | saveState_App_(d); | ||
1101 | break; | ||
1052 | case SDL_APP_TERMINATING: | 1102 | case SDL_APP_TERMINATING: |
1053 | setFreezeDraw_Window(d->window, iTrue); | 1103 | setFreezeDraw_Window(d->window, iTrue); |
1054 | savePrefs_App_(d); | 1104 | savePrefs_App_(d); |
@@ -1149,6 +1199,45 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1149 | ev.wheel.x = -ev.wheel.x; | 1199 | ev.wheel.x = -ev.wheel.x; |
1150 | #endif | 1200 | #endif |
1151 | } | 1201 | } |
1202 | #if defined (LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION) | ||
1203 | /* Convert mouse events to finger events to test the touch handling. */ { | ||
1204 | static float xPrev = 0.0f; | ||
1205 | static float yPrev = 0.0f; | ||
1206 | if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) { | ||
1207 | const float xf = (d->window->pixelRatio * ev.button.x) / (float) d->window->size.x; | ||
1208 | const float yf = (d->window->pixelRatio * ev.button.y) / (float) d->window->size.y; | ||
1209 | ev.type = (ev.type == SDL_MOUSEBUTTONDOWN ? SDL_FINGERDOWN : SDL_FINGERUP); | ||
1210 | ev.tfinger.x = xf; | ||
1211 | ev.tfinger.y = yf; | ||
1212 | ev.tfinger.dx = xf - xPrev; | ||
1213 | ev.tfinger.dy = yf - yPrev; | ||
1214 | xPrev = xf; | ||
1215 | yPrev = yf; | ||
1216 | ev.tfinger.fingerId = 0x1234; | ||
1217 | ev.tfinger.pressure = 1.0f; | ||
1218 | ev.tfinger.timestamp = SDL_GetTicks(); | ||
1219 | ev.tfinger.touchId = SDL_TOUCH_MOUSEID; | ||
1220 | } | ||
1221 | else if (ev.type == SDL_MOUSEMOTION) { | ||
1222 | if (~ev.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT)) { | ||
1223 | continue; /* only when pressing a button */ | ||
1224 | } | ||
1225 | const float xf = (d->window->pixelRatio * ev.motion.x) / (float) d->window->size.x; | ||
1226 | const float yf = (d->window->pixelRatio * ev.motion.y) / (float) d->window->size.y; | ||
1227 | ev.type = SDL_FINGERMOTION; | ||
1228 | ev.tfinger.x = xf; | ||
1229 | ev.tfinger.y = yf; | ||
1230 | ev.tfinger.dx = xf - xPrev; | ||
1231 | ev.tfinger.dy = yf - yPrev; | ||
1232 | xPrev = xf; | ||
1233 | yPrev = yf; | ||
1234 | ev.tfinger.fingerId = 0x1234; | ||
1235 | ev.tfinger.pressure = 1.0f; | ||
1236 | ev.tfinger.timestamp = SDL_GetTicks(); | ||
1237 | ev.tfinger.touchId = SDL_TOUCH_MOUSEID; | ||
1238 | } | ||
1239 | } | ||
1240 | #endif | ||
1152 | iBool wasUsed = processEvent_Window(d->window, &ev); | 1241 | iBool wasUsed = processEvent_Window(d->window, &ev); |
1153 | if (!wasUsed) { | 1242 | if (!wasUsed) { |
1154 | /* There may be a key bindings for this. */ | 1243 | /* There may be a key bindings for this. */ |
@@ -1565,7 +1654,9 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
1565 | postCommandf_App("searchurl address:%s", | 1654 | postCommandf_App("searchurl address:%s", |
1566 | cstrText_InputWidget(findChild_Widget(d, "prefs.searchurl"))); | 1655 | cstrText_InputWidget(findChild_Widget(d, "prefs.searchurl"))); |
1567 | postCommandf_App("cachesize.set arg:%d", | 1656 | postCommandf_App("cachesize.set arg:%d", |
1568 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); | 1657 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); |
1658 | postCommandf_App("memorysize.set arg:%d", | ||
1659 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); | ||
1569 | postCommandf_App("ca.file path:%s", | 1660 | postCommandf_App("ca.file path:%s", |
1570 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); | 1661 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); |
1571 | postCommandf_App("ca.path path:%s", | 1662 | postCommandf_App("ca.path path:%s", |
@@ -1978,7 +2069,7 @@ iBool handleCommand_App(const char *cmd) { | |||
1978 | else if (equal_Command(cmd, "hidetoolbarscroll")) { | 2069 | else if (equal_Command(cmd, "hidetoolbarscroll")) { |
1979 | d->prefs.hideToolbarOnScroll = arg_Command(cmd); | 2070 | d->prefs.hideToolbarOnScroll = arg_Command(cmd); |
1980 | if (!d->prefs.hideToolbarOnScroll) { | 2071 | if (!d->prefs.hideToolbarOnScroll) { |
1981 | showToolbars_Root(get_Root(), iTrue); | 2072 | showToolbar_Root(get_Root(), iTrue); |
1982 | } | 2073 | } |
1983 | return iTrue; | 2074 | return iTrue; |
1984 | } | 2075 | } |
@@ -2127,6 +2218,13 @@ iBool handleCommand_App(const char *cmd) { | |||
2127 | } | 2218 | } |
2128 | return iTrue; | 2219 | return iTrue; |
2129 | } | 2220 | } |
2221 | else if (equal_Command(cmd, "memorysize.set")) { | ||
2222 | d->prefs.maxMemorySize = arg_Command(cmd); | ||
2223 | if (d->prefs.maxMemorySize <= 0) { | ||
2224 | d->prefs.maxMemorySize = 0; | ||
2225 | } | ||
2226 | return iTrue; | ||
2227 | } | ||
2130 | else if (equal_Command(cmd, "searchurl")) { | 2228 | else if (equal_Command(cmd, "searchurl")) { |
2131 | iString *url = &d->prefs.searchUrl; | 2229 | iString *url = &d->prefs.searchUrl; |
2132 | setCStr_String(url, suffixPtr_Command(cmd, "address")); | 2230 | setCStr_String(url, suffixPtr_Command(cmd, "address")); |
@@ -2188,7 +2286,8 @@ iBool handleCommand_App(const char *cmd) { | |||
2188 | } | 2286 | } |
2189 | else if (equal_Command(cmd, "open")) { | 2287 | else if (equal_Command(cmd, "open")) { |
2190 | iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); | 2288 | iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); |
2191 | const iBool noProxy = argLabel_Command(cmd, "noproxy"); | 2289 | const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; |
2290 | const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0; | ||
2192 | iUrl parts; | 2291 | iUrl parts; |
2193 | init_Url(&parts, url); | 2292 | init_Url(&parts, url); |
2194 | if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || | 2293 | if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || |
@@ -2239,7 +2338,9 @@ iBool handleCommand_App(const char *cmd) { | |||
2239 | else { | 2338 | else { |
2240 | urlEncodePath_String(url); | 2339 | urlEncodePath_String(url); |
2241 | } | 2340 | } |
2242 | setUrlFromCache_DocumentWidget(doc, url, isHistory); | 2341 | setUrlFlags_DocumentWidget(doc, url, |
2342 | (isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) | | ||
2343 | (fromSidebar ? openedFromSidebar_DocumentWidgetSetUrlFlag : 0)); | ||
2243 | /* Optionally, jump to a text in the document. This will only work if the document | 2344 | /* Optionally, jump to a text in the document. This will only work if the document |
2244 | is already available, e.g., it's from "about:" or restored from cache. */ | 2345 | is already available, e.g., it's from "about:" or restored from cache. */ |
2245 | const iRangecc gotoHeading = range_Command(cmd, "gotoheading"); | 2346 | const iRangecc gotoHeading = range_Command(cmd, "gotoheading"); |
@@ -2254,6 +2355,36 @@ iBool handleCommand_App(const char *cmd) { | |||
2254 | } | 2355 | } |
2255 | setCurrent_Root(oldRoot); | 2356 | setCurrent_Root(oldRoot); |
2256 | } | 2357 | } |
2358 | else if (equal_Command(cmd, "file.open")) { | ||
2359 | const char *path = suffixPtr_Command(cmd, "path"); | ||
2360 | if (path) { | ||
2361 | postCommandf_App("open temp:%d url:%s", | ||
2362 | argLabel_Command(cmd, "temp"), | ||
2363 | makeFileUrl_CStr(path)); | ||
2364 | return iTrue; | ||
2365 | } | ||
2366 | #if defined (iPlatformAppleMobile) | ||
2367 | pickFileForOpening_iOS(); | ||
2368 | #endif | ||
2369 | return iTrue; | ||
2370 | } | ||
2371 | else if (equal_Command(cmd, "file.delete")) { | ||
2372 | const char *path = suffixPtr_Command(cmd, "path"); | ||
2373 | if (argLabel_Command(cmd, "confirm")) { | ||
2374 | makeQuestion_Widget( | ||
2375 | uiHeading_ColorEscape "${heading.file.delete}", | ||
2376 | format_CStr("${dlg.file.delete.confirm}\n%s", path), | ||
2377 | (iMenuItem[]){ | ||
2378 | { "${cancel}", 0, 0, NULL }, | ||
2379 | { uiTextCaution_ColorEscape "${dlg.file.delete}", 0, 0, | ||
2380 | format_CStr("!file.delete path:%s", path) } }, | ||
2381 | 2); | ||
2382 | } | ||
2383 | else { | ||
2384 | remove(path); | ||
2385 | } | ||
2386 | return iTrue; | ||
2387 | } | ||
2257 | else if (equal_Command(cmd, "document.request.cancelled")) { | 2388 | else if (equal_Command(cmd, "document.request.cancelled")) { |
2258 | /* TODO: How should cancelled requests be treated in the history? */ | 2389 | /* TODO: How should cancelled requests be treated in the history? */ |
2259 | #if 0 | 2390 | #if 0 |
@@ -2405,6 +2536,8 @@ iBool handleCommand_App(const char *cmd) { | |||
2405 | iTrue); | 2536 | iTrue); |
2406 | setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"), | 2537 | setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"), |
2407 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); | 2538 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); |
2539 | setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), | ||
2540 | collectNewFormat_String("%d", d->prefs.maxMemorySize)); | ||
2408 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); | 2541 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); |
2409 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); | 2542 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); |
2410 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.caFile); | 2543 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.caFile); |
@@ -2491,11 +2624,16 @@ iBool handleCommand_App(const char *cmd) { | |||
2491 | return iTrue; | 2624 | return iTrue; |
2492 | } | 2625 | } |
2493 | else if (equal_Command(cmd, "feeds.update.started")) { | 2626 | else if (equal_Command(cmd, "feeds.update.started")) { |
2494 | showCollapsed_Widget(findWidget_App("feeds.progress"), iTrue); | 2627 | iAnyObject *prog = findWidget_Root("feeds.progress"); |
2628 | const iWidget *navBar = findWidget_Root("navbar"); | ||
2629 | updateTextAndResizeWidthCStr_LabelWidget( | ||
2630 | prog, flags_Widget(navBar) & tight_WidgetFlag || deviceType_App() == phone_AppDeviceType ? | ||
2631 | "\u2605" : "\u2605 ${status.feeds}"); | ||
2632 | showCollapsed_Widget(prog, iTrue); | ||
2495 | return iFalse; | 2633 | return iFalse; |
2496 | } | 2634 | } |
2497 | else if (equal_Command(cmd, "feeds.update.finished")) { | 2635 | else if (equal_Command(cmd, "feeds.update.finished")) { |
2498 | showCollapsed_Widget(findWidget_App("feeds.progress"), iFalse); | 2636 | showCollapsed_Widget(findWidget_Root("feeds.progress"), iFalse); |
2499 | refreshFinished_Feeds(); | 2637 | refreshFinished_Feeds(); |
2500 | postRefresh_App(); | 2638 | postRefresh_App(); |
2501 | return iFalse; | 2639 | return iFalse; |
@@ -86,6 +86,7 @@ uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */ | |||
86 | iBool isLandscape_App (void); | 86 | iBool isLandscape_App (void); |
87 | iLocalDef iBool isPortrait_App (void) { return !isLandscape_App(); } | 87 | iLocalDef iBool isPortrait_App (void) { return !isLandscape_App(); } |
88 | enum iAppDeviceType deviceType_App (void); | 88 | enum iAppDeviceType deviceType_App (void); |
89 | iLocalDef iBool isPortraitPhone_App (void) { return isPortrait_App() && deviceType_App() == phone_AppDeviceType; } | ||
89 | iGmCerts * certs_App (void); | 90 | iGmCerts * certs_App (void); |
90 | iVisited * visited_App (void); | 91 | iVisited * visited_App (void); |
91 | iBookmarks * bookmarks_App (void); | 92 | iBookmarks * bookmarks_App (void); |
@@ -96,6 +97,7 @@ iObjectList * listDocuments_App (const iRoot *rootOrNull); /* NULL for a | |||
96 | iStringSet * listOpenURLs_App (void); /* all tabs */ | 97 | iStringSet * listOpenURLs_App (void); /* all tabs */ |
97 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); | 98 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); |
98 | void trimCache_App (void); | 99 | void trimCache_App (void); |
100 | void trimMemory_App (void); | ||
99 | 101 | ||
100 | iDocumentWidget * document_Root (iRoot *); | 102 | iDocumentWidget * document_Root (iRoot *); |
101 | 103 | ||
diff --git a/src/audio/player.c b/src/audio/player.c index 9e026561..94bcd065 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -455,6 +455,8 @@ struct Impl_Player { | |||
455 | iAVFAudioPlayer * avfPlayer; /* iOS */ | 455 | iAVFAudioPlayer * avfPlayer; /* iOS */ |
456 | }; | 456 | }; |
457 | 457 | ||
458 | static iPlayer *activePlayer_; | ||
459 | |||
458 | iDefineTypeConstruction(Player) | 460 | iDefineTypeConstruction(Player) |
459 | 461 | ||
460 | static size_t sampleSize_Player_(const iPlayer *d) { | 462 | static size_t sampleSize_Player_(const iPlayer *d) { |
@@ -655,8 +657,14 @@ void deinit_Player(iPlayer *d) { | |||
655 | #if defined (iPlatformAppleMobile) | 657 | #if defined (iPlatformAppleMobile) |
656 | if (d->avfPlayer) { | 658 | if (d->avfPlayer) { |
657 | delete_AVFAudioPlayer(d->avfPlayer); | 659 | delete_AVFAudioPlayer(d->avfPlayer); |
660 | if (activePlayer_ == d) { | ||
661 | clearNowPlayingInfo_iOS(); | ||
662 | } | ||
658 | } | 663 | } |
659 | #endif | 664 | #endif |
665 | if (activePlayer_ == d) { | ||
666 | activePlayer_ = NULL; | ||
667 | } | ||
660 | } | 668 | } |
661 | 669 | ||
662 | iBool isStarted_Player(const iPlayer *d) { | 670 | iBool isStarted_Player(const iPlayer *d) { |
@@ -724,6 +732,13 @@ void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock * | |||
724 | unlock_Mutex(&input->mtx); | 732 | unlock_Mutex(&input->mtx); |
725 | } | 733 | } |
726 | 734 | ||
735 | size_t sourceDataSize_Player(const iPlayer *d) { | ||
736 | lock_Mutex(&d->data->mtx); | ||
737 | const size_t size = size_Block(&d->data->data); | ||
738 | unlock_Mutex(&d->data->mtx); | ||
739 | return size; | ||
740 | } | ||
741 | |||
727 | iBool start_Player(iPlayer *d) { | 742 | iBool start_Player(iPlayer *d) { |
728 | if (isStarted_Player(d)) { | 743 | if (isStarted_Player(d)) { |
729 | return iFalse; | 744 | return iFalse; |
@@ -732,6 +747,7 @@ iBool start_Player(iPlayer *d) { | |||
732 | if (d->avfPlayer) { | 747 | if (d->avfPlayer) { |
733 | play_AVFAudioPlayer(d->avfPlayer); | 748 | play_AVFAudioPlayer(d->avfPlayer); |
734 | setNotIdle_Player(d); | 749 | setNotIdle_Player(d); |
750 | activePlayer_ = d; | ||
735 | return iTrue; | 751 | return iTrue; |
736 | } | 752 | } |
737 | #endif | 753 | #endif |
@@ -749,6 +765,7 @@ iBool start_Player(iPlayer *d) { | |||
749 | d->decoder->gain = d->volume; | 765 | d->decoder->gain = d->volume; |
750 | SDL_PauseAudioDevice(d->device, SDL_FALSE); | 766 | SDL_PauseAudioDevice(d->device, SDL_FALSE); |
751 | setNotIdle_Player(d); | 767 | setNotIdle_Player(d); |
768 | activePlayer_ = d; | ||
752 | return iTrue; | 769 | return iTrue; |
753 | } | 770 | } |
754 | 771 | ||
@@ -882,3 +899,7 @@ iString *metadataLabel_Player(const iPlayer *d) { | |||
882 | } | 899 | } |
883 | return meta; | 900 | return meta; |
884 | } | 901 | } |
902 | |||
903 | iPlayer *active_Player(void) { | ||
904 | return activePlayer_; | ||
905 | } | ||
diff --git a/src/audio/player.h b/src/audio/player.h index 8753d811..ca307dc4 100644 --- a/src/audio/player.h +++ b/src/audio/player.h | |||
@@ -48,6 +48,7 @@ enum iPlayerTag { | |||
48 | 48 | ||
49 | void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, | 49 | void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, |
50 | enum iPlayerUpdate update); | 50 | enum iPlayerUpdate update); |
51 | size_t sourceDataSize_Player (const iPlayer *); | ||
51 | 52 | ||
52 | iBool start_Player (iPlayer *); | 53 | iBool start_Player (iPlayer *); |
53 | void stop_Player (iPlayer *); | 54 | void stop_Player (iPlayer *); |
@@ -67,3 +68,5 @@ float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ | |||
67 | 68 | ||
68 | uint32_t idleTimeMs_Player (const iPlayer *); | 69 | uint32_t idleTimeMs_Player (const iPlayer *); |
69 | iString * metadataLabel_Player (const iPlayer *); | 70 | iString * metadataLabel_Player (const iPlayer *); |
71 | |||
72 | iPlayer * active_Player (void); | ||
@@ -24,18 +24,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | #include "lang.h" | 25 | #include "lang.h" |
26 | 26 | ||
27 | enum iSourceFormat { | ||
28 | undefined_SourceFormat = -1, | ||
29 | gemini_SourceFormat = 0, | ||
30 | plainText_SourceFormat, | ||
31 | }; | ||
32 | |||
27 | enum iFileVersion { | 33 | enum iFileVersion { |
28 | initial_FileVersion = 0, | 34 | initial_FileVersion = 0, |
29 | addedResponseTimestamps_FileVersion = 1, | 35 | addedResponseTimestamps_FileVersion = 1, |
30 | multipleRoots_FileVersion = 2, | 36 | multipleRoots_FileVersion = 2, |
31 | serializedSidebarState_FileVersion = 3, | 37 | serializedSidebarState_FileVersion = 3, |
38 | addedRecentUrlFlags_FileVersion = 4, | ||
32 | /* meta */ | 39 | /* meta */ |
33 | idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ | 40 | idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ |
34 | latest_FileVersion = 3, | 41 | latest_FileVersion = 4, |
35 | }; | 42 | }; |
36 | 43 | ||
37 | /* Icons */ | 44 | /* Icons */ |
38 | 45 | ||
46 | #define menu_Icon "\U0001d362" | ||
39 | #define rightArrowhead_Icon "\u27a4" | 47 | #define rightArrowhead_Icon "\u27a4" |
40 | #define leftArrowhead_Icon "\u27a4" | 48 | #define leftArrowhead_Icon "\u27a4" |
41 | #define warning_Icon "\u26a0" | 49 | #define warning_Icon "\u26a0" |
@@ -104,6 +112,9 @@ enum iFileVersion { | |||
104 | # define shiftReturn_Icon shift_Icon " " return_Icon | 112 | # define shiftReturn_Icon shift_Icon " " return_Icon |
105 | #endif | 113 | #endif |
106 | 114 | ||
115 | #if defined (iPlatformAppleDesktop) | ||
116 | # define iHaveNativeMenus | ||
117 | #endif | ||
107 | 118 | ||
108 | /* UI labels that depend on the platform */ | 119 | /* UI labels that depend on the platform */ |
109 | 120 | ||
diff --git a/src/gmdocument.c b/src/gmdocument.c index b95f85e7..f15d9d1d 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "gmdocument.h" | 23 | #include "gmdocument.h" |
24 | #include "gmtypesetter.h" | ||
24 | #include "gmutil.h" | 25 | #include "gmutil.h" |
25 | #include "lang.h" | 26 | #include "lang.h" |
26 | #include "ui/color.h" | 27 | #include "ui/color.h" |
@@ -48,7 +49,7 @@ iBool isDark_GmDocumentTheme(enum iGmDocumentTheme d) { | |||
48 | iDeclareType(GmLink) | 49 | iDeclareType(GmLink) |
49 | 50 | ||
50 | struct Impl_GmLink { | 51 | struct Impl_GmLink { |
51 | iString url; | 52 | iString url; /* resolved */ |
52 | iRangecc urlRange; /* URL in the source */ | 53 | iRangecc urlRange; /* URL in the source */ |
53 | iRangecc labelRange; /* label in the source */ | 54 | iRangecc labelRange; /* label in the source */ |
54 | iRangecc labelIcon; /* special icon defined in the label text */ | 55 | iRangecc labelIcon; /* special icon defined in the label text */ |
@@ -74,9 +75,10 @@ iDefineTypeConstruction(GmLink) | |||
74 | 75 | ||
75 | struct Impl_GmDocument { | 76 | struct Impl_GmDocument { |
76 | iObject object; | 77 | iObject object; |
77 | enum iGmDocumentFormat format; | 78 | enum iSourceFormat format; |
78 | iString source; | 79 | iString unormSource; /* unnormalized source */ |
79 | iString url; /* for resolving relative links */ | 80 | iString source; /* normalized source */ |
81 | iString url; /* for resolving relative links */ | ||
80 | iString localHost; | 82 | iString localHost; |
81 | iInt2 size; | 83 | iInt2 size; |
82 | iArray layout; /* contents of source, laid out in document space */ | 84 | iArray layout; /* contents of source, laid out in document space */ |
@@ -90,12 +92,14 @@ struct Impl_GmDocument { | |||
90 | iChar siteIcon; | 92 | iChar siteIcon; |
91 | iMedia * media; | 93 | iMedia * media; |
92 | iStringSet *openURLs; /* currently open URLs for highlighting links */ | 94 | iStringSet *openURLs; /* currently open URLs for highlighting links */ |
95 | iBool isPaletteValid; | ||
96 | iColor palette[tmMax_ColorId]; /* copy of the color palette */ | ||
93 | }; | 97 | }; |
94 | 98 | ||
95 | iDefineObjectConstruction(GmDocument) | 99 | iDefineObjectConstruction(GmDocument) |
96 | 100 | ||
97 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { | 101 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { |
98 | if (d->format == plainText_GmDocumentFormat) { | 102 | if (d->format == plainText_SourceFormat) { |
99 | return text_GmLineType; | 103 | return text_GmLineType; |
100 | } | 104 | } |
101 | return lineType_Rangecc(line); | 105 | return lineType_Rangecc(line); |
@@ -254,9 +258,12 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
254 | if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { | 258 | if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { |
255 | if (desc.start + len < desc.end && | 259 | if (desc.start + len < desc.end && |
256 | (isPictograph_Char(icon) || isEmoji_Char(icon) || | 260 | (isPictograph_Char(icon) || isEmoji_Char(icon) || |
261 | /* TODO: Add range(s) of 0x2nnn symbols. */ | ||
262 | icon == 0x2139 /* info */ || | ||
257 | icon == 0x2191 /* up arrow */ || | 263 | icon == 0x2191 /* up arrow */ || |
264 | icon == 0x2022 /* bullet */ || | ||
258 | icon == 0x2a2f /* close X */ || | 265 | icon == 0x2a2f /* close X */ || |
259 | icon == 0x2022 /* bullet */) && | 266 | icon == 0x2b50) && |
260 | !isFitzpatrickType_Char(icon)) { | 267 | !isFitzpatrickType_Char(icon)) { |
261 | link->flags |= iconFromLabel_GmLinkFlag; | 268 | link->flags |= iconFromLabel_GmLinkFlag; |
262 | link->labelIcon = (iRangecc){ desc.start, desc.start + len }; | 269 | link->labelIcon = (iRangecc){ desc.start, desc.start + len }; |
@@ -282,6 +289,12 @@ static void clearLinks_GmDocument_(iGmDocument *d) { | |||
282 | clear_PtrArray(&d->links); | 289 | clear_PtrArray(&d->links); |
283 | } | 290 | } |
284 | 291 | ||
292 | static iBool isGopher_GmDocument_(const iGmDocument *d) { | ||
293 | const iRangecc scheme = urlScheme_String(&d->url); | ||
294 | return (equalCase_Rangecc(scheme, "gopher") || | ||
295 | equalCase_Rangecc(scheme, "finger")); | ||
296 | } | ||
297 | |||
285 | static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { | 298 | static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { |
286 | const iRangecc scheme = urlScheme_String(&d->url); | 299 | const iRangecc scheme = urlScheme_String(&d->url); |
287 | if (equalCase_Rangecc(scheme, "gemini")) { | 300 | if (equalCase_Rangecc(scheme, "gemini")) { |
@@ -305,7 +318,7 @@ static void linkContentWasLaidOut_GmDocument_(iGmDocument *d, const iGmMediaInfo | |||
305 | 318 | ||
306 | static iBool isNormalized_GmDocument_(const iGmDocument *d) { | 319 | static iBool isNormalized_GmDocument_(const iGmDocument *d) { |
307 | const iPrefs *prefs = prefs_App(); | 320 | const iPrefs *prefs = prefs_App(); |
308 | if (d->format == plainText_GmDocumentFormat) { | 321 | if (d->format == plainText_SourceFormat) { |
309 | return iTrue; /* tabs are always normalized in plain text */ | 322 | return iTrue; /* tabs are always normalized in plain text */ |
310 | } | 323 | } |
311 | if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) { | 324 | if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) { |
@@ -357,6 +370,7 @@ static void updateOpenURLs_GmDocument_(iGmDocument *d) { | |||
357 | static void doLayout_GmDocument_(iGmDocument *d) { | 370 | static void doLayout_GmDocument_(iGmDocument *d) { |
358 | const iPrefs *prefs = prefs_App(); | 371 | const iPrefs *prefs = prefs_App(); |
359 | const iBool isMono = isForcedMonospace_GmDocument_(d); | 372 | const iBool isMono = isForcedMonospace_GmDocument_(d); |
373 | const iBool isGopher = isGopher_GmDocument_(d); | ||
360 | const iBool isNarrow = d->size.x < 90 * gap_Text; | 374 | const iBool isNarrow = d->size.x < 90 * gap_Text; |
361 | const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; | 375 | const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; |
362 | const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; | 376 | const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; |
@@ -434,12 +448,15 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
434 | enum iGmLineType prevType = text_GmLineType; | 448 | enum iGmLineType prevType = text_GmLineType; |
435 | enum iGmLineType prevNonBlankType = text_GmLineType; | 449 | enum iGmLineType prevNonBlankType = text_GmLineType; |
436 | iBool followsBlank = iFalse; | 450 | iBool followsBlank = iFalse; |
437 | if (d->format == plainText_GmDocumentFormat) { | 451 | if (d->format == plainText_SourceFormat) { |
438 | isPreformat = iTrue; | 452 | isPreformat = iTrue; |
439 | isFirstText = iFalse; | 453 | isFirstText = iFalse; |
440 | } | 454 | } |
441 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { | 455 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { |
442 | iRangecc line = contentLine; /* `line` will be trimmed later; would confuse nextSplit */ | 456 | iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ |
457 | if (*line.end == '\r') { | ||
458 | line.end--; /* trim CR always */ | ||
459 | } | ||
443 | iGmRun run = { .color = white_ColorId }; | 460 | iGmRun run = { .color = white_ColorId }; |
444 | enum iGmLineType type; | 461 | enum iGmLineType type; |
445 | float indent = 0.0f; | 462 | float indent = 0.0f; |
@@ -472,7 +489,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
472 | meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & | 489 | meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & |
473 | folded_GmPreMetaFlag; | 490 | folded_GmPreMetaFlag; |
474 | } | 491 | } |
475 | else if (prefs->collapsePreOnLoad) { | 492 | else if (prefs->collapsePreOnLoad && !isGopher) { |
476 | meta.flags |= folded_GmPreMetaFlag; | 493 | meta.flags |= folded_GmPreMetaFlag; |
477 | } | 494 | } |
478 | pushBack_Array(&d->preMeta, &meta); | 495 | pushBack_Array(&d->preMeta, &meta); |
@@ -500,14 +517,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
500 | if (contentLine.start == content.start) { | 517 | if (contentLine.start == content.start) { |
501 | prevType = type; | 518 | prevType = type; |
502 | } | 519 | } |
503 | if (d->format == gemini_GmDocumentFormat && | 520 | if (d->format == gemini_SourceFormat && |
504 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { | 521 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { |
505 | isPreformat = iFalse; | 522 | isPreformat = iFalse; |
506 | addSiteBanner = iFalse; /* overrides the banner */ | 523 | addSiteBanner = iFalse; /* overrides the banner */ |
507 | continue; | 524 | continue; |
508 | } | 525 | } |
509 | run.preId = preId; | 526 | run.preId = preId; |
510 | run.font = (d->format == plainText_GmDocumentFormat ? regularMonospace_FontId : preFont); | 527 | run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); |
511 | indent = indents[type]; | 528 | indent = indents[type]; |
512 | } | 529 | } |
513 | if (addSiteBanner) { | 530 | if (addSiteBanner) { |
@@ -580,7 +597,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
580 | } | 597 | } |
581 | } | 598 | } |
582 | /* Folded blocks are represented by a single run with the alt text. */ | 599 | /* Folded blocks are represented by a single run with the alt text. */ |
583 | if (isPreformat && d->format != plainText_GmDocumentFormat) { | 600 | if (isPreformat && d->format != plainText_SourceFormat) { |
584 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); | 601 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); |
585 | if (meta->flags & folded_GmPreMetaFlag) { | 602 | if (meta->flags & folded_GmPreMetaFlag) { |
586 | const iBool isBlank = isEmpty_Range(&meta->altText); | 603 | const iBool isBlank = isEmpty_Range(&meta->altText); |
@@ -676,7 +693,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
676 | pushBack_Array(&d->layout, &icon); | 693 | pushBack_Array(&d->layout, &icon); |
677 | } | 694 | } |
678 | run.color = colors[type]; | 695 | run.color = colors[type]; |
679 | if (d->format == plainText_GmDocumentFormat) { | 696 | if (d->format == plainText_SourceFormat) { |
680 | run.color = colors[text_GmLineType]; | 697 | run.color = colors[text_GmLineType]; |
681 | } | 698 | } |
682 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ | 699 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ |
@@ -705,8 +722,8 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
705 | type == quote_GmLineType ? 4 : 0); | 722 | type == quote_GmLineType ? 4 : 0); |
706 | } | 723 | } |
707 | const iBool isWordWrapped = | 724 | const iBool isWordWrapped = |
708 | (d->format == plainText_GmDocumentFormat ? prefs->plainTextWrap : !isPreformat); | 725 | (d->format == plainText_SourceFormat ? prefs->plainTextWrap : !isPreformat); |
709 | if (isPreformat && d->format != plainText_GmDocumentFormat) { | 726 | if (isPreformat && d->format != plainText_SourceFormat) { |
710 | /* Remember the top left coordinates of the block (first line of block). */ | 727 | /* Remember the top left coordinates of the block (first line of block). */ |
711 | iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1); | 728 | iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1); |
712 | if (~meta->flags & topLeft_GmPreMetaFlag) { | 729 | if (~meta->flags & topLeft_GmPreMetaFlag) { |
@@ -859,10 +876,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
859 | } | 876 | } |
860 | } | 877 | } |
861 | } | 878 | } |
879 | printf("[GmDocument] layout size: %zu runs (%zu bytes)\n", | ||
880 | size_Array(&d->layout), size_Array(&d->layout) * sizeof(iGmRun)); | ||
862 | } | 881 | } |
863 | 882 | ||
864 | void init_GmDocument(iGmDocument *d) { | 883 | void init_GmDocument(iGmDocument *d) { |
865 | d->format = gemini_GmDocumentFormat; | 884 | d->format = gemini_SourceFormat; |
885 | init_String(&d->unormSource); | ||
866 | init_String(&d->source); | 886 | init_String(&d->source); |
867 | init_String(&d->url); | 887 | init_String(&d->url); |
868 | init_String(&d->localHost); | 888 | init_String(&d->localHost); |
@@ -878,6 +898,8 @@ void init_GmDocument(iGmDocument *d) { | |||
878 | d->siteIcon = 0; | 898 | d->siteIcon = 0; |
879 | d->media = new_Media(); | 899 | d->media = new_Media(); |
880 | d->openURLs = NULL; | 900 | d->openURLs = NULL; |
901 | d->isPaletteValid = iFalse; | ||
902 | iZap(d->palette); | ||
881 | } | 903 | } |
882 | 904 | ||
883 | void deinit_GmDocument(iGmDocument *d) { | 905 | void deinit_GmDocument(iGmDocument *d) { |
@@ -893,6 +915,7 @@ void deinit_GmDocument(iGmDocument *d) { | |||
893 | deinit_String(&d->localHost); | 915 | deinit_String(&d->localHost); |
894 | deinit_String(&d->url); | 916 | deinit_String(&d->url); |
895 | deinit_String(&d->source); | 917 | deinit_String(&d->source); |
918 | deinit_String(&d->unormSource); | ||
896 | } | 919 | } |
897 | 920 | ||
898 | iMedia *media_GmDocument(iGmDocument *d) { | 921 | iMedia *media_GmDocument(iGmDocument *d) { |
@@ -903,6 +926,11 @@ const iMedia *constMedia_GmDocument(const iGmDocument *d) { | |||
903 | return d->media; | 926 | return d->media; |
904 | } | 927 | } |
905 | 928 | ||
929 | const iString *url_GmDocument(const iGmDocument *d) { | ||
930 | return &d->url; | ||
931 | } | ||
932 | |||
933 | #if 0 | ||
906 | void reset_GmDocument(iGmDocument *d) { | 934 | void reset_GmDocument(iGmDocument *d) { |
907 | clear_Media(d->media); | 935 | clear_Media(d->media); |
908 | clearLinks_GmDocument_(d); | 936 | clearLinks_GmDocument_(d); |
@@ -911,8 +939,11 @@ void reset_GmDocument(iGmDocument *d) { | |||
911 | clear_Array(&d->preMeta); | 939 | clear_Array(&d->preMeta); |
912 | clear_String(&d->url); | 940 | clear_String(&d->url); |
913 | clear_String(&d->localHost); | 941 | clear_String(&d->localHost); |
942 | clear_String(&d->source); | ||
943 | clear_String(&d->unormSource); | ||
914 | d->themeSeed = 0; | 944 | d->themeSeed = 0; |
915 | } | 945 | } |
946 | #endif | ||
916 | 947 | ||
917 | static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { | 948 | static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { |
918 | set_Color(tmQuoteIcon_ColorId, | 949 | set_Color(tmQuoteIcon_ColorId, |
@@ -1385,9 +1416,23 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1385 | } | 1416 | } |
1386 | printf("---\n"); | 1417 | printf("---\n"); |
1387 | #endif | 1418 | #endif |
1419 | /* Color functions operate on the global palette for convenience, but we may need to switch | ||
1420 | palettes on the fly if more than one GmDocument is being displayed simultaneously. */ | ||
1421 | memcpy(d->palette, get_Root()->tmPalette, sizeof(d->palette)); | ||
1422 | d->isPaletteValid = iTrue; | ||
1423 | } | ||
1424 | |||
1425 | void makePaletteGlobal_GmDocument(const iGmDocument *d) { | ||
1426 | if (d->isPaletteValid) { | ||
1427 | memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); | ||
1428 | } | ||
1388 | } | 1429 | } |
1389 | 1430 | ||
1390 | void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { | 1431 | void invalidatePalette_GmDocument(iGmDocument *d) { |
1432 | d->isPaletteValid = iFalse; | ||
1433 | } | ||
1434 | |||
1435 | void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { | ||
1391 | d->format = format; | 1436 | d->format = format; |
1392 | } | 1437 | } |
1393 | 1438 | ||
@@ -1413,6 +1458,9 @@ iBool updateOpenURLs_GmDocument(iGmDocument *d) { | |||
1413 | const iBool isOpen = contains_StringSet(d->openURLs, &link->url); | 1458 | const iBool isOpen = contains_StringSet(d->openURLs, &link->url); |
1414 | if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { | 1459 | if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { |
1415 | iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); | 1460 | iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); |
1461 | if (isOpen) { | ||
1462 | link->flags |= visited_GmLinkFlag; | ||
1463 | } | ||
1416 | wasChanged = iTrue; | 1464 | wasChanged = iTrue; |
1417 | } | 1465 | } |
1418 | } | 1466 | } |
@@ -1429,10 +1477,12 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1429 | iRangecc src = range_String(&d->source); | 1477 | iRangecc src = range_String(&d->source); |
1430 | iRangecc line = iNullRange; | 1478 | iRangecc line = iNullRange; |
1431 | iBool isPreformat = iFalse; | 1479 | iBool isPreformat = iFalse; |
1432 | if (d->format == plainText_GmDocumentFormat) { | 1480 | if (d->format == plainText_SourceFormat) { |
1433 | isPreformat = iTrue; /* Cannot be turned off. */ | 1481 | isPreformat = iTrue; /* Cannot be turned off. */ |
1434 | } | 1482 | } |
1435 | const int preTabWidth = 4; /* TODO: user-configurable parameter */ | 1483 | const int preTabWidth = 4; /* TODO: user-configurable parameter */ |
1484 | iBool wasNormalized = iFalse; | ||
1485 | iBool hasTabs = iFalse; | ||
1436 | while (nextSplit_Rangecc(src, "\n", &line)) { | 1486 | while (nextSplit_Rangecc(src, "\n", &line)) { |
1437 | if (isPreformat) { | 1487 | if (isPreformat) { |
1438 | /* Replace any tab characters with spaces for visualization. */ | 1488 | /* Replace any tab characters with spaces for visualization. */ |
@@ -1443,13 +1493,19 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1443 | while (numSpaces-- > 0) { | 1493 | while (numSpaces-- > 0) { |
1444 | appendCStrN_String(normalized, " ", 1); | 1494 | appendCStrN_String(normalized, " ", 1); |
1445 | } | 1495 | } |
1496 | hasTabs = iTrue; | ||
1497 | wasNormalized = iTrue; | ||
1446 | } | 1498 | } |
1447 | else if (*ch != '\r') { | 1499 | else if (*ch != '\v') { |
1448 | appendCStrN_String(normalized, ch, 1); | 1500 | appendCStrN_String(normalized, ch, 1); |
1449 | } | 1501 | } |
1502 | else { | ||
1503 | hasTabs = iTrue; | ||
1504 | wasNormalized = iTrue; | ||
1505 | } | ||
1450 | } | 1506 | } |
1451 | appendCStr_String(normalized, "\n"); | 1507 | appendCStr_String(normalized, "\n"); |
1452 | if (d->format == gemini_GmDocumentFormat && | 1508 | if (d->format == gemini_SourceFormat && |
1453 | lineType_GmDocument_(d, line) == preformatted_GmLineType) { | 1509 | lineType_GmDocument_(d, line) == preformatted_GmLineType) { |
1454 | isPreformat = iFalse; | 1510 | isPreformat = iFalse; |
1455 | } | 1511 | } |
@@ -1465,7 +1521,10 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1465 | int spaceCount = 0; | 1521 | int spaceCount = 0; |
1466 | for (const char *ch = line.start; ch != line.end; ch++) { | 1522 | for (const char *ch = line.start; ch != line.end; ch++) { |
1467 | char c = *ch; | 1523 | char c = *ch; |
1468 | if (c == '\r') continue; | 1524 | if (c == '\v') { |
1525 | wasNormalized = iTrue; | ||
1526 | continue; | ||
1527 | } | ||
1469 | if (isNormalizableSpace_(c)) { | 1528 | if (isNormalizableSpace_(c)) { |
1470 | if (isPrevSpace) { | 1529 | if (isPrevSpace) { |
1471 | if (++spaceCount == 8) { | 1530 | if (++spaceCount == 8) { |
@@ -1474,9 +1533,13 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1474 | popBack_Block(&normalized->chars); | 1533 | popBack_Block(&normalized->chars); |
1475 | pushBack_Block(&normalized->chars, '\t'); | 1534 | pushBack_Block(&normalized->chars, '\t'); |
1476 | } | 1535 | } |
1536 | wasNormalized = iTrue; | ||
1477 | continue; /* skip repeated spaces */ | 1537 | continue; /* skip repeated spaces */ |
1478 | } | 1538 | } |
1479 | c = ' '; | 1539 | if (c != ' ') { |
1540 | c = ' '; | ||
1541 | wasNormalized = iTrue; | ||
1542 | } | ||
1480 | isPrevSpace = iTrue; | 1543 | isPrevSpace = iTrue; |
1481 | } | 1544 | } |
1482 | else { | 1545 | else { |
@@ -1487,8 +1550,14 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1487 | } | 1550 | } |
1488 | appendCStr_String(normalized, "\n"); | 1551 | appendCStr_String(normalized, "\n"); |
1489 | } | 1552 | } |
1553 | printf("hasTabs: %d\n", hasTabs); | ||
1554 | printf("wasNormalized: %d\n", wasNormalized); | ||
1555 | fflush(stdout); | ||
1490 | set_String(&d->source, collect_String(normalized)); | 1556 | set_String(&d->source, collect_String(normalized)); |
1491 | normalize_String(&d->source); /* NFC */ | 1557 | normalize_String(&d->source); /* NFC */ |
1558 | printf("orig:%zu norm:%zu\n", size_String(&d->unormSource), size_String(&d->source)); | ||
1559 | /* normalized source has an extra newline at the end */ | ||
1560 | // iAssert(wasNormalized || equal_String(&d->unormSource, &d->source)); | ||
1492 | } | 1561 | } |
1493 | 1562 | ||
1494 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { | 1563 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { |
@@ -1499,8 +1568,18 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) { | |||
1499 | updateIconBasedOnUrl_GmDocument_(d); | 1568 | updateIconBasedOnUrl_GmDocument_(d); |
1500 | } | 1569 | } |
1501 | 1570 | ||
1502 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width) { | 1571 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width, |
1503 | set_String(&d->source, source); | 1572 | enum iGmDocumentUpdate updateType) { |
1573 | printf("[GmDocument] source update (%zu bytes), width:%d, final:%d\n", | ||
1574 | size_String(source), width, updateType == final_GmDocumentUpdate); | ||
1575 | if (size_String(source) == size_String(&d->unormSource)) { | ||
1576 | iAssert(equal_String(source, &d->unormSource)); | ||
1577 | printf("[GmDocument] source is unchanged!\n"); | ||
1578 | return; /* Nothing to do. */ | ||
1579 | } | ||
1580 | set_String(&d->unormSource, source); | ||
1581 | /* Normalize. */ | ||
1582 | set_String(&d->source, &d->unormSource); | ||
1504 | if (isNormalized_GmDocument_(d)) { | 1583 | if (isNormalized_GmDocument_(d)) { |
1505 | normalize_GmDocument(d); | 1584 | normalize_GmDocument(d); |
1506 | } | 1585 | } |
@@ -1602,6 +1681,14 @@ const iString *source_GmDocument(const iGmDocument *d) { | |||
1602 | return &d->source; | 1681 | return &d->source; |
1603 | } | 1682 | } |
1604 | 1683 | ||
1684 | size_t memorySize_GmDocument(const iGmDocument *d) { | ||
1685 | return size_String(&d->unormSource) + | ||
1686 | size_String(&d->source) + | ||
1687 | size_Array(&d->layout) * sizeof(iGmRun) + | ||
1688 | size_Array(&d->links) * sizeof(iGmLink) + | ||
1689 | memorySize_Media(d->media); | ||
1690 | } | ||
1691 | |||
1605 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { | 1692 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { |
1606 | const char * src = constBegin_String(&d->source); | 1693 | const char * src = constBegin_String(&d->source); |
1607 | const size_t startPos = (start ? start - src : 0); | 1694 | const size_t startPos = (start ? start - src : 0); |
diff --git a/src/gmdocument.h b/src/gmdocument.h index fcbb7e59..831459d8 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include "defs.h" | ||
25 | #include "gmutil.h" | 26 | #include "gmutil.h" |
26 | #include "media.h" | 27 | #include "media.h" |
27 | 28 | ||
@@ -125,12 +126,12 @@ enum iGmRunMediaType { | |||
125 | 126 | ||
126 | struct Impl_GmRun { | 127 | struct Impl_GmRun { |
127 | iRangecc text; | 128 | iRangecc text; |
129 | iRect bounds; /* used for hit testing, may extend to edges */ | ||
130 | iRect visBounds; /* actual visual bounds */ | ||
128 | uint8_t font; | 131 | uint8_t font; |
129 | uint8_t color; | 132 | uint8_t color; |
130 | uint8_t flags; | 133 | uint8_t flags; |
131 | uint8_t mediaType; | 134 | uint8_t mediaType; |
132 | iRect bounds; /* used for hit testing, may extend to edges */ | ||
133 | iRect visBounds; /* actual visual bounds */ | ||
134 | uint16_t preId; /* preformatted block ID (sequential) */ | 135 | uint16_t preId; /* preformatted block ID (sequential) */ |
135 | iGmLinkId linkId; /* zero for non-links */ | 136 | iGmLinkId linkId; /* zero for non-links */ |
136 | uint16_t mediaId; /* zero if not an image */ | 137 | uint16_t mediaId; /* zero if not an image */ |
@@ -148,34 +149,37 @@ iRangecc findLoc_GmRun (const iGmRun *, iInt2 pos); | |||
148 | iDeclareClass(GmDocument) | 149 | iDeclareClass(GmDocument) |
149 | iDeclareObjectConstruction(GmDocument) | 150 | iDeclareObjectConstruction(GmDocument) |
150 | 151 | ||
151 | enum iGmDocumentFormat { | ||
152 | undefined_GmDocumentFormat = -1, | ||
153 | gemini_GmDocumentFormat = 0, | ||
154 | plainText_GmDocumentFormat, | ||
155 | }; | ||
156 | |||
157 | enum iGmDocumentBanner { | 152 | enum iGmDocumentBanner { |
158 | none_GmDocumentBanner, | 153 | none_GmDocumentBanner, |
159 | siteDomain_GmDocumentBanner, | 154 | siteDomain_GmDocumentBanner, |
160 | certificateWarning_GmDocumentBanner, | 155 | certificateWarning_GmDocumentBanner, |
161 | }; | 156 | }; |
162 | 157 | ||
158 | enum iGmDocumentUpdate { | ||
159 | partial_GmDocumentUpdate, /* appending more content */ | ||
160 | final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ | ||
161 | }; | ||
162 | |||
163 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | 163 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); |
164 | void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); | 164 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); |
165 | void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); | 165 | void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); |
166 | void setWidth_GmDocument (iGmDocument *, int width); | 166 | void setWidth_GmDocument (iGmDocument *, int width); |
167 | void redoLayout_GmDocument (iGmDocument *); | 167 | void redoLayout_GmDocument (iGmDocument *); |
168 | iBool updateOpenURLs_GmDocument(iGmDocument *); | 168 | iBool updateOpenURLs_GmDocument(iGmDocument *); |
169 | void setUrl_GmDocument (iGmDocument *, const iString *url); | 169 | void setUrl_GmDocument (iGmDocument *, const iString *url); |
170 | void setSource_GmDocument (iGmDocument *, const iString *source, int width); | 170 | void setSource_GmDocument (iGmDocument *, const iString *source, int width, |
171 | enum iGmDocumentUpdate updateType); | ||
171 | void foldPre_GmDocument (iGmDocument *, uint16_t preId); | 172 | void foldPre_GmDocument (iGmDocument *, uint16_t preId); |
173 | void invalidatePalette_GmDocument(iGmDocument *); | ||
174 | void makePaletteGlobal_GmDocument(const iGmDocument *); /* copies document colors to the global palette */ | ||
172 | 175 | ||
173 | void reset_GmDocument (iGmDocument *); /* free images */ | 176 | //void reset_GmDocument (iGmDocument *); /* free images */ |
174 | 177 | ||
175 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); | 178 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); |
176 | 179 | ||
177 | iMedia * media_GmDocument (iGmDocument *); | 180 | iMedia * media_GmDocument (iGmDocument *); |
178 | const iMedia * constMedia_GmDocument (const iGmDocument *); | 181 | const iMedia * constMedia_GmDocument (const iGmDocument *); |
182 | const iString * url_GmDocument (const iGmDocument *); | ||
179 | 183 | ||
180 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, | 184 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, |
181 | iGmDocumentRenderFunc render, void *); /* includes partial overlaps */ | 185 | iGmDocumentRenderFunc render, void *); /* includes partial overlaps */ |
@@ -190,6 +194,7 @@ enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *); | |||
190 | const iString * bannerText_GmDocument (const iGmDocument *); | 194 | const iString * bannerText_GmDocument (const iGmDocument *); |
191 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ | 195 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
192 | const iString * source_GmDocument (const iGmDocument *); | 196 | const iString * source_GmDocument (const iGmDocument *); |
197 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ | ||
193 | 198 | ||
194 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); | 199 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); |
195 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); | 200 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); |
diff --git a/src/gmtypesetter.c b/src/gmtypesetter.c new file mode 100644 index 00000000..29a1bd93 --- /dev/null +++ b/src/gmtypesetter.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "gmtypesetter.h" | ||
24 | #include "gmdocument.h" | ||
25 | |||
diff --git a/src/gmtypesetter.h b/src/gmtypesetter.h new file mode 100644 index 00000000..aba351dd --- /dev/null +++ b/src/gmtypesetter.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #pragma once | ||
24 | |||
25 | #include "defs.h" | ||
26 | |||
27 | #include <the_Foundation/array.h> | ||
28 | #include <the_Foundation/string.h> | ||
29 | #include <the_Foundation/vec2.h> | ||
30 | |||
31 | /* GmTypesetter has two jobs: it normalizes incoming source text, and typesets it as a | ||
32 | sequence of GmRuns. New data can be appended progressively. */ | ||
33 | |||
34 | iDeclareType(GmTypesetter) | ||
35 | iDeclareTypeConstruction(GmTypesetter) | ||
36 | |||
37 | void reset_GmTypesetter (iGmTypesetter *, enum iSourceFormat format); | ||
38 | void setWidth_GmTypesetter (iGmTypesetter *, int width); | ||
39 | void addInput_GmTypesetter (iGmTypesetter *, const iString *source); | ||
40 | iBool getRuns_GmTypesetter (iGmTypesetter *, iArray *runs_out); /* returns false when no output generated */ | ||
41 | void skip_GmTypesetter (iGmTypesetter *, int ySkip); | ||
diff --git a/src/gmutil.c b/src/gmutil.c index 3ca93901..7a1ae938 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -556,7 +556,7 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { | |||
556 | iString *head = newRange_String( | 556 | iString *head = newRange_String( |
557 | (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); | 557 | (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); |
558 | format_String(cmd, | 558 | format_String(cmd, |
559 | "open newtab:%d gotourlheading:%s url:%s", | 559 | "open fromsidebar:1 newtab:%d gotourlheading:%s url:%s", |
560 | newTab, | 560 | newTab, |
561 | cstr_String(head), | 561 | cstr_String(head), |
562 | cstr_Rangecc((iRangecc){ constBegin_String(url), | 562 | cstr_Rangecc((iRangecc){ constBegin_String(url), |
@@ -564,7 +564,7 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { | |||
564 | delete_String(head); | 564 | delete_String(head); |
565 | } | 565 | } |
566 | else { | 566 | else { |
567 | format_String(cmd, "open newtab:%d url:%s", newTab, cstr_String(url)); | 567 | format_String(cmd, "open fromsidebar:1 newtab:%d url:%s", newTab, cstr_String(url)); |
568 | } | 568 | } |
569 | return cmd; | 569 | return cmd; |
570 | } | 570 | } |
diff --git a/src/history.c b/src/history.c index ed8e6725..2cea393d 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -34,11 +34,14 @@ static const size_t maxStack_History_ = 50; /* back/forward navigable items */ | |||
34 | 34 | ||
35 | void init_RecentUrl(iRecentUrl *d) { | 35 | void init_RecentUrl(iRecentUrl *d) { |
36 | init_String(&d->url); | 36 | init_String(&d->url); |
37 | d->normScrollY = 0; | 37 | d->normScrollY = 0; |
38 | d->cachedResponse = NULL; | 38 | d->cachedResponse = NULL; |
39 | d->cachedDoc = NULL; | ||
40 | d->flags.openedFromSidebar = iFalse; | ||
39 | } | 41 | } |
40 | 42 | ||
41 | void deinit_RecentUrl(iRecentUrl *d) { | 43 | void deinit_RecentUrl(iRecentUrl *d) { |
44 | iRelease(d->cachedDoc); | ||
42 | deinit_String(&d->url); | 45 | deinit_String(&d->url); |
43 | delete_GmResponse(d->cachedResponse); | 46 | delete_GmResponse(d->cachedResponse); |
44 | } | 47 | } |
@@ -48,11 +51,30 @@ iDefineTypeConstruction(RecentUrl) | |||
48 | iRecentUrl *copy_RecentUrl(const iRecentUrl *d) { | 51 | iRecentUrl *copy_RecentUrl(const iRecentUrl *d) { |
49 | iRecentUrl *copy = new_RecentUrl(); | 52 | iRecentUrl *copy = new_RecentUrl(); |
50 | set_String(©->url, &d->url); | 53 | set_String(©->url, &d->url); |
51 | copy->normScrollY = d->normScrollY; | 54 | copy->normScrollY = d->normScrollY; |
52 | copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL; | 55 | copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL; |
56 | copy->cachedDoc = ref_Object(d->cachedDoc); | ||
57 | copy->flags = d->flags; | ||
53 | return copy; | 58 | return copy; |
54 | } | 59 | } |
55 | 60 | ||
61 | size_t cacheSize_RecentUrl(const iRecentUrl *d) { | ||
62 | size_t size = 0; | ||
63 | if (d->cachedResponse) { | ||
64 | size += size_String(&d->cachedResponse->meta); | ||
65 | size += size_Block(&d->cachedResponse->body); | ||
66 | } | ||
67 | return size; | ||
68 | } | ||
69 | |||
70 | size_t memorySize_RecentUrl(const iRecentUrl *d) { | ||
71 | size_t size = cacheSize_RecentUrl(d); | ||
72 | if (d->cachedDoc) { | ||
73 | size += memorySize_GmDocument(d->cachedDoc); | ||
74 | } | ||
75 | return size; | ||
76 | } | ||
77 | |||
56 | /*----------------------------------------------------------------------------------------------*/ | 78 | /*----------------------------------------------------------------------------------------------*/ |
57 | 79 | ||
58 | struct Impl_History { | 80 | struct Impl_History { |
@@ -88,24 +110,45 @@ iHistory *copy_History(const iHistory *d) { | |||
88 | return copy; | 110 | return copy; |
89 | } | 111 | } |
90 | 112 | ||
113 | iMemInfo memoryUsage_History(const iHistory *d) { | ||
114 | iMemInfo mem = { 0, 0 }; | ||
115 | iConstForEach(Array, i, &d->recent) { | ||
116 | const iRecentUrl *item = i.value; | ||
117 | mem.cacheSize += cacheSize_RecentUrl(item); | ||
118 | mem.memorySize += memorySize_RecentUrl(item); | ||
119 | } | ||
120 | return mem; | ||
121 | } | ||
122 | |||
91 | iString *debugInfo_History(const iHistory *d) { | 123 | iString *debugInfo_History(const iHistory *d) { |
92 | iString *str = new_String(); | 124 | iString *str = new_String(); |
93 | format_String(str, | 125 | format_String(str, |
94 | "```\n" | 126 | "```\n" |
95 | "Idx | Size | SP%% | URL\n" | 127 | "Idx | Cache | Memory | SP%% | URL\n" |
96 | "----+---------+-----+-----\n"); | 128 | "----+---------+----------+-----+-----\n"); |
97 | size_t totalSize = 0; | 129 | size_t totalCache = 0; |
130 | size_t totalMemory = 0; | ||
98 | iConstForEach(Array, i, &d->recent) { | 131 | iConstForEach(Array, i, &d->recent) { |
99 | const iRecentUrl *item = i.value; | 132 | const iRecentUrl *item = i.value; |
100 | appendFormat_String( | 133 | appendFormat_String( |
101 | str, " %2zu | ", size_Array(&d->recent) - index_ArrayConstIterator(&i) - 1); | 134 | str, " %2zu | ", size_Array(&d->recent) - index_ArrayConstIterator(&i) - 1); |
102 | if (item->cachedResponse) { | 135 | const size_t cacheSize = cacheSize_RecentUrl(item); |
103 | appendFormat_String(str, "%7zu", size_Block(&item->cachedResponse->body)); | 136 | const size_t memSize = memorySize_RecentUrl(item); |
104 | totalSize += size_Block(&item->cachedResponse->body); | 137 | if (cacheSize) { |
138 | appendFormat_String(str, "%7zu", cacheSize); | ||
139 | totalCache += cacheSize; | ||
105 | } | 140 | } |
106 | else { | 141 | else { |
107 | appendFormat_String(str, " --"); | 142 | appendFormat_String(str, " --"); |
108 | } | 143 | } |
144 | appendCStr_String(str, " | "); | ||
145 | if (memSize) { | ||
146 | appendFormat_String(str, "%8zu", memSize); | ||
147 | totalMemory += memSize; | ||
148 | } | ||
149 | else { | ||
150 | appendFormat_String(str, " --"); | ||
151 | } | ||
109 | appendFormat_String(str, | 152 | appendFormat_String(str, |
110 | " | %3d | %s\n", | 153 | " | %3d | %s\n", |
111 | iRound(100.0f * item->normScrollY), | 154 | iRound(100.0f * item->normScrollY), |
@@ -114,8 +157,10 @@ iString *debugInfo_History(const iHistory *d) { | |||
114 | appendFormat_String(str, "\n```\n"); | 157 | appendFormat_String(str, "\n```\n"); |
115 | appendFormat_String(str, | 158 | appendFormat_String(str, |
116 | "Total cached data: %.3f MB\n" | 159 | "Total cached data: %.3f MB\n" |
160 | "Total memory usage: %.3f MB\n" | ||
117 | "Navigation position: %zu\n\n", | 161 | "Navigation position: %zu\n\n", |
118 | totalSize / 1.0e6f, | 162 | totalCache / 1.0e6f, |
163 | totalMemory / 1.0e6f, | ||
119 | d->recentPos); | 164 | d->recentPos); |
120 | return str; | 165 | return str; |
121 | } | 166 | } |
@@ -128,6 +173,7 @@ void serialize_History(const iHistory *d, iStream *outs) { | |||
128 | const iRecentUrl *item = i.value; | 173 | const iRecentUrl *item = i.value; |
129 | serialize_String(&item->url, outs); | 174 | serialize_String(&item->url, outs); |
130 | write32_Stream(outs, item->normScrollY * 1.0e6f); | 175 | write32_Stream(outs, item->normScrollY * 1.0e6f); |
176 | writeU16_Stream(outs, item->flags.openedFromSidebar ? iBit(1) : 0); | ||
131 | if (item->cachedResponse) { | 177 | if (item->cachedResponse) { |
132 | write8_Stream(outs, 1); | 178 | write8_Stream(outs, 1); |
133 | serialize_GmResponse(item->cachedResponse, outs); | 179 | serialize_GmResponse(item->cachedResponse, outs); |
@@ -149,6 +195,12 @@ void deserialize_History(iHistory *d, iStream *ins) { | |||
149 | init_RecentUrl(&item); | 195 | init_RecentUrl(&item); |
150 | deserialize_String(&item.url, ins); | 196 | deserialize_String(&item.url, ins); |
151 | item.normScrollY = (float) read32_Stream(ins) / 1.0e6f; | 197 | item.normScrollY = (float) read32_Stream(ins) / 1.0e6f; |
198 | if (version_Stream(ins) >= addedRecentUrlFlags_FileVersion) { | ||
199 | uint16_t flags = readU16_Stream(ins); | ||
200 | if (flags & iBit(1)) { | ||
201 | item.flags.openedFromSidebar = iTrue; | ||
202 | } | ||
203 | } | ||
152 | if (read8_Stream(ins)) { | 204 | if (read8_Stream(ins)) { |
153 | item.cachedResponse = new_GmResponse(); | 205 | item.cachedResponse = new_GmResponse(); |
154 | deserialize_GmResponse(item.cachedResponse, ins); | 206 | deserialize_GmResponse(item.cachedResponse, ins); |
@@ -241,6 +293,39 @@ void add_History(iHistory *d, const iString *url ){ | |||
241 | unlock_Mutex(d->mtx); | 293 | unlock_Mutex(d->mtx); |
242 | } | 294 | } |
243 | 295 | ||
296 | iBool preceding_History(iHistory *d, iRecentUrl *recent_out) { | ||
297 | iBool ok = iFalse; | ||
298 | lock_Mutex(d->mtx); | ||
299 | if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { | ||
300 | const iRecentUrl *recent = constAt_Array(&d->recent, size_Array(&d->recent) - 1 - | ||
301 | (d->recentPos + 1)); | ||
302 | set_String(&recent_out->url, &recent->url); | ||
303 | recent_out->normScrollY = recent->normScrollY; | ||
304 | iChangeRef(recent_out->cachedDoc, recent->cachedDoc); | ||
305 | /* Cached response is not returned, would involve a deep copy. */ | ||
306 | ok = iTrue; | ||
307 | } | ||
308 | unlock_Mutex(d->mtx); | ||
309 | return ok; | ||
310 | } | ||
311 | |||
312 | #if 0 | ||
313 | iBool following_History(iHistory *d, iRecentUrl *recent_out) { | ||
314 | iBool ok = iFalse; | ||
315 | lock_Mutex(d->mtx); | ||
316 | if (!isEmpty_Array(&d->recent) && d->recentPos > 0) { | ||
317 | const iRecentUrl *recent = constAt_Array(&d->recent, d->recentPos - 1); | ||
318 | set_String(&recent_out->url, &recent->url); | ||
319 | recent_out->normScrollY = recent->normScrollY; | ||
320 | recent_out->cachedDoc = ref_Object(recent->cachedDoc); | ||
321 | /* Cached response is not returned, would involve a deep copy. */ | ||
322 | ok = iTrue; | ||
323 | } | ||
324 | unlock_Mutex(d->mtx); | ||
325 | return ok; | ||
326 | } | ||
327 | #endif | ||
328 | |||
244 | iBool goBack_History(iHistory *d) { | 329 | iBool goBack_History(iHistory *d) { |
245 | lock_Mutex(d->mtx); | 330 | lock_Mutex(d->mtx); |
246 | if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { | 331 | if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { |
@@ -302,19 +387,42 @@ void setCachedResponse_History(iHistory *d, const iGmResponse *response) { | |||
302 | unlock_Mutex(d->mtx); | 387 | unlock_Mutex(d->mtx); |
303 | } | 388 | } |
304 | 389 | ||
390 | void setCachedDocument_History(iHistory *d, iGmDocument *doc, iBool openedFromSidebar) { | ||
391 | lock_Mutex(d->mtx); | ||
392 | iRecentUrl *item = mostRecentUrl_History(d); | ||
393 | if (item) { | ||
394 | iAssert(equal_String(url_GmDocument(doc), &item->url)); | ||
395 | item->flags.openedFromSidebar = openedFromSidebar; | ||
396 | if (item->cachedDoc != doc) { | ||
397 | iRelease(item->cachedDoc); | ||
398 | item->cachedDoc = ref_Object(doc); | ||
399 | } | ||
400 | } | ||
401 | unlock_Mutex(d->mtx); | ||
402 | } | ||
403 | |||
305 | size_t cacheSize_History(const iHistory *d) { | 404 | size_t cacheSize_History(const iHistory *d) { |
306 | size_t cached = 0; | 405 | size_t cached = 0; |
307 | lock_Mutex(d->mtx); | 406 | lock_Mutex(d->mtx); |
308 | iConstForEach(Array, i, &d->recent) { | 407 | iConstForEach(Array, i, &d->recent) { |
309 | const iRecentUrl *url = i.value; | 408 | const iRecentUrl *url = i.value; |
310 | if (url->cachedResponse) { | 409 | cached += cacheSize_RecentUrl(url); |
311 | cached += size_Block(&url->cachedResponse->body); | ||
312 | } | ||
313 | } | 410 | } |
314 | unlock_Mutex(d->mtx); | 411 | unlock_Mutex(d->mtx); |
315 | return cached; | 412 | return cached; |
316 | } | 413 | } |
317 | 414 | ||
415 | size_t memorySize_History(const iHistory *d) { | ||
416 | size_t bytes = 0; | ||
417 | lock_Mutex(d->mtx); | ||
418 | iConstForEach(Array, i, &d->recent) { | ||
419 | const iRecentUrl *url = i.value; | ||
420 | bytes += memorySize_RecentUrl(url); | ||
421 | } | ||
422 | unlock_Mutex(d->mtx); | ||
423 | return bytes; | ||
424 | } | ||
425 | |||
318 | void clearCache_History(iHistory *d) { | 426 | void clearCache_History(iHistory *d) { |
319 | lock_Mutex(d->mtx); | 427 | lock_Mutex(d->mtx); |
320 | iForEach(Array, i, &d->recent) { | 428 | iForEach(Array, i, &d->recent) { |
@@ -323,6 +431,7 @@ void clearCache_History(iHistory *d) { | |||
323 | delete_GmResponse(url->cachedResponse); | 431 | delete_GmResponse(url->cachedResponse); |
324 | url->cachedResponse = NULL; | 432 | url->cachedResponse = NULL; |
325 | } | 433 | } |
434 | iReleasePtr(&url->cachedDoc); /* release all cached documents and media as well */ | ||
326 | } | 435 | } |
327 | unlock_Mutex(d->mtx); | 436 | unlock_Mutex(d->mtx); |
328 | } | 437 | } |
@@ -338,7 +447,7 @@ size_t pruneLeastImportant_History(iHistory *d) { | |||
338 | const iRecentUrl *url = i.value; | 447 | const iRecentUrl *url = i.value; |
339 | if (url->cachedResponse) { | 448 | if (url->cachedResponse) { |
340 | const double urlScore = | 449 | const double urlScore = |
341 | size_Block(&url->cachedResponse->body) * | 450 | cacheSize_RecentUrl(url) * |
342 | pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25); | 451 | pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25); |
343 | if (urlScore > score) { | 452 | if (urlScore > score) { |
344 | chosen = index_ArrayConstIterator(&i); | 453 | chosen = index_ArrayConstIterator(&i); |
@@ -348,14 +457,60 @@ size_t pruneLeastImportant_History(iHistory *d) { | |||
348 | } | 457 | } |
349 | if (chosen != iInvalidPos) { | 458 | if (chosen != iInvalidPos) { |
350 | iRecentUrl *url = at_Array(&d->recent, chosen); | 459 | iRecentUrl *url = at_Array(&d->recent, chosen); |
351 | delta = size_Block(&url->cachedResponse->body); | 460 | delta = cacheSize_RecentUrl(url); |
352 | delete_GmResponse(url->cachedResponse); | 461 | delete_GmResponse(url->cachedResponse); |
353 | url->cachedResponse = NULL; | 462 | url->cachedResponse = NULL; |
463 | iReleasePtr(&url->cachedDoc); | ||
354 | } | 464 | } |
355 | unlock_Mutex(d->mtx); | 465 | unlock_Mutex(d->mtx); |
356 | return delta; | 466 | return delta; |
357 | } | 467 | } |
358 | 468 | ||
469 | size_t pruneLeastImportantMemory_History(iHistory *d) { | ||
470 | size_t delta = 0; | ||
471 | size_t chosen = iInvalidPos; | ||
472 | double score = 0.0f; | ||
473 | iTime now; | ||
474 | initCurrent_Time(&now); | ||
475 | lock_Mutex(d->mtx); | ||
476 | iConstForEach(Array, i, &d->recent) { | ||
477 | const iRecentUrl *url = i.value; | ||
478 | if (d->recentPos == size_Array(&d->recent) - index_ArrayConstIterator(&i) - 1) { | ||
479 | continue; /* Not the current navigation position. */ | ||
480 | } | ||
481 | if (url->cachedDoc) { | ||
482 | const double urlScore = | ||
483 | memorySize_RecentUrl(url) * | ||
484 | (url->cachedResponse | ||
485 | ? pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25) | ||
486 | : 1.0); | ||
487 | if (urlScore > score) { | ||
488 | chosen = index_ArrayConstIterator(&i); | ||
489 | score = urlScore; | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | if (chosen != iInvalidPos) { | ||
494 | iRecentUrl *url = at_Array(&d->recent, chosen); | ||
495 | const size_t before = memorySize_RecentUrl(url); | ||
496 | iReleasePtr(&url->cachedDoc); | ||
497 | delta = before - memorySize_RecentUrl(url); | ||
498 | } | ||
499 | unlock_Mutex(d->mtx); | ||
500 | return delta; | ||
501 | } | ||
502 | |||
503 | void invalidateTheme_History(iHistory *d) { | ||
504 | lock_Mutex(d->mtx); | ||
505 | iForEach(Array, i, &d->recent) { | ||
506 | iRecentUrl *r = i.value; | ||
507 | if (r->cachedDoc) { | ||
508 | invalidatePalette_GmDocument(r->cachedDoc); | ||
509 | } | ||
510 | } | ||
511 | unlock_Mutex(d->mtx); | ||
512 | } | ||
513 | |||
359 | const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { | 514 | const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { |
360 | iStringArray *urls = iClob(new_StringArray()); | 515 | iStringArray *urls = iClob(new_StringArray()); |
361 | lock_Mutex(d->mtx); | 516 | lock_Mutex(d->mtx); |
diff --git a/src/history.h b/src/history.h index 164a61d6..7dad72df 100644 --- a/src/history.h +++ b/src/history.h | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include "gmdocument.h" | ||
25 | #include "gmrequest.h" | 26 | #include "gmrequest.h" |
26 | 27 | ||
27 | #include <the_Foundation/ptrarray.h> | 28 | #include <the_Foundation/ptrarray.h> |
@@ -37,6 +38,17 @@ struct Impl_RecentUrl { | |||
37 | iString url; | 38 | iString url; |
38 | float normScrollY; /* normalized to document height */ | 39 | float normScrollY; /* normalized to document height */ |
39 | iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ | 40 | iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ |
41 | iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */ | ||
42 | struct { | ||
43 | uint8_t openedFromSidebar : 1; | ||
44 | } flags; | ||
45 | }; | ||
46 | |||
47 | iDeclareType(MemInfo) | ||
48 | |||
49 | struct Impl_MemInfo { | ||
50 | size_t cacheSize; /* number of bytes stored persistently */ | ||
51 | size_t memorySize; /* number of bytes stored in RAM */ | ||
40 | }; | 52 | }; |
41 | 53 | ||
42 | /*----------------------------------------------------------------------------------------------*/ | 54 | /*----------------------------------------------------------------------------------------------*/ |
@@ -51,13 +63,19 @@ void clear_History (iHistory *); | |||
51 | void add_History (iHistory *, const iString *url); | 63 | void add_History (iHistory *, const iString *url); |
52 | void replace_History (iHistory *, const iString *url); | 64 | void replace_History (iHistory *, const iString *url); |
53 | void setCachedResponse_History (iHistory *, const iGmResponse *response); | 65 | void setCachedResponse_History (iHistory *, const iGmResponse *response); |
66 | void setCachedDocument_History (iHistory *, iGmDocument *doc, iBool openedFromSidebar); | ||
54 | iBool goBack_History (iHistory *); | 67 | iBool goBack_History (iHistory *); |
55 | iBool goForward_History (iHistory *); | 68 | iBool goForward_History (iHistory *); |
69 | iBool preceding_History (iHistory *d, iRecentUrl *recent_out); | ||
70 | //iBool following_History (iHistory *d, iRecentUrl *recent_out); | ||
56 | iRecentUrl *recentUrl_History (iHistory *, size_t pos); | 71 | iRecentUrl *recentUrl_History (iHistory *, size_t pos); |
57 | iRecentUrl *mostRecentUrl_History (iHistory *); | 72 | iRecentUrl *mostRecentUrl_History (iHistory *); |
58 | iRecentUrl *findUrl_History (iHistory *, const iString *url); | 73 | iRecentUrl *findUrl_History (iHistory *, const iString *url); |
59 | void clearCache_History (iHistory *); | 74 | |
60 | size_t pruneLeastImportant_History (iHistory *); | 75 | void clearCache_History (iHistory *); |
76 | size_t pruneLeastImportant_History (iHistory *); | ||
77 | size_t pruneLeastImportantMemory_History (iHistory *); | ||
78 | void invalidateTheme_History (iHistory *); /* theme has changed, cached contents need updating */ | ||
61 | 79 | ||
62 | iBool atLatest_History (const iHistory *); | 80 | iBool atLatest_History (const iHistory *); |
63 | iBool atOldest_History (const iHistory *); | 81 | iBool atOldest_History (const iHistory *); |
@@ -73,6 +91,7 @@ const iRecentUrl * | |||
73 | const iGmResponse * | 91 | const iGmResponse * |
74 | cachedResponse_History (const iHistory *); | 92 | cachedResponse_History (const iHistory *); |
75 | size_t cacheSize_History (const iHistory *); | 93 | size_t cacheSize_History (const iHistory *); |
94 | size_t memorySize_History (const iHistory *); | ||
76 | 95 | ||
77 | iString * debugInfo_History (const iHistory *); | 96 | iString * debugInfo_History (const iHistory *); |
78 | 97 | iMemInfo memoryUsage_History (const iHistory *); | |
@@ -28,6 +28,7 @@ iDeclareType(Window) | |||
28 | 28 | ||
29 | enum iHapticEffect { | 29 | enum iHapticEffect { |
30 | tap_HapticEffect, | 30 | tap_HapticEffect, |
31 | gentleTap_HapticEffect, | ||
31 | }; | 32 | }; |
32 | 33 | ||
33 | void setupApplication_iOS (void); | 34 | void setupApplication_iOS (void); |
@@ -35,6 +36,7 @@ void setupWindow_iOS (iWindow *window); | |||
35 | iBool processEvent_iOS (const SDL_Event *); | 36 | iBool processEvent_iOS (const SDL_Event *); |
36 | void playHapticEffect_iOS (enum iHapticEffect effect); | 37 | void playHapticEffect_iOS (enum iHapticEffect effect); |
37 | void exportDownloadedFile_iOS(const iString *path); | 38 | void exportDownloadedFile_iOS(const iString *path); |
39 | void pickFileForOpening_iOS (void); | ||
38 | 40 | ||
39 | iBool isPhone_iOS (void); | 41 | iBool isPhone_iOS (void); |
40 | void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); | 42 | void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); |
@@ -55,3 +57,6 @@ double currentTime_AVFAudioPlayer (const iAVFAudioPlayer *); | |||
55 | double duration_AVFAudioPlayer (const iAVFAudioPlayer *); | 57 | double duration_AVFAudioPlayer (const iAVFAudioPlayer *); |
56 | iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); | 58 | iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); |
57 | iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); | 59 | iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); |
60 | |||
61 | void clearNowPlayingInfo_iOS (void); | ||
62 | void updateNowPlayingInfo_iOS (void); | ||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "ios.h" | 23 | #include "ios.h" |
24 | #include "app.h" | 24 | #include "app.h" |
25 | #include "audio/player.h" | ||
25 | #include "ui/command.h" | 26 | #include "ui/command.h" |
26 | #include "ui/window.h" | 27 | #include "ui/window.h" |
27 | 28 | ||
@@ -32,9 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
32 | #include <SDL_syswm.h> | 33 | #include <SDL_syswm.h> |
33 | #include <SDL_timer.h> | 34 | #include <SDL_timer.h> |
34 | 35 | ||
35 | #import <UIKit/UIKit.h> | ||
36 | #import <CoreHaptics/CoreHaptics.h> | ||
37 | #import <AVFAudio/AVFAudio.h> | 36 | #import <AVFAudio/AVFAudio.h> |
37 | #import <CoreHaptics/CoreHaptics.h> | ||
38 | #import <UIKit/UIKit.h> | ||
39 | #import <MediaPlayer/MediaPlayer.h> | ||
38 | 40 | ||
39 | static iBool isSystemDarkMode_ = iFalse; | 41 | static iBool isSystemDarkMode_ = iFalse; |
40 | static iBool isPhone_ = iFalse; | 42 | static iBool isPhone_ = iFalse; |
@@ -64,6 +66,7 @@ API_AVAILABLE(ios(13.0)) | |||
64 | @interface HapticState : NSObject | 66 | @interface HapticState : NSObject |
65 | @property (nonatomic, strong) CHHapticEngine *engine; | 67 | @property (nonatomic, strong) CHHapticEngine *engine; |
66 | @property (nonatomic, strong) NSDictionary *tapDef; | 68 | @property (nonatomic, strong) NSDictionary *tapDef; |
69 | @property (nonatomic, strong) NSDictionary *gentleTapDef; | ||
67 | @end | 70 | @end |
68 | 71 | ||
69 | @implementation HapticState | 72 | @implementation HapticState |
@@ -105,26 +108,47 @@ API_AVAILABLE(ios(13.0)) | |||
105 | CHHapticPatternKeyEvent: @{ | 108 | CHHapticPatternKeyEvent: @{ |
106 | CHHapticPatternKeyEventType: CHHapticEventTypeHapticTransient, | 109 | CHHapticPatternKeyEventType: CHHapticEventTypeHapticTransient, |
107 | CHHapticPatternKeyTime: @0.0, | 110 | CHHapticPatternKeyTime: @0.0, |
108 | CHHapticPatternKeyEventDuration:@0.1 | 111 | CHHapticPatternKeyEventDuration:@0.1, |
112 | CHHapticPatternKeyEventParameters: @[ | ||
113 | @{ | ||
114 | CHHapticPatternKeyParameterID: CHHapticEventParameterIDHapticIntensity, | ||
115 | CHHapticPatternKeyParameterValue: @1.0 | ||
116 | } | ||
117 | ] | ||
118 | }, | ||
119 | }, | ||
120 | ] | ||
121 | }; | ||
122 | self.gentleTapDef = @{ | ||
123 | CHHapticPatternKeyPattern: | ||
124 | @[ | ||
125 | @{ | ||
126 | CHHapticPatternKeyEvent: @{ | ||
127 | CHHapticPatternKeyEventType: CHHapticEventTypeHapticTransient, | ||
128 | CHHapticPatternKeyTime: @0.0, | ||
129 | CHHapticPatternKeyEventDuration:@0.1, | ||
130 | CHHapticPatternKeyEventParameters: @[ | ||
131 | @{ | ||
132 | CHHapticPatternKeyParameterID: CHHapticEventParameterIDHapticIntensity, | ||
133 | CHHapticPatternKeyParameterValue: @0.33 | ||
134 | } | ||
135 | ] | ||
109 | }, | 136 | }, |
110 | }, | 137 | }, |
111 | ], | 138 | ] |
112 | }; | 139 | }; |
113 | } | 140 | } |
114 | 141 | ||
115 | -(void)playTapEffect { | 142 | |
143 | -(void)playHapticEffect:(NSDictionary *)def { | ||
116 | NSError *error = nil; | 144 | NSError *error = nil; |
117 | CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:self.tapDef | 145 | CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:def |
118 | error:&error]; | 146 | error:&error]; |
119 | // TODO: Check the error. | 147 | // TODO: Check the error. |
120 | id<CHHapticPatternPlayer> player = [self.engine createPlayerWithPattern:pattern error:&error]; | 148 | id<CHHapticPatternPlayer> player = [self.engine createPlayerWithPattern:pattern error:&error]; |
121 | // TODO: Check the error. | 149 | // TODO: Check the error. |
122 | [self.engine startWithCompletionHandler:^(NSError *err){ | 150 | [self.engine startWithCompletionHandler:^(NSError *err){ |
123 | if (err == nil) { | 151 | if (err == nil) { |
124 | /* Just keep it running. */ | ||
125 | // [self.engine notifyWhenPlayersFinished:^(NSError * _Nullable error) { | ||
126 | // return CHHapticEngineFinishedActionStopEngine; | ||
127 | // }]; | ||
128 | NSError *startError = nil; | 152 | NSError *startError = nil; |
129 | [player startAtTime:0.0 error:&startError]; | 153 | [player startAtTime:0.0 error:&startError]; |
130 | } | 154 | } |
@@ -181,11 +205,22 @@ static AppState *appState_; | |||
181 | 205 | ||
182 | - (void)documentPicker:(UIDocumentPickerViewController *)controller | 206 | - (void)documentPicker:(UIDocumentPickerViewController *)controller |
183 | didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { | 207 | didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { |
184 | [self removeSavedFile]; | 208 | if (fileBeingSaved) { |
209 | [self removeSavedFile]; | ||
210 | } | ||
211 | else { | ||
212 | /* A file is being opened. */ | ||
213 | NSURL *url = [urls firstObject]; | ||
214 | iString *path = localFilePathFromUrl_String(collectNewCStr_String([[url absoluteString] | ||
215 | UTF8String])); | ||
216 | postCommandf_App("file.open temp:1 path:%s", cstrCollect_String(path)); | ||
217 | } | ||
185 | } | 218 | } |
186 | 219 | ||
187 | - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { | 220 | - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { |
188 | [self removeSavedFile]; | 221 | if (fileBeingSaved) { |
222 | [self removeSavedFile]; | ||
223 | } | ||
189 | } | 224 | } |
190 | 225 | ||
191 | -(void)keyboardOnScreen:(NSNotification *)notification { | 226 | -(void)keyboardOnScreen:(NSNotification *)notification { |
@@ -230,6 +265,55 @@ void setupApplication_iOS(void) { | |||
230 | name:UIKeyboardWillHideNotification | 265 | name:UIKeyboardWillHideNotification |
231 | object:nil]; | 266 | object:nil]; |
232 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; | 267 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; |
268 | /* Media player remote controls. */ | ||
269 | MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; | ||
270 | [[commandCenter pauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
271 | iPlayer *player = active_Player(); | ||
272 | if (player) { | ||
273 | setPaused_Player(player, iTrue); | ||
274 | return MPRemoteCommandHandlerStatusSuccess; | ||
275 | } | ||
276 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
277 | }]; | ||
278 | [[commandCenter playCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
279 | iPlayer *player = active_Player(); | ||
280 | if (player) { | ||
281 | if (isPaused_Player(player)) { | ||
282 | setPaused_Player(player, iFalse); | ||
283 | } | ||
284 | else { | ||
285 | start_Player(player); | ||
286 | } | ||
287 | return MPRemoteCommandHandlerStatusSuccess; | ||
288 | } | ||
289 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
290 | }]; | ||
291 | [[commandCenter stopCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
292 | iPlayer *player = active_Player(); | ||
293 | if (player) { | ||
294 | stop_Player(player); | ||
295 | return MPRemoteCommandHandlerStatusSuccess; | ||
296 | } | ||
297 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
298 | }]; | ||
299 | [[commandCenter togglePlayPauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
300 | iPlayer *player = active_Player(); | ||
301 | if (player) { | ||
302 | setPaused_Player(player, !isPaused_Player(player)); | ||
303 | return MPRemoteCommandHandlerStatusSuccess; | ||
304 | } | ||
305 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
306 | }]; | ||
307 | [[commandCenter nextTrackCommand] setEnabled:NO]; | ||
308 | [[commandCenter previousTrackCommand] setEnabled:NO]; | ||
309 | [[commandCenter changeRepeatModeCommand] setEnabled:NO]; | ||
310 | [[commandCenter changeShuffleModeCommand] setEnabled:NO]; | ||
311 | [[commandCenter changePlaybackRateCommand] setEnabled:NO]; | ||
312 | [[commandCenter seekForwardCommand] setEnabled:NO]; | ||
313 | [[commandCenter seekBackwardCommand] setEnabled:NO]; | ||
314 | [[commandCenter skipForwardCommand] setEnabled:NO]; | ||
315 | [[commandCenter skipBackwardCommand] setEnabled:NO]; | ||
316 | [[commandCenter changePlaybackPositionCommand] setEnabled:NO]; | ||
233 | } | 317 | } |
234 | 318 | ||
235 | static iBool isDarkMode_(iWindow *window) { | 319 | static iBool isDarkMode_(iWindow *window) { |
@@ -265,7 +349,7 @@ iBool isPhone_iOS(void) { | |||
265 | } | 349 | } |
266 | 350 | ||
267 | int displayRefreshRate_iOS(void) { | 351 | int displayRefreshRate_iOS(void) { |
268 | return uiWindow_(get_Window()).screen.maximumFramesPerSecond; | 352 | return (int) uiWindow_(get_Window()).screen.maximumFramesPerSecond; |
269 | } | 353 | } |
270 | 354 | ||
271 | void setupWindow_iOS(iWindow *window) { | 355 | void setupWindow_iOS(iWindow *window) { |
@@ -279,7 +363,10 @@ void playHapticEffect_iOS(enum iHapticEffect effect) { | |||
279 | HapticState *hs = (HapticState *) appState_.haptic; | 363 | HapticState *hs = (HapticState *) appState_.haptic; |
280 | switch(effect) { | 364 | switch(effect) { |
281 | case tap_HapticEffect: | 365 | case tap_HapticEffect: |
282 | [hs playTapEffect]; | 366 | [hs playHapticEffect:hs.tapDef]; |
367 | break; | ||
368 | case gentleTap_HapticEffect: | ||
369 | [hs playHapticEffect:hs.gentleTapDef]; | ||
283 | break; | 370 | break; |
284 | } | 371 | } |
285 | } | 372 | } |
@@ -324,6 +411,38 @@ iBool processEvent_iOS(const SDL_Event *ev) { | |||
324 | return iFalse; /* allow normal processing */ | 411 | return iFalse; /* allow normal processing */ |
325 | } | 412 | } |
326 | 413 | ||
414 | void updateNowPlayingInfo_iOS(void) { | ||
415 | const iPlayer *player = active_Player(); | ||
416 | if (!player) { | ||
417 | clearNowPlayingInfo_iOS(); | ||
418 | return; | ||
419 | } | ||
420 | NSMutableDictionary<NSString *, id> *info = [[NSMutableDictionary<NSString *, id> alloc] init]; | ||
421 | [info setObject:[NSNumber numberWithDouble:time_Player(player)] | ||
422 | forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; | ||
423 | [info setObject:[NSNumber numberWithInt:MPNowPlayingInfoMediaTypeAudio] | ||
424 | forKey:MPNowPlayingInfoPropertyMediaType]; | ||
425 | [info setObject:[NSNumber numberWithDouble:duration_Player(player)] | ||
426 | forKey:MPMediaItemPropertyPlaybackDuration]; | ||
427 | const iString *title = tag_Player(player, title_PlayerTag); | ||
428 | const iString *artist = tag_Player(player, artist_PlayerTag); | ||
429 | if (isEmpty_String(title)) { | ||
430 | title = collectNewCStr_String("Audio"); /* TODO: Use link label or URL file name */ | ||
431 | } | ||
432 | if (isEmpty_String(artist)) { | ||
433 | artist = collectNewCStr_String("Lagrange"); /* TODO: Use domain or base URL */ | ||
434 | } | ||
435 | [info setObject:[NSString stringWithUTF8String:cstr_String(title)] | ||
436 | forKey:MPMediaItemPropertyTitle]; | ||
437 | [info setObject:[NSString stringWithUTF8String:cstr_String(artist)] | ||
438 | forKey:MPMediaItemPropertyArtist]; | ||
439 | [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:info]; | ||
440 | } | ||
441 | |||
442 | void clearNowPlayingInfo_iOS(void) { | ||
443 | [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil]; | ||
444 | } | ||
445 | |||
327 | void exportDownloadedFile_iOS(const iString *path) { | 446 | void exportDownloadedFile_iOS(const iString *path) { |
328 | NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) | 447 | NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) |
329 | encoding:NSUTF8StringEncoding]]; | 448 | encoding:NSUTF8StringEncoding]]; |
@@ -335,6 +454,17 @@ void exportDownloadedFile_iOS(const iString *path) { | |||
335 | [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; | 454 | [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; |
336 | } | 455 | } |
337 | 456 | ||
457 | void pickFileForOpening_iOS(void) { | ||
458 | UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] | ||
459 | initWithDocumentTypes:@[@"fi.skyjake.lagrange.gemini", | ||
460 | @"public.text", | ||
461 | @"public.image", | ||
462 | @"public.audio"] | ||
463 | inMode:UIDocumentPickerModeImport]; | ||
464 | picker.delegate = appState_; | ||
465 | [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; | ||
466 | } | ||
467 | |||
338 | /*----------------------------------------------------------------------------------------------*/ | 468 | /*----------------------------------------------------------------------------------------------*/ |
339 | 469 | ||
340 | enum iAVFAudioPlayerState { | 470 | enum iAVFAudioPlayerState { |
diff --git a/src/macos.m b/src/macos.m index bbef94c6..6bacfdd1 100644 --- a/src/macos.m +++ b/src/macos.m | |||
@@ -480,7 +480,7 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * | |||
480 | [menu setAutoenablesItems:NO]; | 480 | [menu setAutoenablesItems:NO]; |
481 | for (size_t i = 0; i < count; ++i) { | 481 | for (size_t i = 0; i < count; ++i) { |
482 | const char *label = translateCStr_Lang(items[i].label); | 482 | const char *label = translateCStr_Lang(items[i].label); |
483 | if (label[0] == '\r') { | 483 | if (label[0] == '\v') { |
484 | /* Skip the formatting escape. */ | 484 | /* Skip the formatting escape. */ |
485 | label += 2; | 485 | label += 2; |
486 | } | 486 | } |
@@ -64,7 +64,8 @@ int main(int argc, char **argv) { | |||
64 | "ECDHE-ECDSA-AES128-GCM-SHA256:" | 64 | "ECDHE-ECDSA-AES128-GCM-SHA256:" |
65 | "ECDHE-RSA-AES256-GCM-SHA384:" | 65 | "ECDHE-RSA-AES256-GCM-SHA384:" |
66 | "ECDHE-RSA-CHACHA20-POLY1305:" | 66 | "ECDHE-RSA-CHACHA20-POLY1305:" |
67 | "ECDHE-RSA-AES128-GCM-SHA256"); | 67 | "ECDHE-RSA-AES128-GCM-SHA256:" |
68 | "DHE-RSA-AES256-GCM-SHA384"); | ||
68 | SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); | 69 | SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); |
69 | SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); | 70 | SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); |
70 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { | 71 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { |
diff --git a/src/media.c b/src/media.c index 1313b7da..eb4a8311 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | #include "gmdocument.h" | 24 | #include "gmdocument.h" |
25 | #include "gmrequest.h" | 25 | #include "gmrequest.h" |
26 | #include "ui/window.h" | 26 | #include "ui/window.h" |
27 | #include "ui/paint.h" /* size_SDLTexture */ | ||
27 | #include "audio/player.h" | 28 | #include "audio/player.h" |
28 | #include "app.h" | 29 | #include "app.h" |
29 | #include "stb_image.h" | 30 | #include "stb_image.h" |
@@ -261,6 +262,31 @@ void clear_Media(iMedia *d) { | |||
261 | clear_PtrArray(&d->downloads); | 262 | clear_PtrArray(&d->downloads); |
262 | } | 263 | } |
263 | 264 | ||
265 | size_t memorySize_Media(const iMedia *d) { | ||
266 | size_t memSize = 0; | ||
267 | iConstForEach(PtrArray, i, &d->images) { | ||
268 | const iGmImage *img = i.ptr; | ||
269 | if (img->texture) { | ||
270 | const iInt2 texSize = size_SDLTexture(img->texture); | ||
271 | memSize += 4 * texSize.x * texSize.y; /* RGBA */ | ||
272 | } | ||
273 | else { | ||
274 | memSize += size_Block(&img->partialData); | ||
275 | } | ||
276 | } | ||
277 | iConstForEach(PtrArray, a, &d->audio) { | ||
278 | const iGmAudio *audio = a.ptr; | ||
279 | if (audio->player) { | ||
280 | memSize += sourceDataSize_Player(audio->player); | ||
281 | } | ||
282 | } | ||
283 | iConstForEach(PtrArray, n, &d->downloads) { | ||
284 | const iGmDownload *down = n.ptr; | ||
285 | memSize += down->numBytes; | ||
286 | } | ||
287 | return memSize; | ||
288 | } | ||
289 | |||
264 | iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) { | 290 | iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) { |
265 | iGmDownload *dl = NULL; | 291 | iGmDownload *dl = NULL; |
266 | iMediaId existing = findLinkDownload_Media(d, linkId); | 292 | iMediaId existing = findLinkDownload_Media(d, linkId); |
@@ -464,6 +490,15 @@ iPlayer *audioPlayer_Media(const iMedia *d, iMediaId audioId) { | |||
464 | return NULL; | 490 | return NULL; |
465 | } | 491 | } |
466 | 492 | ||
493 | void pauseAllPlayers_Media(const iMedia *d, iBool setPaused) { | ||
494 | for (size_t i = 0; i < size_PtrArray(&d->audio); ++i) { | ||
495 | const iGmAudio *audio = constAt_PtrArray(&d->audio, i); | ||
496 | if (audio->player) { | ||
497 | setPaused_Player(audio->player, setPaused); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | |||
467 | iBool downloadInfo_Media(const iMedia *d, iMediaId downloadId, iGmMediaInfo *info_out) { | 502 | iBool downloadInfo_Media(const iMedia *d, iMediaId downloadId, iGmMediaInfo *info_out) { |
468 | if (downloadId > 0 && downloadId <= size_PtrArray(&d->downloads)) { | 503 | if (downloadId > 0 && downloadId <= size_PtrArray(&d->downloads)) { |
469 | const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1); | 504 | const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1); |
diff --git a/src/media.h b/src/media.h index 7cc941d0..f7ad6efd 100644 --- a/src/media.h +++ b/src/media.h | |||
@@ -50,6 +50,8 @@ void clear_Media (iMedia *); | |||
50 | iBool setDownloadUrl_Media (iMedia *, uint16_t linkId, const iString *url); | 50 | iBool setDownloadUrl_Media (iMedia *, uint16_t linkId, const iString *url); |
51 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); | 51 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); |
52 | 52 | ||
53 | size_t memorySize_Media (const iMedia *); | ||
54 | |||
53 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); | 55 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); |
54 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out); | 56 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out); |
55 | iInt2 imageSize_Media (const iMedia *, iMediaId imageId); | 57 | iInt2 imageSize_Media (const iMedia *, iMediaId imageId); |
@@ -59,6 +61,7 @@ size_t numAudio_Media (const iMedia *); | |||
59 | iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); | 61 | iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); |
60 | iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out); | 62 | iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out); |
61 | iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); | 63 | iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); |
64 | void pauseAllPlayers_Media(const iMedia *, iBool setPaused); | ||
62 | 65 | ||
63 | iMediaId findLinkDownload_Media (const iMedia *, uint16_t linkId); | 66 | iMediaId findLinkDownload_Media (const iMedia *, uint16_t linkId); |
64 | iBool downloadInfo_Media (const iMedia *, iMediaId downloadId, iGmMediaInfo *info_out); | 67 | iBool downloadInfo_Media (const iMedia *, iMediaId downloadId, iGmMediaInfo *info_out); |
diff --git a/src/prefs.c b/src/prefs.c index 385dee78..f1842e9a 100644 --- a/src/prefs.c +++ b/src/prefs.c | |||
@@ -46,6 +46,7 @@ void init_Prefs(iPrefs *d) { | |||
46 | d->openArchiveIndexPages = iTrue; | 46 | d->openArchiveIndexPages = iTrue; |
47 | d->decodeUserVisibleURLs = iTrue; | 47 | d->decodeUserVisibleURLs = iTrue; |
48 | d->maxCacheSize = 10; | 48 | d->maxCacheSize = 10; |
49 | d->maxMemorySize = 200; | ||
49 | d->font = nunito_TextFont; | 50 | d->font = nunito_TextFont; |
50 | d->headingFont = nunito_TextFont; | 51 | d->headingFont = nunito_TextFont; |
51 | d->monospaceGemini = iFalse; | 52 | d->monospaceGemini = iFalse; |
diff --git a/src/prefs.h b/src/prefs.h index 7185c8f9..655ec949 100644 --- a/src/prefs.h +++ b/src/prefs.h | |||
@@ -66,6 +66,7 @@ struct Impl_Prefs { | |||
66 | iString caPath; | 66 | iString caPath; |
67 | iBool decodeUserVisibleURLs; | 67 | iBool decodeUserVisibleURLs; |
68 | int maxCacheSize; /* MB */ | 68 | int maxCacheSize; /* MB */ |
69 | int maxMemorySize; /* MB */ | ||
69 | iString geminiProxy; | 70 | iString geminiProxy; |
70 | iString gopherProxy; | 71 | iString gopherProxy; |
71 | iString httpProxy; | 72 | iString httpProxy; |
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c index e121b4d0..6e818137 100644 --- a/src/ui/certimportwidget.c +++ b/src/ui/certimportwidget.c | |||
@@ -215,6 +215,14 @@ static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Even | |||
215 | return iTrue; | 215 | return iTrue; |
216 | } | 216 | } |
217 | } | 217 | } |
218 | if (isCommand_UserEvent(ev, "input.paste")) { | ||
219 | if (!tryImportFromClipboard_CertImportWidget_(d)) { | ||
220 | makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.certimport.pasted}", | ||
221 | "${dlg.certimport.notfound}"); | ||
222 | } | ||
223 | postRefresh_App(); | ||
224 | return iTrue; | ||
225 | } | ||
218 | if (isCommand_UserEvent(ev, "certimport.paste")) { | 226 | if (isCommand_UserEvent(ev, "certimport.paste")) { |
219 | tryImportFromClipboard_CertImportWidget_(d); | 227 | tryImportFromClipboard_CertImportWidget_(d); |
220 | return iTrue; | 228 | return iTrue; |
diff --git a/src/ui/color.c b/src/ui/color.c index a6ba18e4..05ec1f6f 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -24,11 +24,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | #include "root.h" | 24 | #include "root.h" |
25 | #include "app.h" | 25 | #include "app.h" |
26 | 26 | ||
27 | #include <the_Foundation/file.h> | ||
28 | #include <the_Foundation/path.h> | ||
27 | #include <the_Foundation/string.h> | 29 | #include <the_Foundation/string.h> |
28 | 30 | ||
29 | static const iColor transparent_; | 31 | static const iColor transparent_; |
30 | 32 | ||
31 | static const iColor darkPalette_[] = { | 33 | static iColor darkPalette_[] = { |
32 | { 0, 0, 0, 255 }, | 34 | { 0, 0, 0, 255 }, |
33 | { 40, 40, 40, 255 }, | 35 | { 40, 40, 40, 255 }, |
34 | { 80, 80, 80, 255 }, | 36 | { 80, 80, 80, 255 }, |
@@ -47,7 +49,7 @@ static const iColor darkPalette_[] = { | |||
47 | { 0, 200, 0, 255 }, | 49 | { 0, 200, 0, 255 }, |
48 | }; | 50 | }; |
49 | 51 | ||
50 | static const iColor lightPalette_[] = { | 52 | static iColor lightPalette_[] = { |
51 | { 0, 0, 0, 255 }, | 53 | { 0, 0, 0, 255 }, |
52 | { 75, 75, 75, 255 }, | 54 | { 75, 75, 75, 255 }, |
53 | { 150, 150, 150, 255 }, | 55 | { 150, 150, 150, 255 }, |
@@ -468,12 +470,12 @@ const char *escape_Color(int color) { | |||
468 | return esc[color]; | 470 | return esc[color]; |
469 | } | 471 | } |
470 | /* TODO: Conflict with format strings! "%" (37) may be used as the color value. */ | 472 | /* TODO: Conflict with format strings! "%" (37) may be used as the color value. */ |
471 | /* Double-\r is used for range extension. */ | 473 | /* Double-\v is used for range extension. */ |
472 | if (color + asciiBase_ColorEscape > 127) { | 474 | if (color + asciiBase_ColorEscape > 127) { |
473 | iAssert(color - asciiExtended_ColorEscape + asciiBase_ColorEscape <= 127); | 475 | iAssert(color - asciiExtended_ColorEscape + asciiBase_ColorEscape <= 127); |
474 | return format_CStr("\r\r%c", color - asciiExtended_ColorEscape + asciiBase_ColorEscape); | 476 | return format_CStr("\v\v%c", color - asciiExtended_ColorEscape + asciiBase_ColorEscape); |
475 | } | 477 | } |
476 | return format_CStr("\r%c", color + asciiBase_ColorEscape); | 478 | return format_CStr("\v%c", color + asciiBase_ColorEscape); |
477 | } | 479 | } |
478 | 480 | ||
479 | iHSLColor setSat_HSLColor(iHSLColor d, float sat) { | 481 | iHSLColor setSat_HSLColor(iHSLColor d, float sat) { |
@@ -804,3 +806,77 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | |||
804 | } | 806 | } |
805 | return clr; | 807 | return clr; |
806 | } | 808 | } |
809 | |||
810 | iBool loadPalette_Color(const char *path) { | ||
811 | iBool wasLoaded = iFalse; | ||
812 | iFile *f = newCStr_File(concatPath_CStr(path, "palette.txt")); | ||
813 | if (open_File(f, text_FileMode | readOnly_FileMode)) { | ||
814 | iColor *dstPal = darkPalette_; | ||
815 | iRangecc srcLine = iNullRange; | ||
816 | const iBlock *src = collect_Block(readAll_File(f)); | ||
817 | while (nextSplit_Rangecc(range_Block(src), "\n", &srcLine)) { | ||
818 | iRangecc line = srcLine; | ||
819 | trim_Rangecc(&line); | ||
820 | if (isEmpty_Range(&line)) { | ||
821 | continue; | ||
822 | } | ||
823 | if (*line.start == '#') { | ||
824 | /* Control directive. */ | ||
825 | line.start++; | ||
826 | trim_Rangecc(&line); | ||
827 | if (equalCase_Rangecc(line, "dark")) { | ||
828 | dstPal = darkPalette_; | ||
829 | } | ||
830 | else if (equalCase_Rangecc(line, "light")) { | ||
831 | dstPal = lightPalette_; | ||
832 | } | ||
833 | continue; | ||
834 | } | ||
835 | static const struct { | ||
836 | const char *label; | ||
837 | int paletteIndex; | ||
838 | } colors_[] = { | ||
839 | { "black:", 0 }, { "gray25:", 1 }, { "gray50:", 2 }, { "gray75:", 3 }, | ||
840 | { "white:", 4 }, { "brown:", 5 }, { "orange:", 6 }, { "teal:", 7 }, | ||
841 | { "cyan:", 8 }, { "yellow:", 9 }, { "red:", 10 }, { "magenta:", 11 }, | ||
842 | { "blue:", 12 }, { "green:", 13 }, | ||
843 | }; | ||
844 | iForIndices(i, colors_) { | ||
845 | if (startsWithCase_Rangecc(line, colors_[i].label)) { | ||
846 | iColor *dst = &dstPal[colors_[i].paletteIndex]; | ||
847 | line.start += strlen(colors_[i].label); | ||
848 | trim_Rangecc(&line); | ||
849 | if (!isEmpty_Range(&line)) { | ||
850 | if (*line.start == '#') { | ||
851 | /* Hexadecimal color. */ | ||
852 | line.start++; | ||
853 | if (size_Range(&line) == 6) { | ||
854 | iBlock *vals = hexDecode_Rangecc(line); | ||
855 | iAssert(size_Block(vals) == 3); | ||
856 | const uint8_t *rgb = constData_Block(vals); | ||
857 | *dst = (iColor){ rgb[0], rgb[1], rgb[2], 255 }; | ||
858 | delete_Block(vals); | ||
859 | } | ||
860 | else { | ||
861 | fprintf(stderr, "[Color] invalid custom color: %s\n", | ||
862 | cstr_Rangecc(line)); | ||
863 | } | ||
864 | } | ||
865 | else { | ||
866 | unsigned int red = 0, green = 0, blue = 0; | ||
867 | sscanf(line.start, "%u %u %u", &red, &green, &blue); | ||
868 | if (red > 255 || green > 255 || blue > 255) { | ||
869 | fprintf(stderr, "[Color] RGB value(s) out of range: %s\n", | ||
870 | cstr_Rangecc(line)); | ||
871 | } | ||
872 | *dst = (iColor){ red, green, blue, 255 }; | ||
873 | } | ||
874 | } | ||
875 | } | ||
876 | } | ||
877 | } | ||
878 | wasLoaded = iTrue; | ||
879 | } | ||
880 | iRelease(f); | ||
881 | return wasLoaded; | ||
882 | } | ||
diff --git a/src/ui/color.h b/src/ui/color.h index d2fa3c00..37ec49eb 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -187,26 +187,26 @@ iLocalDef iBool isRegularText_ColorId(enum iColorId d) { | |||
187 | #define asciiBase_ColorEscape 33 | 187 | #define asciiBase_ColorEscape 33 |
188 | #define asciiExtended_ColorEscape (128 - asciiBase_ColorEscape) | 188 | #define asciiExtended_ColorEscape (128 - asciiBase_ColorEscape) |
189 | 189 | ||
190 | #define restore_ColorEscape "\r\x24" /* ASCII Cancel */ | 190 | #define restore_ColorEscape "\v\x24" /* ASCII Cancel */ |
191 | #define black_ColorEscape "\r!" | 191 | #define black_ColorEscape "\v!" |
192 | #define gray25_ColorEscape "\r\"" | 192 | #define gray25_ColorEscape "\v\"" |
193 | #define gray50_ColorEscape "\r#" | 193 | #define gray50_ColorEscape "\v#" |
194 | #define gray75_ColorEscape "\r$" | 194 | #define gray75_ColorEscape "\v$" |
195 | #define white_ColorEscape "\r%" | 195 | #define white_ColorEscape "\v%" |
196 | #define brown_ColorEscape "\r&" | 196 | #define brown_ColorEscape "\v&" |
197 | #define orange_ColorEscape "\r'" | 197 | #define orange_ColorEscape "\v'" |
198 | #define teal_ColorEscape "\r(" | 198 | #define teal_ColorEscape "\v(" |
199 | #define cyan_ColorEscape "\r)" | 199 | #define cyan_ColorEscape "\v)" |
200 | #define yellow_ColorEscape "\r*" | 200 | #define yellow_ColorEscape "\v*" |
201 | #define red_ColorEscape "\r+" | 201 | #define red_ColorEscape "\v+" |
202 | #define magenta_ColorEscape "\r," | 202 | #define magenta_ColorEscape "\v," |
203 | #define blue_ColorEscape "\r-" | 203 | #define blue_ColorEscape "\v-" |
204 | #define green_ColorEscape "\r." | 204 | #define green_ColorEscape "\v." |
205 | #define uiText_ColorEscape "\r4" | 205 | #define uiText_ColorEscape "\v4" |
206 | #define uiTextAction_ColorEscape "\r<" | 206 | #define uiTextAction_ColorEscape "\v<" |
207 | #define uiTextCaution_ColorEscape "\r=" | 207 | #define uiTextCaution_ColorEscape "\v=" |
208 | #define uiTextStrong_ColorEscape "\r:" | 208 | #define uiTextStrong_ColorEscape "\v:" |
209 | #define uiHeading_ColorEscape "\rR" | 209 | #define uiHeading_ColorEscape "\vR" |
210 | 210 | ||
211 | iDeclareType(Color) | 211 | iDeclareType(Color) |
212 | iDeclareType(HSLColor) | 212 | iDeclareType(HSLColor) |
@@ -244,7 +244,9 @@ iLocalDef void setHsl_Color(int color, iHSLColor hsl) { | |||
244 | set_Color(color, rgb_HSLColor(hsl)); | 244 | set_Color(color, rgb_HSLColor(hsl)); |
245 | } | 245 | } |
246 | 246 | ||
247 | iBool loadPalette_Color (const char *path); | ||
247 | void setThemePalette_Color (enum iColorTheme theme); | 248 | void setThemePalette_Color (enum iColorTheme theme); |
248 | 249 | ||
249 | iColor ansiForeground_Color (iRangecc escapeSequence, int fallback); | 250 | iColor ansiForeground_Color (iRangecc escapeSequence, int fallback); |
250 | const char * escape_Color (int color); | 251 | const char * escape_Color (int color); |
252 | |||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 6184a75a..cb1fde28 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -130,7 +130,8 @@ void deinit_PersistentDocumentState(iPersistentDocumentState *d) { | |||
130 | 130 | ||
131 | void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { | 131 | void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { |
132 | serialize_String(d->url, outs); | 132 | serialize_String(d->url, outs); |
133 | writeU16_Stream(outs, d->reloadInterval & 7); | 133 | uint16_t params = d->reloadInterval & 7; |
134 | writeU16_Stream(outs, params); | ||
134 | serialize_History(d->history, outs); | 135 | serialize_History(d->history, outs); |
135 | } | 136 | } |
136 | 137 | ||
@@ -223,6 +224,7 @@ enum iDocumentWidgetFlag { | |||
223 | movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), | 224 | movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), |
224 | otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ | 225 | otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ |
225 | urlChanged_DocumentWidgetFlag = iBit(13), | 226 | urlChanged_DocumentWidgetFlag = iBit(13), |
227 | openedFromSidebar_DocumentWidgetFlag = iBit(14), | ||
226 | }; | 228 | }; |
227 | 229 | ||
228 | enum iDocumentLinkOrdinalMode { | 230 | enum iDocumentLinkOrdinalMode { |
@@ -315,6 +317,10 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
315 | init_Widget(w); | 317 | init_Widget(w); |
316 | setId_Widget(w, format_CStr("document%03d", ++docEnum_)); | 318 | setId_Widget(w, format_CStr("document%03d", ++docEnum_)); |
317 | setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); | 319 | setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); |
320 | if (deviceType_App() != desktop_AppDeviceType) { | ||
321 | setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag | | ||
322 | horizontalOffset_WidgetFlag, iTrue); | ||
323 | } | ||
318 | init_PersistentDocumentState(&d->mod); | 324 | init_PersistentDocumentState(&d->mod); |
319 | d->flags = 0; | 325 | d->flags = 0; |
320 | d->phoneToolbar = NULL; | 326 | d->phoneToolbar = NULL; |
@@ -392,6 +398,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
392 | } | 398 | } |
393 | 399 | ||
394 | void deinit_DocumentWidget(iDocumentWidget *d) { | 400 | void deinit_DocumentWidget(iDocumentWidget *d) { |
401 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | ||
395 | removeTicker_App(animate_DocumentWidget_, d); | 402 | removeTicker_App(animate_DocumentWidget_, d); |
396 | removeTicker_App(prerender_DocumentWidget_, d); | 403 | removeTicker_App(prerender_DocumentWidget_, d); |
397 | remove_Periodic(periodic_App(), d); | 404 | remove_Periodic(periodic_App(), d); |
@@ -976,6 +983,9 @@ static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
976 | } | 983 | } |
977 | 984 | ||
978 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { | 985 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { |
986 | if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) { | ||
987 | return; | ||
988 | } | ||
979 | invalidate_VisBuf(d->visBuf); | 989 | invalidate_VisBuf(d->visBuf); |
980 | clear_PtrSet(d->invalidRuns); | 990 | clear_PtrSet(d->invalidRuns); |
981 | } | 991 | } |
@@ -1016,9 +1026,7 @@ static void showOrHidePinningIndicator_DocumentWidget_(iDocumentWidget *d) { | |||
1016 | isPinned_DocumentWidget_(d)); | 1026 | isPinned_DocumentWidget_(d)); |
1017 | } | 1027 | } |
1018 | 1028 | ||
1019 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 1029 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { |
1020 | setUrl_GmDocument(d->doc, d->mod.url); | ||
1021 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); | ||
1022 | documentRunsInvalidated_DocumentWidget_(d); | 1030 | documentRunsInvalidated_DocumentWidget_(d); |
1023 | updateWindowTitle_DocumentWidget_(d); | 1031 | updateWindowTitle_DocumentWidget_(d); |
1024 | updateVisible_DocumentWidget_(d); | 1032 | updateVisible_DocumentWidget_(d); |
@@ -1035,6 +1043,26 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | |||
1035 | } | 1043 | } |
1036 | } | 1044 | } |
1037 | showOrHidePinningIndicator_DocumentWidget_(d); | 1045 | showOrHidePinningIndicator_DocumentWidget_(d); |
1046 | setCachedDocument_History(d->mod.history, | ||
1047 | d->doc, /* keeps a ref */ | ||
1048 | (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | ||
1049 | } | ||
1050 | |||
1051 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | ||
1052 | setUrl_GmDocument(d->doc, d->mod.url); | ||
1053 | setSource_GmDocument(d->doc, | ||
1054 | source, | ||
1055 | documentWidth_DocumentWidget_(d), | ||
1056 | isFinished_GmRequest(d->request) ? final_GmDocumentUpdate | ||
1057 | : partial_GmDocumentUpdate); | ||
1058 | documentWasChanged_DocumentWidget_(d); | ||
1059 | } | ||
1060 | |||
1061 | static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { | ||
1062 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | ||
1063 | iRelease(d->doc); | ||
1064 | d->doc = ref_Object(newDoc); | ||
1065 | documentWasChanged_DocumentWidget_(d); | ||
1038 | } | 1066 | } |
1039 | 1067 | ||
1040 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | 1068 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { |
@@ -1143,16 +1171,22 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1143 | { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, | 1171 | { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, |
1144 | 2); | 1172 | 2); |
1145 | } | 1173 | } |
1146 | setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | 1174 | /* Make a new document for the error page.*/ { |
1147 | setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); | 1175 | iGmDocument *errorDoc = new_GmDocument(); |
1176 | setUrl_GmDocument(errorDoc, d->mod.url); | ||
1177 | setBanner_GmDocument(errorDoc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | ||
1178 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); | ||
1179 | replaceDocument_DocumentWidget_(d, errorDoc); | ||
1180 | iRelease(errorDoc); | ||
1181 | } | ||
1148 | translate_Lang(src); | 1182 | translate_Lang(src); |
1183 | d->state = ready_RequestState; | ||
1149 | setSource_DocumentWidget(d, src); | 1184 | setSource_DocumentWidget(d, src); |
1150 | updateTheme_DocumentWidget_(d); | 1185 | updateTheme_DocumentWidget_(d); |
1151 | reset_SmoothScroll(&d->scrollY); | 1186 | reset_SmoothScroll(&d->scrollY); |
1152 | init_Anim(&d->sideOpacity, 0); | 1187 | init_Anim(&d->sideOpacity, 0); |
1153 | init_Anim(&d->altTextOpacity, 0); | 1188 | init_Anim(&d->altTextOpacity, 0); |
1154 | resetWideRuns_DocumentWidget_(d); | 1189 | resetWideRuns_DocumentWidget_(d); |
1155 | d->state = ready_RequestState; | ||
1156 | } | 1190 | } |
1157 | 1191 | ||
1158 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { | 1192 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { |
@@ -1264,9 +1298,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1264 | 2); | 1298 | 2); |
1265 | } | 1299 | } |
1266 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | 1300 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { |
1267 | redoLayout_GmDocument(d->doc); | 1301 | redoLayout_GmDocument(d->doc); |
1268 | updateVisible_DocumentWidget_(d); | 1302 | updateVisible_DocumentWidget_(d); |
1269 | invalidate_DocumentWidget_(d); | 1303 | invalidate_DocumentWidget_(d); |
1270 | } | 1304 | } |
1271 | } | 1305 | } |
1272 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1306 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { |
@@ -1348,7 +1382,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1348 | } | 1382 | } |
1349 | } | 1383 | } |
1350 | 1384 | ||
1351 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response, | 1385 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, |
1386 | const iGmResponse *response, | ||
1387 | iGmDocument *cachedDoc, | ||
1352 | const iBool isInitialUpdate) { | 1388 | const iBool isInitialUpdate) { |
1353 | if (d->state == ready_RequestState) { | 1389 | if (d->state == ready_RequestState) { |
1354 | return; | 1390 | return; |
@@ -1371,7 +1407,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1371 | if (isSuccess_GmStatusCode(statusCode)) { | 1407 | if (isSuccess_GmStatusCode(statusCode)) { |
1372 | /* Check the MIME type. */ | 1408 | /* Check the MIME type. */ |
1373 | iRangecc charset = range_CStr("utf-8"); | 1409 | iRangecc charset = range_CStr("utf-8"); |
1374 | enum iGmDocumentFormat docFormat = undefined_GmDocumentFormat; | 1410 | enum iSourceFormat docFormat = undefined_SourceFormat; |
1375 | const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */ | 1411 | const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */ |
1376 | set_String(&d->sourceMime, mimeStr); | 1412 | set_String(&d->sourceMime, mimeStr); |
1377 | iRangecc mime = range_String(mimeStr); | 1413 | iRangecc mime = range_String(mimeStr); |
@@ -1380,20 +1416,20 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1380 | iRangecc param = seg; | 1416 | iRangecc param = seg; |
1381 | trim_Rangecc(¶m); | 1417 | trim_Rangecc(¶m); |
1382 | if (equal_Rangecc(param, "text/gemini")) { | 1418 | if (equal_Rangecc(param, "text/gemini")) { |
1383 | docFormat = gemini_GmDocumentFormat; | 1419 | docFormat = gemini_SourceFormat; |
1384 | setRange_String(&d->sourceMime, param); | 1420 | setRange_String(&d->sourceMime, param); |
1385 | } | 1421 | } |
1386 | else if (startsWith_Rangecc(param, "text/") || | 1422 | else if (startsWith_Rangecc(param, "text/") || |
1387 | equal_Rangecc(param, "application/json") || | 1423 | equal_Rangecc(param, "application/json") || |
1388 | equal_Rangecc(param, "application/x-pem-file") || | 1424 | equal_Rangecc(param, "application/x-pem-file") || |
1389 | equal_Rangecc(param, "application/pem-certificate-chain")) { | 1425 | equal_Rangecc(param, "application/pem-certificate-chain")) { |
1390 | docFormat = plainText_GmDocumentFormat; | 1426 | docFormat = plainText_SourceFormat; |
1391 | setRange_String(&d->sourceMime, param); | 1427 | setRange_String(&d->sourceMime, param); |
1392 | } | 1428 | } |
1393 | else if (equal_Rangecc(param, "application/zip") || | 1429 | else if (equal_Rangecc(param, "application/zip") || |
1394 | (startsWith_Rangecc(param, "application/") && | 1430 | (startsWith_Rangecc(param, "application/") && |
1395 | endsWithCase_Rangecc(param, "+zip"))) { | 1431 | endsWithCase_Rangecc(param, "+zip"))) { |
1396 | docFormat = gemini_GmDocumentFormat; | 1432 | docFormat = gemini_SourceFormat; |
1397 | setRange_String(&d->sourceMime, param); | 1433 | setRange_String(&d->sourceMime, param); |
1398 | iString *key = collectNew_String(); | 1434 | iString *key = collectNew_String(); |
1399 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); | 1435 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); |
@@ -1420,9 +1456,10 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1420 | startsWith_Rangecc(param, "audio/")) { | 1456 | startsWith_Rangecc(param, "audio/")) { |
1421 | const iBool isAudio = startsWith_Rangecc(param, "audio/"); | 1457 | const iBool isAudio = startsWith_Rangecc(param, "audio/"); |
1422 | /* Make a simple document with an image or audio player. */ | 1458 | /* Make a simple document with an image or audio player. */ |
1423 | docFormat = gemini_GmDocumentFormat; | 1459 | docFormat = gemini_SourceFormat; |
1424 | setRange_String(&d->sourceMime, param); | 1460 | setRange_String(&d->sourceMime, param); |
1425 | const iGmLinkId imgLinkId = 1; /* there's only the one link */ | 1461 | const iGmLinkId imgLinkId = 1; /* there's only the one link */ |
1462 | /* TODO: Do the image loading in `postProcessRequestContent_DocumentWidget_()` */ | ||
1426 | if ((isAudio && isInitialUpdate) || (!isAudio && isRequestFinished)) { | 1463 | if ((isAudio && isInitialUpdate) || (!isAudio && isRequestFinished)) { |
1427 | const char *linkTitle = | 1464 | const char *linkTitle = |
1428 | startsWith_String(mimeStr, "image/") ? "Image" : "Audio"; | 1465 | startsWith_String(mimeStr, "image/") ? "Image" : "Audio"; |
@@ -1432,7 +1469,9 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1432 | linkTitle = | 1469 | linkTitle = |
1433 | baseName_Path(collect_String(newRange_String(parts.path))).start; | 1470 | baseName_Path(collect_String(newRange_String(parts.path))).start; |
1434 | } | 1471 | } |
1435 | format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), linkTitle); | 1472 | format_String(&str, "=> %s %s\n", |
1473 | cstr_String(withSpacesEncoded_String(d->mod.url)), | ||
1474 | linkTitle); | ||
1436 | setData_Media(media_GmDocument(d->doc), | 1475 | setData_Media(media_GmDocument(d->doc), |
1437 | imgLinkId, | 1476 | imgLinkId, |
1438 | mimeStr, | 1477 | mimeStr, |
@@ -1464,7 +1503,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1464 | } | 1503 | } |
1465 | } | 1504 | } |
1466 | } | 1505 | } |
1467 | if (docFormat == undefined_GmDocumentFormat) { | 1506 | if (docFormat == undefined_SourceFormat) { |
1468 | showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta); | 1507 | showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta); |
1469 | deinit_String(&str); | 1508 | deinit_String(&str); |
1470 | return; | 1509 | return; |
@@ -1476,7 +1515,10 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1476 | collect_String(decode_Block(&str.chars, cstr_Rangecc(charset)))); | 1515 | collect_String(decode_Block(&str.chars, cstr_Rangecc(charset)))); |
1477 | } | 1516 | } |
1478 | } | 1517 | } |
1479 | if (setSource) { | 1518 | if (cachedDoc) { |
1519 | replaceDocument_DocumentWidget_(d, cachedDoc); | ||
1520 | } | ||
1521 | else if (setSource) { | ||
1480 | setSource_DocumentWidget(d, &str); | 1522 | setSource_DocumentWidget(d, &str); |
1481 | } | 1523 | } |
1482 | deinit_String(&str); | 1524 | deinit_String(&str); |
@@ -1556,14 +1598,16 @@ static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) { | |||
1556 | } | 1598 | } |
1557 | 1599 | ||
1558 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, | 1600 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, |
1559 | const iGmResponse *resp) { | 1601 | const iGmResponse *resp, iGmDocument *cachedDoc) { |
1560 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 1602 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
1561 | clear_ObjectList(d->media); | 1603 | clear_ObjectList(d->media); |
1562 | delete_Gempub(d->sourceGempub); | 1604 | delete_Gempub(d->sourceGempub); |
1563 | d->sourceGempub = NULL; | 1605 | d->sourceGempub = NULL; |
1564 | reset_GmDocument(d->doc); | 1606 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); |
1607 | iRelease(d->doc); | ||
1565 | destroy_Widget(d->footerButtons); | 1608 | destroy_Widget(d->footerButtons); |
1566 | d->footerButtons = NULL; | 1609 | d->footerButtons = NULL; |
1610 | d->doc = new_GmDocument(); | ||
1567 | resetWideRuns_DocumentWidget_(d); | 1611 | resetWideRuns_DocumentWidget_(d); |
1568 | d->state = fetching_RequestState; | 1612 | d->state = fetching_RequestState; |
1569 | /* Do the fetch. */ { | 1613 | /* Do the fetch. */ { |
@@ -1574,10 +1618,12 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1574 | d->sourceStatus = success_GmStatusCode; | 1618 | d->sourceStatus = success_GmStatusCode; |
1575 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); | 1619 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); |
1576 | set_Block(&d->sourceContent, &resp->body); | 1620 | set_Block(&d->sourceContent, &resp->body); |
1577 | updateDocument_DocumentWidget_(d, resp, iTrue); | 1621 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); |
1578 | postProcessRequestContent_DocumentWidget_(d, iTrue); | 1622 | // setCachedDocument_History(d->mod.history, d->doc, |
1623 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | ||
1579 | } | 1624 | } |
1580 | d->state = ready_RequestState; | 1625 | d->state = ready_RequestState; |
1626 | postProcessRequestContent_DocumentWidget_(d, iTrue); | ||
1581 | init_Anim(&d->altTextOpacity, 0); | 1627 | init_Anim(&d->altTextOpacity, 0); |
1582 | reset_SmoothScroll(&d->scrollY); | 1628 | reset_SmoothScroll(&d->scrollY); |
1583 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); | 1629 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); |
@@ -1587,13 +1633,18 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1587 | cacheDocumentGlyphs_DocumentWidget_(d); | 1633 | cacheDocumentGlyphs_DocumentWidget_(d); |
1588 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | 1634 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; |
1589 | d->flags &= ~urlChanged_DocumentWidgetFlag; | 1635 | d->flags &= ~urlChanged_DocumentWidgetFlag; |
1590 | postCommandf_Root(as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 1636 | postCommandf_Root( |
1637 | as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | ||
1591 | } | 1638 | } |
1592 | 1639 | ||
1593 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | 1640 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { |
1594 | const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); | 1641 | const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); |
1595 | if (recent && recent->cachedResponse) { | 1642 | if (recent && recent->cachedResponse) { |
1596 | updateFromCachedResponse_DocumentWidget_(d, recent->normScrollY, recent->cachedResponse); | 1643 | iChangeFlags(d->flags, |
1644 | openedFromSidebar_DocumentWidgetFlag, | ||
1645 | recent->flags.openedFromSidebar); | ||
1646 | updateFromCachedResponse_DocumentWidget_( | ||
1647 | d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); | ||
1597 | return iTrue; | 1648 | return iTrue; |
1598 | } | 1649 | } |
1599 | else if (!isEmpty_String(d->mod.url)) { | 1650 | else if (!isEmpty_String(d->mod.url)) { |
@@ -1634,7 +1685,7 @@ static void scrollBegan_DocumentWidget_(iAnyObject *any, int offset, uint32_t du | |||
1634 | if (deviceType_App() == phone_AppDeviceType) { | 1685 | if (deviceType_App() == phone_AppDeviceType) { |
1635 | const float normPos = normScrollPos_DocumentWidget_(d); | 1686 | const float normPos = normScrollPos_DocumentWidget_(d); |
1636 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { | 1687 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { |
1637 | showToolbars_Root(as_Widget(d)->root, offset < 0); | 1688 | showToolbar_Root(as_Widget(d)->root, offset < 0); |
1638 | } | 1689 | } |
1639 | } | 1690 | } |
1640 | updateVisible_DocumentWidget_(d); | 1691 | updateVisible_DocumentWidget_(d); |
@@ -1843,13 +1894,15 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1843 | /* Keep scroll position when reloading the same page. */ | 1894 | /* Keep scroll position when reloading the same page. */ |
1844 | reset_SmoothScroll(&d->scrollY); | 1895 | reset_SmoothScroll(&d->scrollY); |
1845 | } | 1896 | } |
1846 | reset_GmDocument(d->doc); /* new content incoming */ | 1897 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); |
1898 | iRelease(d->doc); /* new content incoming */ | ||
1899 | d->doc = new_GmDocument(); | ||
1847 | delete_Gempub(d->sourceGempub); | 1900 | delete_Gempub(d->sourceGempub); |
1848 | d->sourceGempub = NULL; | 1901 | d->sourceGempub = NULL; |
1849 | destroy_Widget(d->footerButtons); | 1902 | destroy_Widget(d->footerButtons); |
1850 | d->footerButtons = NULL; | 1903 | d->footerButtons = NULL; |
1851 | resetWideRuns_DocumentWidget_(d); | 1904 | resetWideRuns_DocumentWidget_(d); |
1852 | updateDocument_DocumentWidget_(d, resp, iTrue); | 1905 | updateDocument_DocumentWidget_(d, resp, NULL, iTrue); |
1853 | break; | 1906 | break; |
1854 | case categoryRedirect_GmStatusCode: | 1907 | case categoryRedirect_GmStatusCode: |
1855 | if (isEmpty_String(&resp->meta)) { | 1908 | if (isEmpty_String(&resp->meta)) { |
@@ -1900,7 +1953,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1900 | switch (category_GmStatusCode(statusCode)) { | 1953 | switch (category_GmStatusCode(statusCode)) { |
1901 | case categorySuccess_GmStatusCode: | 1954 | case categorySuccess_GmStatusCode: |
1902 | /* More content available. */ | 1955 | /* More content available. */ |
1903 | updateDocument_DocumentWidget_(d, resp, iFalse); | 1956 | updateDocument_DocumentWidget_(d, resp, NULL, iFalse); |
1904 | break; | 1957 | break; |
1905 | default: | 1958 | default: |
1906 | break; | 1959 | break; |
@@ -2085,16 +2138,16 @@ static const iString *saveToDownloads_(const iString *url, const iString *mime, | |||
2085 | exportDownloadedFile_iOS(savePath); | 2138 | exportDownloadedFile_iOS(savePath); |
2086 | #else | 2139 | #else |
2087 | if (showDialog) { | 2140 | if (showDialog) { |
2088 | const iMenuItem items[2] = { | 2141 | const iMenuItem items[2] = { |
2089 | { "${dlg.save.opendownload}", 0, 0, | 2142 | { "${dlg.save.opendownload}", 0, 0, |
2090 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, | 2143 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, |
2091 | { "${dlg.message.ok}", 0, 0, "message.ok" }, | 2144 | { "${dlg.message.ok}", 0, 0, "message.ok" }, |
2092 | }; | 2145 | }; |
2093 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", | 2146 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", |
2094 | format_CStr("%s\n${dlg.save.size} %.3f %s", | 2147 | format_CStr("%s\n${dlg.save.size} %.3f %s", |
2095 | cstr_String(path_File(f)), | 2148 | cstr_String(path_File(f)), |
2096 | isMega ? size / 1.0e6f : (size / 1.0e3f), | 2149 | isMega ? size / 1.0e6f : (size / 1.0e3f), |
2097 | isMega ? "${mb}" : "${kb}"), | 2150 | isMega ? "${mb}" : "${kb}"), |
2098 | items, | 2151 | items, |
2099 | iElemCount(items)); | 2152 | iElemCount(items)); |
2100 | } | 2153 | } |
@@ -2208,6 +2261,147 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2208 | return iTrue; | 2261 | return iTrue; |
2209 | } | 2262 | } |
2210 | 2263 | ||
2264 | static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, | ||
2265 | iDocumentWidget *swapBuffersWith) { | ||
2266 | if (doc) { | ||
2267 | iAssert(isInstance_Object(doc, &Class_GmDocument)); | ||
2268 | iGmDocument *copy = ref_Object(doc); | ||
2269 | iRelease(d->doc); | ||
2270 | d->doc = copy; | ||
2271 | d->scrollY = swapBuffersWith->scrollY; | ||
2272 | updateVisible_DocumentWidget_(d); | ||
2273 | iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf); | ||
2274 | iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta); | ||
2275 | iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs); | ||
2276 | invalidate_DocumentWidget_(swapBuffersWith); | ||
2277 | } | ||
2278 | } | ||
2279 | |||
2280 | static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { | ||
2281 | return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); | ||
2282 | } | ||
2283 | |||
2284 | static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | ||
2285 | iWidget *w = as_Widget(d); | ||
2286 | /* Swipe animations are rather complex and utilize both cached GmDocument content | ||
2287 | and temporary DocumentWidgets. Depending on the swipe direction, this DocumentWidget | ||
2288 | may wait until the finger is released to actually perform the navigation action. */ | ||
2289 | if (equal_Command(cmd, "edgeswipe.moved")) { | ||
2290 | //printf("[%p] responds to edgeswipe.moved\n", d); | ||
2291 | as_Widget(d)->offsetRef = NULL; | ||
2292 | const int side = argLabel_Command(cmd, "side"); | ||
2293 | const int offset = arg_Command(cmd); | ||
2294 | if (side == 1) { /* left edge */ | ||
2295 | if (atOldest_History(d->mod.history)) { | ||
2296 | return iTrue; | ||
2297 | } | ||
2298 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2299 | /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ | ||
2300 | iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); | ||
2301 | if (!swipeIn) { | ||
2302 | const iBool sidebarSwipe = (isPortraitPhone_App() && | ||
2303 | d->flags & openedFromSidebar_DocumentWidgetFlag && | ||
2304 | !isVisible_Widget(findWidget_App("sidebar"))); | ||
2305 | swipeIn = new_DocumentWidget(); | ||
2306 | setId_Widget(as_Widget(swipeIn), "swipein"); | ||
2307 | setFlags_Widget(as_Widget(swipeIn), | ||
2308 | disabled_WidgetFlag | refChildrenOffset_WidgetFlag | | ||
2309 | fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2310 | swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); | ||
2311 | swipeIn->widget.rect.size = d->widget.rect.size; | ||
2312 | swipeIn->widget.offsetRef = parent_Widget(w); | ||
2313 | if (!sidebarSwipe) { | ||
2314 | iRecentUrl *recent = new_RecentUrl(); | ||
2315 | preceding_History(d->mod.history, recent); | ||
2316 | if (recent->cachedDoc) { | ||
2317 | iChangeRef(swipeIn->doc, recent->cachedDoc); | ||
2318 | updateScrollMax_DocumentWidget_(d); | ||
2319 | setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); | ||
2320 | updateVisible_DocumentWidget_(swipeIn); | ||
2321 | swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | ||
2322 | } | ||
2323 | delete_RecentUrl(recent); | ||
2324 | } | ||
2325 | addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); | ||
2326 | } | ||
2327 | } | ||
2328 | if (side == 2) { /* right edge */ | ||
2329 | if (offset < -get_Window()->pixelRatio * 10) { | ||
2330 | int animSpan = 10; | ||
2331 | if (!atLatest_History(d->mod.history) && | ||
2332 | ~flags_Widget(w) & dragged_WidgetFlag) { | ||
2333 | animSpan = 0; | ||
2334 | postCommand_Widget(d, "navigate.forward"); | ||
2335 | setFlags_Widget(w, dragged_WidgetFlag, iTrue); | ||
2336 | /* Set up the swipe dummy. */ | ||
2337 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2338 | iDocumentWidget *target = new_DocumentWidget(); | ||
2339 | setId_Widget(as_Widget(target), "swipeout"); | ||
2340 | /* The target takes the old document and jumps on top. */ | ||
2341 | target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); | ||
2342 | target->widget.rect.size = d->widget.rect.size; | ||
2343 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2344 | swap_DocumentWidget_(target, d->doc, d); | ||
2345 | addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos); | ||
2346 | setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue); | ||
2347 | as_Widget(target)->offsetRef = parent_Widget(w); | ||
2348 | destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ | ||
2349 | } | ||
2350 | if (flags_Widget(w) & dragged_WidgetFlag) { | ||
2351 | setVisualOffset_Widget(w, width_Widget(w) + | ||
2352 | width_Widget(d) * offset / size_Root(w->root).x, | ||
2353 | animSpan, 0); | ||
2354 | } | ||
2355 | else { | ||
2356 | setVisualOffset_Widget(w, offset / 4, animSpan, 0); | ||
2357 | } | ||
2358 | } | ||
2359 | return iTrue; | ||
2360 | } | ||
2361 | } | ||
2362 | if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 2) { | ||
2363 | if (argLabel_Command(cmd, "abort") && flags_Widget(w) & dragged_WidgetFlag) { | ||
2364 | postCommand_Widget(d, "navigate.back"); | ||
2365 | } | ||
2366 | setFlags_Widget(w, dragged_WidgetFlag, iFalse); | ||
2367 | setVisualOffset_Widget(w, 0, 100, 0); | ||
2368 | return iTrue; | ||
2369 | } | ||
2370 | if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 1) { | ||
2371 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2372 | iWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); | ||
2373 | if (swipeIn) { | ||
2374 | swipeIn->offsetRef = NULL; | ||
2375 | destroy_Widget(swipeIn); | ||
2376 | } | ||
2377 | } | ||
2378 | if (equal_Command(cmd, "swipe.back")) { | ||
2379 | if (atOldest_History(d->mod.history)) { | ||
2380 | setVisualOffset_Widget(w, 0, 100, 0); | ||
2381 | return iTrue; | ||
2382 | } | ||
2383 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2384 | iDocumentWidget *target = new_DocumentWidget(); | ||
2385 | setId_Widget(as_Widget(target), "swipeout"); | ||
2386 | /* The target takes the old document and jumps on top. */ | ||
2387 | target->widget.rect.pos = windowToInner_Widget(swipeParent, innerToWindow_Widget(w, zero_I2())); | ||
2388 | /* Note: `innerToWindow_Widget` does not apply visual offset. */ | ||
2389 | target->widget.rect.size = w->rect.size; | ||
2390 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2391 | swap_DocumentWidget_(target, d->doc, d); | ||
2392 | addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); | ||
2393 | setFlags_Widget(as_Widget(d), refChildrenOffset_WidgetFlag, iTrue); | ||
2394 | as_Widget(d)->offsetRef = swipeParent; | ||
2395 | setVisualOffset_Widget(as_Widget(target), value_Anim(&w->visualOffset), 0, 0); | ||
2396 | setVisualOffset_Widget(as_Widget(target), width_Widget(target), 150, 0); | ||
2397 | destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ | ||
2398 | setVisualOffset_Widget(w, 0, 0, 0); | ||
2399 | postCommand_Widget(d, "navigate.back"); | ||
2400 | return iTrue; | ||
2401 | } | ||
2402 | return iFalse; | ||
2403 | } | ||
2404 | |||
2211 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 2405 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
2212 | iWidget *w = as_Widget(d); | 2406 | iWidget *w = as_Widget(d); |
2213 | if (equal_Command(cmd, "document.openurls.changed")) { | 2407 | if (equal_Command(cmd, "document.openurls.changed")) { |
@@ -2256,6 +2450,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2256 | return iFalse; | 2450 | return iFalse; |
2257 | } | 2451 | } |
2258 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { | 2452 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { |
2453 | // invalidateTheme_History(d->mod.history); /* cached colors */ | ||
2259 | updateTheme_DocumentWidget_(d); | 2454 | updateTheme_DocumentWidget_(d); |
2260 | updateVisible_DocumentWidget_(d); | 2455 | updateVisible_DocumentWidget_(d); |
2261 | updateTrust_DocumentWidget_(d, NULL); | 2456 | updateTrust_DocumentWidget_(d, NULL); |
@@ -2345,6 +2540,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2345 | msg, "%s\n", formatCStrs_Lang("num.bytes.n", size_Block(&d->sourceContent))); | 2540 | msg, "%s\n", formatCStrs_Lang("num.bytes.n", size_Block(&d->sourceContent))); |
2346 | } | 2541 | } |
2347 | } | 2542 | } |
2543 | /* TODO: On mobile, omit the CA status. */ | ||
2348 | appendFormat_String( | 2544 | appendFormat_String( |
2349 | msg, | 2545 | msg, |
2350 | "\n%s${pageinfo.cert.status}\n" | 2546 | "\n%s${pageinfo.cert.status}\n" |
@@ -2649,6 +2845,18 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2649 | return iTrue; | 2845 | return iTrue; |
2650 | } | 2846 | } |
2651 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { | 2847 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { |
2848 | if (isPortraitPhone_App()) { | ||
2849 | if (d->flags & openedFromSidebar_DocumentWidgetFlag && | ||
2850 | !isVisible_Widget(findWidget_App("sidebar"))) { | ||
2851 | postCommand_App("sidebar.toggle"); | ||
2852 | showToolbar_Root(get_Root(), iTrue); | ||
2853 | #if defined (iPlatformAppleMobile) | ||
2854 | playHapticEffect_iOS(gentleTap_HapticEffect); | ||
2855 | #endif | ||
2856 | return iTrue; | ||
2857 | } | ||
2858 | d->flags &= ~openedFromSidebar_DocumentWidgetFlag; | ||
2859 | } | ||
2652 | if (d->request) { | 2860 | if (d->request) { |
2653 | postCommandf_Root(w->root, | 2861 | postCommandf_Root(w->root, |
2654 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); | 2862 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -2811,7 +3019,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2811 | uiHeading_ColorEscape "${heading.import.bookmarks}", | 3019 | uiHeading_ColorEscape "${heading.import.bookmarks}", |
2812 | formatCStrs_Lang("dlg.import.found.n", count), | 3020 | formatCStrs_Lang("dlg.import.found.n", count), |
2813 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, | 3021 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, |
2814 | { format_CStr(cstrCount_Lang("dlg.import.add.n", count), | 3022 | { format_CStr(cstrCount_Lang("dlg.import.add.n", (int) count), |
2815 | uiTextAction_ColorEscape, | 3023 | uiTextAction_ColorEscape, |
2816 | count), | 3024 | count), |
2817 | 0, | 3025 | 0, |
@@ -2870,6 +3078,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2870 | else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { | 3078 | else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { |
2871 | return handlePinch_DocumentWidget_(d, cmd); | 3079 | return handlePinch_DocumentWidget_(d, cmd); |
2872 | } | 3080 | } |
3081 | else if ((startsWith_CStr(cmd, "edgeswipe.") || startsWith_CStr(cmd, "swipe.")) && | ||
3082 | document_App() == d) { | ||
3083 | return handleSwipe_DocumentWidget_(d, cmd); | ||
3084 | } | ||
2873 | return iFalse; | 3085 | return iFalse; |
2874 | } | 3086 | } |
2875 | 3087 | ||
@@ -3318,6 +3530,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3318 | d->contextLink->linkId) }); | 3530 | d->contextLink->linkId) }); |
3319 | } | 3531 | } |
3320 | } | 3532 | } |
3533 | if (equalCase_Rangecc(scheme, "file")) { | ||
3534 | /* Local files may be deleted. */ | ||
3535 | pushBack_Array( | ||
3536 | &items, | ||
3537 | &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape | ||
3538 | "${link.file.delete}", | ||
3539 | 0, | ||
3540 | 0, | ||
3541 | format_CStr("!file.delete confirm:1 path:%s", | ||
3542 | cstrCollect_String( | ||
3543 | localFilePathFromUrl_String(linkUrl))) }); | ||
3544 | } | ||
3321 | } | 3545 | } |
3322 | else if (deviceType_App() == desktop_AppDeviceType) { | 3546 | else if (deviceType_App() == desktop_AppDeviceType) { |
3323 | if (!isEmpty_Range(&d->selectMark)) { | 3547 | if (!isEmpty_Range(&d->selectMark)) { |
@@ -3870,14 +4094,14 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3870 | } | 4094 | } |
3871 | #endif | 4095 | #endif |
3872 | /* Fill the background. */ { | 4096 | /* Fill the background. */ { |
3873 | if (run->linkId && linkFlags & isOpen_GmLinkFlag) { | 4097 | if (run->linkId && linkFlags & isOpen_GmLinkFlag && ~linkFlags & content_GmLinkFlag) { |
3874 | /* Open links get a highlighted background. */ | 4098 | /* Open links get a highlighted background. */ |
3875 | int bg = tmBackgroundOpenLink_ColorId; | 4099 | int bg = tmBackgroundOpenLink_ColorId; |
3876 | const int frame = tmFrameOpenLink_ColorId; | 4100 | const int frame = tmFrameOpenLink_ColorId; |
3877 | iRect wideRect = { init_I2(left_Rect(d->widgetBounds), visPos.y), | 4101 | iRect wideRect = { init_I2(left_Rect(d->widgetBounds), visPos.y), |
3878 | init_I2(width_Rect(d->widgetBounds) + | 4102 | init_I2(width_Rect(d->widgetBounds) + |
3879 | width_Widget(d->widget->scroll), | 4103 | width_Widget(d->widget->scroll), |
3880 | height_Rect(run->visBounds)) }; | 4104 | height_Rect(run->visBounds)) }; |
3881 | /* The first line is composed of two runs that may be drawn in either order, so | 4105 | /* The first line is composed of two runs that may be drawn in either order, so |
3882 | only draw half of the background. */ | 4106 | only draw half of the background. */ |
3883 | if (run->flags & decoration_GmRunFlag) { | 4107 | if (run->flags & decoration_GmRunFlag) { |
@@ -3931,16 +4155,17 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3931 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); | 4155 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); |
3932 | if (ordChar) { | 4156 | if (ordChar) { |
3933 | const char *circle = "\u25ef"; /* Large Circle */ | 4157 | const char *circle = "\u25ef"; /* Large Circle */ |
4158 | const int circleFont = defaultContentRegular_FontId; | ||
3934 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), | 4159 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), |
3935 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(run->font)) }; | 4160 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; |
3936 | drawRange_Text( | 4161 | drawRange_Text( |
3937 | run->font, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); | 4162 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); |
3938 | iRect circleArea = visualBounds_Text(run->font, range_CStr(circle)); | 4163 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); |
3939 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); | 4164 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); |
3940 | drawCentered_Text(defaultContentSmall_FontId, | 4165 | drawCentered_Text(defaultContentSmall_FontId, |
3941 | circleArea, | 4166 | circleArea, |
3942 | iTrue, | 4167 | iTrue, |
3943 | tmQuote_ColorId, | 4168 | tmQuote_ColorId, |
3944 | "%lc", | 4169 | "%lc", |
3945 | (int) ordChar); | 4170 | (int) ordChar); |
3946 | goto runDrawn; | 4171 | goto runDrawn; |
@@ -4178,7 +4403,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
4178 | iDrawBufs * dbuf = d->drawBufs; | 4403 | iDrawBufs * dbuf = d->drawBufs; |
4179 | iPaint p; | 4404 | iPaint p; |
4180 | init_Paint(&p); | 4405 | init_Paint(&p); |
4181 | setClip_Paint(&p, bounds); | 4406 | setClip_Paint(&p, boundsWithoutVisualOffset_Widget(w)); |
4182 | /* Side icon and current heading. */ | 4407 | /* Side icon and current heading. */ |
4183 | if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { | 4408 | if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { |
4184 | const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); | 4409 | const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); |
@@ -4263,7 +4488,8 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, | |||
4263 | /* Swap buffers around to have room available both before and after the visible region. */ | 4488 | /* Swap buffers around to have room available both before and after the visible region. */ |
4264 | allocVisBuffer_DocumentWidget_(d); | 4489 | allocVisBuffer_DocumentWidget_(d); |
4265 | reposition_VisBuf(visBuf, vis); | 4490 | reposition_VisBuf(visBuf, vis); |
4266 | /* Redraw the invalid ranges. */ { | 4491 | /* Redraw the invalid ranges. */ |
4492 | if (~flags_Widget(constAs_Widget(d)) & destroyPending_WidgetFlag) { | ||
4267 | iPaint *p = &ctx->paint; | 4493 | iPaint *p = &ctx->paint; |
4268 | init_Paint(p); | 4494 | init_Paint(p); |
4269 | iForIndices(i, visBuf->buffers) { | 4495 | iForIndices(i, visBuf->buffers) { |
@@ -4427,12 +4653,17 @@ static void prerender_DocumentWidget_(iAny *context) { | |||
4427 | } | 4653 | } |
4428 | 4654 | ||
4429 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 4655 | static void draw_DocumentWidget_(const iDocumentWidget *d) { |
4430 | const iWidget *w = constAs_Widget(d); | 4656 | const iWidget *w = constAs_Widget(d); |
4431 | const iRect bounds = bounds_Widget(w); | 4657 | const iRect bounds = bounds_Widget(w); |
4658 | const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); | ||
4659 | const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); | ||
4432 | if (width_Rect(bounds) <= 0) { | 4660 | if (width_Rect(bounds) <= 0) { |
4433 | return; | 4661 | return; |
4434 | } | 4662 | } |
4435 | // draw_Widget(w); | 4663 | /* TODO: Come up with a better palette caching system. |
4664 | It should be able to recompute cached colors in `History` when the theme has changed. | ||
4665 | Cache the theme seed in `GmDocument`? */ | ||
4666 | // makePaletteGlobal_GmDocument(d->doc); | ||
4436 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { | 4667 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { |
4437 | updateTimestampBuf_DocumentWidget_(d); | 4668 | updateTimestampBuf_DocumentWidget_(d); |
4438 | } | 4669 | } |
@@ -4447,14 +4678,15 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4447 | .vis = vis, | 4678 | .vis = vis, |
4448 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, | 4679 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, |
4449 | }; | 4680 | }; |
4681 | init_Paint(&ctx.paint); | ||
4450 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); | 4682 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); |
4451 | setClip_Paint(&ctx.paint, bounds); | 4683 | setClip_Paint(&ctx.paint, clipBounds); |
4452 | int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); | 4684 | int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); |
4453 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); | 4685 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); |
4454 | /* Text markers. */ | 4686 | /* Text markers. */ |
4455 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | 4687 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; |
4456 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { | 4688 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { |
4457 | SDL_Renderer *render = renderer_Window(get_Window()); | 4689 | SDL_Renderer *render = renderer_Window(get_Window()); |
4458 | ctx.firstMarkRect = zero_Rect(); | 4690 | ctx.firstMarkRect = zero_Rect(); |
4459 | ctx.lastMarkRect = zero_Rect(); | 4691 | ctx.lastMarkRect = zero_Rect(); |
4460 | SDL_SetRenderDrawBlendMode(render, | 4692 | SDL_SetRenderDrawBlendMode(render, |
@@ -4483,7 +4715,6 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4483 | } | 4715 | } |
4484 | } | 4716 | } |
4485 | drawMedia_DocumentWidget_(d, &ctx.paint); | 4717 | drawMedia_DocumentWidget_(d, &ctx.paint); |
4486 | unsetClip_Paint(&ctx.paint); | ||
4487 | /* Fill the top and bottom, in case the document is short. */ | 4718 | /* Fill the top and bottom, in case the document is short. */ |
4488 | if (yTop > top_Rect(bounds)) { | 4719 | if (yTop > top_Rect(bounds)) { |
4489 | fillRect_Paint(&ctx.paint, | 4720 | fillRect_Paint(&ctx.paint, |
@@ -4497,6 +4728,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4497 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), | 4728 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), |
4498 | tmBackground_ColorId); | 4729 | tmBackground_ColorId); |
4499 | } | 4730 | } |
4731 | unsetClip_Paint(&ctx.paint); | ||
4500 | drawSideElements_DocumentWidget_(d); | 4732 | drawSideElements_DocumentWidget_(d); |
4501 | if (prefs_App()->hoverLink && d->hoverLink) { | 4733 | if (prefs_App()->hoverLink && d->hoverLink) { |
4502 | const int font = uiLabel_FontId; | 4734 | const int font = uiLabel_FontId; |
@@ -4558,6 +4790,23 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4558 | drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", | 4790 | drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", |
4559 | size_Range(&mark)); | 4791 | size_Range(&mark)); |
4560 | } | 4792 | } |
4793 | if (w->offsetRef) { | ||
4794 | const int offX = visualOffsetByReference_Widget(w); | ||
4795 | if (offX) { | ||
4796 | setClip_Paint(&ctx.paint, clipBounds); | ||
4797 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | ||
4798 | ctx.paint.alpha = iAbs(offX) / (float) get_Window()->size.x * 300; | ||
4799 | fillRect_Paint(&ctx.paint, bounds, backgroundFadeColor_Widget()); | ||
4800 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | ||
4801 | unsetClip_Paint(&ctx.paint); | ||
4802 | } | ||
4803 | else { | ||
4804 | /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */ | ||
4805 | iWidget *mut = iConstCast(iWidget *, w); | ||
4806 | mut->offsetRef = NULL; | ||
4807 | mut->flags &= ~refChildrenOffset_WidgetFlag; | ||
4808 | } | ||
4809 | } | ||
4561 | } | 4810 | } |
4562 | 4811 | ||
4563 | /*----------------------------------------------------------------------------------------------*/ | 4812 | /*----------------------------------------------------------------------------------------------*/ |
@@ -4625,9 +4874,12 @@ static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { | |||
4625 | d->flags |= urlChanged_DocumentWidgetFlag; | 4874 | d->flags |= urlChanged_DocumentWidgetFlag; |
4626 | set_String(d->mod.url, url); | 4875 | set_String(d->mod.url, url); |
4627 | } | 4876 | } |
4628 | } | 4877 | } |
4629 | 4878 | ||
4630 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { | 4879 | void setUrlFlags_DocumentWidget(iDocumentWidget *d, const iString *url, int setUrlFlags) { |
4880 | iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, | ||
4881 | (setUrlFlags & openedFromSidebar_DocumentWidgetSetUrlFlag) != 0); | ||
4882 | const iBool isFromCache = (setUrlFlags & useCachedContentIfAvailable_DocumentWidgetSetUrlFlag) != 0; | ||
4631 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4883 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4632 | setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); | 4884 | setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); |
4633 | /* See if there a username in the URL. */ | 4885 | /* See if there a username in the URL. */ |
@@ -4639,6 +4891,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo | |||
4639 | 4891 | ||
4640 | void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, | 4892 | void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, |
4641 | const iBlock *source) { | 4893 | const iBlock *source) { |
4894 | d->flags &= ~openedFromSidebar_DocumentWidgetFlag; | ||
4642 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4895 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4643 | setUrl_DocumentWidget_(d, url); | 4896 | setUrl_DocumentWidget_(d, url); |
4644 | parseUser_DocumentWidget_(d); | 4897 | parseUser_DocumentWidget_(d); |
@@ -4647,7 +4900,7 @@ void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, cons | |||
4647 | initCurrent_Time(&resp->when); | 4900 | initCurrent_Time(&resp->when); |
4648 | set_String(&resp->meta, mime); | 4901 | set_String(&resp->meta, mime); |
4649 | set_Block(&resp->body, source); | 4902 | set_Block(&resp->body, source); |
4650 | updateFromCachedResponse_DocumentWidget_(d, 0, resp); | 4903 | updateFromCachedResponse_DocumentWidget_(d, 0, resp, NULL); |
4651 | delete_GmResponse(resp); | 4904 | delete_GmResponse(resp); |
4652 | } | 4905 | } |
4653 | 4906 | ||
@@ -4656,12 +4909,12 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { | |||
4656 | delete_History(d->mod.history); | 4909 | delete_History(d->mod.history); |
4657 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); | 4910 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); |
4658 | d->mod.history = copy_History(orig->mod.history); | 4911 | d->mod.history = copy_History(orig->mod.history); |
4659 | setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); | 4912 | setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag); |
4660 | return d; | 4913 | return d; |
4661 | } | 4914 | } |
4662 | 4915 | ||
4663 | void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { | 4916 | void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { |
4664 | setUrlFromCache_DocumentWidget(d, url, iFalse); | 4917 | setUrlFlags_DocumentWidget(d, url, 0); |
4665 | } | 4918 | } |
4666 | 4919 | ||
4667 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { | 4920 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { |
@@ -4672,6 +4925,11 @@ void setRedirectCount_DocumentWidget(iDocumentWidget *d, int count) { | |||
4672 | d->redirectCount = count; | 4925 | d->redirectCount = count; |
4673 | } | 4926 | } |
4674 | 4927 | ||
4928 | void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *d, iBool fromSidebar) { | ||
4929 | iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, fromSidebar); | ||
4930 | // setCachedDocument_History(d->mod.history, d->doc, fromSidebar); | ||
4931 | } | ||
4932 | |||
4675 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | 4933 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { |
4676 | return d->request != NULL; | 4934 | return d->request != NULL; |
4677 | } | 4935 | } |
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index c038f981..1921b25a 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h | |||
@@ -45,11 +45,17 @@ const iString * bookmarkTitle_DocumentWidget (const iDocumentWidget *); | |||
45 | const iString * feedTitle_DocumentWidget (const iDocumentWidget *); | 45 | const iString * feedTitle_DocumentWidget (const iDocumentWidget *); |
46 | int documentWidth_DocumentWidget (const iDocumentWidget *); | 46 | int documentWidth_DocumentWidget (const iDocumentWidget *); |
47 | 47 | ||
48 | enum iDocumentWidgetSetUrlFlags { | ||
49 | useCachedContentIfAvailable_DocumentWidgetSetUrlFlag = iBit(1), | ||
50 | openedFromSidebar_DocumentWidgetSetUrlFlag = iBit(2), | ||
51 | }; | ||
52 | |||
48 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); | 53 | void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); |
49 | void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache); | 54 | void setUrlFlags_DocumentWidget (iDocumentWidget *, const iString *url, int setUrlFlags); |
50 | void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source); | 55 | void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source); |
51 | void setInitialScroll_DocumentWidget (iDocumentWidget *, float normScrollY); /* set after content received */ | 56 | void setInitialScroll_DocumentWidget (iDocumentWidget *, float normScrollY); /* set after content received */ |
52 | void setRedirectCount_DocumentWidget (iDocumentWidget *, int count); | 57 | void setRedirectCount_DocumentWidget (iDocumentWidget *, int count); |
53 | void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText); | 58 | void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText); |
59 | void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *, iBool fromSidebar); | ||
54 | 60 | ||
55 | void updateSize_DocumentWidget (iDocumentWidget *); | 61 | void updateSize_DocumentWidget (iDocumentWidget *); |
diff --git a/src/ui/indicatorwidget.c b/src/ui/indicatorwidget.c index 4a829ae3..bc0bd0fa 100644 --- a/src/ui/indicatorwidget.c +++ b/src/ui/indicatorwidget.c | |||
@@ -109,7 +109,7 @@ void draw_IndicatorWidget_(const iIndicatorWidget *d) { | |||
109 | colors[0] = black_ColorId; | 109 | colors[0] = black_ColorId; |
110 | } | 110 | } |
111 | fillRect_Paint(&p, | 111 | fillRect_Paint(&p, |
112 | (iRect){ topLeft_Rect(rect), init_I2(pos * width_Rect(rect), gap_UI / 4)}, | 112 | (iRect){ topLeft_Rect(rect), init_I2(pos * width_Rect(rect), gap_UI / 3)}, |
113 | colors[isCompleted_IndicatorWidget_(d) ? 1 : 0]); | 113 | colors[isCompleted_IndicatorWidget_(d) ? 1 : 0]); |
114 | } | 114 | } |
115 | } | 115 | } |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 32fb5ccb..b108ee17 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -80,6 +80,7 @@ enum iInputWidgetFlag { | |||
80 | markWords_InputWidgetFlag = iBit(8), | 80 | markWords_InputWidgetFlag = iBit(8), |
81 | needUpdateBuffer_InputWidgetFlag = iBit(9), | 81 | needUpdateBuffer_InputWidgetFlag = iBit(9), |
82 | enterKeyEnabled_InputWidgetFlag = iBit(10), | 82 | enterKeyEnabled_InputWidgetFlag = iBit(10), |
83 | enterKeyInsertsLineFeed_InputWidgetFlag = iBit(11), | ||
83 | }; | 84 | }; |
84 | 85 | ||
85 | /*----------------------------------------------------------------------------------------------*/ | 86 | /*----------------------------------------------------------------------------------------------*/ |
@@ -347,8 +348,11 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
347 | d->lastCursor = 0; | 348 | d->lastCursor = 0; |
348 | d->cursorLine = 0; | 349 | d->cursorLine = 0; |
349 | d->lastUpdateWidth = 0; | 350 | d->lastUpdateWidth = 0; |
350 | d->verticalMoveX = -1; /* TODO: Use this. */ | 351 | d->verticalMoveX = -1; /* TODO: Use this. */ |
351 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; | 352 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; |
353 | if (deviceType_App() != desktop_AppDeviceType) { | ||
354 | d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; | ||
355 | } | ||
352 | iZap(d->mark); | 356 | iZap(d->mark); |
353 | setMaxLen_InputWidget(d, maxLen); | 357 | setMaxLen_InputWidget(d, maxLen); |
354 | d->maxLayoutLines = iInvalidSize; | 358 | d->maxLayoutLines = iInvalidSize; |
@@ -464,6 +468,10 @@ void setValidator_InputWidget(iInputWidget *d, iInputWidgetValidatorFunc validat | |||
464 | d->validatorContext = context; | 468 | d->validatorContext = context; |
465 | } | 469 | } |
466 | 470 | ||
471 | void setEnterInsertsLF_InputWidget(iInputWidget *d, iBool enterInsertsLF) { | ||
472 | iChangeFlags(d->inFlags, enterKeyInsertsLineFeed_InputWidgetFlag, enterInsertsLF); | ||
473 | } | ||
474 | |||
467 | void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { | 475 | void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { |
468 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); | 476 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); |
469 | } | 477 | } |
@@ -714,12 +722,12 @@ iLocalDef iBool isLastLine_InputWidget_(const iInputWidget *d, const iInputLine | |||
714 | } | 722 | } |
715 | 723 | ||
716 | static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) { | 724 | static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) { |
725 | size_t index = line->offset; | ||
717 | if (x <= 0) { | 726 | if (x <= 0) { |
718 | return line->offset; | 727 | return index; |
719 | } | 728 | } |
720 | const char *endPos; | 729 | const char *endPos; |
721 | tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos); | 730 | tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos); |
722 | size_t index = line->offset; | ||
723 | if (endPos == constEnd_String(&line->text)) { | 731 | if (endPos == constEnd_String(&line->text)) { |
724 | index += line->len; | 732 | index += line->len; |
725 | } | 733 | } |
@@ -1166,7 +1174,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1166 | case SDLK_KP_ENTER: | 1174 | case SDLK_KP_ENTER: |
1167 | if (mods == KMOD_SHIFT || (d->maxLen == 0 && | 1175 | if (mods == KMOD_SHIFT || (d->maxLen == 0 && |
1168 | ~d->inFlags & isUrl_InputWidgetFlag && | 1176 | ~d->inFlags & isUrl_InputWidgetFlag && |
1169 | deviceType_App() != desktop_AppDeviceType)) { | 1177 | d->inFlags & enterKeyInsertsLineFeed_InputWidgetFlag)) { |
1170 | pushUndo_InputWidget_(d); | 1178 | pushUndo_InputWidget_(d); |
1171 | deleteMarked_InputWidget_(d); | 1179 | deleteMarked_InputWidget_(d); |
1172 | insertChar_InputWidget_(d, '\n'); | 1180 | insertChar_InputWidget_(d, '\n'); |
@@ -1357,13 +1365,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1357 | } | 1365 | } |
1358 | iPaint p; | 1366 | iPaint p; |
1359 | init_Paint(&p); | 1367 | init_Paint(&p); |
1360 | /* `lines` is already up to date and ready for drawing. */ | 1368 | /* `lines` is already up to date and ready for drawing. */ |
1361 | /* TODO: If empty, draw the hint. */ | ||
1362 | // iString *text = visText_InputWidget_(d); | ||
1363 | // if (isWhite_(text) && !isEmpty_String(&d->hint)) { | ||
1364 | // set_String(text, &d->hint); | ||
1365 | // isHint = iTrue; | ||
1366 | // } | ||
1367 | fillRect_Paint( | 1369 | fillRect_Paint( |
1368 | &p, bounds, isFocused ? uiInputBackgroundFocused_ColorId : uiInputBackground_ColorId); | 1370 | &p, bounds, isFocused ? uiInputBackgroundFocused_ColorId : uiInputBackground_ColorId); |
1369 | drawRectThickness_Paint(&p, | 1371 | drawRectThickness_Paint(&p, |
@@ -1374,7 +1376,6 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1374 | setClip_Paint(&p, adjusted_Rect(bounds, init_I2(d->leftPadding, 0), | 1376 | setClip_Paint(&p, adjusted_Rect(bounds, init_I2(d->leftPadding, 0), |
1375 | init_I2(-d->rightPadding, w->flags & extraPadding_WidgetFlag ? -gap_UI / 2 : 0))); | 1377 | init_I2(-d->rightPadding, w->flags & extraPadding_WidgetFlag ? -gap_UI / 2 : 0))); |
1376 | const iRect contentBounds = contentBounds_InputWidget_(d); | 1378 | const iRect contentBounds = contentBounds_InputWidget_(d); |
1377 | // const iInt2 textOrigin = textOrigin_InputWidget_(d); //, cstr_String(text)); | ||
1378 | iInt2 drawPos = topLeft_Rect(contentBounds); | 1379 | iInt2 drawPos = topLeft_Rect(contentBounds); |
1379 | const int fg = isHint ? uiAnnotation_ColorId | 1380 | const int fg = isHint ? uiAnnotation_ColorId |
1380 | : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId | 1381 | : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId |
@@ -1418,19 +1419,6 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1418 | drawPos.y += lineHeight_Text(d->font); | 1419 | drawPos.y += lineHeight_Text(d->font); |
1419 | } | 1420 | } |
1420 | } | 1421 | } |
1421 | // if (d->buffered && !isFocused && !isHint) { | ||
1422 | // /* Most input widgets will use this, since only one is focused at a time. */ | ||
1423 | // draw_TextBuf(d->buffered, textOrigin, white_ColorId); | ||
1424 | // } | ||
1425 | // else { | ||
1426 | // draw_Text(d->font, | ||
1427 | // textOrigin, | ||
1428 | // isHint ? uiAnnotation_ColorId | ||
1429 | // : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId | ||
1430 | // : uiInputText_ColorId, | ||
1431 | // "%s", | ||
1432 | // cstr_String(text)); | ||
1433 | // } | ||
1434 | unsetClip_Paint(&p); | 1422 | unsetClip_Paint(&p); |
1435 | /* Cursor blinking. */ | 1423 | /* Cursor blinking. */ |
1436 | if (isFocused && d->cursorVis) { | 1424 | if (isFocused && d->cursorVis) { |
@@ -1474,7 +1462,6 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1474 | deinit_String(&cur); | 1462 | deinit_String(&cur); |
1475 | } | 1463 | } |
1476 | } | 1464 | } |
1477 | // delete_String(text); | ||
1478 | drawChildren_Widget(w); | 1465 | drawChildren_Widget(w); |
1479 | } | 1466 | } |
1480 | 1467 | ||
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index 5c39aae0..c70d9ad6 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -41,20 +41,21 @@ struct Impl_InputWidgetContentPadding { | |||
41 | 41 | ||
42 | typedef void (*iInputWidgetValidatorFunc)(iInputWidget *, void *context); | 42 | typedef void (*iInputWidgetValidatorFunc)(iInputWidget *, void *context); |
43 | 43 | ||
44 | void setHint_InputWidget (iInputWidget *, const char *hintText); | 44 | void setHint_InputWidget (iInputWidget *, const char *hintText); |
45 | void setMode_InputWidget (iInputWidget *, enum iInputMode mode); | 45 | void setMode_InputWidget (iInputWidget *, enum iInputMode mode); |
46 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); | 46 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); |
47 | void setText_InputWidget (iInputWidget *, const iString *text); | 47 | void setText_InputWidget (iInputWidget *, const iString *text); |
48 | void setTextCStr_InputWidget (iInputWidget *, const char *cstr); | 48 | void setTextCStr_InputWidget (iInputWidget *, const char *cstr); |
49 | void setFont_InputWidget (iInputWidget *, int fontId); | 49 | void setFont_InputWidget (iInputWidget *, int fontId); |
50 | void setCursor_InputWidget (iInputWidget *, size_t pos); | 50 | void setCursor_InputWidget (iInputWidget *, size_t pos); |
51 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ | 51 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ |
52 | void setMaxLayoutLines_InputWidget (iInputWidget *, size_t maxLayoutLines); | 52 | void setMaxLayoutLines_InputWidget (iInputWidget *, size_t maxLayoutLines); |
53 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); | 53 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); |
54 | void setEnterInsertsLF_InputWidget (iInputWidget *, iBool enterInsertsLF); | ||
54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); | 55 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); |
55 | void begin_InputWidget (iInputWidget *); | 56 | void begin_InputWidget (iInputWidget *); |
56 | void end_InputWidget (iInputWidget *, iBool accept); | 57 | void end_InputWidget (iInputWidget *, iBool accept); |
57 | void selectAll_InputWidget (iInputWidget *); | 58 | void selectAll_InputWidget (iInputWidget *); |
58 | 59 | ||
59 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); | 60 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); |
60 | void setSensitiveContent_InputWidget (iInputWidget *, iBool isSensitive); | 61 | void setSensitiveContent_InputWidget (iInputWidget *, iBool isSensitive); |
@@ -62,15 +63,13 @@ void setUrlContent_InputWidget (iInputWidget *, iBool isUrl); | |||
62 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); | 63 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); |
63 | void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape); | 64 | void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape); |
64 | 65 | ||
65 | const iString * text_InputWidget (const iInputWidget *); | 66 | iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *); |
67 | const iString * text_InputWidget (const iInputWidget *); | ||
66 | 68 | ||
67 | iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) { | 69 | iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) { |
68 | return cstr_String(text_InputWidget(d)); | 70 | return cstr_String(text_InputWidget(d)); |
69 | } | 71 | } |
70 | 72 | ||
71 | iInputWidgetContentPadding | ||
72 | contentPadding_InputWidget (const iInputWidget *); | ||
73 | |||
74 | iLocalDef iInputWidget *newHint_InputWidget(size_t maxLen, const char *hint) { | 73 | iLocalDef iInputWidget *newHint_InputWidget(size_t maxLen, const char *hint) { |
75 | iInputWidget *d = new_InputWidget(maxLen); | 74 | iInputWidget *d = new_InputWidget(maxLen); |
76 | setHint_InputWidget(d, hint); | 75 | setHint_InputWidget(d, hint); |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 95f281be..b68ab793 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -44,6 +44,9 @@ struct Impl_LabelWidget { | |||
44 | struct { | 44 | struct { |
45 | uint8_t alignVisual : 1; /* align according to visible bounds, not font metrics */ | 45 | uint8_t alignVisual : 1; /* align according to visible bounds, not font metrics */ |
46 | uint8_t noAutoMinHeight : 1; /* minimum height is not set automatically */ | 46 | uint8_t noAutoMinHeight : 1; /* minimum height is not set automatically */ |
47 | uint8_t drawAsOutline : 1; /* draw as outline, filled with background color */ | ||
48 | uint8_t noTopFrame : 1; | ||
49 | uint8_t wrap : 1; | ||
47 | } flags; | 50 | } flags; |
48 | }; | 51 | }; |
49 | 52 | ||
@@ -206,7 +209,7 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int | |||
206 | } | 209 | } |
207 | } | 210 | } |
208 | int colorEscape = none_ColorId; | 211 | int colorEscape = none_ColorId; |
209 | if (startsWith_String(&d->label, "\r")) { | 212 | if (startsWith_String(&d->label, "\v")) { |
210 | colorEscape = cstr_String(&d->label)[1] - asciiBase_ColorEscape; /* TODO: can be two bytes long */ | 213 | colorEscape = cstr_String(&d->label)[1] - asciiBase_ColorEscape; /* TODO: can be two bytes long */ |
211 | } | 214 | } |
212 | if (isHover_LabelWidget_(d)) { | 215 | if (isHover_LabelWidget_(d)) { |
@@ -289,7 +292,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
289 | }; | 292 | }; |
290 | drawLines_Paint(&p, points + 2, 3, frame2); | 293 | drawLines_Paint(&p, points + 2, 3, frame2); |
291 | drawLines_Paint( | 294 | drawLines_Paint( |
292 | &p, points, !isHover && flags & noTopFrame_WidgetFlag ? 2 : 3, frame); | 295 | &p, points, !isHover && flags & d->flags.noTopFrame ? 2 : 3, frame); |
293 | } | 296 | } |
294 | } | 297 | } |
295 | setClip_Paint(&p, rect); | 298 | setClip_Paint(&p, rect); |
@@ -315,7 +318,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
315 | cstr_String(&str)); | 318 | cstr_String(&str)); |
316 | deinit_String(&str); | 319 | deinit_String(&str); |
317 | } | 320 | } |
318 | if (flags & wrapText_WidgetFlag) { | 321 | if (d->flags.wrap) { |
319 | const iRect inner = adjusted_Rect(innerBounds_Widget(w), init_I2(iconPad, 0), zero_I2()); | 322 | const iRect inner = adjusted_Rect(innerBounds_Widget(w), init_I2(iconPad, 0), zero_I2()); |
320 | const int wrap = inner.size.x; | 323 | const int wrap = inner.size.x; |
321 | drawWrapRange_Text(d->font, topLeft_Rect(inner), wrap, fg, range_String(&d->label)); | 324 | drawWrapRange_Text(d->font, topLeft_Rect(inner), wrap, fg, range_String(&d->label)); |
@@ -348,14 +351,14 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
348 | cstr_String(&d->label)); | 351 | cstr_String(&d->label)); |
349 | } | 352 | } |
350 | else { | 353 | else { |
351 | drawCentered_Text(d->font, | 354 | drawCenteredOutline_Text( |
352 | adjusted_Rect(bounds, | 355 | d->font, |
353 | add_I2(zero_I2(), init_I2(iconPad, 0)), | 356 | adjusted_Rect(bounds, add_I2(zero_I2(), init_I2(iconPad, 0)), neg_I2(zero_I2())), |
354 | neg_I2(zero_I2())), | 357 | d->flags.alignVisual, |
355 | d->flags.alignVisual, | 358 | d->flags.drawAsOutline ? fg : none_ColorId, |
356 | fg, | 359 | d->flags.drawAsOutline ? d->widget.bgColor : fg, |
357 | "%s", | 360 | "%s", |
358 | cstr_String(&d->label)); | 361 | cstr_String(&d->label)); |
359 | } | 362 | } |
360 | if (flags & chevron_WidgetFlag) { | 363 | if (flags & chevron_WidgetFlag) { |
361 | const iRect chRect = rect; | 364 | const iRect chRect = rect; |
@@ -370,7 +373,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
370 | 373 | ||
371 | static void sizeChanged_LabelWidget_(iLabelWidget *d) { | 374 | static void sizeChanged_LabelWidget_(iLabelWidget *d) { |
372 | iWidget *w = as_Widget(d); | 375 | iWidget *w = as_Widget(d); |
373 | if (flags_Widget(w) & wrapText_WidgetFlag) { | 376 | if (d->flags.wrap) { |
374 | if (flags_Widget(w) & fixedHeight_WidgetFlag) { | 377 | if (flags_Widget(w) & fixedHeight_WidgetFlag) { |
375 | /* Calculate a new height based on the wrapping. */ | 378 | /* Calculate a new height based on the wrapping. */ |
376 | w->rect.size.y = advanceWrapRange_Text( | 379 | w->rect.size.y = advanceWrapRange_Text( |
@@ -408,7 +411,7 @@ void updateSize_LabelWidget(iLabelWidget *d) { | |||
408 | w->minSize.y = size.y; /* vertically text must remain visible */ | 411 | w->minSize.y = size.y; /* vertically text must remain visible */ |
409 | } | 412 | } |
410 | /* Wrapped text implies that width must be defined by arrangement. */ | 413 | /* Wrapped text implies that width must be defined by arrangement. */ |
411 | if (!(flags & (fixedWidth_WidgetFlag | wrapText_WidgetFlag))) { | 414 | if (~flags & fixedWidth_WidgetFlag && !d->flags.wrap) { |
412 | w->rect.size.x = size.x; | 415 | w->rect.size.x = size.x; |
413 | } | 416 | } |
414 | if (~flags & fixedHeight_WidgetFlag) { | 417 | if (~flags & fixedHeight_WidgetFlag) { |
@@ -440,8 +443,11 @@ void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { | |||
440 | d->kmods = 0; | 443 | d->kmods = 0; |
441 | init_Click(&d->click, d, !isEmpty_String(&d->command) ? SDL_BUTTON_LEFT : 0); | 444 | init_Click(&d->click, d, !isEmpty_String(&d->command) ? SDL_BUTTON_LEFT : 0); |
442 | setFlags_Widget(w, hover_WidgetFlag, d->click.button != 0); | 445 | setFlags_Widget(w, hover_WidgetFlag, d->click.button != 0); |
443 | d->flags.alignVisual = iFalse; | 446 | d->flags.alignVisual = iFalse; |
444 | d->flags.noAutoMinHeight = iFalse; | 447 | d->flags.noAutoMinHeight = iFalse; |
448 | d->flags.drawAsOutline = iFalse; | ||
449 | d->flags.noTopFrame = iFalse; | ||
450 | d->flags.wrap = iFalse; | ||
445 | updateSize_LabelWidget(d); | 451 | updateSize_LabelWidget(d); |
446 | updateKey_LabelWidget_(d); /* could be bound to another key */ | 452 | updateKey_LabelWidget_(d); /* could be bound to another key */ |
447 | } | 453 | } |
@@ -481,6 +487,20 @@ void setNoAutoMinHeight_LabelWidget(iLabelWidget *d, iBool noAutoMinHeight) { | |||
481 | } | 487 | } |
482 | } | 488 | } |
483 | 489 | ||
490 | void setNoTopFrame_LabelWidget(iLabelWidget *d, iBool noTopFrame) { | ||
491 | d->flags.noTopFrame = noTopFrame; | ||
492 | } | ||
493 | |||
494 | void setWrap_LabelWidget(iLabelWidget *d, iBool wrap) { | ||
495 | d->flags.wrap = wrap; | ||
496 | } | ||
497 | |||
498 | void setOutline_LabelWidget(iLabelWidget *d, iBool drawAsOutline) { | ||
499 | if (d) { | ||
500 | d->flags.drawAsOutline = drawAsOutline; | ||
501 | } | ||
502 | } | ||
503 | |||
484 | void updateText_LabelWidget(iLabelWidget *d, const iString *text) { | 504 | void updateText_LabelWidget(iLabelWidget *d, const iString *text) { |
485 | set_String(&d->label, text); | 505 | set_String(&d->label, text); |
486 | set_String(&d->srcLabel, text); | 506 | set_String(&d->srcLabel, text); |
@@ -495,6 +515,11 @@ void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) { | |||
495 | refresh_Widget(&d->widget); | 515 | refresh_Widget(&d->widget); |
496 | } | 516 | } |
497 | 517 | ||
518 | void updateTextAndResizeWidthCStr_LabelWidget(iLabelWidget *d, const char *text) { | ||
519 | updateTextCStr_LabelWidget(d, text); | ||
520 | d->widget.rect.size.x = defaultSize_LabelWidget(d).x; | ||
521 | } | ||
522 | |||
498 | void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) { | 523 | void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) { |
499 | setCStr_String(&d->label, text); | 524 | setCStr_String(&d->label, text); |
500 | set_String(&d->srcLabel, &d->label); | 525 | set_String(&d->srcLabel, &d->label); |
@@ -537,6 +562,10 @@ iChar icon_LabelWidget(const iLabelWidget *d) { | |||
537 | return d->icon; | 562 | return d->icon; |
538 | } | 563 | } |
539 | 564 | ||
565 | iBool isWrapped_LabelWidget(const iLabelWidget *d) { | ||
566 | return d->flags.wrap; | ||
567 | } | ||
568 | |||
540 | const iString *text_LabelWidget(const iLabelWidget *d) { | 569 | const iString *text_LabelWidget(const iLabelWidget *d) { |
541 | if (!d) return collectNew_String(); | 570 | if (!d) return collectNew_String(); |
542 | return &d->label; | 571 | return &d->label; |
diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h index e38a1dc8..b8b6fd87 100644 --- a/src/ui/labelwidget.h +++ b/src/ui/labelwidget.h | |||
@@ -31,6 +31,9 @@ iDeclareObjectConstructionArgs(LabelWidget, const char *label, const char *comma | |||
31 | 31 | ||
32 | void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); | 32 | void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); |
33 | void setNoAutoMinHeight_LabelWidget(iLabelWidget *, iBool noAutoMinHeight); | 33 | void setNoAutoMinHeight_LabelWidget(iLabelWidget *, iBool noAutoMinHeight); |
34 | void setNoTopFrame_LabelWidget (iLabelWidget *, iBool noTopFrame); | ||
35 | void setWrap_LabelWidget (iLabelWidget *, iBool wrap); | ||
36 | void setOutline_LabelWidget (iLabelWidget *, iBool drawAsOutline); | ||
34 | void setFont_LabelWidget (iLabelWidget *, int fontId); | 37 | void setFont_LabelWidget (iLabelWidget *, int fontId); |
35 | void setTextColor_LabelWidget (iLabelWidget *, int color); | 38 | void setTextColor_LabelWidget (iLabelWidget *, int color); |
36 | void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */ | 39 | void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */ |
@@ -43,12 +46,15 @@ void updateSize_LabelWidget (iLabelWidget *); | |||
43 | void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */ | 46 | void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */ |
44 | void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */ | 47 | void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */ |
45 | 48 | ||
49 | void updateTextAndResizeWidthCStr_LabelWidget (iLabelWidget *, const char *text); | ||
50 | |||
46 | iInt2 defaultSize_LabelWidget (const iLabelWidget *); | 51 | iInt2 defaultSize_LabelWidget (const iLabelWidget *); |
47 | int font_LabelWidget (const iLabelWidget *); | 52 | int font_LabelWidget (const iLabelWidget *); |
48 | const iString * text_LabelWidget (const iLabelWidget *); | 53 | const iString * text_LabelWidget (const iLabelWidget *); |
49 | const iString * sourceText_LabelWidget (const iLabelWidget *); /* untranslated */ | 54 | const iString * sourceText_LabelWidget (const iLabelWidget *); /* untranslated */ |
50 | const iString * command_LabelWidget (const iLabelWidget *); | 55 | const iString * command_LabelWidget (const iLabelWidget *); |
51 | iChar icon_LabelWidget (const iLabelWidget *); | 56 | iChar icon_LabelWidget (const iLabelWidget *); |
57 | iBool isWrapped_LabelWidget (const iLabelWidget *); | ||
52 | 58 | ||
53 | iLabelWidget *newKeyMods_LabelWidget(const char *label, int key, int kmods, const char *command); | 59 | iLabelWidget *newKeyMods_LabelWidget(const char *label, int key, int kmods, const char *command); |
54 | iLabelWidget *newColor_LabelWidget (const char *text, int color); | 60 | iLabelWidget *newColor_LabelWidget (const char *text, int color); |
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c index bc417fc3..fa09b214 100644 --- a/src/ui/mediaui.c +++ b/src/ui/mediaui.c | |||
@@ -118,7 +118,7 @@ void draw_PlayerUI(iPlayerUI *d, iPaint *p) { | |||
118 | isPaused_Player(d->player) ? "\U0001f782" : "\u23f8", | 118 | isPaused_Player(d->player) ? "\U0001f782" : "\u23f8", |
119 | uiContent_FontId); | 119 | uiContent_FontId); |
120 | drawPlayerButton_(p, d->rewindRect, "\u23ee", uiContent_FontId); | 120 | drawPlayerButton_(p, d->rewindRect, "\u23ee", uiContent_FontId); |
121 | drawPlayerButton_(p, d->menuRect, "\U0001d362", uiContent_FontId); | 121 | drawPlayerButton_(p, d->menuRect, menu_Icon, uiContent_FontId); |
122 | if (!isAdjusting) { | 122 | if (!isAdjusting) { |
123 | drawPlayerButton_( | 123 | drawPlayerButton_( |
124 | p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); | 124 | p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); |
diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 263fc141..0ff3fe85 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c | |||
@@ -120,7 +120,7 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd) | |||
120 | } | 120 | } |
121 | iForEach(ObjectList, i, children_Widget(detailStack)) { | 121 | iForEach(ObjectList, i, children_Widget(detailStack)) { |
122 | iWidget *panel = i.object; | 122 | iWidget *panel = i.object; |
123 | setFlags_Widget(panel, edgeDraggable_WidgetFlag, !isSideBySide); | 123 | setFlags_Widget(panel, leftEdgeDraggable_WidgetFlag, !isSideBySide); |
124 | if (isSideBySide) { | 124 | if (isSideBySide) { |
125 | setVisualOffset_Widget(panel, 0, 0, 0); | 125 | setVisualOffset_Widget(panel, 0, 0, 0); |
126 | } | 126 | } |
@@ -150,8 +150,7 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) { | |||
150 | setFlags_Widget(button, selected_WidgetFlag, iTrue); | 150 | setFlags_Widget(button, selected_WidgetFlag, iTrue); |
151 | return iTrue; | 151 | return iTrue; |
152 | } | 152 | } |
153 | if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd) && | 153 | if (equal_Command(cmd, "swipe.back")) { |
154 | argLabel_Command(cmd, "button") == SDL_BUTTON_X1) { | ||
155 | postCommand_App("panel.close"); | 154 | postCommand_App("panel.close"); |
156 | return iTrue; | 155 | return iTrue; |
157 | } | 156 | } |
@@ -201,6 +200,8 @@ static iBool isTwoColumnPage_(iWidget *d) { | |||
201 | 200 | ||
202 | static iBool isOmittedPref_(const iString *id) { | 201 | static iBool isOmittedPref_(const iString *id) { |
203 | static const char *omittedPrefs[] = { | 202 | static const char *omittedPrefs[] = { |
203 | "prefs.userfont", | ||
204 | "prefs.animate", | ||
204 | "prefs.smoothscroll", | 205 | "prefs.smoothscroll", |
205 | "prefs.imageloadscroll", | 206 | "prefs.imageloadscroll", |
206 | "prefs.pinsplit", | 207 | "prefs.pinsplit", |
@@ -413,7 +414,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
413 | setFlags_Widget(sheet, | 414 | setFlags_Widget(sheet, |
414 | frameless_WidgetFlag | | 415 | frameless_WidgetFlag | |
415 | //resizeWidthOfChildren_WidgetFlag | | 416 | //resizeWidthOfChildren_WidgetFlag | |
416 | edgeDraggable_WidgetFlag | | 417 | leftEdgeDraggable_WidgetFlag | |
417 | commandOnClick_WidgetFlag, | 418 | commandOnClick_WidgetFlag, |
418 | iTrue); | 419 | iTrue); |
419 | iPtrArray * contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ | 420 | iPtrArray * contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ |
@@ -446,7 +447,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
446 | } | 447 | } |
447 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | 448 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); |
448 | /* Slide top panel with detail panels. */ { | 449 | /* Slide top panel with detail panels. */ { |
449 | setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); | 450 | setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); |
450 | topPanel->offsetRef = detailStack; | 451 | topPanel->offsetRef = detailStack; |
451 | } | 452 | } |
452 | if (prefsTabs) { | 453 | if (prefsTabs) { |
@@ -472,7 +473,8 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
472 | 0x02699, /* gear */ | 473 | 0x02699, /* gear */ |
473 | 0x1f4f1, /* mobile phone */ | 474 | 0x1f4f1, /* mobile phone */ |
474 | 0x1f3a8, /* palette */ | 475 | 0x1f3a8, /* palette */ |
475 | 0x1f523, | 476 | 0x1f5da, /* aA */ |
477 | 0x1f660, /* pointing bud */ | ||
476 | 0x1f5a7, /* computer network */ | 478 | 0x1f5a7, /* computer network */ |
477 | }; | 479 | }; |
478 | setIcon_LabelWidget(panelButton, icons[i]); | 480 | setIcon_LabelWidget(panelButton, icons[i]); |
@@ -621,6 +623,12 @@ void finalizeSheet_Mobile(iWidget *sheet) { | |||
621 | /* Additional elements for preferences. */ | 623 | /* Additional elements for preferences. */ |
622 | if (isPrefs) { | 624 | if (isPrefs) { |
623 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | 625 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); |
626 | /* Management. */ { | ||
627 | iLabelWidget *idManButton = addChildFlags_Widget(topPanel, | ||
628 | iClob(makePanelButton_(person_Icon " ${sidebar.identities}", "panel.open")), | ||
629 | chevron_WidgetFlag | borderTop_WidgetFlag); | ||
630 | } | ||
631 | addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); | ||
624 | iLabelWidget *aboutButton = addChildFlags_Widget(topPanel, | 632 | iLabelWidget *aboutButton = addChildFlags_Widget(topPanel, |
625 | iClob(makePanelButton_(planet_Icon " ${menu.about}", "panel.open")), | 633 | iClob(makePanelButton_(planet_Icon " ${menu.about}", "panel.open")), |
626 | chevron_WidgetFlag | borderTop_WidgetFlag); | 634 | chevron_WidgetFlag | borderTop_WidgetFlag); |
diff --git a/src/ui/root.c b/src/ui/root.c index 15548e74..5266978b 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -52,10 +52,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
52 | 52 | ||
53 | #include <SDL_timer.h> | 53 | #include <SDL_timer.h> |
54 | 54 | ||
55 | #if defined (iPlatformAppleDesktop) | ||
56 | # define iHaveNativeMenus | ||
57 | #endif | ||
58 | |||
59 | #if defined (iPlatformPcDesktop) | 55 | #if defined (iPlatformPcDesktop) |
60 | /* TODO: Submenus wouldn't hurt here. */ | 56 | /* TODO: Submenus wouldn't hurt here. */ |
61 | static const iMenuItem navMenuItems_[] = { | 57 | static const iMenuItem navMenuItems_[] = { |
@@ -90,6 +86,7 @@ static const iMenuItem navMenuItems_[] = { | |||
90 | #if defined (iPlatformAppleMobile) | 86 | #if defined (iPlatformAppleMobile) |
91 | /* Tablet menu. */ | 87 | /* Tablet menu. */ |
92 | static const iMenuItem tabletNavMenuItems_[] = { | 88 | static const iMenuItem tabletNavMenuItems_[] = { |
89 | { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" }, | ||
93 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, | 90 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, |
94 | { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, | 91 | { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, |
95 | { "---", 0, 0, NULL }, | 92 | { "---", 0, 0, NULL }, |
@@ -110,6 +107,7 @@ static const iMenuItem tabletNavMenuItems_[] = { | |||
110 | 107 | ||
111 | /* Phone menu. */ | 108 | /* Phone menu. */ |
112 | static const iMenuItem phoneNavMenuItems_[] = { | 109 | static const iMenuItem phoneNavMenuItems_[] = { |
110 | { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" }, | ||
113 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, | 111 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, |
114 | { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, | 112 | { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, |
115 | { "---", 0, 0, NULL }, | 113 | { "---", 0, 0, NULL }, |
@@ -273,7 +271,8 @@ void destroyPending_Root(iRoot *d) { | |||
273 | setCurrent_Root(d); | 271 | setCurrent_Root(d); |
274 | iForEach(PtrSet, i, d->pendingDestruction) { | 272 | iForEach(PtrSet, i, d->pendingDestruction) { |
275 | iWidget *widget = *i.value; | 273 | iWidget *widget = *i.value; |
276 | if (!isFinished_Anim(&widget->visualOffset)) { | 274 | if (!isFinished_Anim(&widget->visualOffset) || |
275 | isBeingVisuallyOffsetByReference_Widget(widget)) { | ||
277 | continue; | 276 | continue; |
278 | } | 277 | } |
279 | if (widget->flags & keepOnTop_WidgetFlag) { | 278 | if (widget->flags & keepOnTop_WidgetFlag) { |
@@ -282,7 +281,7 @@ void destroyPending_Root(iRoot *d) { | |||
282 | if (widget->parent) { | 281 | if (widget->parent) { |
283 | removeChild_Widget(widget->parent, widget); | 282 | removeChild_Widget(widget->parent, widget); |
284 | } | 283 | } |
285 | iAssert(widget->parent == NULL); | 284 | iAssert(widget->parent == NULL); |
286 | iRelease(widget); | 285 | iRelease(widget); |
287 | remove_PtrSetIterator(&i); | 286 | remove_PtrSetIterator(&i); |
288 | } | 287 | } |
@@ -433,9 +432,9 @@ static void updateNavBarIdentity_(iWidget *navBar) { | |||
433 | const iGmIdentity *ident = | 432 | const iGmIdentity *ident = |
434 | identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App())); | 433 | identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App())); |
435 | iWidget *button = findChild_Widget(navBar, "navbar.ident"); | 434 | iWidget *button = findChild_Widget(navBar, "navbar.ident"); |
436 | iWidget *tool = findWidget_App("toolbar.ident"); | 435 | iLabelWidget *toolButton = findWidget_App("toolbar.ident"); |
437 | setFlags_Widget(button, selected_WidgetFlag, ident != NULL); | 436 | setFlags_Widget(button, selected_WidgetFlag, ident != NULL); |
438 | setFlags_Widget(tool, selected_WidgetFlag, ident != NULL); | 437 | setOutline_LabelWidget(toolButton, ident == NULL); |
439 | /* Update menu. */ | 438 | /* Update menu. */ |
440 | iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); | 439 | iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); |
441 | const iString *subjectName = ident ? name_GmIdentity(ident) : NULL; | 440 | const iString *subjectName = ident ? name_GmIdentity(ident) : NULL; |
@@ -444,6 +443,12 @@ static void updateNavBarIdentity_(iWidget *navBar) { | |||
444 | subjectName ? format_CStr(uiTextAction_ColorEscape "%s", cstr_String(subjectName)) | 443 | subjectName ? format_CStr(uiTextAction_ColorEscape "%s", cstr_String(subjectName)) |
445 | : "${menu.identity.notactive}"); | 444 | : "${menu.identity.notactive}"); |
446 | setFlags_Widget(as_Widget(idItem), disabled_WidgetFlag, !ident); | 445 | setFlags_Widget(as_Widget(idItem), disabled_WidgetFlag, !ident); |
446 | iLabelWidget *toolName = findWidget_App("toolbar.name"); | ||
447 | if (toolName) { | ||
448 | updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); | ||
449 | setFont_LabelWidget(toolButton, subjectName ? defaultMedium_FontId : uiLabelLarge_FontId); | ||
450 | arrange_Widget(parent_Widget(toolButton)); | ||
451 | } | ||
447 | } | 452 | } |
448 | 453 | ||
449 | static void updateNavDirButtons_(iWidget *navBar) { | 454 | static void updateNavDirButtons_(iWidget *navBar) { |
@@ -515,6 +520,26 @@ void updatePadding_Root(iRoot *d) { | |||
515 | #endif | 520 | #endif |
516 | } | 521 | } |
517 | 522 | ||
523 | void updateToolbarColors_Root(iRoot *d) { | ||
524 | #if defined (iPlatformMobile) | ||
525 | iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); | ||
526 | if (toolBar) { | ||
527 | const iBool isSidebarVisible = isVisible_Widget(findChild_Widget(d->widget, "sidebar")); | ||
528 | const int bg = isSidebarVisible ? uiBackgroundSidebar_ColorId : | ||
529 | tmBannerBackground_ColorId; | ||
530 | setBackgroundColor_Widget(toolBar, bg); | ||
531 | iForEach(ObjectList, i, children_Widget(toolBar)) { | ||
532 | iLabelWidget *btn = i.object; | ||
533 | setTextColor_LabelWidget(i.object, isSidebarVisible ? uiTextDim_ColorId : | ||
534 | tmBannerIcon_ColorId); | ||
535 | setBackgroundColor_Widget(i.object, bg); /* using noBackground, but ident has outline */ | ||
536 | } | ||
537 | } | ||
538 | #else | ||
539 | iUnused(d); | ||
540 | #endif | ||
541 | } | ||
542 | |||
518 | void dismissPortraitPhoneSidebars_Root(iRoot *d) { | 543 | void dismissPortraitPhoneSidebars_Root(iRoot *d) { |
519 | if (deviceType_App() == phone_AppDeviceType && isPortrait_App()) { | 544 | if (deviceType_App() == phone_AppDeviceType && isPortrait_App()) { |
520 | iWidget *sidebar = findChild_Widget(d->widget, "sidebar"); | 545 | iWidget *sidebar = findChild_Widget(d->widget, "sidebar"); |
@@ -551,9 +576,16 @@ static void updateUrlInputContentPadding_(iWidget *navBar) { | |||
551 | } | 576 | } |
552 | 577 | ||
553 | static void showSearchQueryIndicator_(iBool show) { | 578 | static void showSearchQueryIndicator_(iBool show) { |
579 | iWidget *navBar = findWidget_Root("navbar"); | ||
554 | iWidget *indicator = findWidget_App("input.indicator.search"); | 580 | iWidget *indicator = findWidget_App("input.indicator.search"); |
581 | updateTextCStr_LabelWidget((iLabelWidget *) indicator, | ||
582 | (deviceType_App() == phone_AppDeviceType || | ||
583 | flags_Widget(navBar) & tight_WidgetFlag) | ||
584 | ? "${status.query.tight} " return_Icon | ||
585 | : "${status.query} " return_Icon); | ||
586 | indicator->rect.size.x = defaultSize_LabelWidget((iLabelWidget *) indicator).x; /* don't touch height */ | ||
555 | showCollapsed_Widget(indicator, show); | 587 | showCollapsed_Widget(indicator, show); |
556 | updateUrlInputContentPadding_(findWidget_Root("navbar")); | 588 | updateUrlInputContentPadding_(navBar); |
557 | } | 589 | } |
558 | 590 | ||
559 | static int navBarAvailableSpace_(iWidget *navBar) { | 591 | static int navBarAvailableSpace_(iWidget *navBar) { |
@@ -692,6 +724,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
692 | iInputWidget *url = findWidget_Root("url"); | 724 | iInputWidget *url = findWidget_Root("url"); |
693 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); | 725 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); |
694 | trimCache_App(); | 726 | trimCache_App(); |
727 | trimMemory_App(); | ||
695 | visitUrl_Visited(visited_App(), withSpacesEncoded_String(urlStr), 0); /* TODO: internal URI normalization */ | 728 | visitUrl_Visited(visited_App(), withSpacesEncoded_String(urlStr), 0); /* TODO: internal URI normalization */ |
696 | postCommand_App("visited.changed"); /* sidebar will update */ | 729 | postCommand_App("visited.changed"); /* sidebar will update */ |
697 | setText_InputWidget(url, urlStr); | 730 | setText_InputWidget(url, urlStr); |
@@ -827,20 +860,20 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) { | |||
827 | const int viewHeight = size_Root(get_Root()).y; | 860 | const int viewHeight = size_Root(get_Root()).y; |
828 | if (arg_Command(cmd) >= 0) { | 861 | if (arg_Command(cmd) >= 0) { |
829 | postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd)); | 862 | postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd)); |
830 | if (!isVisible) { | 863 | // if (!isVisible) { |
831 | setVisualOffset_Widget(sidebar, viewHeight, 0, 0); | 864 | // setVisualOffset_Widget(sidebar, viewHeight, 0, 0); |
832 | setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); | 865 | // setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); |
833 | } | 866 | // } |
834 | } | 867 | } |
835 | else { | 868 | else { |
836 | postCommandf_App("sidebar.toggle"); | 869 | postCommandf_App("sidebar.toggle"); |
837 | if (isVisible) { | 870 | // if (isVisible) { |
838 | setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag); | 871 | // setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag); |
839 | } | 872 | // } |
840 | else { | 873 | // else { |
841 | setVisualOffset_Widget(sidebar, viewHeight, 0, 0); | 874 | // setVisualOffset_Widget(sidebar, viewHeight, 0, 0); |
842 | setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); | 875 | // setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); |
843 | } | 876 | // } |
844 | } | 877 | } |
845 | return iTrue; | 878 | return iTrue; |
846 | } | 879 | } |
@@ -848,7 +881,10 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) { | |||
848 | /* TODO: Clean this up. */ | 881 | /* TODO: Clean this up. */ |
849 | iWidget *sidebar = findWidget_App("sidebar"); | 882 | iWidget *sidebar = findWidget_App("sidebar"); |
850 | iWidget *sidebar2 = findWidget_App("sidebar2"); | 883 | iWidget *sidebar2 = findWidget_App("sidebar2"); |
851 | dismissSidebar_(sidebar, "toolbar.view"); | 884 | //dismissSidebar_(sidebar, "toolbar.view"); |
885 | if (isVisible_Widget(sidebar)) { | ||
886 | postCommandf_App("sidebar.toggle"); | ||
887 | } | ||
852 | const iBool isVisible = isVisible_Widget(sidebar2); | 888 | const iBool isVisible = isVisible_Widget(sidebar2); |
853 | // setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag, | 889 | // setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag, |
854 | // isVisible); | 890 | // isVisible); |
@@ -921,6 +957,23 @@ void updateMetrics_Root(iRoot *d) { | |||
921 | updatePadding_Root(d); | 957 | updatePadding_Root(d); |
922 | arrange_Widget(d->widget); | 958 | arrange_Widget(d->widget); |
923 | updateUrlInputContentPadding_(navBar); | 959 | updateUrlInputContentPadding_(navBar); |
960 | /* Position the toolbar identity name label manually. */ { | ||
961 | iLabelWidget *idName = findChild_Widget(d->widget, "toolbar.name"); | ||
962 | if (idName) { | ||
963 | const iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); | ||
964 | const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view"); | ||
965 | const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident"); | ||
966 | const int font = defaultSmall_FontId; | ||
967 | setFont_LabelWidget(idName, font); | ||
968 | setPos_Widget(as_Widget(idName), | ||
969 | windowToLocal_Widget(as_Widget(idName), | ||
970 | init_I2(left_Rect(bounds_Widget(idButton)), | ||
971 | bottom_Rect(bounds_Widget(viewButton)) - | ||
972 | lineHeight_Text(font) - gap_UI / 2))); | ||
973 | setFixedSize_Widget(as_Widget(idName), init_I2(width_Widget(idButton), | ||
974 | lineHeight_Text(font))); | ||
975 | } | ||
976 | } | ||
924 | postRefresh_App(); | 977 | postRefresh_App(); |
925 | } | 978 | } |
926 | 979 | ||
@@ -1048,9 +1101,9 @@ void createUserInterface_Root(iRoot *d) { | |||
1048 | resizeHeightOfChildren_WidgetFlag | | 1101 | resizeHeightOfChildren_WidgetFlag | |
1049 | moveToParentRightEdge_WidgetFlag); | 1102 | moveToParentRightEdge_WidgetFlag); |
1050 | /* Feeds refresh indicator is inside the input field. */ { | 1103 | /* Feeds refresh indicator is inside the input field. */ { |
1051 | iLabelWidget *queryInd = | 1104 | iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL); |
1052 | new_LabelWidget(uiTextAction_ColorEscape "${status.query} " return_Icon, NULL); | ||
1053 | setId_Widget(as_Widget(queryInd), "input.indicator.search"); | 1105 | setId_Widget(as_Widget(queryInd), "input.indicator.search"); |
1106 | setTextColor_LabelWidget(queryInd, uiTextAction_ColorId); | ||
1054 | setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); | 1107 | setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); |
1055 | setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); | 1108 | setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); |
1056 | setAlignVisually_LabelWidget(queryInd, iTrue); | 1109 | setAlignVisually_LabelWidget(queryInd, iTrue); |
@@ -1060,9 +1113,9 @@ void createUserInterface_Root(iRoot *d) { | |||
1060 | collapse_WidgetFlag | hidden_WidgetFlag); | 1113 | collapse_WidgetFlag | hidden_WidgetFlag); |
1061 | } | 1114 | } |
1062 | /* Feeds refresh indicator is inside the input field. */ { | 1115 | /* Feeds refresh indicator is inside the input field. */ { |
1063 | iLabelWidget *fprog = new_LabelWidget(uiTextCaution_ColorEscape | 1116 | iLabelWidget *fprog = new_LabelWidget("", NULL); |
1064 | "\u2605 ${status.feeds}", NULL); | ||
1065 | setId_Widget(as_Widget(fprog), "feeds.progress"); | 1117 | setId_Widget(as_Widget(fprog), "feeds.progress"); |
1118 | setTextColor_LabelWidget(fprog, uiTextCaution_ColorId); | ||
1066 | setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); | 1119 | setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); |
1067 | setAlignVisually_LabelWidget(fprog, iTrue); | 1120 | setAlignVisually_LabelWidget(fprog, iTrue); |
1068 | setNoAutoMinHeight_LabelWidget(fprog, iTrue); | 1121 | setNoAutoMinHeight_LabelWidget(fprog, iTrue); |
@@ -1116,7 +1169,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1116 | setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); | 1169 | setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); |
1117 | setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); | 1170 | setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); |
1118 | setAlignVisually_LabelWidget(pageMenuButton, iTrue); | 1171 | setAlignVisually_LabelWidget(pageMenuButton, iTrue); |
1119 | addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags); | 1172 | addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag); |
1120 | updateSize_LabelWidget(pageMenuButton); | 1173 | updateSize_LabelWidget(pageMenuButton); |
1121 | } | 1174 | } |
1122 | /* Reload button. */ { | 1175 | /* Reload button. */ { |
@@ -1146,11 +1199,11 @@ void createUserInterface_Root(iRoot *d) { | |||
1146 | #if !defined (iHaveNativeMenus) | 1199 | #if !defined (iHaveNativeMenus) |
1147 | # if defined (iPlatformAppleMobile) | 1200 | # if defined (iPlatformAppleMobile) |
1148 | iLabelWidget *navMenu = | 1201 | iLabelWidget *navMenu = |
1149 | makeMenuButton_LabelWidget("\U0001d362", isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_, | 1202 | makeMenuButton_LabelWidget(menu_Icon, isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_, |
1150 | isPhone ? iElemCount(phoneNavMenuItems_) : iElemCount(tabletNavMenuItems_)); | 1203 | isPhone ? iElemCount(phoneNavMenuItems_) : iElemCount(tabletNavMenuItems_)); |
1151 | # else | 1204 | # else |
1152 | iLabelWidget *navMenu = | 1205 | iLabelWidget *navMenu = |
1153 | makeMenuButton_LabelWidget("\U0001d362", navMenuItems_, iElemCount(navMenuItems_)); | 1206 | makeMenuButton_LabelWidget(menu_Icon, navMenuItems_, iElemCount(navMenuItems_)); |
1154 | # endif | 1207 | # endif |
1155 | setAlignVisually_LabelWidget(navMenu, iTrue); | 1208 | setAlignVisually_LabelWidget(navMenu, iTrue); |
1156 | setId_Widget(addChildFlags_Widget(navBar, iClob(navMenu), collapse_WidgetFlag), "navbar.menu"); | 1209 | setId_Widget(addChildFlags_Widget(navBar, iClob(navMenu), collapse_WidgetFlag), "navbar.menu"); |
@@ -1161,11 +1214,11 @@ void createUserInterface_Root(iRoot *d) { | |||
1161 | setId_Widget(mainStack, "stack"); | 1214 | setId_Widget(mainStack, "stack"); |
1162 | addChildFlags_Widget(div, iClob(mainStack), resizeChildren_WidgetFlag | expand_WidgetFlag | | 1215 | addChildFlags_Widget(div, iClob(mainStack), resizeChildren_WidgetFlag | expand_WidgetFlag | |
1163 | unhittable_WidgetFlag); | 1216 | unhittable_WidgetFlag); |
1164 | iWidget *tabBar = makeTabs_Widget(mainStack); | 1217 | iWidget *docTabs = makeTabs_Widget(mainStack); |
1165 | setId_Widget(tabBar, "doctabs"); | 1218 | setId_Widget(docTabs, "doctabs"); |
1166 | setBackgroundColor_Widget(tabBar, uiBackground_ColorId); | 1219 | setBackgroundColor_Widget(docTabs, uiBackground_ColorId); |
1167 | appendTabPage_Widget(tabBar, iClob(new_DocumentWidget()), "Document", 0, 0); | 1220 | appendTabPage_Widget(docTabs, iClob(new_DocumentWidget()), "Document", 0, 0); |
1168 | iWidget *buttons = findChild_Widget(tabBar, "tabs.buttons"); | 1221 | iWidget *buttons = findChild_Widget(docTabs, "tabs.buttons"); |
1169 | setFlags_Widget(buttons, collapse_WidgetFlag | hidden_WidgetFlag | | 1222 | setFlags_Widget(buttons, collapse_WidgetFlag | hidden_WidgetFlag | |
1170 | drawBackgroundToHorizontalSafeArea_WidgetFlag, iTrue); | 1223 | drawBackgroundToHorizontalSafeArea_WidgetFlag, iTrue); |
1171 | if (deviceType_App() == phone_AppDeviceType) { | 1224 | if (deviceType_App() == phone_AppDeviceType) { |
@@ -1177,9 +1230,9 @@ void createUserInterface_Root(iRoot *d) { | |||
1177 | } | 1230 | } |
1178 | /* Sidebars. */ { | 1231 | /* Sidebars. */ { |
1179 | iWidget *content = findChild_Widget(root, "tabs.content"); | 1232 | iWidget *content = findChild_Widget(root, "tabs.content"); |
1180 | iSidebarWidget *sidebar1 = new_SidebarWidget(left_SideBarSide); | 1233 | iSidebarWidget *sidebar1 = new_SidebarWidget(left_SidebarSide); |
1181 | addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos); | 1234 | addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos); |
1182 | iSidebarWidget *sidebar2 = new_SidebarWidget(right_SideBarSide); | 1235 | iSidebarWidget *sidebar2 = new_SidebarWidget(right_SidebarSide); |
1183 | if (deviceType_App() != phone_AppDeviceType) { | 1236 | if (deviceType_App() != phone_AppDeviceType) { |
1184 | addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos); | 1237 | addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos); |
1185 | } | 1238 | } |
@@ -1218,6 +1271,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1218 | setHint_InputWidget(input, "${hint.findtext}"); | 1271 | setHint_InputWidget(input, "${hint.findtext}"); |
1219 | setSelectAllOnFocus_InputWidget(input, iTrue); | 1272 | setSelectAllOnFocus_InputWidget(input, iTrue); |
1220 | setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ | 1273 | setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ |
1274 | setEnterInsertsLF_InputWidget(input, iFalse); | ||
1221 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), | 1275 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), |
1222 | "find.input"); | 1276 | "find.input"); |
1223 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); | 1277 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); |
@@ -1237,7 +1291,6 @@ void createUserInterface_Root(iRoot *d) { | |||
1237 | arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag | | 1291 | arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag | |
1238 | commandOnClick_WidgetFlag | | 1292 | commandOnClick_WidgetFlag | |
1239 | drawBackgroundToBottom_WidgetFlag, iTrue); | 1293 | drawBackgroundToBottom_WidgetFlag, iTrue); |
1240 | setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); | ||
1241 | setId_Widget(addChildFlags_Widget(toolBar, | 1294 | setId_Widget(addChildFlags_Widget(toolBar, |
1242 | iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), | 1295 | iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), |
1243 | frameless_WidgetFlag), | 1296 | frameless_WidgetFlag), |
@@ -1254,7 +1307,16 @@ void createUserInterface_Root(iRoot *d) { | |||
1254 | iClob(newLargeIcon_LabelWidget(book_Icon, "toolbar.showview arg:-1")), | 1307 | iClob(newLargeIcon_LabelWidget(book_Icon, "toolbar.showview arg:-1")), |
1255 | frameless_WidgetFlag | commandOnClick_WidgetFlag), | 1308 | frameless_WidgetFlag | commandOnClick_WidgetFlag), |
1256 | "toolbar.view"); | 1309 | "toolbar.view"); |
1257 | iLabelWidget *menuButton = makeMenuButton_LabelWidget("\U0001d362", phoneNavMenuItems_, | 1310 | setId_Widget(addChildFlags_Widget(toolBar, |
1311 | iClob(new_LabelWidget("", "toolbar.showident")), | ||
1312 | frameless_WidgetFlag | | ||
1313 | noBackground_WidgetFlag | | ||
1314 | fixedPosition_WidgetFlag | | ||
1315 | fixedSize_WidgetFlag | | ||
1316 | ignoreForParentWidth_WidgetFlag | | ||
1317 | ignoreForParentHeight_WidgetFlag), | ||
1318 | "toolbar.name"); | ||
1319 | iLabelWidget *menuButton = makeMenuButton_LabelWidget(menu_Icon, phoneNavMenuItems_, | ||
1258 | iElemCount(phoneNavMenuItems_)); | 1320 | iElemCount(phoneNavMenuItems_)); |
1259 | setFont_LabelWidget(menuButton, uiLabelLarge_FontId); | 1321 | setFont_LabelWidget(menuButton, uiLabelLarge_FontId); |
1260 | setId_Widget(as_Widget(menuButton), "toolbar.navmenu"); | 1322 | setId_Widget(as_Widget(menuButton), "toolbar.navmenu"); |
@@ -1262,9 +1324,8 @@ void createUserInterface_Root(iRoot *d) { | |||
1262 | iForEach(ObjectList, i, children_Widget(toolBar)) { | 1324 | iForEach(ObjectList, i, children_Widget(toolBar)) { |
1263 | iLabelWidget *btn = i.object; | 1325 | iLabelWidget *btn = i.object; |
1264 | setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue); | 1326 | setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue); |
1265 | setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId); | ||
1266 | // setBackgroundColor_Widget(i.object, tmBannerSideTitle_ColorId); | ||
1267 | } | 1327 | } |
1328 | updateToolbarColors_Root(d); | ||
1268 | const iMenuItem items[] = { | 1329 | const iMenuItem items[] = { |
1269 | { book_Icon " ${sidebar.bookmarks}", 0, 0, "toolbar.showview arg:0" }, | 1330 | { book_Icon " ${sidebar.bookmarks}", 0, 0, "toolbar.showview arg:0" }, |
1270 | { star_Icon " ${sidebar.feeds}", 0, 0, "toolbar.showview arg:1" }, | 1331 | { star_Icon " ${sidebar.feeds}", 0, 0, "toolbar.showview arg:1" }, |
@@ -1346,7 +1407,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1346 | } | 1407 | } |
1347 | } | 1408 | } |
1348 | 1409 | ||
1349 | void showToolbars_Root(iRoot *d, iBool show) { | 1410 | void showToolbar_Root(iRoot *d, iBool show) { |
1350 | /* The toolbar is only used on phone portrait layout. */ | 1411 | /* The toolbar is only used on phone portrait layout. */ |
1351 | if (isLandscape_App()) return; | 1412 | if (isLandscape_App()) return; |
1352 | iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); | 1413 | iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); |
diff --git a/src/ui/root.h b/src/ui/root.h index 96864a15..740e97c9 100644 --- a/src/ui/root.h +++ b/src/ui/root.h | |||
@@ -34,7 +34,8 @@ void postArrange_Root (iRoot *); | |||
34 | void updateMetrics_Root (iRoot *); | 34 | void updateMetrics_Root (iRoot *); |
35 | void updatePadding_Root (iRoot *); /* TODO: is part of metrics? */ | 35 | void updatePadding_Root (iRoot *); /* TODO: is part of metrics? */ |
36 | void dismissPortraitPhoneSidebars_Root (iRoot *); | 36 | void dismissPortraitPhoneSidebars_Root (iRoot *); |
37 | void showToolbars_Root (iRoot *, iBool show); | 37 | void showToolbar_Root (iRoot *, iBool show); |
38 | void updateToolbarColors_Root (iRoot *); | ||
38 | 39 | ||
39 | iInt2 size_Root (const iRoot *); | 40 | iInt2 size_Root (const iRoot *); |
40 | iRect rect_Root (const iRoot *); | 41 | iRect rect_Root (const iRoot *); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 27646b22..c0a22e99 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -523,15 +523,17 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
523 | addChild_Widget(div, iClob(makePadding_Widget(gap_UI))); | 523 | addChild_Widget(div, iClob(makePadding_Widget(gap_UI))); |
524 | addChild_Widget(div, iClob(new_LabelWidget("${menu.identity.import}", "ident.import"))); | 524 | addChild_Widget(div, iClob(new_LabelWidget("${menu.identity.import}", "ident.import"))); |
525 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ | 525 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ |
526 | iLabelWidget *linkLabel; | ||
526 | setBackgroundColor_Widget( | 527 | setBackgroundColor_Widget( |
527 | addChildFlags_Widget( | 528 | addChildFlags_Widget( |
528 | div, | 529 | div, |
529 | iClob(new_LabelWidget(format_CStr(cstr_Lang("ident.gotohelp"), | 530 | iClob(linkLabel = new_LabelWidget(format_CStr(cstr_Lang("ident.gotohelp"), |
530 | uiTextStrong_ColorEscape, | 531 | uiTextStrong_ColorEscape, |
531 | restore_ColorEscape), | 532 | restore_ColorEscape), |
532 | "!open newtab:1 gotoheading:1.6 url:about:help")), | 533 | "!open newtab:1 gotoheading:1.6 url:about:help")), |
533 | frameless_WidgetFlag | fixedHeight_WidgetFlag | wrapText_WidgetFlag), | 534 | frameless_WidgetFlag | fixedHeight_WidgetFlag), |
534 | uiBackgroundSidebar_ColorId); | 535 | uiBackgroundSidebar_ColorId); |
536 | setWrap_LabelWidget(linkLabel, iTrue); | ||
535 | addChild_Widget(d->blank, iClob(div)); | 537 | addChild_Widget(d->blank, iClob(div)); |
536 | } | 538 | } |
537 | // arrange_Widget(d->blank); | 539 | // arrange_Widget(d->blank); |
@@ -625,13 +627,14 @@ static void updateMetrics_SidebarWidget_(iSidebarWidget *d) { | |||
625 | void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | 627 | void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { |
626 | iWidget *w = as_Widget(d); | 628 | iWidget *w = as_Widget(d); |
627 | init_Widget(w); | 629 | init_Widget(w); |
628 | setId_Widget(w, side == left_SideBarSide ? "sidebar" : "sidebar2"); | 630 | setId_Widget(w, side == left_SidebarSide ? "sidebar" : "sidebar2"); |
629 | initCopy_String(&d->cmdPrefix, id_Widget(w)); | 631 | initCopy_String(&d->cmdPrefix, id_Widget(w)); |
630 | appendChar_String(&d->cmdPrefix, '.'); | 632 | appendChar_String(&d->cmdPrefix, '.'); |
631 | setBackgroundColor_Widget(w, none_ColorId); | 633 | setBackgroundColor_Widget(w, none_ColorId); |
632 | setFlags_Widget(w, | 634 | setFlags_Widget(w, |
633 | collapse_WidgetFlag | hidden_WidgetFlag | arrangeHorizontal_WidgetFlag | | 635 | collapse_WidgetFlag | hidden_WidgetFlag | arrangeHorizontal_WidgetFlag | |
634 | resizeWidthOfChildren_WidgetFlag, | 636 | resizeWidthOfChildren_WidgetFlag | noFadeBackground_WidgetFlag | |
637 | noShadowBorder_WidgetFlag, | ||
635 | iTrue); | 638 | iTrue); |
636 | iZap(d->modeScroll); | 639 | iZap(d->modeScroll); |
637 | d->side = side; | 640 | d->side = side; |
@@ -659,7 +662,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
659 | d->actions = NULL; | 662 | d->actions = NULL; |
660 | /* On a phone, the right sidebar is used exclusively for Identities. */ | 663 | /* On a phone, the right sidebar is used exclusively for Identities. */ |
661 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; | 664 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; |
662 | if (!isPhone || d->side == left_SideBarSide) { | 665 | if (!isPhone || d->side == left_SidebarSide) { |
663 | iWidget *buttons = new_Widget(); | 666 | iWidget *buttons = new_Widget(); |
664 | setId_Widget(buttons, "buttons"); | 667 | setId_Widget(buttons, "buttons"); |
665 | for (int i = 0; i < max_SidebarMode; i++) { | 668 | for (int i = 0; i < max_SidebarMode; i++) { |
@@ -678,8 +681,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
678 | iClob(buttons), | 681 | iClob(buttons), |
679 | arrangeHorizontal_WidgetFlag | | 682 | arrangeHorizontal_WidgetFlag | |
680 | resizeWidthOfChildren_WidgetFlag | | 683 | resizeWidthOfChildren_WidgetFlag | |
681 | arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag | | 684 | arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag); // | |
682 | drawBackgroundToHorizontalSafeArea_WidgetFlag); | 685 | // drawBackgroundToHorizontalSafeArea_WidgetFlag); |
683 | setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); | 686 | setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); |
684 | } | 687 | } |
685 | else { | 688 | else { |
@@ -700,13 +703,13 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
700 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); | 703 | setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); |
701 | addChildFlags_Widget(listAndActions, | 704 | addChildFlags_Widget(listAndActions, |
702 | iClob(d->list), | 705 | iClob(d->list), |
703 | expand_WidgetFlag | drawBackgroundToHorizontalSafeArea_WidgetFlag); | 706 | expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); |
704 | setId_Widget(addChildPosFlags_Widget(listAndActions, | 707 | setId_Widget(addChildPosFlags_Widget(listAndActions, |
705 | iClob(d->actions = new_Widget()), | 708 | iClob(d->actions = new_Widget()), |
706 | isPhone ? front_WidgetAddPos : back_WidgetAddPos, | 709 | isPhone ? front_WidgetAddPos : back_WidgetAddPos, |
707 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | | 710 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | |
708 | resizeWidthOfChildren_WidgetFlag | | 711 | resizeWidthOfChildren_WidgetFlag), // | |
709 | drawBackgroundToHorizontalSafeArea_WidgetFlag), | 712 | // drawBackgroundToHorizontalSafeArea_WidgetFlag), |
710 | "actions"); | 713 | "actions"); |
711 | setBackgroundColor_Widget(d->actions, uiBackgroundSidebar_ColorId); | 714 | setBackgroundColor_Widget(d->actions, uiBackgroundSidebar_ColorId); |
712 | d->contextItem = NULL; | 715 | d->contextItem = NULL; |
@@ -715,24 +718,24 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
715 | addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); | 718 | addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); |
716 | addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); | 719 | addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); |
717 | setMode_SidebarWidget(d, | 720 | setMode_SidebarWidget(d, |
718 | deviceType_App() == phone_AppDeviceType && d->side == right_SideBarSide ? | 721 | deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide ? |
719 | identities_SidebarMode : bookmarks_SidebarMode); | 722 | identities_SidebarMode : bookmarks_SidebarMode); |
720 | d->resizer = | 723 | d->resizer = |
721 | addChildFlags_Widget(w, | 724 | addChildFlags_Widget(w, |
722 | iClob(new_Widget()), | 725 | iClob(new_Widget()), |
723 | hover_WidgetFlag | commandOnClick_WidgetFlag | fixedWidth_WidgetFlag | | 726 | hover_WidgetFlag | commandOnClick_WidgetFlag | fixedWidth_WidgetFlag | |
724 | resizeToParentHeight_WidgetFlag | | 727 | resizeToParentHeight_WidgetFlag | |
725 | (side == left_SideBarSide ? moveToParentRightEdge_WidgetFlag | 728 | (side == left_SidebarSide ? moveToParentRightEdge_WidgetFlag |
726 | : moveToParentLeftEdge_WidgetFlag)); | 729 | : moveToParentLeftEdge_WidgetFlag)); |
727 | if (deviceType_App() == phone_AppDeviceType) { | 730 | if (deviceType_App() == phone_AppDeviceType) { |
728 | setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); | 731 | setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); |
729 | } | 732 | } |
730 | setId_Widget(d->resizer, side == left_SideBarSide ? "sidebar.grab" : "sidebar2.grab"); | 733 | setId_Widget(d->resizer, side == left_SidebarSide ? "sidebar.grab" : "sidebar2.grab"); |
731 | setBackgroundColor_Widget(d->resizer, none_ColorId); | 734 | setBackgroundColor_Widget(d->resizer, none_ColorId); |
732 | d->menu = NULL; | 735 | d->menu = NULL; |
733 | addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh"); | 736 | addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh"); |
734 | updateMetrics_SidebarWidget_(d); | 737 | updateMetrics_SidebarWidget_(d); |
735 | if (side == left_SideBarSide) { | 738 | if (side == left_SidebarSide) { |
736 | postCommand_App("~sidebar.update"); /* unread count */ | 739 | postCommand_App("~sidebar.update"); /* unread count */ |
737 | } | 740 | } |
738 | } | 741 | } |
@@ -773,6 +776,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
773 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); | 776 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); |
774 | postCommandf_App("document.goto loc:%p", head->text.start); | 777 | postCommandf_App("document.goto loc:%p", head->text.start); |
775 | dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); | 778 | dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); |
779 | setOpenedFromSidebar_DocumentWidget(document_App(), iTrue); | ||
776 | break; | 780 | break; |
777 | } | 781 | } |
778 | case feeds_SidebarMode: { | 782 | case feeds_SidebarMode: { |
@@ -783,7 +787,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
783 | case bookmarks_SidebarMode: | 787 | case bookmarks_SidebarMode: |
784 | case history_SidebarMode: { | 788 | case history_SidebarMode: { |
785 | if (!isEmpty_String(&item->url)) { | 789 | if (!isEmpty_String(&item->url)) { |
786 | postCommandf_Root(get_Root(), "open newtab:%d url:%s", | 790 | postCommandf_Root(get_Root(), "open fromsidebar:1 newtab:%d url:%s", |
787 | openTabMode_Sym(modState_Keys()), | 791 | openTabMode_Sym(modState_Keys()), |
788 | cstr_String(&item->url)); | 792 | cstr_String(&item->url)); |
789 | } | 793 | } |
@@ -799,7 +803,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
799 | updateContextMenu_SidebarWidget_(d); | 803 | updateContextMenu_SidebarWidget_(d); |
800 | arrange_Widget(d->menu); | 804 | arrange_Widget(d->menu); |
801 | openMenu_Widget(d->menu, | 805 | openMenu_Widget(d->menu, |
802 | d->side == left_SideBarSide | 806 | d->side == left_SidebarSide |
803 | ? topRight_Rect(itemRect_ListWidget(d->list, itemIndex)) | 807 | ? topRight_Rect(itemRect_ListWidget(d->list, itemIndex)) |
804 | : addX_I2(topLeft_Rect(itemRect_ListWidget(d->list, itemIndex)), | 808 | : addX_I2(topLeft_Rect(itemRect_ListWidget(d->list, itemIndex)), |
805 | -width_Widget(d->menu))); | 809 | -width_Widget(d->menu))); |
@@ -857,7 +861,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { | |||
857 | if (!isFixedWidth) { | 861 | if (!isFixedWidth) { |
858 | /* Even less space if the other sidebar is visible, too. */ | 862 | /* Even less space if the other sidebar is visible, too. */ |
859 | const int otherWidth = | 863 | const int otherWidth = |
860 | width_Widget(findWidget_App(d->side == left_SideBarSide ? "sidebar2" : "sidebar")); | 864 | width_Widget(findWidget_App(d->side == left_SidebarSide ? "sidebar2" : "sidebar")); |
861 | width = iClamp(width, 30 * gap_UI, size_Root(w->root).x - 50 * gap_UI - otherWidth); | 865 | width = iClamp(width, 30 * gap_UI, size_Root(w->root).x - 50 * gap_UI - otherWidth); |
862 | } | 866 | } |
863 | d->widthAsGaps = (float) width / (float) gap_UI; | 867 | d->widthAsGaps = (float) width / (float) gap_UI; |
@@ -938,33 +942,39 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
938 | } | 942 | } |
939 | const iBool isAnimated = prefs_App()->uiAnimations && | 943 | const iBool isAnimated = prefs_App()->uiAnimations && |
940 | argLabel_Command(cmd, "noanim") == 0 && | 944 | argLabel_Command(cmd, "noanim") == 0 && |
941 | (deviceType_App() != phone_AppDeviceType); | 945 | (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType); |
942 | int visX = 0; | 946 | int visX = 0; |
943 | if (isVisible_Widget(w)) { | 947 | if (isVisible_Widget(w)) { |
944 | visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); | 948 | visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); |
945 | } | 949 | } |
946 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); | 950 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); |
951 | /* Safe area inset for mobile. */ | ||
952 | const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0); | ||
947 | if (isVisible_Widget(w)) { | 953 | if (isVisible_Widget(w)) { |
948 | setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); | 954 | setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); |
949 | w->rect.size.x = d->widthAsGaps * gap_UI; | 955 | w->rect.size.x = d->widthAsGaps * gap_UI; |
950 | invalidate_ListWidget(d->list); | 956 | invalidate_ListWidget(d->list); |
951 | if (isAnimated) { | 957 | if (isAnimated) { |
952 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); | 958 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); |
953 | setVisualOffset_Widget(w, (d->side == left_SideBarSide ? -1 : 1) * w->rect.size.x, 0, 0); | 959 | setVisualOffset_Widget( |
960 | w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0); | ||
954 | setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); | 961 | setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); |
955 | } | 962 | } |
956 | } | 963 | } |
957 | else if (isAnimated) { | 964 | else if (isAnimated) { |
958 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); | 965 | setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); |
959 | if (d->side == right_SideBarSide) { | 966 | if (d->side == right_SidebarSide) { |
960 | setVisualOffset_Widget(w, visX, 0, 0); | 967 | setVisualOffset_Widget(w, visX, 0, 0); |
961 | setVisualOffset_Widget(w, visX + w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); | 968 | setVisualOffset_Widget( |
969 | w, visX + w->rect.size.x + safePad, 300, easeOut_AnimFlag | softer_AnimFlag); | ||
962 | } | 970 | } |
963 | else { | 971 | else { |
964 | setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); | 972 | setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); |
965 | setVisualOffset_Widget(w, -w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); | 973 | setVisualOffset_Widget( |
974 | w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag); | ||
966 | } | 975 | } |
967 | } | 976 | } |
977 | updateToolbarColors_Root(w->root); | ||
968 | arrange_Widget(w->parent); | 978 | arrange_Widget(w->parent); |
969 | /* BUG: Rearranging because the arrange above didn't fully resolve the height. */ | 979 | /* BUG: Rearranging because the arrange above didn't fully resolve the height. */ |
970 | arrange_Widget(w); | 980 | arrange_Widget(w); |
@@ -984,11 +994,21 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
984 | /* Handle commands. */ | 994 | /* Handle commands. */ |
985 | if (isResize_UserEvent(ev)) { | 995 | if (isResize_UserEvent(ev)) { |
986 | checkModeButtonLayout_SidebarWidget_(d); | 996 | checkModeButtonLayout_SidebarWidget_(d); |
987 | if (deviceType_App() == phone_AppDeviceType && d->side == left_SideBarSide) { | 997 | if (deviceType_App() == phone_AppDeviceType && d->side == left_SidebarSide) { |
998 | setFlags_Widget(w, rightEdgeDraggable_WidgetFlag, isPortrait_App()); | ||
988 | /* In landscape, visibility of the toolbar is controlled separately. */ | 999 | /* In landscape, visibility of the toolbar is controlled separately. */ |
989 | if (isVisible_Widget(w)) { | 1000 | if (isVisible_Widget(w)) { |
990 | postCommand_Widget(w, "sidebar.toggle"); | 1001 | postCommand_Widget(w, "sidebar.toggle"); |
991 | } | 1002 | } |
1003 | setFlags_Widget(findChild_Widget(w, "buttons"), | ||
1004 | drawBackgroundToHorizontalSafeArea_WidgetFlag, | ||
1005 | isLandscape_App()); | ||
1006 | setFlags_Widget(findChild_Widget(w, "actions"), | ||
1007 | drawBackgroundToHorizontalSafeArea_WidgetFlag, | ||
1008 | isLandscape_App()); | ||
1009 | setFlags_Widget(as_Widget(d->list), | ||
1010 | drawBackgroundToHorizontalSafeArea_WidgetFlag, | ||
1011 | isLandscape_App()); | ||
992 | return iFalse; | 1012 | return iFalse; |
993 | } | 1013 | } |
994 | } | 1014 | } |
@@ -1029,6 +1049,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1029 | postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); | 1049 | postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); |
1030 | return iTrue; | 1050 | return iTrue; |
1031 | } | 1051 | } |
1052 | else if (isPortraitPhone_App() && isVisible_Widget(w) && d->side == left_SidebarSide && | ||
1053 | equal_Command(cmd, "swipe.forward")) { | ||
1054 | postCommand_App("sidebar.toggle"); | ||
1055 | return iTrue; | ||
1056 | } | ||
1032 | else if (startsWith_CStr(cmd, cstr_String(&d->cmdPrefix))) { | 1057 | else if (startsWith_CStr(cmd, cstr_String(&d->cmdPrefix))) { |
1033 | if (handleSidebarCommand_SidebarWidget_(d, cmd + size_String(&d->cmdPrefix))) { | 1058 | if (handleSidebarCommand_SidebarWidget_(d, cmd + size_String(&d->cmdPrefix))) { |
1034 | return iTrue; | 1059 | return iTrue; |
@@ -1059,7 +1084,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1059 | const int resMid = d->resizer->rect.size.x / 2; | 1084 | const int resMid = d->resizer->rect.size.x / 2; |
1060 | setWidth_SidebarWidget( | 1085 | setWidth_SidebarWidget( |
1061 | d, | 1086 | d, |
1062 | ((d->side == left_SideBarSide | 1087 | ((d->side == left_SidebarSide |
1063 | ? inner.x | 1088 | ? inner.x |
1064 | : (right_Rect(rect_Root(w->root)) - coord_Command(cmd).x)) + | 1089 | : (right_Rect(rect_Root(w->root)) - coord_Command(cmd).x)) + |
1065 | resMid) / (float) gap_UI); | 1090 | resMid) / (float) gap_UI); |
@@ -1528,15 +1553,17 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
1528 | const iRect bounds = bounds_Widget(w); | 1553 | const iRect bounds = bounds_Widget(w); |
1529 | iPaint p; | 1554 | iPaint p; |
1530 | init_Paint(&p); | 1555 | init_Paint(&p); |
1531 | if (flags_Widget(w) & visualOffset_WidgetFlag && | 1556 | if (deviceType_App() != phone_AppDeviceType) { |
1532 | flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { | 1557 | if (flags_Widget(w) & visualOffset_WidgetFlag && |
1533 | fillRect_Paint(&p, boundsWithoutVisualOffset_Widget(w), tmBackground_ColorId); | 1558 | flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { |
1559 | fillRect_Paint(&p, boundsWithoutVisualOffset_Widget(w), tmBackground_ColorId); | ||
1560 | } | ||
1534 | } | 1561 | } |
1535 | draw_Widget(w); | 1562 | draw_Widget(w); |
1536 | if (isVisible_Widget(w)) { | 1563 | if (isVisible_Widget(w)) { |
1537 | drawVLine_Paint( | 1564 | drawVLine_Paint( |
1538 | &p, | 1565 | &p, |
1539 | addX_I2(d->side == left_SideBarSide ? topRight_Rect(bounds) : topLeft_Rect(bounds), -1), | 1566 | addX_I2(d->side == left_SidebarSide ? topRight_Rect(bounds) : topLeft_Rect(bounds), -1), |
1540 | height_Rect(bounds), | 1567 | height_Rect(bounds), |
1541 | uiSeparator_ColorId); | 1568 | uiSeparator_ColorId); |
1542 | } | 1569 | } |
@@ -1782,13 +1809,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1782 | : uiTextFramelessHover_ColorId) | 1809 | : uiTextFramelessHover_ColorId) |
1783 | : uiTextDim_ColorId; | 1810 | : uiTextDim_ColorId; |
1784 | if (!d->listItem.isSelected && !isUsedOnDomain) { | 1811 | if (!d->listItem.isSelected && !isUsedOnDomain) { |
1785 | /* Draw an outline of the icon. */ | 1812 | drawOutline_Text(font, cPos, metaFg, none_ColorId, range_String(&icon)); |
1786 | for (int off = 0; off < 4; ++off) { | ||
1787 | drawRange_Text(font, | ||
1788 | add_I2(cPos, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), | ||
1789 | metaFg, | ||
1790 | range_String(&icon)); | ||
1791 | } | ||
1792 | } | 1813 | } |
1793 | drawRange_Text(font, | 1814 | drawRange_Text(font, |
1794 | cPos, | 1815 | cPos, |
diff --git a/src/ui/sidebarwidget.h b/src/ui/sidebarwidget.h index 2e418aa4..130242ab 100644 --- a/src/ui/sidebarwidget.h +++ b/src/ui/sidebarwidget.h | |||
@@ -36,8 +36,8 @@ enum iSidebarMode { | |||
36 | const char * icon_SidebarMode (enum iSidebarMode mode); | 36 | const char * icon_SidebarMode (enum iSidebarMode mode); |
37 | 37 | ||
38 | enum iSidebarSide { | 38 | enum iSidebarSide { |
39 | left_SideBarSide, | 39 | left_SidebarSide, |
40 | right_SideBarSide, | 40 | right_SidebarSide, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | enum iFeedsMode { | 43 | enum iFeedsMode { |
diff --git a/src/ui/text.c b/src/ui/text.c index 55fd4254..edbc6583 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -116,6 +116,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
116 | 116 | ||
117 | struct Impl_Font { | 117 | struct Impl_Font { |
118 | iBlock * data; | 118 | iBlock * data; |
119 | enum iTextFont family; | ||
119 | stbtt_fontinfo font; | 120 | stbtt_fontinfo font; |
120 | float xScale, yScale; | 121 | float xScale, yScale; |
121 | int vertOffset; /* offset due to scaling */ | 122 | int vertOffset; /* offset due to scaling */ |
@@ -134,6 +135,15 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
134 | enum iFontSize sizeId, iBool isMonospaced) { | 135 | enum iFontSize sizeId, iBool isMonospaced) { |
135 | init_Hash(&d->glyphs); | 136 | init_Hash(&d->glyphs); |
136 | d->data = NULL; | 137 | d->data = NULL; |
138 | d->family = undefined_TextFont; | ||
139 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ | ||
140 | if (data == &fontNunitoRegular_Embedded || | ||
141 | data == &fontNunitoBold_Embedded || | ||
142 | data == &fontNunitoExtraBold_Embedded || | ||
143 | data == &fontNunitoLightItalic_Embedded || | ||
144 | data == &fontNunitoExtraLight_Embedded) { | ||
145 | d->family = nunito_TextFont; | ||
146 | } | ||
137 | d->isMonospaced = isMonospaced; | 147 | d->isMonospaced = isMonospaced; |
138 | d->height = height; | 148 | d->height = height; |
139 | iZap(d->font); | 149 | iZap(d->font); |
@@ -306,6 +316,7 @@ static void initFonts_Text_(iText *d) { | |||
306 | { &fontSourceSans3Regular_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, | 316 | { &fontSourceSans3Regular_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, |
307 | { &fontSourceSans3Regular_Embedded, uiSize * 1.333f, 1.0f, uiBig_FontSize }, | 317 | { &fontSourceSans3Regular_Embedded, uiSize * 1.333f, 1.0f, uiBig_FontSize }, |
308 | { &fontSourceSans3Regular_Embedded, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, | 318 | { &fontSourceSans3Regular_Embedded, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, |
319 | { &fontSourceSans3Semibold_Embedded, uiSize * 0.8f, 1.0f, uiNormal_FontSize }, | ||
309 | /* UI fonts: bold weight */ | 320 | /* UI fonts: bold weight */ |
310 | { &fontSourceSans3Bold_Embedded, uiSize, 1.0f, uiNormal_FontSize }, | 321 | { &fontSourceSans3Bold_Embedded, uiSize, 1.0f, uiNormal_FontSize }, |
311 | { &fontSourceSans3Bold_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, | 322 | { &fontSourceSans3Bold_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, |
@@ -389,7 +400,9 @@ static void initCache_Text_(iText *d) { | |||
389 | d->cacheRowAllocStep = iMax(2, textSize / 6); | 400 | d->cacheRowAllocStep = iMax(2, textSize / 6); |
390 | /* Allocate initial (empty) rows. These will be assigned actual locations in the cache | 401 | /* Allocate initial (empty) rows. These will be assigned actual locations in the cache |
391 | once at least one glyph is stored. */ | 402 | once at least one glyph is stored. */ |
392 | for (int h = d->cacheRowAllocStep; h <= 2 * textSize + d->cacheRowAllocStep; h += d->cacheRowAllocStep) { | 403 | for (int h = d->cacheRowAllocStep; |
404 | h <= 2.5 * textSize + d->cacheRowAllocStep; | ||
405 | h += d->cacheRowAllocStep) { | ||
393 | pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); | 406 | pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); |
394 | } | 407 | } |
395 | d->cacheBottom = 0; | 408 | d->cacheBottom = 0; |
@@ -1010,10 +1023,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1010 | prevCh = 0; | 1023 | prevCh = 0; |
1011 | continue; | 1024 | continue; |
1012 | } | 1025 | } |
1013 | if (ch == '\r') { /* color change */ | 1026 | if (ch == '\v') { /* color change */ |
1014 | iChar esc = nextChar_(&chPos, args->text.end); | 1027 | iChar esc = nextChar_(&chPos, args->text.end); |
1015 | int colorNum = args->color; | 1028 | int colorNum = args->color; |
1016 | if (esc == '\r') { /* Extended range. */ | 1029 | if (esc == '\v') { /* Extended range. */ |
1017 | esc = nextChar_(&chPos, args->text.end) + asciiExtended_ColorEscape; | 1030 | esc = nextChar_(&chPos, args->text.end) + asciiExtended_ColorEscape; |
1018 | colorNum = esc - asciiBase_ColorEscape; | 1031 | colorNum = esc - asciiBase_ColorEscape; |
1019 | } | 1032 | } |
@@ -1128,14 +1141,26 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1128 | const iChar next = nextChar_(&peek, args->text.end); | 1141 | const iChar next = nextChar_(&peek, args->text.end); |
1129 | if (enableKerning_Text && !d->manualKernOnly && next) { | 1142 | if (enableKerning_Text && !d->manualKernOnly && next) { |
1130 | const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); | 1143 | const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); |
1131 | const int kern = stbtt_GetGlyphKernAdvance( | 1144 | int kern = stbtt_GetGlyphKernAdvance( |
1132 | &glyph->font->font, glyph->glyphIndex, nextGlyphIndex); | 1145 | &glyph->font->font, glyph->glyphIndex, nextGlyphIndex); |
1146 | /* Nunito needs some kerning fixes. */ | ||
1147 | if (glyph->font->family == nunito_TextFont) { | ||
1148 | if (ch == 'W' && (next == 'i' || next == 'h')) { | ||
1149 | kern = -30; | ||
1150 | } | ||
1151 | else if (ch == 'T' && next == 'h') { | ||
1152 | kern = -15; | ||
1153 | } | ||
1154 | else if (ch == 'V' && next == 'i') { | ||
1155 | kern = -15; | ||
1156 | } | ||
1157 | } | ||
1133 | if (kern) { | 1158 | if (kern) { |
1134 | // printf("%lc(%u) -> %lc(%u): kern %d (%f)\n", ch, glyph->glyphIndex, next, | 1159 | // printf("%lc(%u) -> %lc(%u): kern %d (%f)\n", ch, glyph->glyphIndex, next, |
1135 | // nextGlyphIndex, | 1160 | // nextGlyphIndex, |
1136 | // kern, d->xScale * kern); | 1161 | // kern, d->xScale * kern); |
1137 | xpos += d->xScale * kern; | 1162 | xpos += glyph->font->xScale * kern; |
1138 | xposExtend += d->xScale * kern; | 1163 | xposExtend += glyph->font->xScale * kern; |
1139 | } | 1164 | } |
1140 | } | 1165 | } |
1141 | } | 1166 | } |
@@ -1313,6 +1338,18 @@ void drawRangeN_Text(int fontId, iInt2 pos, int color, iRangecc text, size_t max | |||
1313 | drawBoundedN_Text_(fontId, pos, 0, color, text, maxChars); | 1338 | drawBoundedN_Text_(fontId, pos, 0, color, text, maxChars); |
1314 | } | 1339 | } |
1315 | 1340 | ||
1341 | void drawOutline_Text(int fontId, iInt2 pos, int outlineColor, int fillColor, iRangecc text) { | ||
1342 | for (int off = 0; off < 4; ++off) { | ||
1343 | drawRange_Text(fontId, | ||
1344 | add_I2(pos, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), | ||
1345 | outlineColor, | ||
1346 | text); | ||
1347 | } | ||
1348 | if (fillColor != none_ColorId) { | ||
1349 | drawRange_Text(fontId, pos, fillColor, text); | ||
1350 | } | ||
1351 | } | ||
1352 | |||
1316 | iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) { | 1353 | iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) { |
1317 | iInt2 size = zero_I2(); | 1354 | iInt2 size = zero_I2(); |
1318 | const char *endp; | 1355 | const char *endp; |
@@ -1354,6 +1391,31 @@ void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, con | |||
1354 | deinit_Block(&chars); | 1391 | deinit_Block(&chars); |
1355 | } | 1392 | } |
1356 | 1393 | ||
1394 | void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, | ||
1395 | int fillColor, const char *format, ...) { | ||
1396 | iBlock chars; | ||
1397 | init_Block(&chars, 0); { | ||
1398 | va_list args; | ||
1399 | va_start(args, format); | ||
1400 | vprintf_Block(&chars, format, args); | ||
1401 | va_end(args); | ||
1402 | } | ||
1403 | if (outlineColor != none_ColorId) { | ||
1404 | for (int off = 0; off < 4; ++off) { | ||
1405 | drawCenteredRange_Text( | ||
1406 | fontId, | ||
1407 | moved_Rect(rect, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), | ||
1408 | alignVisual, | ||
1409 | outlineColor, | ||
1410 | range_Block(&chars)); | ||
1411 | } | ||
1412 | } | ||
1413 | if (fillColor != none_ColorId) { | ||
1414 | drawCenteredRange_Text(fontId, rect, alignVisual, fillColor, range_Block(&chars)); | ||
1415 | } | ||
1416 | deinit_Block(&chars); | ||
1417 | } | ||
1418 | |||
1357 | void drawCenteredRange_Text(int fontId, iRect rect, iBool alignVisual, int color, iRangecc text) { | 1419 | void drawCenteredRange_Text(int fontId, iRect rect, iBool alignVisual, int color, iRangecc text) { |
1358 | iRect textBounds = alignVisual ? visualBounds_Text(fontId, text) | 1420 | iRect textBounds = alignVisual ? visualBounds_Text(fontId, text) |
1359 | : (iRect){ zero_I2(), advanceRange_Text(fontId, text) }; | 1421 | : (iRect){ zero_I2(), advanceRange_Text(fontId, text) }; |
@@ -1504,9 +1566,10 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB | |||
1504 | if (d->texture) { | 1566 | if (d->texture) { |
1505 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); | 1567 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); |
1506 | SDL_SetRenderTarget(render, d->texture); | 1568 | SDL_SetRenderTarget(render, d->texture); |
1569 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | ||
1570 | SDL_SetRenderDrawColor(render, 255, 255, 255, 0); | ||
1571 | SDL_RenderClear(render); | ||
1507 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ | 1572 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ |
1508 | SDL_SetRenderDrawColor(text_.render, 0, 0, 0, 0); | ||
1509 | SDL_RenderClear(text_.render); | ||
1510 | const int fg = color | fillBackground_ColorId; | 1573 | const int fg = color | fillBackground_ColorId; |
1511 | iRangecc range = range_CStr(text); | 1574 | iRangecc range = range_CStr(text); |
1512 | if (maxWidth == 0) { | 1575 | if (maxWidth == 0) { |
diff --git a/src/ui/text.h b/src/ui/text.h index 2f2bcf3a..5a099142 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -50,6 +50,7 @@ enum iFontId { | |||
50 | defaultMedium_FontId, | 50 | defaultMedium_FontId, |
51 | defaultBig_FontId, | 51 | defaultBig_FontId, |
52 | defaultLarge_FontId, | 52 | defaultLarge_FontId, |
53 | defaultSmall_FontId, | ||
53 | /* UI fonts: bold weight */ | 54 | /* UI fonts: bold weight */ |
54 | defaultBold_FontId, | 55 | defaultBold_FontId, |
55 | defaultMediumBold_FontId, | 56 | defaultMediumBold_FontId, |
@@ -116,7 +117,8 @@ iLocalDef iBool isJapanese_FontId(enum iFontId id) { | |||
116 | #define emojiVariationSelector_Char ((iChar) 0xfe0f) | 117 | #define emojiVariationSelector_Char ((iChar) 0xfe0f) |
117 | 118 | ||
118 | enum iTextFont { | 119 | enum iTextFont { |
119 | nunito_TextFont, | 120 | undefined_TextFont = -1, |
121 | nunito_TextFont = 0, | ||
120 | firaSans_TextFont, | 122 | firaSans_TextFont, |
121 | literata_TextFont, | 123 | literata_TextFont, |
122 | tinos_TextFont, | 124 | tinos_TextFont, |
@@ -162,9 +164,11 @@ void draw_Text (int fontId, iInt2 pos, int color, const char *t | |||
162 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); | 164 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); |
163 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 165 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |
164 | void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); | 166 | void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); |
167 | void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, int fillColor, const char *text, ...); | ||
165 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); | 168 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); |
166 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); | 169 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); |
167 | void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); | 170 | void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); |
171 | void drawOutline_Text (int fontId, iInt2 pos, int outlineColor, int fillColor, iRangecc text); | ||
168 | void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ | 172 | void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ |
169 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ | 173 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ |
170 | 174 | ||
diff --git a/src/ui/touch.c b/src/ui/touch.c index 74a22baf..dac1152e 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -40,6 +40,10 @@ iDeclareType(TouchState) | |||
40 | #define numHistory_Touch_ 5 | 40 | #define numHistory_Touch_ 5 |
41 | #define lastIndex_Touch_ (numHistory_Touch_ - 1) | 41 | #define lastIndex_Touch_ (numHistory_Touch_ - 1) |
42 | 42 | ||
43 | static const uint32_t longPressSpanMs_ = 500; | ||
44 | static const uint32_t shortPressSpanMs_ = 250; | ||
45 | static const int tapRadiusPt_ = 10; | ||
46 | |||
43 | enum iTouchEdge { | 47 | enum iTouchEdge { |
44 | none_TouchEdge, | 48 | none_TouchEdge, |
45 | left_TouchEdge, | 49 | left_TouchEdge, |
@@ -55,12 +59,13 @@ enum iTouchAxis { | |||
55 | struct Impl_Touch { | 59 | struct Impl_Touch { |
56 | SDL_FingerID id; | 60 | SDL_FingerID id; |
57 | iWidget *affinity; /* widget on which the touch started */ | 61 | iWidget *affinity; /* widget on which the touch started */ |
58 | iWidget *edgeDragging; | 62 | // iWidget *edgeDragging; |
59 | iBool hasMoved; | 63 | iBool hasMoved; |
60 | iBool isTapBegun; | 64 | iBool isTapBegun; |
61 | iBool isLeftDown; | 65 | iBool isLeftDown; |
62 | iBool isTouchDrag; | 66 | iBool isTouchDrag; |
63 | iBool isTapAndHold; | 67 | iBool isTapAndHold; |
68 | iBool didPostEdgeMove; | ||
64 | iBool didBeginOnTouchDrag; | 69 | iBool didBeginOnTouchDrag; |
65 | int pinchId; | 70 | int pinchId; |
66 | enum iTouchEdge edge; | 71 | enum iTouchEdge edge; |
@@ -132,9 +137,6 @@ static iTouch *find_TouchState_(iTouchState *d, SDL_FingerID id) { | |||
132 | return NULL; | 137 | return NULL; |
133 | } | 138 | } |
134 | 139 | ||
135 | static const uint32_t longPressSpanMs_ = 500; | ||
136 | static const int tapRadiusPt_ = 10; | ||
137 | |||
138 | iLocalDef float distance_Touch_(const iTouch *d) { | 140 | iLocalDef float distance_Touch_(const iTouch *d) { |
139 | return length_F3(sub_F3(d->pos[0], d->startPos)); | 141 | return length_F3(sub_F3(d->pos[0], d->startPos)); |
140 | } | 142 | } |
@@ -246,6 +248,11 @@ iLocalDef double accurateTicks_(void) { | |||
246 | return 1000.0 * (double) count / (double) freq; | 248 | return 1000.0 * (double) count / (double) freq; |
247 | } | 249 | } |
248 | 250 | ||
251 | static iFloat3 gestureVector_Touch_(const iTouch *d) { | ||
252 | const size_t lastIndex = iMin(d->posCount - 1, lastIndex_Touch_); | ||
253 | return sub_F3(d->pos[0], d->pos[lastIndex]); | ||
254 | } | ||
255 | |||
249 | static void update_TouchState_(void *ptr) { | 256 | static void update_TouchState_(void *ptr) { |
250 | iTouchState *d = ptr; | 257 | iTouchState *d = ptr; |
251 | /* Check for long presses to simulate right clicks. */ | 258 | /* Check for long presses to simulate right clicks. */ |
@@ -255,6 +262,24 @@ static void update_TouchState_(void *ptr) { | |||
255 | if (touch->pinchId || touch->isTouchDrag) { | 262 | if (touch->pinchId || touch->isTouchDrag) { |
256 | continue; | 263 | continue; |
257 | } | 264 | } |
265 | if (touch->edge) { | ||
266 | const iFloat3 pos = touch->pos[0]; | ||
267 | /* Cancel the swipe if the finger doesn't move or moves mostly vertically. */ | ||
268 | const iFloat3 gestureVector = gestureVector_Touch_(touch); | ||
269 | if (fabsf(2 * x_F3(gestureVector)) < fabsf(y_F3(gestureVector)) || | ||
270 | (isStationary_Touch_(touch) && nowTime - touch->startTime > shortPressSpanMs_)) { | ||
271 | //const int swipeDir = x_F3(gestureVector) > 0 ? +1 : -1; | ||
272 | //dispatchClick_Touch_(touch, | ||
273 | // touch->edge == left_TouchEdge && swipeDir > 0 ? SDL_BUTTON_X1 : | ||
274 | // touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); | ||
275 | // setHover_Widget(NULL); | ||
276 | postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", touch->edge, touch->id); | ||
277 | touch->edge = none_TouchEdge; | ||
278 | /* May be a regular drag along the edge so don't remove. */ | ||
279 | //remove_ArrayIterator(&i); | ||
280 | } | ||
281 | continue; | ||
282 | } | ||
258 | /* Holding a touch will reset previous momentum for this widget. */ | 283 | /* Holding a touch will reset previous momentum for this widget. */ |
259 | if (isStationary_Touch_(touch)) { | 284 | if (isStationary_Touch_(touch)) { |
260 | const int elapsed = nowTime - touch->startTime; | 285 | const int elapsed = nowTime - touch->startTime; |
@@ -340,6 +365,7 @@ static void update_TouchState_(void *ptr) { | |||
340 | } | 365 | } |
341 | } | 366 | } |
342 | 367 | ||
368 | #if 0 | ||
343 | static iWidget *findSlidePanel_Widget_(iWidget *d) { | 369 | static iWidget *findSlidePanel_Widget_(iWidget *d) { |
344 | for (iWidget *w = d; w; w = parent_Widget(w)) { | 370 | for (iWidget *w = d; w; w = parent_Widget(w)) { |
345 | if (isVisible_Widget(w) && flags_Widget(w) & edgeDraggable_WidgetFlag) { | 371 | if (isVisible_Widget(w) && flags_Widget(w) & edgeDraggable_WidgetFlag) { |
@@ -348,6 +374,7 @@ static iWidget *findSlidePanel_Widget_(iWidget *d) { | |||
348 | } | 374 | } |
349 | return NULL; | 375 | return NULL; |
350 | } | 376 | } |
377 | #endif | ||
351 | 378 | ||
352 | static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) { | 379 | static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) { |
353 | iWidget *affinity = newTouch->affinity; | 380 | iWidget *affinity = newTouch->affinity; |
@@ -365,6 +392,12 @@ static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) { | |||
365 | pinch.touchIds[1] = other->id; | 392 | pinch.touchIds[1] = other->id; |
366 | newTouch->pinchId = other->pinchId = pinch.id; | 393 | newTouch->pinchId = other->pinchId = pinch.id; |
367 | clearWidgetMomentum_TouchState_(d, affinity); | 394 | clearWidgetMomentum_TouchState_(d, affinity); |
395 | if (other->edge && other->didPostEdgeMove) { | ||
396 | postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", other->edge, other->id); | ||
397 | other->didPostEdgeMove = iFalse; | ||
398 | } | ||
399 | other->edge = none_TouchEdge; | ||
400 | newTouch->edge = none_TouchEdge; | ||
368 | /* Remember current positions to determine pinch amount. */ | 401 | /* Remember current positions to determine pinch amount. */ |
369 | newTouch->startPos = newTouch->pos[0]; | 402 | newTouch->startPos = newTouch->pos[0]; |
370 | other->startPos = other->pos[0]; | 403 | other->startPos = other->pos[0]; |
@@ -452,6 +485,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
452 | edge = right_TouchEdge; | 485 | edge = right_TouchEdge; |
453 | } | 486 | } |
454 | iWidget *aff = hitChild_Window(window, init_I2(iRound(x), iRound(y_F3(pos)))); | 487 | iWidget *aff = hitChild_Window(window, init_I2(iRound(x), iRound(y_F3(pos)))); |
488 | #if 0 | ||
455 | if (edge == left_TouchEdge) { | 489 | if (edge == left_TouchEdge) { |
456 | dragging = findSlidePanel_Widget_(aff); | 490 | dragging = findSlidePanel_Widget_(aff); |
457 | if (dragging) { | 491 | if (dragging) { |
@@ -460,6 +494,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
460 | setFlags_Widget(dragging, dragged_WidgetFlag, iTrue); | 494 | setFlags_Widget(dragging, dragged_WidgetFlag, iTrue); |
461 | } | 495 | } |
462 | } | 496 | } |
497 | #endif | ||
463 | /* TODO: We must retain a reference to the affinity widget, or otherwise it might | 498 | /* TODO: We must retain a reference to the affinity widget, or otherwise it might |
464 | be destroyed during the gesture. */ | 499 | be destroyed during the gesture. */ |
465 | // printf("aff:[%p] %s:'%s'\n", aff, aff ? class_Widget(aff)->name : "-", | 500 | // printf("aff:[%p] %s:'%s'\n", aff, aff ? class_Widget(aff)->name : "-", |
@@ -469,7 +504,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
469 | iTouch newTouch = { | 504 | iTouch newTouch = { |
470 | .id = fing->fingerId, | 505 | .id = fing->fingerId, |
471 | .affinity = aff, | 506 | .affinity = aff, |
472 | .edgeDragging = dragging, | 507 | // .edgeDragging = dragging, |
473 | .didBeginOnTouchDrag = (flags_Widget(aff) & touchDrag_WidgetFlag) != 0, | 508 | .didBeginOnTouchDrag = (flags_Widget(aff) & touchDrag_WidgetFlag) != 0, |
474 | .edge = edge, | 509 | .edge = edge, |
475 | .startTime = nowTime, | 510 | .startTime = nowTime, |
@@ -487,6 +522,16 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
487 | } | 522 | } |
488 | else if (ev->type == SDL_FINGERMOTION) { | 523 | else if (ev->type == SDL_FINGERMOTION) { |
489 | iTouch *touch = find_TouchState_(d, fing->fingerId); | 524 | iTouch *touch = find_TouchState_(d, fing->fingerId); |
525 | if (touch && touch->edge) { | ||
526 | clear_Array(d->moms); | ||
527 | pushPos_Touch_(touch, pos, nowTime); | ||
528 | postCommandf_App("edgeswipe.moved arg:%d side:%d id:%llu", | ||
529 | (int) (x_F3(pos) - x_F3(touch->startPos)), | ||
530 | touch->edge, | ||
531 | touch->id); | ||
532 | touch->didPostEdgeMove = iTrue; | ||
533 | return iTrue; | ||
534 | } | ||
490 | if (touch && touch->affinity) { | 535 | if (touch && touch->affinity) { |
491 | if (touch->isTouchDrag) { | 536 | if (touch->isTouchDrag) { |
492 | dispatchMotion_Touch_(pos, SDL_BUTTON_LMASK); | 537 | dispatchMotion_Touch_(pos, SDL_BUTTON_LMASK); |
@@ -556,36 +601,18 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
556 | touch->axis = y_TouchAxis; | 601 | touch->axis = y_TouchAxis; |
557 | } | 602 | } |
558 | } | 603 | } |
559 | /* Edge swipe aborted? */ | 604 | iAssert(touch->edge == none_TouchEdge); |
560 | if (touch->edge == left_TouchEdge) { | ||
561 | if (fing->dx < 0 && x_F3(touch->pos[0]) < tapRadiusPt_ * window->pixelRatio) { | ||
562 | touch->edge = none_TouchEdge; | ||
563 | if (touch->edgeDragging) { | ||
564 | setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); | ||
565 | setVisualOffset_Widget(touch->edgeDragging, 0, 200, easeOut_AnimFlag); | ||
566 | touch->edgeDragging = NULL; | ||
567 | } | ||
568 | } | ||
569 | else if (touch->edgeDragging) { | ||
570 | setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0); | ||
571 | } | ||
572 | } | ||
573 | if (touch->edge == right_TouchEdge && fing->dx > 0) { | ||
574 | touch->edge = none_TouchEdge; | ||
575 | } | ||
576 | if (touch->edge) { | ||
577 | pixels.y = 0; | ||
578 | } | ||
579 | if (touch->axis == x_TouchAxis) { | 605 | if (touch->axis == x_TouchAxis) { |
580 | pixels.y = 0; | 606 | pixels.y = 0; |
581 | } | 607 | } |
582 | if (touch->axis == y_TouchAxis) { | 608 | if (touch->axis == y_TouchAxis) { |
583 | pixels.x = 0; | 609 | pixels.x = 0; |
584 | } | 610 | } |
585 | // printf("%p (%s) py: %i wy: %f acc: %f\n", | 611 | // printf("%p (%s) py: %i wy: %f acc: %f edge: %d\n", |
586 | // touch->affinity, | 612 | // touch->affinity, |
587 | // class_Widget(touch->affinity)->name, | 613 | // class_Widget(touch->affinity)->name, |
588 | // pixels.y, y_F3(amount), y_F3(touch->accum)); | 614 | // pixels.y, y_F3(amount), y_F3(touch->accum), |
615 | // touch->edge); | ||
589 | if (pixels.x || pixels.y) { | 616 | if (pixels.x || pixels.y) { |
590 | setFocus_Widget(NULL); | 617 | setFocus_Widget(NULL); |
591 | dispatchMotion_Touch_(touch->pos[0], 0); | 618 | dispatchMotion_Touch_(touch->pos[0], 0); |
@@ -612,9 +639,21 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
612 | endPinch_TouchState_(d, touch->pinchId); | 639 | endPinch_TouchState_(d, touch->pinchId); |
613 | break; | 640 | break; |
614 | } | 641 | } |
642 | #if 0 | ||
615 | if (touch->edgeDragging) { | 643 | if (touch->edgeDragging) { |
616 | setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); | 644 | setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); |
617 | } | 645 | } |
646 | #endif | ||
647 | if (touch->edge && !isStationary_Touch_(touch)) { | ||
648 | const iFloat3 gesture = gestureVector_Touch_(touch); | ||
649 | const float pixel = window->pixelRatio; | ||
650 | const int moveDir = x_F3(gesture) < -pixel ? -1 : x_F3(gesture) > pixel ? +1 : 0; | ||
651 | const int didAbort = (touch->edge == left_TouchEdge && moveDir < 0) || | ||
652 | (touch->edge == right_TouchEdge && moveDir > 0); | ||
653 | postCommandf_App("edgeswipe.ended abort:%d side:%d id:%llu", didAbort, touch->edge, touch->id); | ||
654 | remove_ArrayIterator(&i); | ||
655 | continue; | ||
656 | } | ||
618 | if (flags_Widget(touch->affinity) & touchDrag_WidgetFlag) { | 657 | if (flags_Widget(touch->affinity) & touchDrag_WidgetFlag) { |
619 | if (!touch->isLeftDown && !touch->isTapAndHold) { | 658 | if (!touch->isLeftDown && !touch->isTapAndHold) { |
620 | /* This will be a click on a touchDrag widget. */ | 659 | /* This will be a click on a touchDrag widget. */ |
@@ -638,6 +677,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
638 | const uint32_t duration = nowTime - touch->startTime; | 677 | const uint32_t duration = nowTime - touch->startTime; |
639 | const iFloat3 gestureVector = sub_F3(pos, touch->pos[lastIndex]); | 678 | const iFloat3 gestureVector = sub_F3(pos, touch->pos[lastIndex]); |
640 | iFloat3 velocity = zero_F3(); | 679 | iFloat3 velocity = zero_F3(); |
680 | #if 0 | ||
641 | if (touch->edge && fabsf(2 * x_F3(gestureVector)) > fabsf(y_F3(gestureVector)) && | 681 | if (touch->edge && fabsf(2 * x_F3(gestureVector)) > fabsf(y_F3(gestureVector)) && |
642 | !isStationary_Touch_(touch)) { | 682 | !isStationary_Touch_(touch)) { |
643 | const int swipeDir = x_F3(gestureVector) > 0 ? +1 : -1; | 683 | const int swipeDir = x_F3(gestureVector) > 0 ? +1 : -1; |
@@ -646,7 +686,9 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
646 | touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); | 686 | touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); |
647 | setHover_Widget(NULL); | 687 | setHover_Widget(NULL); |
648 | } | 688 | } |
649 | else { | 689 | else |
690 | #endif | ||
691 | { | ||
650 | const uint32_t elapsed = fing->timestamp - touch->posTime[lastIndex]; | 692 | const uint32_t elapsed = fing->timestamp - touch->posTime[lastIndex]; |
651 | const float minVelocity = 400.0f; | 693 | const float minVelocity = 400.0f; |
652 | if (elapsed < 150) { | 694 | if (elapsed < 150) { |
diff --git a/src/ui/util.c b/src/ui/util.c index c4fb8886..e0b05a44 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -710,7 +710,8 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
710 | menu, | 710 | menu, |
711 | iClob(newKeyMods_LabelWidget(labelText, item->key, item->kmods, item->command)), | 711 | iClob(newKeyMods_LabelWidget(labelText, item->key, item->kmods, item->command)), |
712 | noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | | 712 | noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | |
713 | drawKey_WidgetFlag | (isInfo ? wrapText_WidgetFlag : 0) | itemFlags); | 713 | drawKey_WidgetFlag | itemFlags); |
714 | setWrap_LabelWidget(label, isInfo); | ||
714 | haveIcons |= checkIcon_LabelWidget(label); | 715 | haveIcons |= checkIcon_LabelWidget(label); |
715 | updateSize_LabelWidget(label); /* drawKey was set */ | 716 | updateSize_LabelWidget(label); /* drawKey was set */ |
716 | if (isInfo) { | 717 | if (isInfo) { |
@@ -776,7 +777,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { | |||
776 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | 777 | if (isInstance_Object(i.object, &Class_LabelWidget)) { |
777 | iLabelWidget *label = i.object; | 778 | iLabelWidget *label = i.object; |
778 | const iBool isCaution = startsWith_String(text_LabelWidget(label), uiTextCaution_ColorEscape); | 779 | const iBool isCaution = startsWith_String(text_LabelWidget(label), uiTextCaution_ColorEscape); |
779 | if (flags_Widget(as_Widget(label)) & wrapText_WidgetFlag) { | 780 | if (isWrapped_LabelWidget(label)) { |
780 | continue; | 781 | continue; |
781 | } | 782 | } |
782 | if (deviceType_App() == desktop_AppDeviceType) { | 783 | if (deviceType_App() == desktop_AppDeviceType) { |
@@ -979,8 +980,8 @@ static void addTabPage_Widget_(iWidget *tabs, enum iWidgetAddPos addPos, iWidget | |||
979 | iClob(newKeyMods_LabelWidget(label, key, kmods, format_CStr("tabs.switch page:%p", page))), | 980 | iClob(newKeyMods_LabelWidget(label, key, kmods, format_CStr("tabs.switch page:%p", page))), |
980 | addPos); | 981 | addPos); |
981 | setFlags_Widget(button, selected_WidgetFlag, isSel); | 982 | setFlags_Widget(button, selected_WidgetFlag, isSel); |
982 | setFlags_Widget( | 983 | setFlags_Widget(button, commandOnClick_WidgetFlag | expand_WidgetFlag, iTrue); |
983 | button, noTopFrame_WidgetFlag | commandOnClick_WidgetFlag | expand_WidgetFlag, iTrue); | 984 | setNoTopFrame_LabelWidget((iLabelWidget *) button, iTrue); |
984 | addChildPos_Widget(pages, page, addPos); | 985 | addChildPos_Widget(pages, page, addPos); |
985 | if (tabCount_Widget(tabs) > 1) { | 986 | if (tabCount_Widget(tabs) > 1) { |
986 | setFlags_Widget(buttons, hidden_WidgetFlag, iFalse); | 987 | setFlags_Widget(buttons, hidden_WidgetFlag, iFalse); |
@@ -1317,7 +1318,7 @@ void updateValueInput_Widget(iWidget *d, const char *title, const char *prompt) | |||
1317 | 1318 | ||
1318 | static iBool messageHandler_(iWidget *msg, const char *cmd) { | 1319 | static iBool messageHandler_(iWidget *msg, const char *cmd) { |
1319 | /* Almost any command dismisses the sheet. */ | 1320 | /* Almost any command dismisses the sheet. */ |
1320 | /* TODO: Use a "notification" prefix (like `) to ignore all types of commands line this? */ | 1321 | /* TODO: Add a "notification" type of user events to separate them from user actions. */ |
1321 | if (!(equal_Command(cmd, "media.updated") || | 1322 | if (!(equal_Command(cmd, "media.updated") || |
1322 | equal_Command(cmd, "media.player.update") || | 1323 | equal_Command(cmd, "media.player.update") || |
1323 | equal_Command(cmd, "bookmarks.request.finished") || | 1324 | equal_Command(cmd, "bookmarks.request.finished") || |
@@ -1326,6 +1327,7 @@ static iBool messageHandler_(iWidget *msg, const char *cmd) { | |||
1326 | equal_Command(cmd, "document.request.updated") || | 1327 | equal_Command(cmd, "document.request.updated") || |
1327 | equal_Command(cmd, "scrollbar.fade") || | 1328 | equal_Command(cmd, "scrollbar.fade") || |
1328 | equal_Command(cmd, "widget.overflow") || | 1329 | equal_Command(cmd, "widget.overflow") || |
1330 | equal_Command(cmd, "edgeswipe.ended") || | ||
1329 | startsWith_CStr(cmd, "window."))) { | 1331 | startsWith_CStr(cmd, "window."))) { |
1330 | setupSheetTransition_Mobile(msg, iFalse); | 1332 | setupSheetTransition_Mobile(msg, iFalse); |
1331 | destroy_Widget(msg); | 1333 | destroy_Widget(msg); |
@@ -1599,8 +1601,6 @@ iWidget *makePreferences_Widget(void) { | |||
1599 | /* General preferences. */ { | 1601 | /* General preferences. */ { |
1600 | appendTwoColumnPage_(tabs, "${heading.prefs.general}", '1', &headings, &values); | 1602 | appendTwoColumnPage_(tabs, "${heading.prefs.general}", '1', &headings, &values); |
1601 | #if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) | 1603 | #if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) |
1602 | //addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.downloads}"))); | ||
1603 | //setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads"); | ||
1604 | addPrefsInputWithHeading_(headings, values, "prefs.downloads", iClob(new_InputWidget(0))); | 1604 | addPrefsInputWithHeading_(headings, values, "prefs.downloads", iClob(new_InputWidget(0))); |
1605 | #endif | 1605 | #endif |
1606 | iInputWidget *searchUrl; | 1606 | iInputWidget *searchUrl; |
@@ -1608,12 +1608,6 @@ iWidget *makePreferences_Widget(void) { | |||
1608 | setUrlContent_InputWidget(searchUrl, iTrue); | 1608 | setUrlContent_InputWidget(searchUrl, iTrue); |
1609 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); | 1609 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); |
1610 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); | 1610 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); |
1611 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.collapsepreonload}"))); | ||
1612 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.collapsepreonload"))); | ||
1613 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.plaintext.wrap}"))); | ||
1614 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.plaintext.wrap"))); | ||
1615 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.centershort}"))); | ||
1616 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort"))); | ||
1617 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.hoverlink}"))); | 1611 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.hoverlink}"))); |
1618 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); | 1612 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); |
1619 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.archive.openindex}"))); | 1613 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.archive.openindex}"))); |
@@ -1674,26 +1668,6 @@ iWidget *makePreferences_Widget(void) { | |||
1674 | } | 1668 | } |
1675 | /* User Interface. */ { | 1669 | /* User Interface. */ { |
1676 | appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values); | 1670 | appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values); |
1677 | #if defined (iPlatformApple) || defined (iPlatformMSys) | ||
1678 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); | ||
1679 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme"))); | ||
1680 | #endif | ||
1681 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.theme}"))); | ||
1682 | iWidget *themes = new_Widget(); | ||
1683 | /* Themes. */ { | ||
1684 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.black}", "theme.set arg:0"))), "prefs.theme.0"); | ||
1685 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.dark}", "theme.set arg:1"))), "prefs.theme.1"); | ||
1686 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.light}", "theme.set arg:2"))), "prefs.theme.2"); | ||
1687 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.white}", "theme.set arg:3"))), "prefs.theme.3"); | ||
1688 | } | ||
1689 | addChildFlags_Widget(values, iClob(themes), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | ||
1690 | /* Accents. */ | ||
1691 | iWidget *accent = new_Widget(); { | ||
1692 | setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.teal}", "accent.set arg:0"))), "prefs.accent.0"); | ||
1693 | setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.orange}", "accent.set arg:1"))), "prefs.accent.1"); | ||
1694 | } | ||
1695 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.accent}"))); | ||
1696 | addChildFlags_Widget(values, iClob(accent), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | ||
1697 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 1671 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
1698 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}"))); | 1672 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}"))); |
1699 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe"))); | 1673 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe"))); |
@@ -1718,6 +1692,27 @@ iWidget *makePreferences_Widget(void) { | |||
1718 | } | 1692 | } |
1719 | /* Colors. */ { | 1693 | /* Colors. */ { |
1720 | appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values); | 1694 | appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values); |
1695 | makeTwoColumnHeading_("${heading.prefs.uitheme}", headings, values); | ||
1696 | #if defined (iPlatformApple) || defined (iPlatformMSys) | ||
1697 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); | ||
1698 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme"))); | ||
1699 | #endif | ||
1700 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.theme}"))); | ||
1701 | iWidget *themes = new_Widget(); | ||
1702 | /* Themes. */ { | ||
1703 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.black}", "theme.set arg:0"))), "prefs.theme.0"); | ||
1704 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.dark}", "theme.set arg:1"))), "prefs.theme.1"); | ||
1705 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.light}", "theme.set arg:2"))), "prefs.theme.2"); | ||
1706 | setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.white}", "theme.set arg:3"))), "prefs.theme.3"); | ||
1707 | } | ||
1708 | addChildFlags_Widget(values, iClob(themes), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | ||
1709 | /* Accents. */ | ||
1710 | iWidget *accent = new_Widget(); { | ||
1711 | setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.teal}", "accent.set arg:0"))), "prefs.accent.0"); | ||
1712 | setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.orange}", "accent.set arg:1"))), "prefs.accent.1"); | ||
1713 | } | ||
1714 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.accent}"))); | ||
1715 | addChildFlags_Widget(values, iClob(accent), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | ||
1721 | makeTwoColumnHeading_("${heading.prefs.pagecontent}", headings, values); | 1716 | makeTwoColumnHeading_("${heading.prefs.pagecontent}", headings, values); |
1722 | for (int i = 0; i < 2; ++i) { | 1717 | for (int i = 0; i < 2; ++i) { |
1723 | const iBool isDark = (i == 0); | 1718 | const iBool isDark = (i == 0); |
@@ -1750,14 +1745,15 @@ iWidget *makePreferences_Widget(void) { | |||
1750 | } | 1745 | } |
1751 | addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1746 | addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1752 | } | 1747 | } |
1753 | /* Layout. */ { | 1748 | /* Fonts. */ { |
1754 | setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values), "prefs.page.style"); | 1749 | setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts"); |
1755 | makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values); | ||
1756 | /* Fonts. */ { | 1750 | /* Fonts. */ { |
1757 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); | 1751 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); |
1758 | addFontButtons_(values, "headingfont"); | 1752 | addFontButtons_(values, "headingfont"); |
1759 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font}"))); | 1753 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font}"))); |
1760 | addFontButtons_(values, "font"); | 1754 | addFontButtons_(values, "font"); |
1755 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); | ||
1756 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); | ||
1761 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}"))); | 1757 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}"))); |
1762 | iWidget *mono = new_Widget(); { | 1758 | iWidget *mono = new_Widget(); { |
1763 | iWidget *tog; | 1759 | iWidget *tog; |
@@ -1789,9 +1785,18 @@ iWidget *makePreferences_Widget(void) { | |||
1789 | updateSize_LabelWidget((iLabelWidget *) tog); | 1785 | updateSize_LabelWidget((iLabelWidget *) tog); |
1790 | } | 1786 | } |
1791 | addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1787 | addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1792 | addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(new_InputWidget(0))); | 1788 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); |
1793 | } | 1789 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); |
1794 | makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); | 1790 | /* Custom font. */ { |
1791 | iInputWidget *customFont = new_InputWidget(0); | ||
1792 | setHint_InputWidget(customFont, "${hint.prefs.userfont}"); | ||
1793 | addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont)); | ||
1794 | } | ||
1795 | } | ||
1796 | } | ||
1797 | /* Style. */ { | ||
1798 | setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '5', &headings, &values), "prefs.page.style"); | ||
1799 | // makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); | ||
1795 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); | 1800 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); |
1796 | iWidget *widths = new_Widget(); | 1801 | iWidget *widths = new_Widget(); |
1797 | /* Line widths. */ { | 1802 | /* Line widths. */ { |
@@ -1811,14 +1816,20 @@ iWidget *makePreferences_Widget(void) { | |||
1811 | addChildFlags_Widget(values, iClob(quote), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1816 | addChildFlags_Widget(values, iClob(quote), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1812 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.biglede}"))); | 1817 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.biglede}"))); |
1813 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede"))); | 1818 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede"))); |
1819 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.plaintext.wrap}"))); | ||
1820 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.plaintext.wrap"))); | ||
1821 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.collapsepreonload}"))); | ||
1822 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.collapsepreonload"))); | ||
1814 | // makeTwoColumnHeading_("${heading.prefs.widelayout}", headings, values); | 1823 | // makeTwoColumnHeading_("${heading.prefs.widelayout}", headings, values); |
1815 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); | 1824 | addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); |
1816 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); | 1825 | addChild_Widget(values, iClob(makePadding_Widget(bigGap))); |
1817 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.sideicon}"))); | 1826 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.sideicon}"))); |
1818 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.sideicon"))); | 1827 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.sideicon"))); |
1828 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.centershort}"))); | ||
1829 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort"))); | ||
1819 | } | 1830 | } |
1820 | /* Network. */ { | 1831 | /* Network. */ { |
1821 | appendTwoColumnPage_(tabs, "${heading.prefs.network}", '5', &headings, &values); | 1832 | appendTwoColumnPage_(tabs, "${heading.prefs.network}", '6', &headings, &values); |
1822 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); | 1833 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); |
1823 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | 1834 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); |
1824 | /* Cache size. */ { | 1835 | /* Cache size. */ { |
@@ -1832,6 +1843,17 @@ iWidget *makePreferences_Widget(void) { | |||
1832 | resizeToParentHeight_WidgetFlag); | 1843 | resizeToParentHeight_WidgetFlag); |
1833 | setContentPadding_InputWidget(cache, 0, width_Widget(unit) - 4 * gap_UI); | 1844 | setContentPadding_InputWidget(cache, 0, width_Widget(unit) - 4 * gap_UI); |
1834 | } | 1845 | } |
1846 | /* Memory size. */ { | ||
1847 | iInputWidget *mem = new_InputWidget(4); | ||
1848 | setSelectAllOnFocus_InputWidget(mem, iTrue); | ||
1849 | addPrefsInputWithHeading_(headings, values, "prefs.memorysize", iClob(mem)); | ||
1850 | iWidget *unit = | ||
1851 | addChildFlags_Widget(as_Widget(mem), | ||
1852 | iClob(new_LabelWidget("${mb}", NULL)), | ||
1853 | frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag | | ||
1854 | resizeToParentHeight_WidgetFlag); | ||
1855 | setContentPadding_InputWidget(mem, 0, width_Widget(unit) - 4 * gap_UI); | ||
1856 | } | ||
1835 | makeTwoColumnHeading_("${heading.prefs.certs}", headings, values); | 1857 | makeTwoColumnHeading_("${heading.prefs.certs}", headings, values); |
1836 | addPrefsInputWithHeading_(headings, values, "prefs.ca.file", iClob(new_InputWidget(0))); | 1858 | addPrefsInputWithHeading_(headings, values, "prefs.ca.file", iClob(new_InputWidget(0))); |
1837 | addPrefsInputWithHeading_(headings, values, "prefs.ca.path", iClob(new_InputWidget(0))); | 1859 | addPrefsInputWithHeading_(headings, values, "prefs.ca.path", iClob(new_InputWidget(0))); |
@@ -1843,7 +1865,7 @@ iWidget *makePreferences_Widget(void) { | |||
1843 | /* Keybindings. */ | 1865 | /* Keybindings. */ |
1844 | if (deviceType_App() == desktop_AppDeviceType) { | 1866 | if (deviceType_App() == desktop_AppDeviceType) { |
1845 | iBindingsWidget *bind = new_BindingsWidget(); | 1867 | iBindingsWidget *bind = new_BindingsWidget(); |
1846 | appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY); | 1868 | appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '7', KMOD_PRIMARY); |
1847 | } | 1869 | } |
1848 | addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); | 1870 | addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); |
1849 | updatePreferencesLayout_Widget(dlg); | 1871 | updatePreferencesLayout_Widget(dlg); |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 543b8bc9..992f115d 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -82,10 +82,10 @@ static void visualOffsetAnimation_Widget_(void *ptr) { | |||
82 | 82 | ||
83 | void deinit_Widget(iWidget *d) { | 83 | void deinit_Widget(iWidget *d) { |
84 | releaseChildren_Widget(d); | 84 | releaseChildren_Widget(d); |
85 | //#if !defined (NDEBUG) | 85 | #if 0 && !defined (NDEBUG) |
86 | // printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), | 86 | printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), |
87 | // d->flags & keepOnTop_WidgetFlag ? 1 : 0); | 87 | d->flags & keepOnTop_WidgetFlag ? 1 : 0); |
88 | //#endif | 88 | #endif |
89 | deinit_String(&d->id); | 89 | deinit_String(&d->id); |
90 | if (d->flags & keepOnTop_WidgetFlag) { | 90 | if (d->flags & keepOnTop_WidgetFlag) { |
91 | removeAll_PtrArray(onTop_Root(d->root), d); | 91 | removeAll_PtrArray(onTop_Root(d->root), d); |
@@ -321,7 +321,6 @@ static iBool setWidth_Widget_(iWidget *d, int width) { | |||
321 | d->rect.size.x = width; | 321 | d->rect.size.x = width; |
322 | TRACE(d, "width has changed to %d", width); | 322 | TRACE(d, "width has changed to %d", width); |
323 | if (class_Widget(d)->sizeChanged) { | 323 | if (class_Widget(d)->sizeChanged) { |
324 | const int oldHeight = d->rect.size.y; | ||
325 | class_Widget(d)->sizeChanged(d); | 324 | class_Widget(d)->sizeChanged(d); |
326 | } | 325 | } |
327 | return iTrue; | 326 | return iTrue; |
@@ -407,6 +406,11 @@ static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) { | |||
407 | iRect childRect = child->rect; | 406 | iRect childRect = child->rect; |
408 | if (child->flags & ignoreForParentWidth_WidgetFlag) { | 407 | if (child->flags & ignoreForParentWidth_WidgetFlag) { |
409 | childRect.size.x = 0; | 408 | childRect.size.x = 0; |
409 | childRect.pos.x = bounds_out->pos.x; | ||
410 | } | ||
411 | if (child->flags & ignoreForParentHeight_WidgetFlag) { | ||
412 | childRect.size.y = 0; | ||
413 | childRect.pos.y = bounds_out->pos.y; | ||
410 | } | 414 | } |
411 | if (isEmpty_Rect(*bounds_out)) { | 415 | if (isEmpty_Rect(*bounds_out)) { |
412 | *bounds_out = childRect; | 416 | *bounds_out = childRect; |
@@ -760,6 +764,27 @@ void arrange_Widget(iWidget *d) { | |||
760 | } | 764 | } |
761 | } | 765 | } |
762 | 766 | ||
767 | iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) { | ||
768 | return visualOffsetByReference_Widget(d) != 0; | ||
769 | } | ||
770 | |||
771 | int visualOffsetByReference_Widget(const iWidget *d) { | ||
772 | if (d->offsetRef && d->flags & refChildrenOffset_WidgetFlag) { | ||
773 | int offX = 0; | ||
774 | iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { | ||
775 | const iWidget *child = i.object; | ||
776 | if (child == d) continue; | ||
777 | if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { | ||
778 | // const float factor = width_Widget(d) / (float) size_Root(d->root).x; | ||
779 | const int invOff = width_Widget(d) - iRound(value_Anim(&child->visualOffset)); | ||
780 | offX -= invOff / 4; | ||
781 | } | ||
782 | } | ||
783 | return offX; | ||
784 | } | ||
785 | return 0; | ||
786 | } | ||
787 | |||
763 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { | 788 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { |
764 | if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { | 789 | if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { |
765 | const int off = iRound(value_Anim(&d->visualOffset)); | 790 | const int off = iRound(value_Anim(&d->visualOffset)); |
@@ -774,14 +799,7 @@ static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { | |||
774 | pos->y -= value_Anim(d->animOffsetRef); | 799 | pos->y -= value_Anim(d->animOffsetRef); |
775 | } | 800 | } |
776 | if (d->flags & refChildrenOffset_WidgetFlag) { | 801 | if (d->flags & refChildrenOffset_WidgetFlag) { |
777 | iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { | 802 | pos->x += visualOffsetByReference_Widget(d); |
778 | const iWidget *child = i.object; | ||
779 | if (child == d) continue; | ||
780 | if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { | ||
781 | const int invOff = size_Root(d->root).x - iRound(value_Anim(&child->visualOffset)); | ||
782 | pos->x -= invOff / 4; | ||
783 | } | ||
784 | } | ||
785 | } | 803 | } |
786 | } | 804 | } |
787 | 805 | ||
@@ -1057,10 +1075,38 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1057 | isCommand_UserEvent(ev, "widget.overflow")) { | 1075 | isCommand_UserEvent(ev, "widget.overflow")) { |
1058 | scrollOverflow_Widget(d, 0); /* check bounds */ | 1076 | scrollOverflow_Widget(d, 0); /* check bounds */ |
1059 | } | 1077 | } |
1060 | if (ev->user.code == command_UserEventCode && d->commandHandler && | 1078 | if (ev->user.code == command_UserEventCode) { |
1061 | d->commandHandler(d, ev->user.data1)) { | 1079 | const char *cmd = command_UserEvent(ev); |
1062 | iAssert(get_Root() == d->root); | 1080 | if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) && |
1063 | return iTrue; | 1081 | isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag && |
1082 | equal_Command(cmd, "edgeswipe.moved")) { | ||
1083 | /* Check the side. */ | ||
1084 | const int side = argLabel_Command(cmd, "side"); | ||
1085 | if ((side == 1 && d->flags & leftEdgeDraggable_WidgetFlag) || | ||
1086 | (side == 2 && d->flags & rightEdgeDraggable_WidgetFlag)) { | ||
1087 | if (~d->flags & dragged_WidgetFlag) { | ||
1088 | setFlags_Widget(d, dragged_WidgetFlag, iTrue); | ||
1089 | } | ||
1090 | setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)) * | ||
1091 | width_Widget(d) / size_Root(d->root).x, | ||
1092 | 10, 0); | ||
1093 | return iTrue; | ||
1094 | } | ||
1095 | } | ||
1096 | if (d->flags & dragged_WidgetFlag && equal_Command(cmd, "edgeswipe.ended")) { | ||
1097 | if (argLabel_Command(cmd, "abort")) { | ||
1098 | setVisualOffset_Widget(d, 0, 200, easeOut_AnimFlag); | ||
1099 | } | ||
1100 | else { | ||
1101 | postCommand_Widget( | ||
1102 | d, argLabel_Command(cmd, "side") == 1 ? "swipe.back" : "swipe.forward"); | ||
1103 | } | ||
1104 | setFlags_Widget(d, dragged_WidgetFlag, iFalse); | ||
1105 | } | ||
1106 | if (d->commandHandler && d->commandHandler(d, ev->user.data1)) { | ||
1107 | iAssert(get_Root() == d->root); | ||
1108 | return iTrue; | ||
1109 | } | ||
1064 | } | 1110 | } |
1065 | break; | 1111 | break; |
1066 | } | 1112 | } |
@@ -1091,6 +1137,17 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1091 | return iFalse; | 1137 | return iFalse; |
1092 | } | 1138 | } |
1093 | 1139 | ||
1140 | int backgroundFadeColor_Widget(void) { | ||
1141 | switch (colorTheme_App()) { | ||
1142 | case light_ColorTheme: | ||
1143 | return gray25_ColorId; | ||
1144 | case pureWhite_ColorTheme: | ||
1145 | return gray50_ColorId; | ||
1146 | default: | ||
1147 | return black_ColorId; | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1094 | void drawBackground_Widget(const iWidget *d) { | 1151 | void drawBackground_Widget(const iWidget *d) { |
1095 | if (d->flags & noBackground_WidgetFlag) { | 1152 | if (d->flags & noBackground_WidgetFlag) { |
1096 | return; | 1153 | return; |
@@ -1107,14 +1164,13 @@ void drawBackground_Widget(const iWidget *d) { | |||
1107 | shadowBorder = iFalse; | 1164 | shadowBorder = iFalse; |
1108 | } | 1165 | } |
1109 | } | 1166 | } |
1110 | if (shadowBorder) { | 1167 | if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) { |
1111 | iPaint p; | 1168 | iPaint p; |
1112 | init_Paint(&p); | 1169 | init_Paint(&p); |
1113 | drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); | 1170 | drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); |
1114 | } | 1171 | } |
1115 | const iBool isFaded = fadeBackground && | 1172 | const iBool isFaded = fadeBackground && |
1116 | ~d->flags & noFadeBackground_WidgetFlag;/* && | 1173 | ~d->flags & noFadeBackground_WidgetFlag; |
1117 | ~d->flags & destroyPending_WidgetFlag;*/ | ||
1118 | if (isFaded) { | 1174 | if (isFaded) { |
1119 | iPaint p; | 1175 | iPaint p; |
1120 | init_Paint(&p); | 1176 | init_Paint(&p); |
@@ -1125,19 +1181,7 @@ void drawBackground_Widget(const iWidget *d) { | |||
1125 | p.alpha *= (area > 0 ? visibleArea / area : 0.0f); | 1181 | p.alpha *= (area > 0 ? visibleArea / area : 0.0f); |
1126 | } | 1182 | } |
1127 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 1183 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); |
1128 | int fadeColor; | 1184 | fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget()); |
1129 | switch (colorTheme_App()) { | ||
1130 | default: | ||
1131 | fadeColor = black_ColorId; | ||
1132 | break; | ||
1133 | case light_ColorTheme: | ||
1134 | fadeColor = gray25_ColorId; | ||
1135 | break; | ||
1136 | case pureWhite_ColorTheme: | ||
1137 | fadeColor = gray50_ColorId; | ||
1138 | break; | ||
1139 | } | ||
1140 | fillRect_Paint(&p, rect_Root(d->root), fadeColor); | ||
1141 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 1185 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
1142 | } | 1186 | } |
1143 | if (d->bgColor >= 0 || d->frameColor >= 0) { | 1187 | if (d->bgColor >= 0 || d->frameColor >= 0) { |
diff --git a/src/ui/widget.h b/src/ui/widget.h index 8de62b7a..41784b99 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -87,12 +87,12 @@ enum iWidgetFlag { | |||
87 | }; | 87 | }; |
88 | 88 | ||
89 | /* 64-bit extended flags */ | 89 | /* 64-bit extended flags */ |
90 | //#define wasCollapsed_WidgetFlag iBit64(32) | 90 | #define rightEdgeDraggable_WidgetFlag iBit64(31) |
91 | #define disabledWhenHidden_WidgetFlag iBit64(32) | 91 | #define disabledWhenHidden_WidgetFlag iBit64(32) |
92 | #define centerHorizontal_WidgetFlag iBit64(33) | 92 | #define centerHorizontal_WidgetFlag iBit64(33) |
93 | #define moveToParentLeftEdge_WidgetFlag iBit64(34) | 93 | #define moveToParentLeftEdge_WidgetFlag iBit64(34) |
94 | #define moveToParentRightEdge_WidgetFlag iBit64(35) | 94 | #define moveToParentRightEdge_WidgetFlag iBit64(35) |
95 | #define wrapText_WidgetFlag iBit64(36) | 95 | #define noShadowBorder_WidgetFlag iBit64(36) |
96 | #define borderTop_WidgetFlag iBit64(37) | 96 | #define borderTop_WidgetFlag iBit64(37) |
97 | #define overflowScrollable_WidgetFlag iBit64(38) | 97 | #define overflowScrollable_WidgetFlag iBit64(38) |
98 | #define focusRoot_WidgetFlag iBit64(39) | 98 | #define focusRoot_WidgetFlag iBit64(39) |
@@ -103,7 +103,7 @@ enum iWidgetFlag { | |||
103 | #define drawBackgroundToVerticalSafeArea_WidgetFlag iBit64(44) | 103 | #define drawBackgroundToVerticalSafeArea_WidgetFlag iBit64(44) |
104 | #define visualOffset_WidgetFlag iBit64(45) | 104 | #define visualOffset_WidgetFlag iBit64(45) |
105 | #define parentCannotResize_WidgetFlag iBit64(46) | 105 | #define parentCannotResize_WidgetFlag iBit64(46) |
106 | #define noTopFrame_WidgetFlag iBit64(47) | 106 | #define ignoreForParentHeight_WidgetFlag iBit64(47) |
107 | #define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */ | 107 | #define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */ |
108 | #define extraPadding_WidgetFlag iBit64(49) | 108 | #define extraPadding_WidgetFlag iBit64(49) |
109 | #define borderBottom_WidgetFlag iBit64(50) | 109 | #define borderBottom_WidgetFlag iBit64(50) |
@@ -117,8 +117,8 @@ enum iWidgetFlag { | |||
117 | #define parentCannotResizeHeight_WidgetFlag iBit64(58) | 117 | #define parentCannotResizeHeight_WidgetFlag iBit64(58) |
118 | #define ignoreForParentWidth_WidgetFlag iBit64(59) | 118 | #define ignoreForParentWidth_WidgetFlag iBit64(59) |
119 | #define noFadeBackground_WidgetFlag iBit64(60) | 119 | #define noFadeBackground_WidgetFlag iBit64(60) |
120 | #define destroyPending_WidgetFlag iBit64(61) /* TODO: needed? */ | 120 | #define destroyPending_WidgetFlag iBit64(61) |
121 | #define edgeDraggable_WidgetFlag iBit64(62) | 121 | #define leftEdgeDraggable_WidgetFlag iBit64(62) |
122 | #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ | 122 | #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ |
123 | 123 | ||
124 | enum iWidgetAddPos { | 124 | enum iWidgetAddPos { |
@@ -242,8 +242,9 @@ iBool isSelected_Widget (const iAnyObject *); | |||
242 | iBool isUnderKeyRoot_Widget (const iAnyObject *); | 242 | iBool isUnderKeyRoot_Widget (const iAnyObject *); |
243 | iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd); | 243 | iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd); |
244 | iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); | 244 | iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); |
245 | iBool isAffectedByVisualOffset_Widget | 245 | iBool isAffectedByVisualOffset_Widget (const iWidget *); |
246 | (const iWidget *); | 246 | iBool isBeingVisuallyOffsetByReference_Widget (const iWidget *); |
247 | int visualOffsetByReference_Widget (const iWidget *); | ||
247 | void setId_Widget (iWidget *, const char *id); | 248 | void setId_Widget (iWidget *, const char *id); |
248 | void setFlags_Widget (iWidget *, int64_t flags, iBool set); | 249 | void setFlags_Widget (iWidget *, int64_t flags, iBool set); |
249 | void setPos_Widget (iWidget *, iInt2 pos); | 250 | void setPos_Widget (iWidget *, iInt2 pos); |
@@ -276,6 +277,8 @@ void refresh_Widget (const iAnyObject *); | |||
276 | 277 | ||
277 | iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); | 278 | iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); |
278 | 279 | ||
280 | int backgroundFadeColor_Widget (void); | ||
281 | |||
279 | void setFocus_Widget (iWidget *); | 282 | void setFocus_Widget (iWidget *); |
280 | iWidget * focus_Widget (void); | 283 | iWidget * focus_Widget (void); |
281 | void setHover_Widget (iWidget *); | 284 | void setHover_Widget (iWidget *); |
diff --git a/src/ui/window.c b/src/ui/window.c index 96a22fee..f71d8102 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -69,10 +69,6 @@ iDefineTypeConstructionArgs(Window, (iRect rect), rect) | |||
69 | 69 | ||
70 | /* TODO: Define menus per platform. */ | 70 | /* TODO: Define menus per platform. */ |
71 | 71 | ||
72 | #if defined (iPlatformAppleDesktop) | ||
73 | # define iHaveNativeMenus | ||
74 | #endif | ||
75 | |||
76 | #if defined (iHaveNativeMenus) | 72 | #if defined (iHaveNativeMenus) |
77 | /* Using native menus. */ | 73 | /* Using native menus. */ |
78 | static const iMenuItem fileMenuItems_[] = { | 74 | static const iMenuItem fileMenuItems_[] = { |
@@ -202,7 +198,7 @@ static void windowSizeChanged_Window_(iWindow *d) { | |||
202 | } | 198 | } |
203 | 199 | ||
204 | static void setupUserInterface_Window(iWindow *d) { | 200 | static void setupUserInterface_Window(iWindow *d) { |
205 | #if defined (iPlatformAppleDesktop) | 201 | #if defined (iHaveNativeMenus) |
206 | insertMacMenus_(); | 202 | insertMacMenus_(); |
207 | #endif | 203 | #endif |
208 | /* One root is created by default. */ | 204 | /* One root is created by default. */ |
@@ -913,7 +909,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
913 | } | 909 | } |
914 | } | 910 | } |
915 | if (isCommand_UserEvent(&event, "lang.changed")) { | 911 | if (isCommand_UserEvent(&event, "lang.changed")) { |
916 | #if defined (iPlatformAppleDesktop) | 912 | #if defined (iHaveNativeMenus) |
917 | /* Retranslate the menus. */ | 913 | /* Retranslate the menus. */ |
918 | removeMacMenus_(); | 914 | removeMacMenus_(); |
919 | insertMacMenus_(); | 915 | insertMacMenus_(); |