From e5298074e70b1ccc4109b2a2b2b88db983b67184 Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Thu, 19 Mar 2009 13:36:46 +0000 Subject: [PATCH] work on bettre touchscreen text selection support. not 100% done yet. been playing with ideas. this one seems best. SVN revision: 39579 --- legacy/elementary/data/themes/default.edc | 33 +++ legacy/elementary/src/lib/Elementary.h.in | 1 + legacy/elementary/src/lib/elc_hoversel.c | 27 ++- legacy/elementary/src/lib/elm_entry.c | 239 +++++++++++++++++++++- legacy/elementary/src/lib/els_scroller.c | 3 + 5 files changed, 295 insertions(+), 8 deletions(-) diff --git a/legacy/elementary/data/themes/default.edc b/legacy/elementary/data/themes/default.edc index b443680159..82dc9ca749 100644 --- a/legacy/elementary/data/themes/default.edc +++ b/legacy/elementary/data/themes/default.edc @@ -3649,6 +3649,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: EDITABLE; + select_mode: EXPLICIT; multiline: 1; source: "elm/entry/selection/default"; // selection under // source2: "X"; // selection over @@ -3681,6 +3682,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: EDITABLE; + select_mode: EXPLICIT; multiline: 1; source: "elm/entry/selection/default"; // selection under source4: "elm/entry/cursor/default"; // cursorover @@ -3692,6 +3694,20 @@ collections { } } } + part { name: "sel"; + type: RECT; + mouse_events: 0; + description { state: "default" 0.0; + align: 1.0 1.0; + max: 16 16; + aspect: 1.0 1.0; + color: 255 0 0 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + color: 255 0 0 50; + } + } } programs { program { name: "focus"; @@ -3700,6 +3716,18 @@ collections { action: FOCUS_SET; target: "elm.text"; } + program { name: "selmode0"; + signal: "elm,state,select,on"; + source: "elm"; + action: STATE_SET "visible" 0.0; + target: "sel"; + } + program { name: "selmode1"; + signal: "elm,state,select,off"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "sel"; + } } } @@ -3722,6 +3750,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: EDITABLE; + select_mode: EXPLICIT; multiline: 0; source: "elm/entry/selection/default"; // selection under source4: "elm/entry/cursor/default"; // cursorover @@ -3751,6 +3780,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: PLAIN; + select_mode: EXPLICIT; multiline: 0; source: "elm/entry/selection/default"; // selection under source5: "elm/entry/anchor/default"; // anchor under @@ -3779,6 +3809,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: PLAIN; + select_mode: EXPLICIT; multiline: 1; source: "elm/entry/selection/default"; // selection under source5: "elm/entry/anchor/default"; // anchor under @@ -3807,6 +3838,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: PLAIN; + select_mode: EXPLICIT; multiline: 1; source: "elm/entry/selection/default"; // selection under source5: "elm/entry/anchor/default"; // anchor under @@ -3835,6 +3867,7 @@ collections { mouse_events: 1; scale: 1; entry_mode: PASSWORD; + select_mode: EXPLICIT; multiline: 0; source: "elm/entry/selection/default"; // selection under source4: "elm/entry/cursor/default"; // cursorover diff --git a/legacy/elementary/src/lib/Elementary.h.in b/legacy/elementary/src/lib/Elementary.h.in index fd74d219a0..eeec12e371 100644 --- a/legacy/elementary/src/lib/Elementary.h.in +++ b/legacy/elementary/src/lib/Elementary.h.in @@ -489,6 +489,7 @@ extern "C" { EAPI void elm_hoversel_hover_parent_set(Evas_Object *obj, Evas_Object *parent); EAPI void elm_hoversel_label_set(Evas_Object *obj, const char *label); EAPI void elm_hoversel_icon_set(Evas_Object *obj, Evas_Object *icon); + EAPI void elm_hoversel_hover_begin(Evas_Object *obj); EAPI void elm_hoversel_hover_end(Evas_Object *obj); EAPI Elm_Hoversel_Item *elm_hoversel_item_add(Evas_Object *obj, const char *label, const char *icon_file, Elm_Icon_Type icon_type, void (*func) (void *data, Evas_Object *obj, void *event_info), const void *data); EAPI void elm_hoversel_item_del(Elm_Hoversel_Item *item); diff --git a/legacy/elementary/src/lib/elc_hoversel.c b/legacy/elementary/src/lib/elc_hoversel.c index 87f792c775..3404d822fd 100644 --- a/legacy/elementary/src/lib/elc_hoversel.c +++ b/legacy/elementary/src/lib/elc_hoversel.c @@ -78,17 +78,17 @@ _item_clicked(void *data, Evas_Object *obj, void *event_info) } static void -_button_clicked(void *data, Evas_Object *obj, void *event_info) +_activate(Evas_Object *obj) { - Widget_Data *wd = elm_widget_data_get(data); + Widget_Data *wd = elm_widget_data_get(obj); if (!wd) return; Evas_Object *bt, *bx, *ic; const Eina_List *l; const Elm_Hoversel_Item *it; - wd->hover = elm_hover_add(data); + wd->hover = elm_hover_add(obj); elm_hover_style_set(wd->hover, "hoversel_vertical"); - evas_object_smart_callback_add(wd->hover, "clicked", _hover_clicked, data); + evas_object_smart_callback_add(wd->hover, "clicked", _hover_clicked, obj); elm_hover_parent_set(wd->hover, wd->hover_parent); elm_hover_target_set(wd->hover, wd->btn); @@ -102,7 +102,7 @@ _button_clicked(void *data, Evas_Object *obj, void *event_info) elm_button_label_set(bt, it->label); if (it->icon_file) { - ic = elm_icon_add(data); + ic = elm_icon_add(obj); elm_icon_scale_set(ic, 0, 1); if (it->icon_type == ELM_ICON_FILE) elm_icon_file_set(ic, it->icon_file, NULL); @@ -125,7 +125,13 @@ _button_clicked(void *data, Evas_Object *obj, void *event_info) evas_object_show(bx); evas_object_show(wd->hover); - evas_object_smart_callback_call(data, "clicked", NULL); + evas_object_smart_callback_call(obj, "clicked", NULL); +} + +static void +_button_clicked(void *data, Evas_Object *obj, void *event_info) +{ + _activate(data); } static void @@ -188,6 +194,15 @@ elm_hoversel_icon_set(Evas_Object *obj, Evas_Object *icon) elm_button_icon_set(wd->btn, icon); } +EAPI void +elm_hoversel_hover_begin(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (wd->hover) return; + _activate(obj); +} + EAPI void elm_hoversel_hover_end(Evas_Object *obj) { diff --git a/legacy/elementary/src/lib/elm_entry.c b/legacy/elementary/src/lib/elm_entry.c index 3fa3dd3086..bac3b3e53c 100644 --- a/legacy/elementary/src/lib/elm_entry.c +++ b/legacy/elementary/src/lib/elm_entry.c @@ -6,11 +6,14 @@ typedef struct _Widget_Data Widget_Data; struct _Widget_Data { Evas_Object *ent; + Evas_Object *hoversel; Ecore_Job *deferred_recalc_job; Ecore_Event_Handler *sel_notify_handler; Ecore_Event_Handler *sel_clear_handler; + Ecore_Timer *longpress_timer; const char *cut_sel; Evas_Coord lastw; + Evas_Coord downx, downy; Evas_Bool changed : 1; Evas_Bool linewrap : 1; Evas_Bool single_line : 1; @@ -18,6 +21,7 @@ struct _Widget_Data Evas_Bool editable : 1; Evas_Bool selection_asked : 1; Evas_Bool have_selection : 1; + Evas_Bool selmode : 1; }; static void _del_hook(Evas_Object *obj); @@ -48,6 +52,7 @@ _del_hook(Evas_Object *obj) #endif if (wd->cut_sel) eina_stringshare_del(wd->cut_sel); if (wd->deferred_recalc_job) ecore_job_del(wd->deferred_recalc_job); + if (wd->longpress_timer) ecore_timer_del(wd->longpress_timer); free(wd); } @@ -127,13 +132,225 @@ _on_focus_hook(void *data, Evas_Object *obj) } } +static void +_hoversel_position(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + Evas_Coord cx, cy, cw, ch, x, y, mw, mh; + evas_object_geometry_get(wd->ent, &x, &y, NULL, NULL); + edje_object_part_text_cursor_geometry_get(wd->ent, "elm.text", + &cx, &cy, &cw, &ch); + evas_object_size_hint_min_get(wd->hoversel, &mw, &mh); + if (cw < mw) + { + cx += (cw - mw) / 2; + cw = mw; + } + if (ch < mh) + { + cy += (ch - mh) / 2; + ch = mh; + } + evas_object_move(wd->hoversel, x + cx, y + cy); + evas_object_resize(wd->hoversel, cw, ch); +} + +static void +_move(void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + if (wd->hoversel) _hoversel_position(data); +} + static void _resize(void *data, Evas *e, Evas_Object *obj, void *event_info) { Widget_Data *wd = elm_widget_data_get(data); if (wd->linewrap) _sizing_eval(data); - Evas_Coord ww, hh; - evas_object_geometry_get(wd->ent, NULL, NULL, &ww, &hh); + if (wd->hoversel) _hoversel_position(data); +// Evas_Coord ww, hh; +// evas_object_geometry_get(wd->ent, NULL, NULL, &ww, &hh); +} + +static void +_dismissed(void *data, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + if (wd->hoversel) evas_object_hide(wd->hoversel); + if (wd->selmode) + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 1); +} + +static void +_select(void *data, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + wd->selmode = 1; + edje_object_part_text_select_none(wd->ent, "elm.text"); + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 1); + edje_object_signal_emit(wd->ent, "elm,state,select,on", "elm"); + elm_widget_scroll_hold_push(data); +} + +static void +_paste(void *data, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + evas_object_smart_callback_call(data, "selection,paste", NULL); + if (wd->sel_notify_handler) + { +#ifdef HAVE_ELEMENTARY_X + if (elm_win_xwindow_get(elm_widget_top_get(data))) + { + ecore_x_selection_primary_request + (elm_win_xwindow_get(elm_widget_top_get(data)), + ECORE_X_SELECTION_TARGET_UTF8_STRING); + wd->selection_asked = 1; + } +#endif + } +} + +static void +_store_selection(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + const char *sel = edje_object_part_text_selection_get(wd->ent, "elm.text"); + if (wd->cut_sel) eina_stringshare_del(wd->cut_sel); + wd->cut_sel = eina_stringshare_add(sel); +} + +static void +_cut(void *data, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + wd->selmode = 0; + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 0); + edje_object_signal_emit(wd->ent, "elm,state,select,off", "elm"); + elm_widget_scroll_hold_pop(data); + _store_selection(data); + edje_object_part_text_insert(wd->ent, "elm.text", ""); + edje_object_part_text_select_none(wd->ent, "elm.text"); +} + +static void +_copy(void *data, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + wd->selmode = 0; + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 0); + edje_object_signal_emit(wd->ent, "elm,state,select,off", "elm"); + elm_widget_scroll_hold_pop(data); + _store_selection(data); + edje_object_part_text_select_none(wd->ent, "elm.text"); +} + +static int +_long_press(void *data) +{ + Widget_Data *wd = elm_widget_data_get(data); + if (wd->hoversel) evas_object_del(wd->hoversel); + wd->hoversel = elm_hoversel_add(data); + elm_widget_sub_object_add(data, wd->hoversel); + elm_hoversel_label_set(wd->hoversel, "Text"); + elm_hoversel_hover_parent_set(wd->hoversel, elm_widget_top_get(data)); + evas_object_smart_callback_add(wd->hoversel, "dismissed", _dismissed, data); + if (!wd->selmode) + { + elm_hoversel_item_add(wd->hoversel, "Select", NULL, ELM_ICON_NONE, _select, data); + elm_hoversel_item_add(wd->hoversel, "Paste", NULL, ELM_ICON_NONE, _paste, data); + } + else + { + elm_hoversel_item_add(wd->hoversel, "Copy", NULL, ELM_ICON_NONE, _copy, data); + elm_hoversel_item_add(wd->hoversel, "Cut", NULL, ELM_ICON_NONE, _cut, data); + } + if (wd->hoversel) + { + _hoversel_position(data); + evas_object_show(wd->hoversel); + elm_hoversel_hover_begin(wd->hoversel); + } + wd->longpress_timer = NULL; + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 0); + return 0; +} + +static void +_mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + Evas_Event_Mouse_Down *ev = event_info; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; + if (ev->button != 1) return; + // if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) + if (wd->longpress_timer) ecore_timer_del(wd->longpress_timer); + wd->longpress_timer = ecore_timer_add(1.0, _long_press, data); + wd->downx = ev->canvas.x; + wd->downy = ev->canvas.y; +} + +static void +_mouse_up(void *data, Evas *evas, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + Evas_Event_Mouse_Up *ev = event_info; + if (ev->button != 1) return; + if (wd->longpress_timer) + { + ecore_timer_del(wd->longpress_timer); + wd->longpress_timer = NULL; + } +} + +static void +_mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info) +{ + Widget_Data *wd = elm_widget_data_get(data); + Evas_Event_Mouse_Move *ev = event_info; + if (!wd->selmode) + { + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) + { + if (wd->longpress_timer) + { + ecore_timer_del(wd->longpress_timer); + wd->longpress_timer = NULL; + } + } + else if (wd->longpress_timer) + { + Evas_Coord dx, dy; + + dx = wd->downx - ev->cur.canvas.x; + dx *= dx; + dy = wd->downy - ev->cur.canvas.y; + dy *= dy; + if ((dx + dy) > + ((_elm_config->finger_size / 2) * + (_elm_config->finger_size / 2))) + { + ecore_timer_del(wd->longpress_timer); + wd->longpress_timer = NULL; + } + } + } + else if (wd->longpress_timer) + { + Evas_Coord dx, dy; + + dx = wd->downx - ev->cur.canvas.x; + dx *= dx; + dy = wd->downy - ev->cur.canvas.y; + dy *= dy; + if ((dx + dy) > + ((_elm_config->finger_size / 2) * + (_elm_config->finger_size / 2))) + { + ecore_timer_del(wd->longpress_timer); + wd->longpress_timer = NULL; + } + } } static const char * @@ -335,6 +552,7 @@ _text_to_mkup(const char *text) else if (ch == '\t') str = _str_append(str, "<\t>", &str_len, &str_alloc); else if (ch == '<') str = _str_append(str, "<", &str_len, &str_alloc); else if (ch == '>') str = _str_append(str, ">", &str_len, &str_alloc); + else if (ch == '&') str = _str_append(str, "&", &str_len, &str_alloc); else { char tstr[16]; @@ -594,6 +812,7 @@ _event_selection_notify(void *data, int type, void *event) char *txt = _text_to_mkup(text_data->text); if (txt) { + printf("inst: %s\n", txt); elm_entry_entry_insert(data, txt); free(txt); } @@ -639,7 +858,11 @@ elm_entry_add(Evas_Object *parent) wd->editable = 1; wd->ent = edje_object_add(e); + evas_object_event_callback_add(wd->ent, EVAS_CALLBACK_MOVE, _move, obj); evas_object_event_callback_add(wd->ent, EVAS_CALLBACK_RESIZE, _resize, obj); + evas_object_event_callback_add(wd->ent, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, obj); + evas_object_event_callback_add(wd->ent, EVAS_CALLBACK_MOUSE_UP, _mouse_up, obj); + evas_object_event_callback_add(wd->ent, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, obj); _elm_theme_set(wd->ent, "entry", "base", "default"); edje_object_signal_callback_add(wd->ent, "entry,changed", "elm.text", _signal_entry_changed, obj); @@ -782,6 +1005,12 @@ EAPI void elm_entry_select_none(Evas_Object *obj) { Widget_Data *wd = elm_widget_data_get(obj); + if (wd->selmode) + { + wd->selmode = 0; + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 0); + edje_object_signal_emit(wd->ent, "elm,state,select,off", "elm"); + } wd->have_selection = 0; edje_object_part_text_select_none(wd->ent, "elm.text"); } @@ -790,6 +1019,12 @@ EAPI void elm_entry_select_all(Evas_Object *obj) { Widget_Data *wd = elm_widget_data_get(obj); + if (wd->selmode) + { + wd->selmode = 0; + edje_object_part_text_select_allow_set(wd->ent, "elm.text", 0); + edje_object_signal_emit(wd->ent, "elm,state,select,off", "elm"); + } wd->have_selection = 1; edje_object_part_text_select_all(wd->ent, "elm.text"); } diff --git a/legacy/elementary/src/lib/els_scroller.c b/legacy/elementary/src/lib/els_scroller.c index f4a615b106..bb7876702c 100644 --- a/legacy/elementary/src/lib/els_scroller.c +++ b/legacy/elementary/src/lib/els_scroller.c @@ -205,6 +205,7 @@ elm_smart_scroller_child_pos_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y) double vx, vy; API_ENTRY return; + // FIXME: allow for bounce outside of range sd->pan_func.max_get(sd->pan_obj, &mx, &my); if (mx > 0) vx = (double)x / (double)mx; else vx = 0.0; @@ -514,6 +515,7 @@ _smart_event_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info) sd = data; ev = event_info; + // FIXME: respect elm_widget_scroll_hold_get of parent container if (_elm_config->thumbscroll_enable) { if (ev->button == 1) @@ -583,6 +585,7 @@ _smart_event_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info) sd = data; ev = event_info; + // FIXME: respect elm_widget_scroll_hold_get of parent container if (_elm_config->thumbscroll_enable) { if (sd->down.now)