Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Awesome/Monad style automatic layouts to Sway #1024

Merged
merged 36 commits into from
Jan 14, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
97f7d47
Added Awesome/Monad type "auto" layouts
willakat Dec 10, 2016
5425d04
Handle resize in auto layouts
willakat Dec 19, 2016
8b0073b
Added "layout incnmaster|incncol" commands
willakat Dec 21, 2016
ed71e67
[fix] handle cases where nb_master > children->length in auto layout
willakat Dec 21, 2016
bc3dc97
[fix] Handle auto layout resize with multiple slave groups
willakat Dec 21, 2016
0ff9fe9
introduce next/prev as a direction for focus/move commands.
willakat Dec 22, 2016
2b0e3c2
[fix] move next/prev behavior for vert/horiz layout
willakat Dec 22, 2016
a0aa8d9
cleanup in auto layouts
willakat Dec 29, 2016
1b87193
Added "layout promote" command.
willakat Dec 29, 2016
4b1d9b0
Added a word in the Readme about the purpose of the fork.
willakat Dec 30, 2016
15745ab
[fix] cycle auto layouts backwards
willakat Dec 31, 2016
c01b898
Fix inline is_auto_layout
ddevault Jan 1, 2017
0412e95
Document new layout command syntax
ddevault Jan 1, 2017
a62048f
changed "layout promote" command to "move first"
willakat Jan 1, 2017
97f7098
[fix] cleanups suggested by Sway community
willakat Jan 1, 2017
704b2db
Merge branch 'master' of https://github.com/willakat/sway
willakat Jan 1, 2017
bd41502
Moved auto_* layout functions from resize.c to layout.c
willakat Jan 7, 2017
d99efb5
[fix] corner cases win nb_children < nb_master|nb_col
willakat Jan 7, 2017
f726968
[fix] scale check to prevent un-necessary layouts was in the wrong pl…
willakat Jan 7, 2017
3c84250
[fix] resize should now preserve surrounding container's dimensions
willakat Jan 7, 2017
1f47c58
simplification of apply_auto_layout
willakat Jan 7, 2017
f24ebd7
Added mouse resize for auto layouts
willakat Jan 7, 2017
653e96f
Merge branch 'master' into master
willakat Jan 7, 2017
2040c62
Merge branch 'master' of https://github.com/SirCmpwn/sway
willakat Jan 7, 2017
d822150
[fix] Keep Clang happy
willakat Jan 8, 2017
063c798
Indent cleanups
willakat Jan 8, 2017
07474a4
reworked "layout auto*" star commands
willakat Jan 8, 2017
52f3a8d
fixed up space-after-cast style issues
willakat Jan 8, 2017
32430d3
Merge branch 'master' of https://github.com/willakat/sway
willakat Jan 8, 2017
dc361fd
Merge branch 'master' into master
willakat Jan 8, 2017
4c06a10
Merge branch 'master' into master
willakat Jan 14, 2017
b74870f
Improved behavior of insert/remove child in auto layouts
willakat Jan 14, 2017
71b3869
replaced "bot" with "bottom" in auto layout commands
willakat Jan 14, 2017
5c40cc4
Added a sample config for Awesome-like behavior
willakat Jan 14, 2017
a90ddde
[fix] handle auto layout of empty container
willakat Jan 14, 2017
a2cf3be
Move awesome config to contrib/
ddevault Jan 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions samples/awesome-like.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#
# Replicate some of Awesome's default layout manipulation configuration for Sway
#
# Differences:
# - Layout switching doesn't use the spacebar (i.e. i3/Sway behavior to switch to/from floating windows)
# and uses the 'A' key instead (as in auto)
# - Resizing windows uses i3/Sway's more versatile Mod4+r
# - no tags
# - no Maximize/Minize, alternatives to Maximize would be to switch to Stacked/Tabbed layouts
# via Mod4+w or Mod4+s.
# - kill focused client is available on Mod4+Shift+q (instead of Mod4+Shift+c, which maps to Sway's
# config reload)
# - probably many more ...

# Awesome-style container traversal using Vim-like binding
set $next j
set $prev k

#
# Moving around:
#
# Move your focus around
bindsym $mod+$next focus next
bindsym $mod+$prev focus prev

# _move_ the focused window with the same, but add Shift
bindsym $mod+Shift+$next move next
bindsym $mod+Shift+$prev move prev

#
# Layout:
#
workspace_layout auto left

# This is usually bound to $mod+space, but this works well in practice by keeping
# all the layout switching keys grouped together.
bindsym $mod+a layout auto next
bindsym $mod+Shift+a layout auto prev

# Promote a child to master position in an auto layout
bindsym $mod+Control+Return move first

# Increase/decrease number of master elements in auto layout
bindsym $mod+Shift+h layout auto master inc 1
bindsym $mod+Shift+l layout auto master inc -1

# Increase/decrease number of slave element groups in auto layout
bindsym $mod+Control+h layout auto ncol inc 1
bindsym $mod+Control+l layout auto ncol inc -1

