forked from enlightenment/enlightenment
tiling: make windows placable by dragging them arround
If someone starts to drag a client arround, then the client will shrink into a icon, that now is always at the position of the mouse cursor, if the drag ends, the client will be placed in the client currently below it. The client will be placed in a place where the mouse cursor was currently closer to.
This commit is contained in:
parent
9b211b80c3
commit
35e729f941
Binary file not shown.
|
@ -18,9 +18,18 @@ typedef struct geom_t
|
|||
int x, y, w, h;
|
||||
} geom_t;
|
||||
|
||||
typedef enum {
|
||||
POSITION_TOP = 0,
|
||||
POSITION_RIGHT = 1,
|
||||
POSITION_BOTTOM = 2,
|
||||
POSITION_LEFT = 3
|
||||
} Position_On_Client;
|
||||
|
||||
typedef struct Client_Extra
|
||||
{
|
||||
E_Client *client;
|
||||
Evas_Object *drag_object;
|
||||
Evas_Object *hint_object;
|
||||
geom_t expected;
|
||||
struct
|
||||
{
|
||||
|
@ -79,8 +88,10 @@ static struct tiling_mod_main_g
|
|||
E_Config_DD *config_edd, *vdesk_edd;
|
||||
Ecore_Event_Handler *handler_client_resize, *handler_client_move,
|
||||
*handler_client_iconify, *handler_client_uniconify,
|
||||
*handler_desk_set, *handler_compositor_resize;
|
||||
E_Client_Hook *handler_client_resize_begin, *handler_client_add;
|
||||
*handler_desk_set, *handler_compositor_resize,
|
||||
*mouse_up;
|
||||
E_Client_Hook *handler_client_resize_begin, *handler_client_add,
|
||||
*handler_move_begin, *handler_move_end;
|
||||
E_Client_Menu_Hook *client_menu_hook;
|
||||
|
||||
Tiling_Info *tinfo;
|
||||
|
@ -584,6 +595,96 @@ _tilable_client(int x, int y)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static Position_On_Client
|
||||
_calculate_position_preference(E_Client *ec)
|
||||
{
|
||||
int x,y;
|
||||
float bounded_x, bounded_y;
|
||||
Eina_Rectangle rect;
|
||||
evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
|
||||
|
||||
evas_object_geometry_get(ec->frame, &rect.x, &rect.y, &rect.w, &rect.h);
|
||||
|
||||
if (!eina_rectangle_coords_inside(&ec->client, x, y))
|
||||
{
|
||||
ERR("Coorinates are not in there");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//for the calculation we think of a X cross in the rectangle
|
||||
bounded_x = ((float)x - rect.x)/((float)rect.w);
|
||||
bounded_y = ((float)y - rect.y)/((float)rect.h);
|
||||
|
||||
if (bounded_y < bounded_x)
|
||||
{
|
||||
//right upper part
|
||||
if (bounded_y < (1.0 - bounded_x))
|
||||
{
|
||||
//left upper
|
||||
return POSITION_TOP;
|
||||
}
|
||||
else
|
||||
{
|
||||
//right lower
|
||||
return POSITION_RIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//lower left part
|
||||
if (bounded_y < (1.0 - bounded_x))
|
||||
{
|
||||
//left upper
|
||||
return POSITION_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
//right lower
|
||||
return POSITION_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
_insert_client_prefered(E_Client *ec)
|
||||
{
|
||||
Window_Tree *parent;
|
||||
Tiling_Split_Type type = TILING_SPLIT_VERTICAL;
|
||||
Window_Tree *item;
|
||||
Eina_Bool before;
|
||||
int x,y;
|
||||
|
||||
evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
|
||||
parent = _tilable_client(x,y);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
//calculate a good position where we would like to stay
|
||||
Position_On_Client c;
|
||||
|
||||
c = _calculate_position_preference(parent->client);
|
||||
if (c == POSITION_TOP || c == POSITION_BOTTOM)
|
||||
{
|
||||
before = (c == POSITION_TOP);
|
||||
type = TILING_SPLIT_VERTICAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
before = (c == POSITION_LEFT);
|
||||
type = TILING_SPLIT_HORIZONTAL;
|
||||
}
|
||||
|
||||
item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
|
||||
_G.tinfo->tree = tiling_window_tree_insert(_G.tinfo->tree, parent, ec, type, before);
|
||||
}
|
||||
else
|
||||
{
|
||||
_G.tinfo->tree = tiling_window_tree_add(_G.tinfo->tree, NULL, ec, _G.split_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_insert_client(E_Client *ec, Tiling_Split_Type type)
|
||||
{
|
||||
|
@ -592,23 +693,17 @@ _insert_client(E_Client *ec, Tiling_Split_Type type)
|
|||
|
||||
if (ec_focused == ec)
|
||||
{
|
||||
//if we are placing the currently focused client, search for client under the focused client
|
||||
Eina_Rectangle c;
|
||||
|
||||
e_client_geometry_get(ec, &c.x, &c.y, &c.w, &c.h);
|
||||
|
||||
place = _tilable_client(c.x + c.w/2, c.y + c.h/2);
|
||||
_insert_client_prefered(ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
//otherwise place next to the given client
|
||||
place = tiling_window_tree_client_find(_G.tinfo->tree,
|
||||
ec_focused);
|
||||
_G.tinfo->tree = tiling_window_tree_add(_G.tinfo->tree, place, ec, type);
|
||||
|
||||
}
|
||||
|
||||
_G.tinfo->tree =
|
||||
tiling_window_tree_add(_G.tinfo->tree, place, ec, type);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
|
@ -1168,32 +1263,6 @@ _resize_hook(void *data EINA_UNUSED, int type EINA_UNUSED,
|
|||
return true;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_move_hook(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Client *event)
|
||||
{
|
||||
E_Client *ec = event->ec;
|
||||
Client_Extra *extra = tiling_entry_func(ec);
|
||||
|
||||
if (!extra || !extra->tiled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A hack because e doesn't trigger events for all property changes */
|
||||
if (!is_tilable(ec))
|
||||
{
|
||||
toggle_floating(ec);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
e_client_act_move_end(event->ec, NULL);
|
||||
|
||||
_reapply_tree();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
_frame_del_cb(void *data, Evas *evas EINA_UNUSED,
|
||||
Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
|
@ -1434,6 +1503,140 @@ E_API E_Module_Api e_modapi = {
|
|||
"Tiling"
|
||||
};
|
||||
|
||||
static void
|
||||
_center_on_mouse(Evas_Object *obj)
|
||||
{
|
||||
Evas *e;
|
||||
int x,y,w,h;
|
||||
|
||||
e = evas_object_evas_get(obj);
|
||||
evas_pointer_canvas_xy_get(e, &x, &y);
|
||||
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
|
||||
evas_object_move(obj, x-w/2, y-h/2);
|
||||
}
|
||||
|
||||
static void
|
||||
_client_drag_mouse_move(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
Window_Tree *client;
|
||||
int x,y;
|
||||
Client_Extra *extra = tiling_entry_func(data);
|
||||
|
||||
//move the drag object to the center of the object
|
||||
_center_on_mouse(extra->drag_object);
|
||||
|
||||
//now check if we can hint somehow
|
||||
evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
|
||||
client = _tilable_client(x, y);
|
||||
|
||||
//if there is nothing below, we cannot hint to anything
|
||||
if (!client) return;
|
||||
Position_On_Client c = _calculate_position_preference(client->client);
|
||||
|
||||
if (!extra->hint_object)
|
||||
{
|
||||
extra->hint_object = edje_object_add(e_comp->evas);
|
||||
if (!e_theme_edje_object_set(extra->hint_object,
|
||||
"base/theme/modules/tiling",
|
||||
"modules/tiling/indicator"))
|
||||
edje_object_file_set(extra->hint_object, _G.edj_path, "modules/tiling/indicator");
|
||||
evas_object_layer_set(extra->hint_object, E_LAYER_CLIENT_DRAG);
|
||||
evas_object_show(extra->hint_object);
|
||||
}
|
||||
|
||||
//set the geometry on the hint object
|
||||
Eina_Rectangle pos = client->client->client;
|
||||
if (c == POSITION_LEFT)
|
||||
evas_object_geometry_set(extra->hint_object, pos.x, pos.y, pos.w/2, pos.h);
|
||||
else if (c == POSITION_RIGHT)
|
||||
evas_object_geometry_set(extra->hint_object, pos.x+pos.w/2, pos.y, pos.w/2, pos.h);
|
||||
else if (c == POSITION_BOTTOM)
|
||||
evas_object_geometry_set(extra->hint_object, pos.x, pos.y + pos.h/2, pos.w, pos.h/2);
|
||||
else if (c == POSITION_TOP)
|
||||
evas_object_geometry_set(extra->hint_object, pos.x, pos.y, pos.w, pos.h/2);
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
_client_drag_mouse_up(void *data, int event EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
E_Client *ec = data;
|
||||
Client_Extra *extra = tiling_entry_func(ec);
|
||||
|
||||
if (!extra)
|
||||
{
|
||||
return ECORE_CALLBACK_PASS_ON;
|
||||
}
|
||||
|
||||
//we grappend the comp when we started the drag
|
||||
e_comp_ungrab_input(EINA_TRUE, EINA_FALSE);
|
||||
|
||||
//insert the client at the position where the up was
|
||||
_insert_client_prefered(ec);
|
||||
|
||||
//remove the hint object
|
||||
evas_object_del(extra->hint_object);
|
||||
extra->hint_object = NULL;
|
||||
|
||||
//delete the icon on the screen
|
||||
evas_object_del(extra->drag_object);
|
||||
evas_event_callback_del_full(evas_object_evas_get(e_comp->elm), EVAS_CALLBACK_MOUSE_MOVE, _client_drag_mouse_move, ec);
|
||||
extra->drag_object = NULL;
|
||||
|
||||
//bring up the client again
|
||||
ec->hidden = EINA_FALSE;
|
||||
e_client_comp_hidden_set(ec, EINA_FALSE);
|
||||
evas_object_show(ec->frame);
|
||||
|
||||
//remove the mouse up
|
||||
ecore_event_handler_del(_G.mouse_up);
|
||||
_G.mouse_up = NULL;
|
||||
|
||||
_reapply_tree();
|
||||
|
||||
evas_object_focus_set(ec->frame, EINA_TRUE);
|
||||
|
||||
return ECORE_CALLBACK_PASS_ON;
|
||||
}
|
||||
|
||||
static void
|
||||
_client_move_begin(void *data EINA_UNUSED, E_Client *ec)
|
||||
{
|
||||
Client_Extra *extra = tiling_entry_func(ec);
|
||||
Window_Tree *item;
|
||||
|
||||
if (!extra || !extra->tiled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
|
||||
_G.tinfo->tree = tiling_window_tree_remove(_G.tinfo->tree, item);
|
||||
|
||||
e_comp_grab_input(EINA_TRUE, EINA_FALSE);
|
||||
|
||||
//create the drag object
|
||||
extra->drag_object = e_client_icon_add(ec, evas_object_evas_get(e_comp->elm));
|
||||
evas_object_resize(extra->drag_object, 40, 40);
|
||||
evas_object_show(extra->drag_object);
|
||||
evas_object_layer_set(extra->drag_object, E_LAYER_CLIENT_DRAG);
|
||||
|
||||
//listen for mouse moves
|
||||
evas_event_callback_add(evas_object_evas_get(e_comp->elm), EVAS_CALLBACK_MOUSE_MOVE, _client_drag_mouse_move, ec);
|
||||
|
||||
//the up will terminate the whole operation and destroy the drag and restore the client
|
||||
//we cannot use the move_end hook since the move is ending when we are hiding the client
|
||||
_G.mouse_up = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _client_drag_mouse_up, ec);
|
||||
|
||||
//center the drag object on the mouse
|
||||
_center_on_mouse(extra->drag_object);
|
||||
|
||||
ec->hidden = EINA_TRUE;
|
||||
e_client_comp_hidden_set(ec, EINA_TRUE);
|
||||
evas_object_hide(ec->frame);
|
||||
|
||||
_reapply_tree();
|
||||
}
|
||||
|
||||
E_API void *
|
||||
e_modapi_init(E_Module *m)
|
||||
{
|
||||
|
@ -1461,6 +1664,9 @@ e_modapi_init(E_Module *m)
|
|||
|
||||
_G.handler_client_resize_begin =
|
||||
e_client_hook_add(E_CLIENT_HOOK_RESIZE_BEGIN, _resize_begin_hook, NULL);
|
||||
_G.handler_move_begin =
|
||||
e_client_hook_add(E_CLIENT_HOOK_MOVE_BEGIN, _client_move_begin, NULL);
|
||||
|
||||
if (e_comp->comp_type == E_PIXMAP_TYPE_X)
|
||||
_G.handler_client_add =
|
||||
e_client_hook_add(E_CLIENT_HOOK_EVAL_PRE_FRAME_ASSIGN, _add_hook, NULL);
|
||||
|
@ -1468,7 +1674,6 @@ e_modapi_init(E_Module *m)
|
|||
_G.handler_client_add =
|
||||
e_client_hook_add(E_CLIENT_HOOK_UNIGNORE, _add_hook, NULL);
|
||||
HANDLER(_G.handler_client_resize, CLIENT_RESIZE, _resize_hook);
|
||||
HANDLER(_G.handler_client_move, CLIENT_MOVE, _move_hook);
|
||||
|
||||
HANDLER(_G.handler_client_iconify, CLIENT_ICONIFY, _iconify_hook);
|
||||
HANDLER(_G.handler_client_uniconify, CLIENT_UNICONIFY, _iconify_hook);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "window_tree.h"
|
||||
#include "e_mod_tiling.h"
|
||||
|
||||
void tiling_window_tree_dump(Window_Tree *root, int level);
|
||||
|
||||
void
|
||||
tiling_window_tree_walk(Window_Tree *root, void (*func)(void *))
|
||||
{
|
||||
|
@ -26,7 +28,7 @@ tiling_window_tree_free(Window_Tree *root)
|
|||
}
|
||||
|
||||
static void
|
||||
_tiling_window_tree_split_add(Window_Tree *parent, Window_Tree *new_node)
|
||||
_tiling_window_tree_split_add(Window_Tree *parent, Window_Tree *new_node, Eina_Bool append)
|
||||
{
|
||||
/* Make a new node for the parent client and split the weights in half. */
|
||||
Window_Tree *new_parent_client = calloc(1, sizeof(*new_node));
|
||||
|
@ -38,10 +40,13 @@ _tiling_window_tree_split_add(Window_Tree *parent, Window_Tree *new_node)
|
|||
new_parent_client->weight = 0.5;
|
||||
new_node->weight = 0.5;
|
||||
|
||||
parent->children =
|
||||
eina_inlist_append(parent->children, EINA_INLIST_GET(new_parent_client));
|
||||
parent->children =
|
||||
eina_inlist_append(parent->children, EINA_INLIST_GET(new_node));
|
||||
parent->children = eina_inlist_append(parent->children, EINA_INLIST_GET(new_parent_client));
|
||||
|
||||
if (append)
|
||||
parent->children = eina_inlist_append(parent->children, EINA_INLIST_GET(new_node));
|
||||
else
|
||||
parent->children = eina_inlist_prepend(parent->children, EINA_INLIST_GET(new_node));
|
||||
|
||||
}
|
||||
static void
|
||||
_tiling_window_tree_parent_add(Window_Tree *parent, Window_Tree *new_node, Window_Tree *rel, Eina_Bool append)
|
||||
|
@ -88,6 +93,29 @@ _tiling_window_tree_split_type_get(Window_Tree *node)
|
|||
return ret % 2;
|
||||
}
|
||||
|
||||
|
||||
Window_Tree *
|
||||
tiling_window_tree_insert(Window_Tree *root, Window_Tree *buddy,
|
||||
E_Client *client, Tiling_Split_Type split_type, Eina_Bool before)
|
||||
{
|
||||
Window_Tree *new_node = calloc(1, sizeof(*new_node));
|
||||
Window_Tree *parent = buddy->parent;
|
||||
Tiling_Split_Type parent_split_type = _tiling_window_tree_split_type_get(parent);
|
||||
|
||||
new_node->client = client;
|
||||
|
||||
if (parent_split_type == split_type)
|
||||
{
|
||||
_tiling_window_tree_parent_add(parent, new_node, buddy, !before);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tiling_window_tree_split_add(buddy, new_node, !before);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Window_Tree *
|
||||
tiling_window_tree_add(Window_Tree *root, Window_Tree *parent,
|
||||
E_Client *client, Tiling_Split_Type split_type)
|
||||
|
@ -128,7 +156,7 @@ tiling_window_tree_add(Window_Tree *root, Window_Tree *parent,
|
|||
}
|
||||
else
|
||||
{
|
||||
_tiling_window_tree_split_add(parent, new_node);
|
||||
_tiling_window_tree_split_add(parent, new_node, EINA_TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -142,7 +170,7 @@ tiling_window_tree_add(Window_Tree *root, Window_Tree *parent,
|
|||
else
|
||||
{
|
||||
root = calloc(1, sizeof(*root));
|
||||
_tiling_window_tree_split_add(parent, new_node);
|
||||
_tiling_window_tree_split_add(parent, new_node, EINA_TRUE);
|
||||
root->weight = 1.0;
|
||||
root->children =
|
||||
eina_inlist_append(root->children, EINA_INLIST_GET(parent));
|
||||
|
@ -640,7 +668,7 @@ _tiling_window_tree_node_join(Window_Tree *root, Window_Tree *node, Eina_Bool di
|
|||
//unref has not changed the tree
|
||||
if (!pn->children)
|
||||
{
|
||||
_tiling_window_tree_split_add(pn, node);
|
||||
_tiling_window_tree_split_add(pn, node, EINA_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -728,4 +756,4 @@ tiling_window_tree_dump(Window_Tree *root, int level)
|
|||
{
|
||||
tiling_window_tree_dump(itr, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ void tiling_window_tree_walk(Window_Tree *root, void (*func)(void *));
|
|||
Window_Tree *tiling_window_tree_add(Window_Tree *root, Window_Tree *parent,
|
||||
E_Client *client, Tiling_Split_Type split_type);
|
||||
|
||||
Window_Tree *tiling_window_tree_insert(Window_Tree *root, Window_Tree *buddy,
|
||||
E_Client *client, Tiling_Split_Type split_type, Eina_Bool before);
|
||||
|
||||
Window_Tree *tiling_window_tree_remove(Window_Tree *root, Window_Tree *item);
|
||||
|
||||
Window_Tree *tiling_window_tree_client_find(Window_Tree *root,
|
||||
|
|
Loading…
Reference in New Issue