From a0aa8d9780c6c8b0138800e3b2c2c0053174a2c5 Mon Sep 17 00:00:00 2001
From: wil <william.barsse@gmail.com>
Date: Thu, 29 Dec 2016 20:26:35 +0100
Subject: cleanup in auto layouts

- added L_AUTO_FIRST/LAST instead of using explicit layouts.
- when switching between auto layout that don't share the same major axis, invert the
  width/height of their child views to preserve their relative proportions.
---
 include/sway/container.h         |  3 +++
 include/sway/layout.h            |  2 +-
 sway/commands/layout.c           | 48 ++++++++++++++++++++++++++++++----------
 sway/commands/workspace_layout.c | 10 ++++++++-
 sway/container.c                 | 23 ++++++++++++++++---
 sway/sway.5.txt                  |  2 +-
 6 files changed, 70 insertions(+), 18 deletions(-)

diff --git a/include/sway/container.h b/include/sway/container.h
index 1d0fb265..f0574b1b 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -44,6 +44,9 @@ enum swayc_layouts {
 	L_AUTO_TOP,
 	L_AUTO_BOTTOM,
 
+	L_AUTO_FIRST = L_AUTO_LEFT,
+	L_AUTO_LAST = L_AUTO_BOTTOM,
+
 	// Keep last
 	L_LAYOUTS,
 };
diff --git a/include/sway/layout.h b/include/sway/layout.h
index 38096947..a771a72e 100644
--- a/include/sway/layout.h
+++ b/include/sway/layout.h
@@ -76,6 +76,6 @@ void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ..
 enum swayc_layouts default_layout(swayc_t *output);
 
 inline bool is_auto_layout(enum swayc_layouts layout) {
-	return (layout >= L_AUTO_LEFT) && (layout <= L_AUTO_BOTTOM);
+	return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
 }
 #endif
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 9e468c21..5426186e 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -56,24 +56,12 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
 				swayc_change_layout(parent, L_HORIZ);
 			}
 		} else if (strcasecmp(argv[0], "auto_left") == 0) {
-			if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
-				parent = new_container(parent, L_AUTO_LEFT);
-			}
 			swayc_change_layout(parent, L_AUTO_LEFT);
 		} else if (strcasecmp(argv[0], "auto_right") == 0) {
-			if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
-				parent = new_container(parent, L_AUTO_RIGHT);
-			}
 			swayc_change_layout(parent, L_AUTO_RIGHT);
 		} else if (strcasecmp(argv[0], "auto_top") == 0) {
-			if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
-				parent = new_container(parent, L_AUTO_TOP);
-			}
 			swayc_change_layout(parent, L_AUTO_TOP);
 		} else if (strcasecmp(argv[0], "auto_bot") == 0) {
-			if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
-				parent = new_container(parent, L_AUTO_BOTTOM);
-			}
 			swayc_change_layout(parent, L_AUTO_BOTTOM);
 		} else if (strcasecmp(argv[0], "incnmaster") == 0)  {
 			if ((error = checkarg(argc, "layout incnmaster",
@@ -105,6 +93,42 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
 			    ((int)container->parent->nb_slave_groups + inc >= 1)) {
 				container->parent->nb_slave_groups += inc;
 			}
+		} else if (strcasecmp(argv[0], "auto") == 0) {
+			if ((error = checkarg(argc, "auto", EXPECTED_EQUAL_TO, 2))) {
+				return error;
+			}
+			swayc_t *container = get_focused_view(swayc_active_workspace());
+			swayc_t *parent = container->parent;
+			enum swayc_layouts layout;
+			if (strcasecmp(argv[1], "next") == 0) {
+				if (is_auto_layout(parent->layout) && parent->layout < L_AUTO_LAST) {
+					layout = parent->layout + 1;
+				} else {
+					layout = L_AUTO_FIRST;
+				}
+			} else if (strcasecmp(argv[1], "prev") == 0) {
+				if (is_auto_layout(parent->layout) && parent->layout > L_AUTO_FIRST) {
+					layout = parent->layout - 1;
+				} else {
+					layout = L_AUTO_FIRST;
+				}
+			} else {
+				return cmd_results_new(CMD_FAILURE, "layout auto",
+						       "Must be one of <prev|next>.");
+			}
+			swayc_change_layout(parent, layout);
+		} else if (strcasecmp(argv[0], "promote") == 0) {
+			// swap first child in auto layout with currently focused child
+			swayc_t *container = get_focused_view(swayc_active_workspace());
+			swayc_t *parent = container->parent;
+			if (is_auto_layout(parent->layout)) {
+				int focused_idx = index_child(container);
+				swayc_t *first = parent->children->items[0];
+				if (focused_idx > 0) {
+					list_swap(parent->children, 0, focused_idx);
+					swap_geometry(first, container);
+				}
+			}
 		}
 	}
 
diff --git a/sway/commands/workspace_layout.c b/sway/commands/workspace_layout.c
index b7b4b033..3e0a12ce 100644
--- a/sway/commands/workspace_layout.c
+++ b/sway/commands/workspace_layout.c
@@ -13,8 +13,16 @@ struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
 		config->default_layout = L_STACKED;
 	} else if (strcasecmp(argv[0], "tabbed") == 0) {
 		config->default_layout = L_TABBED;
+	} else if (strcasecmp(argv[0], "auto_left") == 0) {
+		config->default_layout = L_AUTO_LEFT;
+	} else if (strcasecmp(argv[0], "auto_right") == 0) {
+		config->default_layout = L_AUTO_RIGHT;
+	} else if (strcasecmp(argv[0], "auto_top") == 0) {
+		config->default_layout = L_AUTO_TOP;
+	} else if (strcasecmp(argv[0], "auto_bottom") == 0) {
+		config->default_layout = L_AUTO_BOTTOM;
 	} else {
-		return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed>'");
+		return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto_left|auto_right|auto_top|auto_bottom>'");
 	}
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/container.c b/sway/container.c
index d034115f..3bdc8b6c 100644
--- a/sway/container.c
+++ b/sway/container.c
@@ -962,11 +962,28 @@ swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
 }
 
 swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
