diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-06 19:03:17 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-06 19:03:17 +0300 |
commit | 9d7469ded746cbfa1e86c1ecd67e4a36f29f635b (patch) | |
tree | ebff33e64e8953ef46ef5de54559fce234c20303 | |
parent | 7c7af3b7425f0496203d68d9bc42be0603048a9b (diff) | |
parent | 186573dae1d0eab4560f02fe3b999418ea0f4614 (diff) |
Merge branch 'dev' into work/v1.7
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | po/eo.po | 12 | ||||
-rw-r--r-- | po/fi.po | 16 | ||||
-rw-r--r-- | po/ie.po | 22 | ||||
-rw-r--r-- | po/sr.po | 58 | ||||
-rw-r--r-- | po/tok.po | 8 | ||||
-rw-r--r-- | res/about/version.gmi | 12 | ||||
-rw-r--r-- | res/arg-help.txt | 1 | ||||
-rw-r--r-- | res/fi.skyjake.Lagrange.appdata.xml | 36 | ||||
-rw-r--r-- | res/lang/fi.bin | bin | 24879 -> 24893 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 24133 -> 24247 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 35978 -> 36801 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 22702 -> 22683 bytes | |||
-rw-r--r-- | src/app.c | 10 | ||||
-rw-r--r-- | src/ipc.c | 2 | ||||
-rw-r--r-- | src/ui/color.c | 9 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 2 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 140 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 3 | ||||
-rw-r--r-- | src/ui/root.c | 9 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 2 |
22 files changed, 286 insertions, 64 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 89bdcdb4..7b43b6de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -18,7 +18,7 @@ | |||
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.6.2 | 21 | VERSION 1.6.3 |
22 | DESCRIPTION "A Beautiful Gemini Client" | 22 | DESCRIPTION "A Beautiful Gemini Client" |
23 | LANGUAGES C | 23 | LANGUAGES C |
24 | ) | 24 | ) |
@@ -49,16 +49,16 @@ The required tools are a C11 compiler (e.g., Clang or GCC), CMake and `pkg-confi | |||
49 | 49 | ||
50 | ### Unicode text rendering | 50 | ### Unicode text rendering |
51 | 51 | ||
52 | Lagrange relies on the [HarfBuzz](https://harfbuzz.github.io) and [GNU FriBidi](https://github.com/fribidi/fribidi/) libraries for handling complex scripts and bidirectional text. This repository includes these two libraries as submodules. By default, HarfBuzz and GNU FriBidi will be compiled as part of the app, without any additional dependencies. This allows the app to be built on systems where these libraries are not readily available. | 52 | Lagrange relies on the [HarfBuzz](https://harfbuzz.github.io) and [GNU FriBidi](https://github.com/fribidi/fribidi/) libraries for handling complex scripts and bidirectional text. This repository includes these two libraries as submodules. By default, if HarfBuzz and GNU FriBidi are not available on the system, they will be compiled as part of the app without any additional dependencies. |
53 | 53 | ||
54 | Note that compiling these libraries has the following requirements: | 54 | Note that compiling these libraries has the following requirements: |
55 | 55 | ||
56 | * HarfBuzz requires a C++ compiler. | 56 | * HarfBuzz requires a C++ compiler. |
57 | * GNU FriBidi cannot be compiled with CMake; you need to have [Meson](https://mesonbuild.com) and [Ninja](https://ninja-build.org). | 57 | * GNU FriBidi cannot be compiled with CMake; you need to have [Meson](https://mesonbuild.com) and [Ninja](https://ninja-build.org). |
58 | 58 | ||
59 | If these requirements cannot be met, or you would prefer the use the system-provided HarfBuzz and GNU FriBidi, the build options can be changed. See the table below for `ENABLE_HARFBUZZ_MINIMAL` and `ENABLE_FRIBIDI_BUILD` (set both to **NO**). Note that a system-provided HarfBuzz likely has dependencies to other libraries, such as FreeType and GLib. | 59 | If these requirements cannot be met, or you would prefer the use the system-provided HarfBuzz and GNU FriBidi, please refer to the list of build options below: `ENABLE_HARFBUZZ_MINIMAL` and `ENABLE_FRIBIDI_BUILD` should both be set to **NO**. Note that a system-provided HarfBuzz likely has dependencies to other libraries, such as FreeType and GLib. |
60 | 60 | ||
61 | You also may disable HarfBuzz and/or GNU FriBidi entirely. The old text renderer that supports only non-complex left-to-right scripts is then used. | 61 | You also may disable HarfBuzz and/or GNU FriBidi entirely. The old text renderer that only supports non-complex left-to-right scripts is then used. |
62 | 62 | ||
63 | ### Installing to a custom directory | 63 | ### Installing to a custom directory |
64 | 64 | ||
@@ -1,7 +1,7 @@ | |||
1 | msgid "" | 1 | msgid "" |
2 | msgstr "" | 2 | msgstr "" |
3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | 3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" |
4 | "PO-Revision-Date: 2021-05-15 06:18+0000\n" | 4 | "PO-Revision-Date: 2021-07-31 12:07+0000\n" |
5 | "Last-Translator: Nikolay Korotkiy <sikmir@gmail.com>\n" | 5 | "Last-Translator: Nikolay Korotkiy <sikmir@gmail.com>\n" |
6 | "Language-Team: Esperanto <http://weblate.skyjake.fi/projects/lagrange/ui/eo/>" | 6 | "Language-Team: Esperanto <http://weblate.skyjake.fi/projects/lagrange/ui/eo/>" |
7 | "\n" | 7 | "\n" |
@@ -169,7 +169,7 @@ msgid "panel.back" | |||
169 | msgstr "Malantaŭen" | 169 | msgstr "Malantaŭen" |
170 | 170 | ||
171 | msgid "hint.findtext" | 171 | msgid "hint.findtext" |
172 | msgstr "Serĉi tekston sur la paĝo" | 172 | msgstr "serĉi tekston sur la paĝo" |
173 | 173 | ||
174 | msgid "sidebar.entry.bookmark" | 174 | msgid "sidebar.entry.bookmark" |
175 | msgstr "Aldoni legosignon…" | 175 | msgstr "Aldoni legosignon…" |
@@ -414,7 +414,7 @@ msgid "heading.prefs" | |||
414 | msgstr "AGORDOJ" | 414 | msgstr "AGORDOJ" |
415 | 415 | ||
416 | msgid "heading.prefs.fonts" | 416 | msgid "heading.prefs.fonts" |
417 | msgstr "TIPAROJ" | 417 | msgstr "Tiparoj" |
418 | 418 | ||
419 | # tab button | 419 | # tab button |
420 | msgid "heading.prefs.network" | 420 | msgid "heading.prefs.network" |
@@ -698,3 +698,9 @@ msgstr "Versio" | |||
698 | 698 | ||
699 | msgid "lang.pl" | 699 | msgid "lang.pl" |
700 | msgstr "Pola" | 700 | msgstr "Pola" |
701 | |||
702 | msgid "close" | ||
703 | msgstr "Fermi" | ||
704 | |||
705 | msgid "menu.openfile" | ||
706 | msgstr "Malfermi dosieron…" | ||
@@ -3,7 +3,7 @@ msgstr "" | |||
3 | "Project-Id-Version: PACKAGE VERSION\n" | 3 | "Project-Id-Version: PACKAGE VERSION\n" |
4 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | 4 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" |
5 | "POT-Creation-Date: 2021-03-23 09:09+0000\n" | 5 | "POT-Creation-Date: 2021-03-23 09:09+0000\n" |
6 | "PO-Revision-Date: 2021-07-24 05:45+0000\n" | 6 | "PO-Revision-Date: 2021-08-03 05:24+0000\n" |
7 | "Last-Translator: Weblate Admin <jaakko.keranen@iki.fi>\n" | 7 | "Last-Translator: Weblate Admin <jaakko.keranen@iki.fi>\n" |
8 | "Language-Team: Finnish <http://weblate.skyjake.fi/projects/lagrange/ui/fi/>\n" | 8 | "Language-Team: Finnish <http://weblate.skyjake.fi/projects/lagrange/ui/fi/>\n" |
9 | "Language: fi\n" | 9 | "Language: fi\n" |
@@ -1766,3 +1766,17 @@ msgstr "" | |||
1766 | 1766 | ||
1767 | msgid "dlg.uploadport.set" | 1767 | msgid "dlg.uploadport.set" |
1768 | msgstr "Aseta portti" | 1768 | msgstr "Aseta portti" |
1769 | |||
1770 | msgid "media.untitled.image" | ||
1771 | msgstr "Kuva" | ||
1772 | |||
1773 | msgid "media.untitled.audio" | ||
1774 | msgstr "Ääni" | ||
1775 | |||
1776 | msgid "feeds.atom.translated" | ||
1777 | msgstr "" | ||
1778 | "Tämä Atom-XML-dokumentti on automaattisesti käännetty Gemini-syötteeksi, " | ||
1779 | "jotta siihen voi tehdä tilauksen." | ||
1780 | |||
1781 | msgid "bookmark.export.title.folder" | ||
1782 | msgstr "Kirjanmerkit" | ||
@@ -1,7 +1,7 @@ | |||
1 | msgid "" | 1 | msgid "" |
2 | msgstr "" | 2 | msgstr "" |
3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | 3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" |
4 | "PO-Revision-Date: 2021-07-25 18:06+0000\n" | 4 | "PO-Revision-Date: 2021-08-03 05:24+0000\n" |
5 | "Last-Translator: Olga Smirnova <mistresssilvara@hotmail.com>\n" | 5 | "Last-Translator: Olga Smirnova <mistresssilvara@hotmail.com>\n" |
6 | "Language-Team: Occidental <http://weblate.skyjake.fi/projects/lagrange/ui/ie/" | 6 | "Language-Team: Occidental <http://weblate.skyjake.fi/projects/lagrange/ui/ie/" |
7 | ">\n" | 7 | ">\n" |
@@ -1772,3 +1772,23 @@ msgstr "Code de carga:" | |||
1772 | 1772 | ||
1773 | msgid "hint.upload.token" | 1773 | msgid "hint.upload.token" |
1774 | msgstr "vi instructiones del servitor" | 1774 | msgstr "vi instructiones del servitor" |
1775 | |||
1776 | msgid "media.untitled.image" | ||
1777 | msgstr "Image" | ||
1778 | |||
1779 | msgid "media.untitled.audio" | ||
1780 | msgstr "Audio" | ||
1781 | |||
1782 | msgid "bookmark.export.title.folder" | ||
1783 | msgstr "Marca-págines" | ||
1784 | |||
1785 | msgid "bookmark.export.title.time" | ||
1786 | msgstr "Creat marca-págines" | ||
1787 | |||
1788 | msgid "bookmark.export.count" | ||
1789 | msgid_plural "bookmark.export.count.n" | ||
1790 | msgstr[0] "Hay %d marca-págine." | ||
1791 | msgstr[1] "Hay %d marca-págines." | ||
1792 | |||
1793 | msgid "bookmark.export.format.linklines" | ||
1794 | msgstr "Chascun ligament representa un marca-págine." | ||
@@ -1,7 +1,7 @@ | |||
1 | msgid "" | 1 | msgid "" |
2 | msgstr "" | 2 | msgstr "" |
3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | 3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" |
4 | "PO-Revision-Date: 2021-07-25 18:06+0000\n" | 4 | "PO-Revision-Date: 2021-08-03 05:24+0000\n" |
5 | "Last-Translator: Страхиња Радић <contact@strahinja.org>\n" | 5 | "Last-Translator: Страхиња Радић <contact@strahinja.org>\n" |
6 | "Language-Team: Serbian <http://weblate.skyjake.fi/projects/lagrange/ui/sr/>\n" | 6 | "Language-Team: Serbian <http://weblate.skyjake.fi/projects/lagrange/ui/sr/>\n" |
7 | "Language: sr\n" | 7 | "Language: sr\n" |
@@ -1806,3 +1806,59 @@ msgstr "" | |||
1806 | 1806 | ||
1807 | msgid "keys.upload" | 1807 | msgid "keys.upload" |
1808 | msgstr "Слање странице преко Титана" | 1808 | msgstr "Слање странице преко Титана" |
1809 | |||
1810 | msgid "media.untitled.image" | ||
1811 | msgstr "Слика" | ||
1812 | |||
1813 | msgid "feeds.atom.translated" | ||
1814 | msgstr "" | ||
1815 | "Овај Atom XML документ је аутоматски преведен у Џемини фид да би се на њега " | ||
1816 | "омогућила претплата." | ||
1817 | |||
1818 | msgid "bookmark.export.count" | ||
1819 | msgid_plural "bookmark.export.count.n" | ||
1820 | msgstr[0] "Имате %d обележивач." | ||
1821 | msgstr[1] "Имате %d обележивача." | ||
1822 | msgstr[2] "Имате %d обележивача." | ||
1823 | |||
1824 | msgid "bookmark.export.taginfo" | ||
1825 | msgstr "" | ||
1826 | "У овој листи сваки наслов представља ознаку обележивача. Приказани су само " | ||
1827 | "означени обележивачи. Обележивачи са више ознака су поновљени под сваком " | ||
1828 | "ознаком." | ||
1829 | |||
1830 | msgid "bookmark.export.format.otherlines" | ||
1831 | msgstr "" | ||
1832 | "Набрајања и цитати су резервисани за додатне информације о претходном " | ||
1833 | "обележивачу. Текстуални редови и преформатирани текст се сматрају " | ||
1834 | "коментарима и биће игнорисани." | ||
1835 | |||
1836 | msgid "media.untitled.audio" | ||
1837 | msgstr "Аудио" | ||
1838 | |||
1839 | msgid "bookmark.export.title.folder" | ||
1840 | msgstr "Обележивачи" | ||
1841 | |||
1842 | msgid "bookmark.export.title.tag" | ||
1843 | msgstr "Ознаке обележивача" | ||
1844 | |||
1845 | msgid "bookmark.export.title.time" | ||
1846 | msgstr "Креирани обележивачи" | ||
1847 | |||
1848 | msgid "bookmark.export.saving" | ||
1849 | msgstr "" | ||
1850 | "Сачувајте ову страницу да бисте их извезли, или их ископирајте на клипборд." | ||
1851 | |||
1852 | msgid "bookmark.export.format.sub" | ||
1853 | msgstr "" | ||
1854 | "Ова страница је форматирана према додатку спецификације „Subscribing to " | ||
1855 | "Gemini pages“." | ||
1856 | |||
1857 | msgid "bookmark.export.format.linklines" | ||
1858 | msgstr "Свака веза представља обележивач." | ||
1859 | |||
1860 | msgid "bookmark.export.format.folders" | ||
1861 | msgstr "Структура фасцикли је дефинисана преко наслова нивоа 2 и 3." | ||
1862 | |||
1863 | msgid "bookmark.export.format.tags" | ||
1864 | msgstr "Ознаке су задате насловима нивоа 2." | ||
@@ -1,7 +1,7 @@ | |||
1 | msgid "" | 1 | msgid "" |
2 | msgstr "" | 2 | msgstr "" |
3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" | 3 | "Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" |
4 | "PO-Revision-Date: 2021-07-28 13:07+0000\n" | 4 | "PO-Revision-Date: 2021-07-31 12:07+0000\n" |
5 | "Last-Translator: jan Anja <cyber@sysrq.in>\n" | 5 | "Last-Translator: jan Anja <cyber@sysrq.in>\n" |
6 | "Language-Team: Toki Pona <http://weblate.skyjake.fi/projects/lagrange/ui/tok/" | 6 | "Language-Team: Toki Pona <http://weblate.skyjake.fi/projects/lagrange/ui/tok/" |
7 | ">\n" | 7 | ">\n" |
@@ -767,7 +767,7 @@ msgid "heading.prefs.colors" | |||
767 | msgstr "kule" | 767 | msgstr "kule" |
768 | 768 | ||
769 | msgid "heading.prefs.fonts" | 769 | msgid "heading.prefs.fonts" |
770 | msgstr "Nasin lukin tawa sitelen" | 770 | msgstr "sitelen" |
771 | 771 | ||
772 | # tab button | 772 | # tab button |
773 | msgid "heading.prefs.interface" | 773 | msgid "heading.prefs.interface" |
@@ -819,7 +819,7 @@ msgstr "SULI MUTE" | |||
819 | 819 | ||
820 | # tab button | 820 | # tab button |
821 | msgid "heading.prefs.style" | 821 | msgid "heading.prefs.style" |
822 | msgstr "sitelen" | 822 | msgstr "lipu" |
823 | 823 | ||
824 | msgid "prefs.theme" | 824 | msgid "prefs.theme" |
825 | msgstr "nasin lukin:" | 825 | msgstr "nasin lukin:" |
@@ -1717,7 +1717,7 @@ msgid "error.certexpired" | |||
1717 | msgstr "sitelen len li tawa weka" | 1717 | msgstr "sitelen len li tawa weka" |
1718 | 1718 | ||
1719 | msgid "error.certexpired.msg" | 1719 | msgid "error.certexpired.msg" |
1720 | msgstr "tenpo pini la sitelen len li tawa weka. tan ni la kon li weka." | 1720 | msgstr "tenpo pini la sitelen len li tawa weka. tan ni la toki li pini." |
1721 | 1721 | ||
1722 | msgid "error.certverify" | 1722 | msgid "error.certverify" |
1723 | msgstr "ilo pana li ike lukin" | 1723 | msgstr "ilo pana li ike lukin" |
diff --git a/res/about/version.gmi b/res/about/version.gmi index b56a9311..72b46e23 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -6,13 +6,23 @@ | |||
6 | ``` | 6 | ``` |
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 1.6.3 | ||
10 | * Select all text in an input field using Shift+Ctrl+A (macOS: ⌘A). | ||
11 | * Input fields do not lose focus when the window becomes inactive, making it easier to resume input afterwards. | ||
12 | * Fixed the line break key modifier inadvertently affecting URL input fields, where line breaks are not allowed. | ||
13 | * Fixed the line break key modifier affecting the upload dialog's text field. | ||
14 | |||
9 | ## 1.6.2 | 15 | ## 1.6.2 |
16 | * Added `--tab-url` to print currently active tab's URL. | ||
10 | * Upload dialog expands to full window height when the entered text is long. | 17 | * Upload dialog expands to full window height when the entered text is long. |
11 | * Home/End keys in an input field move to start/end of the wrapped line segment. | 18 | * Home/End keys in an input field move to start/end of the wrapped line segment. |
19 | * Scroll with mouse wheel in input fields. | ||
20 | * Slower cursor blink rate in input fields. | ||
21 | * Adjusted dark mode colors for focused input fields. | ||
12 | * Fixed incorrect behavior in input fields when typing or deleting text while holding down the Shift key. | 22 | * Fixed incorrect behavior in input fields when typing or deleting text while holding down the Shift key. |
13 | * Fixed crash in Upload dialog if server responds with a redirect. | 23 | * Fixed crash in Upload dialog if server responds with a redirect. |
14 | * Fixed buffered graphics (UI, fonts) getting lost under rare circumstances. | 24 | * Fixed buffered graphics (UI, fonts) getting lost under rare circumstances. |
15 | * Fixed drawing of wrapped text when the app is compiled with HarfBuzz. | 25 | * Fixed drawing of wrapped text when the app is compiled without HarfBuzz. |
16 | * macOS: Fixed UI not updating when system dark mode is toggled while the window is hidden. | 26 | * macOS: Fixed UI not updating when system dark mode is toggled while the window is hidden. |
17 | 27 | ||
18 | ## 1.6.1 | 28 | ## 1.6.1 |
diff --git a/res/arg-help.txt b/res/arg-help.txt index e87881d3..9f6548f2 100644 --- a/res/arg-help.txt +++ b/res/arg-help.txt | |||
@@ -21,3 +21,4 @@ Options that control a running instance of Lagrange: | |||
21 | running, nothing is printed. | 21 | running, nothing is printed. |
22 | --new-tab [URL] Open a new tab. If the URL is omitted, the user's | 22 | --new-tab [URL] Open a new tab. If the URL is omitted, the user's |
23 | homepage is opened. | 23 | homepage is opened. |
24 | --tab-url Print the URL of the active tab. | ||
diff --git a/res/fi.skyjake.Lagrange.appdata.xml b/res/fi.skyjake.Lagrange.appdata.xml index 0110e8c5..1db2717c 100644 --- a/res/fi.skyjake.Lagrange.appdata.xml +++ b/res/fi.skyjake.Lagrange.appdata.xml | |||
@@ -45,6 +45,39 @@ | |||
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.6.2" date="2021-08-03"> | ||
49 | <description> | ||
50 | <p>Version 1.6 adds support for bidirectional text and complex scripts, | ||
51 | right-to-left paragraph layout, uploads using the Titan protocol, | ||
52 | and has an improved mechanism for tracking trust in server | ||
53 | certificates. Page contents can be fully cached in memory for more | ||
54 | efficient backward navigation. There are also UI improvements like | ||
55 | a reorganized Preferences and a setting for smooth scrolling | ||
56 | speed.</p> | ||
57 | <p>Changes and fixes in v1.6.2:</p> | ||
58 | <ul> | ||
59 | <li>Added --tab-url to print currently active tab's URL.</li> | ||
60 | <li>Upload dialog expands to full window height when the entered | ||
61 | text is long.</li> | ||
62 | <li>Home/End keys in an input field move to start/end of the | ||
63 | wrapped line segment.</li> | ||
64 | <li>Scroll with mouse wheel in input fields.</li> | ||
65 | <li>Slower cursor blink rate in input fields.</li> | ||
66 | <li>Adjusted dark mode colors for focused input fields.</li> | ||
67 | <li>Fixed incorrect behavior in input fields when typing or | ||
68 | deleting text while holding down the Shift key.</li> | ||
69 | <li>Fixed crash in Upload dialog if server responds with a | ||
70 | redirect.</li> | ||
71 | <li>Fixed buffered graphics (UI, fonts) getting lost under rare | ||
72 | circumstances.</li> | ||
73 | <li>macOS: Fixed UI not updating when system dark mode is toggled | ||
74 | while the window is hidden.</li> | ||
75 | </ul> | ||
76 | <p>The full release notes can be viewed inside the app by opening | ||
77 | the "about:version" page.</p> | ||
78 | </description> | ||
79 | <url>https://github.com/skyjake/lagrange/releases/tag/v1.6.2</url> | ||
80 | </release> | ||
48 | <release version="1.6.1" date="2021-07-30"> | 81 | <release version="1.6.1" date="2021-07-30"> |
49 | <description> | 82 | <description> |
50 | <p>Version 1.6 adds support for bidirectional text and complex scripts, | 83 | <p>Version 1.6 adds support for bidirectional text and complex scripts, |
@@ -59,7 +92,8 @@ | |||
59 | <li>Potential crash when inserting a line break.</li> | 92 | <li>Potential crash when inserting a line break.</li> |
60 | <li>Cursor positioning in fixed-length input fields.</li> | 93 | <li>Cursor positioning in fixed-length input fields.</li> |
61 | <li>Copying text in input fields (the wrong region was copied).</li> | 94 | <li>Copying text in input fields (the wrong region was copied).</li> |
62 | <li>URL input field contents not being selected after opening a new tab.</li> | 95 | <li>URL input field contents not being selected after opening |
96 | a new tab.</li> | ||
63 | <li>Upload dialog not showing when reloading a Titan URL.</li> | 97 | <li>Upload dialog not showing when reloading a Titan URL.</li> |
64 | </ul> | 98 | </ul> |
65 | <p>The full release notes can be viewed inside the app by opening | 99 | <p>The full release notes can be viewed inside the app by opening |
diff --git a/res/lang/fi.bin b/res/lang/fi.bin index 7b7b6c28..2f1bb18f 100644 --- a/res/lang/fi.bin +++ b/res/lang/fi.bin | |||
Binary files differ | |||
diff --git a/res/lang/ie.bin b/res/lang/ie.bin index 97c02a90..a8bce39d 100644 --- a/res/lang/ie.bin +++ b/res/lang/ie.bin | |||
Binary files differ | |||
diff --git a/res/lang/sr.bin b/res/lang/sr.bin index 4a299323..df8e532b 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 4cd4212d..f2b59891 100644 --- a/res/lang/tok.bin +++ b/res/lang/tok.bin | |||
Binary files differ | |||
@@ -581,6 +581,9 @@ static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance, | |||
581 | else if (equal_CommandLineConstIterator(&i, "close-tab")) { | 581 | else if (equal_CommandLineConstIterator(&i, "close-tab")) { |
582 | appendCStr_String(cmds, "tabs.close\n"); | 582 | appendCStr_String(cmds, "tabs.close\n"); |
583 | } | 583 | } |
584 | else if (equal_CommandLineConstIterator(&i, "tab-url")) { | ||
585 | appendFormat_String(cmds, "ipc.active.url pid:%d\n", pid); | ||
586 | } | ||
584 | else if (equal_CommandLineConstIterator(&i, listTabUrls_CommandLineOption)) { | 587 | else if (equal_CommandLineConstIterator(&i, listTabUrls_CommandLineOption)) { |
585 | appendFormat_String(cmds, "ipc.list.urls pid:%d\n", pid); | 588 | appendFormat_String(cmds, "ipc.list.urls pid:%d\n", pid); |
586 | } | 589 | } |
@@ -658,6 +661,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
658 | defineValues_CommandLine(&d->args, listTabUrls_CommandLineOption, 0); | 661 | defineValues_CommandLine(&d->args, listTabUrls_CommandLineOption, 0); |
659 | defineValues_CommandLine(&d->args, openUrlOrSearch_CommandLineOption, 1); | 662 | defineValues_CommandLine(&d->args, openUrlOrSearch_CommandLineOption, 1); |
660 | defineValuesN_CommandLine(&d->args, "new-tab", 0, 1); | 663 | defineValuesN_CommandLine(&d->args, "new-tab", 0, 1); |
664 | defineValues_CommandLine(&d->args, "tab-url", 0); | ||
661 | defineValues_CommandLine(&d->args, "sw", 0); | 665 | defineValues_CommandLine(&d->args, "sw", 0); |
662 | defineValues_CommandLine(&d->args, "version;V", 0); | 666 | defineValues_CommandLine(&d->args, "version;V", 0); |
663 | } | 667 | } |
@@ -2772,6 +2776,12 @@ iBool handleCommand_App(const char *cmd) { | |||
2772 | } | 2776 | } |
2773 | return iTrue; | 2777 | return iTrue; |
2774 | } | 2778 | } |
2779 | else if (equal_Command(cmd, "ipc.active.url")) { | ||
2780 | write_Ipc(argLabel_Command(cmd, "pid"), | ||
2781 | collectNewFormat_String("%s\n", cstr_String(url_DocumentWidget(document_App()))), | ||
2782 | response_IpcWrite); | ||
2783 | return iTrue; | ||
2784 | } | ||
2775 | else if (equal_Command(cmd, "ipc.signal")) { | 2785 | else if (equal_Command(cmd, "ipc.signal")) { |
2776 | if (argLabel_Command(cmd, "raise")) { | 2786 | if (argLabel_Command(cmd, "raise")) { |
2777 | if (d->window && d->window->win) { | 2787 | if (d->window && d->window->win) { |
@@ -172,6 +172,7 @@ static void handleSignal_IpcResponse_(int sig) { | |||
172 | } | 172 | } |
173 | 173 | ||
174 | iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { | 174 | iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { |
175 | if (!pid) return iFalse; | ||
175 | iBool ok = iFalse; | 176 | iBool ok = iFalse; |
176 | iFile *f = newCStr_File(inputFilePath_(&ipc_, pid)); | 177 | iFile *f = newCStr_File(inputFilePath_(&ipc_, pid)); |
177 | if (open_File(f, text_FileMode | append_FileMode)) { | 178 | if (open_File(f, text_FileMode | append_FileMode)) { |
@@ -281,6 +282,7 @@ void listen_Ipc(void) { | |||
281 | } | 282 | } |
282 | 283 | ||
283 | iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { | 284 | iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { |
285 | if (!pid) return iFalse; | ||
284 | iUnused(type); | 286 | iUnused(type); |
285 | HANDLE slot = CreateFile(slotName_(pid), | 287 | HANDLE slot = CreateFile(slotName_(pid), |
286 | GENERIC_WRITE, | 288 | GENERIC_WRITE, |
diff --git a/src/ui/color.c b/src/ui/color.c index 05ec1f6f..656de6f0 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -85,6 +85,7 @@ void setThemePalette_Color(enum iColorTheme theme) { | |||
85 | const int accentLo = (prefs->accent == cyan_ColorAccent ? teal_ColorId : brown_ColorId); | 85 | const int accentLo = (prefs->accent == cyan_ColorAccent ? teal_ColorId : brown_ColorId); |
86 | const int altAccentHi = (prefs->accent == cyan_ColorAccent ? orange_ColorId : cyan_ColorId); | 86 | const int altAccentHi = (prefs->accent == cyan_ColorAccent ? orange_ColorId : cyan_ColorId); |
87 | const int altAccentLo = (prefs->accent == cyan_ColorAccent ? brown_ColorId : teal_ColorId); | 87 | const int altAccentLo = (prefs->accent == cyan_ColorAccent ? brown_ColorId : teal_ColorId); |
88 | const iColor altAccentMid = mix_Color(get_Color(altAccentHi), get_Color(altAccentLo), 0.5f); | ||
88 | switch (theme) { | 89 | switch (theme) { |
89 | case pureBlack_ColorTheme: { | 90 | case pureBlack_ColorTheme: { |
90 | copy_(uiBackground_ColorId, black_ColorId); | 91 | copy_(uiBackground_ColorId, black_ColorId); |
@@ -123,7 +124,7 @@ void setThemePalette_Color(enum iColorTheme theme) { | |||
123 | copy_(uiInputTextFocused_ColorId, white_ColorId); | 124 | copy_(uiInputTextFocused_ColorId, white_ColorId); |
124 | copy_(uiInputFrame_ColorId, gray25_ColorId); | 125 | copy_(uiInputFrame_ColorId, gray25_ColorId); |
125 | copy_(uiInputFrameHover_ColorId, accentHi); | 126 | copy_(uiInputFrameHover_ColorId, accentHi); |
126 | copy_(uiInputFrameFocused_ColorId, altAccentHi); | 127 | set_Color(uiInputFrameFocused_ColorId, altAccentMid); |
127 | copy_(uiInputCursor_ColorId, altAccentHi); | 128 | copy_(uiInputCursor_ColorId, altAccentHi); |
128 | copy_(uiInputCursorText_ColorId, black_ColorId); | 129 | copy_(uiInputCursorText_ColorId, black_ColorId); |
129 | copy_(uiHeading_ColorId, accentHi); | 130 | copy_(uiHeading_ColorId, accentHi); |
@@ -171,10 +172,12 @@ void setThemePalette_Color(enum iColorTheme theme) { | |||
171 | mix_Color(get_Color(black_ColorId), get_Color(gray25_ColorId), 0.7f)); | 172 | mix_Color(get_Color(black_ColorId), get_Color(gray25_ColorId), 0.7f)); |
172 | copy_(uiInputBackgroundFocused_ColorId, black_ColorId); | 173 | copy_(uiInputBackgroundFocused_ColorId, black_ColorId); |
173 | copy_(uiInputText_ColorId, gray75_ColorId); | 174 | copy_(uiInputText_ColorId, gray75_ColorId); |
174 | copy_(uiInputTextFocused_ColorId, white_ColorId); | 175 | //copy_(uiInputTextFocused_ColorId, white_ColorId); |
176 | set_Color(uiInputTextFocused_ColorId, mix_Color(get_Color(white_ColorId), | ||
177 | get_Color(altAccentHi), 0.15f)); | ||
175 | copy_(uiInputFrame_ColorId, uiInputBackground_ColorId); | 178 | copy_(uiInputFrame_ColorId, uiInputBackground_ColorId); |
176 | copy_(uiInputFrameHover_ColorId, accentHi); | 179 | copy_(uiInputFrameHover_ColorId, accentHi); |
177 | copy_(uiInputFrameFocused_ColorId, altAccentHi); | 180 | set_Color(uiInputFrameFocused_ColorId, altAccentMid); |
178 | copy_(uiInputCursor_ColorId, altAccentHi); | 181 | copy_(uiInputCursor_ColorId, altAccentHi); |
179 | copy_(uiInputCursorText_ColorId, black_ColorId); | 182 | copy_(uiInputCursorText_ColorId, black_ColorId); |
180 | copy_(uiHeading_ColorId, accentHi); | 183 | copy_(uiHeading_ColorId, accentHi); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 8de1162f..674b0352 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -493,7 +493,7 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | |||
493 | const iPrefs * prefs = prefs_App(); | 493 | const iPrefs * prefs = prefs_App(); |
494 | const int minWidth = 50 * gap_UI; /* lines must fit a word at least */ | 494 | const int minWidth = 50 * gap_UI; /* lines must fit a word at least */ |
495 | const float adjust = iClamp((float) bounds.size.x / gap_UI / 11 - 12, | 495 | const float adjust = iClamp((float) bounds.size.x / gap_UI / 11 - 12, |
496 | -2.0f, 10.0f); /* adapt to width */ | 496 | -1.0f, 10.0f); /* adapt to width */ |
497 | //printf("%f\n", adjust); fflush(stdout); | 497 | //printf("%f\n", adjust); fflush(stdout); |
498 | return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2), | 498 | return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2), |
499 | fontSize_UI * prefs->lineWidth * prefs->zoomPercent / 100); | 499 | fontSize_UI * prefs->lineWidth * prefs->zoomPercent / 100); |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 8ed52022..f1f368b6 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -39,7 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
39 | # include "macos.h" | 39 | # include "macos.h" |
40 | #endif | 40 | #endif |
41 | 41 | ||
42 | static const int refreshInterval_InputWidget_ = 256; | 42 | static const int refreshInterval_InputWidget_ = 512; |
43 | static const size_t maxUndo_InputWidget_ = 64; | 43 | static const size_t maxUndo_InputWidget_ = 64; |
44 | static const int unlimitedWidth_InputWidget_ = 1000000; /* TODO: WrapText disables some functionality if maxWidth==0 */ | 44 | static const int unlimitedWidth_InputWidget_ = 1000000; /* TODO: WrapText disables some functionality if maxWidth==0 */ |
45 | 45 | ||
@@ -188,9 +188,9 @@ enum iInputWidgetFlag { | |||
188 | markWords_InputWidgetFlag = iBit(8), | 188 | markWords_InputWidgetFlag = iBit(8), |
189 | needUpdateBuffer_InputWidgetFlag = iBit(9), | 189 | needUpdateBuffer_InputWidgetFlag = iBit(9), |
190 | enterKeyEnabled_InputWidgetFlag = iBit(10), | 190 | enterKeyEnabled_InputWidgetFlag = iBit(10), |
191 | enterKeyInsertsLineFeed_InputWidgetFlag | 191 | lineBreaksEnabled_InputWidgetFlag= iBit(11), |
192 | = iBit(11), | ||
193 | needBackup_InputWidgetFlag = iBit(12), | 192 | needBackup_InputWidgetFlag = iBit(12), |
193 | useReturnKeyBehavior_InputWidgetFlag = iBit(13), | ||
194 | }; | 194 | }; |
195 | 195 | ||
196 | /*----------------------------------------------------------------------------------------------*/ | 196 | /*----------------------------------------------------------------------------------------------*/ |
@@ -216,6 +216,7 @@ struct Impl_InputWidget { | |||
216 | iArray undoStack; | 216 | iArray undoStack; |
217 | int font; | 217 | int font; |
218 | iClick click; | 218 | iClick click; |
219 | int wheelAccum; | ||
219 | int cursorVis; | 220 | int cursorVis; |
220 | uint32_t timer; | 221 | uint32_t timer; |
221 | iTextBuf * buffered; /* pre-rendered static text */ | 222 | iTextBuf * buffered; /* pre-rendered static text */ |
@@ -566,6 +567,28 @@ static void updateAllLinesAndResizeHeight_InputWidget_(iInputWidget *d) { | |||
566 | } | 567 | } |
567 | } | 568 | } |
568 | 569 | ||
570 | static uint32_t cursorTimer_(uint32_t interval, void *w) { | ||
571 | iInputWidget *d = w; | ||
572 | if (d->cursorVis > 1) { | ||
573 | d->cursorVis--; | ||
574 | } | ||
575 | else { | ||
576 | d->cursorVis ^= 1; | ||
577 | } | ||
578 | refresh_Widget(w); | ||
579 | return interval; | ||
580 | } | ||
581 | |||
582 | static void startOrStopCursorTimer_InputWidget_(iInputWidget *d, iBool doStart) { | ||
583 | if (doStart && !d->timer) { | ||
584 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | ||
585 | } | ||
586 | else if (!doStart && d->timer) { | ||
587 | SDL_RemoveTimer(d->timer); | ||
588 | d->timer = 0; | ||
589 | } | ||
590 | } | ||
591 | |||
569 | void init_InputWidget(iInputWidget *d, size_t maxLen) { | 592 | void init_InputWidget(iInputWidget *d, size_t maxLen) { |
570 | iWidget *w = &d->widget; | 593 | iWidget *w = &d->widget; |
571 | init_Widget(w); | 594 | init_Widget(w); |
@@ -587,10 +610,11 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
587 | d->cursor = zero_I2(); | 610 | d->cursor = zero_I2(); |
588 | d->prevCursor = zero_I2(); | 611 | d->prevCursor = zero_I2(); |
589 | d->lastUpdateWidth = 0; | 612 | d->lastUpdateWidth = 0; |
590 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; | 613 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag | |
591 | if (deviceType_App() != desktop_AppDeviceType) { | 614 | lineBreaksEnabled_InputWidgetFlag | useReturnKeyBehavior_InputWidgetFlag; |
592 | d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; | 615 | // if (deviceType_App() != desktop_AppDeviceType) { |
593 | } | 616 | // d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; |
617 | // } | ||
594 | iZap(d->mark); | 618 | iZap(d->mark); |
595 | setMaxLen_InputWidget(d, maxLen); | 619 | setMaxLen_InputWidget(d, maxLen); |
596 | d->visWrapLines.start = 0; | 620 | d->visWrapLines.start = 0; |
@@ -600,6 +624,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
600 | splitToLines_(&iStringLiteral(""), &d->lines); | 624 | splitToLines_(&iStringLiteral(""), &d->lines); |
601 | setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */ | 625 | setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */ |
602 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 626 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
627 | d->wheelAccum = 0; | ||
603 | d->timer = 0; | 628 | d->timer = 0; |
604 | d->cursorVis = 0; | 629 | d->cursorVis = 0; |
605 | d->buffered = NULL; | 630 | d->buffered = NULL; |
@@ -625,9 +650,7 @@ void deinit_InputWidget(iInputWidget *d) { | |||
625 | delete_TextBuf(d->buffered); | 650 | delete_TextBuf(d->buffered); |
626 | clearUndo_InputWidget_(d); | 651 | clearUndo_InputWidget_(d); |
627 | deinit_Array(&d->undoStack); | 652 | deinit_Array(&d->undoStack); |
628 | if (d->timer) { | 653 | startOrStopCursorTimer_InputWidget_(d, iFalse); |
629 | SDL_RemoveTimer(d->timer); | ||
630 | } | ||
631 | deinit_String(&d->srcHint); | 654 | deinit_String(&d->srcHint); |
632 | deinit_String(&d->hint); | 655 | deinit_String(&d->hint); |
633 | deinit_String(&d->oldText); | 656 | deinit_String(&d->oldText); |
@@ -730,14 +753,18 @@ void setValidator_InputWidget(iInputWidget *d, iInputWidgetValidatorFunc validat | |||
730 | d->validatorContext = context; | 753 | d->validatorContext = context; |
731 | } | 754 | } |
732 | 755 | ||
733 | void setEnterInsertsLF_InputWidget(iInputWidget *d, iBool enterInsertsLF) { | 756 | void setLineBreaksEnabled_InputWidget(iInputWidget *d, iBool lineBreaksEnabled) { |
734 | iChangeFlags(d->inFlags, enterKeyInsertsLineFeed_InputWidgetFlag, enterInsertsLF); | 757 | iChangeFlags(d->inFlags, lineBreaksEnabled_InputWidgetFlag, lineBreaksEnabled); |
735 | } | 758 | } |
736 | 759 | ||
737 | void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { | 760 | void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { |
738 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); | 761 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); |
739 | } | 762 | } |
740 | 763 | ||
764 | void setUseReturnKeyBehavior_InputWidget(iInputWidget *d, iBool useReturnKeyBehavior) { | ||
765 | iChangeFlags(d->inFlags, useReturnKeyBehavior_InputWidgetFlag, useReturnKeyBehavior); | ||
766 | } | ||
767 | |||
741 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { | 768 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { |
742 | /* Keep original for retranslations. */ | 769 | /* Keep original for retranslations. */ |
743 | setCStr_String(&d->srcHint, hintText); | 770 | setCStr_String(&d->srcHint, hintText); |
@@ -864,18 +891,6 @@ void setTextCStr_InputWidget(iInputWidget *d, const char *cstr) { | |||
864 | delete_String(str); | 891 | delete_String(str); |
865 | } | 892 | } |
866 | 893 | ||
867 | static uint32_t cursorTimer_(uint32_t interval, void *w) { | ||
868 | iInputWidget *d = w; | ||
869 | if (d->cursorVis > 1) { | ||
870 | d->cursorVis--; | ||
871 | } | ||
872 | else { | ||
873 | d->cursorVis ^= 1; | ||
874 | } | ||
875 | refresh_Widget(w); | ||
876 | return interval; | ||
877 | } | ||
878 | |||
879 | static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { | 894 | static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { |
880 | if (pos.y < 0) { | 895 | if (pos.y < 0) { |
881 | return 0; | 896 | return 0; |
@@ -928,7 +943,7 @@ void begin_InputWidget(iInputWidget *d) { | |||
928 | setFlags_Widget(w, selected_WidgetFlag, iTrue); | 943 | setFlags_Widget(w, selected_WidgetFlag, iTrue); |
929 | showCursor_InputWidget_(d); | 944 | showCursor_InputWidget_(d); |
930 | refresh_Widget(w); | 945 | refresh_Widget(w); |
931 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | 946 | startOrStopCursorTimer_InputWidget_(d, iTrue); |
932 | d->inFlags &= ~enterPressed_InputWidgetFlag; | 947 | d->inFlags &= ~enterPressed_InputWidgetFlag; |
933 | if (d->inFlags & selectAllOnFocus_InputWidgetFlag) { | 948 | if (d->inFlags & selectAllOnFocus_InputWidgetFlag) { |
934 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; | 949 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; |
@@ -953,8 +968,7 @@ void end_InputWidget(iInputWidget *d, iBool accept) { | |||
953 | splitToLines_(&d->oldText, &d->lines); | 968 | splitToLines_(&d->oldText, &d->lines); |
954 | } | 969 | } |
955 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 970 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
956 | SDL_RemoveTimer(d->timer); | 971 | startOrStopCursorTimer_InputWidget_(d, iFalse); |
957 | d->timer = 0; | ||
958 | SDL_StopTextInput(); | 972 | SDL_StopTextInput(); |
959 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); | 973 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); |
960 | const char *id = cstr_String(id_Widget(as_Widget(d))); | 974 | const char *id = cstr_String(id_Widget(as_Widget(d))); |
@@ -1381,6 +1395,20 @@ static iBool isArrowUpDownConsumed_InputWidget_(const iInputWidget *d) { | |||
1381 | return d->maxWrapLines > 1; | 1395 | return d->maxWrapLines > 1; |
1382 | } | 1396 | } |
1383 | 1397 | ||
1398 | static iBool checkLineBreakMods_InputWidget_(const iInputWidget *d, int mods) { | ||
1399 | if (d->inFlags & useReturnKeyBehavior_InputWidgetFlag) { | ||
1400 | return mods == lineBreakKeyMod_ReturnKeyBehavior(prefs_App()->returnKey); | ||
1401 | } | ||
1402 | return mods == 0; | ||
1403 | } | ||
1404 | |||
1405 | static iBool checkAcceptMods_InputWidget_(const iInputWidget *d, int mods) { | ||
1406 | if (d->inFlags & useReturnKeyBehavior_InputWidgetFlag) { | ||
1407 | return mods == acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey); | ||
1408 | } | ||
1409 | return mods == 0; | ||
1410 | } | ||
1411 | |||
1384 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | 1412 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { |
1385 | iWidget *w = as_Widget(d); | 1413 | iWidget *w = as_Widget(d); |
1386 | /* Resize according to width immediately. */ | 1414 | /* Resize according to width immediately. */ |
@@ -1399,6 +1427,13 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1399 | begin_InputWidget(d); | 1427 | begin_InputWidget(d); |
1400 | return iFalse; | 1428 | return iFalse; |
1401 | } | 1429 | } |
1430 | else if (isEditing_InputWidget_(d) && (isCommand_UserEvent(ev, "window.focus.lost") || | ||
1431 | isCommand_UserEvent(ev, "window.focus.gained"))) { | ||
1432 | startOrStopCursorTimer_InputWidget_(d, isCommand_UserEvent(ev, "window.focus.gained")); | ||
1433 | d->cursorVis = 1; | ||
1434 | refresh_Widget(d); | ||
1435 | return iFalse; | ||
1436 | } | ||
1402 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { | 1437 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { |
1403 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 1438 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
1404 | } | 1439 | } |
@@ -1467,6 +1502,31 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1467 | ? SDL_SYSTEM_CURSOR_IBEAM | 1502 | ? SDL_SYSTEM_CURSOR_IBEAM |
1468 | : SDL_SYSTEM_CURSOR_ARROW); | 1503 | : SDL_SYSTEM_CURSOR_ARROW); |
1469 | } | 1504 | } |
1505 | if (ev->type == SDL_MOUSEWHEEL && contains_Widget(w, coord_MouseWheelEvent(&ev->wheel))) { | ||
1506 | const int lineHeight = lineHeight_Text(d->font); | ||
1507 | if (isPerPixel_MouseWheelEvent(&ev->wheel)) { | ||
1508 | d->wheelAccum -= ev->wheel.y; | ||
1509 | } | ||
1510 | else { | ||
1511 | d->wheelAccum -= ev->wheel.y * 3 * lineHeight; | ||
1512 | } | ||
1513 | int lineDelta = d->wheelAccum / lineHeight; | ||
1514 | if (lineDelta < 0) { | ||
1515 | lineDelta = iMax(lineDelta, -d->visWrapLines.start); | ||
1516 | if (!lineDelta) d->wheelAccum = 0; | ||
1517 | } | ||
1518 | else if (lineDelta > 0) { | ||
1519 | lineDelta = iMin(lineDelta, | ||
1520 | lastLine_InputWidget_(d)->wrapLines.end - d->visWrapLines.end); | ||
1521 | if (!lineDelta) d->wheelAccum = 0; | ||
1522 | } | ||
1523 | d->wheelAccum -= lineDelta * lineHeight; | ||
1524 | d->visWrapLines.start += lineDelta; | ||
1525 | d->visWrapLines.end += lineDelta; | ||
1526 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1527 | refresh_Widget(d); | ||
1528 | return iTrue; | ||
1529 | } | ||
1470 | switch (processEvent_Click(&d->click, ev)) { | 1530 | switch (processEvent_Click(&d->click, ev)) { |
1471 | case none_ClickResult: | 1531 | case none_ClickResult: |
1472 | break; | 1532 | break; |
@@ -1497,6 +1557,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1497 | selectAll_InputWidget(d); | 1557 | selectAll_InputWidget(d); |
1498 | } | 1558 | } |
1499 | } | 1559 | } |
1560 | refresh_Widget(d); | ||
1500 | return iTrue; | 1561 | return iTrue; |
1501 | } | 1562 | } |
1502 | case aborted_ClickResult: | 1563 | case aborted_ClickResult: |
@@ -1586,10 +1647,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1586 | return iTrue; | 1647 | return iTrue; |
1587 | case SDLK_RETURN: | 1648 | case SDLK_RETURN: |
1588 | case SDLK_KP_ENTER: | 1649 | case SDLK_KP_ENTER: |
1589 | if (~d->inFlags & isSensitive_InputWidgetFlag && d->maxLen == 0) { | 1650 | if (~d->inFlags & isSensitive_InputWidgetFlag && |
1590 | if (mods == lineBreakKeyMod_ReturnKeyBehavior(prefs_App()->returnKey) || | 1651 | ~d->inFlags & isUrl_InputWidgetFlag && |
1591 | (~d->inFlags & isUrl_InputWidgetFlag && | 1652 | d->inFlags & lineBreaksEnabled_InputWidgetFlag && d->maxLen == 0) { |
1592 | d->inFlags & enterKeyInsertsLineFeed_InputWidgetFlag)) { | 1653 | if (checkLineBreakMods_InputWidget_(d, mods)) { |
1593 | pushUndo_InputWidget_(d); | 1654 | pushUndo_InputWidget_(d); |
1594 | deleteMarked_InputWidget_(d); | 1655 | deleteMarked_InputWidget_(d); |
1595 | insertChar_InputWidget_(d, '\n'); | 1656 | insertChar_InputWidget_(d, '\n'); |
@@ -1598,7 +1659,8 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1598 | } | 1659 | } |
1599 | } | 1660 | } |
1600 | if (d->inFlags & enterKeyEnabled_InputWidgetFlag && | 1661 | if (d->inFlags & enterKeyEnabled_InputWidgetFlag && |
1601 | mods == acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey)) { | 1662 | (checkAcceptMods_InputWidget_(d, mods) || |
1663 | (~d->inFlags & lineBreaksEnabled_InputWidgetFlag))) { | ||
1602 | d->inFlags |= enterPressed_InputWidgetFlag; | 1664 | d->inFlags |= enterPressed_InputWidgetFlag; |
1603 | setFocus_Widget(NULL); | 1665 | setFocus_Widget(NULL); |
1604 | return iTrue; | 1666 | return iTrue; |
@@ -1700,6 +1762,9 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1700 | case SDLK_a: | 1762 | case SDLK_a: |
1701 | #if defined (iPlatformApple) | 1763 | #if defined (iPlatformApple) |
1702 | if (mods == KMOD_PRIMARY) { | 1764 | if (mods == KMOD_PRIMARY) { |
1765 | #else | ||
1766 | if (mods == (KMOD_PRIMARY | KMOD_SHIFT)) { | ||
1767 | #endif | ||
1703 | selectAll_InputWidget(d); | 1768 | selectAll_InputWidget(d); |
1704 | d->mark.start = 0; | 1769 | d->mark.start = 0; |
1705 | d->mark.end = cursorToIndex_InputWidget_(d, curMax); | 1770 | d->mark.end = cursorToIndex_InputWidget_(d, curMax); |
@@ -1708,7 +1773,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1708 | refresh_Widget(w); | 1773 | refresh_Widget(w); |
1709 | return iTrue; | 1774 | return iTrue; |
1710 | } | 1775 | } |
1711 | #endif | ||
1712 | /* fall through for Emacs-style Home/End */ | 1776 | /* fall through for Emacs-style Home/End */ |
1713 | case SDLK_e: | 1777 | case SDLK_e: |
1714 | if (mods == KMOD_CTRL || mods == (KMOD_CTRL | KMOD_SHIFT)) { | 1778 | if (mods == KMOD_CTRL || mods == (KMOD_CTRL | KMOD_SHIFT)) { |
@@ -1889,12 +1953,11 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1889 | wrapText.wrapFunc = NULL; | 1953 | wrapText.wrapFunc = NULL; |
1890 | wrapText.context = NULL; | 1954 | wrapText.context = NULL; |
1891 | } | 1955 | } |
1892 | unsetClip_Paint(&p); | ||
1893 | /* Draw the insertion point. */ | 1956 | /* Draw the insertion point. */ |
1894 | if (isFocused && d->cursorVis) { | 1957 | if (isFocused && d->cursorVis && contains_Range(&visLines, d->cursor.y)) { |
1895 | iInt2 curSize; | 1958 | iInt2 curSize; |
1896 | iRangecc cursorChar = iNullRange; | 1959 | iRangecc cursorChar = iNullRange; |
1897 | int visWrapsAbove = 0; | 1960 | int visWrapsAbove = 0; |
1898 | for (int i = d->cursor.y - 1; i >= visLines.start; i--) { | 1961 | for (int i = d->cursor.y - 1; i >= visLines.start; i--) { |
1899 | const iInputLine *line = constAt_Array(&d->lines, i); | 1962 | const iInputLine *line = constAt_Array(&d->lines, i); |
1900 | visWrapsAbove += numWrapLines_InputLine_(line); | 1963 | visWrapsAbove += numWrapLines_InputLine_(line); |
@@ -1938,6 +2001,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1938 | cursorChar); | 2001 | cursorChar); |
1939 | } | 2002 | } |
1940 | } | 2003 | } |
2004 | unsetClip_Paint(&p); | ||
1941 | drawChildren_Widget(w); | 2005 | drawChildren_Widget(w); |
1942 | } | 2006 | } |
1943 | 2007 | ||
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index a94291ed..0d327ca6 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -50,8 +50,9 @@ void setFont_InputWidget (iInputWidget *, int fontId); | |||
50 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ | 50 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ |
51 | void setLineLimits_InputWidget (iInputWidget *, int minLines, int maxLines); | 51 | void setLineLimits_InputWidget (iInputWidget *, int minLines, int maxLines); |
52 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); | 52 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); |
53 | void setEnterInsertsLF_InputWidget (iInputWidget *, iBool enterInsertsLF); | 53 | void setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled); |
54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); | 54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); |
55 | void setUseReturnKeyBehavior_InputWidget(iInputWidget *, iBool useReturnKeyBehavior); | ||
55 | void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); | 56 | void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); |
56 | void begin_InputWidget (iInputWidget *); | 57 | void begin_InputWidget (iInputWidget *); |
57 | void end_InputWidget (iInputWidget *, iBool accept); | 58 | void end_InputWidget (iInputWidget *, iBool accept); |
diff --git a/src/ui/root.c b/src/ui/root.c index 9d92c44e..a8b9f998 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -358,9 +358,9 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
358 | return iTrue; | 358 | return iTrue; |
359 | } | 359 | } |
360 | else if (equal_Command(cmd, "window.focus.lost")) { | 360 | else if (equal_Command(cmd, "window.focus.lost")) { |
361 | #if !defined (iPlatformMobile) /* apps don't share input focus on mobile */ | 361 | //#if !defined (iPlatformMobile) /* apps don't share input focus on mobile */ |
362 | setFocus_Widget(NULL); | 362 | // setFocus_Widget(NULL); |
363 | #endif | 363 | //#endif |
364 | setTextColor_LabelWidget(findWidget_App("winbar.app"), uiAnnotation_ColorId); | 364 | setTextColor_LabelWidget(findWidget_App("winbar.app"), uiAnnotation_ColorId); |
365 | setTextColor_LabelWidget(findWidget_App("winbar.title"), uiAnnotation_ColorId); | 365 | setTextColor_LabelWidget(findWidget_App("winbar.title"), uiAnnotation_ColorId); |
366 | return iFalse; | 366 | return iFalse; |
@@ -1077,6 +1077,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1077 | setSelectAllOnFocus_InputWidget(url, iTrue); | 1077 | setSelectAllOnFocus_InputWidget(url, iTrue); |
1078 | setId_Widget(as_Widget(url), "url"); | 1078 | setId_Widget(as_Widget(url), "url"); |
1079 | setLineLimits_InputWidget(url, 1, 1); /* just one line while not focused */ | 1079 | setLineLimits_InputWidget(url, 1, 1); /* just one line while not focused */ |
1080 | setLineBreaksEnabled_InputWidget(url, iFalse); | ||
1080 | setUrlContent_InputWidget(url, iTrue); | 1081 | setUrlContent_InputWidget(url, iTrue); |
1081 | setNotifyEdits_InputWidget(url, iTrue); | 1082 | setNotifyEdits_InputWidget(url, iTrue); |
1082 | setTextCStr_InputWidget(url, "gemini://"); | 1083 | setTextCStr_InputWidget(url, "gemini://"); |
@@ -1272,7 +1273,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1272 | setHint_InputWidget(input, "${hint.findtext}"); | 1273 | setHint_InputWidget(input, "${hint.findtext}"); |
1273 | setSelectAllOnFocus_InputWidget(input, iTrue); | 1274 | setSelectAllOnFocus_InputWidget(input, iTrue); |
1274 | setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ | 1275 | setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ |
1275 | setEnterInsertsLF_InputWidget(input, iFalse); | 1276 | setLineBreaksEnabled_InputWidget(input, iFalse); |
1276 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), | 1277 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), |
1277 | "find.input"); | 1278 | "find.input"); |
1278 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); | 1279 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 7bfa73bd..57b6b6b7 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -116,8 +116,8 @@ void init_UploadWidget(iUploadWidget *d) { | |||
116 | setId_Widget(as_Widget(d->input), "upload.text"); | 116 | setId_Widget(as_Widget(d->input), "upload.text"); |
117 | setFont_InputWidget(d->input, monospace_FontId); | 117 | setFont_InputWidget(d->input, monospace_FontId); |
118 | setLineLimits_InputWidget(d->input, 7, 20); | 118 | setLineLimits_InputWidget(d->input, 7, 20); |
119 | setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */ | ||
119 | setHint_InputWidget(d->input, "${hint.upload.text}"); | 120 | setHint_InputWidget(d->input, "${hint.upload.text}"); |
120 | setEnterInsertsLF_InputWidget(d->input, iTrue); | ||
121 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); | 121 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); |
122 | addChild_Widget(page, iClob(d->input)); | 122 | addChild_Widget(page, iClob(d->input)); |
123 | appendFramelessTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0); | 123 | appendFramelessTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0); |