#
# Resizing containers:
# Again, not really the way Awesome works well, but in spirit with i3/Sway and it works well.
#
mode "resize" {
bindsym Left resize shrink width 20 px
bindsym Down resize grow height 20 px
bindsym Up resize shrink height 20 px
bindsym Right resize grow width 20 px
}
bindsym $mod+r mode "resize"

new_window pixel 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put this into a directory called contrib instead?

4 changes: 2 additions & 2 deletions sway/commands/layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **
struct cmd_results *error = NULL;
const char *cmd_name = "layout auto";
const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
const char *err_msg = "Allowed arguments are <right|left|top|bot|next|prev|master|ncol>";
const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";

bool need_layout_update = false;
enum swayc_layouts old_layout = container->layout;
Expand All @@ -90,7 +90,7 @@ static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **
layout = L_AUTO_RIGHT;
} else if (strcasecmp(argv[1], "top") == 0) {
layout = L_AUTO_TOP;
} else if (strcasecmp(argv[1], "bot") == 0) {
} else if (strcasecmp(argv[1], "bottom") == 0) {
layout = L_AUTO_BOTTOM;
} else if (strcasecmp(argv[1], "next") == 0) {
if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
Expand Down
31 changes: 21 additions & 10 deletions sway/commands/workspace_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "workspace_layout", EXPECTED_EQUAL_TO, 1))) {
if ((error = checkarg(argc, "workspace_layout", EXPECTED_AT_LEAST, 1))) {
return error;
}

Expand All @@ -13,16 +13,27 @@ 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 if (strcasecmp(argv[0], "auto") == 0) {
if (argc == 1) {
config->default_layout = L_AUTO_FIRST;
} else {
if ((error = checkarg(argc, "workspace_layout auto", EXPECTED_EQUAL_TO, 2))) {
return error;
}
if (strcasecmp(argv[0], "left") == 0) {
config->default_layout = L_AUTO_LEFT;
} else if (strcasecmp(argv[0], "right") == 0) {
config->default_layout = L_AUTO_RIGHT;
} else if (strcasecmp(argv[0], "top") == 0) {
config->default_layout = L_AUTO_TOP;
} else if (strcasecmp(argv[0], "bottom") == 0) {
config->default_layout = L_AUTO_BOTTOM;
} else {
return cmd_results_new(CMD_INVALID, "workspace_layout auto", "Expected 'workspace_layout auto <left|right|top|bottom>'");
}
}
} else {
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_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto|auto left|auto right|auto top|auto bottom>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
139 changes: 110 additions & 29 deletions sway/layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ void add_child(swayc_t *parent, swayc_t *child) {
}
}

static double *get_height(swayc_t *cont) {
return &cont->height;
}

static double *get_width(swayc_t *cont) {
return &cont->width;
}

void insert_child(swayc_t *parent, swayc_t *child, int index) {
if (index > parent->children->length) {
index = parent->children->length;
Expand All @@ -86,7 +94,44 @@ void insert_child(swayc_t *parent, swayc_t *child, int index) {
if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
child = new_container(child, parent->workspace_layout);
}

if (is_auto_layout(parent->layout)) {
/* go through each group, adjust the size of the first child of each group */
double *(*get_maj_dim)(swayc_t *cont);
double *(*get_min_dim)(swayc_t *cont);
if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
get_maj_dim = get_width;
get_min_dim = get_height;
} else {
get_maj_dim = get_height;
get_min_dim = get_width;
}
for (int i = index; i < parent->children->length;) {
int start = auto_group_start_index(parent, i);
int end = auto_group_end_index(parent, i);
swayc_t *first = parent->children->items[start];
if (start + 1 < parent->children->length) {
/* preserve the group's dimension along major axis */
*get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
} else {
/* new group, let the apply_layout handle it */
first->height = first->width = 0;
break;
}
double remaining = *get_min_dim(parent);
for (int j = end - 1; j > start; --j) {
swayc_t *sibling = parent->children->items[j];
if (sibling == child) {
/* the inserted child won't yet have its minor
dimension set */
remaining -= *get_min_dim(parent) / (end - start);
} else {
remaining -= *get_min_dim(sibling);
}
}
*get_min_dim(first) = remaining;
i = end;
}
}
}

void add_floating(swayc_t *ws, swayc_t *child) {
Expand Down Expand Up @@ -185,6 +230,42 @@ swayc_t *remove_child(swayc_t *child) {
break;
}
}
if (is_auto_layout(parent->layout) && parent->children->length) {
/* go through each group, adjust the size of the last child of each group */
double *(*get_maj_dim)(swayc_t *cont);
double *(*get_min_dim)(swayc_t *cont);
if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
get_maj_dim = get_width;
get_min_dim = get_height;
} else {
get_maj_dim = get_height;
get_min_dim = get_width;
}
for (int j = parent->children->length - 1; j >= i;) {
int start = auto_group_start_index(parent, j);
int end = auto_group_end_index(parent, j);
swayc_t *first = parent->children->items[start];
if (i == start) {
/* removed element was first child in the current group,
use its size along the major axis */
*get_maj_dim(first) = *get_maj_dim(child);
} else if (start > i) {
/* preserve the group's dimension along major axis */
*get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
}
if (end != parent->children->length) {
double remaining = *get_min_dim(parent);
for (int k = start; k < end - 1; ++k) {
swayc_t *sibling = parent->children->items[k];
remaining -= *get_min_dim(sibling);
}
/* last element of the group gets remaining size, elements
that don't change groups keep their ratio */
*get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
} /* else last group, let apply_layout handle it */
j = start - 1;
}
}
}
// Set focused to new container
if (parent->focused == child) {
Expand Down Expand Up @@ -250,6 +331,24 @@ void swap_geometry(swayc_t *a, swayc_t *b) {
b->height = h;
}

static void swap_children(swayc_t *container, int a, int b) {
if (a >= 0 && b >= 0 && a < container->children->length
&& b < container->children->length
&& a != b) {
swayc_t *pa = (swayc_t *)container->children->items[a];
swayc_t *pb = (swayc_t *)container->children->items[b];
container->children->items[a] = container->children->items[b];
container->children->items[b] = pa;
if (is_auto_layout(container->layout)) {
size_t ga = auto_group_index(container, a);
size_t gb = auto_group_index(container, b);
if (ga != gb) {
swap_geometry(pa, pb);
}
}
}
}

void move_container(swayc_t *container, enum movement_direction dir) {
enum swayc_layouts layout = L_NONE;
swayc_t *parent = container->parent;
Expand Down Expand Up @@ -319,29 +418,6 @@ void move_container(swayc_t *container, enum movement_direction dir) {
} else if (desired >= parent->children->length) {
desired = 0;
}
// if move command makes container change from master to slave
// (or the contrary), reset its geometry an the one of the replaced item.
if (parent->nb_master
&& (size_t)parent->children->length > parent->nb_master) {
swayc_t *swap_geom = NULL;
// if child is being promoted/demoted, it will swap geometry
// with the sibling being demoted/promoted.
if ((dir == MOVE_NEXT && desired == 0)
|| (dir == MOVE_PREV && (size_t)desired == parent->nb_master - 1)) {
swap_geom = parent->children->items[parent->nb_master - 1];
} else if ((dir == MOVE_NEXT && (size_t)desired == parent->nb_master)
|| (dir == MOVE_PREV && desired == parent->children->length - 1)) {
swap_geom = parent->children->items[parent->nb_master];
}
if (swap_geom) {
double h = child->height;
double w = child->width;
child->width = swap_geom->width;
child->height = swap_geom->height;
swap_geom->width = w;
swap_geom->height = h;
}
}
}
// when it has ascended, legal insertion position is 0:len
// when it has not, legal insertion position is 0:len-1
Expand All @@ -365,10 +441,14 @@ void move_container(swayc_t *container, enum movement_direction dir) {
container->width = container->height = 0;
}
}
swayc_t *old_parent = remove_child(container);
insert_child(parent, container, desired);
destroy_container(old_parent);
sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
if (container->parent == parent) {
swap_children(parent, idx, desired);
} else {
swayc_t *old_parent = remove_child(container);
insert_child(parent, container, desired);
destroy_container(old_parent);
sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
}
break;
}
}
Expand Down Expand Up @@ -1535,7 +1615,8 @@ size_t auto_slave_group_count(const swayc_t *container) {
* Return the combined number of master and slave groups in the container.
*/
size_t auto_group_count(const swayc_t *container) {
return auto_slave_group_count(container) + (container->nb_master ? 1 : 0);
return auto_slave_group_count(container)
+ (container->children->length && container->nb_master ? 1 : 0);
}

/**
Expand Down
17 changes: 8 additions & 9 deletions sway/sway.5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,11 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
**focus** <direction>::
Direction may be one of _up_, _down_, _left_, _right_, _next_, _prev_,
_parent_, or _child_. The directional focus commands will move the focus
in that direction. The auto_next and auto_prev will focus the next,
respectively previous, element in the current container if it is using
one of the _auto_ layouts. The parent focus command will change the
focus to the parent of the currently focused container, which is useful,
for example, to open a sibling of the parent container, or to move the
entire container around.
in that direction. The _next_ and _prev_ directions will focus the next,
respectively previous, element in the current container. The parent
focus command will change the focus to the parent of the currently
focused container, which is useful, for example, to open a sibling of
the parent container, or to move the entire container around.

**focus** output <direction|name>::
Direction may be one of _up_, _down_, _left_, _right_. The directional focus
Expand All @@ -88,7 +87,7 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(

**layout** auto <mode>::
Sets layout to one of the auto modes, i.e. one of _left_, right_, _top_,
or _bot_.
or _bottom_.

**layout** auto <next|prev>::
Cycles between available auto layouts.
Expand Down Expand Up @@ -381,8 +380,8 @@ 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|auto_left|auto_right|auto_top|auto_bottom>::
Specifies the start layout for new workspaces.
**workspace_layout** <default|stacking|tabbed|auto|auto left|auto right|auto
top|auto bottom>:: Specifies the start layout for new workspaces.

**include** <path>::
Includes a sub config file by _path_. _path_ can be either a full path or a
Expand Down