+	// if layout change modifies the auto layout's major axis, swap width and height
+	// to preserve current ratios.
+	if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
+		enum swayc_layouts prev_major = (container->layout == L_AUTO_LEFT ||
+						 container->layout == L_AUTO_RIGHT)
+			? L_HORIZ : L_VERT;
+		enum swayc_layouts new_major = (layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT)
+			? L_HORIZ : L_VERT;
+		if (new_major != prev_major) {
+			for (int i = 0; i < container->children->length; ++i) {
+				swayc_t *child = container->children->items[i];
+				double h = child->height;
+				child->height = child->width;
+				child->width = h;
+			}
+		}
+	}
 	if (container->type == C_WORKSPACE) {
 		container->workspace_layout = layout;
-    if (layout == L_HORIZ || layout == L_VERT) {
-      container->layout = layout;
-    }
+		if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
+			container->layout = layout;
+		}
 	} else {
 		container->layout = layout;
 	}
diff --git a/sway/sway.5.txt b/sway/sway.5.txt
index 8f8302f0..c0c4bfb2 100644
--- a/sway/sway.5.txt
+++ b/sway/sway.5.txt
@@ -371,7 +371,7 @@ The default colors are:
 	switch to workspace 2, then invoke the "workspace 2" command again, you
 	will be returned to workspace 1. Defaults to _no_.
 
-**workspace_layout** <default|stacking|tabbed>::
+**workspace_layout** <default|stacking|tabbed|auto_left|auto_right|auto_top|auto_bottom>::
 	Specifies the start layout for new workspaces.
 
 **include** <path>::
-- 
cgit v1.2.3