summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po6
-rw-r--r--res/lang/de.binbin18773 -> 18855 bytes
-rw-r--r--res/lang/en.binbin17261 -> 17343 bytes
-rw-r--r--res/lang/es.binbin19352 -> 19434 bytes
-rw-r--r--res/lang/fi.binbin19055 -> 19137 bytes
-rw-r--r--res/lang/fr.binbin19806 -> 19888 bytes
-rw-r--r--res/lang/ia.binbin19244 -> 19326 bytes
-rw-r--r--res/lang/ie.binbin18476 -> 18558 bytes
-rw-r--r--res/lang/ru.binbin28964 -> 29046 bytes
-rw-r--r--res/lang/sr.binbin27459 -> 27541 bytes
-rw-r--r--res/lang/zh_Hans.binbin16651 -> 16733 bytes
-rw-r--r--res/lang/zh_Hant.binbin16725 -> 16807 bytes
-rw-r--r--src/app.c41
-rw-r--r--src/ui/documentwidget.c5
-rw-r--r--src/ui/root.c22
-rw-r--r--src/ui/util.c8
-rw-r--r--src/ui/util.h1
-rw-r--r--src/ui/widget.c10
-rw-r--r--src/ui/widget.h2
-rw-r--r--src/ui/window.c91
-rw-r--r--src/ui/window.h15
21 files changed, 176 insertions, 25 deletions
diff --git a/po/en.po b/po/en.po
index c78fc948..fff2962e 100644
--- a/po/en.po
+++ b/po/en.po
@@ -681,6 +681,12 @@ msgstr "Open Link in New Tab"
681msgid "link.newtab.background" 681msgid "link.newtab.background"
682msgstr "Open Link in Background Tab" 682msgstr "Open Link in Background Tab"
683 683
684msgid "link.side"
685msgstr "Open Link to the Side"
686
687msgid "link.side.newtab"
688msgstr "Open Link in New Tab to the Side"
689
684msgid "link.browser" 690msgid "link.browser"
685msgstr "Open Link in Default Browser" 691msgstr "Open Link in Default Browser"
686 692
diff --git a/res/lang/de.bin b/res/lang/de.bin
index a9eb2cf7..84482e24 100644
--- a/res/lang/de.bin
+++ b/res/lang/de.bin
Binary files differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index ca8b35c4..a1bff669 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 62c776a7..90fd5c56 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 15e3fd0a..b9d36a25 100644
--- a/res/lang/fi.bin
+++ b/res/lang/fi.bin
Binary files differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index b5ad72ba..d356cb9a 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index f2600b2f..b79f60c7 100644
--- a/res/lang/ia.bin
+++ b/res/lang/ia.bin
Binary files differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index ff44e3ff..32f58e81 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 03223901..5b7740e2 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 241f0651..3049e072 100644
--- a/res/lang/sr.bin
+++ b/res/lang/sr.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 380250dd..c01c8d4b 100644
--- a/res/lang/zh_Hans.bin
+++ b/res/lang/zh_Hans.bin
Binary files differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index d127059a..57529199 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 2f1e435b..29d8cac4 100644
--- a/src/app.c
+++ b/src/app.c
@@ -406,7 +406,10 @@ static iBool loadState_App_(iApp *d) {
406 readData_File(f, 4, magic); 406 readData_File(f, 4, magic);
407 if (!memcmp(magic, magicTabDocument_App_, 4)) { 407 if (!memcmp(magic, magicTabDocument_App_, 4)) {
408 const int8_t flags = read8_File(f); 408 const int8_t flags = read8_File(f);
409 const int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; 409 int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0;
410 if (rootIndex > numRoots_Window(d->window) - 1) {
411 rootIndex = 0;
412 }
410 setCurrent_Root(d->window->roots[rootIndex]); 413 setCurrent_Root(d->window->roots[rootIndex]);
411 if (isFirstTab[rootIndex]) { 414 if (isFirstTab[rootIndex]) {
412 isFirstTab[rootIndex] = iFalse; 415 isFirstTab[rootIndex] = iFalse;
@@ -1148,9 +1151,12 @@ static int run_App_(iApp *d) {
1148 while (d->isRunning) { 1151 while (d->isRunning) {
1149 dispatchCommands_Periodic(&d->periodic); 1152 dispatchCommands_Periodic(&d->periodic);
1150 processEvents_App(waitForNewEvents_AppEventMode); 1153 processEvents_App(waitForNewEvents_AppEventMode);
1151 setCurrent_Root(NULL);
1152 runTickers_App_(d); 1154 runTickers_App_(d);
1153 refresh_App(); 1155 refresh_App();
1156 /* Change the widget tree while we are not iterating through it. */
1157 if (d->window->splitMode != d->window->pendingSplitMode) {
1158 setSplitMode_Window(d->window, d->window->pendingSplitMode);
1159 }
1154 recycle_Garbage(); 1160 recycle_Garbage();
1155 } 1161 }
1156 SDL_DelEventWatch(resizeWatcher_, d); 1162 SDL_DelEventWatch(resizeWatcher_, d);
@@ -1654,6 +1660,15 @@ iBool handleCommand_App(const char *cmd) {
1654 d->prefs.langTo = argLabel_Command(cmd, "to"); 1660 d->prefs.langTo = argLabel_Command(cmd, "to");
1655 return iTrue; 1661 return iTrue;
1656 } 1662 }
1663 else if (equal_Command(cmd, "ui.frames")) {
1664 d->window->pendingSplitMode =
1665 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);
1666 return iTrue;
1667 }
1668// else if (equal_Command(cmd, "window.updatelayout")) {
1669// resize_Window(d->window, -1, -1);
1670// return iTrue;
1671// }
1657 else if (equal_Command(cmd, "window.retain")) { 1672 else if (equal_Command(cmd, "window.retain")) {
1658 d->prefs.retainWindowSize = arg_Command(cmd); 1673 d->prefs.retainWindowSize = arg_Command(cmd);
1659 return iTrue; 1674 return iTrue;
@@ -2009,15 +2024,22 @@ iBool handleCommand_App(const char *cmd) {
2009 arrange_Widget(tabs); 2024 arrange_Widget(tabs);
2010 return iTrue; 2025 return iTrue;
2011 } 2026 }
2012 if (tabCount_Widget(tabs) > 1) { 2027 const iBool isSplit = numRoots_Window(get_Window()) > 1;
2028 if (tabCount_Widget(tabs) > 1 || isSplit) {
2013 iWidget *closed = removeTabPage_Widget(tabs, index); 2029 iWidget *closed = removeTabPage_Widget(tabs, index);
2014 destroy_Widget(closed); /* released later */ 2030 destroy_Widget(closed); /* released later */
2015 if (index == tabCount_Widget(tabs)) { 2031 if (index == tabCount_Widget(tabs)) {
2016 index--; 2032 index--;
2017 } 2033 }
2018 arrange_Widget(tabs); 2034 if (tabCount_Widget(tabs) == 0) {
2019 if (wasCurrent) { 2035 iAssert(isSplit);
2020 postCommandf_App("tabs.switch page:%p", tabPage_Widget(tabs, index)); 2036 postCommand_App("ui.frames arg:0");
2037 }
2038 else {
2039 arrange_Widget(tabs);
2040 if (wasCurrent) {
2041 postCommandf_App("tabs.switch page:%p", tabPage_Widget(tabs, index));
2042 }
2021 } 2043 }
2022 } 2044 }
2023 else { 2045 else {
@@ -2113,7 +2135,7 @@ iBool handleCommand_App(const char *cmd) {
2113 const iPtrArray *homepages = 2135 const iPtrArray *homepages =
2114 list_Bookmarks(d->bookmarks, NULL, filterTagsRegExp_Bookmarks, pattern); 2136 list_Bookmarks(d->bookmarks, NULL, filterTagsRegExp_Bookmarks, pattern);
2115 if (isEmpty_PtrArray(homepages)) { 2137 if (isEmpty_PtrArray(homepages)) {
2116 postCommand_App("open url:about:lagrange"); 2138 postCommand_Root(get_Root(), "open url:about:lagrange");
2117 } 2139 }
2118 else { 2140 else {
2119 iStringSet *urls = iClob(new_StringSet()); 2141 iStringSet *urls = iClob(new_StringSet());
@@ -2125,13 +2147,13 @@ iBool handleCommand_App(const char *cmd) {
2125 } 2147 }
2126 } 2148 }
2127 if (!isEmpty_StringSet(urls)) { 2149 if (!isEmpty_StringSet(urls)) {
2128 postCommandf_App( 2150 postCommandf_Root(get_Root(),
2129 "open url:%s", 2151 "open url:%s",
2130 cstr_String(constAt_StringSet(urls, iRandoms(0, size_StringSet(urls))))); 2152 cstr_String(constAt_StringSet(urls, iRandoms(0, size_StringSet(urls)))));
2131 } 2153 }
2132 } 2154 }
2133 if (argLabel_Command(cmd, "focus")) { 2155 if (argLabel_Command(cmd, "focus")) {
2134 postCommand_App("navigate.focus"); 2156 postCommand_Root(get_Root(), "navigate.focus");
2135 } 2157 }
2136 return iTrue; 2158 return iTrue;
2137 } 2159 }
@@ -2335,6 +2357,7 @@ iObjectList *listDocuments_App(const iRoot *rootOrNull) {
2335 iObjectList *docs = new_ObjectList(); 2357 iObjectList *docs = new_ObjectList();
2336 iForIndices(i, win->roots) { 2358 iForIndices(i, win->roots) {
2337 iRoot *root = win->roots[i]; 2359 iRoot *root = win->roots[i];
2360 if (!root) continue;
2338 if (!rootOrNull || root == rootOrNull) { 2361 if (!rootOrNull || root == rootOrNull) {
2339 const iWidget *tabs = findChild_Widget(root->widget, "doctabs"); 2362 const iWidget *tabs = findChild_Widget(root->widget, "doctabs");
2340 iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { 2363 iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 21c0aa23..410b793e 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -3969,6 +3969,11 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx,
3969} 3969}
3970 3970
3971static void prerender_DocumentWidget_(iAny *context) { 3971static void prerender_DocumentWidget_(iAny *context) {
3972 if (current_Root() == NULL) {
3973 /* The widget has probably been removed from the widget tree, pending destruction.
3974 Tickers are not cancelled until the widget is actually destroyed. */
3975 return;
3976 }
3972 const iDocumentWidget *d = context; 3977 const iDocumentWidget *d = context;
3973 iDrawContext ctx = { 3978 iDrawContext ctx = {
3974 .widget = d, 3979 .widget = d,
diff --git a/src/ui/root.c b/src/ui/root.c
index 247faf38..2eca982c 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -304,6 +304,12 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
304 } 304 }
305 return iTrue; 305 return iTrue;
306 } 306 }
307 else if (equal_Command(cmd, "splitmenu.open")) {
308 iWidget *menu = findWidget_Root("splitmenu");
309 openMenu_Widget(menu, zero_I2());
310 setPos_Widget(menu, sub_I2(divi_I2(size_Root(get_Root()), 2), divi_I2(menu->rect.size, 2)));
311 return iTrue;
312 }
307 else if (equal_Command(cmd, "contextclick")) { 313 else if (equal_Command(cmd, "contextclick")) {
308 iBool showBarMenu = iFalse; 314 iBool showBarMenu = iFalse;
309 if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) { 315 if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) {
@@ -879,7 +885,7 @@ void updateMetrics_Root(iRoot *d) {
879} 885}
880 886
881void createUserInterface_Root(iRoot *d) { 887void createUserInterface_Root(iRoot *d) {
882 iWidget *root = d->widget = new_Widget(); 888 iWidget *root = d->widget = new_Widget();
883 iAssert(root->root == d); 889 iAssert(root->root == d);
884 setId_Widget(root, "root"); 890 setId_Widget(root, "root");
885 /* Children of root cover the entire window. */ 891 /* Children of root cover the entire window. */
@@ -1231,9 +1237,22 @@ void createUserInterface_Root(iRoot *d) {
1231 { clipboard_Icon " ${menu.paste}", 0, 0, "input.paste" }, 1237 { clipboard_Icon " ${menu.paste}", 0, 0, "input.paste" },
1232 }, 1238 },
1233 4); 1239 4);
1240 iWidget *splitMenu = makeMenu_Widget(root, (iMenuItem[]){
1241 { "Single Frame", '1', 0, "ui.frames arg:0" },
1242 { "---", 0, 0, NULL },
1243 { "Horizontal", '2', 0, "ui.frames arg:3 axis:0" },
1244 { "Horizontal 1:2", SDLK_d, 0, "ui.frames arg:1 axis:0" },
1245 { "Horizontal 2:1", SDLK_e, 0, "ui.frames arg:2 axis:0" },
1246 { "---", 0, 0, NULL },
1247 { "Vertical", '3', 0, "ui.frames arg:3 axis:1" },
1248 { "Vertical 1:2", SDLK_f, 0, "ui.frames arg:1 axis:1" },
1249 { "Vertical 2:1", SDLK_r, 0, "ui.frames arg:2 axis:1" },
1250 }, 9);
1251 setFlags_Widget(splitMenu, disabledWhenHidden_WidgetFlag, iTrue); /* enabled when open */
1234 setId_Widget(tabsMenu, "doctabs.menu"); 1252 setId_Widget(tabsMenu, "doctabs.menu");
1235 setId_Widget(barMenu, "barmenu"); 1253 setId_Widget(barMenu, "barmenu");
1236 setId_Widget(clipMenu, "clipmenu"); 1254 setId_Widget(clipMenu, "clipmenu");
1255 setId_Widget(splitMenu, "splitmenu");
1237 } 1256 }
1238 /* Global keyboard shortcuts. */ { 1257 /* Global keyboard shortcuts. */ {
1239 addAction_Widget(root, 'l', KMOD_PRIMARY, "navigate.focus"); 1258 addAction_Widget(root, 'l', KMOD_PRIMARY, "navigate.focus");
@@ -1248,6 +1267,7 @@ void createUserInterface_Root(iRoot *d) {
1248 addAction_Widget(root, '3', rightSidebar_KeyModifier, "sidebar2.mode arg:2 toggle:1"); 1267 addAction_Widget(root, '3', rightSidebar_KeyModifier, "sidebar2.mode arg:2 toggle:1");
1249 addAction_Widget(root, '4', rightSidebar_KeyModifier, "sidebar2.mode arg:3 toggle:1"); 1268 addAction_Widget(root, '4', rightSidebar_KeyModifier, "sidebar2.mode arg:3 toggle:1");
1250 addAction_Widget(root, '5', rightSidebar_KeyModifier, "sidebar2.mode arg:4 toggle:1"); 1269 addAction_Widget(root, '5', rightSidebar_KeyModifier, "sidebar2.mode arg:4 toggle:1");
1270 addAction_Widget(root, SDLK_j, KMOD_PRIMARY, "splitmenu.open");
1251 } 1271 }
1252 updateMetrics_Root(d); 1272 updateMetrics_Root(d);
1253} 1273}
diff --git a/src/ui/util.c b/src/ui/util.c
index a1d76709..fd8740b4 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -984,6 +984,14 @@ void prependTabPage_Widget(iWidget *tabs, iWidget *page, const char *label, int
984 addTabPage_Widget_(tabs, front_WidgetAddPos, page, label, key, kmods); 984 addTabPage_Widget_(tabs, front_WidgetAddPos, page, label, key, kmods);
985} 985}
986 986
987void moveTabButtonToEnd_Widget(iWidget *tabButton) {
988 iWidget *buttons = tabButton->parent;
989 iWidget *tabs = buttons->parent;
990 removeChild_Widget(buttons, tabButton);
991 addChild_Widget(buttons, iClob(tabButton));
992 arrange_Widget(tabs);
993}
994
987iWidget *tabPage_Widget(iWidget *tabs, size_t index) { 995iWidget *tabPage_Widget(iWidget *tabs, size_t index) {
988 iWidget *pages = findChild_Widget(tabs, "tabs.pages"); 996 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
989 return child_Widget(pages, index); 997 return child_Widget(pages, index);
diff --git a/src/ui/util.h b/src/ui/util.h
index 8dd16a7f..cbedefaa 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -246,6 +246,7 @@ void setTabPageLabel_Widget (iWidget *tabs, const iAnyObject *page,
246iWidget * tabPage_Widget (iWidget *tabs, size_t index); 246iWidget * tabPage_Widget (iWidget *tabs, size_t index);
247iLabelWidget * tabPageButton_Widget (iWidget *tabs, const iAnyObject *page); 247iLabelWidget * tabPageButton_Widget (iWidget *tabs, const iAnyObject *page);
248iBool isTabButton_Widget (const iWidget *); 248iBool isTabButton_Widget (const iWidget *);
249void moveTabButtonToEnd_Widget(iWidget *tabButton);
249size_t tabPageIndex_Widget (const iWidget *tabs, const iAnyObject *page); 250size_t tabPageIndex_Widget (const iWidget *tabs, const iAnyObject *page);
250const iWidget * currentTabPage_Widget (const iWidget *tabs); 251const iWidget * currentTabPage_Widget (const iWidget *tabs);
251size_t tabCount_Widget (const iWidget *tabs); 252size_t tabCount_Widget (const iWidget *tabs);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 590dbe70..e4d92b35 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -225,6 +225,13 @@ void setCommandHandler_Widget(iWidget *d, iBool (*handler)(iWidget *, const char
225 d->commandHandler = handler; 225 d->commandHandler = handler;
226} 226}
227 227
228void setRoot_Widget(iWidget *d, iRoot *root) {
229 d->root = root;
230 iForEach(ObjectList, i, d->children) {
231 setRoot_Widget(i.object, root);
232 }
233}
234
228static int numExpandingChildren_Widget_(const iWidget *d) { 235static int numExpandingChildren_Widget_(const iWidget *d) {
229 int count = 0; 236 int count = 0;
230 iConstForEach(ObjectList, i, d->children) { 237 iConstForEach(ObjectList, i, d->children) {
@@ -749,7 +756,8 @@ iLocalDef iBool isMouseEvent_(const SDL_Event *ev) {
749static iBool filterEvent_Widget_(const iWidget *d, const SDL_Event *ev) { 756static iBool filterEvent_Widget_(const iWidget *d, const SDL_Event *ev) {
750 const iBool isKey = isKeyboardEvent_(ev); 757 const iBool isKey = isKeyboardEvent_(ev);
751 const iBool isMouse = isMouseEvent_(ev); 758 const iBool isMouse = isMouseEvent_(ev);
752 if (d->flags & disabled_WidgetFlag) { 759 if ((d->flags & disabled_WidgetFlag) || (d->flags & hidden_WidgetFlag &&
760 d->flags & disabledWhenHidden_WidgetFlag)) {
753 if (isKey || isMouse) return iFalse; 761 if (isKey || isMouse) return iFalse;
754 } 762 }
755 if (d->flags & hidden_WidgetFlag) { 763 if (d->flags & hidden_WidgetFlag) {
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 2ef035b6..c3c9609e 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -116,6 +116,7 @@ enum iWidgetFlag {
116#define parentCannotResizeHeight_WidgetFlag iBit64(58) 116#define parentCannotResizeHeight_WidgetFlag iBit64(58)
117#define ignoreForParentWidth_WidgetFlag iBit64(59) 117#define ignoreForParentWidth_WidgetFlag iBit64(59)
118#define noFadeBackground_WidgetFlag iBit64(60) 118#define noFadeBackground_WidgetFlag iBit64(60)
119#define disabledWhenHidden_WidgetFlag iBit64(61)
119 120
120enum iWidgetAddPos { 121enum iWidgetAddPos {
121 back_WidgetAddPos, 122 back_WidgetAddPos,
@@ -247,6 +248,7 @@ void showCollapsed_Widget (iWidget *, iBool show); /* takes care of re
247void setBackgroundColor_Widget (iWidget *, int bgColor); 248void setBackgroundColor_Widget (iWidget *, int bgColor);
248void setFrameColor_Widget (iWidget *, int frameColor); 249void setFrameColor_Widget (iWidget *, int frameColor);
249void setCommandHandler_Widget (iWidget *, iBool (*handler)(iWidget *, const char *)); 250void setCommandHandler_Widget (iWidget *, iBool (*handler)(iWidget *, const char *));
251void setRoot_Widget (iWidget *, iRoot *root); /* updates the entire tree */
250iAny * addChild_Widget (iWidget *, iAnyObject *child); /* holds a ref */ 252iAny * addChild_Widget (iWidget *, iAnyObject *child); /* holds a ref */
251iAny * addChildPos_Widget (iWidget *, iAnyObject *child, enum iWidgetAddPos addPos); 253iAny * addChildPos_Widget (iWidget *, iAnyObject *child, enum iWidgetAddPos addPos);
252iAny * addChildPosFlags_Widget (iWidget *, iAnyObject *child, enum iWidgetAddPos addPos, int64_t childFlags); 254iAny * addChildPosFlags_Widget (iWidget *, iAnyObject *child, enum iWidgetAddPos addPos, int64_t childFlags);
diff --git a/src/ui/window.c b/src/ui/window.c
index 54f05a20..da1db514 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -161,7 +161,7 @@ static void removeMacMenus_(void) {
161} 161}
162#endif 162#endif
163 163
164static int numRoots_Window(const iWindow *d) { 164int numRoots_Window(const iWindow *d) {
165 int num = 0; 165 int num = 0;
166 iForIndices(i, d->roots) { 166 iForIndices(i, d->roots) {
167 if (d->roots[i]) num++; 167 if (d->roots[i]) num++;
@@ -173,27 +173,38 @@ static void setupUserInterface_Window(iWindow *d) {
173#if defined (iPlatformAppleDesktop) 173#if defined (iPlatformAppleDesktop)
174 insertMacMenus_(); 174 insertMacMenus_();
175#endif 175#endif
176 iForIndices(i, d->roots) { 176 /* One root is created by default. */
177 d->roots[i] = new_Root(); 177 d->roots[0] = new_Root();
178 setCurrent_Root(d->roots[i]); 178 setCurrent_Root(d->roots[0]);
179 createUserInterface_Root(d->roots[i]); 179 createUserInterface_Root(d->roots[0]);
180 setCurrent_Root(NULL); 180 setCurrent_Root(NULL);
181 }
182 /* One of the roots always has keyboard input focus. */ 181 /* One of the roots always has keyboard input focus. */
183 d->keyRoot = d->roots[0]; 182 d->keyRoot = d->roots[0];
184} 183}
185 184
186static void windowSizeChanged_Window_(iWindow *d) { 185static void windowSizeChanged_Window_(iWindow *d) {
187 const int numRoots = numRoots_Window(d); 186 const int numRoots = numRoots_Window(d);
188 /* Horizontal split frame. */
189 const iInt2 rootSize = d->size; 187 const iInt2 rootSize = d->size;
188 const int weights[2] = {
189 d->roots[0] ? (d->splitMode & twoToOne_WindowSplit ? 2 : 1) : 0,
190 d->roots[1] ? (d->splitMode & oneToTwo_WindowSplit ? 2 : 1) : 0,
191 };
192 const int totalWeight = weights[0] + weights[1];
193 int w = 0;
190 iForIndices(i, d->roots) { 194 iForIndices(i, d->roots) {
191 iRoot *root = d->roots[i]; 195 iRoot *root = d->roots[i];
192 if (root) { 196 if (root) {
193 iRect *rect = &root->widget->rect; 197 iRect *rect = &root->widget->rect;
194 /* Horizontal split frame. */ 198 /* Horizontal split frame. */
195 rect->pos = init_I2(rootSize.x * i / numRoots, 0); 199 if (d->splitMode & vertical_WindowSplit) {
196 rect->size = init_I2(rootSize.x * (i + 1) / numRoots - rect->pos.x, rootSize.y); 200 rect->pos = init_I2(0, rootSize.y * w / totalWeight);
201 rect->size = init_I2(rootSize.x, rootSize.y * (w + weights[i]) / totalWeight - rect->pos.y);
202 }
203 else {
204 rect->pos = init_I2(rootSize.x * w / totalWeight, 0);
205 rect->size = init_I2(rootSize.x * (w + weights[i]) / totalWeight - rect->pos.x, rootSize.y);
206 }
207 w += weights[i];
197 root->widget->minSize = rect->size; 208 root->widget->minSize = rect->size;
198 updatePadding_Root(root); 209 updatePadding_Root(root);
199 arrange_Widget(root->widget); 210 arrange_Widget(root->widget);
@@ -378,6 +389,7 @@ void init_Window(iWindow *d, iRect rect) {
378 d->win = NULL; 389 d->win = NULL;
379 d->size = zero_I2(); /* will be updated below */ 390 d->size = zero_I2(); /* will be updated below */
380 iZap(d->roots); 391 iZap(d->roots);
392 d->splitMode = d->pendingSplitMode = 0;
381 d->hover = NULL; 393 d->hover = NULL;
382 d->mouseGrab = NULL; 394 d->mouseGrab = NULL;
383 d->focus = NULL; 395 d->focus = NULL;
@@ -1004,7 +1016,8 @@ void draw_Window(iWindow *d) {
1004 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size }); 1016 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size });
1005 } 1017 }
1006#endif 1018#endif
1007 /* Root separator and keyboard focus indicator. */ { 1019 /* Root separator and keyboard focus indicator. */
1020 if (numRoots_Window(d) > 1){
1008 iPaint p; 1021 iPaint p;
1009 init_Paint(&p); 1022 init_Paint(&p);
1010 const iRect bounds = bounds_Widget(root->widget); 1023 const iRect bounds = bounds_Widget(root->widget);
@@ -1017,7 +1030,7 @@ void draw_Window(iWindow *d) {
1017 if (root == d->keyRoot) { 1030 if (root == d->keyRoot) {
1018 fillRect_Paint(&p, (iRect){ 1031 fillRect_Paint(&p, (iRect){
1019 topLeft_Rect(bounds), 1032 topLeft_Rect(bounds),
1020 init_I2(width_Rect(bounds), gap_UI / 2) 1033 init_I2(width_Rect(bounds), gap_UI / 2)
1021 }, uiBackgroundSelected_ColorId); 1034 }, uiBackgroundSelected_ColorId);
1022 } 1035 }
1023 } 1036 }
@@ -1037,8 +1050,13 @@ void draw_Window(iWindow *d) {
1037} 1050}
1038 1051
1039void resize_Window(iWindow *d, int w, int h) { 1052void resize_Window(iWindow *d, int w, int h) {
1040 SDL_SetWindowSize(d->win, w, h); 1053 if (w > 0 && h > 0) {
1041 updateSize_Window_(d, iFalse); 1054 SDL_SetWindowSize(d->win, w, h);
1055 updateSize_Window_(d, iFalse);
1056 }
1057 else {
1058 updateSize_Window_(d, iTrue); /* notify always */
1059 }
1042} 1060}
1043 1061
1044void setTitle_Window(iWindow *d, const iString *title) { 1062void setTitle_Window(iWindow *d, const iString *title) {
@@ -1126,6 +1144,51 @@ void setKeyboardHeight_Window(iWindow *d, int height) {
1126 } 1144 }
1127} 1145}
1128 1146
1147void setSplitMode_Window(iWindow *d, int splitMode) {
1148 iAssert(current_Root() == NULL);
1149 if (d->splitMode != splitMode) {
1150 int oldCount = numRoots_Window(d);
1151 setFreezeDraw_Window(d, iTrue);
1152 if (oldCount == 2 && splitMode == 0) {
1153 /* Keep references to the tabs of the second root. */
1154 iObjectList *tabs = listDocuments_App(d->roots[1]);
1155 iForEach(ObjectList, i, tabs) {
1156 setRoot_Widget(i.object, d->roots[0]);
1157 }
1158 delete_Root(d->roots[1]);
1159 d->roots[1] = NULL;
1160 d->keyRoot = d->roots[0];
1161 /* Move the deleted root's tabs to the first root. */
1162 setCurrent_Root(d->roots[0]);
1163 iWidget *docTabs = findWidget_Root("doctabs");
1164 iForEach(ObjectList, j, tabs) {
1165 appendTabPage_Widget(docTabs, j.object, "", 0, 0);
1166 }
1167 /* The last child is the [+] button for adding a tab. */
1168 moveTabButtonToEnd_Widget(findChild_Widget(docTabs, "newtab"));
1169 iRelease(tabs);
1170 }
1171 else if ((splitMode & mask_WindowSplit) && oldCount == 1) {
1172 /* Add a second root. */
1173 iAssert(d->roots[1] == NULL);
1174 d->roots[1] = new_Root();
1175 setCurrent_Root(d->roots[1]);
1176 createUserInterface_Root(d->roots[1]);
1177 /* If the old root has multiple tabs, move the current one to the new split. */
1178
1179 postCommand_Root(d->roots[1], "navigate.home");
1180 setCurrent_Root(NULL);
1181 }
1182 d->splitMode = splitMode;
1183// windowSizeChanged_Window_(d);
1184 updateSize_Window_(d, iTrue);
1185// postCommand_App("window.resized");
1186 // postCommand_App("metrics.resized");
1187// postCommand_App("window.updatelayout");
1188 postCommand_App("window.unfreeze");
1189 }
1190}
1191
1129void setSnap_Window(iWindow *d, int snapMode) { 1192void setSnap_Window(iWindow *d, int snapMode) {
1130 if (!prefs_App()->customFrame) { 1193 if (!prefs_App()->customFrame) {
1131 if (snapMode == maximized_WindowSnap) { 1194 if (snapMode == maximized_WindowSnap) {
diff --git a/src/ui/window.h b/src/ui/window.h
index b2b22e90..ad577ce4 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -56,6 +56,17 @@ struct Impl_WindowPlacement {
56 int lastHit; 56 int lastHit;
57}; 57};
58 58
59enum iWindowSplit {
60 vertical_WindowSplit = iBit(1),
61 oneToTwo_WindowSplit = iBit(2),
62 twoToOne_WindowSplit = iBit(3),
63 equal_WindowSplit = oneToTwo_WindowSplit | twoToOne_WindowSplit,
64 /* meta */
65 mode_WindowSplit = vertical_WindowSplit | equal_WindowSplit,
66 mask_WindowSplit = equal_WindowSplit,
67 merge_WindowSplit = iBit(10),
68};
69
59struct Impl_Window { 70struct Impl_Window {
60 SDL_Window * win; 71 SDL_Window * win;
61 iWindowPlacement place; 72 iWindowPlacement place;
@@ -67,6 +78,8 @@ struct Impl_Window {
67 uint32_t focusGainedAt; 78 uint32_t focusGainedAt;
68 SDL_Renderer *render; 79 SDL_Renderer *render;
69 iInt2 size; 80 iInt2 size;
81 int splitMode;
82 int pendingSplitMode;
70 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */ 83 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */
71 iRoot * keyRoot; /* root that has the current keyboard input focus */ 84 iRoot * keyRoot; /* root that has the current keyboard input focus */
72 iWidget * hover; 85 iWidget * hover;
@@ -98,6 +111,7 @@ iBool setKeyRoot_Window (iWindow *, iRoot *root);
98void setCursor_Window (iWindow *, int cursor); 111void setCursor_Window (iWindow *, int cursor);
99void setSnap_Window (iWindow *, int snapMode); 112void setSnap_Window (iWindow *, int snapMode);
100void setKeyboardHeight_Window(iWindow *, int height); 113void setKeyboardHeight_Window(iWindow *, int height);
114void setSplitMode_Window (iWindow *, int splitMode);
101void showToolbars_Window (iWindow *, iBool show); 115void showToolbars_Window (iWindow *, iBool show);
102iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *); 116iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *);
103 117
@@ -112,6 +126,7 @@ uint32_t frameTime_Window (const iWindow *);
112SDL_Renderer *renderer_Window (const iWindow *); 126SDL_Renderer *renderer_Window (const iWindow *);
113int snap_Window (const iWindow *); 127int snap_Window (const iWindow *);
114iBool isFullscreen_Window (const iWindow *); 128iBool isFullscreen_Window (const iWindow *);
129int numRoots_Window (const iWindow *);
115iRoot * findRoot_Window (const iWindow *, const iWidget *widget); 130iRoot * findRoot_Window (const iWindow *, const iWidget *widget);
116iRoot * otherRoot_Window (const iWindow *, iRoot *root); 131iRoot * otherRoot_Window (const iWindow *, iRoot *root);
117 132