summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c95
-rw-r--r--src/app.h3
-rw-r--r--src/gmdocument.c19
-rw-r--r--src/gmdocument.h5
-rw-r--r--src/prefs.c23
-rw-r--r--src/prefs.h29
-rw-r--r--src/ui/documentwidget.c7
-rw-r--r--src/ui/util.c16
8 files changed, 135 insertions, 62 deletions
diff --git a/src/app.c b/src/app.c
index c0c0c47e..9c1ec7a4 100644
--- a/src/app.c
+++ b/src/app.c
@@ -94,17 +94,20 @@ struct Impl_App {
94 iTime lastDropTime; /* for detecting drops of multiple items */ 94 iTime lastDropTime; /* for detecting drops of multiple items */
95 /* Preferences: */ 95 /* Preferences: */
96 iBool commandEcho; /* --echo */ 96 iBool commandEcho; /* --echo */
97 iBool retainWindowSize; 97 iBool forceSoftwareRender; /* --sw */
98 iRect initialWindowRect; 98 iRect initialWindowRect;
99 iPrefs prefs;
100#if 0
101 iBool retainWindowSize;
99 float uiScale; 102 float uiScale;
100 int zoomPercent; 103 int zoomPercent;
101 iBool forceWrap; 104 iBool forceWrap;
102 iBool forceSoftwareRender;
103 enum iColorTheme theme; 105 enum iColorTheme theme;
104 iBool useSystemTheme; 106 iBool useSystemTheme;
105 iString gopherProxy; 107 iString gopherProxy;
106 iString httpProxy; 108 iString httpProxy;
107 iString downloadDir; 109 iString downloadDir;
110#endif
108}; 111};
109 112
110static iApp app_; 113static iApp app_;
@@ -134,8 +137,8 @@ const iString *dateStr_(const iDate *date) {
134static iString *serializePrefs_App_(const iApp *d) { 137static iString *serializePrefs_App_(const iApp *d) {
135 iString *str = new_String(); 138 iString *str = new_String();
136 const iSidebarWidget *sidebar = findWidget_App("sidebar"); 139 const iSidebarWidget *sidebar = findWidget_App("sidebar");
137 appendFormat_String(str, "window.retain arg:%d\n", d->retainWindowSize); 140 appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize);
138 if (d->retainWindowSize) { 141 if (d->prefs.retainWindowSize) {
139 int w, h, x, y; 142 int w, h, x, y;
140 SDL_GetWindowSize(d->window->win, &w, &h); 143 SDL_GetWindowSize(d->window->win, &w, &h);
141 SDL_GetWindowPosition(d->window->win, &x, &y); 144 SDL_GetWindowPosition(d->window->win, &x, &y);
@@ -153,17 +156,17 @@ static iString *serializePrefs_App_(const iApp *d) {
153 if (isVisible_Widget(sidebar)) { 156 if (isVisible_Widget(sidebar)) {
154 appendCStr_String(str, "sidebar.toggle\n"); 157 appendCStr_String(str, "sidebar.toggle\n");
155 } 158 }
156 if (d->forceWrap) { 159 if (d->prefs.forceLineWrap) {
157 appendFormat_String(str, "forcewrap.toggle\n"); 160 appendFormat_String(str, "forcewrap.toggle\n");
158 } 161 }
159 appendFormat_String(str, "sidebar.mode arg:%d\n", mode_SidebarWidget(sidebar)); 162 appendFormat_String(str, "sidebar.mode arg:%d\n", mode_SidebarWidget(sidebar));
160 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 163 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
161 appendFormat_String(str, "zoom.set arg:%d\n", d->zoomPercent); 164 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
162 appendFormat_String(str, "theme.set arg:%d auto:1\n", d->theme); 165 appendFormat_String(str, "theme.set arg:%d auto:1\n", d->prefs.theme);
163 appendFormat_String(str, "ostheme arg:%d\n", d->useSystemTheme); 166 appendFormat_String(str, "ostheme arg:%d\n", d->prefs.useSystemTheme);
164 appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->gopherProxy)); 167 appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy));
165 appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->httpProxy)); 168 appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy));
166 appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->downloadDir)); 169 appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->prefs.downloadDir));
167 return str; 170 return str;
168} 171}
169 172
@@ -299,24 +302,18 @@ static void init_App_(iApp *d, int argc, char **argv) {
299 d->lastTickerTime = SDL_GetTicks(); 302 d->lastTickerTime = SDL_GetTicks();
300 d->elapsedSinceLastTicker = 0; 303 d->elapsedSinceLastTicker = 0;
301 d->commandEcho = checkArgument_CommandLine(&d->args, "echo") != NULL; 304 d->commandEcho = checkArgument_CommandLine(&d->args, "echo") != NULL;
305 d->forceSoftwareRender = checkArgument_CommandLine(&d->args, "sw") != NULL;
302 d->initialWindowRect = init_Rect(-1, -1, 900, 560); 306 d->initialWindowRect = init_Rect(-1, -1, 900, 560);
303 d->theme = dark_ColorTheme; 307 init_Prefs(&d->prefs);
304 d->useSystemTheme = iTrue; 308 setCStr_String(&d->prefs.downloadDir, downloadDir_App_);
305 d->running = iFalse; 309 d->running = iFalse;
306 d->window = NULL; 310 d->window = NULL;
307 d->retainWindowSize = iTrue;
308 d->pendingRefresh = iFalse; 311 d->pendingRefresh = iFalse;
309 d->zoomPercent = 100;
310 d->forceWrap = iFalse;
311 d->forceSoftwareRender = checkArgument_CommandLine(&d->args, "sw") != NULL;
312 d->certs = new_GmCerts(dataDir_App_); 312 d->certs = new_GmCerts(dataDir_App_);
313 d->visited = new_Visited(); 313 d->visited = new_Visited();
314 d->bookmarks = new_Bookmarks(); 314 d->bookmarks = new_Bookmarks();
315 d->tabEnum = 0; /* generates unique IDs for tab pages */ 315 d->tabEnum = 0; /* generates unique IDs for tab pages */
316 init_String(&d->gopherProxy); 316 setThemePalette_Color(d->prefs.theme);
317 init_String(&d->httpProxy);
318 initCStr_String(&d->downloadDir, downloadDir_App_);
319 setThemePalette_Color(d->theme);
320#if defined (iPlatformApple) 317#if defined (iPlatformApple)
321 setupApplication_MacOS(); 318 setupApplication_MacOS();
322#endif 319#endif
@@ -390,9 +387,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
390static void deinit_App(iApp *d) { 387static void deinit_App(iApp *d) {
391 saveState_App_(d); 388 saveState_App_(d);
392 savePrefs_App_(d); 389 savePrefs_App_(d);
393 deinit_String(&d->downloadDir); 390 deinit_Prefs(&d->prefs);
394 deinit_String(&d->httpProxy);
395 deinit_String(&d->gopherProxy);
396 save_Bookmarks(d->bookmarks, dataDir_App_); 391 save_Bookmarks(d->bookmarks, dataDir_App_);
397 delete_Bookmarks(d->bookmarks); 392 delete_Bookmarks(d->bookmarks);
398 save_Visited(d->visited, dataDir_App_); 393 save_Visited(d->visited, dataDir_App_);
@@ -414,7 +409,7 @@ const iString *dataDir_App(void) {
414} 409}
415 410
416const iString *downloadDir_App(void) { 411const iString *downloadDir_App(void) {
417 return collect_String(cleaned_Path(&app_.downloadDir)); 412 return collect_String(cleaned_Path(&app_.prefs.downloadDir));
418} 413}
419 414
420const iString *debugInfo_App(void) { 415const iString *debugInfo_App(void) {
@@ -538,12 +533,12 @@ uint32_t elapsedSinceLastTicker_App(void) {
538 return app_.elapsedSinceLastTicker; 533 return app_.elapsedSinceLastTicker;
539} 534}
540 535
541int zoom_App(void) { 536const iPrefs *prefs_App(void) {
542 return app_.zoomPercent; 537 return &app_.prefs;
543} 538}
544 539
545iBool forceLineWrap_App(void) { 540iBool forceLineWrap_App(void) {
546 return app_.forceWrap; 541 return app_.prefs.forceLineWrap;
547} 542}
548 543
549iBool forceSoftwareRender_App(void) { 544iBool forceSoftwareRender_App(void) {
@@ -559,16 +554,16 @@ iBool forceSoftwareRender_App(void) {
559} 554}
560 555
561enum iColorTheme colorTheme_App(void) { 556enum iColorTheme colorTheme_App(void) {
562 return app_.theme; 557 return app_.prefs.theme;
563} 558}
564 559
565const iString *schemeProxy_App(iRangecc scheme) { 560const iString *schemeProxy_App(iRangecc scheme) {
566 iApp *d = &app_; 561 iApp *d = &app_;
567 if (equalCase_Rangecc(scheme, "gopher")) { 562 if (equalCase_Rangecc(scheme, "gopher")) {
568 return &d->gopherProxy; 563 return &d->prefs.gopherProxy;
569 } 564 }
570 if (equalCase_Rangecc(scheme, "http") || equalCase_Rangecc(scheme, "https")) { 565 if (equalCase_Rangecc(scheme, "http") || equalCase_Rangecc(scheme, "https")) {
571 return &d->httpProxy; 566 return &d->prefs.httpProxy;
572 } 567 }
573 return NULL; 568 return NULL;
574} 569}
@@ -801,11 +796,11 @@ static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) {
801iBool handleCommand_App(const char *cmd) { 796iBool handleCommand_App(const char *cmd) {
802 iApp *d = &app_; 797 iApp *d = &app_;
803 if (equal_Command(cmd, "window.retain")) { 798 if (equal_Command(cmd, "window.retain")) {
804 d->retainWindowSize = arg_Command(cmd); 799 d->prefs.retainWindowSize = arg_Command(cmd);
805 return iTrue; 800 return iTrue;
806 } 801 }
807 else if (equal_Command(cmd, "downloads")) { 802 else if (equal_Command(cmd, "downloads")) {
808 setCStr_String(&d->downloadDir, suffixPtr_Command(cmd, "path")); 803 setCStr_String(&d->prefs.downloadDir, suffixPtr_Command(cmd, "path"));
809 return iTrue; 804 return iTrue;
810 } 805 }
811 else if (equal_Command(cmd, "open")) { 806 else if (equal_Command(cmd, "open")) {
@@ -813,8 +808,8 @@ iBool handleCommand_App(const char *cmd) {
813 iUrl parts; 808 iUrl parts;
814 init_Url(&parts, url); 809 init_Url(&parts, url);
815 if (equalCase_Rangecc(parts.scheme, "mailto") || 810 if (equalCase_Rangecc(parts.scheme, "mailto") ||
816 (isEmpty_String(&d->httpProxy) && (equalCase_Rangecc(parts.scheme, "http") || 811 (isEmpty_String(&d->prefs.httpProxy) && (equalCase_Rangecc(parts.scheme, "http") ||
817 equalCase_Rangecc(parts.scheme, "https")))) { 812 equalCase_Rangecc(parts.scheme, "https")))) {
818 openInDefaultBrowser_App(url); 813 openInDefaultBrowser_App(url);
819 return iTrue; 814 return iTrue;
820 } 815 }
@@ -905,9 +900,9 @@ iBool handleCommand_App(const char *cmd) {
905 else if (equal_Command(cmd, "preferences")) { 900 else if (equal_Command(cmd, "preferences")) {
906 iWidget *dlg = makePreferences_Widget(); 901 iWidget *dlg = makePreferences_Widget();
907 updatePrefsThemeButtons_(dlg); 902 updatePrefsThemeButtons_(dlg);
908 setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->downloadDir); 903 setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->prefs.downloadDir);
909 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->useSystemTheme); 904 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
910 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->retainWindowSize); 905 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
911 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 906 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
912 collectNewFormat_String("%g", uiScale_Window(d->window))); 907 collectNewFormat_String("%g", uiScale_Window(d->window)));
913 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), 908 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"),
@@ -943,8 +938,8 @@ iBool handleCommand_App(const char *cmd) {
943 } 938 }
944 else if (equal_Command(cmd, "zoom.set")) { 939 else if (equal_Command(cmd, "zoom.set")) {
945 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */ 940 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */
946 d->zoomPercent = arg_Command(cmd); 941 d->prefs.zoomPercent = arg_Command(cmd);
947 setContentFontSize_Text((float) d->zoomPercent / 100.0f); 942 setContentFontSize_Text((float) d->prefs.zoomPercent / 100.0f);
948 postCommand_App("font.changed"); 943 postCommand_App("font.changed");
949 postCommand_App("window.unfreeze"); 944 postCommand_App("window.unfreeze");
950 return iTrue; 945 return iTrue;
@@ -952,17 +947,17 @@ iBool handleCommand_App(const char *cmd) {
952 else if (equal_Command(cmd, "zoom.delta")) { 947 else if (equal_Command(cmd, "zoom.delta")) {
953 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */ 948 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */
954 int delta = arg_Command(cmd); 949 int delta = arg_Command(cmd);
955 if (d->zoomPercent < 100 || (delta < 0 && d->zoomPercent == 100)) { 950 if (d->prefs.zoomPercent < 100 || (delta < 0 && d->prefs.zoomPercent == 100)) {
956 delta /= 2; 951 delta /= 2;
957 } 952 }
958 d->zoomPercent = iClamp(d->zoomPercent + delta, 50, 200); 953 d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200);
959 setContentFontSize_Text((float) d->zoomPercent / 100.0f); 954 setContentFontSize_Text((float) d->prefs.zoomPercent / 100.0f);
960 postCommand_App("font.changed"); 955 postCommand_App("font.changed");
961 postCommand_App("window.unfreeze"); 956 postCommand_App("window.unfreeze");
962 return iTrue; 957 return iTrue;
963 } 958 }
964 else if (equal_Command(cmd, "forcewrap.toggle")) { 959 else if (equal_Command(cmd, "forcewrap.toggle")) {
965 d->forceWrap = !d->forceWrap; 960 d->prefs.forceLineWrap = !d->prefs.forceLineWrap;
966 updateSize_DocumentWidget(document_App()); 961 updateSize_DocumentWidget(document_App());
967 return iTrue; 962 return iTrue;
968 } 963 }
@@ -1002,20 +997,20 @@ iBool handleCommand_App(const char *cmd) {
1002 } 997 }
1003 else if (equal_Command(cmd, "theme.set")) { 998 else if (equal_Command(cmd, "theme.set")) {
1004 const int isAuto = argLabel_Command(cmd, "auto"); 999 const int isAuto = argLabel_Command(cmd, "auto");
1005 d->theme = arg_Command(cmd); 1000 d->prefs.theme = arg_Command(cmd);
1006 if (!isAuto) { 1001 if (!isAuto) {
1007 postCommand_App("ostheme arg:0"); 1002 postCommand_App("ostheme arg:0");
1008 } 1003 }
1009 setThemePalette_Color(d->theme); 1004 setThemePalette_Color(d->prefs.theme);
1010 postCommandf_App("theme.changed auto:%d", isAuto); 1005 postCommandf_App("theme.changed auto:%d", isAuto);
1011 return iTrue; 1006 return iTrue;
1012 } 1007 }
1013 else if (equal_Command(cmd, "ostheme")) { 1008 else if (equal_Command(cmd, "ostheme")) {
1014 d->useSystemTheme = arg_Command(cmd); 1009 d->prefs.useSystemTheme = arg_Command(cmd);
1015 return iTrue; 1010 return iTrue;
1016 } 1011 }
1017 else if (equal_Command(cmd, "os.theme.changed")) { 1012 else if (equal_Command(cmd, "os.theme.changed")) {
1018 if (d->useSystemTheme) { 1013 if (d->prefs.useSystemTheme) {
1019 const int dark = argLabel_Command(cmd, "dark"); 1014 const int dark = argLabel_Command(cmd, "dark");
1020 const int contrast = argLabel_Command(cmd, "contrast"); 1015 const int contrast = argLabel_Command(cmd, "contrast");
1021 postCommandf_App("theme.set arg:%d auto:1", 1016 postCommandf_App("theme.set arg:%d auto:1",
@@ -1025,11 +1020,11 @@ iBool handleCommand_App(const char *cmd) {
1025 return iFalse; 1020 return iFalse;
1026 } 1021 }
1027 else if (equal_Command(cmd, "proxy.gopher")) { 1022 else if (equal_Command(cmd, "proxy.gopher")) {
1028 setCStr_String(&d->gopherProxy, suffixPtr_Command(cmd, "address")); 1023 setCStr_String(&d->prefs.gopherProxy, suffixPtr_Command(cmd, "address"));
1029 return iTrue; 1024 return iTrue;
1030 } 1025 }
1031 else if (equal_Command(cmd, "proxy.http")) { 1026 else if (equal_Command(cmd, "proxy.http")) {
1032 setCStr_String(&d->httpProxy, suffixPtr_Command(cmd, "address")); 1027 setCStr_String(&d->prefs.httpProxy, suffixPtr_Command(cmd, "address"));
1033 return iTrue; 1028 return iTrue;
1034 } 1029 }
1035 else { 1030 else {
diff --git a/src/app.h b/src/app.h
index 75d9b48c..db22230e 100644
--- a/src/app.h
+++ b/src/app.h
@@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
28#include <the_Foundation/string.h> 28#include <the_Foundation/string.h>
29#include <the_Foundation/time.h> 29#include <the_Foundation/time.h>
30 30
31#include "prefs.h"
31#include "ui/color.h" 32#include "ui/color.h"
32 33
33iDeclareType(Bookmarks) 34iDeclareType(Bookmarks)
@@ -58,7 +59,7 @@ void refresh_App (void);
58iBool isRefreshPending_App (void); 59iBool isRefreshPending_App (void);
59uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */ 60uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */
60 61
61int zoom_App (void); 62const iPrefs * prefs_App (void);
62iBool forceLineWrap_App (void); 63iBool forceLineWrap_App (void);
63iBool forceSoftwareRender_App(void); 64iBool forceSoftwareRender_App(void);
64enum iColorTheme colorTheme_App (void); 65enum iColorTheme colorTheme_App (void);
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 39c45c80..0129db64 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -331,6 +331,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
331 static const char *globe = "\U0001f310"; 331 static const char *globe = "\U0001f310";
332 static const char *quote = "\u201c"; 332 static const char *quote = "\u201c";
333 const float midRunSkip = 0; /*0.120f;*/ /* extra space between wrapped text/quote lines */ 333 const float midRunSkip = 0; /*0.120f;*/ /* extra space between wrapped text/quote lines */
334 const iPrefs *prefs = prefs_App();
334 clear_Array(&d->layout); 335 clear_Array(&d->layout);
335 clearLinks_GmDocument_(d); 336 clearLinks_GmDocument_(d);
336 clear_Array(&d->headings); 337 clear_Array(&d->headings);
@@ -341,7 +342,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
341 const iRangecc content = range_String(&d->source); 342 const iRangecc content = range_String(&d->source);
342 iRangecc contentLine = iNullRange; 343 iRangecc contentLine = iNullRange;
343 iInt2 pos = zero_I2(); 344 iInt2 pos = zero_I2();
344 iBool isFirstText = isGemini; 345 iBool isFirstText = isGemini && prefs->bigFirstParagraph;
345 iBool addQuoteIcon = iTrue; 346 iBool addQuoteIcon = iTrue;
346 iBool isPreformat = iFalse; 347 iBool isPreformat = iFalse;
347 iRangecc preAltText = iNullRange; 348 iRangecc preAltText = iNullRange;
@@ -650,9 +651,11 @@ void reset_GmDocument(iGmDocument *d) {
650} 651}
651 652
652void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { 653void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
653 const float saturationLevel = 1.0f; /* TODO: user setting */ 654 const iPrefs * prefs = prefs_App();
654 const iBool isLightMode = isLight_ColorTheme(colorTheme_App()); 655 enum iGmDocumentTheme theme =
655 const iBool isDarkMode = !isLightMode; 656 (isDark_ColorTheme(colorTheme_App()) ? prefs->docThemeDark : prefs->docThemeLight);
657// const iBool isLightMode = isLight_ColorTheme(colorTheme_App());
658// const iBool isDarkMode = !isLightMode;
656 static const iChar siteIcons[] = { 659 static const iChar siteIcons[] = {
657 0x203b, 0x2042, 0x205c, 0x2182, 0x25ed, 0x2600, 0x2601, 0x2604, 0x2605, 0x2606, 660 0x203b, 0x2042, 0x205c, 0x2182, 0x25ed, 0x2600, 0x2601, 0x2604, 0x2605, 0x2606,
658 0x265c, 0x265e, 0x2690, 0x2691, 0x2693, 0x2698, 0x2699, 0x26f0, 0x270e, 0x2728, 661 0x265c, 0x265e, 0x2690, 0x2691, 0x2693, 0x2698, 0x2699, 0x26f0, 0x270e, 0x2728,
@@ -662,7 +665,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
662 0x1f533, 0x1f657, 0x1f659, 0x1f665, 0x1f668, 0x1f66b, 0x1f78b, 0x1f796, 0x1f79c, 665 0x1f533, 0x1f657, 0x1f659, 0x1f665, 0x1f668, 0x1f66b, 0x1f78b, 0x1f796, 0x1f79c,
663 }; 666 };
664 /* Default colors. */ { 667 /* Default colors. */ {
665 if (isDarkMode) { 668 if (theme == colorfulDark_GmDocumentTheme) {
666 const iHSLColor base = { 200, 0, 0.15f, 1.0f }; 669 const iHSLColor base = { 200, 0, 0.15f, 1.0f };
667 setHsl_Color(tmBackground_ColorId, base); 670 setHsl_Color(tmBackground_ColorId, base);
668 set_Color(tmParagraph_ColorId, get_Color(gray75_ColorId)); 671 set_Color(tmParagraph_ColorId, get_Color(gray75_ColorId));
@@ -784,7 +787,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
784 // printf("background: %d %f %f\n", (int) base.hue, base.sat, base.lum); 787 // printf("background: %d %f %f\n", (int) base.hue, base.sat, base.lum);
785 // printf("isDarkBgSat: %d\n", isDarkBgSat); 788 // printf("isDarkBgSat: %d\n", isDarkBgSat);
786 789
787 if (isDarkMode) { 790 if (theme == colorfulDark_GmDocumentTheme) {
788 iHSLColor base = { hues[primIndex], 791 iHSLColor base = { hues[primIndex],
789 0.8f * (d->themeSeed >> 24) / 255.0f, 792 0.8f * (d->themeSeed >> 24) / 255.0f,
790 0.06f + 0.09f * ((d->themeSeed >> 5) & 0x7) / 7.0f, 793 0.06f + 0.09f * ((d->themeSeed >> 5) & 0x7) / 7.0f,
@@ -836,7 +839,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
836 /* Adjust colors based on light/dark mode. */ 839 /* Adjust colors based on light/dark mode. */
837 for (int i = tmFirst_ColorId; i < max_ColorId; i++) { 840 for (int i = tmFirst_ColorId; i < max_ColorId; i++) {
838 iHSLColor color = hsl_Color(get_Color(i)); 841 iHSLColor color = hsl_Color(get_Color(i));
839 if (isLightMode) { 842 if (theme == white_GmDocumentTheme) {
840#if 0 843#if 0
841 if (isLink_ColorId(i)) continue; 844 if (isLink_ColorId(i)) continue;
842 color.lum = 1.0f - color.lum; /* All colors invert lightness. */ 845 color.lum = 1.0f - color.lum; /* All colors invert lightness. */
@@ -894,7 +897,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
894 } 897 }
895 } 898 }
896 /* Modify overall saturation. */ 899 /* Modify overall saturation. */
897 color.sat *= saturationLevel; 900 color.sat *= prefs->saturation;
898 setHsl_Color(i, color); 901 setHsl_Color(i, color);
899 } 902 }
900 } 903 }
diff --git a/src/gmdocument.h b/src/gmdocument.h
index b453fba9..b16df677 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -35,6 +35,11 @@ iDeclareType(GmImageInfo)
35iDeclareType(GmHeading) 35iDeclareType(GmHeading)
36iDeclareType(GmRun) 36iDeclareType(GmRun)
37 37
38enum iGmDocumentTheme {
39 colorfulDark_GmDocumentTheme,
40 white_GmDocumentTheme,
41};
42
38typedef uint16_t iGmLinkId; 43typedef uint16_t iGmLinkId;
39 44
40enum iGmLinkFlags { 45enum iGmLinkFlags {
diff --git a/src/prefs.c b/src/prefs.c
new file mode 100644
index 00000000..d773acbd
--- /dev/null
+++ b/src/prefs.c
@@ -0,0 +1,23 @@
1#include "prefs.h"
2
3void init_Prefs(iPrefs *d) {
4 d->theme = dark_ColorTheme;
5 d->useSystemTheme = iTrue;
6 d->retainWindowSize = iTrue;
7 d->zoomPercent = 100;
8 d->forceLineWrap = iFalse;
9 d->lineWidth = 40;
10 d->bigFirstParagraph = iTrue;
11 d->docThemeDark = colorfulDark_GmDocumentTheme;
12 d->docThemeLight = white_GmDocumentTheme;
13 d->saturation = 1.0f;
14 init_String(&d->gopherProxy);
15 init_String(&d->httpProxy);
16 init_String(&d->downloadDir);
17}
18
19void deinit_Prefs(iPrefs *d) {
20 deinit_String(&d->gopherProxy);
21 deinit_String(&d->httpProxy);
22 deinit_String(&d->downloadDir);
23}
diff --git a/src/prefs.h b/src/prefs.h
new file mode 100644
index 00000000..d4e300ec
--- /dev/null
+++ b/src/prefs.h
@@ -0,0 +1,29 @@
1#pragma once
2#include <the_Foundation/string.h>
3
4#include "ui/color.h"
5#include "gmdocument.h"
6
7/* User preferences */
8
9iDeclareType(Prefs)
10
11struct Impl_Prefs {
12 iBool retainWindowSize;
13 float uiScale;
14 int zoomPercent;
15 iBool useSystemTheme;
16 enum iColorTheme theme;
17 iString gopherProxy;
18 iString httpProxy;
19 iString downloadDir;
20 /* Content */
21 int lineWidth;
22 iBool bigFirstParagraph;
23 iBool forceLineWrap;
24 enum iGmDocumentTheme docThemeDark;
25 enum iGmDocumentTheme docThemeLight;
26 float saturation;
27};
28
29iDeclareTypeConstruction(Prefs)
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 735a7214..69dde8c7 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -243,10 +243,11 @@ static void resetSmoothScroll_DocumentWidget_(iDocumentWidget *d) {
243} 243}
244 244
245static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { 245static int documentWidth_DocumentWidget_(const iDocumentWidget *d) {
246 const iWidget *w = constAs_Widget(d); 246 const iWidget *w = constAs_Widget(d);
247 const iRect bounds = bounds_Widget(w); 247 const iRect bounds = bounds_Widget(w);
248 const iPrefs * prefs = prefs_App();
248 return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, 249 return iMini(bounds.size.x - gap_UI * d->pageMargin * 2,
249 fontSize_UI * 40 * zoom_App() / 100); /* TODO: Add user preference .*/ 250 fontSize_UI * prefs->lineWidth * prefs->zoomPercent / 100);
250} 251}
251 252
252static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { 253static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) {
diff --git a/src/ui/util.c b/src/ui/util.c
index bdc1c813..52107d81 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -878,6 +878,22 @@ iWidget *makePreferences_Widget(void) {
878 addChild_Widget(headings, iClob(makeHeading_Widget("UI scale factor:"))); 878 addChild_Widget(headings, iClob(makeHeading_Widget("UI scale factor:")));
879 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale"); 879 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale");
880 } 880 }
881 /* Layout. */ {
882 appendTwoColumnPage_(tabs, "Layout", &headings, &values);
883 addChild_Widget(headings, iClob(makeHeading_Widget("Line width:")));
884 addChild_Widget(values, iClob(new_LabelWidget("Normal", 0, 0, NULL)));
885 addChild_Widget(headings, iClob(makeHeading_Widget("First paragaph:")));
886 addChild_Widget(values, iClob(new_LabelWidget("Emphasized", 0, 0, NULL)));
887 }
888 /* Colors. */ {
889 appendTwoColumnPage_(tabs, "Colors", &headings, &values);
890 addChild_Widget(headings, iClob(makeHeading_Widget("Saturation:")));
891 addChild_Widget(values, iClob(new_LabelWidget("Full", 0, 0, 0)));
892 addChild_Widget(headings, iClob(makeHeading_Widget("Dark theme:")));
893 addChild_Widget(values, iClob(new_LabelWidget("Colorful", 0, 0, 0)));
894 addChild_Widget(headings, iClob(makeHeading_Widget("Light theme:")));
895 addChild_Widget(values, iClob(new_LabelWidget("White", 0, 0, 0)));
896 }
881 /* Proxies. */ { 897 /* Proxies. */ {
882 appendTwoColumnPage_(tabs, "Proxies", &headings, &values); 898 appendTwoColumnPage_(tabs, "Proxies", &headings, &values);
883 addChild_Widget(headings, iClob(makeHeading_Widget("Gopher proxy:"))); 899 addChild_Widget(headings, iClob(makeHeading_Widget("Gopher proxy:")));