Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and splitv layouts. splith is equivalent to default with orientation horizontal and splitv is equivalent to default with orientation vertical. The "split h" and "split v" commands continue to work as before, they split the current container and you will end up in a split container with layout splith (after "split h") or splitv (after "split v"). To change a splith container into a splitv container, use either "layout splitv" or "layout toggle split". The latter command is used in the default config as mod+l (previously "layout default"). In case you have "layout default" in your config file, it is recommended to just replace it by "layout toggle split", which will work as "layout default" did before when pressing it once, but toggle between horizontal/vertical when pressing it repeatedly. The rationale behind this commit is that it’s cleaner to have all parameters that influence how windows are rendered in the layout itself rather than having a special parameter in combination with only one layout. This enables us to change existing split containers in all cases without breaking existing features (see ticket #464). Also, users should feel more confident about whether they are actually splitting or just changing an existing split container now. As a nice side-effect, this commit brings back the "layout toggle" feature we once had in i3 version 3 (see the userguide). AFAIK, it is safe to use in-place restart to upgrade into versions after this commit (switching to an older version will break your layout, though). Fixes #464
This commit is contained in:
@ -35,13 +35,13 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
|
||||
Con *resize_con = con;
|
||||
while (resize_con->type != CT_WORKSPACE &&
|
||||
resize_con->type != CT_FLOATING_CON &&
|
||||
resize_con->parent->orientation != orientation)
|
||||
con_orientation(resize_con->parent) != orientation)
|
||||
resize_con = resize_con->parent;
|
||||
|
||||
DLOG("resize_con = %p\n", resize_con);
|
||||
if (resize_con->type != CT_WORKSPACE &&
|
||||
resize_con->type != CT_FLOATING_CON &&
|
||||
resize_con->parent->orientation == orientation) {
|
||||
con_orientation(resize_con->parent) == orientation) {
|
||||
first = resize_con;
|
||||
second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes);
|
||||
if (second == TAILQ_END(&(first->nodes_head))) {
|
||||
@ -145,7 +145,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click
|
||||
|
||||
if ((check_con->layout == L_STACKED ||
|
||||
check_con->layout == L_TABBED ||
|
||||
check_con->orientation == HORIZ) &&
|
||||
con_orientation(check_con) == HORIZ) &&
|
||||
con_num_children(check_con) > 1) {
|
||||
DLOG("Not handling this resize, this container has > 1 child.\n");
|
||||
return false;
|
||||
|
@ -530,7 +530,7 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
|
||||
|
||||
do {
|
||||
if (current->parent->orientation != search_orientation) {
|
||||
if (con_orientation(current->parent) != search_orientation) {
|
||||
current = current->parent;
|
||||
continue;
|
||||
}
|
||||
@ -541,7 +541,7 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
||||
percentage = 1.0 / children;
|
||||
LOG("default percentage = %f\n", percentage);
|
||||
|
||||
orientation_t orientation = current->parent->orientation;
|
||||
orientation_t orientation = con_orientation(current->parent);
|
||||
|
||||
if ((orientation == HORIZ &&
|
||||
(strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
|
||||
@ -612,7 +612,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
|
||||
|
||||
while (current->type != CT_WORKSPACE &&
|
||||
current->type != CT_FLOATING_CON &&
|
||||
current->parent->orientation != search_orientation)
|
||||
con_orientation(current->parent) != search_orientation)
|
||||
current = current->parent;
|
||||
|
||||
/* get the default percentage */
|
||||
@ -621,7 +621,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
|
||||
double percentage = 1.0 / children;
|
||||
LOG("default percentage = %f\n", percentage);
|
||||
|
||||
orientation_t orientation = current->parent->orientation;
|
||||
orientation_t orientation = con_orientation(current->parent);
|
||||
|
||||
if ((orientation == HORIZ &&
|
||||
strcmp(direction, "height") == 0) ||
|
||||
@ -1397,17 +1397,27 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of 'layout default|stacked|stacking|tabbed'.
|
||||
* Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
|
||||
*
|
||||
*/
|
||||
void cmd_layout(I3_CMD, char *layout_str) {
|
||||
if (strcmp(layout_str, "stacking") == 0)
|
||||
layout_str = "stacked";
|
||||
DLOG("changing layout to %s\n", layout_str);
|
||||
owindow *current;
|
||||
int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
|
||||
(strcmp(layout_str, "stacked") == 0 ? L_STACKED :
|
||||
L_TABBED));
|
||||
int layout;
|
||||
/* default is a special case which will be handled in con_set_layout(). */
|
||||
if (strcmp(layout_str, "default") == 0)
|
||||
layout = L_DEFAULT;
|
||||
else if (strcmp(layout_str, "stacked") == 0)
|
||||
layout = L_STACKED;
|
||||
else if (strcmp(layout_str, "tabbed") == 0)
|
||||
layout = L_TABBED;
|
||||
else if (strcmp(layout_str, "splitv") == 0)
|
||||
layout = L_SPLITV;
|
||||
else if (strcmp(layout_str, "splith") == 0)
|
||||
layout = L_SPLITH;
|
||||
|
||||
DLOG("changing layout to %s (%d)\n", layout_str, layout);
|
||||
|
||||
/* check if the match is empty, not if the result is empty */
|
||||
if (match_is_empty(current_match))
|
||||
@ -1424,6 +1434,33 @@ void cmd_layout(I3_CMD, char *layout_str) {
|
||||
ysuccess(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of 'layout toggle [all|split]'.
|
||||
*
|
||||
*/
|
||||
void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
|
||||
owindow *current;
|
||||
|
||||
if (toggle_mode == NULL)
|
||||
toggle_mode = "default";
|
||||
|
||||
DLOG("toggling layout (mode = %s)\n", toggle_mode);
|
||||
|
||||
/* check if the match is empty, not if the result is empty */
|
||||
if (match_is_empty(current_match))
|
||||
con_toggle_layout(focused->parent, toggle_mode);
|
||||
else {
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
con_toggle_layout(current->con, toggle_mode);
|
||||
}
|
||||
}
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
// XXX: default reply for now, make this a better reply
|
||||
ysuccess(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementaiton of 'exit'.
|
||||
*
|
||||
@ -1472,6 +1509,7 @@ void cmd_restart(I3_CMD) {
|
||||
void cmd_open(I3_CMD) {
|
||||
LOG("opening new container\n");
|
||||
Con *con = tree_open_con(NULL, NULL);
|
||||
con->layout = L_SPLITH;
|
||||
con_focus(con);
|
||||
|
||||
y(map_open);
|
||||
|
125
src/con.c
125
src/con.c
@ -217,8 +217,8 @@ bool con_accepts_window(Con *con) {
|
||||
if (con->type == CT_WORKSPACE)
|
||||
return false;
|
||||
|
||||
if (con->orientation != NO_ORIENTATION) {
|
||||
DLOG("container %p does not accepts windows, orientation != NO_ORIENTATION\n", con);
|
||||
if (con->split) {
|
||||
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -265,8 +265,11 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
|
||||
while (con_orientation(parent) != orientation) {
|
||||
DLOG("Need to go one level further up\n");
|
||||
parent = parent->parent;
|
||||
/* Abort when we reach a floating con */
|
||||
if (parent && parent->type == CT_FLOATING_CON)
|
||||
/* Abort when we reach a floating con, or an output con */
|
||||
if (parent &&
|
||||
(parent->type == CT_FLOATING_CON ||
|
||||
parent->type == CT_OUTPUT ||
|
||||
(parent->parent && parent->parent->type == CT_OUTPUT)))
|
||||
parent = NULL;
|
||||
if (parent == NULL)
|
||||
break;
|
||||
@ -697,14 +700,32 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||
*
|
||||
*/
|
||||
int con_orientation(Con *con) {
|
||||
/* stacking containers behave like they are in vertical orientation */
|
||||
if (con->layout == L_STACKED)
|
||||
return VERT;
|
||||
switch (con->layout) {
|
||||
case L_SPLITV:
|
||||
/* stacking containers behave like they are in vertical orientation */
|
||||
case L_STACKED:
|
||||
return VERT;
|
||||
|
||||
if (con->layout == L_TABBED)
|
||||
return HORIZ;
|
||||
case L_SPLITH:
|
||||
/* tabbed containers behave like they are in vertical orientation */
|
||||
case L_TABBED:
|
||||
return HORIZ;
|
||||
|
||||
return con->orientation;
|
||||
case L_DEFAULT:
|
||||
DLOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
|
||||
assert(false);
|
||||
return HORIZ;
|
||||
|
||||
case L_DOCKAREA:
|
||||
case L_OUTPUT:
|
||||
DLOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con);
|
||||
assert(false);
|
||||
return HORIZ;
|
||||
|
||||
default:
|
||||
DLOG("con_orientation() ran into default\n");
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1017,23 +1038,16 @@ void con_set_layout(Con *con, int layout) {
|
||||
Con *new = con_new(NULL, NULL);
|
||||
new->parent = con;
|
||||
|
||||
/* 2: set the requested layout on the split con */
|
||||
/* 2: Set the requested layout on the split container and mark it as
|
||||
* split. */
|
||||
new->layout = layout;
|
||||
|
||||
/* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
|
||||
* to be set. Otherwise, this con will not be interpreted as a split
|
||||
* container. */
|
||||
if (config.default_orientation == NO_ORIENTATION) {
|
||||
new->orientation = (con->rect.height > con->rect.width) ? VERT : HORIZ;
|
||||
} else {
|
||||
new->orientation = config.default_orientation;
|
||||
}
|
||||
new->split = true;
|
||||
|
||||
Con *old_focused = TAILQ_FIRST(&(con->focus_head));
|
||||
if (old_focused == TAILQ_END(&(con->focus_head)))
|
||||
old_focused = NULL;
|
||||
|
||||
/* 4: move the existing cons of this workspace below the new con */
|
||||
/* 3: move the existing cons of this workspace below the new con */
|
||||
DLOG("Moving cons\n");
|
||||
Con *child;
|
||||
while (!TAILQ_EMPTY(&(con->nodes_head))) {
|
||||
@ -1054,7 +1068,66 @@ void con_set_layout(Con *con, int layout) {
|
||||
return;
|
||||
}
|
||||
|
||||
con->layout = layout;
|
||||
if (layout == L_DEFAULT) {
|
||||
/* Special case: the layout formerly known as "default" (in combination
|
||||
* with an orientation). Since we switched to splith/splitv layouts,
|
||||
* using the "default" layout (which "only" should happen when using
|
||||
* legacy configs) is using the last split layout (either splith or
|
||||
* splitv) in order to still do the same thing.
|
||||
*
|
||||
* Starting from v4.6 though, we will nag users about using "layout
|
||||
* default", and in v4.9 we will remove it entirely (with an
|
||||
* appropriate i3-migrate-config mechanism). */
|
||||
con->layout = con->last_split_layout;
|
||||
} else {
|
||||
/* We fill in last_split_layout when switching to a different layout
|
||||
* since there are many places in the code that don’t use
|
||||
* con_set_layout(). */
|
||||
if (con->layout == L_SPLITH || con->layout == L_SPLITV)
|
||||
con->last_split_layout = con->layout;
|
||||
con->layout = layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function toggles the layout of a given container. toggle_mode can be
|
||||
* either 'default' (toggle only between stacked/tabbed/last_split_layout),
|
||||
* 'split' (toggle only between splitv/splith) or 'all' (toggle between all
|
||||
* layouts).
|
||||
*
|
||||
*/
|
||||
void con_toggle_layout(Con *con, const char *toggle_mode) {
|
||||
if (strcmp(toggle_mode, "split") == 0) {
|
||||
/* Toggle between splits. When the current layout is not a split
|
||||
* layout, we just switch back to last_split_layout. Otherwise, we
|
||||
* change to the opposite split layout. */
|
||||
if (con->layout != L_SPLITH && con->layout != L_SPLITV)
|
||||
con_set_layout(con, con->last_split_layout);
|
||||
else {
|
||||
if (con->layout == L_SPLITH)
|
||||
con_set_layout(con, L_SPLITV);
|
||||
else con_set_layout(con, L_SPLITH);
|
||||
}
|
||||
} else {
|
||||
if (con->layout == L_STACKED)
|
||||
con_set_layout(con, L_TABBED);
|
||||
else if (con->layout == L_TABBED) {
|
||||
if (strcmp(toggle_mode, "all") == 0)
|
||||
con_set_layout(con, L_SPLITH);
|
||||
else con_set_layout(con, con->last_split_layout);
|
||||
} else if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
|
||||
if (strcmp(toggle_mode, "all") == 0) {
|
||||
/* When toggling through all modes, we toggle between
|
||||
* splith/splitv, whereas normally we just directly jump to
|
||||
* stacked. */
|
||||
if (con->layout == L_SPLITH)
|
||||
con_set_layout(con, L_SPLITV);
|
||||
else con_set_layout(con, L_STACKED);
|
||||
} else {
|
||||
con_set_layout(con, L_STACKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1131,12 +1204,12 @@ Rect con_minimum_size(Con *con) {
|
||||
/* For horizontal/vertical split containers we sum up the width (h-split)
|
||||
* or height (v-split) and use the maximum of the height (h-split) or width
|
||||
* (v-split) as minimum size. */
|
||||
if (con->orientation == HORIZ || con->orientation == VERT) {
|
||||
if (con->split) {
|
||||
uint32_t width = 0, height = 0;
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
Rect min = con_minimum_size(child);
|
||||
if (con->orientation == HORIZ) {
|
||||
if (con->layout == L_SPLITH) {
|
||||
width += min.width;
|
||||
height = max(height, min.height);
|
||||
} else {
|
||||
@ -1148,8 +1221,8 @@ Rect con_minimum_size(Con *con) {
|
||||
return (Rect){ 0, 0, width, height };
|
||||
}
|
||||
|
||||
ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n",
|
||||
con->type, con->layout, con->orientation);
|
||||
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
||||
con->type, con->layout, con->split);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ void floating_enable(Con *con, bool automatic) {
|
||||
}
|
||||
|
||||
/* 1: If the container is a workspace container, we need to create a new
|
||||
* split-container with the same orientation and make that one floating. We
|
||||
* split-container with the same layout and make that one floating. We
|
||||
* cannot touch the workspace container itself because floating containers
|
||||
* are children of the workspace. */
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
@ -52,7 +52,7 @@ void floating_enable(Con *con, bool automatic) {
|
||||
/* TODO: refactor this with src/con.c:con_set_layout */
|
||||
Con *new = con_new(NULL, NULL);
|
||||
new->parent = con;
|
||||
new->orientation = con->orientation;
|
||||
new->layout = con->layout;
|
||||
|
||||
/* since the new container will be set into floating mode directly
|
||||
* afterwards, we need to copy the workspace rect. */
|
||||
@ -97,8 +97,9 @@ void floating_enable(Con *con, bool automatic) {
|
||||
* otherwise. */
|
||||
Con *ws = con_get_workspace(con);
|
||||
nc->parent = ws;
|
||||
nc->orientation = NO_ORIENTATION;
|
||||
nc->split = true;
|
||||
nc->type = CT_FLOATING_CON;
|
||||
nc->layout = L_SPLITH;
|
||||
/* We insert nc already, even though its rect is not yet calculated. This
|
||||
* is necessary because otherwise the workspace might be empty (and get
|
||||
* closed in tree_close()) even though it’s not. */
|
||||
|
37
src/ipc.c
37
src/ipc.c
@ -161,17 +161,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
ystr("type");
|
||||
y(integer, con->type);
|
||||
|
||||
/* provided for backwards compatibility only. */
|
||||
ystr("orientation");
|
||||
switch (con->orientation) {
|
||||
case NO_ORIENTATION:
|
||||
ystr("none");
|
||||
break;
|
||||
case HORIZ:
|
||||
if (!con->split)
|
||||
ystr("none");
|
||||
else {
|
||||
if (con_orientation(con) == HORIZ)
|
||||
ystr("horizontal");
|
||||
break;
|
||||
case VERT:
|
||||
ystr("vertical");
|
||||
break;
|
||||
else ystr("vertical");
|
||||
}
|
||||
|
||||
ystr("scratchpad_state");
|
||||
@ -203,10 +200,20 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
ystr("focused");
|
||||
y(bool, (con == focused));
|
||||
|
||||
ystr("split");
|
||||
y(bool, con->split);
|
||||
|
||||
ystr("layout");
|
||||
switch (con->layout) {
|
||||
case L_DEFAULT:
|
||||
ystr("default");
|
||||
DLOG("About to dump layout=default, this is a bug in the code.\n");
|
||||
assert(false);
|
||||
break;
|
||||
case L_SPLITV:
|
||||
ystr("splitv");
|
||||
break;
|
||||
case L_SPLITH:
|
||||
ystr("splith");
|
||||
break;
|
||||
case L_STACKED:
|
||||
ystr("stacked");
|
||||
@ -222,6 +229,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
break;
|
||||
}
|
||||
|
||||
ystr("last_split_layout");
|
||||
switch (con->layout) {
|
||||
case L_SPLITV:
|
||||
ystr("splitv");
|
||||
break;
|
||||
default:
|
||||
ystr("splith");
|
||||
break;
|
||||
}
|
||||
|
||||
ystr("border");
|
||||
switch (con->border_style) {
|
||||
case BS_NORMAL:
|
||||
|
@ -156,15 +156,25 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
memcpy(json_node->sticky_group, val, len);
|
||||
LOG("sticky_group of this container is %s\n", json_node->sticky_group);
|
||||
} else if (strcasecmp(last_key, "orientation") == 0) {
|
||||
/* Upgrade path from older versions of i3 (doing an inplace restart
|
||||
* to a newer version):
|
||||
* "orientation" is dumped before "layout". Therefore, we store
|
||||
* whether the orientation was horizontal or vertical in the
|
||||
* last_split_layout. When we then encounter layout == "default",
|
||||
* we will use the last_split_layout as layout instead. */
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
if (strcasecmp(buf, "none") == 0)
|
||||
json_node->orientation = NO_ORIENTATION;
|
||||
else if (strcasecmp(buf, "horizontal") == 0)
|
||||
json_node->orientation = HORIZ;
|
||||
if (strcasecmp(buf, "none") == 0 ||
|
||||
strcasecmp(buf, "horizontal") == 0)
|
||||
json_node->last_split_layout = L_SPLITH;
|
||||
else if (strcasecmp(buf, "vertical") == 0)
|
||||
json_node->orientation = VERT;
|
||||
json_node->last_split_layout = L_SPLITV;
|
||||
else LOG("Unhandled orientation: %s\n", buf);
|
||||
|
||||
/* What used to be an implicit check whether orientation !=
|
||||
* NO_ORIENTATION is now a proper separate flag. */
|
||||
if (strcasecmp(buf, "none") != 0)
|
||||
json_node->split = true;
|
||||
free(buf);
|
||||
} else if (strcasecmp(last_key, "border") == 0) {
|
||||
char *buf = NULL;
|
||||
@ -181,17 +191,33 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
if (strcasecmp(buf, "default") == 0)
|
||||
json_node->layout = L_DEFAULT;
|
||||
/* This set above when we read "orientation". */
|
||||
json_node->layout = json_node->last_split_layout;
|
||||
else if (strcasecmp(buf, "stacked") == 0)
|
||||
json_node->layout = L_STACKED;
|
||||
else if (strcasecmp(buf, "tabbed") == 0)
|
||||
json_node->layout = L_TABBED;
|
||||
else if (strcasecmp(buf, "dockarea") == 0)
|
||||
else if (strcasecmp(buf, "dockarea") == 0) {
|
||||
json_node->layout = L_DOCKAREA;
|
||||
else if (strcasecmp(buf, "output") == 0)
|
||||
/* Necessary for migrating from older versions of i3. */
|
||||
json_node->split = false;
|
||||
} else if (strcasecmp(buf, "output") == 0)
|
||||
json_node->layout = L_OUTPUT;
|
||||
else if (strcasecmp(buf, "splith") == 0)
|
||||
json_node->layout = L_SPLITH;
|
||||
else if (strcasecmp(buf, "splitv") == 0)
|
||||
json_node->layout = L_SPLITV;
|
||||
else LOG("Unhandled \"layout\": %s\n", buf);
|
||||
free(buf);
|
||||
} else if (strcasecmp(last_key, "last_split_layout") == 0) {
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
if (strcasecmp(buf, "splith") == 0)
|
||||
json_node->last_split_layout = L_SPLITH;
|
||||
else if (strcasecmp(buf, "splitv") == 0)
|
||||
json_node->last_split_layout = L_SPLITV;
|
||||
else LOG("Unhandled \"last_splitlayout\": %s\n", buf);
|
||||
free(buf);
|
||||
} else if (strcasecmp(last_key, "mark") == 0) {
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
@ -288,6 +314,9 @@ static int json_bool(void *ctx, int val) {
|
||||
to_focus = json_node;
|
||||
}
|
||||
|
||||
if (strcasecmp(last_key, "split") == 0)
|
||||
json_node->split = val;
|
||||
|
||||
if (parsing_swallows) {
|
||||
if (strcasecmp(last_key, "restart_mode") == 0)
|
||||
current_swallow->restart_mode = val;
|
||||
|
11
src/randr.c
11
src/randr.c
@ -256,7 +256,6 @@ void output_init_con(Output *output) {
|
||||
Con *topdock = con_new(NULL, NULL);
|
||||
topdock->type = CT_DOCKAREA;
|
||||
topdock->layout = L_DOCKAREA;
|
||||
topdock->orientation = VERT;
|
||||
/* this container swallows dock clients */
|
||||
Match *match = scalloc(sizeof(Match));
|
||||
match_init(match);
|
||||
@ -278,6 +277,7 @@ void output_init_con(Output *output) {
|
||||
DLOG("adding main content container\n");
|
||||
Con *content = con_new(NULL, NULL);
|
||||
content->type = CT_CON;
|
||||
content->layout = L_SPLITH;
|
||||
FREE(content->name);
|
||||
content->name = sstrdup("content");
|
||||
|
||||
@ -290,7 +290,6 @@ void output_init_con(Output *output) {
|
||||
Con *bottomdock = con_new(NULL, NULL);
|
||||
bottomdock->type = CT_DOCKAREA;
|
||||
bottomdock->layout = L_DOCKAREA;
|
||||
bottomdock->orientation = VERT;
|
||||
/* this container swallows dock clients */
|
||||
match = scalloc(sizeof(Match));
|
||||
match_init(match);
|
||||
@ -447,11 +446,11 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
|
||||
if (con_num_children(workspace) > 1)
|
||||
continue;
|
||||
|
||||
workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
|
||||
DLOG("Setting workspace [%d,%s]'s orientation to %d.\n", workspace->num, workspace->name, workspace->orientation);
|
||||
workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
|
||||
DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout);
|
||||
if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) {
|
||||
child->orientation = workspace->orientation;
|
||||
DLOG("Setting child [%d,%s]'s orientation to %d.\n", child->num, child->name, child->orientation);
|
||||
child->layout = workspace->layout;
|
||||
DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
src/render.c
12
src/render.c
@ -106,9 +106,9 @@ static void render_l_output(Con *con) {
|
||||
*/
|
||||
void render_con(Con *con, bool render_fullscreen) {
|
||||
int children = con_num_children(con);
|
||||
DLOG("Rendering %snode %p / %s / layout %d / children %d / orient %d\n",
|
||||
DLOG("Rendering %snode %p / %s / layout %d / children %d\n",
|
||||
(render_fullscreen ? "fullscreen " : ""), con, con->name, con->layout,
|
||||
children, con->orientation);
|
||||
children);
|
||||
|
||||
/* Copy container rect, subtract container border */
|
||||
/* This is the actually usable space inside this container for clients */
|
||||
@ -208,11 +208,11 @@ void render_con(Con *con, bool render_fullscreen) {
|
||||
|
||||
/* precalculate the sizes to be able to correct rounding errors */
|
||||
int sizes[children];
|
||||
if (con->layout == L_DEFAULT && children > 0) {
|
||||
if ((con->layout == L_SPLITH || con->layout == L_SPLITV) && children > 0) {
|
||||
assert(!TAILQ_EMPTY(&con->nodes_head));
|
||||
Con *child;
|
||||
int i = 0, assigned = 0;
|
||||
int total = con->orientation == HORIZ ? rect.width : rect.height;
|
||||
int total = con_orientation(con) == HORIZ ? rect.width : rect.height;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
double percentage = child->percent > 0.0 ? child->percent : 1.0 / children;
|
||||
assigned += sizes[i++] = percentage * total;
|
||||
@ -289,8 +289,8 @@ void render_con(Con *con, bool render_fullscreen) {
|
||||
assert(children > 0);
|
||||
|
||||
/* default layout */
|
||||
if (con->layout == L_DEFAULT) {
|
||||
if (con->orientation == HORIZ) {
|
||||
if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
|
||||
if (con->layout == L_SPLITH) {
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = sizes[i];
|
||||
|
28
src/tree.c
28
src/tree.c
@ -39,6 +39,7 @@ static Con *_create___i3(void) {
|
||||
content->type = CT_CON;
|
||||
FREE(content->name);
|
||||
content->name = sstrdup("content");
|
||||
content->layout = L_SPLITH;
|
||||
|
||||
x_set_name(content, "[i3 con] content __i3");
|
||||
con_attach(content, __i3, false);
|
||||
@ -48,6 +49,7 @@ static Con *_create___i3(void) {
|
||||
ws->type = CT_WORKSPACE;
|
||||
ws->num = -1;
|
||||
ws->name = sstrdup("__i3_scratch");
|
||||
ws->layout = L_SPLITH;
|
||||
con_attach(ws, content, false);
|
||||
x_set_name(ws, "[i3 con] workspace __i3_scratch");
|
||||
ws->fullscreen_mode = CF_OUTPUT;
|
||||
@ -112,6 +114,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) {
|
||||
FREE(croot->name);
|
||||
croot->name = "root";
|
||||
croot->type = CT_ROOT;
|
||||
croot->layout = L_SPLITH;
|
||||
croot->rect = (Rect){
|
||||
geometry->x,
|
||||
geometry->y,
|
||||
@ -151,6 +154,7 @@ Con *tree_open_con(Con *con, i3Window *window) {
|
||||
|
||||
/* 3. create the container and attach it to its parent */
|
||||
Con *new = con_new(con, window);
|
||||
new->layout = L_SPLITH;
|
||||
|
||||
/* 4: re-calculate child->percent for each child */
|
||||
con_fix_percent(con);
|
||||
@ -337,7 +341,7 @@ void tree_split(Con *con, orientation_t orientation) {
|
||||
/* for a workspace, we just need to change orientation */
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("Workspace, simply changing orientation to %d\n", orientation);
|
||||
con->orientation = orientation;
|
||||
con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -351,8 +355,9 @@ void tree_split(Con *con, orientation_t orientation) {
|
||||
* child (its split functionality is unused so far), we just change the
|
||||
* orientation (more intuitive than splitting again) */
|
||||
if (con_num_children(parent) == 1 &&
|
||||
parent->layout == L_DEFAULT) {
|
||||
parent->orientation = orientation;
|
||||
(parent->layout == L_SPLITH ||
|
||||
parent->layout == L_SPLITV)) {
|
||||
parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
||||
DLOG("Just changing orientation of existing container\n");
|
||||
return;
|
||||
}
|
||||
@ -364,7 +369,8 @@ void tree_split(Con *con, orientation_t orientation) {
|
||||
TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
|
||||
TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
|
||||
new->parent = parent;
|
||||
new->orientation = orientation;
|
||||
new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
||||
new->split = true;
|
||||
|
||||
/* 3: swap 'percent' (resize factor) */
|
||||
new->percent = con->percent;
|
||||
@ -594,7 +600,9 @@ void tree_flatten(Con *con) {
|
||||
DLOG("Checking if I can flatten con = %p / %s\n", con, con->name);
|
||||
|
||||
/* We only consider normal containers without windows */
|
||||
if (con->type != CT_CON || con->window != NULL)
|
||||
if (con->type != CT_CON ||
|
||||
parent->layout == L_OUTPUT || /* con == "content" */
|
||||
con->window != NULL)
|
||||
goto recurse;
|
||||
|
||||
/* Ensure it got only one child */
|
||||
@ -602,12 +610,14 @@ void tree_flatten(Con *con) {
|
||||
if (child == NULL || TAILQ_NEXT(child, nodes) != NULL)
|
||||
goto recurse;
|
||||
|
||||
DLOG("child = %p, con = %p, parent = %p\n", child, con, parent);
|
||||
|
||||
/* The child must have a different orientation than the con but the same as
|
||||
* the con’s parent to be redundant */
|
||||
if (con->orientation == NO_ORIENTATION ||
|
||||
child->orientation == NO_ORIENTATION ||
|
||||
con->orientation == child->orientation ||
|
||||
child->orientation != parent->orientation)
|
||||
if (con->split ||
|
||||
child->split ||
|
||||
con_orientation(con) == con_orientation(child) ||
|
||||
con_orientation(child) != con_orientation(parent))
|
||||
goto recurse;
|
||||
|
||||
DLOG("Alright, I have to flatten this situation now. Stay calm.\n");
|
||||
|
@ -14,6 +14,25 @@
|
||||
* back-and-forth switching. */
|
||||
static char *previous_workspace_name = NULL;
|
||||
|
||||
/*
|
||||
* Sets ws->layout to splith/splitv if default_orientation was specified in the
|
||||
* configfile. Otherwise, it uses splith/splitv depending on whether the output
|
||||
* is higher than wide.
|
||||
*
|
||||
*/
|
||||
static void _workspace_apply_default_orientation(Con *ws) {
|
||||
/* If default_orientation is set to NO_ORIENTATION we determine
|
||||
* orientation depending on output resolution. */
|
||||
if (config.default_orientation == NO_ORIENTATION) {
|
||||
Con *output = con_get_output(ws);
|
||||
ws->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
|
||||
DLOG("Auto orientation. Workspace size set to (%d,%d), setting layout to %d.\n",
|
||||
output->rect.width, output->rect.height, ws->layout);
|
||||
} else {
|
||||
ws->layout = (config.default_orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a pointer to the workspace with the given number (starting at 0),
|
||||
* creating the workspace if necessary (by allocating the necessary amount of
|
||||
@ -64,16 +83,8 @@ Con *workspace_get(const char *num, bool *created) {
|
||||
else workspace->num = parsed_num;
|
||||
LOG("num = %d\n", workspace->num);
|
||||
|
||||
/* If default_orientation is set to NO_ORIENTATION we
|
||||
* determine workspace orientation from workspace size.
|
||||
* Otherwise we just set the orientation to default_orientation. */
|
||||
if (config.default_orientation == NO_ORIENTATION) {
|
||||
workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
|
||||
DLOG("Auto orientation. Output resolution set to (%d,%d), setting orientation to %d.\n",
|
||||
workspace->rect.width, workspace->rect.height, workspace->orientation);
|
||||
} else {
|
||||
workspace->orientation = config.default_orientation;
|
||||
}
|
||||
workspace->parent = content;
|
||||
_workspace_apply_default_orientation(workspace);
|
||||
|
||||
con_attach(workspace, content, false);
|
||||
|
||||
@ -198,19 +209,12 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
||||
|
||||
ws->fullscreen_mode = CF_OUTPUT;
|
||||
|
||||
/* If default_orientation is set to NO_ORIENTATION we determine
|
||||
* orientation depending on output resolution. */
|
||||
if (config.default_orientation == NO_ORIENTATION) {
|
||||
ws->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
|
||||
DLOG("Auto orientation. Workspace size set to (%d,%d), setting orientation to %d.\n",
|
||||
output->rect.width, output->rect.height, ws->orientation);
|
||||
} else {
|
||||
ws->orientation = config.default_orientation;
|
||||
}
|
||||
_workspace_apply_default_orientation(ws);
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns true if the workspace is currently visible. Especially important for
|
||||
* multi-monitor environments, as they can have multiple currenlty active
|
||||
@ -686,8 +690,7 @@ void workspace_update_urgent_flag(Con *ws) {
|
||||
|
||||
/*
|
||||
* 'Forces' workspace orientation by moving all cons into a new split-con with
|
||||
* the same orientation as the workspace and then changing the workspace
|
||||
* orientation.
|
||||
* the same layout as the workspace and then changing the workspace layout.
|
||||
*
|
||||
*/
|
||||
void ws_force_orientation(Con *ws, orientation_t orientation) {
|
||||
@ -695,9 +698,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
|
||||
Con *split = con_new(NULL, NULL);
|
||||
split->parent = ws;
|
||||
|
||||
/* 2: copy layout and orientation from workspace */
|
||||
/* 2: copy layout from workspace */
|
||||
split->layout = ws->layout;
|
||||
split->orientation = ws->orientation;
|
||||
|
||||
Con *old_focused = TAILQ_FIRST(&(ws->focus_head));
|
||||
|
||||
@ -709,8 +711,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
|
||||
con_attach(child, split, true);
|
||||
}
|
||||
|
||||
/* 4: switch workspace orientation */
|
||||
ws->orientation = orientation;
|
||||
/* 4: switch workspace layout */
|
||||
ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
|
||||
|
||||
/* 5: attach the new split container to the workspace */
|
||||
DLOG("Attaching new split to ws\n");
|
||||
@ -745,19 +747,11 @@ Con *workspace_attach_to(Con *ws) {
|
||||
/* 1: create a new split container */
|
||||
Con *new = con_new(NULL, NULL);
|
||||
new->parent = ws;
|
||||
new->split = true;
|
||||
|
||||
/* 2: set the requested layout on the split con */
|
||||
new->layout = config.default_layout;
|
||||
|
||||
/* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
|
||||
* to be set. Otherwise, this con will not be interpreted as a split
|
||||
* container. */
|
||||
if (config.default_orientation == NO_ORIENTATION) {
|
||||
new->orientation = (ws->rect.height > ws->rect.width) ? VERT : HORIZ;
|
||||
} else {
|
||||
new->orientation = config.default_orientation;
|
||||
}
|
||||
|
||||
/* 4: attach the new split container to the workspace */
|
||||
DLOG("Attaching new split %p to workspace %p\n", new, ws);
|
||||
con_attach(new, ws, false);
|
||||
|
Reference in New Issue
Block a user