From 4c0f0c638f0708afd7cf830c1fb5c059ceb45e66 Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Wed, 29 Oct 2014 08:10:01 +0000 Subject: [PATCH] tiling: Implemented tiling window position manipulation Summary: This implements the possibility to "break out" a node in the tree which means that the node will be appended or prependen to the parent in the grandparent. The other function "joins" the node to the node before or after. Basically it will be added as a child, and if necessarry the client of this node will be added in a new Window Tree and also added as a child. With the new actions you can move the focused window right/left/up/down with keybindings. If the window will "break out" or "join" depends on the parent split type. Sample: 1|4|6 2|4|6 3|5|6 (Same Number means same Window) 1 is focused. Left Key is pressed. 1 is in a vertical split so the window will "break out". Result: 2|1|4|6 2|1|5|6 Now another key: Down Key is pressed. 1 is in a vertical split so the window will "join". Result: 1|2|4|6 3 3|5|6 @feature Fixes T1350 Reviewers: tasn Subscribers: cedric Differential Revision: https://phab.enlightenment.org/D1382 --- src/modules/tiling/e_mod_tiling.c | 12 +- src/modules/tiling/window_tree.c | 261 ++++++++++++++++++++++---------------- src/modules/tiling/window_tree.h | 3 +- 3 files changed, 160 insertions(+), 116 deletions(-) diff --git a/src/modules/tiling/e_mod_tiling.c b/src/modules/tiling/e_mod_tiling.c index 0f0f2f1..2adb534 100644 --- a/src/modules/tiling/e_mod_tiling.c +++ b/src/modules/tiling/e_mod_tiling.c @@ -786,7 +786,7 @@ _e_mod_menu_border_cb(void *data, E_Menu *m EINA_UNUSED, /* {{{ Move windows */ static void -_action_swap(int cross_edge) +_action_move(int cross_edge) { E_Desk *desk; E_Client *focused_ec; @@ -807,7 +807,7 @@ _action_swap(int cross_edge) if (item) { - tiling_window_tree_node_move(item, cross_edge); + tiling_window_tree_node_change_pos(item, cross_edge); _reapply_tree(); } @@ -817,28 +817,28 @@ static void _e_mod_action_move_left_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED) { - _action_swap(TILING_WINDOW_TREE_EDGE_LEFT); + _action_move(TILING_WINDOW_TREE_EDGE_LEFT); } static void _e_mod_action_move_right_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED) { - _action_swap(TILING_WINDOW_TREE_EDGE_RIGHT); + _action_move(TILING_WINDOW_TREE_EDGE_RIGHT); } static void _e_mod_action_move_up_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED) { - _action_swap(TILING_WINDOW_TREE_EDGE_TOP); + _action_move(TILING_WINDOW_TREE_EDGE_TOP); } static void _e_mod_action_move_down_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED) { - _action_swap(TILING_WINDOW_TREE_EDGE_BOTTOM); + _action_move(TILING_WINDOW_TREE_EDGE_BOTTOM); } /* }}} */ diff --git a/src/modules/tiling/window_tree.c b/src/modules/tiling/window_tree.c index f29593c..e22a353 100644 --- a/src/modules/tiling/window_tree.c +++ b/src/modules/tiling/window_tree.c @@ -43,9 +43,8 @@ _tiling_window_tree_split_add(Window_Tree *parent, Window_Tree *new_node) parent->children = eina_inlist_append(parent->children, EINA_INLIST_GET(new_node)); } - static void -_tiling_window_tree_parent_add(Window_Tree *parent, Window_Tree *new_node, Window_Tree *rel) +_tiling_window_tree_parent_add(Window_Tree *parent, Window_Tree *new_node, Window_Tree *rel, Eina_Bool append) { /* Adjust existing children's weights */ Window_Tree *itr; @@ -60,10 +59,19 @@ _tiling_window_tree_parent_add(Window_Tree *parent, Window_Tree *new_node, Windo { itr->weight *= weight; } + if (append) + { + parent->children = eina_inlist_append_relative(parent->children, + EINA_INLIST_GET(new_node), + EINA_INLIST_GET(rel)); + } + else + { + parent->children = eina_inlist_prepend_relative(parent->children, + EINA_INLIST_GET(new_node), + EINA_INLIST_GET(rel)); + } - parent->children = - eina_inlist_append_relative(parent->children, EINA_INLIST_GET(new_node), - EINA_INLIST_GET(rel)); } static int @@ -116,7 +124,7 @@ tiling_window_tree_add(Window_Tree *root, Window_Tree *parent, { if (parent->children) { - _tiling_window_tree_parent_add(parent, new_node, NULL); + _tiling_window_tree_parent_add(parent, new_node, NULL, EINA_TRUE); } else { @@ -129,7 +137,7 @@ tiling_window_tree_add(Window_Tree *root, Window_Tree *parent, if (grand_parent && grand_parent->children) { - _tiling_window_tree_parent_add(grand_parent, new_node, orig_parent); + _tiling_window_tree_parent_add(grand_parent, new_node, orig_parent, EINA_TRUE); } else { @@ -145,20 +153,14 @@ tiling_window_tree_add(Window_Tree *root, Window_Tree *parent, return root; } -Window_Tree * -tiling_window_tree_remove(Window_Tree *root, Window_Tree *item) +static Window_Tree * +tiling_window_tree_unref(Window_Tree *root, Window_Tree *item) { - if (root == item) + if (!item->client) { - free(item); + ERR("Tried to unref node %p that doesn't have a client.", item); return NULL; } - else if (!item->client) - { - ERR("Tried deleting node %p that doesn't have a client.", item); - return root; - } - Window_Tree *parent = item->parent; int children_count = eina_inlist_count(item->parent->children); @@ -167,30 +169,24 @@ tiling_window_tree_remove(Window_Tree *root, Window_Tree *item) Window_Tree *grand_parent = parent->parent; Window_Tree *item_keep = NULL; - /* Adjust existing children's weights */ EINA_INLIST_FOREACH(parent->children, item_keep) { if (item_keep != item) break; } - if (!item_keep) { - /* Special case of deleting the last vertical split item. */ - free(item); - free(root); - return NULL; + parent->children =eina_inlist_remove(parent->children, EINA_INLIST_GET(item)); + return parent; } - else if (!item_keep->children) + else if (!item_keep->children && (parent != root)) { parent->client = item_keep->client; parent->children = NULL; - - free(item_keep); + return grand_parent; //we must have a grand_parent here, case the parent is not root } else { - parent->children = eina_inlist_remove(parent->children, EINA_INLIST_GET(item)); if (grand_parent) @@ -216,12 +212,14 @@ tiling_window_tree_remove(Window_Tree *root, Window_Tree *item) EINA_INLIST_GET(parent)); free(parent); } + return grand_parent; } - else + else if(item_keep) { /* This is fine, as this is a child of the root so we allow * two levels. */ item_keep->weight = 1.0; + return item_keep->parent; } } } @@ -238,9 +236,34 @@ tiling_window_tree_remove(Window_Tree *root, Window_Tree *item) { itr->weight /= weight; } + return parent; } + ERR("This is a state where we should never come to.\n"); + return NULL; +} +Window_Tree * +tiling_window_tree_remove(Window_Tree *root, Window_Tree *item) +{ + if (root == item) + { + free(item); + return NULL; + } + else if (!item->client) + { + ERR("Tried deleting node %p that doesn't have a client.", item); + return root; + } + tiling_window_tree_unref(root, item); free(item); + if (eina_inlist_count(root->children) == 0) + { + //the last possible client was closed so we remove root + free(root); + return NULL; + } + return root; } @@ -511,103 +534,125 @@ tiling_window_tree_edges_get(Window_Tree *node) } /* Node move */ -static struct _Node_Move_Context -{ - Window_Tree *node; - Window_Tree *ret; - int cross_edge; - int best_match; -} _node_move_ctx; - -#define CNODE (_node_move_ctx.node) - -#define IF_MATCH_SET_LR(node) _tiling_window_tree_node_move_if_match_set(node, \ - CNODE->client->y, CNODE->client->h, node->client->y, node->client->h) -#define IF_MATCH_SET_TB(node) _tiling_window_tree_node_move_if_match_set(node, \ - CNODE->client->x, CNODE->client->w, node->client->x, node->client->w) - -static void -_tiling_window_tree_node_move_if_match_set(Window_Tree *node, Evas_Coord cx, - Evas_Coord cw, Evas_Coord ox, Evas_Coord ow) +/** + * - break would mean that the node will be breaked out in the parent node and + * put into the grand parent node. + * + * - join would mean that the node will be put together with the next/previous + * child into a new split. + * + * - The eina_bool dir is the flag in which direction the node will be added , + * appended or prependet, or joined with the previous child or the next child + */ +void +_tiling_window_tree_node_break_out(Window_Tree *root, Window_Tree *node, Eina_Bool dir) { - Evas_Coord leftx, rightx; - int match; - - leftx = _TILE_MAX(cx, ox); - rightx = _TILE_MIN(cx + cw, ox + ow); - match = rightx - leftx; + Window_Tree *res, *grand_parent; - if (match > _node_move_ctx.best_match) + if (dir) { - _node_move_ctx.best_match = match; - _node_move_ctx.ret = node; + res = _inlist_next(node->parent); + if (res) + dir = EINA_FALSE; } -} - -static void -_tiling_window_tree_node_move_walker(void *_node) -{ - Window_Tree *node = _node; - int p = tiling_g.config->window_padding; - /* We are only interested in nodes with clients. */ - if (!node->client) - return; - - switch (_node_move_ctx.cross_edge) + else { - case TILING_WINDOW_TREE_EDGE_LEFT: - if ((node->client->x + node->client->w + p) == CNODE->client->x) - IF_MATCH_SET_LR(node); - break; - - case TILING_WINDOW_TREE_EDGE_RIGHT: - if (node->client->x == (CNODE->client->x + CNODE->client->w + p)) - IF_MATCH_SET_LR(node); - break; + res = _inlist_prev(node->parent); + if (res) + dir = EINA_TRUE; + } - case TILING_WINDOW_TREE_EDGE_TOP: - if ((node->client->y + node->client->h + p) == CNODE->client->y) - IF_MATCH_SET_TB(node); - break; + grand_parent = node->parent->parent; + if (!grand_parent) + return; - case TILING_WINDOW_TREE_EDGE_BOTTOM: - if (node->client->y == (CNODE->client->y + CNODE->client->h + p)) - IF_MATCH_SET_TB(node); - break; + tiling_window_tree_unref(root, node); - default: - break; - } + _tiling_window_tree_parent_add(grand_parent, node, res, dir); } -#undef CNODE -#undef IF_MATCH_SET_LR -#undef IF_MATCH_SET_TB - void -tiling_window_tree_node_move(Window_Tree *node, int cross_edge) +_tiling_window_tree_node_join(Window_Tree *root, Window_Tree *node, Eina_Bool dir) { - Window_Tree *root = node; + Window_Tree *pn, *pl, *wts, *par; - /* FIXME: This is very slow and possibly buggy. Can be done much better, but - * is very easy to implement. */ - - while (root->parent) - root = root->parent; + if (dir) + pn = _inlist_next(node); + else + pn = _inlist_prev(node); - _node_move_ctx.node = node; - _node_move_ctx.cross_edge = cross_edge; - _node_move_ctx.ret = NULL; - _node_move_ctx.best_match = 0; + if (!pn) + return; - tiling_window_tree_walk(root, _tiling_window_tree_node_move_walker); + par = node->parent; + if ((eina_inlist_count(par->children) == 2) && /* swap if there are just 2 */ + par->parent && (eina_inlist_count(par->parent->children) > 1)) /* do not swap if we are in the first level */ + { + par->children = eina_inlist_demote(par->children, eina_inlist_first(par->children)); + return; + } + else + { + pl = tiling_window_tree_unref(root, node); + if (pl == node->parent) + { + //unref has not changed the tree + if (!pn->children) + _tiling_window_tree_split_add(pn, node); + else + _tiling_window_tree_parent_add(pn, node, NULL, EINA_TRUE); + } + else + { + //unref changed the position of pn in the tree, result of unref + //will be the new parent + //we need to search the e_client ptr to get the corret relative pos + wts = pn->parent; + while(wts->client != pn->client) + wts = _inlist_next(wts); + _tiling_window_tree_parent_add(pl, node, wts, EINA_TRUE); + } + } +} - if (_node_move_ctx.ret) - { - E_Client *ec = node->client; +void +tiling_window_tree_node_change_pos(Window_Tree *node, int key) +{ + if (!node->parent) + return; - node->client = _node_move_ctx.ret->client; - _node_move_ctx.ret->client = ec; + Tiling_Split_Type parent_split_type = + _tiling_window_tree_split_type_get(node->parent); + + Window_Tree *root = node->parent; + while(root->parent) + root = root->parent; + switch(key) + { + case TILING_WINDOW_TREE_EDGE_LEFT: + if (parent_split_type == TILING_SPLIT_HORIZONTAL) + _tiling_window_tree_node_join(root, node, EINA_FALSE); + else if(parent_split_type == TILING_SPLIT_VERTICAL) + _tiling_window_tree_node_break_out(root, node, EINA_FALSE); + break; + case TILING_WINDOW_TREE_EDGE_RIGHT: + if (parent_split_type == TILING_SPLIT_HORIZONTAL) + _tiling_window_tree_node_join(root, node, EINA_TRUE); + else if(parent_split_type == TILING_SPLIT_VERTICAL) + _tiling_window_tree_node_break_out(root, node, EINA_TRUE); + break; + case TILING_WINDOW_TREE_EDGE_TOP: + if (parent_split_type == TILING_SPLIT_HORIZONTAL) + _tiling_window_tree_node_break_out(root, node, EINA_FALSE); + else if(parent_split_type == TILING_SPLIT_VERTICAL) + _tiling_window_tree_node_join(root, node, EINA_FALSE); + break; + case TILING_WINDOW_TREE_EDGE_BOTTOM: + if (parent_split_type == TILING_SPLIT_HORIZONTAL) + _tiling_window_tree_node_break_out(root, node, EINA_TRUE); + else if(parent_split_type == TILING_SPLIT_VERTICAL) + _tiling_window_tree_node_join(root, node, EINA_TRUE); + break; } } diff --git a/src/modules/tiling/window_tree.h b/src/modules/tiling/window_tree.h index df5034b..f6ff79a 100644 --- a/src/modules/tiling/window_tree.h +++ b/src/modules/tiling/window_tree.h @@ -48,6 +48,5 @@ void tiling_window_tree_apply(Window_Tree *root, Evas_Coord x, Evas_Coor Eina_Bool tiling_window_tree_node_resize(Window_Tree *node, int w_dir, double w_diff, int h_dir, double h_diff); -void tiling_window_tree_node_move(Window_Tree *node, int cross_edge); - +void tiling_window_tree_node_change_pos(Window_Tree *node, int key); #endif -- 2.7.4