Improved behavior of insert/remove child in auto layouts

Previous implementation would not preserve dimension of groups
along the major axis. This should avoid weird behavior when
using container motion commands.
master
wil 8 years ago
parent 4c06a10004
commit b74870f516

@ -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) { void insert_child(swayc_t *parent, swayc_t *child, int index) {
if (index > parent->children->length) { if (index > parent->children->length) {
index = parent->children->length; index = parent->children->length;
@ -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)) { 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); 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) { void add_floating(swayc_t *ws, swayc_t *child) {
@ -185,6 +230,42 @@ swayc_t *remove_child(swayc_t *child) {
break; 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 // Set focused to new container
if (parent->focused == child) { if (parent->focused == child) {
@ -250,6 +331,24 @@ void swap_geometry(swayc_t *a, swayc_t *b) {
b->height = h; 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) { void move_container(swayc_t *container, enum movement_direction dir) {
enum swayc_layouts layout = L_NONE; enum swayc_layouts layout = L_NONE;
swayc_t *parent = container->parent; swayc_t *parent = container->parent;
@ -319,29 +418,6 @@ void move_container(swayc_t *container, enum movement_direction dir) {
} else if (desired >= parent->children->length) { } else if (desired >= parent->children->length) {
desired = 0; 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 ascended, legal insertion position is 0:len
// when it has not, legal insertion position is 0:len-1 // when it has not, legal insertion position is 0:len-1
@ -365,10 +441,14 @@ void move_container(swayc_t *container, enum movement_direction dir) {
container->width = container->height = 0; container->width = container->height = 0;
} }
} }
if (container->parent == parent) {
swap_children(parent, idx, desired);
} else {
swayc_t *old_parent = remove_child(container); swayc_t *old_parent = remove_child(container);
insert_child(parent, container, desired); insert_child(parent, container, desired);
destroy_container(old_parent); destroy_container(old_parent);
sway_log(L_DEBUG,"Moving to %p %d", parent, desired); sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
}
break; break;
} }
} }

Loading…
Cancel
Save