diff --git a/ChangeLog.theme b/ChangeLog.theme index 21ac9cc4..604534f4 100644 --- a/ChangeLog.theme +++ b/ChangeLog.theme @@ -6,8 +6,14 @@ Changes since 1.6.0: -------------------- * In group "terminology/tabbar_back", add signal "bell,off" from "terminology" to unmark a tab as having missed the bell. - * In group "terminology/background", signals "tab,drag" and "tab,drag,stop" - are emitted when the current tab is dragged. + * In group "terminology/background", add signals "hdrag,on" (default) and + "hdrag,off" to restrict (default) tab to be dragged only horizontally + * In group "terminology/background", signals "tab,mouse,down" and + "tab,mouse,up" whenever the left mouse button is pressed on a tab. + * In group "terminology/background", signals "tab,hdrag" and "tab,drag,stop" + are emitted when the current tab is dragged between tabs. + * In group "terminology/background", signal "tab,drag,move" is emitted when + the current tab is dragged outside of the tabbar. Changes since 1.5.0: -------------------- diff --git a/THEME.md b/THEME.md index 72b48b1d..1de21d14 100644 --- a/THEME.md +++ b/THEME.md @@ -54,7 +54,25 @@ It reacts to the following signals: ### `terminology.content` Here is swallowed an object of group `terminology.background`. -__TODO__ +### TODO + +## Signal received +### `tabbar,off` and `tabbar,on` +Whether to display a tab bar. Default is off. +### `tab_btn,off` and `tab_btn,on` +Whether to display a tab button to easily navigate through tabs. Default is off. + +### TODO + +## Signal emitted +### `tab,hdrag` +To notify that the current tab is being dragged. +### `tab,drag,stop` +To notify that the current tab is no longer being dragged. +### `tab,drag,move` +To notify that the current tab is being dragged outside of other tabs. + +### TODO @@ -83,10 +101,14 @@ Where actual text grid goes. Whether to display a tab bar. Default is off. ### `tab_btn,off` and `tab_btn,on` Whether to display a tab button to easily navigate through tabs. Default is off. +### `hdrag,on` and `hdrag,off` +Whether to restrict (default) horizontal tab drag ## Signal emitted -### `tab,drag` and `tab,drage,stop` +### `tab,drag` and `tab,drag,stop` To notify that the current tab is being dragged. +### `tab,mouse,down` and `tab,mouse,up` +Whenever the left mouse button is pressed on a tab. diff --git a/data/themes/default/background.edc b/data/themes/default/background.edc index 1df0f3a0..6f7f070a 100644 --- a/data/themes/default/background.edc +++ b/data/themes/default/background.edc @@ -1361,6 +1361,19 @@ group { name: "terminology/background"; rel2.to_x: "terminology.tab_btn"; fixed: 1 1; } + description { state: "hdrag,off" 0.0; + inherit: "default" 0.0; + } + } + program { + signal: "hdrag,on"; source: "terminology"; + action: STATE_SET "default" 0.0; + target: "tabdrag"; + } + program { + signal: "hdrag,off"; source: "terminology"; + action: STATE_SET "hdrag,off" 0.0; + target: "tabdrag"; } // left boundary of the active tab (dragable 0.0 -> 1.0) part { name: "terminology.tabl"; type: SPACER; @@ -1540,8 +1553,16 @@ group { name: "terminology/background"; signal: "mouse,move"; source: "tabmiddle"; script { new y, h, drag_x, drag_w; + new state[31]; \ + new Float:vl; + if (!get_mouse_buttons()) - return; + return; + get_state(PART:"tabdrag", state, 30, vl); + if (!strcmp(state, "hdrag,off")) { + emit("tab,drag,move", "terminology"); + return + } get_geometry(PART:"tabdrag", drag_x, y, drag_w, h); if (drag_w > 0) { new m_x, m_y; @@ -1561,7 +1582,7 @@ group { name: "terminology/background"; d = (v2 - v1) / 2; set_drag(PART:"terminology.tabl", mid - d, 0.0); set_drag(PART:"terminology.tabr", mid + d, 0.0); - emit("tab,drag", "terminology"); + emit("tab,hdrag", "terminology"); } } } @@ -1570,6 +1591,14 @@ group { name: "terminology/background"; signal: "mouse,down,1,double"; source: "tabmiddle"; action: SIGNAL_EMIT "tab,title" "terminology"; } + program { + signal: "mouse,down,1"; source: "tabmiddle"; + action: SIGNAL_EMIT "tab,mouse,down" "terminology"; + } + program { + signal: "mouse,up,1"; source: "tabmiddle"; + action: SIGNAL_EMIT "tab,mouse,up" "terminology"; + } program { signal: "mouse,clicked,1"; source: "tabclose"; action: SIGNAL_EMIT "tab,close" "terminology"; diff --git a/src/bin/win.c b/src/bin/win.c index 77aaf3e3..505aa061 100644 --- a/src/bin/win.c +++ b/src/bin/win.c @@ -62,6 +62,9 @@ int _win_log_dom = -1; #define PANES_TOP "top" #define PANES_BOTTOM "bottom" +#define DRAG_TIMEOUT 0.4 +#define ANIM_TIME 0.8 + /* {{{ Structs */ typedef struct _Split Split; @@ -69,6 +72,27 @@ typedef struct _Tabbar Tabbar; typedef struct _Solo Solo; typedef struct _Tabs Tabs; typedef struct _Tab_Item Tab_Item; +typedef struct _Anim_Icon Anim_Icon; +typedef struct _Drag_Anim Drag_Anim; + +struct _Anim_Icon +{ + int start_x; + int start_y; + Evas_Object *o; +}; + +struct _Drag_Anim +{ + Evas_Object *icwin; + Evas_Coord mdx; /* Mouse-down x */ + Evas_Coord mdy; /* Mouse-down y */ + Anim_Icon *icon; + Evas *e; + Ecore_Timer *timer; + Ecore_Animator *ea; + Term *term; +}; struct _Tabbar { @@ -99,6 +123,7 @@ struct _Term Evas_Object *tab_region_bg; Evas_Object *tab_inactive; Tab_Item *tab_item; + Drag_Anim *drag_anim; Eina_List *popmedia_queue; Ecore_Timer *sendfile_request_hide_timer; Ecore_Timer *sendfile_progress_hide_timer; @@ -525,6 +550,11 @@ _solo_set_title(Term_Container *tc, { elm_layout_text_set(term->bg, "terminology.tab.title", title); } + if (term->drag_anim && term->drag_anim->icon) + { + elm_layout_text_set(term->drag_anim->icon->o, + "terminology.title", title); + } tc->parent->set_title(tc->parent, tc, title); } @@ -2917,20 +2947,51 @@ _tabs_recompute_drag(Tabs *tabs) edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", v2, 0.0); } +static Eina_Bool +_term_hdrag_on(Term *term, void *data EINA_UNUSED) +{ + elm_layout_signal_emit(term->bg, "hdrag,on", "terminology"); + + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_term_hdrag_off(Term *term, void *data EINA_UNUSED) +{ + elm_layout_signal_emit(term->bg, "hdrag,off", "terminology"); + + return ECORE_CALLBACK_PASS_ON; +} static void -_tabs_on_drag_stop(void *data, - Evas_Object *o EINA_UNUSED, - const char *emission EINA_UNUSED, - const char *source EINA_UNUSED) +_drag_anim_free(Term *term) { - Term *term = data; - Tabs *tabs = evas_object_data_get(term->bg, "tabs"); + Anim_Icon *icon; + Drag_Anim *drag_anim = term->drag_anim; - edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", - tabs->v1_orig, 0.0); - edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", - tabs->v2_orig, 0.0); + if (!drag_anim) + return; + + for_each_term_do(drag_anim->term->wn, &_term_hdrag_on, NULL); + + ecore_timer_del(drag_anim->timer); + drag_anim->timer = NULL; + + ecore_animator_del(drag_anim->ea); + drag_anim->ea = NULL; + + icon = drag_anim->icon; + + if (icon) + { + evas_object_hide(icon->o); + evas_object_del(icon->o); + free(icon); + } + + term->drag_anim = NULL; + term_unref(drag_anim->term); + free(drag_anim); } static void @@ -2953,6 +3014,13 @@ _tabs_on_drag(void *data, tabs = evas_object_data_get(term->bg, "tabs"); n = eina_list_count(tabs->tabs); + tab_item = tabs->current; + assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO); + solo = (Solo*)tab_item->tc; + term = solo->term; + + _drag_anim_free(term); + tab_active_idx = -1; EINA_LIST_FOREACH(tabs->tabs, l, tab_item) { @@ -2961,9 +3029,6 @@ _tabs_on_drag(void *data, break; } tab_item = tabs->current; - assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO); - solo = (Solo*)tab_item->tc; - term = solo->term; edje_object_part_drag_value_get(term->bg_edj, "terminology.tabl", &v1, NULL); @@ -2986,7 +3051,9 @@ _tabs_on_drag(void *data, eina_list_data_get(l), next); _tabs_recompute_drag(tabs); - return; + tab_active_idx++; + if (v2 <= tabs->v2_orig) + return; } while ((tab_active_idx > 0) && ((v1 < tabs->v1_orig - tabs->hysteresis_step) || @@ -3005,10 +3072,205 @@ _tabs_on_drag(void *data, eina_list_data_get(prev), l); _tabs_recompute_drag(tabs); - return; + tab_active_idx--; + if (v1 >= tabs->v1_orig) + return; } } +static void +_tabs_drag_reinsert(Term *term, Evas_Coord mx, Evas_Coord my) +{ + Evas_Coord x = 0, y = 0, w = 0, h = 0; + Tabs *tabs = evas_object_data_get(term->bg, "tabs"); + double mid; + + edje_object_part_geometry_get(term->bg_edj, "tabdrag", + &x, NULL, &w, NULL); + edje_object_part_geometry_get(term->bg_edj, "tabmiddle", + NULL, &y, NULL, &h); + + if (!ELM_RECTS_INTERSECT(x,y,w,h, mx,my,1,1)) + return; + + mid = (double)(mx - x) / (double)w; + edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", mid, 0.0); + edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", mid, 0.0); + + _tabs_on_drag(term, NULL, NULL, NULL); + /* In case there is no drag, need to recompute to something valid */ + _tabs_recompute_drag(tabs); +} + +static void +_tabs_drag_anim_stop(Term *term) +{ + Evas_Coord mx = 0, my = 0; + Drag_Anim *drag_anim = term->drag_anim; + Win *wn = term->wn; + Term_Container *tc = (Term_Container*) wn; + Term *term_at_coords; + + assert(drag_anim); + + evas_pointer_canvas_xy_get(drag_anim->e, &mx, &my); + term_at_coords = tc->find_term_at_coords(tc, mx, my); + if (!term_at_coords) + goto end; + + if (term_at_coords == term) + { + /* Reinsert in same set of Tabs */ + _tabs_drag_reinsert(term, mx, my); + } + else + { + /* Move to different set of Tabs */ + /* TODO: boris */ + } + +end: + _drag_anim_free(term); +} + +static void +_tabs_on_drag_stop(void *data, + Evas_Object *o EINA_UNUSED, + const char *emission EINA_UNUSED, + const char *source EINA_UNUSED) +{ + Term *term = data; + Tabs *tabs = evas_object_data_get(term->bg, "tabs"); + + if (term->drag_anim && term->drag_anim->icon) + { + _tabs_drag_anim_stop(term); + return; + } + _drag_anim_free(term); + + edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", + tabs->v1_orig, 0.0); + edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", + tabs->v2_orig, 0.0); +} + + +static void +_tabs_drag_mouse_move( + void *data, + Evas_Object *obj EINA_UNUSED, + const char *emission EINA_UNUSED, + const char *source EINA_UNUSED) +{ + Term *term = data; + Drag_Anim *drag_anim = term->drag_anim; + Anim_Icon *icon; + int x, y, w, h; + Evas_Coord xm, ym; + + if (!drag_anim) + return; + + icon = drag_anim->icon; + if (!icon) + return; + + evas_object_geometry_get(icon->o, NULL, NULL, &w, &h); + evas_pointer_canvas_xy_get(drag_anim->e, &xm, &ym); + x = (xm - (w/2)); + y = (ym - (h/2)); + evas_object_move(icon->o, x, y); +} + +static Eina_Bool +_drag_anim_play(void *data, double pos) +{ + Drag_Anim *drag_anim = data; + Anim_Icon *icon; + int x, y, w, h; + Evas_Coord xm, ym; + + if (!drag_anim) + return ECORE_CALLBACK_CANCEL; + + icon = drag_anim->icon; + if (pos > 0.99) + { + drag_anim->ea = NULL; /* Avoid deleting on mouse up */ + return ECORE_CALLBACK_CANCEL; + } + + evas_object_geometry_get(icon->o, NULL, NULL, &w, &h); + evas_pointer_canvas_xy_get(drag_anim->e, &xm, &ym); + x = icon->start_x + (pos * (xm - (icon->start_x + (w/2)))); + y = icon->start_y + (pos * (ym - (icon->start_y + (h/2)))); + evas_object_move(icon->o, x, y); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_drag_anim_start(void *data) +{ + /* Start icons animation before actually drag-starts */ + Drag_Anim *drag_anim = data; + Evas_Coord w, h; + Term *term = drag_anim->term; + Anim_Icon *icon = calloc(1, sizeof(*icon)); + Evas_Object *o = elm_layout_add(term->bg); + + theme_apply_elm(o, term->config, "terminology/tabbar_back"); + elm_layout_text_set(o, "terminology.title", + term->container->title); + + for_each_term_do(drag_anim->term->wn, &_term_hdrag_off, NULL); + + edje_object_part_geometry_get(term->bg_edj, "tabmiddle", + &icon->start_x, &icon->start_y, &w, &h); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + + evas_object_move(o, icon->start_x, icon->start_y); + evas_object_resize(o, w, h); + evas_object_show(o); + + icon->o = o; + drag_anim->icon = icon; + + drag_anim->ea = ecore_animator_timeline_add(DRAG_TIMEOUT, + _drag_anim_play, drag_anim); + + drag_anim->timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_tabs_mouse_down( + void *data, + Evas_Object *obj EINA_UNUSED, + const char *emission EINA_UNUSED, + const char *source EINA_UNUSED) +{ + /* Launch a timer to start drag animation */ + Term *term = data; + Evas_Coord mx = 0, my = 0; + Drag_Anim *drag_anim = calloc(1, sizeof(*drag_anim)); + + drag_anim->e = evas_object_evas_get(term->bg); + evas_pointer_canvas_xy_get(drag_anim->e, &mx, &my); + + term_ref(term); + + term->drag_anim = drag_anim; + drag_anim->mdx = mx; + drag_anim->mdy = my; + drag_anim->term = term; + drag_anim->timer = ecore_timer_add(DRAG_TIMEOUT, + _drag_anim_start, drag_anim); +} + + static Evas_Object* _tab_inactive_get_or_create(Evas *canvas, Term *term, @@ -6008,10 +6270,14 @@ _term_bg_config(Term *term) _cb_tab_close, term); elm_layout_signal_callback_add(term->bg, "tab,title", "terminology", _cb_tab_title, term); - elm_layout_signal_callback_add(term->bg, "tab,drag", "*", + elm_layout_signal_callback_add(term->bg, "tab,hdrag", "*", _tabs_on_drag, term); + elm_layout_signal_callback_add(term->bg, "tab,drag,move", "*", + _tabs_drag_mouse_move, term); elm_layout_signal_callback_add(term->bg, "tab,drag,stop", "*", _tabs_on_drag_stop, term); + elm_layout_signal_callback_add(term->bg, "tab,mouse,down", "*", + _tabs_mouse_down, term); elm_layout_content_set(term->core, "terminology.content", term->termio); elm_layout_content_set(term->bg, "terminology.content", term->core); elm_layout_content_set(term->bg, "terminology.miniview", term->miniview);