aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOrestis Floros <orestisflo@gmail.com>2024-01-31 08:14:32 +0100
committerGitHub <noreply@github.com>2024-01-31 08:14:32 +0100
commitf8befe378ab3c9f8d035657ca0247c9b8c242b6b (patch)
tree01529a5c9ca721d5c253f20edd5e601239fd9e3a
parent230147c81547848c83ac7764a6c1691f26f10e05 (diff)
downloadi3-f8befe378ab3c9f8d035657ca0247c9b8c242b6b.tar.gz
i3-f8befe378ab3c9f8d035657ca0247c9b8c242b6b.zip
Avoid creating redundant containers when switching between tabbed/stacked and split layouts (#5469)
Fixes #3001
-rw-r--r--release-notes/changes/3-redundant-containers1
-rw-r--r--src/con.c25
-rw-r--r--testcases/t/550-split-redundant-containers.t82
3 files changed, 108 insertions, 0 deletions
diff --git a/release-notes/changes/3-redundant-containers b/release-notes/changes/3-redundant-containers
new file mode 100644
index 00000000..cd6ab9f3
--- /dev/null
+++ b/release-notes/changes/3-redundant-containers
@@ -0,0 +1 @@
+avoid creating redundant containers when switching between tabbed/stacked and split layouts
diff --git a/src/con.c b/src/con.c
index aebc46cf..917e797a 100644
--- a/src/con.c
+++ b/src/con.c
@@ -2063,6 +2063,31 @@ void con_set_layout(Con *con, layout_t layout) {
}
}
+ if (con->type != CT_WORKSPACE && con->parent->type != CT_WORKSPACE &&
+ con_num_children(con) == 1 && con_num_children(con->parent) == 1) {
+ /* Special case: Avoid creating redundant containers (#3001):
+ * split h / v (tree_split()) will avoid creating new containers when
+ * the target container is already a single child in L_SPLITH /
+ * L_SPLITV. However, if the layout is tabbed / stacked, a new split is
+ * created. This means, however, that when the user continuously
+ * switches between split h/v and tabbed / stacked, an endless series
+ * of 1-child containers will be created. Since a single level of split
+ * containers on top of tabbed / stacked containers are useful, we want
+ * to avoid this situation here.
+ * Example of past behaviour: S[V[w]] -> S[S[w]] -> S[S[V[w]]] -> …
+ * Example of desired behaviour: S[V[w]] -> S[w] -> S[v[w]] -> …
+ * Therefore, when both the current & parent containers have a single
+ * child, we just close the redundant middle container and proceed with
+ * the parent. */
+ Con *parent = con->parent;
+ Con *child = TAILQ_FIRST(&(con->nodes_head));
+ con_detach(child);
+ con_attach(child, parent, true);
+ parent->last_split_layout = con->last_split_layout;
+ tree_close_internal(con, DONT_KILL_WINDOW, true);
+ con = parent;
+ }
+
if (layout == L_DEFAULT) {
/* Special case: the layout formerly known as "default" (in combination
* with an orientation). Since we switched to splith/splitv layouts,
diff --git a/testcases/t/550-split-redundant-containers.t b/testcases/t/550-split-redundant-containers.t
new file mode 100644
index 00000000..3dfef399
--- /dev/null
+++ b/testcases/t/550-split-redundant-containers.t
@@ -0,0 +1,82 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • https://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • https://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • https://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that splitting and stacked/tabbed layouts do not create redundant
+# containers.
+# Ticket: #3001
+# Bug still in: 4.22-24-ga5da4d54
+use i3test;
+
+cmp_tree(
+ msg => 'toggling between split h/v',
+ layout_before => 'H[a*]',
+ layout_after => 'V[a*]',
+ cb => sub {
+ cmd 'split v, split h, split v';
+ });
+cmp_tree(
+ msg => 'toggling between tabbed/stacked',
+ layout_before => 'H[a*]',
+ layout_after => 'S[a*]',
+ cb => sub {
+ cmd 'layout tabbed, layout stacked';
+ });
+cmp_tree(
+ msg => 'split h to v and then tabbed',
+ layout_before => 'H[a*]',
+ layout_after => 'T[a*]',
+ cb => sub {
+ cmd 'split v, layout tabbed';
+ });
+cmp_tree(
+ msg => 'repeat tabbed layout',
+ layout_before => 'H[a*]',
+ layout_after => 'T[a*]',
+ cb => sub {
+ cmd 'layout tabbed' for 1..5;
+ });
+cmp_tree(
+ msg => 'split v inside tabbed',
+ layout_before => 'H[a*]',
+ layout_after => 'T[V[a*]]',
+ cb => sub {
+ cmd 'layout tabbed, split v';
+ });
+cmp_tree(
+ msg => 'split v inside tabbed and back to just tabbed',
+ layout_before => 'H[a*]',
+ layout_after => 'T[a*]',
+ cb => sub {
+ cmd 'layout tabbed, split v, layout tabbed';
+ });
+cmp_tree(
+ msg => 'toggle split v inside tabbed',
+ layout_before => 'H[a*]',
+ layout_after => 'T[V[a*]]',
+ cb => sub {
+ cmd 'layout tabbed, split v, layout tabbed, split v';
+ });
+cmp_tree(
+ msg => 'tabbed with 2 nodes inside other tabbed',
+ layout_before => 'T[a*]',
+ layout_after => 'T[T[a b*]]',
+ cb => sub {
+ cmd 'split v';
+ open_window(wm_class => "b", name => "b");
+ cmd 'layout tabbed';
+ });
+
+done_testing;