aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sway/container.h17
-rw-r--r--include/sway/layout.h3
-rw-r--r--sway/commands/layout.c20
-rw-r--r--sway/container.c2
-rw-r--r--sway/debug_log.c4
-rw-r--r--sway/layout.c426
-rw-r--r--sway/sway.5.txt3
7 files changed, 367 insertions, 108 deletions
diff --git a/include/sway/container.h b/include/sway/container.h
index 2bedd136..1d0fb265 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -37,6 +37,13 @@ enum swayc_layouts {
L_STACKED,
L_TABBED,
L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */
+
+ /* Awesome/Monad style auto layouts */
+ L_AUTO_LEFT,
+ L_AUTO_RIGHT,
+ L_AUTO_TOP,
+ L_AUTO_BOTTOM,
+
// Keep last
L_LAYOUTS,
};
@@ -144,6 +151,16 @@ struct sway_container {
struct wlc_geometry title_bar_geometry;
struct wlc_geometry actual_geometry;
int border_thickness;
+
+ /**
+ * Number of master views in auto layouts.
+ */
+ uint32_t nb_master;
+
+ /**
+ * Number of slave groups (e.g. columns) in auto layouts.
+ */
+ uint32_t nb_slave_groups;
};
enum visibility_mask {
diff --git a/include/sway/layout.h b/include/sway/layout.h
index b982365c..38096947 100644
--- a/include/sway/layout.h
+++ b/include/sway/layout.h
@@ -75,4 +75,7 @@ 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);
+}
#endif
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 08336150..e6fa7ef1 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -54,6 +54,26 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
} else {
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);
}
}
diff --git a/sway/container.c b/sway/container.c
index e557f450..d034115f 100644
--- a/sway/container.c
+++ b/sway/container.c
@@ -32,6 +32,8 @@ static swayc_t *new_swayc(enum swayc_types type) {
c->layout = L_NONE;
c->workspace_layout = L_NONE;
c->type = type;
+ c->nb_master = 1;
+ c->nb_slave_groups = 1;
if (type != C_VIEW) {
c->children = create_list();
}
diff --git a/sway/debug_log.c b/sway/debug_log.c
index 8d891a44..d1eafae8 100644
--- a/sway/debug_log.c
+++ b/sway/debug_log.c
@@ -38,6 +38,10 @@ static void container_log(const swayc_t *c, int depth) {
c->layout == L_STACKED ? "Stack":
c->layout == L_TABBED ? "Tab":
c->layout == L_FLOATING ? "Float":
+ c->layout == L_AUTO_LEFT ? "A_lft":
+ c->layout == L_AUTO_RIGHT ? "A_rgt":
+ c->layout == L_AUTO_TOP ? "A_top":
+ c->layout == L_AUTO_BOTTOM ? "A_bot":
"Unknown");
fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y);
diff --git a/sway/layout.c b/sway/layout.c
index ea4a680d..52473d0d 100644
--- a/sway/layout.c
+++ b/sway/layout.c
@@ -118,7 +118,11 @@ swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
list_add(parent->floating, active);
} else {
int i = index_child(fixed);
- list_insert(parent->children, i + 1, active);
+ if (is_auto_layout(parent->layout)) {
+ list_add(parent->children, active);
+ } else {
+ list_insert(parent->children, i + 1, active);
+ }
}
}
active->parent = parent;
@@ -769,6 +773,26 @@ void update_geometry(swayc_t *container) {
}
}
+/**
+ * Layout application prototypes
+ */
+static void apply_horiz_layout(swayc_t *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+static void apply_vert_layout(swayc_t *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
+ double y, double width,
+ double height);
+
+static void apply_auto_layout(swayc_t *container, const double x, const double y,
+ const double width, const double height,
+ enum swayc_layouts group_layout,
+ bool master_first);
+
static void arrange_windows_r(swayc_t *container, double width, double height) {
int i;
if (width == -1 || height == -1) {
@@ -776,14 +800,15 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
width = container->width;
height = container->height;
}
- // pixels are indivisable. if we don't round the pixels, then the view
+ // pixels are indivisible. if we don't round the pixels, then the view
// calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
// 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
width = floor(width);
height = floor(height);
sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
- container->name, container->width, container->height, container->x, container->y);
+ container->name, container->width, container->height, container->x,
+ container->y);
double x = 0, y = 0;
switch (container->type) {
@@ -895,136 +920,323 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
break;
}
- double scale = 0;
switch (container->layout) {
case L_HORIZ:
default:
- // Calculate total width
- for (i = 0; i < container->children->length; ++i) {
- double *old_width = &((swayc_t *)container->children->items[i])->width;
- if (*old_width <= 0) {
- if (container->children->length > 1) {
- *old_width = width / (container->children->length - 1);
- } else {
- *old_width = width;
+ apply_horiz_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ case L_VERT:
+ apply_vert_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ case L_TABBED:
+ case L_STACKED:
+ apply_tabbed_or_stacked_layout(container, x, y, width, height);
+ break;
+ case L_AUTO_LEFT:
+ apply_auto_layout(container, x, y, width, height, L_VERT, true);
+ break;
+ case L_AUTO_RIGHT:
+ apply_auto_layout(container, x, y, width, height, L_VERT, false);
+ break;
+ case L_AUTO_TOP:
+ apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
+ break;
+ case L_AUTO_BOTTOM:
+ apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
+ break;
+ }
+
+ // Arrage floating layouts for workspaces last
+ if (container->type == C_WORKSPACE) {
+ for (int i = 0; i < container->floating->length; ++i) {
+ swayc_t *view = container->floating->items[i];
+ if (view->type == C_VIEW) {
+ update_geometry(view);
+ sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
+ view->width, view->height, view->x, view->y);
+ if (swayc_is_fullscreen(view)) {
+ wlc_view_bring_to_front(view->handle);
+ } else if (!container->focused ||
+ !swayc_is_fullscreen(container->focused)) {
+ wlc_view_bring_to_front(view->handle);
}
}
- scale += *old_width;
}
+ }
+}
- // Resize windows
- if (scale > 0.1) {
- scale = width / scale;
- sway_log(L_DEBUG, "Arranging %p horizontally", container);
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale);
- child->x = x;
- child->y = y;
-
- if (child == container->focused) {
- focused = child;
- }
+void apply_horiz_layout(swayc_t *container, const double x, const double y,
+ const double width, const double height,
+ const int start, const int end) {
+ double scale = 0;
+ // Calculate total width
+ for (int i = start; i < end; ++i) {
+ double *old_width = &((swayc_t *)container->children->items[i])->width;
+ if (*old_width <= 0) {
+ if (end - start > 1) {
+ *old_width = width / (end - start - 1);
+ } else {
+ *old_width = width;
+ }
+ }
+ scale += *old_width;
+ }
- if (i == container->children->length - 1) {
- double remaining_width = container->x + width - x;
- arrange_windows_r(child, remaining_width, height);
- } else {
- arrange_windows_r(child, child->width * scale, height);
- }
- x += child->width;
+ // Resize windows
+ double child_x = x;
+ if (scale > 0.1) {
+ scale = width / scale;
+ sway_log(L_DEBUG, "Arranging %p horizontally", container);
+ swayc_t *focused = NULL;
+ for (int i = start; i < end; ++i) {
+ swayc_t *child = container->children->items[i];
+ sway_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)", child,
+ child->type, width, scale);
+ child->x = child_x;
+ child->y = y;
+
+ if (child == container->focused) {
+ focused = child;
}
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
+ if (i == end - 1) {
+ double remaining_width = x + width - child_x;
+ arrange_windows_r(child, remaining_width, height);
+ } else {
+ arrange_windows_r(child, child->width * scale, height);
}
+ child_x += child->width;
}
- break;
- case L_VERT:
- // Calculate total height
- for (i = 0; i < container->children->length; ++i) {
- double *old_height = &((swayc_t *)container->children->items[i])->height;
- if (*old_height <= 0) {
- if (container->children->length > 1) {
- *old_height = height / (container->children->length - 1);
- } else {
- *old_height = height;
- }
- }
- scale += *old_height;
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
}
- // Resize
- if (scale > 0.1) {
- scale = height / scale;
- sway_log(L_DEBUG, "Arranging %p vertically", container);
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale);
- child->x = x;
- child->y = y;
-
- if (child == container->focused) {
- focused = child;
- }
+ }
+}
- if (i == container->children->length - 1) {
- double remaining_height = container->y + height - y;
- arrange_windows_r(child, width, remaining_height);
- } else {
- arrange_windows_r(child, width, child->height * scale);
- }
- y += child->height;
+void apply_vert_layout(swayc_t *container, const double x, const double y,
+ const double width, const double height, const int start,
+ const int end) {
+ int i;
+ double scale = 0;
+ // Calculate total height
+ for (i = start; i < end; ++i) {
+ double *old_height = &((swayc_t *)container->children->items[i])->height;
+ if (*old_height <= 0) {
+ if (end - start > 1) {
+ *old_height = height / (end - start - 1);
+ } else {
+ *old_height = height;
+ }
+ }
+ scale += *old_height;
+ }
+ // Resize
+ double child_y = y;
+ if (scale > 0.1) {
+ scale = height / scale;
+ sway_log(L_DEBUG, "Arranging %p vertically", container);
+ swayc_t *focused = NULL;
+ for (i = start; i < end; ++i) {
+ swayc_t *child = container->children->items[i];
+ sway_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)", child,
+ child->type, height, scale);
+ child->x = x;
+ child->y = child_y;
+
+ if (child == container->focused) {
+ focused = child;
}
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
+ if (i == end - 1) {
+ double remaining_height = y + height - child_y;
+ arrange_windows_r(child, width, remaining_height);
+ } else {
+ arrange_windows_r(child, width, child->height * scale);
}
+ child_y += child->height;
+ }
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
+ }
+ }
+}
+
+void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
+ double width, double height) {
+ int i;
+ swayc_t *focused = NULL;
+ for (i = 0; i < container->children->length; ++i) {
+ swayc_t *child = container->children->items[i];
+ child->x = x;
+ child->y = y;
+ if (child == container->focused) {
+ focused = child;
+ } else {
+ arrange_windows_r(child, width, height);
}
+ }
+
+ if (focused) {
+ arrange_windows_r(focused, width, height);
+ }
+}
+
+void apply_auto_layout(swayc_t *container, const double x, const double y,
+ const double width, const double height,
+ enum swayc_layouts group_layout,
+ bool master_first) {
+ // Auto layout "container" in width x height @ x, y
+ // using "group_layout" for each of the groups in the container.
+ // There is one "master" group, plus container->nb_slave_groups.
+ // Each group is layed out side by side following the "major" axis.
+ // The direction of the layout used for groups is the "minor" axis.
+ // Example:
+ //
+ // ---- major axis -->
+ // +---------+-----------+
+ // | | | |
+ // | master | slave 1 | |
+ // | +-----------+ | minor axis (direction of group_layout)
+ // | | | |
+ // | | slave 2 | V
+ // +---------+-----------+
+ //
+ // container with three children (one master and two slaves) and
+ // a single slave group (containing slave 1 and 2). The master
+ // group and slave group are layed out using L_VERT.
+
+ uint_fast32_t nb_slaves = container->children->length - container->nb_master;
+ uint_fast32_t nb_groups = (container->nb_master > 0 ? 1 : 0) +
+ MIN(container->nb_slave_groups, nb_slaves);
+
+ // the target dimension of the container along the "major" axis, each
+ // group in the container will be layed out using "group_layout" along
+ // the "minor" axis.
+ double dim_maj;
+ double pos_maj;
+
+ // x and y coords for the next group to be laid out.
+ const double *group_x, *group_y;
+
+ // pos of the next group to layout along the major axis
+ double pos;
+
+ // size of the next group along the major axis.
+ double group_dim;
+
+ // height and width of next group to be laid out.
+ const double *group_h, *group_w;
+
+ switch(group_layout) {
+ default:
+ sway_log(L_ERROR, "Unknown layout type (%d) used in %s()",
+ group_layout, __func__);
+ /* fall through */
+ case L_VERT:
+ dim_maj = width;
+ pos_maj = x;
+
+ group_x = &pos;
+ group_y = &y;
+ group_w = &group_dim;
+ group_h = &height;
break;
- case L_TABBED:
- case L_STACKED:
- {
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- child->x = x;
- child->y = y;
- if (child == container->focused) {
- focused = child;
- } else {
- arrange_windows_r(child, width, height);
- }
- }
+ case L_HORIZ:
+ dim_maj = height;
+ pos_maj = y;
- if (focused) {
- arrange_windows_r(focused, width, height);
+ group_x = &x;
+ group_y = &pos;
+ group_w = &width;
+ group_h = &group_dim;
+ break;
+ }
+
+ /* Determine the dimension of each of the groups in the layout.
+ * Dimension will be width for a VERT layout and height for a HORIZ
+ * layout. */
+ double old_group_dim[nb_groups];
+ double old_dim = 0;
+ uint_fast32_t group = 0;
+ for (int i = 0; i < container->children->length;) {
+ swayc_t *child = container->children->items[i];
+ double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
+ if (*dim <= 0) {
+ // New child with uninitialized dimension
+ *dim = dim_maj;
+ if (nb_groups > 1) {
+ // child gets a dimension proportional to existing groups,
+ // it will be later scaled based on to the available size
+ // in the major axis.
+ *dim /= (nb_groups - 1);
}
- break;
}
+ if (i == 0 && container->nb_master > 0) {
+ i += container->nb_master;
+ } else {
+ i += (nb_slaves - i + container->nb_master) / (nb_groups - group);
+ }
+ old_dim += *dim;
+ old_group_dim[group++] = *dim;
}
- // Arrage floating layouts for workspaces last
- if (container->type == C_WORKSPACE) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *view = container->floating->items[i];
- if (view->type == C_VIEW) {
- update_geometry(view);
- sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", view->width,
- view->height, view->x, view->y);
- if (swayc_is_fullscreen(view)) {
- wlc_view_bring_to_front(view->handle);
- } else if (!container->focused
- || !swayc_is_fullscreen(container->focused)) {
- wlc_view_bring_to_front(view->handle);
- }
+ double scale = dim_maj / old_dim;
+
+ /* Apply layout to each group */
+ pos = pos_maj;
+
+ // first child in the current group
+ int start;
+
+ // index immediately after the last child in the current group
+ int end = 0;
+
+ for (group = 0; group < nb_groups; ++group) {
+ // column to include next by increasing position.
+ uint_fast32_t layout_group = master_first ? group : (group + 1) % nb_groups;
+
+ // adjusted size of the group
+ group_dim = old_group_dim[layout_group] * scale;
+ if (container->nb_master > 0 && layout_group == 0) {
+ start = 0;
+ end = container->nb_master;
+ } else {
+ if (group == 0) {
+ start = container->nb_master;
+ } else {
+ start = end;
}
+ end = start +
+ (nb_slaves - start + container->nb_master) / (nb_groups - layout_group);
}
+ if (group == nb_groups - 1) {
+ group_dim = pos_maj + dim_maj - pos; // remaining width
+ }
+ sway_log(L_DEBUG, "Arranging container %p column %" PRIuFAST32
+ ", children [%d,%d[ (%fx%f+%f,%f)",
+ container, group, start, end, *group_w, *group_h, *group_x, *group_y);
+ switch (group_layout) {
+ default:
+ case L_VERT:
+ apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
+ break;
+ case L_HORIZ:
+ apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
+ break;
+ }
+
+ /* update position for next group */
+ pos += group_dim;
}
}
diff --git a/sway/sway.5.txt b/sway/sway.5.txt
index 7980ba82..1f25ce14 100644
--- a/sway/sway.5.txt
+++ b/sway/sway.5.txt
@@ -81,7 +81,8 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
**layout** <mode>::
Sets the layout mode of the focused container. _mode_ can be one of _splith_,
- _splitv_, _toggle split_, _stacking_ or _tabbed_.
+ _splitv_, _toggle split_, _stacking_, _tabbed_, _auto_left_, _auto_right_,
+ _auto_top, _auto_bottom_.
**move** <left|right|up|down>::
Moves the focused container _left_, _right_, _up_, or _down_.