summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-17 09:00:40 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-17 09:00:40 +0300
commit3528bb86ab14c275c41adc7cfa29a5f5eb167ff2 (patch)
tree4b93b0f2bb9718408d4b855d1f8b8ed17a3e690f
parent7e536572b602cba180ad4e85bd9c071479f6fa22 (diff)
Working on a UI for uploading text/data
`UploadWidget` allows entering long-form text or dropping a file for uploading. InputWidget isn't yet well suited for really long documents... Some optimizations will be needed.
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/app.c10
-rw-r--r--src/gmrequest.c3
-rw-r--r--src/ui/certimportwidget.c17
-rw-r--r--src/ui/uploadwidget.c148
-rw-r--r--src/ui/uploadwidget.h33
-rw-r--r--src/ui/util.c46
-rw-r--r--src/ui/util.h4
8 files changed, 226 insertions, 37 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a4a2f06..5b5fa9cd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -200,6 +200,8 @@ set (SOURCES
200 src/ui/touch.h 200 src/ui/touch.h
201 src/ui/translation.c 201 src/ui/translation.c
202 src/ui/translation.h 202 src/ui/translation.h
203 src/ui/uploadwidget.c
204 src/ui/uploadwidget.h
203 src/ui/util.c 205 src/ui/util.c
204 src/ui/util.h 206 src/ui/util.h
205 src/ui/visbuf.c 207 src/ui/visbuf.c
diff --git a/src/app.c b/src/app.c
index 3ee78ee4..a1543a60 100644
--- a/src/app.c
+++ b/src/app.c
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
41#include "ui/labelwidget.h" 41#include "ui/labelwidget.h"
42#include "ui/root.h" 42#include "ui/root.h"
43#include "ui/sidebarwidget.h" 43#include "ui/sidebarwidget.h"
44#include "ui/uploadwidget.h"
44#include "ui/text.h" 45#include "ui/text.h"
45#include "ui/util.h" 46#include "ui/util.h"
46#include "ui/window.h" 47#include "ui/window.h"
@@ -2324,6 +2325,15 @@ iBool handleCommand_App(const char *cmd) {
2324 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0; 2325 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0;
2325 iUrl parts; 2326 iUrl parts;
2326 init_Url(&parts, url); 2327 init_Url(&parts, url);
2328 if (equalCase_Rangecc(parts.scheme, "titan")) {
2329 iUploadWidget *upload = new_UploadWidget();
2330 setUrl_UploadWidget(upload, url);
2331 setResponseViewer_UploadWidget(upload, document_App());
2332 addChild_Widget(get_Root()->widget, iClob(upload));
2333 finalizeSheet_Mobile(as_Widget(upload));
2334 postRefresh_App();
2335 return iTrue;
2336 }
2327 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || 2337 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") ||
2328 ((noProxy || isEmpty_String(&d->prefs.httpProxy)) && 2338 ((noProxy || isEmpty_String(&d->prefs.httpProxy)) &&
2329 (equalCase_Rangecc(parts.scheme, "http") || 2339 (equalCase_Rangecc(parts.scheme, "http") ||
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 1d84ef47..2471f311 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -909,7 +909,8 @@ void submit_GmRequest(iGmRequest *d) {
909 size_Block(&d->titan->data)); 909 size_Block(&d->titan->data));
910 if (!isEmpty_String(&d->titan->token)) { 910 if (!isEmpty_String(&d->titan->token)) {
911 appendCStr_Block(&content, ";token="); 911 appendCStr_Block(&content, ";token=");
912 append_Block(&content, utf8_String(&d->titan->token)); 912 append_Block(&content,
913 utf8_String(collect_String(urlEncode_String(&d->titan->token))));
913 } 914 }
914 appendCStr_Block(&content, "\r\n"); 915 appendCStr_Block(&content, "\r\n");
915 append_Block(&content, &d->titan->data); 916 append_Block(&content, &d->titan->data);
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
index 6e818137..a8346e19 100644
--- a/src/ui/certimportwidget.c
+++ b/src/ui/certimportwidget.c
@@ -107,17 +107,8 @@ void init_CertImportWidget(iCertImportWidget *d) {
107 init_Widget(w); 107 init_Widget(w);
108 setId_Widget(w, "certimport"); 108 setId_Widget(w, "certimport");
109 d->cert = NULL; 109 d->cert = NULL;
110 /* This should behave similar to sheets. */ { 110 /* This should behave similar to sheets. */
111 setPadding1_Widget(w, 3 * gap_UI); 111 useSheetStyle_Widget(w);
112 setFrameColor_Widget(w, uiSeparator_ColorId);
113 setBackgroundColor_Widget(w, uiBackground_ColorId);
114 setFlags_Widget(w,
115 mouseModal_WidgetFlag | keepOnTop_WidgetFlag | arrangeVertical_WidgetFlag |
116 arrangeSize_WidgetFlag | centerHorizontal_WidgetFlag |
117 parentCannotResize_WidgetFlag |
118 overflowScrollable_WidgetFlag,
119 iTrue);
120 }
121 addChildFlags_Widget( 112 addChildFlags_Widget(
122 w, 113 w,
123 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.certimport}", NULL)), 114 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.certimport}", NULL)),
@@ -168,7 +159,6 @@ void init_CertImportWidget(iCertImportWidget *d) {
168 "certimport.accept" } }, 159 "certimport.accept" } },
169 2); 160 2);
170 addChild_Widget(w, iClob(buttons)); 161 addChild_Widget(w, iClob(buttons));
171// arrange_Widget(w);
172 if (deviceType_App() != desktop_AppDeviceType) { 162 if (deviceType_App() != desktop_AppDeviceType) {
173 /* Try auto-pasting. */ 163 /* Try auto-pasting. */
174 postCommand_App("certimport.paste"); 164 postCommand_App("certimport.paste");
@@ -263,8 +253,7 @@ static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Even
263} 253}
264 254
265static void draw_CertImportWidget_(const iCertImportWidget *d) { 255static void draw_CertImportWidget_(const iCertImportWidget *d) {
266 const iWidget *w = constAs_Widget(d); 256 draw_Widget(constAs_Widget(d));
267 draw_Widget(w);
268} 257}
269 258
270iBeginDefineSubclass(CertImportWidget, Widget) 259iBeginDefineSubclass(CertImportWidget, Widget)
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
new file mode 100644
index 00000000..036571a5
--- /dev/null
+++ b/src/ui/uploadwidget.c
@@ -0,0 +1,148 @@
1/* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi>
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are met:
5
61. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
82. Redistributions in binary form must reproduce the above copyright notice,
9 this list of conditions and the following disclaimer in the documentation
10 and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23#include "uploadwidget.h"
24#include "labelwidget.h"
25#include "inputwidget.h"
26#include "documentwidget.h"
27#include "color.h"
28#include "gmrequest.h"
29#include "app.h"
30
31iDefineObjectConstruction(UploadWidget)
32
33struct Impl_UploadWidget {
34 iWidget widget;
35 iString url;
36 iDocumentWidget *viewer;
37 iGmRequest * request;
38 iLabelWidget * info;
39 iInputWidget * mime;
40 iInputWidget * token;
41 iInputWidget * input;
42};
43
44void init_UploadWidget(iUploadWidget *d) {
45 iWidget *w = as_Widget(d);
46 init_Widget(w);
47 setId_Widget(w, "upload");
48 useSheetStyle_Widget(w);
49 init_String(&d->url);
50 d->viewer = NULL;
51 d->request = NULL;
52 addChildFlags_Widget(w,
53 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.upload}", NULL)),
54 frameless_WidgetFlag);
55 d->info = addChildFlags_Widget(w, iClob(new_LabelWidget("", NULL)), frameless_WidgetFlag);
56 /* Tabs for input data. */
57 iWidget *tabs = makeTabs_Widget(w);
58 iWidget *headings, *values;
59 setBackgroundColor_Widget(findChild_Widget(tabs, "tabs.buttons"), uiBackgroundSidebar_ColorId);
60 setId_Widget(tabs, "upload.tabs");
61// const int bigGap = lineHeight_Text(uiLabel_FontId) * 3 / 4;
62 /* Text input. */ {
63 //appendTwoColumnTabPage_Widget(tabs, "${heading.upload.text}", '1', &headings, &values);
64 iWidget *page = new_Widget();
65 setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue);
66 d->input = new_InputWidget(0);
67 setEnterInsertsLF_InputWidget(d->input, iTrue);
68 setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1));
69 addChild_Widget(page, iClob(d->input));
70 appendTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0);
71 }
72 /* File content. */ {
73 appendTwoColumnTabPage_Widget(tabs, "${heading.upload.file}", '2', &headings, &values);
74// iWidget *pad = addChild_Widget(headings, iClob(makePadding_Widget(0)));
75// iWidget *hint = addChild_Widget(values, iClob(new_LabelWidget("${upload.file.drophint}", NULL)));
76// pad->sizeRef = hint;
77 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.name}", NULL)));
78 addChild_Widget(values, iClob(new_LabelWidget("filename.ext", NULL)));
79 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.size}", NULL)));
80 addChild_Widget(values, iClob(new_LabelWidget("0 KB", NULL)));
81 d->mime = new_InputWidget(0);
82 setFixedSize_Widget(as_Widget(d->mime), init_I2(50 * gap_UI, -1));
83 addTwoColumnDialogInputField_Widget(headings, values, "${upload.mime}", "upload.mime", iClob(d->mime));
84 }
85 /* Token. */ {
86 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
87 iWidget *page = makeTwoColumns_Widget(&headings, &values);
88 d->token = addTwoColumnDialogInputField_Widget(
89 headings, values, "${upload.token}", "upload.token", iClob(new_InputWidget(0)));
90 setHint_InputWidget(d->token, "${hint.upload.token}");
91 setFixedSize_Widget(as_Widget(d->token), init_I2(50 * gap_UI, -1));
92 addChild_Widget(w, iClob(page));
93 }
94 /* Buttons. */ {
95 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
96 iWidget *buttons =
97 makeDialogButtons_Widget((iMenuItem[]){ { "${cancel}", SDLK_ESCAPE, 0, "upload.cancel" },
98 { uiTextAction_ColorEscape "${dlg.upload.send}",
99 SDLK_RETURN,
100 KMOD_PRIMARY,
101 "upload.accept" } },
102 2);
103 addChild_Widget(w, iClob(buttons));
104 }
105 resizeToLargestPage_Widget(tabs);
106 setFocus_Widget(as_Widget(d->token));
107}
108
109void deinit_UploadWidget(iUploadWidget *d) {
110 deinit_String(&d->url);
111 iRelease(d->request);
112}
113
114void setUrl_UploadWidget(iUploadWidget *d, const iString *url) {
115 set_String(&d->url, url);
116 setText_LabelWidget(d->info, &d->url);
117}
118
119void setResponseViewer_UploadWidget(iUploadWidget *d, iDocumentWidget *doc) {
120 d->viewer = doc;
121}
122
123static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
124 iWidget *w = as_Widget(d);
125 if (isCommand_Widget(w, ev, "upload.cancel")) {
126 /* TODO: If text has been entered, ask for confirmation. */
127 setupSheetTransition_Mobile(w, iFalse);
128 destroy_Widget(w);
129 return iTrue;
130 }
131 if (isCommand_Widget(w, ev, "upload.accept")) {
132 /* Make a GmRequest and send the data. */
133 /* The dialog will remain open until the request finishes, showing upload progress. */
134 }
135 if (ev->type == SDL_DROPFILE) {
136 /* Switch to File tab. */
137 }
138 return processEvent_Widget(w, ev);
139}
140
141static void draw_UploadWidget_(const iUploadWidget *d) {
142 draw_Widget(constAs_Widget(d));
143}
144
145iBeginDefineSubclass(UploadWidget, Widget)
146 .processEvent = (iAny *) processEvent_UploadWidget_,
147 .draw = (iAny *) draw_UploadWidget_,
148iEndDefineSubclass(UploadWidget)
diff --git a/src/ui/uploadwidget.h b/src/ui/uploadwidget.h
new file mode 100644
index 00000000..5a7de45e
--- /dev/null
+++ b/src/ui/uploadwidget.h
@@ -0,0 +1,33 @@
1/* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi>
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are met:
5
61. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
82. Redistributions in binary form must reproduce the above copyright notice,
9 this list of conditions and the following disclaimer in the documentation
10 and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23#pragma once
24
25#include "widget.h"
26
27iDeclareWidgetClass(UploadWidget)
28iDeclareObjectConstruction(UploadWidget)
29
30iDeclareType(DocumentWidget)
31
32void setUrl_UploadWidget (iUploadWidget *, const iString *url);
33void setResponseViewer_UploadWidget (iUploadWidget *, iDocumentWidget *doc);
diff --git a/src/ui/util.c b/src/ui/util.c
index 220e3a50..6bc358de 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1118,20 +1118,22 @@ size_t tabCount_Widget(const iWidget *tabs) {
1118 1118
1119/*-----------------------------------------------------------------------------------------------*/ 1119/*-----------------------------------------------------------------------------------------------*/
1120 1120
1121
1122iWidget *makeSheet_Widget(const char *id) { 1121iWidget *makeSheet_Widget(const char *id) {
1123 iWidget *sheet = new_Widget(); 1122 iWidget *sheet = new_Widget();
1124 setId_Widget(sheet, id); 1123 setId_Widget(sheet, id);
1125 setPadding1_Widget(sheet, 3 * gap_UI); 1124 useSheetStyle_Widget(sheet);
1126 setFrameColor_Widget(sheet, uiSeparator_ColorId); 1125 return sheet;
1127 setBackgroundColor_Widget(sheet, uiBackground_ColorId); 1126}
1128 setFlags_Widget(sheet, 1127
1129 parentCannotResize_WidgetFlag | 1128void useSheetStyle_Widget(iWidget *d) {
1130 focusRoot_WidgetFlag | mouseModal_WidgetFlag | keepOnTop_WidgetFlag | 1129 setPadding1_Widget(d, 3 * gap_UI);
1131 arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag | 1130 setFrameColor_Widget(d, uiSeparator_ColorId);
1131 setBackgroundColor_Widget(d, uiBackground_ColorId);
1132 setFlags_Widget(d,
1133 parentCannotResize_WidgetFlag | focusRoot_WidgetFlag | mouseModal_WidgetFlag |
1134 keepOnTop_WidgetFlag | arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag |
1132 centerHorizontal_WidgetFlag | overflowScrollable_WidgetFlag, 1135 centerHorizontal_WidgetFlag | overflowScrollable_WidgetFlag,
1133 iTrue); 1136 iTrue);
1134 return sheet;
1135} 1137}
1136 1138
1137static void acceptValueInput_(iWidget *dlg) { 1139static void acceptValueInput_(iWidget *dlg) {
@@ -1435,7 +1437,7 @@ static void appendFramelessTabPage_(iWidget *tabs, iWidget *page, const char *ti
1435 iTrue); 1437 iTrue);
1436} 1438}
1437 1439
1438static iWidget *makeTwoColumnWidget_(iWidget **headings, iWidget **values) { 1440iWidget *makeTwoColumns_Widget(iWidget **headings, iWidget **values) {
1439 iWidget *page = new_Widget(); 1441 iWidget *page = new_Widget();
1440 setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); 1442 setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
1441 *headings = addChildFlags_Widget( 1443 *headings = addChildFlags_Widget(
@@ -1445,8 +1447,8 @@ static iWidget *makeTwoColumnWidget_(iWidget **headings, iWidget **values) {
1445 return page; 1447 return page;
1446} 1448}
1447 1449
1448static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int shortcut, iWidget **headings, 1450iWidget *appendTwoColumnTabPage_Widget(iWidget *tabs, const char *title, int shortcut, iWidget **headings,
1449 iWidget **values) { 1451 iWidget **values) {
1450 /* TODO: Use `makeTwoColumnWidget_()`, see above. */ 1452 /* TODO: Use `makeTwoColumnWidget_()`, see above. */
1451 iWidget *page = new_Widget(); 1453 iWidget *page = new_Widget();
1452 setFlags_Widget(page, arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag, iTrue); 1454 setFlags_Widget(page, arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
@@ -1600,7 +1602,7 @@ iWidget *makePreferences_Widget(void) {
1600 iWidget *headings, *values; 1602 iWidget *headings, *values;
1601 const int bigGap = lineHeight_Text(uiLabel_FontId) * 3 / 4; 1603 const int bigGap = lineHeight_Text(uiLabel_FontId) * 3 / 4;
1602 /* General preferences. */ { 1604 /* General preferences. */ {
1603 appendTwoColumnPage_(tabs, "${heading.prefs.general}", '1', &headings, &values); 1605 appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.general}", '1', &headings, &values);
1604#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) 1606#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
1605 addPrefsInputWithHeading_(headings, values, "prefs.downloads", iClob(new_InputWidget(0))); 1607 addPrefsInputWithHeading_(headings, values, "prefs.downloads", iClob(new_InputWidget(0)));
1606#endif 1608#endif
@@ -1668,7 +1670,7 @@ iWidget *makePreferences_Widget(void) {
1668 } 1670 }
1669 } 1671 }
1670 /* User Interface. */ { 1672 /* User Interface. */ {
1671 appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values); 1673 appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.interface}", '2', &headings, &values);
1672#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 1674#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
1673 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}"))); 1675 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}")));
1674 addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe"))); 1676 addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe")));
@@ -1711,7 +1713,7 @@ iWidget *makePreferences_Widget(void) {
1711 } 1713 }
1712 } 1714 }
1713 /* Colors. */ { 1715 /* Colors. */ {
1714 appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values); 1716 appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.colors}", '3', &headings, &values);
1715 makeTwoColumnHeading_("${heading.prefs.uitheme}", headings, values); 1717 makeTwoColumnHeading_("${heading.prefs.uitheme}", headings, values);
1716#if defined (iPlatformApple) || defined (iPlatformMSys) 1718#if defined (iPlatformApple) || defined (iPlatformMSys)
1717 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); 1719 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}")));
@@ -1766,7 +1768,7 @@ iWidget *makePreferences_Widget(void) {
1766 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 1768 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
1767 } 1769 }
1768 /* Fonts. */ { 1770 /* Fonts. */ {
1769 setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts"); 1771 setId_Widget(appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts");
1770 /* Fonts. */ { 1772 /* Fonts. */ {
1771 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); 1773 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}")));
1772 addFontButtons_(values, "headingfont"); 1774 addFontButtons_(values, "headingfont");
@@ -1815,7 +1817,7 @@ iWidget *makePreferences_Widget(void) {
1815 } 1817 }
1816 } 1818 }
1817 /* Style. */ { 1819 /* Style. */ {
1818 setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '5', &headings, &values), "prefs.page.style"); 1820 setId_Widget(appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.style}", '5', &headings, &values), "prefs.page.style");
1819// makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); 1821// makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values);
1820 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); 1822 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}")));
1821 iWidget *widths = new_Widget(); 1823 iWidget *widths = new_Widget();
@@ -1850,7 +1852,7 @@ iWidget *makePreferences_Widget(void) {
1850 addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort"))); 1852 addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort")));
1851 } 1853 }
1852 /* Network. */ { 1854 /* Network. */ {
1853 appendTwoColumnPage_(tabs, "${heading.prefs.network}", '6', &headings, &values); 1855 appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.network}", '6', &headings, &values);
1854 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); 1856 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}")));
1855 addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); 1857 addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls")));
1856 /* Cache size. */ { 1858 /* Cache size. */ {
@@ -1908,7 +1910,7 @@ iWidget *makeBookmarkEditor_Widget(void) {
1908 frameless_WidgetFlag), 1910 frameless_WidgetFlag),
1909 "bmed.heading"); 1911 "bmed.heading");
1910 iWidget *headings, *values; 1912 iWidget *headings, *values;
1911 addChild_Widget(dlg, iClob(makeTwoColumnWidget_(&headings, &values))); 1913 addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values)));
1912 iInputWidget *inputs[4]; 1914 iInputWidget *inputs[4];
1913 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.title}", "bmed.title", iClob(inputs[0] = new_InputWidget(0))); 1915 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.title}", "bmed.title", iClob(inputs[0] = new_InputWidget(0)));
1914 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.url}", "bmed.url", iClob(inputs[1] = new_InputWidget(0))); 1916 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.url}", "bmed.url", iClob(inputs[1] = new_InputWidget(0)));
@@ -1917,7 +1919,7 @@ iWidget *makeBookmarkEditor_Widget(void) {
1917 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1))); 1919 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1)));
1918 /* Buttons for special tags. */ 1920 /* Buttons for special tags. */
1919 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 1921 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
1920 addChild_Widget(dlg, iClob(makeTwoColumnWidget_(&headings, &values))); 1922 addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values)));
1921 makeTwoColumnHeading_("SPECIAL TAGS", headings, values); 1923 makeTwoColumnHeading_("SPECIAL TAGS", headings, values);
1922 addChild_Widget(headings, iClob(makeHeading_Widget("${bookmark.tag.home}"))); 1924 addChild_Widget(headings, iClob(makeHeading_Widget("${bookmark.tag.home}")));
1923 addChild_Widget(values, iClob(makeToggle_Widget("bmed.tag.home"))); 1925 addChild_Widget(values, iClob(makeToggle_Widget("bmed.tag.home")));
@@ -2048,7 +2050,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
2048 frameless_WidgetFlag), 2050 frameless_WidgetFlag),
2049 "feedcfg.heading"); 2051 "feedcfg.heading");
2050 iWidget *headings, *values; 2052 iWidget *headings, *values;
2051 addChild_Widget(dlg, iClob(makeTwoColumnWidget_(&headings, &values))); 2053 addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values)));
2052 iInputWidget *input = new_InputWidget(0); 2054 iInputWidget *input = new_InputWidget(0);
2053 addDialogInputWithHeading_(headings, values, "${dlg.feed.title}", "feedcfg.title", iClob(input)); 2055 addDialogInputWithHeading_(headings, values, "${dlg.feed.title}", "feedcfg.title", iClob(input));
2054 addChild_Widget(headings, iClob(makeHeading_Widget("${dlg.feed.entrytype}"))); 2056 addChild_Widget(headings, iClob(makeHeading_Widget("${dlg.feed.entrytype}")));
@@ -2223,7 +2225,7 @@ iWidget *makeTranslation_Widget(iWidget *parent) {
2223 addChild_Widget(dlg, iClob(makePadding_Widget(lineHeight_Text(uiLabel_FontId)))); 2225 addChild_Widget(dlg, iClob(makePadding_Widget(lineHeight_Text(uiLabel_FontId))));
2224 iWidget *headings, *values; 2226 iWidget *headings, *values;
2225 iWidget *page; 2227 iWidget *page;
2226 addChild_Widget(dlg, iClob(page = makeTwoColumnWidget_(&headings, &values))); 2228 addChild_Widget(dlg, iClob(page = makeTwoColumns_Widget(&headings, &values)));
2227 setId_Widget(page, "xlt.langs"); 2229 setId_Widget(page, "xlt.langs");
2228 iLabelWidget *fromLang, *toLang; 2230 iLabelWidget *fromLang, *toLang;
2229 /* Source language. */ { 2231 /* Source language. */ {
diff --git a/src/ui/util.h b/src/ui/util.h
index 43aeb172..5b02a4b3 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -242,6 +242,8 @@ iLabelWidget * makeMenuButton_LabelWidget (const char *label, const iMenuItem
242 242
243iWidget * makeTabs_Widget (iWidget *parent); 243iWidget * makeTabs_Widget (iWidget *parent);
244void appendTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods); 244void appendTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);
245iWidget * appendTwoColumnTabPage_Widget(iWidget *tabs, const char *title, int shortcut, iWidget **headings,
246 iWidget **values);
245void prependTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods); 247void prependTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);
246iWidget * removeTabPage_Widget (iWidget *tabs, size_t index); /* returns the page */ 248iWidget * removeTabPage_Widget (iWidget *tabs, size_t index); /* returns the page */
247void resizeToLargestPage_Widget (iWidget *tabs); 249void resizeToLargestPage_Widget (iWidget *tabs);
@@ -258,7 +260,9 @@ size_t tabCount_Widget (const iWidget *tabs);
258/*-----------------------------------------------------------------------------------------------*/ 260/*-----------------------------------------------------------------------------------------------*/
259 261
260iWidget * makeSheet_Widget (const char *id); 262iWidget * makeSheet_Widget (const char *id);
263void useSheetStyle_Widget (iWidget *);
261iWidget * makeDialogButtons_Widget (const iMenuItem *actions, size_t numActions); 264iWidget * makeDialogButtons_Widget (const iMenuItem *actions, size_t numActions);
265iWidget * makeTwoColumns_Widget (iWidget **headings, iWidget **values);
262 266
263iInputWidget *addTwoColumnDialogInputField_Widget(iWidget *headings, iWidget *values, 267iInputWidget *addTwoColumnDialogInputField_Widget(iWidget *headings, iWidget *values,
264 const char *labelText, const char *inputId, 268 const char *labelText, const char *inputId,