diff --git a/src/lib/elementary/elm_entry.c b/src/lib/elementary/elm_entry.c index 4cbf725cfb..737be8141b 100644 --- a/src/lib/elementary/elm_entry.c +++ b/src/lib/elementary/elm_entry.c @@ -1,6277 +1,6278 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_LAYOUT_CALC_PROTECTED #define EFL_ACCESS_OBJECT_PROTECTED #define EFL_ACCESS_TEXT_PROTECTED #define EFL_ACCESS_EDITABLE_TEXT_PROTECTED #define ELM_LAYOUT_PROTECTED #define EFL_UI_FOCUS_OBJECT_PROTECTED #define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED 1 #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_PART_PROTECTED #include #include #include "elm_priv.h" #include "elm_widget_entry.h" #include "elm_entry_part.eo.h" #include "elm_part_helper.h" #include "elm_hoversel_eo.h" #define MY_CLASS ELM_ENTRY_CLASS #define MY_CLASS_PFX elm_entry #define MY_CLASS_NAME "Elm_Entry" #define MY_CLASS_NAME_LEGACY "elm_entry" /* Maximum chunk size to be inserted to the entry at once * FIXME: This size is arbitrary, should probably choose a better size. * Possibly also find a way to set it to a low value for weak computers, * and to a big value for better computers. */ #define ELM_ENTRY_CHUNK_SIZE 10000 #define ELM_ENTRY_DELAY_WRITE_TIME 2.0 #define ELM_PRIV_ENTRY_SIGNALS(cmd) \ cmd(SIG_ABORTED, "aborted", "") \ cmd(SIG_ACTIVATED, "activated", "") \ cmd(SIG_ANCHOR_CLICKED, "anchor,clicked", "") \ cmd(SIG_ANCHOR_DOWN, "anchor,down", "") \ cmd(SIG_ANCHOR_HOVER_OPENED, "anchor,hover,opened", "") \ cmd(SIG_ANCHOR_IN, "anchor,in", "") \ cmd(SIG_ANCHOR_OUT, "anchor,out", "") \ cmd(SIG_ANCHOR_UP, "anchor,up", "") \ cmd(SIG_CHANGED, "changed", "") \ cmd(SIG_CHANGED_USER, "changed,user", "") \ cmd(SIG_CLICKED, "clicked", "") \ cmd(SIG_CLICKED_DOUBLE, "clicked,double", "") \ cmd(SIG_CLICKED_TRIPLE, "clicked,triple", "") \ cmd(SIG_CURSOR_CHANGED, "cursor,changed", "") \ cmd(SIG_CURSOR_CHANGED_MANUAL, "cursor,changed,manual", "") \ cmd(SIG_FOCUSED, "focused", "") \ cmd(SIG_UNFOCUSED, "unfocused", "") \ cmd(SIG_LONGPRESSED, "longpressed", "") \ cmd(SIG_MAX_LENGTH, "maxlength,reached", "") \ cmd(SIG_PREEDIT_CHANGED, "preedit,changed", "") \ cmd(SIG_PRESS, "press", "") \ cmd(SIG_REDO_REQUEST, "redo,request", "") \ cmd(SIG_SELECTION_CHANGED, "selection,changed", "") \ cmd(SIG_SELECTION_CLEARED, "selection,cleared", "") \ cmd(SIG_SELECTION_COPY, "selection,copy", "") \ cmd(SIG_SELECTION_CUT, "selection,cut", "") \ cmd(SIG_SELECTION_PASTE, "selection,paste", "") \ cmd(SIG_SELECTION_START, "selection,start", "") \ cmd(SIG_TEXT_SET_DONE, "text,set,done", "") \ cmd(SIG_THEME_CHANGED, "theme,changed", "") \ cmd(SIG_UNDO_REQUEST, "undo,request", "") \ cmd(SIG_REJECTED, "rejected", "") ELM_PRIV_ENTRY_SIGNALS(ELM_PRIV_STATIC_VARIABLE_DECLARE); #define ENTRY_PASSWORD_MASK_CHARACTER 0x002A static const Evas_Smart_Cb_Description _smart_callbacks[] = { ELM_PRIV_ENTRY_SIGNALS(ELM_PRIV_SMART_CALLBACKS_DESC) {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {NULL, NULL} }; #undef ELM_PRIV_ENTRY_SIGNALS static const Elm_Layout_Part_Alias_Description _text_aliases[] = { {"default", "elm.text"}, {"guide", "elm.guide"}, {NULL, NULL} }; static const Elm_Layout_Part_Alias_Description _content_aliases[] = { {"icon", "elm.swallow.icon"}, {"end", "elm.swallow.end"}, {NULL, NULL} }; static Eina_List *entries = NULL; struct _Mod_Api { void (*obj_hook)(Evas_Object *obj); void (*obj_unhook)(Evas_Object *obj); void (*obj_longpress)(Evas_Object *obj); }; static void _create_selection_handlers(Evas_Object *obj, Elm_Entry_Data *sd); static void _magnifier_move(void *data); static Evas_Object * _entry_win_get(Evas_Object *obj) { Evas_Object *top; top = elm_widget_top_get(obj); if ((!elm_win_window_id_get(top)) && (elm_win_type_get(top) == ELM_WIN_INLINED_IMAGE)) top = efl_ui_win_inlined_parent_get(top); return top; } static Mod_Api * _module_find(Evas_Object *obj EINA_UNUSED) { static Elm_Module *m = NULL; if (m) goto ok; // already found - just use if (!(m = _elm_module_find_as("entry/api"))) return NULL; // get module api m->api = malloc(sizeof(Mod_Api)); if (!m->api) return NULL; ((Mod_Api *)(m->api))->obj_hook = // called on creation _elm_module_symbol_get(m, "obj_hook"); ((Mod_Api *)(m->api))->obj_unhook = // called on deletion _elm_module_symbol_get(m, "obj_unhook"); ((Mod_Api *)(m->api))->obj_longpress = // called on long press menu _elm_module_symbol_get(m, "obj_longpress"); ok: // ok - return api return m->api; } static char * _file_load(Eo *obj) { Eina_File *f; char *text = NULL; void *tmp = NULL; size_t size; f = eina_file_dup(efl_file_mmap_get(obj)); size = eina_file_size_get(f); if (size) { tmp = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); if (!tmp) goto on_error; } text = malloc(size + 1); if (!text) goto on_error; if (size) memcpy(text, tmp, size); text[size] = 0; if (eina_file_map_faulted(f, tmp)) { ELM_SAFE_FREE(text, free); } on_error: if (tmp) eina_file_map_free(f, tmp); eina_file_close(f); return text; } static char * _plain_load(Eo *obj) { char *text; text = _file_load(obj); if (text) { char *text2; text2 = elm_entry_utf8_to_markup(text); free(text); return text2; } return NULL; } static Eina_Error _load_do(Evas_Object *obj) { char *text; Eina_Bool fail = EINA_FALSE; Eina_Error err = 0; ELM_ENTRY_DATA_GET(obj, sd); if (!sd->file) { elm_object_text_set(obj, ""); return 0; } switch (sd->format) { case ELM_TEXT_FORMAT_PLAIN_UTF8: text = _plain_load(obj); fail = !text; break; case ELM_TEXT_FORMAT_MARKUP_UTF8: text = _file_load(obj); fail = !text; break; default: text = NULL; err = EINVAL; break; } if (fail) { err = errno; /* FIXME: this is more like a hint but not totally accurate... */ if (!err) err = ENOENT; } if (text) { elm_object_text_set(obj, text); free(text); return 0; } elm_object_text_set(obj, ""); return err; } static void _utf8_markup_save(const char *file, const char *text) { FILE *f; if (!text) { ecore_file_unlink(file); return; } f = fopen(file, "wb"); if (!f) { ERR("Failed to open %s for writing", file); return; } if (fputs(text, f) == EOF) ERR("Failed to write text to file %s", file); fclose(f); } static void _utf8_plain_save(const char *file, const char *text) { char *text2; text2 = elm_entry_markup_to_utf8(text); if (!text2) return; _utf8_markup_save(file, text2); free(text2); } static void _save_do(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (!efl_file_loaded_get(obj)) return; switch (sd->format) { case ELM_TEXT_FORMAT_PLAIN_UTF8: _utf8_plain_save(sd->file, elm_object_text_get(obj)); break; case ELM_TEXT_FORMAT_MARKUP_UTF8: _utf8_markup_save(sd->file, elm_object_text_get(obj)); break; default: break; } } static Eina_Bool _delay_write(void *data) { ELM_ENTRY_DATA_GET(data, sd); _save_do(data); sd->delay_write = NULL; return ECORE_CALLBACK_CANCEL; } static void _elm_entry_guide_update(Evas_Object *obj, Eina_Bool has_text) { ELM_ENTRY_DATA_GET(obj, sd); if ((has_text) && (!sd->has_text)) edje_object_signal_emit(sd->entry_edje, "elm,guide,disabled", "elm"); else if ((!has_text) && (sd->has_text)) edje_object_signal_emit(sd->entry_edje, "elm,guide,enabled", "elm"); sd->has_text = has_text; } static void _validate(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); Eina_Bool res; Elm_Validate_Content vc; Eina_Strbuf *buf; if (sd->validators == 0) return; vc.text = edje_object_part_text_get(sd->entry_edje, "elm.text"); res = efl_event_callback_legacy_call(obj, ELM_ENTRY_EVENT_VALIDATE, (void *)&vc); buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "validation,%s,%s", vc.signal, res == EINA_FALSE ? "fail" : "pass"); edje_object_signal_emit(sd->scr_edje, eina_strbuf_string_get(buf), "elm"); eina_tmpstr_del(vc.signal); eina_strbuf_free(buf); } static Elm_Entry_Markup_Filter * _filter_new(Elm_Entry_Filter_Cb func, void *data) { Elm_Entry_Markup_Filter *tf = ELM_NEW(Elm_Entry_Markup_Filter); if (!tf) return NULL; tf->func = func; tf->orig_data = data; if (func == elm_entry_filter_limit_size) { Elm_Entry_Filter_Limit_Size *lim = data, *lim2; if (!data) { free(tf); return NULL; } lim2 = malloc(sizeof(Elm_Entry_Filter_Limit_Size)); if (!lim2) { free(tf); return NULL; } memcpy(lim2, lim, sizeof(Elm_Entry_Filter_Limit_Size)); tf->data = lim2; } else if (func == elm_entry_filter_accept_set) { Elm_Entry_Filter_Accept_Set *as = data, *as2; if (!data) { free(tf); return NULL; } as2 = malloc(sizeof(Elm_Entry_Filter_Accept_Set)); if (!as2) { free(tf); return NULL; } if (as->accepted) as2->accepted = eina_stringshare_add(as->accepted); else as2->accepted = NULL; if (as->rejected) as2->rejected = eina_stringshare_add(as->rejected); else as2->rejected = NULL; tf->data = as2; } else tf->data = data; return tf; } static void _filter_free(Elm_Entry_Markup_Filter *tf) { if (tf->func == elm_entry_filter_limit_size) { Elm_Entry_Filter_Limit_Size *lim = tf->data; free(lim); } else if (tf->func == elm_entry_filter_accept_set) { Elm_Entry_Filter_Accept_Set *as = tf->data; if (as) { eina_stringshare_del(as->accepted); eina_stringshare_del(as->rejected); free(as); } } free(tf); } static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl) { ELM_ENTRY_DATA_GET(obj, sd); edje_object_mirrored_set(sd->entry_edje, rtl); if (sd->anchor_hover.hover) efl_ui_mirrored_set(sd->anchor_hover.hover, rtl); } static void _hide_selection_handler(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (!sd->start_handler) return; if (sd->start_handler_shown) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } if (sd->end_handler_shown) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } } static Eina_Rectangle * _viewport_region_get(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); Eina_Rectangle *rect = eina_rectangle_new(0, 0, 0, 0); Evas_Object *parent; if (!rect) return NULL; if (sd->scroll) elm_interface_scrollable_content_viewport_geometry_get (obj, &rect->x, &rect->y, &rect->w, &rect->h); else evas_object_geometry_get(sd->entry_edje, &rect->x, &rect->y, &rect->w, &rect->h); parent = elm_widget_parent_get(obj); while (parent) { if (efl_isa(parent, ELM_INTERFACE_SCROLLABLE_MIXIN)) { Eina_Rectangle r; EINA_RECTANGLE_SET(&r, 0, 0, 0, 0); evas_object_geometry_get(parent, &r.x, &r.y, &r.w, &r.h); if (!eina_rectangle_intersection(rect, &r)) { rect->x = rect->y = rect->w = rect->h = 0; break; } } parent = elm_widget_parent_get(parent); } return rect; } static void _update_selection_handler(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); Evas_Coord sx, sy, sh; Evas_Coord ent_x, ent_y; Evas_Coord ex, ey, eh; int start_pos, end_pos, last_pos; if (!sd->sel_handler_disabled) { Eina_Rectangle *rect; Evas_Coord hx, hy; Eina_Bool hidden = EINA_FALSE; if (!sd->start_handler) _create_selection_handlers(obj, sd); rect = _viewport_region_get(obj); start_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_BEGIN); end_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_END); evas_object_geometry_get(sd->entry_edje, &ent_x, &ent_y, NULL, NULL); last_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, start_pos); edje_object_part_text_cursor_geometry_get(sd->entry_edje, "elm.text", &sx, &sy, NULL, &sh); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, end_pos); edje_object_part_text_cursor_geometry_get(sd->entry_edje, "elm.text", &ex, &ey, NULL, &eh); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, last_pos); if (start_pos < end_pos) { hx = ent_x + sx; hy = ent_y + sy + sh; evas_object_move(sd->start_handler, hx, hy); } else { hx = ent_x + ex; hy = ent_y + ey + eh; evas_object_move(sd->start_handler, hx, hy); } if (!eina_rectangle_xcoord_inside(rect, hx) || !eina_rectangle_ycoord_inside(rect, hy)) { hidden = EINA_TRUE; } if (!sd->start_handler_shown && !hidden) { edje_object_signal_emit(sd->start_handler, "elm,handler,show", "elm"); sd->start_handler_shown = EINA_TRUE; } else if (sd->start_handler_shown && hidden) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } hidden = EINA_FALSE; if (start_pos < end_pos) { hx = ent_x + ex; hy = ent_y + ey + eh; evas_object_move(sd->end_handler, hx, hy); } else { hx = ent_x + sx; hy = ent_y + sy + sh; evas_object_move(sd->end_handler, hx, hy); } if (!eina_rectangle_xcoord_inside(rect, hx) || !eina_rectangle_ycoord_inside(rect, hy)) { hidden = EINA_TRUE; } if (!sd->end_handler_shown && !hidden) { edje_object_signal_emit(sd->end_handler, "elm,handler,show", "elm"); sd->end_handler_shown = EINA_TRUE; } else if (sd->end_handler_shown && hidden) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } eina_rectangle_free(rect); } else { if (sd->start_handler_shown) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } if (sd->end_handler_shown) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } } } static const char * _elm_entry_theme_group_get(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (sd->editable) { if (sd->password) return "base-password"; else { if (sd->single_line) return "base-single"; else { switch (sd->line_wrap) { case ELM_WRAP_CHAR: return "base-charwrap"; case ELM_WRAP_WORD: return "base"; case ELM_WRAP_MIXED: return "base-mixedwrap"; case ELM_WRAP_NONE: default: return "base-nowrap"; } } } } else { if (sd->password) return "base-password"; else { if (sd->single_line) return "base-single-noedit"; else { switch (sd->line_wrap) { case ELM_WRAP_CHAR: return "base-noedit-charwrap"; case ELM_WRAP_WORD: return "base-noedit"; case ELM_WRAP_MIXED: return "base-noedit-mixedwrap"; case ELM_WRAP_NONE: default: return "base-nowrap-noedit"; } } } } } static void _edje_entry_user_insert(Evas_Object *obj, const char *data) { if (!data) return; ELM_ENTRY_DATA_GET(obj, sd); sd->changed = EINA_TRUE; edje_object_part_text_user_insert(sd->entry_edje, "elm.text", data); elm_layout_sizing_eval(obj); } static Eina_Bool _selection_data_cb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Selection_Data *sel_data) { char *buf; if (!sel_data->data) return EINA_FALSE; ELM_ENTRY_DATA_GET(obj, sd); buf = malloc(sel_data->len + 1); if (!buf) { ERR("Failed to allocate memory, obj: %p", obj); return EINA_FALSE; } memcpy(buf, sel_data->data, sel_data->len); buf[sel_data->len] = '\0'; if ((sel_data->format & ELM_SEL_FORMAT_IMAGE) && (sd->cnp_mode != ELM_CNP_MODE_NO_IMAGE)) { char *entry_tag; int len; static const char *tag_string = ""; len = strlen(tag_string) + strlen(buf); entry_tag = alloca(len + 1); snprintf(entry_tag, len + 1, tag_string, buf); _edje_entry_user_insert(obj, entry_tag); } else if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) { char *txt = _elm_util_text_to_mkup(buf); if (txt) { _edje_entry_user_insert(obj, txt); free(txt); } else { ERR("Failed to convert text to markup text!"); } } else { if (!(sel_data->format & ELM_SEL_FORMAT_MARKUP)) { char *txt = _elm_util_text_to_mkup(buf); if (txt) { _edje_entry_user_insert(obj, txt); free(txt); } } else _edje_entry_user_insert(obj, buf); } free(buf); return EINA_TRUE; } static void _dnd_enter_cb(void *data EINA_UNUSED, Evas_Object *obj) { elm_object_focus_set(obj, EINA_TRUE); } static void _dnd_leave_cb(void *data EINA_UNUSED, Evas_Object *obj) { if (_elm_config->desktop_entry) elm_object_focus_set(obj, EINA_FALSE); } static void _dnd_pos_cb(void *data EINA_UNUSED, Evas_Object *obj, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action EINA_UNUSED) { int pos; Evas_Coord ox, oy, ex, ey; ELM_ENTRY_DATA_GET(obj, sd); evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); x = x + ox - ex; y = y + oy - ey; edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, x, y); pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_USER); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); } static Eina_Bool _dnd_drop_cb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Selection_Data *drop) { Eina_Bool rv; ELM_ENTRY_DATA_GET(obj, sd); rv = edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, drop->x, drop->y); if (!rv) WRN("Warning: Failed to position cursor: paste anyway"); rv = _selection_data_cb(NULL, obj, drop); return rv; } static Elm_Sel_Format _get_drop_format(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if ((sd->editable) && (!sd->single_line) && (!sd->password) && (!sd->disabled)) return ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_IMAGE; return ELM_SEL_FORMAT_MARKUP; } static void _flush_disabled_state(Eo *obj, Elm_Entry_Data *sd) { const char *emission; emission = efl_ui_widget_disabled_get(obj) ? "elm,state,disabled" : "elm,state,enabled"; edje_object_signal_emit(sd->entry_edje, emission, "elm"); if (sd->scroll) { edje_object_signal_emit(sd->scr_edje, emission, "elm"); elm_interface_scrollable_freeze_set(obj, efl_ui_widget_disabled_get(obj)); } } /* we can't reuse layout's here, because it's on entry_edje only */ EOLIAN static void _elm_entry_efl_ui_widget_disabled_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool disabled) { efl_ui_widget_disabled_set(efl_super(obj, MY_CLASS), disabled); elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); _flush_disabled_state(obj, sd); sd->disabled = efl_ui_widget_disabled_get(obj); if (!efl_ui_widget_disabled_get(obj)) { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); } } /* It gets the background object from from_edje object and * sets the background object to to_edje object. * The background object has to be moved to proper Edje object * when scrollable status is changed. */ static void _elm_entry_background_switch(Evas_Object *from_edje, Evas_Object *to_edje) { Evas_Object *bg_obj; if (!from_edje || !to_edje) return; if (edje_object_part_exists(from_edje, "elm.swallow.background") && edje_object_part_exists(to_edje, "elm.swallow.background") && !edje_object_part_swallow_get(to_edje, "elm.swallow.background")) { bg_obj = edje_object_part_swallow_get(from_edje, "elm.swallow.background"); if (bg_obj) { edje_object_part_unswallow(from_edje, bg_obj); edje_object_part_swallow(to_edje, "elm.swallow.background", bg_obj); } } } /* we can't issue the layout's theming code here, cause it assumes an * unique edje object, always */ EOLIAN static Eina_Error _elm_entry_efl_ui_widget_theme_apply(Eo *obj, Elm_Entry_Data *sd) { const char *str; const char *t; const char *stl_user; const char *style = elm_widget_style_get(obj); Eina_Error theme_apply; int cursor_pos; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC); // Note: We are skipping elm_layout here! This is by design. // This assumes the following inheritance: my_class -> layout -> widget ... theme_apply = efl_ui_widget_theme_apply(efl_cast(obj, EFL_UI_WIDGET_CLASS)); if (theme_apply == EFL_UI_THEME_APPLY_ERROR_GENERIC) return EFL_UI_THEME_APPLY_ERROR_GENERIC; evas_event_freeze(evas_object_evas_get(obj)); edje_object_part_text_hide_visible_password(sd->entry_edje, "elm.text"); edje_object_mirrored_set (wd->resize_obj, efl_ui_mirrored_get(obj)); edje_object_scale_set (wd->resize_obj, efl_gfx_entity_scale_get(obj) * elm_config_scale_get()); if (sd->scroll) { edje_object_scale_set (sd->entry_edje, efl_gfx_entity_scale_get(obj) * elm_config_scale_get()); } _mirrored_set(obj, efl_ui_mirrored_get(obj)); stl_user = eina_stringshare_add(edje_object_part_text_style_user_peek(sd->entry_edje, "elm.text")); t = eina_stringshare_add(elm_object_text_get(obj)); elm_widget_theme_object_set (obj, sd->entry_edje, "entry", _elm_entry_theme_group_get(obj), style); if (sd->sel_allow && _elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); else edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_part_text_style_user_push(sd->entry_edje, "elm.text", stl_user); eina_stringshare_del(stl_user); cursor_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); if (cursor_pos != sd->cursor_pos) { sd->cursor_pos = cursor_pos; sd->cur_changed = EINA_TRUE; } elm_object_text_set(obj, t); eina_stringshare_del(t); if (elm_widget_disabled_get(obj)) edje_object_signal_emit(sd->entry_edje, "elm,state,disabled", "elm"); edje_object_part_text_input_panel_layout_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Layout)sd->input_panel_layout); edje_object_part_text_input_panel_layout_variation_set (sd->entry_edje, "elm.text", sd->input_panel_layout_variation); edje_object_part_text_autocapital_type_set (sd->entry_edje, "elm.text", (Edje_Text_Autocapital_Type)sd->autocapital_type); edje_object_part_text_prediction_allow_set (sd->entry_edje, "elm.text", sd->prediction_allow); edje_object_part_text_input_hint_set (sd->entry_edje, "elm.text", (Edje_Input_Hints)sd->input_hints); edje_object_part_text_input_panel_enabled_set (sd->entry_edje, "elm.text", sd->input_panel_enable); edje_object_part_text_input_panel_imdata_set (sd->entry_edje, "elm.text", sd->input_panel_imdata, sd->input_panel_imdata_len); edje_object_part_text_input_panel_return_key_type_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Return_Key_Type)sd->input_panel_return_key_type); edje_object_part_text_input_panel_return_key_disabled_set (sd->entry_edje, "elm.text", sd->input_panel_return_key_disabled); edje_object_part_text_input_panel_show_on_demand_set (sd->entry_edje, "elm.text", sd->input_panel_show_on_demand); edje_object_part_text_prediction_hint_set (sd->entry_edje, "elm.text", sd->prediction_hint); evas_object_ref(obj); if (cursor_pos) elm_entry_cursor_pos_set(obj, cursor_pos); if (efl_ui_focus_object_focus_get(obj)) { edje_object_signal_emit(sd->entry_edje, "elm,action,focus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,focus", "elm"); } edje_object_message_signal_process(sd->entry_edje); Evas_Object* clip = evas_object_clip_get(sd->entry_edje); evas_object_clip_set(sd->hit_rect, clip); if (sd->scroll) { Eina_Error err = EFL_UI_THEME_APPLY_ERROR_GENERIC; efl_ui_mirrored_set(obj, efl_ui_mirrored_get(obj)); if (sd->single_line) err = elm_widget_theme_object_set (obj, sd->scr_edje, "scroller", "entry_single", style); if (err) elm_widget_theme_object_set (obj, sd->scr_edje, "scroller", "entry", style); + elm_interface_scrollable_reset_signals(obj); _elm_entry_background_switch(sd->entry_edje, sd->scr_edje); str = edje_object_data_get(sd->scr_edje, "focus_highlight"); } else { _elm_entry_background_switch(sd->scr_edje, sd->entry_edje); str = edje_object_data_get(sd->entry_edje, "focus_highlight"); } if ((str) && (!strcmp(str, "on"))) elm_widget_highlight_in_theme_set(obj, EINA_TRUE); else elm_widget_highlight_in_theme_set(obj, EINA_FALSE); if (sd->start_handler) { _elm_theme_object_set(obj, sd->start_handler, "entry", "handler/start", style); _elm_theme_object_set(obj, sd->end_handler, "entry", "handler/end", style); } elm_entry_icon_visible_set(obj, EINA_TRUE); elm_entry_end_visible_set(obj, EINA_TRUE); if (sd->scroll) efl_layout_signal_emit(sd->entry_edje, "elm,scroll,enable", "elm"); else efl_layout_signal_emit(sd->entry_edje, "elm,scroll,disable", "elm"); sd->changed = EINA_TRUE; elm_layout_sizing_eval(obj); sd->has_text = !sd->has_text; _elm_entry_guide_update(obj, !sd->has_text); evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); efl_event_callback_legacy_call(obj, EFL_UI_LAYOUT_EVENT_THEME_CHANGED, NULL); evas_object_unref(obj); _flush_disabled_state(obj, sd); return theme_apply; } static void _cursor_geometry_recalc(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (!sd->deferred_recalc_job) { if (sd->cur_changed) { Eina_Rect sr = {}; sd->cur_changed = EINA_FALSE; edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", &sr.x, &sr.y, &sr.w, &sr.h); elm_widget_show_region_set(obj, sr, EINA_FALSE); } } else sd->deferred_cur = EINA_TRUE; } static void _deferred_recalc_job(void *data) { Evas_Coord minh = -1, resw = -1, minw = -1, fw = 0, fh = 0; ELM_ENTRY_DATA_GET(data, sd); sd->deferred_recalc_job = NULL; evas_object_geometry_get(sd->entry_edje, NULL, NULL, &resw, NULL); edje_object_size_min_restricted_calc(sd->entry_edje, &minw, &minh, resw, 0); elm_coords_finger_size_adjust(1, &minw, 1, &minh); /* This is a hack to workaround the way min size hints are treated. * If the minimum width is smaller than the restricted width, it * means the minimum doesn't matter. */ if (minw <= resw) { Evas_Coord ominw = -1; evas_object_size_hint_combined_min_get(data, &ominw, NULL); minw = ominw; } sd->ent_mw = minw; sd->ent_mh = minh; elm_coords_finger_size_adjust(1, &fw, 1, &fh); if (sd->scroll) { Evas_Coord vmw = 0, vmh = 0; edje_object_size_min_calc(sd->scr_edje, &vmw, &vmh); if (sd->single_line) { evas_object_size_hint_min_set(data, vmw, minh + vmh); evas_object_size_hint_max_set(data, -1, minh + vmh); } else { evas_object_size_hint_min_set(data, vmw, vmh); evas_object_size_hint_max_set(data, -1, -1); } } else { if (sd->single_line) { evas_object_size_hint_min_set(data, minw, minh); evas_object_size_hint_max_set(data, -1, minh); } else { evas_object_size_hint_min_set(data, fw, minh); evas_object_size_hint_max_set(data, -1, -1); } } if (sd->deferred_cur) { if (sd->cur_changed) { Eina_Rect sr = {}; sd->cur_changed = EINA_FALSE; edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", &sr.x, &sr.y, &sr.w, &sr.h); elm_widget_show_region_set(data, sr, EINA_FALSE); } } } EOLIAN static void _elm_entry_efl_canvas_group_group_calculate(Eo *obj, Elm_Entry_Data *sd) { Evas_Coord minw = -1, minh = -1; Evas_Coord resw, resh; evas_object_geometry_get(obj, NULL, NULL, &resw, &resh); if (sd->line_wrap) { if ((resw == sd->last_w) && (!sd->changed)) { if (sd->scroll) { Evas_Coord vw = 0, vh = 0, w = 0, h = 0; elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &vw, &vh); w = sd->ent_mw; h = sd->ent_mh; if (vw > sd->ent_mw) w = vw; if (vh > sd->ent_mh) h = vh; evas_object_resize(sd->entry_edje, w, h); return; } return; } evas_event_freeze(evas_object_evas_get(obj)); sd->changed = EINA_FALSE; sd->last_w = resw; if (sd->scroll) { Evas_Coord vw = 0, vh = 0, vmw = 0, vmh = 0, w = -1, h = -1; evas_object_resize(sd->scr_edje, resw, resh); edje_object_size_min_calc(sd->scr_edje, &vmw, &vmh); elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &vw, &vh); edje_object_size_min_restricted_calc (sd->entry_edje, &minw, &minh, vw, 0); elm_coords_finger_size_adjust(1, &minw, 1, &minh); /* This is a hack to workaround the way min size hints * are treated. If the minimum width is smaller than the * restricted width, it means the minimum doesn't * matter. */ if (minw <= vw) { Evas_Coord ominw = -1; evas_object_size_hint_combined_min_get(sd->entry_edje, &ominw, NULL); minw = ominw; } sd->ent_mw = minw; sd->ent_mh = minh; if ((minw > 0) && (vw < minw)) vw = minw; if (minh > vh) vh = minh; if (sd->single_line) h = vmh + minh; else h = vmh; evas_object_resize(sd->entry_edje, vw, vh); evas_object_size_hint_min_set(obj, w, h); if (sd->single_line) evas_object_size_hint_max_set(obj, -1, h); else evas_object_size_hint_max_set(obj, -1, -1); } else { ecore_job_del(sd->deferred_recalc_job); sd->deferred_recalc_job = ecore_job_add(_deferred_recalc_job, obj); } evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); } else { if (!sd->changed) return; evas_event_freeze(evas_object_evas_get(obj)); sd->changed = EINA_FALSE; sd->last_w = resw; if (sd->scroll) { Evas_Coord vw = 0, vh = 0, vmw = 0, vmh = 0, w = -1, h = -1; edje_object_size_min_calc(sd->entry_edje, &minw, &minh); sd->ent_mw = minw; sd->ent_mh = minh; elm_coords_finger_size_adjust(1, &minw, 1, &minh); elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &vw, &vh); if (minw > vw) vw = minw; if (minh > vh) vh = minh; evas_object_resize(sd->entry_edje, vw, vh); edje_object_size_min_calc(sd->scr_edje, &vmw, &vmh); if (sd->single_line) h = vmh + minh; else h = vmh; evas_object_size_hint_min_set(obj, w, h); if (sd->single_line) evas_object_size_hint_max_set(obj, -1, h); else evas_object_size_hint_max_set(obj, -1, -1); } else { edje_object_size_min_calc(sd->entry_edje, &minw, &minh); sd->ent_mw = minw; sd->ent_mh = minh; elm_coords_finger_size_adjust(1, &minw, 1, &minh); evas_object_size_hint_min_set(obj, minw, minh); if (sd->single_line) evas_object_size_hint_max_set(obj, -1, minh); else evas_object_size_hint_max_set(obj, -1, -1); } evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); } _cursor_geometry_recalc(obj); } static void _return_key_enabled_check(Evas_Object *obj) { Eina_Bool return_key_disabled = EINA_FALSE; ELM_ENTRY_DATA_GET(obj, sd); if (!sd->auto_return_key) return; if (elm_entry_is_empty(obj) == EINA_TRUE) return_key_disabled = EINA_TRUE; elm_entry_input_panel_return_key_disabled_set(obj, return_key_disabled); } static void _elm_entry_focus_update(Eo *obj, Elm_Entry_Data *sd) { Evas_Object *top; Eina_Bool top_is_win = EINA_FALSE; top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) top_is_win = EINA_TRUE; if (efl_ui_focus_object_focus_get(obj) && sd->editable) { evas_object_focus_set(sd->entry_edje, EINA_TRUE); edje_object_signal_emit(sd->entry_edje, "elm,action,focus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,focus", "elm"); if (top && top_is_win && sd->input_panel_enable && !sd->input_panel_show_on_demand && !edje_object_part_text_imf_context_get(sd->entry_edje, "elm.text")) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_ON); if (_elm_config->atspi_mode) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_FOCUSED, EINA_TRUE); _return_key_enabled_check(obj); _validate(obj); } else { edje_object_signal_emit(sd->entry_edje, "elm,action,unfocus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,unfocus", "elm"); evas_object_focus_set(sd->entry_edje, EINA_FALSE); if (top && top_is_win && sd->input_panel_enable && !edje_object_part_text_imf_context_get(sd->entry_edje, "elm.text")) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_OFF); if (_elm_config->atspi_mode) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_FOCUSED, EINA_FALSE); if (_elm_config->selection_clear_enable) { if ((sd->have_selection) && (!sd->hoversel)) { sd->sel_mode = EINA_FALSE; elm_widget_scroll_hold_pop(obj); edje_object_part_text_select_allow_set(sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); edje_object_part_text_select_none(sd->entry_edje, "elm.text"); } } if (sd->scr_edje) edje_object_signal_emit(sd->scr_edje, "validation,default", "elm"); } } EOLIAN static Eina_Bool _elm_entry_efl_ui_focus_object_on_focus_update(Eo *obj, Elm_Entry_Data *sd) { _elm_entry_focus_update(obj, sd); return EINA_TRUE; } EOLIAN static Eina_Rect _elm_entry_efl_ui_widget_interest_region_get(const Eo *obj, Elm_Entry_Data *sd) { Evas_Coord cx, cy, cw, ch; Evas_Coord edx, edy; Evas_Coord elx, ely, elw, elh; Eina_Rect r; edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", &cx, &cy, &cw, &ch); if (sd->single_line) { evas_object_geometry_get(sd->entry_edje, &edx, &edy, NULL, &ch); cy = 0; } else { evas_object_geometry_get(sd->entry_edje, &edx, &edy, NULL, NULL); } evas_object_geometry_get(obj, &elx, &ely, &elw, &elh); r.x = cx + edx - elx; if ((cw < elw) && (r.x + cw > elw)) r.x = elw - cw; r.y = cy + edy - ely; if ((ch < elh) && (r.y + ch > elh)) r.y = elh - ch; r.w = MAX(cw, 1); r.h = MAX(ch, 1); return r; } static void _show_region_hook(void *data EINA_UNUSED, Evas_Object *obj, Eina_Rect r) { elm_interface_scrollable_content_region_show(obj, r.x, r.y, r.w, r.h); } EOLIAN static Eina_Bool _elm_entry_efl_ui_widget_widget_sub_object_del(Eo *obj, Elm_Entry_Data *sd, Evas_Object *sobj) { Eina_Bool ret = EINA_FALSE; /* unfortunately entry doesn't follow the signal pattern * elm,state,icon,{visible,hidden}, so we have to replicate this * smart function */ if (sobj == edje_object_part_swallow_get(sd->scr_edje, "elm.swallow.icon")) { edje_object_signal_emit(sd->scr_edje, "elm,action,hide,icon", "elm"); } else if (sobj == edje_object_part_swallow_get(sd->scr_edje, "elm.swallow.end")) { edje_object_signal_emit(sd->scr_edje, "elm,action,hide,end", "elm"); } ret = elm_widget_sub_object_del(efl_super(obj, MY_CLASS), sobj); if (!ret) return EINA_FALSE; return EINA_TRUE; } static void _hoversel_position(Evas_Object *obj) { Evas_Coord cx, cy, cw, ch, x, y, mw, mh, w, h; ELM_ENTRY_DATA_GET(obj, sd); cx = cy = 0; cw = ch = 1; evas_object_geometry_get(sd->entry_edje, &x, &y, &w, &h); if (sd->use_down) { cx = sd->downx - x; cy = sd->downy - y; cw = 1; ch = 1; } else edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", &cx, &cy, &cw, &ch); if (efl_canvas_group_need_recalculate_get(sd->hoversel)) efl_canvas_group_calculate(sd->hoversel); evas_object_size_hint_combined_min_get(sd->hoversel, &mw, &mh); if (cx + mw > w) cx = w - mw; if (cy + mh > h) cy = h - mh; evas_object_geometry_set(sd->hoversel, x + cx, y + cy, mw, mh); } static void _hover_del_job(void *data) { ELM_ENTRY_DATA_GET(data, sd); ELM_SAFE_FREE(sd->hoversel, evas_object_del); sd->hov_deljob = NULL; } static void _hover_dismissed_cb(void *data, const Efl_Event *event EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); sd->use_down = 0; if (sd->hoversel) evas_object_hide(sd->hoversel); if (sd->sel_mode) { if (!_elm_config->desktop_entry) { if (!sd->password) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); } } elm_widget_scroll_freeze_pop(data); ecore_job_del(sd->hov_deljob); sd->hov_deljob = ecore_job_add(_hover_del_job, data); } static void _hover_selected_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd->sel_allow) return; sd->sel_mode = EINA_TRUE; edje_object_part_text_select_none(sd->entry_edje, "elm.text"); if (!_elm_config->desktop_entry) { if (!sd->password) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); } edje_object_signal_emit(sd->entry_edje, "elm,state,select,on", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_push(data); } static char * _item_tags_remove(const char *str) { char *ret; Eina_Strbuf *buf; if (!str) return NULL; buf = eina_strbuf_new(); if (!buf) return NULL; if (!eina_strbuf_append(buf, str)) { eina_strbuf_free(buf); return NULL; } while (EINA_TRUE) { const char *temp = eina_strbuf_string_get(buf); char *start_tag = NULL; char *end_tag = NULL; size_t sindex; size_t eindex; start_tag = strstr(temp, ""); else break; if (!end_tag || start_tag > end_tag) break; sindex = start_tag - temp; eindex = end_tag - temp + 1; if (!eina_strbuf_remove(buf, sindex, eindex)) break; } ret = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return ret; } void _elm_entry_entry_paste(Evas_Object *obj, const char *entry) { char *str = NULL; if (!entry) return; ELM_ENTRY_CHECK(obj); ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; if (sd->cnp_mode == ELM_CNP_MODE_NO_IMAGE) { str = _item_tags_remove(entry); if (!str) str = strdup(entry); } else str = strdup(entry); if (!str) str = (char *)entry; _edje_entry_user_insert(obj, str); if (str != entry) free(str); } static void _paste_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Sel_Format formats = ELM_SEL_FORMAT_MARKUP; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_PASTE, NULL); sd->selection_asked = EINA_TRUE; if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) formats = ELM_SEL_FORMAT_TEXT; else if (sd->cnp_mode != ELM_CNP_MODE_NO_IMAGE) formats |= ELM_SEL_FORMAT_IMAGE; elm_cnp_selection_get (data, ELM_SEL_TYPE_CLIPBOARD, formats, _selection_data_cb, NULL); } static void _selection_clear(void *data, Elm_Sel_Type selection) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (!sd->have_selection) return; if ((selection == ELM_SEL_TYPE_CLIPBOARD) || (selection == ELM_SEL_TYPE_PRIMARY)) { elm_entry_select_none(data); } } static void _selection_store(Elm_Sel_Type seltype, Evas_Object *obj) { const char *sel; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; sel = edje_object_part_text_selection_get(sd->entry_edje, "elm.text"); if ((!sel) || (!sel[0])) return; /* avoid deleting our own selection */ elm_cnp_selection_set (obj, seltype, ELM_SEL_FORMAT_MARKUP, sel, strlen(sel)); elm_cnp_selection_loss_callback_set(obj, seltype, _selection_clear, obj); if (seltype == ELM_SEL_TYPE_CLIPBOARD) eina_stringshare_replace(&sd->cut_sel, sel); } static void _cut_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_CUT, NULL); /* Store it */ sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_pop(data); _selection_store(ELM_SEL_TYPE_CLIPBOARD, data); _edje_entry_user_insert(data, ""); } static void _copy_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_COPY, NULL); sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) { edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); elm_widget_scroll_hold_pop(data); } _selection_store(ELM_SEL_TYPE_CLIPBOARD, data); } static void _hover_cancel_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_pop(data); edje_object_part_text_select_none(sd->entry_edje, "elm.text"); } static void _hover_item_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Entry_Context_Menu_Item *it = data; if (!it) return; if (it->func) it->func(it->data, it->obj, it); } static void _menu_call(Evas_Object *obj) { Evas_Object *top; const Eina_List *l; const Elm_Entry_Context_Menu_Item *it; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; if (sd->anchor_hover.hover) return; efl_event_callback_legacy_call(obj, ELM_ENTRY_EVENT_CONTEXT_OPEN, NULL); if ((sd->api) && (sd->api->obj_longpress)) { sd->api->obj_longpress(obj); } else if (sd->context_menu) { const char *context_menu_orientation; Eina_Bool ownersel; ownersel = elm_selection_selection_has_owner(obj); if (!sd->items) { /* prevent stupid blank hoversel */ if (sd->have_selection && sd->password) return; if (_elm_config->desktop_entry && (!sd->have_selection) && ((!sd->editable) || (!ownersel))) return; } if (sd->hoversel) evas_object_del(sd->hoversel); else elm_widget_scroll_freeze_push(obj); sd->hoversel = elm_hoversel_add(obj); elm_object_tree_focus_allow_set(sd->hoversel, EINA_FALSE); context_menu_orientation = edje_object_data_get (sd->entry_edje, "context_menu_orientation"); if ((context_menu_orientation) && (!strcmp(context_menu_orientation, "horizontal"))) elm_hoversel_horizontal_set(sd->hoversel, EINA_TRUE); elm_object_style_set(sd->hoversel, "entry"); elm_widget_sub_object_add(obj, sd->hoversel); elm_object_text_set(sd->hoversel, "Text"); top = elm_widget_top_get(obj); if (top) elm_hoversel_hover_parent_set(sd->hoversel, top); efl_event_callback_add (sd->hoversel, ELM_HOVERSEL_EVENT_DISMISSED, _hover_dismissed_cb, obj); if (sd->have_selection) { if (!sd->password) { if (sd->editable) elm_hoversel_item_add (sd->hoversel, E_("Cut"), NULL, ELM_ICON_NONE, _cut_cb, obj); elm_hoversel_item_add (sd->hoversel, E_("Copy"), NULL, ELM_ICON_NONE, _copy_cb, obj); if (sd->editable && ownersel) elm_hoversel_item_add (sd->hoversel, E_("Paste"), NULL, ELM_ICON_NONE, _paste_cb, obj); elm_hoversel_item_add (sd->hoversel, E_("Cancel"), NULL, ELM_ICON_NONE, _hover_cancel_cb, obj); } } else { if (!sd->sel_mode) { if (sd->sel_allow && !_elm_config->desktop_entry) { if (!sd->password) elm_hoversel_item_add (sd->hoversel, E_("Select"), NULL, ELM_ICON_NONE, _hover_selected_cb, obj); } if (ownersel) { if (sd->editable) elm_hoversel_item_add (sd->hoversel, E_("Paste"), NULL, ELM_ICON_NONE, _paste_cb, obj); } } else elm_hoversel_item_add (sd->hoversel, E_("Cancel"), NULL, ELM_ICON_NONE, _hover_cancel_cb, obj); } EINA_LIST_FOREACH(sd->items, l, it) { elm_hoversel_item_add(sd->hoversel, it->label, it->icon_file, it->icon_type, _hover_item_clicked_cb, it); } if (sd->hoversel) { _hoversel_position(obj); elm_hoversel_hover_begin(sd->hoversel); evas_object_show(sd->hoversel); } if (!_elm_config->desktop_entry) { edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_part_text_select_abort(sd->entry_edje, "elm.text"); } } } static void _magnifier_proxy_update(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _magnifier_move(data); } static void _magnifier_create(void *data) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; double scale = _elm_config->magnifier_scale; Evas *e; Evas_Coord w, h, mw, mh; evas_object_del(sd->mgf_proxy); evas_object_del(sd->mgf_bg); evas_object_del(sd->mgf_clip); e = evas_object_evas_get(data); //Bg sd->mgf_bg = edje_object_add(e); _elm_theme_object_set(data, sd->mgf_bg, "entry", "magnifier", "default"); evas_object_show(sd->mgf_bg); //Proxy sd->mgf_proxy = evas_object_image_add(e); evas_object_event_callback_add(sd->mgf_proxy, EVAS_CALLBACK_RESIZE, _magnifier_proxy_update, data); evas_object_event_callback_add(sd->mgf_proxy, EVAS_CALLBACK_MOVE, _magnifier_proxy_update, data); edje_object_part_swallow(sd->mgf_bg, "elm.swallow.content", sd->mgf_proxy); evas_object_image_source_set(sd->mgf_proxy, data); evas_object_geometry_get(data, NULL, NULL, &w, &h); //Clipper sd->mgf_clip = evas_object_rectangle_add(e); evas_object_color_set(sd->mgf_clip, 0, 0, 0, 0); evas_object_show(sd->mgf_clip); evas_object_clip_set(sd->mgf_proxy, sd->mgf_clip); mw = (Evas_Coord)(scale * (float) w); mh = (Evas_Coord)(scale * (float) h); if ((mw <= 0) || (mh <= 0)) return; evas_object_layer_set(sd->mgf_bg, EVAS_LAYER_MAX); evas_object_layer_set(sd->mgf_proxy, EVAS_LAYER_MAX); } static void _magnifier_move(void *data) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; Evas_Coord x, y, w, h; Evas_Coord px, py, pw, ph; Evas_Coord cx, cy, ch; Evas_Coord ex, ey; Evas_Coord mx, my, mw, mh; Evas_Coord diffx = 0; Evas_Object *top; double fx, fy, fw, fh; double scale = _elm_config->magnifier_scale; edje_object_part_text_cursor_geometry_get(sd->entry_edje, "elm.text", &cx, &cy, NULL, &ch); if (sd->scroll) { Evas_Coord ox, oy; evas_object_geometry_get(sd->scr_edje, &ex, &ey, NULL, NULL); elm_interface_scrollable_content_pos_get(data, &ox, &oy); ex -= ox; ey -= oy; } else { evas_object_geometry_get(data, &ex, &ey, NULL, NULL); } cx += ex; cy += ey; // calculate the position of the magnifier edje_object_parts_extends_calc(sd->mgf_bg, &x, &y, &w, &h); mx = cx - x - (w / 2); my = cy - y - h; mw = w; mh = h; // keep magnifier inside window top = elm_widget_top_get(data); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) { Evas_Coord wh, ww; evas_object_geometry_get(top, NULL, NULL, &ww, &wh); if (mx < 0) { diffx = mx; mx = 0; } if (mx + mw > ww) { diffx = - (ww - (mx + mw)); mx = ww - mw; } if (my < 0) my = 0; if (my + mh > wh) my = wh - mh; } // move the magnifier to the proper position evas_object_move(sd->mgf_bg, mx, my); //Set the Proxy Render Area evas_object_geometry_get(data, &x, &y, &w, &h); evas_object_geometry_get(sd->mgf_proxy, &px, &py, &pw, &ph); fx = -((cx - x) * scale) + (pw * 0.5) + diffx; fy = -((cy - y) * scale) + (ph * 0.5) - (ch * 0.5 * scale); fw = w * scale; fh = h * scale; evas_object_image_fill_set(sd->mgf_proxy, fx, fy, fw, fh); //Update Clipper Area int tx = fx; int ty = fy; int tw = fw; int th = fh; if (tx > 0) px += tx; if (ty > 0) py += ty; if (-(tx - pw) > tw) pw -= (-((tx - pw) + tw)); if (-(ty - ph) > th) ph -= (-((ty - ph) + th)); evas_object_geometry_set(sd->mgf_clip, px, py, pw, ph); } static void _magnifier_hide(void *data) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; edje_object_signal_emit(sd->mgf_bg, "elm,action,hide,magnifier", "elm"); elm_widget_scroll_freeze_pop(data); evas_object_hide(sd->mgf_clip); } static void _magnifier_show(void *data) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; edje_object_signal_emit(sd->mgf_bg, "elm,action,show,magnifier", "elm"); elm_widget_scroll_freeze_push(data); evas_object_show(sd->mgf_clip); } static Eina_Bool _long_press_cb(void *data) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return ECORE_CALLBACK_CANCEL; if (_elm_config->magnifier_enable) { _magnifier_create(data); _magnifier_show(data); _magnifier_move(data); } /* Context menu will not appear if context menu disabled is set * as false on a long press callback */ else if (!_elm_config->context_menu_disabled && (!_elm_config->desktop_entry)) _menu_call(data); sd->long_pressed = EINA_TRUE; sd->longpress_timer = NULL; evas_object_smart_callback_call (data, "longpressed", NULL); return ECORE_CALLBACK_CANCEL; } static void _key_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Key_Down *ev = event_info; /* First check if context menu disabled is false or not, and * then check for key id */ if ((!_elm_config->context_menu_disabled) && !strcmp(ev->key, "Menu")) _menu_call(data); } static void _mouse_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Down *ev = event_info; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (sd->disabled) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; sd->downx = ev->canvas.x; sd->downy = ev->canvas.y; sd->long_pressed = EINA_FALSE; if (ev->button == 1) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->longpress_timer = ecore_timer_add (_elm_config->longpress_timeout, _long_press_cb, data); } /* If right button is pressed and context menu disabled is true, * then only context menu will appear */ else if (ev->button == 3 && (!_elm_config->context_menu_disabled)) { if (_elm_config->desktop_entry) { sd->use_down = 1; _menu_call(data); } } } static void _mouse_up_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Up *ev = event_info; Eina_Bool top_is_win = EINA_FALSE; Evas_Object *top; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (sd->disabled) return; if (ev->button == 1) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); /* Since context menu disabled flag was checked at long press start while mouse * down, hence the same should be checked at mouse up from a long press * as well */ if ((sd->long_pressed) && (_elm_config->magnifier_enable)) { _magnifier_hide(data); if (!_elm_config->context_menu_disabled) { _menu_call(data); } } else { top = elm_widget_top_get(data); if (top) { if (efl_isa(top, EFL_UI_WIN_CLASS)) top_is_win = EINA_TRUE; if (top_is_win && sd->input_panel_enable && sd->input_panel_show_on_demand && !edje_object_part_text_imf_context_get(sd->entry_edje, "elm.text")) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_ON); } } } /* Since context menu disabled flag was checked at mouse right key down, * hence the same should be stopped at mouse up of right key as well */ else if ((ev->button == 3) && (!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry)) { sd->use_down = 1; _menu_call(data); } } static void _mouse_move_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Move *ev = event_info; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (sd->disabled) return; if (ev->buttons == 1) { if ((sd->long_pressed) && (_elm_config->magnifier_enable)) { Evas_Coord x, y; Eina_Bool rv; evas_object_geometry_get(sd->entry_edje, &x, &y, NULL, NULL); rv = edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, ev->cur.canvas.x - x, ev->cur.canvas.y - y); if (rv) { edje_object_part_text_cursor_copy (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, EDJE_CURSOR_MAIN); } else WRN("Warning: Cannot move cursor"); _magnifier_move(data); } } if (!sd->sel_mode) { if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } else if (sd->longpress_timer) { Evas_Coord dx, dy; dx = sd->downx - ev->cur.canvas.x; dx *= dx; dy = sd->downy - ev->cur.canvas.y; dy *= dy; if ((dx + dy) > ((_elm_config->finger_size / 2) * (_elm_config->finger_size / 2))) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } } } else if (sd->longpress_timer) { Evas_Coord dx, dy; dx = sd->downx - ev->cur.canvas.x; dx *= dx; dy = sd->downy - ev->cur.canvas.y; dy *= dy; if ((dx + dy) > ((_elm_config->finger_size / 2) * (_elm_config->finger_size / 2))) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } } } static void _entry_changed_handle(void *data, const Efl_Event_Description* event) { Evas_Coord minh; const char *text; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; evas_event_freeze(evas_object_evas_get(data)); sd->changed = EINA_TRUE; /* Reset the size hints which are no more relevant. Keep the * height, this is a hack, but doesn't really matter cause we'll * re-eval in a moment. */ evas_object_size_hint_combined_min_get(data, NULL, &minh); evas_object_size_hint_min_set(data, -1, minh); elm_layout_sizing_eval(data); ELM_SAFE_FREE(sd->text, eina_stringshare_del); ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); evas_event_thaw(evas_object_evas_get(data)); evas_event_thaw_eval(evas_object_evas_get(data)); if ((sd->auto_save) && (sd->file)) sd->delay_write = ecore_timer_add(ELM_ENTRY_DELAY_WRITE_TIME, _delay_write, data); _return_key_enabled_check(data); text = edje_object_part_text_get(sd->entry_edje, "elm.text"); if (text) { if (text[0]) _elm_entry_guide_update(data, EINA_TRUE); else _elm_entry_guide_update(data, EINA_FALSE); } _validate(data); /* callback - this could call callbacks that delete the * entry... thus... any access to sd after this could be * invalid */ efl_event_callback_legacy_call(data, event, NULL); } static void _entry_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _entry_changed_handle(data, ELM_ENTRY_EVENT_CHANGED); } static void _entry_changed_user_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Change_Info info; Edje_Entry_Change_Info *edje_info = (Edje_Entry_Change_Info *) edje_object_signal_callback_extra_data_get(); if (edje_info) { memcpy(&info, edje_info, sizeof(info)); efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_CHANGED_USER, &info); } else { efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_CHANGED_USER, NULL); } if (_elm_config->atspi_mode) { Efl_Access_Text_Change_Info atspi_info; if (edje_info && edje_info->insert) { atspi_info.content = edje_info->change.insert.content; atspi_info.pos = edje_info->change.insert.pos; atspi_info.len = edje_info->change.insert.plain_length; efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_INSERTED, &atspi_info); } else if (edje_info && !edje_info->insert) { atspi_info.content = edje_info->change.del.content; atspi_info.pos = MIN(edje_info->change.del.start, edje_info->change.del.end); atspi_info.len = MAX(edje_info->change.del.start, edje_info->change.del.end) - atspi_info.pos; efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_REMOVED, &atspi_info); } } } static void _entry_preedit_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { char *text = NULL; Edje_Entry_Change_Info *edje_info = (Edje_Entry_Change_Info *) edje_object_signal_callback_extra_data_get(); _entry_changed_handle(data, ELM_ENTRY_EVENT_PREEDIT_CHANGED); if (_elm_config->atspi_mode) { Efl_Access_Text_Change_Info atspi_info; if (edje_info && edje_info->insert) { text = elm_entry_markup_to_utf8(edje_info->change.insert.content); atspi_info.content = text; atspi_info.pos = edje_info->change.insert.pos; atspi_info.len = edje_info->change.insert.plain_length; efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_INSERTED, &atspi_info); free(text); } } } static void _entry_undo_request_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_UNDO_REQUEST, NULL); } static void _entry_redo_request_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_REDO_REQUEST, NULL); } static void _entry_selection_start_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { const Eina_List *l; Evas_Object *entry; EINA_LIST_FOREACH(entries, l, entry) { if (entry != data) elm_entry_select_none(entry); } efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_START, NULL); elm_object_focus_set(data, EINA_TRUE); } static void _entry_selection_all_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { elm_entry_select_all(data); } static void _entry_selection_none_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { elm_entry_select_none(data); } // XXX: still try primary selection even if on wl in case it's // supported //static inline Eina_Bool //_entry_win_is_wl(Evas_Object *obj) //{ // Evas_Object *win = _entry_win_get(obj); // /* primary selection does not exist (yet) */ // return win && elm_win_wl_window_get(win); //} static void _entry_selection_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->have_selection = EINA_TRUE; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_CHANGED, NULL); // XXX: still try primary selection even if on wl in case it's // supported // if (!_entry_win_is_wl(data)) _selection_store(ELM_SEL_TYPE_PRIMARY, data); _update_selection_handler(data); if (_elm_config->atspi_mode) efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_SELECTION_CHANGED, NULL); } static void _entry_selection_cleared_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (!sd->have_selection) return; sd->have_selection = EINA_FALSE; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_CLEARED, NULL); // XXX: still try primary selection even if on wl in case it's // supported // if (!_entry_win_is_wl(data)) { if (sd->cut_sel) { elm_cnp_selection_set (data, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_MARKUP, sd->cut_sel, eina_stringshare_strlen(sd->cut_sel)); elm_cnp_selection_loss_callback_set(data, ELM_SEL_TYPE_PRIMARY, _selection_clear, data); ELM_SAFE_FREE(sd->cut_sel, eina_stringshare_del); } else { elm_object_cnp_selection_clear(data, ELM_SEL_TYPE_PRIMARY); } } _hide_selection_handler(data); } static void _entry_paste_request_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source EINA_UNUSED) { Evas_Object *top; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; Elm_Sel_Type type = (emission[sizeof("ntry,paste,request,")] == '1') ? ELM_SEL_TYPE_PRIMARY : ELM_SEL_TYPE_CLIPBOARD; if (!sd->editable) return; // XXX: still try primary selection even if on wl in case it's // supported // if ((type == ELM_SEL_TYPE_PRIMARY) && _entry_win_is_wl(data)) return; efl_event_callback_legacy_call (data, EFL_UI_EVENT_SELECTION_PASTE, NULL); top = _entry_win_get(data); if (top) { Elm_Sel_Format formats = ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_TEXT; sd->selection_asked = EINA_TRUE; if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) formats = ELM_SEL_FORMAT_TEXT; else if (sd->cnp_mode != ELM_CNP_MODE_NO_IMAGE) formats |= ELM_SEL_FORMAT_IMAGE; elm_cnp_selection_get(data, type, formats, _selection_data_cb, NULL); } } static void _entry_copy_notify_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _copy_cb(data, NULL, NULL); } static void _entry_cut_notify_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _cut_cb(data, NULL, NULL); } static void _entry_cursor_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->cursor_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); sd->cur_changed = EINA_TRUE; if (efl_ui_focus_object_focus_get(data)) edje_object_signal_emit(sd->entry_edje, "elm,action,show,cursor", "elm"); _cursor_geometry_recalc(data); efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_CURSOR_CHANGED, NULL); if (_elm_config->atspi_mode) efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_CARET_MOVED, NULL); } static void _entry_cursor_changed_manual_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_legacy_call (data, ELM_ENTRY_EVENT_CURSOR_CHANGED_MANUAL, NULL); if (_elm_config->atspi_mode) efl_access_object_event_emit( data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_CARET_MOVED, NULL); } static void _signal_anchor_geoms_do_things_with_lol(Elm_Entry_Data *sd, Elm_Entry_Anchor_Info *ei) { Evas_Textblock_Rectangle *r; const Eina_List *geoms, *l; Evas_Coord px, py, x, y; geoms = edje_object_part_text_anchor_geometry_get (sd->entry_edje, "elm.text", ei->name); if (!geoms) return; evas_object_geometry_get( edje_object_part_object_get(sd->entry_edje, "elm.text"), &x, &y, NULL, NULL); evas_pointer_canvas_xy_get (evas_object_evas_get(sd->entry_edje), &px, &py); EINA_LIST_FOREACH(geoms, l, r) { if (((r->x + x) <= px) && ((r->y + y) <= py) && ((r->x + x + r->w) > px) && ((r->y + y + r->h) > py)) { ei->x = r->x + x; ei->y = r->y + y; ei->w = r->w; ei->h = r->h; break; } } } static void _entry_anchor_down_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; const char *p; char *p2; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; p = emission + sizeof("nchor,mouse,down,"); ei.button = strtol(p, &p2, 10); ei.name = p2 + 1; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ANCHOR_DOWN, &ei); } static void _entry_anchor_up_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; const char *p; char *p2; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; p = emission + sizeof("nchor,mouse,up,"); ei.button = strtol(p, &p2, 10); ei.name = p2 + 1; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ANCHOR_UP, &ei); } static void _anchor_hover_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; ELM_SAFE_FREE(sd->anchor_hover.pop, evas_object_del); evas_object_event_callback_del_full (sd->anchor_hover.hover, EVAS_CALLBACK_DEL, _anchor_hover_del_cb, obj); } static void _anchor_hover_clicked_cb(void *data, const Efl_Event *event EINA_UNUSED) { elm_entry_anchor_hover_end(data); } static void _entry_hover_anchor_clicked_do(Evas_Object *obj, Elm_Entry_Anchor_Info *info) { Evas_Object *hover_parent; Evas_Coord x, w, y, h, px, py; Elm_Entry_Anchor_Hover_Info ei; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; if (sd->hoversel) return; ei.anchor_info = info; sd->anchor_hover.pop = elm_icon_add(obj); evas_object_geometry_set(sd->anchor_hover.pop, info->x, info->y, info->w, info->h); sd->anchor_hover.hover = elm_hover_add(obj); evas_object_event_callback_add (sd->anchor_hover.hover, EVAS_CALLBACK_DEL, _anchor_hover_del_cb, obj); efl_ui_mirrored_set (sd->anchor_hover.hover, efl_ui_mirrored_get(obj)); if (sd->anchor_hover.hover_style) elm_object_style_set (sd->anchor_hover.hover, sd->anchor_hover.hover_style); hover_parent = sd->anchor_hover.hover_parent; if (!hover_parent) hover_parent = obj; elm_hover_parent_set(sd->anchor_hover.hover, hover_parent); elm_hover_target_set(sd->anchor_hover.hover, sd->anchor_hover.pop); ei.hover = sd->anchor_hover.hover; evas_object_geometry_get(hover_parent, &x, &y, &w, &h); ei.hover_parent.x = x; ei.hover_parent.y = y; ei.hover_parent.w = w; ei.hover_parent.h = h; px = info->x + (info->w / 2); py = info->y + (info->h / 2); ei.hover_left = 1; if (px < (x + (w / 3))) ei.hover_left = 0; ei.hover_right = 1; if (px > (x + ((w * 2) / 3))) ei.hover_right = 0; ei.hover_top = 1; if (py < (y + (h / 3))) ei.hover_top = 0; ei.hover_bottom = 1; if (py > (y + ((h * 2) / 3))) ei.hover_bottom = 0; /* Swap right and left because they switch sides in RTL */ if (efl_ui_mirrored_get(sd->anchor_hover.hover)) { Eina_Bool tmp = ei.hover_left; ei.hover_left = ei.hover_right; ei.hover_right = tmp; } efl_event_callback_legacy_call(obj, ELM_ENTRY_EVENT_ANCHOR_HOVER_OPENED, &ei); efl_event_callback_add (sd->anchor_hover.hover, EFL_INPUT_EVENT_CLICKED, _anchor_hover_clicked_cb, obj); /* FIXME: Should just check if there's any callback registered to * the smart events instead. This is used to determine if anyone * cares about the hover or not. */ if (!elm_layout_content_get(sd->anchor_hover.hover, "middle") && !elm_layout_content_get(sd->anchor_hover.hover, "left") && !elm_layout_content_get(sd->anchor_hover.hover, "right") && !elm_layout_content_get(sd->anchor_hover.hover, "top") && !elm_layout_content_get(sd->anchor_hover.hover, "bottom")) { ELM_SAFE_FREE(sd->anchor_hover.hover, evas_object_del); } else evas_object_show(sd->anchor_hover.hover); } static void _entry_anchor_clicked_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; const char *p; char *p2; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; p = emission + sizeof("nchor,mouse,clicked,"); ei.button = strtol(p, &p2, 10); ei.name = p2 + 1; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) { efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ANCHOR_CLICKED, &ei); _entry_hover_anchor_clicked_do(data, &ei); } } static void _entry_anchor_move_signal_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { } static void _entry_anchor_in_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; ei.name = emission + sizeof("nchor,mouse,in,"); ei.button = 0; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ANCHOR_IN, &ei); } static void _entry_anchor_out_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; ei.name = emission + sizeof("nchor,mouse,out,"); ei.button = 0; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ANCHOR_OUT, &ei); } static void _entry_key_enter_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (sd->single_line) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ACTIVATED, NULL); } static void _entry_key_escape_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (sd->single_line) efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_ABORTED, NULL); } static void _entry_mouse_down_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_legacy_call(data, ELM_ENTRY_EVENT_PRESS, NULL); } static void _entry_mouse_clicked_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { evas_object_smart_callback_call ( data, "clicked", NULL); } static void _entry_mouse_double_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { evas_object_smart_callback_call ( data, "clicked,double", NULL); } static void _entry_mouse_triple_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { evas_object_smart_callback_call ( data, "clicked,triple", NULL); } static Evas_Object * _item_get(void *data, Evas_Object *edje EINA_UNUSED, const char *part EINA_UNUSED, const char *item) { Eina_List *l; Evas_Object *o; Elm_Entry_Item_Provider *ip; const char *style = elm_widget_style_get(data); ELM_ENTRY_DATA_GET(data, sd); if (!sd) return NULL; EINA_LIST_FOREACH(sd->item_providers, l, ip) { o = ip->func(ip->data, data, item); if (o) return o; } if (item && !strncmp(item, "file://", 7)) { const char *fname = item + 7; o = evas_object_image_filled_add(evas_object_evas_get(data)); evas_object_image_load_orientation_set(o, EINA_TRUE); evas_object_image_file_set(o, fname, NULL); if (evas_object_image_load_error_get(o) == EVAS_LOAD_ERROR_NONE) { evas_object_show(o); } else { evas_object_del(o); o = edje_object_add(evas_object_evas_get(data)); _elm_theme_object_set (data, o, "entry/emoticon", "wtf", style); } return o; } o = edje_object_add(evas_object_evas_get(data)); if (_elm_theme_object_set (data, o, "entry", item, style) == EFL_UI_THEME_APPLY_ERROR_GENERIC) _elm_theme_object_set (data, o, "entry/emoticon", "wtf", style); return o; } static Eina_Bool _entry_has_new_line(const char *text) { if (!text) return EINA_FALSE; const char * pTemp = text; while ((pTemp = strchr(pTemp, '<'))) { pTemp++; if (!strncmp(pTemp, "br", 2) || !strncmp(pTemp, "ps", 2)) { pTemp += 2; if (pTemp[0] != '\0' && (pTemp[0] == '>' || (pTemp[0] == '/' && pTemp[1] == '>'))) { return EINA_TRUE; } } } return EINA_FALSE; } static char * _entry_remove_new_line(const char *text) { Eina_Strbuf *str; char *new_text; if (!_entry_has_new_line(text)) return NULL; str = eina_strbuf_new(); eina_strbuf_append(str, text); eina_strbuf_replace_all(str, "
", ""); eina_strbuf_replace_all(str, "
", ""); eina_strbuf_replace_all(str, "", ""); eina_strbuf_replace_all(str, "", ""); new_text = eina_strbuf_string_steal(str); eina_strbuf_free(str); return new_text; } static void _entry_new_line_filter_init(Evas_Object *obj) { const char *text; char *text2 = NULL; if (elm_entry_is_empty(obj)) return; text = elm_entry_entry_get(obj); text2 = _entry_remove_new_line(text); if (text2) { elm_entry_entry_set(obj, text2); free(text2); } } static void _entry_new_line_filter_cb(void *data EINA_UNUSED, Evas_Object *entry EINA_UNUSED, char **text) { char *ret; if (!*text) return; ret = _entry_remove_new_line(*text); if (ret) { free(*text); *text = ret; } } static void _markup_filter_cb(void *data, Evas_Object *edje EINA_UNUSED, const char *part EINA_UNUSED, char **text) { Eina_List *l; Elm_Entry_Markup_Filter *tf; ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; EINA_LIST_FOREACH(sd->markup_filters, l, tf) { tf->func(tf->data, data, text); if (!*text) break; } } /* This function is used to insert text by chunks in jobs */ static Eina_Bool _text_append_idler(void *data) { int start; char backup; Evas_Object *obj = (Evas_Object *)data; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return ECORE_CALLBACK_CANCEL; evas_event_freeze(evas_object_evas_get(obj)); ELM_SAFE_FREE(sd->text, eina_stringshare_del); sd->changed = EINA_TRUE; start = sd->append_text_position; if ((start + ELM_ENTRY_CHUNK_SIZE) < sd->append_text_len) { int pos = start; int tag_start, esc_start; tag_start = esc_start = -1; /* Find proper markup cut place */ while (pos - start < ELM_ENTRY_CHUNK_SIZE) { int prev_pos = pos; Eina_Unicode tmp = eina_unicode_utf8_next_get(sd->append_text_left, &pos); if (esc_start == -1) { if (tmp == '<') tag_start = prev_pos; else if (tmp == '>') tag_start = -1; } if (tag_start == -1) { if (tmp == '&') esc_start = prev_pos; else if (tmp == ';') esc_start = -1; } } if (tag_start >= 0) { sd->append_text_position = tag_start; } else if (esc_start >= 0) { sd->append_text_position = esc_start; } else { sd->append_text_position = pos; } } else { sd->append_text_position = sd->append_text_len; } backup = sd->append_text_left[sd->append_text_position]; sd->append_text_left[sd->append_text_position] = '\0'; edje_object_part_text_append (sd->entry_edje, "elm.text", sd->append_text_left + start); sd->append_text_left[sd->append_text_position] = backup; evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); _elm_entry_guide_update(obj, EINA_TRUE); /* If there's still more to go, renew the idler, else, cleanup */ if (sd->append_text_position < sd->append_text_len) { return ECORE_CALLBACK_RENEW; } else { edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, sd->cursor_pos); free(sd->append_text_left); sd->append_text_left = NULL; sd->append_text_idler = NULL; efl_event_callback_legacy_call (obj, ELM_ENTRY_EVENT_TEXT_SET_DONE, NULL); return ECORE_CALLBACK_CANCEL; } } static void my_string_copy_truncate(char *dest, const char *src, size_t len) { char *p; for (p = dest; len > 0; p++, src++, len--) { *p = *src; if (*src == 0) break; } } static void _chars_add_till_limit(Evas_Object *obj, char **text, int can_add, Length_Unit unit) { int i = 0, current_len = 0; char *new_text; if (!*text) return; if (unit >= LENGTH_UNIT_LAST) return; if (strstr(*text, "entry_edje, emission, source); edje_object_message_signal_process(sd->entry_edje); if (sd->scr_edje) { edje_object_signal_emit(sd->scr_edje, emission, source); edje_object_message_signal_process(sd->scr_edje); } } static Eina_Bool _elm_entry_efl_layout_signal_signal_callback_add(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *emission, const char *source, void *func_data, EflLayoutSignalCb func, Eina_Free_Cb func_free_cb) { Eina_Bool ok; ok = efl_layout_signal_callback_add(sd->entry_edje, emission, source, func_data, func, func_free_cb); if (sd->scr_edje) ok = efl_layout_signal_callback_add(sd->scr_edje, emission, source, func_data, func, func_free_cb); return ok; } static Eina_Bool _elm_entry_efl_layout_signal_signal_callback_del(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *emission, const char *source, void *func_data, EflLayoutSignalCb func, Eina_Free_Cb func_free_cb) { Eina_Bool ok; ok = efl_layout_signal_callback_del(sd->entry_edje, emission, source, func_data, func, func_free_cb); if (sd->scr_edje) ok = efl_layout_signal_callback_del(sd->scr_edje, emission, source, func_data, func, func_free_cb); return ok; } // Legacy support... del() returns the user data. void _elm_entry_signal_callback_add_legacy(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data) { Elm_Entry_Data *sd; sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; _elm_layout_signal_callback_add_legacy(obj, sd->entry_edje, &sd->edje_signals, emission, source, func_cb, data); if (sd->scr_edje) efl_layout_signal_callback_add(sd->scr_edje, emission, source, data, func_cb, NULL); } void * _elm_entry_signal_callback_del_legacy(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb) { Elm_Entry_Data *sd; void *data; sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; data = _elm_layout_signal_callback_del_legacy(obj, sd->entry_edje, &sd->edje_signals, emission, source, func_cb); if (sd->scr_edje) efl_layout_signal_callback_del(sd->scr_edje, emission, source, data, func_cb, NULL); return data; } static Eina_Bool _elm_entry_content_set(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, const char *part, Evas_Object *content) { Eina_Bool int_ret = EINA_FALSE; int_ret = efl_content_set(efl_part(efl_super(obj, MY_CLASS), part), content); if (!int_ret) return EINA_FALSE; /* too bad entry does not follow the pattern * "elm,state,{icon,end},visible", we have to repeat ourselves */ if (!part || !strcmp(part, "icon") || !strcmp(part, "elm.swallow.icon")) elm_entry_icon_visible_set(obj, EINA_TRUE); if (part && (!strcmp(part, "end") || !strcmp(part, "elm.swallow.end"))) elm_entry_end_visible_set(obj, EINA_TRUE); return EINA_TRUE; } static Evas_Object* _elm_entry_content_unset(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, const char *part) { Evas_Object *ret = NULL; ret = efl_content_unset(efl_part(efl_super(obj, MY_CLASS), part)); if (!ret) return NULL; /* too bad entry does not follow the pattern * "elm,state,{icon,end},hidden", we have to repeat ourselves */ if (!part || !strcmp(part, "icon") || !strcmp(part, "elm.swallow.icon")) elm_entry_icon_visible_set(obj, EINA_FALSE); if (part && (!strcmp(part, "end") || !strcmp(part, "elm.swallow.end"))) elm_entry_end_visible_set(obj, EINA_FALSE); return ret; } static void _entry_text_append(Evas_Object* obj, const char* entry, Eina_Bool set) { int len = 0; if (!entry) return; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; len = strlen(entry); if (sd->append_text_left) { char *tmpbuf; tmpbuf = realloc(sd->append_text_left, sd->append_text_len + len + 1); if (!tmpbuf) { /* Do something */ return; } sd->append_text_left = tmpbuf; memcpy(sd->append_text_left + sd->append_text_len, entry, len + 1); sd->append_text_len += len; } else { if (len > ELM_ENTRY_CHUNK_SIZE) { sd->append_text_left = (char *)malloc(len + 1); } if (sd->append_text_left) { memcpy(sd->append_text_left, entry, len + 1); sd->append_text_position = 0; sd->append_text_len = len; sd->append_text_idler = ecore_idler_add(_text_append_idler, obj); } else { if (set) { edje_object_part_text_set(sd->entry_edje, "elm.text", entry); } else { edje_object_part_text_append(sd->entry_edje, "elm.text", entry); } edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, sd->cursor_pos); efl_event_callback_legacy_call(obj, ELM_ENTRY_EVENT_TEXT_SET_DONE, NULL); } } } static Eina_Bool _elm_entry_text_set(Eo *obj, Elm_Entry_Data *sd, const char *part, const char *entry) { int len = 0; const char * current_text = NULL; if (!entry) entry = ""; if (!_elm_layout_part_aliasing_eval(obj, &part, EINA_TRUE)) return EINA_FALSE; if (strcmp(part, "elm.text")) { edje_object_part_text_set(sd->entry_edje, part, entry); return EINA_TRUE; } evas_event_freeze(evas_object_evas_get(obj)); /* Clear currently pending job if there is one */ if (sd->append_text_idler) { ecore_idler_del(sd->append_text_idler); ELM_SAFE_FREE(sd->append_text_left, free); sd->append_text_idler = NULL; } len = strlen(entry); if (sd->append_text_left) { free(sd->append_text_left); sd->append_text_left = NULL; } /* If old and new text are the same do nothing */ current_text = edje_object_part_text_get(sd->entry_edje, "elm.text"); if (eina_streq(current_text, entry)) goto done; ELM_SAFE_FREE(sd->text, eina_stringshare_del); sd->changed = EINA_TRUE; /* Need to clear the entry first */ sd->cursor_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); edje_object_part_text_set(sd->entry_edje, "elm.text", ""); _entry_text_append(obj, entry, EINA_TRUE); if (len > 0) _elm_entry_guide_update(obj, EINA_TRUE); else _elm_entry_guide_update(obj, EINA_FALSE); done: evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); return EINA_TRUE; } static const char * _elm_entry_text_get(Eo *obj, Elm_Entry_Data *sd, const char *item) { const char *text; if (!_elm_layout_part_aliasing_eval(obj, &item, EINA_TRUE)) return NULL; if (strcmp(item, "elm.text")) return edje_object_part_text_get(sd->entry_edje, item); text = edje_object_part_text_get(sd->entry_edje, "elm.text"); if (!text) { ERR("text=NULL for edje %p, part 'elm.text'", sd->entry_edje); return NULL; } if (sd->append_text_len > 0) { char *tmpbuf; size_t len, tlen; tlen = strlen(text); len = tlen + sd->append_text_len - sd->append_text_position; /* FIXME: need that or we do copy uninitialised data */ tmpbuf = calloc(1, len + 1); if (!tmpbuf) { ERR("Failed to allocate memory for entry's text %p", obj); return NULL; } memcpy(tmpbuf, text, tlen); if (sd->append_text_left) memcpy(tmpbuf + tlen, sd->append_text_left + sd->append_text_position, sd->append_text_len - sd->append_text_position); tmpbuf[len] = '\0'; eina_stringshare_replace(&sd->text, tmpbuf); free(tmpbuf); } else { eina_stringshare_replace(&sd->text, text); } return sd->text; } static char * _access_info_cb(void *data EINA_UNUSED, Evas_Object *obj) { const char *txt; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return NULL; if (sd->password) return NULL; txt = elm_widget_access_info_get(obj); if (!txt) return _elm_util_mkup_to_text(elm_entry_entry_get(obj)); else return strdup(txt); } static char * _access_state_cb(void *data EINA_UNUSED, Evas_Object *obj) { Eina_Strbuf *buf; char *ret; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return NULL; ret = NULL; buf = eina_strbuf_new(); if (elm_widget_disabled_get(obj)) eina_strbuf_append(buf, "State: Disabled"); if (!sd->editable) { if (!eina_strbuf_length_get(buf)) eina_strbuf_append(buf, "State: Not Editable"); else eina_strbuf_append(buf, ", Not Editable"); } if (sd->password) { if (!eina_strbuf_length_get(buf)) eina_strbuf_append(buf, "State: Password"); else eina_strbuf_append(buf, ", Password"); } if (!eina_strbuf_length_get(buf)) goto buf_free; ret = eina_strbuf_string_steal(buf); buf_free: eina_strbuf_free(buf); return ret; } static void _entry_selection_callbacks_unregister(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); edje_object_signal_callback_del_full (sd->entry_edje, "selection,start", "elm.text", _entry_selection_start_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "selection,changed", "elm.text", _entry_selection_changed_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "entry,selection,all,request", "elm.text", _entry_selection_all_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "entry,selection,none,request", "elm.text", _entry_selection_none_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "selection,cleared", "elm.text", _entry_selection_cleared_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "entry,paste,request,*", "elm.text", _entry_paste_request_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "entry,copy,notify", "elm.text", _entry_copy_notify_signal_cb, obj); edje_object_signal_callback_del_full (sd->entry_edje, "entry,cut,notify", "elm.text", _entry_cut_notify_signal_cb, obj); } static void _entry_selection_callbacks_register(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); edje_object_signal_callback_add (sd->entry_edje, "selection,start", "elm.text", _entry_selection_start_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "selection,changed", "elm.text", _entry_selection_changed_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,selection,all,request", "elm.text", _entry_selection_all_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,selection,none,request", "elm.text", _entry_selection_none_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "selection,cleared", "elm.text", _entry_selection_cleared_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,paste,request,*", "elm.text", _entry_paste_request_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,copy,notify", "elm.text", _entry_copy_notify_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,cut,notify", "elm.text", _entry_cut_notify_signal_cb, obj); } static void _elm_entry_resize_internal(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; if (sd->line_wrap) { elm_layout_sizing_eval(obj); } else if (sd->scroll) { Evas_Coord vw = 0, vh = 0; elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &vw, &vh); if (vw < sd->ent_mw) vw = sd->ent_mw; if (vh < sd->ent_mh) vh = sd->ent_mh; evas_object_resize(sd->entry_edje, vw, vh); } if (sd->hoversel) _hoversel_position(obj); } static void _resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_entry_resize_internal(data); } static void _selection_handlers_offset_calc(Evas_Object *obj, Evas_Object *handler, Evas_Coord canvasx, Evas_Coord canvasy) { Evas_Coord ex, ey; Evas_Coord cx, cy, cw, ch; Evas_Coord hh; ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); edje_object_part_text_cursor_geometry_get(sd->entry_edje, "elm.text", &cx, &cy, &cw, &ch); edje_object_size_min_calc(handler, NULL, &hh); sd->ox = canvasx - (ex + cx + (cw / 2)); if (ch > hh) sd->oy = canvasy - (ey + cy + ch); else sd->oy = canvasy - (ey + cy + (ch / 2)); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) { _magnifier_create(obj); _magnifier_show(obj); _magnifier_move(obj); } } static void _start_handler_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; Evas_Event_Mouse_Down *ev = event_info; int start_pos, end_pos, main_pos, pos; sd->start_handler_down = EINA_TRUE; start_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_BEGIN); end_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_END); main_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); if (start_pos <= end_pos) { pos = start_pos; sd->sel_handler_cursor = EDJE_CURSOR_SELECTION_BEGIN; } else { pos = end_pos; sd->sel_handler_cursor = EDJE_CURSOR_SELECTION_END; } if (pos != main_pos) edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); _selection_handlers_offset_calc(data, sd->start_handler, ev->canvas.x, ev->canvas.y); } static void _start_handler_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->start_handler_down = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_hide(data); /* Context menu should not appear, even in case of selector mode, if the * flag is false (disabled) */ if ((!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry) && (sd->long_pressed)) _menu_call(data); } static void _start_handler_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (!sd->start_handler_down) return; Evas_Event_Mouse_Move *ev = event_info; Evas_Coord ex, ey; Evas_Coord cx, cy; int pos; evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); cx = ev->cur.canvas.x - sd->ox - ex; cy = ev->cur.canvas.y - sd->oy - ey; if (cx <= 0) cx = 1; edje_object_part_text_cursor_coord_set(sd->entry_edje, "elm.text", sd->sel_handler_cursor, cx, cy); pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", sd->sel_handler_cursor); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_move(data); } static void _end_handler_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; Evas_Event_Mouse_Down *ev = event_info; int pos, start_pos, end_pos, main_pos; sd->end_handler_down = EINA_TRUE; start_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_BEGIN); end_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_END); if (start_pos < end_pos) { pos = end_pos; sd->sel_handler_cursor = EDJE_CURSOR_SELECTION_END; } else { pos = start_pos; sd->sel_handler_cursor = EDJE_CURSOR_SELECTION_BEGIN; } main_pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); if (pos != main_pos) edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); _selection_handlers_offset_calc(data, sd->end_handler, ev->canvas.x, ev->canvas.y); } static void _end_handler_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->end_handler_down = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_hide(data); /* Context menu appear was checked in case of selector start, and hence * the same should be checked at selector end as well */ if ((!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry) && (sd->long_pressed)) _menu_call(data); } static void _end_handler_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; if (!sd->end_handler_down) return; Evas_Event_Mouse_Move *ev = event_info; Evas_Coord ex, ey; Evas_Coord cx, cy; int pos; evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); cx = ev->cur.canvas.x - sd->ox - ex; cy = ev->cur.canvas.y - sd->oy - ey; if (cx <= 0) cx = 1; edje_object_part_text_cursor_coord_set(sd->entry_edje, "elm.text", sd->sel_handler_cursor, cx, cy); pos = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", sd->sel_handler_cursor); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_move(data); } static void _entry_on_size_evaluate_signal(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); if (!sd) return; sd->cur_changed = EINA_TRUE; elm_entry_calc_force(data); } EOLIAN static void _elm_entry_efl_canvas_group_group_add(Eo *obj, Elm_Entry_Data *priv) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "entry"); efl_canvas_group_add(efl_super(obj, MY_CLASS)); priv->entry_edje = wd->resize_obj; priv->cnp_mode = ELM_CNP_MODE_MARKUP; priv->line_wrap = ELM_WRAP_WORD; priv->context_menu = EINA_TRUE; priv->auto_save = EINA_TRUE; priv->editable = EINA_TRUE; priv->sel_allow = _elm_config->entry_select_allow; priv->drop_format = ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_IMAGE; elm_drop_target_add(obj, priv->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), elm_widget_theme_element_get(obj), elm_widget_theme_style_get(obj)) == EFL_UI_THEME_APPLY_ERROR_GENERIC) CRI("Failed to set layout!"); priv->hit_rect = evas_object_rectangle_add(evas_object_evas_get(obj)); evas_object_data_set(priv->hit_rect, "_elm_leaveme", obj); Evas_Object* clip = evas_object_clip_get(priv->entry_edje); evas_object_clip_set(priv->hit_rect, clip); evas_object_smart_member_add(priv->hit_rect, obj); elm_widget_sub_object_add(obj, priv->hit_rect); /* common scroller hit rectangle setup */ evas_object_color_set(priv->hit_rect, 0, 0, 0, 0); evas_object_show(priv->hit_rect); evas_object_repeat_events_set(priv->hit_rect, EINA_TRUE); elm_interface_scrollable_objects_set(obj, priv->entry_edje, priv->hit_rect); edje_object_item_provider_set(priv->entry_edje, _item_get, obj); edje_object_text_markup_filter_callback_add (priv->entry_edje, "elm.text", _markup_filter_cb, obj); evas_object_event_callback_add (priv->entry_edje, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, obj); evas_object_event_callback_add (priv->entry_edje, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, obj); evas_object_event_callback_add (priv->entry_edje, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, obj); evas_object_event_callback_add (priv->entry_edje, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move_cb, obj); /* this code can't go in smart_resize. sizing gets wrong */ evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,changed", "elm.text", _entry_changed_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,changed,user", "elm.text", _entry_changed_user_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "preedit,changed", "elm.text", _entry_preedit_changed_signal_cb, obj); _entry_selection_callbacks_register(obj); edje_object_signal_callback_add (priv->entry_edje, "cursor,changed", "elm.text", _entry_cursor_changed_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "cursor,changed,manual", "elm.text", _entry_cursor_changed_manual_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,down,*", "elm.text", _entry_anchor_down_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,up,*", "elm.text", _entry_anchor_up_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,clicked,*", "elm.text", _entry_anchor_clicked_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,move,*", "elm.text", _entry_anchor_move_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,in,*", "elm.text", _entry_anchor_in_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "anchor,mouse,out,*", "elm.text", _entry_anchor_out_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,key,enter", "elm.text", _entry_key_enter_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,key,escape", "elm.text", _entry_key_escape_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "mouse,down,1", "elm.text", _entry_mouse_down_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "mouse,clicked,1", "elm.text", _entry_mouse_clicked_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "mouse,down,1,double", "elm.text", _entry_mouse_double_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "mouse,down,1,triple", "elm.text", _entry_mouse_triple_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,undo,request", "elm.text", _entry_undo_request_signal_cb, obj); edje_object_signal_callback_add (priv->entry_edje, "entry,redo,request", "elm.text", _entry_redo_request_signal_cb, obj); elm_layout_text_set(obj, "elm.text", ""); elm_object_sub_cursor_set (wd->resize_obj, obj, ELM_CURSOR_XTERM); elm_widget_can_focus_set(obj, EINA_TRUE); if (priv->sel_allow && _elm_config->desktop_entry) edje_object_part_text_select_allow_set (priv->entry_edje, "elm.text", EINA_TRUE); elm_layout_sizing_eval(obj); elm_entry_input_panel_layout_set(obj, ELM_INPUT_PANEL_LAYOUT_NORMAL); elm_entry_input_panel_enabled_set(obj, EINA_TRUE); elm_entry_prediction_allow_set(obj, EINA_TRUE); elm_entry_input_hint_set(obj, ELM_INPUT_HINT_AUTO_COMPLETE); priv->autocapital_type = (Elm_Autocapital_Type)edje_object_part_text_autocapital_type_get (priv->entry_edje, "elm.text"); entries = eina_list_prepend(entries, obj); // module - find module for entry priv->api = _module_find(obj); // if found - hook in if ((priv->api) && (priv->api->obj_hook)) priv->api->obj_hook(obj); _mirrored_set(obj, efl_ui_mirrored_get(obj)); // access _elm_access_object_register(obj, priv->entry_edje); _elm_access_text_set (_elm_access_info_get(obj), ELM_ACCESS_TYPE, E_("Entry")); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_STATE, _access_state_cb, NULL); if (_elm_config->desktop_entry) priv->sel_handler_disabled = EINA_TRUE; edje_object_signal_callback_add (priv->entry_edje, "size,eval", "elm", _entry_on_size_evaluate_signal, obj); } static void _create_selection_handlers(Evas_Object *obj, Elm_Entry_Data *sd) { Evas_Object *handle; const char *style = elm_widget_style_get(obj); handle = edje_object_add(evas_object_evas_get(obj)); sd->start_handler = handle; _elm_theme_object_set(obj, handle, "entry", "handler/start", style); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_DOWN, _start_handler_mouse_down_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_MOVE, _start_handler_mouse_move_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_UP, _start_handler_mouse_up_cb, obj); evas_object_show(handle); handle = edje_object_add(evas_object_evas_get(obj)); sd->end_handler = handle; _elm_theme_object_set(obj, handle, "entry", "handler/end", style); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_DOWN, _end_handler_mouse_down_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_MOVE, _end_handler_mouse_move_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_UP, _end_handler_mouse_up_cb, obj); evas_object_show(handle); } EOLIAN static void _elm_entry_efl_canvas_group_group_del(Eo *obj, Elm_Entry_Data *sd) { Elm_Entry_Context_Menu_Item *it; Elm_Entry_Item_Provider *ip; Elm_Entry_Markup_Filter *tf; if (sd->delay_write) { ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); if (sd->auto_save) _save_do(obj); } edje_object_signal_callback_del_full (sd->entry_edje, "size,eval", "elm", _entry_on_size_evaluate_signal, obj); if (sd->scroll) elm_interface_scrollable_content_viewport_resize_cb_set(obj, NULL); elm_entry_anchor_hover_end(obj); elm_entry_anchor_hover_parent_set(obj, NULL); evas_event_freeze(evas_object_evas_get(obj)); eina_stringshare_del(sd->file); ecore_job_del(sd->hov_deljob); if ((sd->api) && (sd->api->obj_unhook)) sd->api->obj_unhook(obj); // module - unhook evas_object_del(sd->mgf_proxy); evas_object_del(sd->mgf_bg); evas_object_del(sd->mgf_clip); entries = eina_list_remove(entries, obj); eina_stringshare_del(sd->cut_sel); eina_stringshare_del(sd->text); ecore_job_del(sd->deferred_recalc_job); if (sd->append_text_idler) { ecore_idler_del(sd->append_text_idler); ELM_SAFE_FREE(sd->append_text_left, free); sd->append_text_idler = NULL; } ecore_timer_del(sd->longpress_timer); EINA_LIST_FREE(sd->items, it) { eina_stringshare_del(it->label); eina_stringshare_del(it->icon_file); eina_stringshare_del(it->icon_group); free(it); } EINA_LIST_FREE(sd->item_providers, ip) { free(ip); } EINA_LIST_FREE(sd->markup_filters, tf) { _filter_free(tf); } ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); free(sd->input_panel_imdata); if (sd->prediction_hint) { ELM_SAFE_FREE(sd->prediction_hint, free); } eina_stringshare_del(sd->anchor_hover.hover_style); evas_event_thaw(evas_object_evas_get(obj)); evas_event_thaw_eval(evas_object_evas_get(obj)); if (sd->start_handler) { evas_object_del(sd->start_handler); evas_object_del(sd->end_handler); } efl_canvas_group_del(efl_super(obj, MY_CLASS)); } EOLIAN static void _elm_entry_efl_gfx_entity_position_set(Eo *obj, Elm_Entry_Data *sd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos); efl_gfx_entity_position_set(sd->hit_rect, pos); if (sd->hoversel) _hoversel_position(obj); if (edje_object_part_text_selection_get(sd->entry_edje, "elm.text")) _update_selection_handler(obj); } EOLIAN static void _elm_entry_efl_gfx_entity_size_set(Eo *obj, Elm_Entry_Data *sd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; efl_gfx_entity_size_set(sd->hit_rect, sz); if (sd->have_selection) _update_selection_handler(obj); efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz); } EOLIAN static void _elm_entry_efl_gfx_entity_visible_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool vis) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis)) return; efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), vis); if (sd->have_selection) { if (vis) _update_selection_handler(obj); else _hide_selection_handler(obj); } } EOLIAN static void _elm_entry_efl_canvas_group_group_member_add(Eo *obj, Elm_Entry_Data *sd, Evas_Object *member) { efl_canvas_group_member_add(efl_super(obj, MY_CLASS), member); if (sd->hit_rect) evas_object_raise(sd->hit_rect); } EAPI Evas_Object * elm_entry_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); return elm_legacy_add(MY_CLASS, parent); } static void _cb_added(void *data EINA_UNUSED, const Efl_Event *ev) { const Efl_Callback_Array_Item_Full *event = ev->info; ELM_ENTRY_DATA_GET(ev->object, sd); // XXX: BUG - not walking the array until a NULL entry if (event->desc == ELM_ENTRY_EVENT_VALIDATE) sd->validators++; } static void _cb_deleted(void *data EINA_UNUSED, const Efl_Event *ev) { const Efl_Callback_Array_Item_Full *event = ev->info; ELM_ENTRY_DATA_GET(ev->object, sd); // XXX: BUG - not walking the array until a NULL entry if (event->desc == ELM_ENTRY_EVENT_VALIDATE) sd->validators--; return; } EOLIAN static Eo * _elm_entry_efl_object_constructor(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_object_role_set(obj, EFL_ACCESS_ROLE_ENTRY); efl_event_callback_add(obj, EFL_EVENT_CALLBACK_ADD, _cb_added, NULL); efl_event_callback_add(obj, EFL_EVENT_CALLBACK_DEL, _cb_deleted, NULL); legacy_object_focus_handle(obj); return obj; } EOLIAN static void _elm_entry_text_style_user_push(Eo *obj, Elm_Entry_Data *sd, const char *style) { edje_object_part_text_style_user_push(sd->entry_edje, "elm.text", style); efl_ui_widget_theme_apply(obj); } EOLIAN static void _elm_entry_text_style_user_pop(Eo *obj, Elm_Entry_Data *sd) { edje_object_part_text_style_user_pop(sd->entry_edje, "elm.text"); efl_ui_widget_theme_apply(obj); } EOLIAN static const char* _elm_entry_text_style_user_peek(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_style_user_peek(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_single_line_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool single_line) { if (sd->single_line == single_line) return; sd->single_line = single_line; sd->line_wrap = ELM_WRAP_NONE; if (elm_entry_cnp_mode_get(obj) == ELM_CNP_MODE_MARKUP) elm_entry_cnp_mode_set(obj, ELM_CNP_MODE_NO_IMAGE); if (sd->single_line) { _entry_new_line_filter_init(obj); elm_entry_markup_filter_append(obj, _entry_new_line_filter_cb, NULL); } else { elm_entry_markup_filter_remove(obj, _entry_new_line_filter_cb, NULL); } efl_ui_widget_theme_apply(obj); if (sd->scroll) { if (sd->single_line) elm_interface_scrollable_policy_set(obj, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF); else { elm_interface_scrollable_policy_set(obj, sd->policy_h, sd->policy_v); } elm_layout_sizing_eval(obj); } } EOLIAN static Eina_Bool _elm_entry_single_line_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->single_line; } EOLIAN static void _elm_entry_password_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool password) { password = !!password; if (sd->password == password) return; sd->password = password; elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (password) { sd->single_line = EINA_TRUE; sd->line_wrap = ELM_WRAP_NONE; elm_entry_input_hint_set(obj, ((sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE) | ELM_INPUT_HINT_SENSITIVE_DATA)); _entry_selection_callbacks_unregister(obj); efl_access_object_role_set(obj, EFL_ACCESS_ROLE_PASSWORD_TEXT); } else { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); elm_entry_input_hint_set(obj, ((sd->input_hints | ELM_INPUT_HINT_AUTO_COMPLETE) & ~ELM_INPUT_HINT_SENSITIVE_DATA)); _entry_selection_callbacks_register(obj); efl_access_object_role_set(obj, EFL_ACCESS_ROLE_ENTRY); } efl_ui_widget_theme_apply(obj); } EOLIAN static Eina_Bool _elm_entry_password_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->password; } EAPI void elm_entry_calc_force(Evas_Object *obj) { efl_layout_calc_force(obj); } EAPI void elm_entry_entry_set(Evas_Object *obj, const char *entry) { ELM_ENTRY_CHECK(obj); efl_text_set(efl_part(obj, "elm.text"), entry); } EAPI const char * elm_entry_entry_get(const Evas_Object *obj) { ELM_ENTRY_CHECK(obj) NULL; const char *text = NULL; text = efl_text_get(efl_part(obj, "elm.text")); return text; } EOLIAN static void _elm_entry_entry_append(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *entry) { if (!entry) entry = ""; sd->changed = EINA_TRUE; _entry_text_append(obj, entry, EINA_FALSE); } EOLIAN static Eina_Bool _elm_entry_is_empty(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_cursor_copy (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, EDJE_CURSOR_USER); edje_object_part_text_cursor_pos_set (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, 1); if (edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_USER) == 1) return EINA_FALSE; return EINA_TRUE; } EOLIAN static Evas_Object* _elm_entry_textblock_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return (Evas_Object *)edje_object_part_object_get (sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_efl_layout_calc_calc_force(Eo *obj, Elm_Entry_Data *sd) { edje_object_calc_force(sd->entry_edje); sd->changed = EINA_TRUE; elm_layout_sizing_eval(obj); } EOLIAN static const char* _elm_entry_selection_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { if ((sd->password)) return NULL; return edje_object_part_text_selection_get(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_selection_handler_disabled_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool disabled) { if (sd->sel_handler_disabled == disabled) return; sd->sel_handler_disabled = disabled; } EOLIAN static Eina_Bool _elm_entry_selection_handler_disabled_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->sel_handler_disabled; } EOLIAN static void _elm_entry_entry_insert(Eo *obj, Elm_Entry_Data *sd, const char *entry) { edje_object_part_text_insert(sd->entry_edje, "elm.text", entry); sd->changed = EINA_TRUE; elm_layout_sizing_eval(obj); } EOLIAN static void _elm_entry_line_wrap_set(Eo *obj, Elm_Entry_Data *sd, Elm_Wrap_Type wrap) { if (sd->line_wrap == wrap) return; sd->last_w = -1; sd->line_wrap = wrap; if (wrap == ELM_WRAP_NONE) ELM_SAFE_FREE(sd->deferred_recalc_job, ecore_job_del); efl_ui_widget_theme_apply(obj); } EOLIAN static Elm_Wrap_Type _elm_entry_line_wrap_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->line_wrap; } EOLIAN static void _elm_entry_editable_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool editable) { if (sd->editable == editable) return; sd->editable = editable; efl_ui_widget_theme_apply(obj); _elm_entry_focus_update(obj, sd); //legacy focus event emission if (efl_ui_focus_object_focus_get(obj)) evas_object_smart_callback_call(obj, "focused", NULL); else evas_object_smart_callback_call(obj, "unfocused", NULL); elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (editable) { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); } } EOLIAN static Eina_Bool _elm_entry_editable_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->editable; } EOLIAN static void _elm_entry_select_none(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { if ((sd->password)) return; if (sd->sel_mode) { sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); } if (sd->have_selection) efl_event_callback_legacy_call (obj, EFL_UI_EVENT_SELECTION_CLEARED, NULL); sd->have_selection = EINA_FALSE; edje_object_part_text_select_none(sd->entry_edje, "elm.text"); _hide_selection_handler(obj); } EOLIAN static void _elm_entry_select_all(Eo *obj, Elm_Entry_Data *sd) { if (elm_entry_is_empty(obj)) return; if ((sd->password)) return; if (sd->sel_mode) { sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); } edje_object_part_text_select_all(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_select_region_set(Eo *obj, Elm_Entry_Data *sd, int start, int end) { if (elm_entry_is_empty(obj)) return; if ((sd->password)) return; if (sd->sel_mode) { sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); } /* Set have selection false to not be cleared handler in selection_cleared_signal_cb() since that callback will be called while resetting edje text. */ sd->have_selection = EINA_FALSE; edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, start); edje_object_part_text_select_begin(sd->entry_edje, "elm.text"); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, end); edje_object_part_text_select_extend(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_select_region_get(const Eo *obj, Elm_Entry_Data *sd, int *start, int *end) { if (!elm_entry_selection_get(obj)) { if (start) *start = -1; if (end) *end = -1; return; } if (start) *start = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_BEGIN); if (end) *end = edje_object_part_text_cursor_pos_get(sd->entry_edje, "elm.text", EDJE_CURSOR_SELECTION_END); } EOLIAN static Eina_Bool _elm_entry_textblock_cursor_geometry_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h) { edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", x, y, w, h); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_cursor_next(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_next (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static Eina_Bool _elm_entry_cursor_prev(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_prev (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static Eina_Bool _elm_entry_cursor_up(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_up (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static Eina_Bool _elm_entry_cursor_down(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_down (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_begin_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_cursor_begin_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_end_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_cursor_end_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_line_begin_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_cursor_line_begin_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_line_end_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_cursor_line_end_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_selection_begin(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_select_begin(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_cursor_selection_end(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_select_extend(sd->entry_edje, "elm.text"); } EOLIAN static Eina_Bool _elm_entry_cursor_is_format_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_is_format_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static Eina_Bool _elm_entry_cursor_is_visible_format_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_is_visible_format_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static char* _elm_entry_textblock_cursor_content_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_content_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_cursor_pos_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, int pos) { edje_object_part_text_cursor_pos_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); edje_object_message_signal_process(sd->entry_edje); } EOLIAN static int _elm_entry_cursor_pos_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); } EOLIAN static void _elm_entry_selection_cut(Eo *obj, Elm_Entry_Data *sd) { if ((sd->password)) return; _cut_cb(obj, NULL, NULL); } EOLIAN static void _elm_entry_selection_copy(Eo *obj, Elm_Entry_Data *sd) { if ((sd->password)) return; _copy_cb(obj, NULL, NULL); } EOLIAN static void _elm_entry_selection_paste(Eo *obj, Elm_Entry_Data *sd) { if ((sd->password)) return; _paste_cb(obj, NULL, NULL); } EOLIAN static void _elm_entry_context_menu_clear(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { Elm_Entry_Context_Menu_Item *it; EINA_LIST_FREE(sd->items, it) { eina_stringshare_del(it->label); eina_stringshare_del(it->icon_file); eina_stringshare_del(it->icon_group); free(it); } } EOLIAN static void _elm_entry_context_menu_item_add(Eo *obj, Elm_Entry_Data *sd, const char *label, const char *icon_file, Elm_Icon_Type icon_type, Evas_Smart_Cb func, const void *data) { Elm_Entry_Context_Menu_Item *it; it = calloc(1, sizeof(Elm_Entry_Context_Menu_Item)); if (!it) return; sd->items = eina_list_append(sd->items, it); it->obj = obj; it->label = eina_stringshare_add(label); it->icon_file = eina_stringshare_add(icon_file); it->icon_type = icon_type; it->func = func; it->data = (void *)data; } EOLIAN static void _elm_entry_context_menu_disabled_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool disabled) { if (sd->context_menu == !disabled) return; sd->context_menu = !disabled; } EOLIAN static Eina_Bool _elm_entry_context_menu_disabled_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return !sd->context_menu; } EAPI const char * elm_entry_context_menu_item_label_get(const Elm_Entry_Context_Menu_Item *item) { if (!item) return NULL; return item->label; } EAPI void elm_entry_context_menu_item_icon_get(const Elm_Entry_Context_Menu_Item *item, const char **icon_file, const char **icon_group, Elm_Icon_Type *icon_type) { if (!item) return; if (icon_file) *icon_file = item->icon_file; if (icon_group) *icon_group = item->icon_group; if (icon_type) *icon_type = item->icon_type; } EOLIAN static void _elm_entry_item_provider_append(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) { Elm_Entry_Item_Provider *ip; EINA_SAFETY_ON_NULL_RETURN(func); ip = calloc(1, sizeof(Elm_Entry_Item_Provider)); if (!ip) return; ip->func = func; ip->data = data; sd->item_providers = eina_list_append(sd->item_providers, ip); } EOLIAN static void _elm_entry_item_provider_prepend(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) { Elm_Entry_Item_Provider *ip; EINA_SAFETY_ON_NULL_RETURN(func); ip = calloc(1, sizeof(Elm_Entry_Item_Provider)); if (!ip) return; ip->func = func; ip->data = data; sd->item_providers = eina_list_prepend(sd->item_providers, ip); } EOLIAN static void _elm_entry_item_provider_remove(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Item_Provider_Cb func, void *data) { Eina_List *l; Elm_Entry_Item_Provider *ip; EINA_SAFETY_ON_NULL_RETURN(func); EINA_LIST_FOREACH(sd->item_providers, l, ip) { if ((ip->func == func) && ((!data) || (ip->data == data))) { sd->item_providers = eina_list_remove_list(sd->item_providers, l); free(ip); return; } } } EOLIAN static void _elm_entry_markup_filter_append(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Filter_Cb func, void *data) { Elm_Entry_Markup_Filter *tf; EINA_SAFETY_ON_NULL_RETURN(func); tf = _filter_new(func, data); if (!tf) return; sd->markup_filters = eina_list_append(sd->markup_filters, tf); } EOLIAN static void _elm_entry_markup_filter_prepend(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Filter_Cb func, void *data) { Elm_Entry_Markup_Filter *tf; EINA_SAFETY_ON_NULL_RETURN(func); tf = _filter_new(func, data); if (!tf) return; sd->markup_filters = eina_list_prepend(sd->markup_filters, tf); } EOLIAN static void _elm_entry_markup_filter_remove(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Entry_Filter_Cb func, void *data) { Eina_List *l; Elm_Entry_Markup_Filter *tf; EINA_SAFETY_ON_NULL_RETURN(func); EINA_LIST_FOREACH(sd->markup_filters, l, tf) { if ((tf->func == func) && ((!data) || (tf->orig_data == data))) { sd->markup_filters = eina_list_remove_list(sd->markup_filters, l); _filter_free(tf); return; } } } EAPI char * elm_entry_markup_to_utf8(const char *s) { char *ss = _elm_util_mkup_to_text(s); if (!ss) ss = strdup(""); return ss; } EAPI char * elm_entry_utf8_to_markup(const char *s) { char *ss = _elm_util_text_to_mkup(s); if (!ss) ss = strdup(""); return ss; } static const char * _text_get(const Evas_Object *obj) { return elm_object_text_get(obj); } EAPI void elm_entry_filter_limit_size(void *data, Evas_Object *entry, char **text) { const char *(*text_get)(const Evas_Object *); Elm_Entry_Filter_Limit_Size *lim = data; char *current, *utfstr; int len, newlen; EINA_SAFETY_ON_NULL_RETURN(data); EINA_SAFETY_ON_NULL_RETURN(entry); EINA_SAFETY_ON_NULL_RETURN(text); /* hack. I don't want to copy the entire function to work with * scrolled_entry */ text_get = _text_get; current = elm_entry_markup_to_utf8(text_get(entry)); utfstr = elm_entry_markup_to_utf8(*text); if (lim->max_char_count > 0) { len = evas_string_char_len_get(current); newlen = evas_string_char_len_get(utfstr); if ((len >= lim->max_char_count) && (newlen > 0)) { efl_event_callback_legacy_call (entry, ELM_ENTRY_EVENT_MAXLENGTH_REACHED, NULL); ELM_SAFE_FREE(*text, free); free(current); free(utfstr); return; } if ((len + newlen) > lim->max_char_count) _chars_add_till_limit (entry, text, (lim->max_char_count - len), LENGTH_UNIT_CHAR); } else if (lim->max_byte_count > 0) { len = strlen(current); newlen = strlen(utfstr); if ((len >= lim->max_byte_count) && (newlen > 0)) { efl_event_callback_legacy_call (entry, ELM_ENTRY_EVENT_MAXLENGTH_REACHED, NULL); ELM_SAFE_FREE(*text, free); free(current); free(utfstr); return; } if ((len + newlen) > lim->max_byte_count) _chars_add_till_limit (entry, text, (lim->max_byte_count - len), LENGTH_UNIT_BYTE); } free(current); free(utfstr); } EAPI void elm_entry_filter_accept_set(void *data, Evas_Object *entry, char **text) { int read_idx, last_read_idx = 0, read_char; Elm_Entry_Filter_Accept_Set *as = data; Eina_Bool goes_in; Eina_Bool rejected = EINA_FALSE; const char *set; char *insert; EINA_SAFETY_ON_NULL_RETURN(data); EINA_SAFETY_ON_NULL_RETURN(text); if ((!as->accepted) && (!as->rejected)) return; if (as->accepted) { set = as->accepted; goes_in = EINA_TRUE; } else { set = as->rejected; goes_in = EINA_FALSE; } insert = *text; read_idx = evas_string_char_next_get(*text, 0, &read_char); while (read_char) { int cmp_idx, cmp_char; Eina_Bool in_set = EINA_FALSE; if (read_char == '<') { while (read_char && (read_char != '>')) read_idx = evas_string_char_next_get(*text, read_idx, &read_char); if (goes_in) in_set = EINA_TRUE; else in_set = EINA_FALSE; } else { if (read_char == '&') { while (read_char && (read_char != ';')) read_idx = evas_string_char_next_get(*text, read_idx, &read_char); if (!read_char) { if (goes_in) in_set = EINA_TRUE; else in_set = EINA_FALSE; goto inserting; } if (read_char == ';') { char *tag; int utf8 = 0; tag = malloc(read_idx - last_read_idx + 1); if (tag) { char *markup; strncpy(tag, (*text) + last_read_idx, read_idx - last_read_idx); tag[read_idx - last_read_idx] = 0; markup = elm_entry_markup_to_utf8(tag); free(tag); if (markup) { utf8 = *markup; free(markup); } if (!utf8) { in_set = EINA_FALSE; goto inserting; } read_char = utf8; } } } cmp_idx = evas_string_char_next_get(set, 0, &cmp_char); while (cmp_char) { if (read_char == cmp_char) { in_set = EINA_TRUE; break; } cmp_idx = evas_string_char_next_get(set, cmp_idx, &cmp_char); } } inserting: if (in_set == goes_in) { int size = read_idx - last_read_idx; const char *src = (*text) + last_read_idx; if (src != insert) memcpy(insert, *text + last_read_idx, size); insert += size; } else { rejected = EINA_TRUE; } if (read_char) { last_read_idx = read_idx; read_idx = evas_string_char_next_get(*text, read_idx, &read_char); } } *insert = 0; if (rejected) efl_event_callback_legacy_call(entry, ELM_ENTRY_EVENT_REJECTED, NULL); } EOLIAN static void _elm_entry_file_text_format_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Text_Format format) { sd->format = format; } EAPI Eina_Bool elm_entry_file_set(Evas_Object *obj, const char *file, Elm_Text_Format format) { Eina_Bool ret = EINA_FALSE; ELM_ENTRY_DATA_GET_OR_RETURN_VAL(obj, sd, ret); ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); if (sd->auto_save) _save_do(obj); elm_obj_entry_file_text_format_set(obj, format); sd->file_setting = EINA_TRUE; ret = efl_file_simple_load(obj, file, NULL); sd->file_setting = EINA_FALSE; return ret; } EOLIAN static void _elm_entry_efl_file_unload(Eo *obj, Elm_Entry_Data *sd EINA_UNUSED) { elm_object_text_set(obj, ""); efl_file_unload(efl_super(obj, MY_CLASS)); } EOLIAN static Eina_Error _elm_entry_efl_file_load(Eo *obj, Elm_Entry_Data *sd) { Eina_Error err; if (!sd->file_setting) CRI("EO methods should not be used directly on legacy objects!"); if (efl_file_loaded_get(obj)) return 0; err = efl_file_load(efl_super(obj, MY_CLASS)); if (err) return err; return _load_do(obj); } EOLIAN static Eina_Error _elm_entry_efl_file_file_set(Eo *obj, Elm_Entry_Data *sd, const char *file) { if (!sd->file_setting) CRI("EO methods should not be used directly on legacy objects!"); eina_stringshare_replace(&sd->file, file); return efl_file_set(efl_super(obj, MY_CLASS), file); } EAPI void elm_entry_file_get(const Evas_Object *obj, const char **file, Elm_Text_Format *format) { if (file) *file = efl_file_get(obj); if (format) { ELM_ENTRY_DATA_GET(obj, sd); if (!sd) return; *format = sd->format; } } EOLIAN static void _elm_entry_file_save(Eo *obj, Elm_Entry_Data *sd) { ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); _save_do(obj); sd->delay_write = ecore_timer_add(ELM_ENTRY_DELAY_WRITE_TIME, _delay_write, obj); } EOLIAN static void _elm_entry_autosave_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool auto_save) { sd->auto_save = !!auto_save; } EOLIAN static Eina_Bool _elm_entry_autosave_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->auto_save; } EINA_DEPRECATED EAPI void elm_entry_cnp_textonly_set(Evas_Object *obj, Eina_Bool textonly) { Elm_Cnp_Mode cnp_mode = ELM_CNP_MODE_MARKUP; ELM_ENTRY_CHECK(obj); if (textonly) cnp_mode = ELM_CNP_MODE_NO_IMAGE; elm_entry_cnp_mode_set(obj, cnp_mode); } EINA_DEPRECATED EAPI Eina_Bool elm_entry_cnp_textonly_get(const Evas_Object *obj) { ELM_ENTRY_CHECK(obj) EINA_FALSE; return elm_entry_cnp_mode_get(obj) != ELM_CNP_MODE_MARKUP; } EOLIAN static void _elm_entry_cnp_mode_set(Eo *obj, Elm_Entry_Data *sd, Elm_Cnp_Mode cnp_mode) { Elm_Sel_Format format = ELM_SEL_FORMAT_MARKUP; if (sd->cnp_mode == cnp_mode) return; sd->cnp_mode = cnp_mode; if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) format = ELM_SEL_FORMAT_TEXT; else if (cnp_mode == ELM_CNP_MODE_MARKUP) format |= ELM_SEL_FORMAT_IMAGE; elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); sd->drop_format = format; elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); } EOLIAN static Elm_Cnp_Mode _elm_entry_cnp_mode_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->cnp_mode; } static void _elm_entry_content_viewport_resize_cb(Evas_Object *obj, Evas_Coord w EINA_UNUSED, Evas_Coord h EINA_UNUSED) { _elm_entry_resize_internal(obj); } static void _scroll_cb(Evas_Object *obj, void *data EINA_UNUSED) { ELM_ENTRY_DATA_GET(obj, sd); /* here we need to emit the signal that the elm_scroller would have done */ evas_object_smart_callback_call(obj, "scroll", NULL); if (sd->have_selection) _update_selection_handler(obj); } EOLIAN static void _elm_entry_scrollable_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool scroll) { scroll = !!scroll; if (sd->scroll == scroll) return; sd->scroll = scroll; if (sd->scroll) { /* we now must re-theme ourselves to a scroller decoration * and move the entry looking object to be the content of the * scrollable view */ elm_widget_resize_object_set(obj, NULL); elm_widget_sub_object_add(obj, sd->entry_edje); if (!sd->scr_edje) { sd->scr_edje = edje_object_add(evas_object_evas_get(obj)); elm_widget_theme_object_set (obj, sd->scr_edje, "scroller", "entry", elm_widget_style_get(obj)); evas_object_size_hint_weight_set (sd->scr_edje, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set (sd->scr_edje, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_propagate_events_set(sd->scr_edje, EINA_TRUE); } elm_widget_resize_object_set(obj, sd->scr_edje); elm_interface_scrollable_objects_set(obj, sd->scr_edje, sd->hit_rect); elm_interface_scrollable_scroll_cb_set(obj, _scroll_cb); elm_interface_scrollable_bounce_allow_set(obj, sd->h_bounce, sd->v_bounce); if (sd->single_line) elm_interface_scrollable_policy_set(obj, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF); else elm_interface_scrollable_policy_set(obj, sd->policy_h, sd->policy_v); elm_interface_scrollable_content_set(obj, sd->entry_edje); elm_interface_scrollable_content_viewport_resize_cb_set(obj, _elm_entry_content_viewport_resize_cb); elm_widget_on_show_region_hook_set(obj, NULL, _show_region_hook, NULL); } else { if (sd->scr_edje) { elm_interface_scrollable_content_set(obj, NULL); evas_object_hide(sd->scr_edje); } elm_widget_resize_object_set(obj, sd->entry_edje); if (sd->scr_edje) elm_widget_sub_object_add(obj, sd->scr_edje); elm_interface_scrollable_objects_set(obj, sd->entry_edje, sd->hit_rect); elm_widget_on_show_region_hook_set(obj, NULL, NULL, NULL); } sd->last_w = -1; efl_ui_widget_theme_apply(obj); } EOLIAN static Eina_Bool _elm_entry_scrollable_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->scroll; } EOLIAN static void _elm_entry_icon_visible_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool setting) { if (!edje_object_part_swallow_get(sd->scr_edje, "elm.swallow.icon")) return; if (setting) edje_object_signal_emit(sd->scr_edje, "elm,action,show,icon", "elm"); else edje_object_signal_emit(sd->scr_edje, "elm,action,hide,icon", "elm"); elm_layout_sizing_eval(obj); } EOLIAN static void _elm_entry_end_visible_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool setting) { if (!edje_object_part_swallow_get(sd->scr_edje, "elm.swallow.end")) return; if (setting) edje_object_signal_emit(sd->scr_edje, "elm,action,show,end", "elm"); else edje_object_signal_emit(sd->scr_edje, "elm,action,hide,end", "elm"); elm_layout_sizing_eval(obj); } EAPI void elm_entry_scrollbar_policy_set(Evas_Object *obj, Elm_Scroller_Policy h, Elm_Scroller_Policy v) { ELM_ENTRY_CHECK(obj); elm_interface_scrollable_policy_set(obj, h, v); } EOLIAN static void _elm_entry_elm_interface_scrollable_policy_set(Eo *obj, Elm_Entry_Data *sd, Elm_Scroller_Policy h, Elm_Scroller_Policy v) { sd->policy_h = h; sd->policy_v = v; elm_interface_scrollable_policy_set(efl_super(obj, MY_CLASS), sd->policy_h, sd->policy_v); } EAPI void elm_entry_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce) { ELM_ENTRY_CHECK(obj); elm_interface_scrollable_bounce_allow_set(obj, h_bounce, v_bounce); } EOLIAN static void _elm_entry_elm_interface_scrollable_bounce_allow_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool h_bounce, Eina_Bool v_bounce) { sd->h_bounce = h_bounce; sd->v_bounce = v_bounce; elm_interface_scrollable_bounce_allow_set(efl_super(obj, MY_CLASS), h_bounce, v_bounce); } EAPI void elm_entry_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce) { ELM_ENTRY_CHECK(obj); elm_interface_scrollable_bounce_allow_get((Eo *) obj, h_bounce, v_bounce); } EOLIAN static void _elm_entry_input_panel_layout_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Input_Panel_Layout layout) { sd->input_panel_layout = layout; edje_object_part_text_input_panel_layout_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Layout)layout); switch (layout) { case ELM_INPUT_PANEL_LAYOUT_URL: case ELM_INPUT_PANEL_LAYOUT_EMAIL: case ELM_INPUT_PANEL_LAYOUT_PASSWORD: elm_entry_autocapital_type_set(obj, ELM_AUTOCAPITAL_TYPE_NONE); break; default: elm_entry_autocapital_type_set(obj, ELM_AUTOCAPITAL_TYPE_SENTENCE); break; } if (layout == ELM_INPUT_PANEL_LAYOUT_PASSWORD) elm_entry_input_hint_set(obj, ((sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE) | ELM_INPUT_HINT_SENSITIVE_DATA)); else if (layout == ELM_INPUT_PANEL_LAYOUT_TERMINAL) elm_entry_input_hint_set(obj, (sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE)); } EOLIAN static Elm_Input_Panel_Layout _elm_entry_input_panel_layout_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_layout; } EOLIAN static void _elm_entry_input_panel_layout_variation_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, int variation) { sd->input_panel_layout_variation = variation; edje_object_part_text_input_panel_layout_variation_set (sd->entry_edje, "elm.text", variation); if (sd->input_panel_layout == ELM_INPUT_PANEL_LAYOUT_NORMAL && variation == ELM_INPUT_PANEL_LAYOUT_NORMAL_VARIATION_PERSON_NAME) elm_entry_autocapital_type_set(obj, ELM_AUTOCAPITAL_TYPE_WORD); } EOLIAN static int _elm_entry_input_panel_layout_variation_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_layout_variation; } EOLIAN static void _elm_entry_autocapital_type_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Autocapital_Type autocapital_type) { sd->autocapital_type = autocapital_type; edje_object_part_text_autocapital_type_set (sd->entry_edje, "elm.text", (Edje_Text_Autocapital_Type)autocapital_type); } EOLIAN static Elm_Autocapital_Type _elm_entry_autocapital_type_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->autocapital_type; } EOLIAN static void _elm_entry_prediction_allow_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool prediction) { sd->prediction_allow = prediction; edje_object_part_text_prediction_allow_set (sd->entry_edje, "elm.text", prediction); } EOLIAN static Eina_Bool _elm_entry_prediction_allow_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->prediction_allow; } EOLIAN static void _elm_entry_input_hint_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Input_Hints hints) { sd->input_hints = hints; edje_object_part_text_input_hint_set (sd->entry_edje, "elm.text", (Edje_Input_Hints)hints); } EOLIAN static Elm_Input_Hints _elm_entry_input_hint_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_hints; } EOLIAN static void _elm_entry_prediction_hint_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *prediction_hint) { if (sd->prediction_hint) free(sd->prediction_hint); sd->prediction_hint = strdup(prediction_hint); edje_object_part_text_prediction_hint_set (sd->entry_edje, "elm.text", prediction_hint); } EOLIAN static Eina_Bool _elm_entry_prediction_hint_hash_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *key, const char *value) { return edje_object_part_text_prediction_hint_hash_set (sd->entry_edje, "elm.text", key, value); } EOLIAN static Eina_Bool _elm_entry_prediction_hint_hash_del(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *key) { return edje_object_part_text_prediction_hint_hash_del (sd->entry_edje, "elm.text", key); } EOLIAN static void _elm_entry_imf_context_reset(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_imf_context_reset(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_input_panel_enabled_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool enabled) { sd->input_panel_enable = enabled; edje_object_part_text_input_panel_enabled_set (sd->entry_edje, "elm.text", enabled); } EOLIAN static Eina_Bool _elm_entry_input_panel_enabled_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_enable; } EOLIAN static void _elm_entry_input_panel_show(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_input_panel_show(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_input_panel_hide(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { edje_object_part_text_input_panel_hide(sd->entry_edje, "elm.text"); } EOLIAN static void _elm_entry_input_panel_language_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Input_Panel_Lang lang) { sd->input_panel_lang = lang; edje_object_part_text_input_panel_language_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Lang)lang); } EOLIAN static Elm_Input_Panel_Lang _elm_entry_input_panel_language_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_lang; } EOLIAN static void _elm_entry_input_panel_imdata_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const void *data, int len) { free(sd->input_panel_imdata); sd->input_panel_imdata = calloc(1, len); sd->input_panel_imdata_len = len; memcpy(sd->input_panel_imdata, data, len); edje_object_part_text_input_panel_imdata_set (sd->entry_edje, "elm.text", sd->input_panel_imdata, sd->input_panel_imdata_len); } EOLIAN static void _elm_entry_input_panel_imdata_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, void *data, int *len) { edje_object_part_text_input_panel_imdata_get (sd->entry_edje, "elm.text", data, len); } EOLIAN static void _elm_entry_input_panel_return_key_type_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Elm_Input_Panel_Return_Key_Type return_key_type) { sd->input_panel_return_key_type = return_key_type; edje_object_part_text_input_panel_return_key_type_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Return_Key_Type)return_key_type); } EOLIAN static Elm_Input_Panel_Return_Key_Type _elm_entry_input_panel_return_key_type_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_return_key_type; } EOLIAN static void _elm_entry_input_panel_return_key_disabled_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool disabled) { sd->input_panel_return_key_disabled = disabled; edje_object_part_text_input_panel_return_key_disabled_set (sd->entry_edje, "elm.text", disabled); } EOLIAN static Eina_Bool _elm_entry_input_panel_return_key_disabled_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_return_key_disabled; } EOLIAN static void _elm_entry_input_panel_return_key_autoenabled_set(Eo *obj, Elm_Entry_Data *sd, Eina_Bool enabled) { sd->auto_return_key = enabled; _return_key_enabled_check(obj); } EOLIAN static void _elm_entry_input_panel_show_on_demand_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool ondemand) { sd->input_panel_show_on_demand = ondemand; edje_object_part_text_input_panel_show_on_demand_set (sd->entry_edje, "elm.text", ondemand); } EOLIAN static Eina_Bool _elm_entry_input_panel_show_on_demand_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->input_panel_show_on_demand; } EOLIAN static void* _elm_entry_imf_context_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { if (!sd) return NULL; return edje_object_part_text_imf_context_get(sd->entry_edje, "elm.text"); } /* START - ANCHOR HOVER */ static void _anchor_parent_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_ENTRY_DATA_GET(data, sd); sd->anchor_hover.hover_parent = NULL; } EOLIAN static void _elm_entry_anchor_hover_parent_set(Eo *obj, Elm_Entry_Data *sd, Evas_Object *parent) { if (sd->anchor_hover.hover_parent) evas_object_event_callback_del_full (sd->anchor_hover.hover_parent, EVAS_CALLBACK_DEL, _anchor_parent_del_cb, obj); sd->anchor_hover.hover_parent = parent; if (sd->anchor_hover.hover_parent) evas_object_event_callback_add (sd->anchor_hover.hover_parent, EVAS_CALLBACK_DEL, _anchor_parent_del_cb, obj); } EOLIAN static Evas_Object* _elm_entry_anchor_hover_parent_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->anchor_hover.hover_parent; } EOLIAN static void _elm_entry_anchor_hover_style_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, const char *style) { eina_stringshare_replace(&sd->anchor_hover.hover_style, style); } EOLIAN static const char* _elm_entry_anchor_hover_style_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->anchor_hover.hover_style; } EOLIAN static void _elm_entry_anchor_hover_end(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { ELM_SAFE_FREE(sd->anchor_hover.hover, evas_object_del); ELM_SAFE_FREE(sd->anchor_hover.pop, evas_object_del); } /* END - ANCHOR HOVER */ static void _activate(Evas_Object *obj) { ELM_ENTRY_DATA_GET(obj, sd); if (!elm_widget_disabled_get(obj) && !evas_object_freeze_events_get(obj)) { evas_object_smart_callback_call ( obj, "clicked", NULL); if (sd->editable && sd->input_panel_enable) edje_object_part_text_input_panel_show(sd->entry_edje, "elm.text"); } } EOLIAN static Eina_Bool _elm_entry_efl_ui_widget_on_access_activate(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, Efl_Ui_Activate act) { if (act != EFL_UI_ACTIVATE_DEFAULT) return EINA_FALSE; _activate(obj); return EINA_TRUE; } EOLIAN static void _elm_entry_select_allow_set(Eo *obj EINA_UNUSED, Elm_Entry_Data *sd, Eina_Bool allow) { if (sd->sel_allow == allow) return; sd->sel_allow = allow; edje_object_part_text_select_allow_set(sd->entry_edje, "elm.text", allow); } EOLIAN static Eina_Bool _elm_entry_select_allow_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd) { return sd->sel_allow; } static void _elm_entry_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } // ATSPI Accessibility EOLIAN static Eina_Unicode _elm_entry_efl_access_text_character_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int offset) { char *txt; int idx = 0; Eina_Unicode ret = 0; if (offset < 0) return ret; txt = _elm_util_mkup_to_text(elm_entry_entry_get(obj)); if (!txt) return ret; ret = eina_unicode_utf8_next_get(txt, &idx); while (offset--) ret = eina_unicode_utf8_next_get(txt, &idx); free(txt); if (_pd->password) ret = ENTRY_PASSWORD_MASK_CHARACTER; return ret; } EOLIAN static int _elm_entry_efl_access_text_character_count_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED) { char *txt; int ret = -1; txt = _elm_util_mkup_to_text(elm_entry_entry_get(obj)); if (!txt) return ret; ret = eina_unicode_utf8_get_len(txt); free(txt); return ret; } EOLIAN static char* _elm_entry_efl_access_text_string_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, Efl_Access_Text_Granularity granularity, int *start_offset, int *end_offset) { Evas_Textblock_Cursor *cur = NULL, *cur2 = NULL; Evas_Object *tblk; char *ret = NULL; tblk = elm_entry_textblock_get(obj); if (!tblk) goto fail; cur = evas_object_textblock_cursor_new(tblk); cur2 = evas_object_textblock_cursor_new(tblk); if (!cur || !cur2) goto fail; evas_textblock_cursor_pos_set(cur, *start_offset); if (evas_textblock_cursor_pos_get(cur) != *start_offset) goto fail; switch (granularity) { case EFL_ACCESS_TEXT_GRANULARITY_CHAR: break; case EFL_ACCESS_TEXT_GRANULARITY_WORD: evas_textblock_cursor_word_start(cur); break; case EFL_ACCESS_TEXT_GRANULARITY_SENTENCE: // TODO - add sentence support in textblock first break; case EFL_ACCESS_TEXT_GRANULARITY_LINE: evas_textblock_cursor_line_char_first(cur); break; case EFL_ACCESS_TEXT_GRANULARITY_PARAGRAPH: evas_textblock_cursor_paragraph_char_first(cur); break; } *start_offset = evas_textblock_cursor_pos_get(cur); evas_textblock_cursor_copy(cur, cur2); switch (granularity) { case EFL_ACCESS_TEXT_GRANULARITY_CHAR: evas_textblock_cursor_char_next(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_WORD: evas_textblock_cursor_word_end(cur2); // since word_end sets cursor position ON (before) last // char of word, we need to manually advance cursor to get // proper string from function range_text_get evas_textblock_cursor_char_next(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_SENTENCE: // TODO - add sentence support in textblock first break; case EFL_ACCESS_TEXT_GRANULARITY_LINE: evas_textblock_cursor_line_char_last(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_PARAGRAPH: evas_textblock_cursor_paragraph_char_last(cur2); break; } if (end_offset) *end_offset = evas_textblock_cursor_pos_get(cur2); ret = evas_textblock_cursor_range_text_get(cur, cur2, EVAS_TEXTBLOCK_TEXT_PLAIN); evas_textblock_cursor_free(cur); evas_textblock_cursor_free(cur2); if (ret && _pd->password) { int i = 0; while (ret[i] != '\0') ret[i++] = ENTRY_PASSWORD_MASK_CHARACTER; } return ret; fail: if (start_offset) *start_offset = -1; if (end_offset) *end_offset = -1; if (cur) evas_textblock_cursor_free(cur); if (cur2) evas_textblock_cursor_free(cur2); return NULL; } EOLIAN static char* _elm_entry_efl_access_text_access_text_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int start_offset, int end_offset) { Evas_Textblock_Cursor *cur = NULL, *cur2 = NULL; Evas_Object *tblk; char *ret = NULL; tblk = elm_entry_textblock_get(obj); if (!tblk) goto fail; cur = evas_object_textblock_cursor_new(tblk); cur2 = evas_object_textblock_cursor_new(tblk); if (!cur || !cur2) goto fail; evas_textblock_cursor_pos_set(cur, start_offset); if (evas_textblock_cursor_pos_get(cur) != start_offset) goto fail; evas_textblock_cursor_pos_set(cur2, end_offset); if (evas_textblock_cursor_pos_get(cur2) != end_offset) goto fail; ret = evas_textblock_cursor_range_text_get(cur, cur2, EVAS_TEXTBLOCK_TEXT_PLAIN); evas_textblock_cursor_free(cur); evas_textblock_cursor_free(cur2); if (ret && _pd->password) { int i = 0; while (ret[i] != '\0') ret[i++] = ENTRY_PASSWORD_MASK_CHARACTER; } return ret; fail: if (cur) evas_textblock_cursor_free(cur); if (cur2) evas_textblock_cursor_free(cur2); return NULL; } EOLIAN static int _elm_entry_efl_access_text_caret_offset_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED) { return elm_entry_cursor_pos_get(obj); } EOLIAN static Eina_Bool _elm_entry_efl_access_text_caret_offset_set(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int offset) { elm_entry_cursor_pos_set(obj, offset); return EINA_TRUE; } EOLIAN static int _elm_entry_efl_access_text_selections_count_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED) { return elm_entry_selection_get(obj) ? 1 : 0; } EOLIAN static void _elm_entry_efl_access_text_access_selection_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int selection_number, int *start_offset, int *end_offset) { if (selection_number != 0) return; elm_obj_entry_select_region_get(obj, start_offset, end_offset); } EOLIAN static Eina_Bool _elm_entry_efl_access_text_access_selection_set(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int selection_number, int start_offset, int end_offset) { if (selection_number != 0) return EINA_FALSE; elm_entry_select_region_set(obj, start_offset, end_offset); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_text_selection_remove(Eo *obj, Elm_Entry_Data *pd EINA_UNUSED, int selection_number) { if (selection_number != 0) return EINA_FALSE; elm_entry_select_none(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_text_selection_add(Eo *obj, Elm_Entry_Data *pd EINA_UNUSED, int start_offset, int end_offset) { elm_entry_select_region_set(obj, start_offset, end_offset); return EINA_TRUE; } EOLIAN static Eina_List* _elm_entry_efl_access_text_bounded_ranges_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *_pd EINA_UNUSED, Eina_Bool screen_coods EINA_UNUSED, Eina_Rect rect EINA_UNUSED, Efl_Access_Text_Clip_Type xclip EINA_UNUSED, Efl_Access_Text_Clip_Type yclip EINA_UNUSED) { return NULL; } EOLIAN static int _elm_entry_efl_access_text_offset_at_point_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, Eina_Bool screen_coods, int x, int y) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur; int ret; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return -1; cur = evas_object_textblock_cursor_new(txtblk); if (!cur) return -1; if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); x -= ee_x; y -= ee_y; } if (!evas_textblock_cursor_char_coord_set(cur, x, y)) { evas_textblock_cursor_free(cur); return -1; } ret = evas_textblock_cursor_pos_get(cur); evas_textblock_cursor_free(cur); return ret; } EOLIAN static Eina_Bool _elm_entry_efl_access_text_character_extents_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int offset, Eina_Bool screen_coods, Eina_Rect *rect) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur; int ret; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return EINA_FALSE; cur = evas_object_textblock_cursor_new(txtblk); if (!cur) return EINA_FALSE; evas_textblock_cursor_pos_set(cur, offset); ret = evas_textblock_cursor_char_geometry_get(cur, &rect->x, &rect->y, &rect->w, &rect->h); evas_textblock_cursor_free(cur); if (ret == -1) return EINA_FALSE; if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); rect->x += ee_x; rect->y += ee_y; } return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_text_range_extents_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, Eina_Bool screen_coods, int start_offset, int end_offset, Eina_Rect *rect) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur1, *cur2; int ret; int x, xx, y, yy; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return EINA_FALSE; cur1 = evas_object_textblock_cursor_new(txtblk); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(txtblk); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, start_offset); evas_textblock_cursor_pos_set(cur2, end_offset); ret = evas_textblock_cursor_char_geometry_get(cur1, &x, &y, NULL, NULL); ret += evas_textblock_cursor_char_geometry_get(cur2, &xx, &yy, NULL, NULL); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (ret != 0) return EINA_FALSE; rect->x = x < xx ? x : xx; rect->y = y < yy ? y : yy; rect->w = abs(x - xx); rect->h = abs(y - yy); if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); rect->x += ee_x; rect->y += ee_y; } return EINA_TRUE; } static Efl_Access_Text_Attribute* _textblock_node_format_to_atspi_text_attr(const Evas_Object_Textblock_Node_Format *format) { Efl_Access_Text_Attribute *ret = NULL; const char *txt; txt = evas_textblock_node_format_text_get(format); if (!txt) return NULL; if (txt[0] == '-') return NULL; // skip closing format if (!strncmp(txt, "+ ", 2)) { const char *tmp = &txt[2]; while (*tmp != '\0' && *tmp != '=') tmp++; if (*tmp++ != '=') return NULL; ret = calloc(1, sizeof(Efl_Access_Text_Attribute)); if (!ret) return NULL; ret->value = eina_stringshare_add(tmp); int size = &txt[2] - tmp + 1; ret->name = eina_stringshare_add_length(&txt[2], size > 0 ? size : -size); } return ret; } EOLIAN static Eina_Bool _elm_entry_efl_access_text_attribute_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, const char *attr_name EINA_UNUSED, int *start_offset, int *end_offset, char **value) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur1, *cur2; Eina_List *formats, *l; Evas_Object_Textblock_Node_Format *format; Efl_Access_Text_Attribute *attr; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return EINA_FALSE; cur1 = evas_object_textblock_cursor_new(txtblk); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(txtblk); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, *start_offset); evas_textblock_cursor_pos_set(cur2, *end_offset); formats = evas_textblock_cursor_range_formats_get(cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (!formats) return EINA_FALSE; EINA_LIST_FOREACH(formats, l , format) { attr = _textblock_node_format_to_atspi_text_attr(format); if (!attr) continue; if (!strcmp(attr->name, attr_name)) { *value = attr->value ? strdup(attr->value) : NULL; elm_atspi_text_text_attribute_free(attr); return EINA_TRUE; } elm_atspi_text_text_attribute_free(attr); } return EINA_FALSE; } EOLIAN static Eina_List* _elm_entry_efl_access_text_text_attributes_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int *start_offset, int *end_offset) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur1, *cur2; Eina_List *formats, *ret = NULL, *l; Evas_Object_Textblock_Node_Format *format; Efl_Access_Text_Attribute *attr; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return NULL; cur1 = evas_object_textblock_cursor_new(txtblk); if (!cur1) return NULL; cur2 = evas_object_textblock_cursor_new(txtblk); if (!cur2) { evas_textblock_cursor_free(cur1); return NULL; } evas_textblock_cursor_pos_set(cur1, *start_offset); evas_textblock_cursor_pos_set(cur2, *end_offset); formats = evas_textblock_cursor_range_formats_get(cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (!formats) return NULL; EINA_LIST_FOREACH(formats, l , format) { attr = _textblock_node_format_to_atspi_text_attr(format); if (!attr) continue; ret = eina_list_append(ret, attr); } return ret; } EOLIAN static Eina_List* _elm_entry_efl_access_text_default_attributes_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *_pd EINA_UNUSED) { Evas_Object *txtblk; Eina_List *ret = NULL; const Evas_Object_Textblock_Node_Format *format; Efl_Access_Text_Attribute *attr; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return NULL; format = evas_textblock_node_format_first_get(txtblk); if (!format) return NULL; do { attr = _textblock_node_format_to_atspi_text_attr(format); if (!attr) continue; ret = eina_list_append(ret, attr); } while ((format = evas_textblock_node_format_next_get(format)) != NULL); return ret; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_text_content_set(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, const char *content) { elm_entry_entry_set(obj, content); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_insert(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, const char *string, int position) { elm_entry_cursor_pos_set(obj, position); elm_entry_entry_insert(obj, string); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_copy(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int start, int end) { elm_entry_select_region_set(obj, start, end); elm_entry_selection_copy(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_delete(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int start_offset, int end_offset) { Evas_Object *txtblk; Evas_Textblock_Cursor *cur1, *cur2; txtblk = elm_entry_textblock_get(obj); if (!txtblk) return EINA_FALSE; cur1 = evas_object_textblock_cursor_new(txtblk); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(txtblk); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, start_offset); evas_textblock_cursor_pos_set(cur2, end_offset); evas_textblock_cursor_range_delete(cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); elm_entry_calc_force(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_paste(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int position) { elm_entry_cursor_pos_set(obj, position); elm_entry_selection_paste(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _elm_entry_efl_access_editable_text_cut(Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED, int start, int end) { elm_entry_select_region_set(obj, start, end); elm_entry_selection_cut(obj); return EINA_TRUE; } EOLIAN static Efl_Access_State_Set _elm_entry_efl_access_object_state_set_get(const Eo *obj, Elm_Entry_Data *_pd EINA_UNUSED) { Efl_Access_State_Set ret; ret = efl_access_object_state_set_get(efl_super(obj, ELM_ENTRY_CLASS)); if (elm_entry_editable_get(obj)) STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_EDITABLE); return ret; } EOLIAN static const char* _elm_entry_efl_access_object_i18n_name_get(const Eo *obj, Elm_Entry_Data *sd) { const char *name; name = efl_access_object_i18n_name_get(efl_super(obj, ELM_ENTRY_CLASS)); if (name && strncmp("", name, 1)) return name; if (sd->password) return NULL; name = _elm_widget_accessible_plain_name_get(obj, elm_entry_entry_get(obj)); if (name && strncmp("", name, 1)) return name; const char *ret = edje_object_part_text_get(sd->entry_edje, "elm.guide"); return _elm_widget_accessible_plain_name_get(obj, ret); } static Eina_Bool _action_activate(Evas_Object *obj, const char *params EINA_UNUSED) { _activate(obj); return EINA_TRUE; } EOLIAN const Efl_Access_Action_Data * _elm_entry_efl_access_widget_action_elm_actions_get(const Eo *obj EINA_UNUSED, Elm_Entry_Data *sd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "activate", "activate", NULL, _action_activate }, { NULL, NULL, NULL, NULL} }; return &atspi_actions[0]; } EOLIAN static Efl_Ui_Focus_Manager* _elm_entry_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Elm_Entry_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root) { return efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj, efl_ui_focus_manager_root_set(efl_added, root) ); } /* Efl.Part begin */ ELM_PART_OVERRIDE(elm_entry, ELM_ENTRY, Elm_Entry_Data) ELM_PART_OVERRIDE_CONTENT_SET(elm_entry, ELM_ENTRY, Elm_Entry_Data) ELM_PART_OVERRIDE_CONTENT_UNSET(elm_entry, ELM_ENTRY, Elm_Entry_Data) ELM_PART_OVERRIDE_TEXT_SET(elm_entry, ELM_ENTRY, Elm_Entry_Data) ELM_PART_OVERRIDE_TEXT_GET(elm_entry, ELM_ENTRY, Elm_Entry_Data) ELM_PART_CONTENT_DEFAULT_GET(elm_entry, "icon") #include "elm_entry_part.eo.c" /* Efl.Part end */ /* Internal EO APIs and hidden overrides */ EFL_UI_LAYOUT_CONTENT_ALIASES_IMPLEMENT(MY_CLASS_PFX) EFL_UI_LAYOUT_TEXT_ALIASES_IMPLEMENT(MY_CLASS_PFX) #define ELM_ENTRY_EXTRA_OPS \ ELM_PART_CONTENT_DEFAULT_OPS(elm_entry), \ EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_entry), \ EFL_UI_LAYOUT_CONTENT_ALIASES_OPS(MY_CLASS_PFX), \ EFL_UI_LAYOUT_TEXT_ALIASES_OPS(MY_CLASS_PFX), \ EFL_CANVAS_GROUP_CALC_OPS(elm_entry) #include "elm_entry_eo.c" diff --git a/src/lib/elementary/elm_interface_scrollable.c b/src/lib/elementary/elm_interface_scrollable.c index 8a2c390f9f..7130efbd94 100644 --- a/src/lib/elementary/elm_interface_scrollable.c +++ b/src/lib/elementary/elm_interface_scrollable.c @@ -1,4998 +1,5010 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #include #define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED #include "elm_priv.h" #include "elm_interface_scrollable.h" #include "elm_pan_eo.h" #define MY_PAN_CLASS ELM_PAN_CLASS #define MY_PAN_CLASS_NAME "Elm_Pan" #define MY_PAN_CLASS_NAME_LEGACY "elm_pan" #define ELM_PAN_DATA_GET_OR_RETURN(o, ptr) \ Elm_Pan_Smart_Data *ptr = efl_data_scope_get(o, MY_PAN_CLASS); \ if (!ptr) \ { \ CRI("No smart data for object %p (%s)", \ o, evas_object_type_get(o)); \ return; \ } #define ELM_PAN_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ Elm_Pan_Smart_Data *ptr = efl_data_scope_get(o, MY_PAN_CLASS); \ if (!ptr) \ { \ CRI("No smart data for object %p (%s)", \ o, evas_object_type_get(o)); \ return val; \ } #define ELM_ANIMATOR_CONNECT(Obj, Bool, Callback, Data) \ efl_event_callback_del(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ efl_event_callback_add(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ Bool = 1; #define ELM_ANIMATOR_DISCONNECT(Obj, Bool, Callback, Data) \ efl_event_callback_del(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ Bool = 0; #ifndef CLAMP # define CLAMP(x, min, \ max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) #endif static const char SIG_CHANGED[] = "changed"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_CHANGED, ""}, {NULL, NULL} }; static void _elm_pan_content_set(Evas_Object *, Evas_Object *); static void _elm_scroll_scroll_to_x(Elm_Scrollable_Smart_Interface_Data *sid, double t_in, Evas_Coord pos_x); static void _elm_scroll_scroll_to_y(Elm_Scrollable_Smart_Interface_Data *sid, double t_in, Evas_Coord pos_y); static void _elm_scroll_wanted_coordinates_update(Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y); static void _elm_scroll_hold_animator(void *data, const Efl_Event *event); static void _elm_scroll_on_hold_animator(void *data, const Efl_Event *event); static void _elm_scroll_scroll_to_y_animator(void *data, const Efl_Event *event); static void _elm_scroll_scroll_to_x_animator(void *data, const Efl_Event *event); static void _elm_scroll_bounce_y_animator(void *data, const Efl_Event *event); static void _elm_scroll_bounce_x_animator(void *data, const Efl_Event *event); static void _elm_scroll_momentum_animator(void *data, const Efl_Event *event); static const char iface_scr_legacy_dragable_hbar[] = "elm.dragable.hbar"; static const char iface_scr_legacy_dragable_vbar[] = "elm.dragable.vbar"; static const char iface_scr_efl_ui_dragable_hbar[] = "efl.draggable.horizontal_bar"; static const char iface_scr_efl_ui_dragable_vbar[] = "efl.draggable.vertical_bar"; static double _round(double value, int pos) { double temp; temp = value * pow( 10, pos ); temp = floor( temp + 0.5 ); temp *= pow( 10, -pos ); return temp; } static void _elm_pan_update(Elm_Pan_Smart_Data *psd) { if (psd->content) { Efl_Ui_Focus_Manager *manager; manager = psd->interface_object; efl_ui_focus_manager_dirty_logic_freeze(manager); evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py); efl_ui_focus_manager_dirty_logic_unfreeze(manager); //XXX: hack, right now there is no api in efl_ui_focus_manager_sub.eo to mark it dirty // If we have moved the content, then emit this event, in order to ensure that the focus_manager_sub // logic tries to fetch the viewport again efl_event_callback_call(manager, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL); } } EOLIAN static void _elm_pan_efl_canvas_group_group_add(Eo *obj, Elm_Pan_Smart_Data *priv) { efl_canvas_group_add(efl_super(obj, MY_PAN_CLASS)); priv->self = obj; } EOLIAN static void _elm_pan_efl_canvas_group_group_del(Eo *obj, Elm_Pan_Smart_Data *_pd EINA_UNUSED) { _elm_pan_content_set(obj, NULL); efl_canvas_group_del(efl_super(obj, MY_PAN_CLASS)); } EOLIAN static void _elm_pan_efl_gfx_entity_position_set(Eo *obj, Elm_Pan_Smart_Data *psd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; efl_gfx_entity_position_set(efl_super(obj, MY_PAN_CLASS), pos); psd->x = pos.x; psd->y = pos.y; _elm_pan_update(psd); } EOLIAN static void _elm_pan_efl_gfx_entity_size_set(Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *psd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; efl_gfx_entity_size_set(efl_super(obj, MY_PAN_CLASS), sz); psd->w = sz.w; psd->h = sz.h; _elm_pan_update(psd); efl_event_callback_legacy_call(psd->self, ELM_PAN_EVENT_CHANGED, NULL); } EOLIAN static void _elm_pan_efl_gfx_entity_visible_set(Eo *obj, Elm_Pan_Smart_Data *psd, Eina_Bool vis) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis)) return; efl_gfx_entity_visible_set(efl_super(obj, MY_PAN_CLASS), vis); if (psd->content) efl_gfx_entity_visible_set(psd->content, vis); } EOLIAN static void _elm_pan_pos_set(Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *psd, Evas_Coord x, Evas_Coord y) { if ((x == psd->px) && (y == psd->py)) return; psd->px = x; psd->py = y; _elm_pan_update(psd); efl_event_callback_legacy_call(psd->self, ELM_PAN_EVENT_CHANGED, NULL); } EOLIAN static void _elm_pan_pos_get(const Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *psd, Evas_Coord *x, Evas_Coord *y) { if (x) *x = psd->px; if (y) *y = psd->py; } EOLIAN static void _elm_pan_pos_max_get(const Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *psd, Evas_Coord *x, Evas_Coord *y) { if (x) { if (psd->w < psd->content_w) *x = psd->content_w - psd->w; else *x = 0; } if (y) { if (psd->h < psd->content_h) *y = psd->content_h - psd->h; else *y = 0; } } EOLIAN static void _elm_pan_pos_min_get(const Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *_pd EINA_UNUSED, Evas_Coord *x, Evas_Coord *y) { if (x) *x = 0; if (y) *y = 0; } EOLIAN static void _elm_pan_content_size_get(const Eo *obj EINA_UNUSED, Elm_Pan_Smart_Data *psd, Evas_Coord *w, Evas_Coord *h) { if (w) *w = psd->content_w; if (h) *h = psd->content_h; } static Evas_Object * _elm_pan_add(Evas *evas) { return elm_legacy_add(MY_PAN_CLASS, evas); } EOLIAN static Eo * _elm_pan_efl_object_constructor(Eo *obj, Elm_Pan_Smart_Data *_pd EINA_UNUSED) { efl_canvas_group_clipped_set(obj, EINA_TRUE); obj = efl_constructor(efl_super(obj, MY_PAN_CLASS)); efl_canvas_object_type_set(obj, MY_PAN_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); return obj; } static void _elm_pan_content_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Pan_Smart_Data *psd; psd = data; psd->content = NULL; psd->content_w = psd->content_h = psd->px = psd->py = 0; efl_event_callback_legacy_call(psd->self, ELM_PAN_EVENT_CHANGED, NULL); } static void _elm_pan_content_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Pan_Smart_Data *psd; Evas_Coord w, h; psd = data; evas_object_geometry_get(psd->content, NULL, NULL, &w, &h); if ((w != psd->content_w) || (h != psd->content_h)) { psd->content_w = w; psd->content_h = h; _elm_pan_update(psd); } efl_event_callback_legacy_call(psd->self, ELM_PAN_EVENT_CHANGED, NULL); } static void _elm_pan_content_set(Evas_Object *obj, Evas_Object *content) { Evas_Coord w, h; ELM_PAN_DATA_GET_OR_RETURN(obj, psd); if (content == psd->content) return; if (psd->content) { evas_object_smart_member_del(psd->content); evas_object_event_callback_del_full (psd->content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd); evas_object_event_callback_del_full (psd->content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb, psd); psd->content = NULL; } if (!content) goto end; psd->content = content; evas_object_smart_member_add(psd->content, psd->self); evas_object_geometry_get(psd->content, NULL, NULL, &w, &h); psd->content_w = w; psd->content_h = h; evas_object_event_callback_add (content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd); evas_object_event_callback_add (content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb, psd); if (evas_object_visible_get(psd->self)) evas_object_show(psd->content); else evas_object_hide(psd->content); _elm_pan_update(psd); end: efl_event_callback_legacy_call(psd->self, ELM_PAN_EVENT_CHANGED, NULL); } EOLIAN static void _elm_pan_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_PAN_CLASS_NAME_LEGACY, klass); } /* pan smart object on top, scroller interface on bottom */ /* ============================================================ */ #define MY_SCROLLABLE_INTERFACE ELM_INTERFACE_SCROLLABLE_MIXIN #define MY_SCROLLABLE_INTERFACE_NAME "Elm_Interface_Scrollable" #define MY_SCROLLABLE_INTERFACE_NAME_LEGACY "elm_interface_scrollable" #define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(o, ptr) \ Elm_Scrollable_Smart_Interface_Data *ptr = \ (!efl_isa(o, MY_SCROLLABLE_INTERFACE) ? NULL : \ efl_data_scope_get(o, MY_SCROLLABLE_INTERFACE)); \ if (!ptr) \ { \ CRI("No interface data for object %p (%s)", \ o, evas_object_type_get(o)); \ return; \ } #define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ Elm_Scrollable_Smart_Interface_Data *ptr = \ (!efl_isa(o, MY_SCROLLABLE_INTERFACE) ? NULL : \ efl_data_scope_get(o, MY_SCROLLABLE_INTERFACE)); \ if (!ptr) \ { \ CRI("No interface data for object %p (%s)", \ o, evas_object_type_get(o)); \ return val; \ } static void _elm_scroll_scroll_bar_size_adjust( Elm_Scrollable_Smart_Interface_Data *); static void _elm_scroll_wanted_region_set(Evas_Object *); static Eina_Bool _paging_is_enabled(Elm_Scrollable_Smart_Interface_Data *sid); static Evas_Coord _elm_scroll_page_x_get( Elm_Scrollable_Smart_Interface_Data *sid, int offset, Eina_Bool limit); static Evas_Coord _elm_scroll_page_y_get( Elm_Scrollable_Smart_Interface_Data *sid, int offset, Eina_Bool limit); #define LEFT 0 #define RIGHT 1 #define UP 2 #define DOWN 3 //#define SCROLLDBG 1 /* smoothness debug calls - for debugging how much smooth your app is */ #define SMOOTHDBG 1 #ifdef SMOOTHDBG #define SMOOTH_DEBUG_COUNT 100 #define FPS 1 / 60 typedef struct _smooth_debug_info smooth_debug_info; struct _smooth_debug_info { double t; double dt; Evas_Coord pos; Evas_Coord dpos; double vpos; }; static smooth_debug_info smooth_x_history[SMOOTH_DEBUG_COUNT]; static smooth_debug_info smooth_y_history[SMOOTH_DEBUG_COUNT]; static int smooth_info_x_count = 0; static int smooth_info_y_count = 0; static double start_time = 0; static int _elm_scroll_smooth_debug = 0; void _elm_scroll_smooth_debug_init(void) { start_time = ecore_time_get(); smooth_info_x_count = 0; smooth_info_y_count = 0; memset(&(smooth_x_history[0]), 0, sizeof(smooth_x_history[0]) * SMOOTH_DEBUG_COUNT); memset(&(smooth_y_history[0]), 0, sizeof(smooth_y_history[0]) * SMOOTH_DEBUG_COUNT); return; } void _elm_scroll_smooth_debug_shutdown(void) { int i = 0; int info_x_count = 0; int info_y_count = 0; double x_ave = 0, y_ave = 0; double x_sum = 0, y_sum = 0; double x_dev = 0, y_dev = 0; double x_dev_sum = 0, y_dev_sum = 0; if (smooth_info_x_count >= SMOOTH_DEBUG_COUNT) info_x_count = SMOOTH_DEBUG_COUNT; else info_x_count = smooth_info_x_count; if (smooth_info_y_count >= SMOOTH_DEBUG_COUNT) info_y_count = SMOOTH_DEBUG_COUNT; else info_y_count = smooth_info_y_count; DBG("\n\n<<< X-axis Smoothness >>>\n"); DBG("| Num | t(time) | dt | x | dx |vx(dx/1fps) |\n"); for (i = info_x_count - 1; i >= 0; i--) { DBG("| %4d | %1.6f | %1.6f | %4d | %4d | %9.3f |\n", info_x_count - i, smooth_x_history[i].t, smooth_x_history[i].dt, smooth_x_history[i].pos, smooth_x_history[i].dpos, smooth_x_history[i].vpos); if (i == info_x_count - 1) continue; x_sum += smooth_x_history[i].vpos; } x_ave = x_sum / (info_x_count - 1); for (i = 0; i < info_x_count - 1; i++) { x_dev_sum += (smooth_x_history[i].vpos - x_ave) * (smooth_x_history[i].vpos - x_ave); } x_dev = x_dev_sum / (info_x_count - 1); DBG(" Standard deviation of X-axis velocity: %9.3f\n", sqrt(x_dev)); DBG("\n\n<<< Y-axis Smoothness >>>\n"); DBG("| Num | t(time) | dt | y | dy |vy(dy/1fps) |\n"); for (i = info_y_count - 1; i >= 0; i--) { DBG("| %4d | %1.6f | %1.6f | %4d | %4d | %9.3f |\n", info_y_count - i, smooth_y_history[i].t, smooth_y_history[i].dt, smooth_y_history[i].pos, smooth_y_history[i].dpos, smooth_y_history[i].vpos); if (i == info_y_count - 1) continue; y_sum += smooth_y_history[i].vpos; } y_ave = y_sum / (info_y_count - 1); for (i = 0; i < info_y_count - 1; i++) { y_dev_sum += (smooth_y_history[i].vpos - y_ave) * (smooth_y_history[i].vpos - y_ave); } y_dev = y_dev_sum / (info_y_count - 1); DBG(" Standard deviation of Y-axis velocity: %9.3f\n", sqrt(y_dev)); } static void _elm_direction_arrows_eval(Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool rely_on_cache) { Eina_Bool go_left = EINA_TRUE, go_right = EINA_TRUE; Eina_Bool go_up = EINA_TRUE, go_down = EINA_TRUE; Evas_Coord x = 0, y = 0, mx = 0, my = 0, minx = 0, miny = 0; if (!sid->edje_obj || !sid->pan_obj) return; elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_get(sid->pan_obj, &x, &y); if (x <= minx) go_left = EINA_FALSE; if (x >= (mx + minx)) go_right = EINA_FALSE; if (y <= miny) go_up = EINA_FALSE; if (y >= (my + miny)) go_down = EINA_FALSE; if (sid->loop_v) { go_up = EINA_TRUE; go_down = EINA_TRUE; } if (sid->loop_h) { go_right = EINA_TRUE; go_left = EINA_TRUE; } if (!rely_on_cache || go_left != sid->go_left) { if (go_left) edje_object_signal_emit(sid->edje_obj, "elm,action,show,left", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,action,hide,left", "elm"); sid->go_left = go_left; } if (!rely_on_cache || go_right != sid->go_right) { if (go_right) edje_object_signal_emit(sid->edje_obj, "elm,action,show,right", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,action,hide,right", "elm"); sid->go_right= go_right; } if (!rely_on_cache ||go_up != sid->go_up) { if (go_up) edje_object_signal_emit(sid->edje_obj, "elm,action,show,up", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,action,hide,up", "elm"); sid->go_up = go_up; } if (!rely_on_cache ||go_down != sid->go_down) { if (go_down) edje_object_signal_emit(sid->edje_obj, "elm,action,show,down", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,action,hide,down", "elm"); sid->go_down= go_down; } } void _elm_scroll_smooth_debug_movetime_add(int x, int y) { double tim = 0; static int bx = 0; static int by = 0; tim = ecore_time_get(); if (bx != x) { smooth_info_x_count++; memmove(&(smooth_x_history[1]), &(smooth_x_history[0]), sizeof(smooth_x_history[0]) * (SMOOTH_DEBUG_COUNT - 1)); smooth_x_history[0].t = tim - start_time; smooth_x_history[0].dt = smooth_x_history[0].t - smooth_x_history[1].t; smooth_x_history[0].pos = x; smooth_x_history[0].dpos = smooth_x_history[0].pos - smooth_x_history[1].pos; if (smooth_x_history[0].dpos >= 0) smooth_x_history[0].vpos = (double)(smooth_x_history[0].dpos) / smooth_x_history[0].dt * FPS; else smooth_x_history[0].vpos = -((double)(smooth_x_history[0].dpos) / smooth_x_history[0].dt * FPS); } if (by != y) { smooth_info_y_count++; memmove(&(smooth_y_history[1]), &(smooth_y_history[0]), sizeof(smooth_y_history[0]) * (SMOOTH_DEBUG_COUNT - 1)); smooth_y_history[0].t = tim - start_time; smooth_y_history[0].dt = smooth_y_history[0].t - smooth_y_history[1].t; smooth_y_history[0].pos = y; smooth_y_history[0].dpos = smooth_y_history[0].pos - smooth_y_history[1].pos; if (smooth_y_history[0].dpos >= 0) smooth_y_history[0].vpos = (double)(smooth_y_history[0].dpos) / smooth_y_history[0].dt * FPS; else smooth_y_history[0].vpos = -((double)(smooth_y_history[0].dpos) / smooth_y_history[0].dt * FPS); } bx = x; by = y; } #endif static void _elm_scroll_scroll_bar_h_visibility_apply(Elm_Scrollable_Smart_Interface_Data *sid) { if (sid->hbar_flags != ELM_SCROLLER_POLICY_OFF) { if (sid->hbar_visible) edje_object_signal_emit (sid->edje_obj, "elm,action,show,hbar", "elm"); else edje_object_signal_emit (sid->edje_obj, "elm,action,hide,hbar", "elm"); } else edje_object_signal_emit(sid->edje_obj, "elm,action,hide,hbar", "elm"); edje_object_message_signal_process(sid->edje_obj); _elm_scroll_scroll_bar_size_adjust(sid); _elm_direction_arrows_eval(sid, EINA_FALSE); if (sid->cb_func.content_min_limit) sid->cb_func.content_min_limit(sid->obj, sid->min_w, sid->min_h); } static void _elm_scroll_scroll_bar_v_visibility_apply(Elm_Scrollable_Smart_Interface_Data *sid) { if (sid->vbar_flags != ELM_SCROLLER_POLICY_OFF) { if (sid->vbar_visible) edje_object_signal_emit (sid->edje_obj, "elm,action,show,vbar", "elm"); else edje_object_signal_emit (sid->edje_obj, "elm,action,hide,vbar", "elm"); } else edje_object_signal_emit (sid->edje_obj, "elm,action,hide,vbar", "elm"); edje_object_message_signal_process(sid->edje_obj); _elm_scroll_scroll_bar_size_adjust(sid); _elm_direction_arrows_eval(sid, EINA_FALSE); if (sid->cb_func.content_min_limit) sid->cb_func.content_min_limit(sid->obj, sid->min_w, sid->min_h); } static int _elm_scroll_scroll_bar_h_visibility_adjust( Elm_Scrollable_Smart_Interface_Data *sid) { int scroll_h_vis_change = 0; Evas_Coord w, vw = 0, vh = 0; if (!sid->edje_obj) return 0; w = sid->content_info.w; if (sid->pan_obj) evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh); if (sid->hbar_visible) { if (sid->min_w) { scroll_h_vis_change = 1; sid->hbar_visible = EINA_FALSE; } else { if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO) { if ((sid->content) || (sid->extern_pan)) { if (w <= vw) { scroll_h_vis_change = 1; sid->hbar_visible = EINA_FALSE; } } else { scroll_h_vis_change = 1; sid->hbar_visible = EINA_FALSE; } } else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF) { scroll_h_vis_change = 1; sid->hbar_visible = EINA_FALSE; } } } else { if (!sid->min_w) { if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO) { if ((sid->content) || (sid->extern_pan)) { if (w > vw) { scroll_h_vis_change = 1; sid->hbar_visible = EINA_TRUE; } } } else if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON) { scroll_h_vis_change = 1; sid->hbar_visible = EINA_TRUE; } } } if (scroll_h_vis_change) _elm_scroll_scroll_bar_h_visibility_apply(sid); _elm_direction_arrows_eval(sid, EINA_TRUE); return scroll_h_vis_change; } static int _elm_scroll_scroll_bar_v_visibility_adjust( Elm_Scrollable_Smart_Interface_Data *sid) { int scroll_v_vis_change = 0; Evas_Coord h, vw = 0, vh = 0; if (!sid->edje_obj) return 0; h = sid->content_info.h; if (sid->pan_obj) evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh); if (sid->vbar_visible) { if (sid->min_h) { scroll_v_vis_change = 1; sid->vbar_visible = EINA_FALSE; } else { if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO) { if ((sid->content) || (sid->extern_pan)) { if (h <= vh) { scroll_v_vis_change = 1; sid->vbar_visible = EINA_FALSE; } } else { scroll_v_vis_change = 1; sid->vbar_visible = EINA_FALSE; } } else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF) { scroll_v_vis_change = 1; sid->vbar_visible = EINA_FALSE; } } } else { if (!sid->min_h) { if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO) { if ((sid->content) || (sid->extern_pan)) { if (h > vh) { scroll_v_vis_change = 1; sid->vbar_visible = EINA_TRUE; } } } else if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON) { scroll_v_vis_change = 1; sid->vbar_visible = EINA_TRUE; } } } if (scroll_v_vis_change) _elm_scroll_scroll_bar_v_visibility_apply(sid); _elm_direction_arrows_eval(sid, EINA_TRUE); return scroll_v_vis_change; } static inline void _elm_scroll_scroll_bar_auto_visibility_adjust(Elm_Scrollable_Smart_Interface_Data *sid) { Eina_Size2D sz; int w, h; if ((sid->vbar_flags != ELM_SCROLLER_POLICY_AUTO) || (sid->hbar_flags != ELM_SCROLLER_POLICY_AUTO) || !sid->hbar_visible || !sid->vbar_visible) return; if (!sid->content && !sid->extern_pan) return; w = sid->content_info.w; h = sid->content_info.h; sz = efl_gfx_entity_size_get(sid->edje_obj); // Adjust when the content may fit but the bars are visible. The if() test // does not guarantee that the content will fit (offsets & margins depend // on the theme). if ((w <= sz.w) && (h <= sz.h)) { sid->hbar_visible = EINA_FALSE; sid->vbar_visible = EINA_FALSE; _elm_scroll_scroll_bar_h_visibility_apply(sid); _elm_scroll_scroll_bar_v_visibility_apply(sid); _elm_scroll_scroll_bar_h_visibility_adjust(sid); _elm_scroll_scroll_bar_v_visibility_adjust(sid); } } static void _elm_scroll_scroll_bar_visibility_adjust( Elm_Scrollable_Smart_Interface_Data *sid) { int changed = 0; changed |= _elm_scroll_scroll_bar_h_visibility_adjust(sid); changed |= _elm_scroll_scroll_bar_v_visibility_adjust(sid); if (changed) { _elm_scroll_scroll_bar_h_visibility_adjust(sid); _elm_scroll_scroll_bar_v_visibility_adjust(sid); } _elm_scroll_scroll_bar_auto_visibility_adjust(sid); } static inline EINA_PURE Eina_Bool _elm_scroll_has_bars(const Elm_Scrollable_Smart_Interface_Data *sid) { const char *iface_scr_dragable_hbar = NULL; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) { iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; } else { iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; } return edje_object_part_exists(sid->edje_obj, iface_scr_dragable_hbar) || edje_object_part_exists(sid->edje_obj, iface_scr_dragable_vbar); } static void _elm_scroll_scroll_bar_size_adjust(Elm_Scrollable_Smart_Interface_Data *sid) { if (!sid->pan_obj || !sid->edje_obj) return; if (efl_invalidated_get(sid->pan_obj) || efl_invalidated_get(sid->edje_obj)) return; if (sid->size_adjust_recurse_abort) return; if (sid->size_adjust_recurse > 20) { sid->size_adjust_recurse_abort = EINA_TRUE; return; } sid->size_adjust_recurse++; const char *iface_scr_dragable_hbar = NULL; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) { iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; } else { iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; } if ((sid->content) || (sid->extern_pan)) { Evas_Coord x, y, w, h, mx = 0, my = 0, vw = 0, vh = 0, px = 0, py = 0, minx = 0, miny = 0; double vx = 0.0, vy = 0.0, size; edje_object_calc_force(sid->edje_obj); if (elm_widget_is_legacy(sid->obj)) { edje_object_part_geometry_get (sid->edje_obj, "elm.swallow.content", NULL, NULL, &vw, &vh); } else { edje_object_part_geometry_get (sid->edje_obj, "efl.content", NULL, NULL, &vw, &vh); } if (!_elm_scroll_has_bars(sid)) goto skip_bars; w = sid->content_info.w; if (w < 1) w = 1; size = (double)vw / (double)w; if (size > 1.0) { size = 1.0; edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_hbar, 0.0, 0.0); } edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_hbar, size, 1.0); h = sid->content_info.h; if (h < 1) h = 1; size = (double)vh / (double)h; if (size > 1.0) { size = 1.0; edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, 0.0); } edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_vbar, 1.0, size); edje_object_part_drag_value_get (sid->edje_obj, iface_scr_dragable_hbar, &vx, NULL); edje_object_part_drag_value_get (sid->edje_obj, iface_scr_dragable_vbar, NULL, &vy); elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); x = vx * mx + minx; y = vy * my + miny; edje_object_part_drag_step_set (sid->edje_obj, iface_scr_dragable_hbar, (double)sid->step.x / (double)w, 0.0); edje_object_part_drag_step_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, (double)sid->step.y / (double)h); if (sid->page.x > 0) edje_object_part_drag_page_set (sid->edje_obj, iface_scr_dragable_hbar, (double)sid->page.x / (double)w, 0.0); else edje_object_part_drag_page_set (sid->edje_obj, iface_scr_dragable_hbar, -((double)sid->page.x * ((double)vw / (double)w)) / 100.0, 0.0); if (sid->page.y > 0) edje_object_part_drag_page_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, (double)sid->page.y / (double)h); else edje_object_part_drag_page_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, -((double)sid->page.y * ((double)vh / (double)h)) / 100.0); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); if (vx != mx) x = px; if (vy != my) y = py; elm_obj_pan_pos_set(sid->pan_obj, x, y); if (mx > 0) vx = (double)(x - minx) / (double)mx; else vx = 0.0; if (vx < 0.0) vx = 0.0; else if (vx > 1.0) vx = 1.0; if (my > 0) vy = (double)(y - miny) / (double)my; else vy = 0.0; if (vy < 0.0) vy = 0.0; else if (vy > 1.0) vy = 1.0; edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, vy); edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_hbar, vx, 0.0); } else { Evas_Coord px = 0, py = 0, minx = 0, miny = 0; if (_elm_scroll_has_bars(sid)) { edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_vbar, 1.0, 1.0); edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_hbar, 1.0, 1.0); } elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); elm_obj_pan_pos_set(sid->pan_obj, minx, miny); if ((px != minx) || (py != miny)) edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); } skip_bars: _elm_scroll_scroll_bar_visibility_adjust(sid); sid->size_adjust_recurse--; if (sid->size_adjust_recurse <= 0) { sid->size_adjust_recurse = 0; sid->size_adjust_recurse_abort = EINA_FALSE; } } static void _elm_scroll_scroll_bar_read_and_update( Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord x, y, mx = 0, my = 0, minx = 0, miny = 0; double vx, vy; if (!sid->edje_obj || !sid->pan_obj) return; if ((sid->down.dragged) || (sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || (sid->down.momentum_animator) || (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) return; if (!_elm_scroll_has_bars(sid)) return; const char *iface_scr_dragable_hbar = NULL; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) { iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; } else { iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; } edje_object_part_drag_value_get (sid->edje_obj, iface_scr_dragable_vbar, NULL, &vy); edje_object_part_drag_value_get (sid->edje_obj, iface_scr_dragable_hbar, &vx, NULL); elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); x = _round(vx * (double)mx + minx, 1); y = _round(vy * (double)my + miny, 1); elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); _elm_scroll_wanted_coordinates_update(sid, x, y); } static void _elm_scroll_drag_start(Elm_Scrollable_Smart_Interface_Data *sid) { sid->current_page.x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); sid->current_page.y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); if (sid->cb_func.drag_start) sid->cb_func.drag_start(sid->obj, NULL); } static void _elm_scroll_drag_stop(Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord x, y; if (!(sid->down.bounce_x_animator) && !(sid->down.bounce_y_animator) && !(sid->scrollto.x.animator) && !(sid->scrollto.y.animator)) { x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); if (sid->cb_func.page_change && ((x != sid->current_page.x) || (y != sid->current_page.y))) sid->cb_func.page_change(sid->obj, NULL); sid->current_page.x = x; sid->current_page.y = y; } if (sid->cb_func.drag_stop) sid->cb_func.drag_stop(sid->obj, NULL); } static void _elm_scroll_anim_start(Elm_Scrollable_Smart_Interface_Data *sid) { sid->current_page.x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); sid->current_page.y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); if (sid->cb_func.animate_start) sid->cb_func.animate_start(sid->obj, NULL); } static void _elm_scroll_anim_stop(Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord x, y; if (sid->cb_func.page_change) { x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); if ((x != sid->current_page.x) || (y != sid->current_page.y)) sid->cb_func.page_change(sid->obj, NULL); sid->current_page.x = x; sid->current_page.y = y; } if (sid->cb_func.animate_stop) sid->cb_func.animate_stop(sid->obj, NULL); } static void _elm_scroll_policy_signal_emit(Elm_Scrollable_Smart_Interface_Data *sid) { if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON) edje_object_signal_emit (sid->edje_obj, "elm,action,show_always,hbar", "elm"); else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF) edje_object_signal_emit (sid->edje_obj, "elm,action,hide,hbar", "elm"); else edje_object_signal_emit (sid->edje_obj, "elm,action,show_notalways,hbar", "elm"); if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON) edje_object_signal_emit (sid->edje_obj, "elm,action,show_always,vbar", "elm"); else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF) edje_object_signal_emit (sid->edje_obj, "elm,action,hide,vbar", "elm"); else edje_object_signal_emit (sid->edje_obj, "elm,action,show_notalways,vbar", "elm"); edje_object_message_signal_process(sid->edje_obj); _elm_scroll_scroll_bar_size_adjust(sid); _elm_direction_arrows_eval(sid, EINA_FALSE); } static void _elm_scroll_reload_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_policy_signal_emit(sid); _elm_scroll_scroll_bar_h_visibility_apply(sid); _elm_scroll_scroll_bar_v_visibility_apply(sid); } static void _elm_scroll_vbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.vbar_drag) sid->cb_func.vbar_drag(sid->obj, NULL); _elm_scroll_scroll_bar_read_and_update(sid); } static void _elm_scroll_vbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.vbar_press) sid->cb_func.vbar_press(sid->obj, NULL); } static void _elm_scroll_vbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.vbar_unpress) sid->cb_func.vbar_unpress(sid->obj, NULL); } static void _elm_scroll_edje_drag_v_start_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); _elm_scroll_drag_start(sid); sid->freeze = EINA_TRUE; } static void _elm_scroll_edje_drag_v_stop_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); _elm_scroll_drag_stop(sid); sid->freeze = sid->freeze_want; } static void _elm_scroll_edje_drag_v_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); } static void _elm_scroll_hbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.hbar_drag) sid->cb_func.hbar_drag(sid->obj, NULL); _elm_scroll_scroll_bar_read_and_update(sid); } static void _elm_scroll_hbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.hbar_press) sid->cb_func.hbar_press(sid->obj, NULL); } static void _elm_scroll_hbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.hbar_unpress) sid->cb_func.hbar_unpress(sid->obj, NULL); } static void _elm_scroll_edje_drag_h_start_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); _elm_scroll_drag_start(sid); sid->freeze = EINA_TRUE; } static void _elm_scroll_edje_drag_h_stop_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); _elm_scroll_drag_stop(sid); sid->freeze = sid->freeze_want; } static void _elm_scroll_edje_drag_h_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; _elm_scroll_scroll_bar_read_and_update(sid); } EOLIAN static void _elm_interface_scrollable_content_size_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *w, Evas_Coord *h) { elm_obj_pan_content_size_get(sid->pan_obj, w, h); } EOLIAN static void _elm_interface_scrollable_content_viewport_geometry_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h) { if (!sid->pan_obj || !sid->edje_obj) return; edje_object_calc_force(sid->edje_obj); evas_object_geometry_get(sid->pan_obj, x, y, w, h); } EOLIAN static void _elm_interface_scrollable_content_min_limit(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool w, Eina_Bool h) { if (!sid->edje_obj) return; if (!sid->cb_func.content_min_limit) { ERR("Content minimim size limiting is unimplemented -- you " "must provide it yourself\n"); return; } sid->min_w = !!w; sid->min_h = !!h; sid->cb_func.content_min_limit(sid->obj, w, h); } static Evas_Coord _elm_scroll_x_mirrored_get(const Evas_Object *obj, Evas_Coord x) { Evas_Coord cw = 0, w = 0, min = 0, ret; ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, x); if (!sid->pan_obj) return 0; elm_obj_pan_pos_min_get(sid->pan_obj, &min, NULL); elm_interface_scrollable_content_viewport_geometry_get ((Eo *)obj, NULL, NULL, &w, NULL); elm_obj_pan_content_size_get(sid->pan_obj, &cw, NULL); ret = cw - w - x + min + min; return (ret >= min) ? ret : min; } /* Update the wanted coordinates according to the x, y passed * widget directionality, content size and etc. */ static void _elm_scroll_wanted_coordinates_update(Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y) { Evas_Coord mx = 0, my = 0, minx = 0, miny = 0; if (!sid->pan_obj) return; elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); /* Update wx/y/w/h - and if the requested positions aren't legal * adjust a bit. */ elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &sid->ww, &sid->wh); if (x < minx && !sid->is_mirrored) { if (!sid->loop_h) sid->wx = minx; else sid->wx = mx; } else if (sid->is_mirrored) sid->wx = _elm_scroll_x_mirrored_get(sid->obj, x); else if (!sid->loop_h && (x > mx)) sid->wx = mx; else if (sid->loop_h && x >= (sid->ww + mx)) sid->wx = minx; else sid->wx = x; if (y < miny) { if (!sid->loop_v) sid->wy = miny; else sid->wy = my; } else if (!sid->loop_v && (y > my)) sid->wy = my; else if (sid->loop_v && y >= (sid->wh + my)) sid->wy = miny; else sid->wy = y; } static void _elm_scroll_momentum_end(Elm_Scrollable_Smart_Interface_Data *sid) { if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator)) return; if (sid->down.momentum_animator) { Evas_Coord px = 0, py = 0; elm_interface_scrollable_content_pos_get(sid->obj, &px, &py); _elm_scroll_wanted_coordinates_update(sid, px, py); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); sid->down.bounce_x_hold = EINA_FALSE; sid->down.bounce_y_hold = EINA_FALSE; sid->down.ax = 0; sid->down.ay = 0; sid->down.dx = 0; sid->down.dy = 0; sid->down.pdx = 0; sid->down.pdy = 0; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } } static void _elm_scroll_bounce_x_animator(void *data, const Efl_Event *event EINA_UNUSED) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(data, sid); Evas_Coord x, y, dx, w, odx, ed, md; double t, p, dt, pd, r; t = ecore_loop_time_get(); dt = t - sid->down.anim_start2; if (dt >= 0.0) { dt = dt / _elm_config->thumbscroll_bounce_friction; odx = sid->down.b2x - sid->down.bx; elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, NULL); if (!sid->down.momentum_animator && (w > abs(odx))) { pd = (double)odx / (double)w; pd = (pd > 0) ? pd : -pd; pd = 1.0 - ((1.0 - pd) * (1.0 - pd)); dt = dt / pd; } if (dt > 1.0) dt = 1.0; p = 1.0 - ((1.0 - dt) * (1.0 - dt)); elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); dx = (odx * p); r = 1.0; if (sid->down.momentum_animator) { ed = abs((int)(sid->down.dx * _elm_config->thumbscroll_momentum_friction - sid->down.b0x)); md = abs((int)(_elm_config->thumbscroll_momentum_friction * 5 * w)); if (ed > md) r = (double)(md) / (double)ed; } x = sid->down.b2x + (int)((double)(dx - odx) * r); if (!sid->down.cancelled) elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); if (dt >= 1.0) { if (sid->down.momentum_animator) sid->down.bounce_x_hold = EINA_TRUE; if ((!sid->down.bounce_y_animator) && (!sid->scrollto.y.animator)) _elm_scroll_anim_stop(sid); sid->down.pdx = 0; sid->bouncemex = EINA_FALSE; _elm_scroll_momentum_end(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); ELM_ANIMATOR_CONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); } } } static void _elm_scroll_bounce_y_animator(void *data, const Efl_Event *event EINA_UNUSED) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(data, sid); Evas_Coord x, y, dy, h, ody, ed, md; double t, p, dt, pd, r; t = ecore_loop_time_get(); dt = t - sid->down.anim_start3; if (dt >= 0.0) { dt = dt / _elm_config->thumbscroll_bounce_friction; ody = sid->down.b2y - sid->down.by; elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, NULL, &h); if (!sid->down.momentum_animator && (h > abs(ody))) { pd = (double)ody / (double)h; pd = (pd > 0) ? pd : -pd; pd = 1.0 - ((1.0 - pd) * (1.0 - pd)); dt = dt / pd; } if (dt > 1.0) dt = 1.0; p = 1.0 - ((1.0 - dt) * (1.0 - dt)); elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); dy = (ody * p); r = 1.0; if (sid->down.momentum_animator) { ed = abs((int)(sid->down.dy * _elm_config->thumbscroll_momentum_friction - sid->down.b0y)); md = abs((int)(_elm_config->thumbscroll_momentum_friction * 5 * h)); if (ed > md) r = (double)(md) / (double)ed; } y = sid->down.b2y + (int)((double)(dy - ody) * r); if (!sid->down.cancelled) elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); if (dt >= 1.0) { if (sid->down.momentum_animator) sid->down.bounce_y_hold = EINA_TRUE; if ((!sid->down.bounce_x_animator) && (!sid->scrollto.y.animator)) _elm_scroll_anim_stop(sid); sid->down.pdy = 0; sid->bouncemey = EINA_FALSE; _elm_scroll_momentum_end(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); } } } static void _elm_scroll_bounce_eval(Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord mx = 0, my = 0, px = 0, py = 0, bx, by, b2x, b2y, minx = 0, miny = 0; if (!sid->pan_obj) return; if (sid->freeze) return; if ((!sid->bouncemex) && (!sid->bouncemey)) return; if (sid->down.now) return; // down bounce while still held down if (sid->down.onhold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (sid->down.hold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del); elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); bx = px; by = py; if (px < minx) px = minx; if ((px - minx) > mx) px = mx + minx; if (py < miny) py = miny; if ((py - miny) > my) py = my + miny; b2x = px; b2y = py; if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get(sid->obj))) { if ((!sid->down.bounce_x_animator) && (!sid->bounce_animator_disabled)) { if (sid->bouncemex) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_CONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); sid->down.anim_start2 = ecore_loop_time_get(); sid->down.bx = bx; sid->down.bx0 = bx; sid->down.b2x = b2x; if (sid->down.momentum_animator) sid->down.b0x = sid->down.ax; else sid->down.b0x = 0; } } } if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get(sid->obj))) { if ((!sid->down.bounce_y_animator) && (!sid->bounce_animator_disabled)) { if (sid->bouncemey) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); ELM_ANIMATOR_CONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); sid->down.anim_start3 = ecore_loop_time_get(); sid->down.by = by; sid->down.by0 = by; sid->down.b2y = b2y; if (sid->down.momentum_animator) sid->down.b0y = sid->down.ay; else sid->down.b0y = 0; } } } } EOLIAN static void _elm_interface_scrollable_content_pos_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y) { if (!sid->pan_obj) return; elm_obj_pan_pos_get(sid->pan_obj, x, y); } EOLIAN static void _elm_interface_scrollable_content_pos_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y, Eina_Bool sig) { Evas_Coord mx = 0, my = 0, px = 0, py = 0, spx = 0, spy = 0, minx = 0, miny = 0; Evas_Coord cw = 0, ch = 0, pw = 0, ph = 0; double vx, vy; if (!sid->edje_obj || !sid->pan_obj) return; // FIXME: allow for bounce outside of range elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); evas_object_geometry_get(sid->pan_obj, NULL, NULL, &pw, &ph); if (_paging_is_enabled(sid)) { if (sid->page_snap_horiz && !sid->loop_h) { //we passed one page to the right if (x > sid->current_page.x + sid->pagesize_h) x = sid->current_page.x + sid->pagesize_h; //we passed one page to the left if (x < sid->current_page.x - sid->pagesize_h) x = sid->current_page.x - sid->pagesize_h; } if (sid->page_snap_vert && !sid->loop_v) { //we passed one page to the bottom if (y > sid->current_page.y + sid->pagesize_v) y = sid->current_page.y + sid->pagesize_v; //we passed one page to the top if (y < sid->current_page.y - sid->pagesize_v) y = sid->current_page.y - sid->pagesize_v; } } if (sid->loop_h && cw > 0) { if (x < 0) x = cw + (x % cw); else if (x >= cw) x = (x % cw); } if (sid->loop_v && ch > 0) { if (y < 0) y = ch + (y % ch); else if (y >= ch) y = (y % ch); } if (!_elm_config->thumbscroll_bounce_enable) { if (x < minx) x = minx; if (!sid->loop_h && (x - minx) > mx) x = mx + minx; if (y < miny) y = miny; if (!sid->loop_v && (y - miny) > my) y = my + miny; } if (!sid->bounce_horiz) { if (x < minx) x = minx; if (!sid->loop_h && (x - minx) > mx) x = mx + minx; } if (!sid->bounce_vert) { if (y < miny) y = miny; if (!sid->loop_v && (y - miny) > my) y = my + miny; } elm_obj_pan_pos_set(sid->pan_obj, x, y); elm_obj_pan_pos_get(sid->pan_obj, &spx, &spy); if (mx > 0) vx = (double)(spx - minx) / (double)mx; else vx = 0.0; if (vx < 0.0) vx = 0.0; else if (vx > 1.0) vx = 1.0; if (my > 0) vy = (double)(spy - miny) / (double)my; else vy = 0.0; if (vy < 0.0) vy = 0.0; else if (vy > 1.0) vy = 1.0; const char *iface_scr_dragable_hbar = NULL; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) { iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; } else { iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; } if (_elm_scroll_has_bars(sid)) { edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, vy); edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_hbar, vx, 0.0); } if (!sid->loop_h && !sid->down.bounce_x_animator) { if (((x < minx) && (0 <= sid->down.dx)) || ((x > (mx + minx)) && (0 >= sid->down.dx))) { sid->bouncemex = EINA_TRUE; _elm_scroll_bounce_eval(sid); } else sid->bouncemex = EINA_FALSE; } if (!sid->loop_v && !sid->down.bounce_y_animator) { if (((y < miny) && (0 <= sid->down.dy)) || ((y > (my + miny)) && (0 >= sid->down.dy))) { sid->bouncemey = EINA_TRUE; _elm_scroll_bounce_eval(sid); } else sid->bouncemey = EINA_FALSE; } if (sig) { if ((x != px) || (y != py)) { if (sid->cb_func.scroll) sid->cb_func.scroll(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); if (x < px) { if (sid->cb_func.scroll_left) sid->cb_func.scroll_left(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,action,scroll,left", "elm"); } if (x > px) { if (sid->cb_func.scroll_right) sid->cb_func.scroll_right(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,action,scroll,right", "elm"); } if (y < py) { if (sid->cb_func.scroll_up) sid->cb_func.scroll_up(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,action,scroll,up", "elm"); } if (y > py) { if (sid->cb_func.scroll_down) sid->cb_func.scroll_down(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,action,scroll,down", "elm"); } } if (x != px) { if (x == minx) { if (sid->cb_func.edge_left) sid->cb_func.edge_left(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,edge,left", "elm"); } if (x == (mx + minx)) { if (sid->cb_func.edge_right) sid->cb_func.edge_right(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,edge,right", "elm"); } } if (y != py) { if (y == miny) { if (sid->cb_func.edge_top) sid->cb_func.edge_top(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,edge,top", "elm"); } if (y == my + miny) { if (sid->cb_func.edge_bottom) sid->cb_func.edge_bottom(obj, NULL); edje_object_signal_emit(sid->edje_obj, "elm,edge,bottom", "elm"); } } } _elm_direction_arrows_eval(sid, EINA_TRUE); } EOLIAN static void _elm_interface_scrollable_efl_ui_i18n_mirrored_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool mirrored) { Evas_Coord wx; if (!sid->edje_obj) return; mirrored = !!mirrored; if (sid->is_mirrored == mirrored) return; sid->is_mirrored = mirrored; edje_object_mirrored_set(sid->edje_obj, mirrored); if (sid->is_mirrored) wx = _elm_scroll_x_mirrored_get(sid->obj, sid->wx); else wx = sid->wx; elm_interface_scrollable_content_pos_set(sid->obj, wx, sid->wy, EINA_FALSE); efl_ui_mirrored_set(efl_super(obj, ELM_INTERFACE_SCROLLABLE_MIXIN), mirrored); } /* returns TRUE when we need to move the scroller, FALSE otherwise. * Updates w and h either way, so save them if you need them. */ static Eina_Bool _elm_scroll_content_region_show_internal(Evas_Object *obj, Evas_Coord *_x, Evas_Coord *_y, Evas_Coord w, Evas_Coord h) { Evas_Coord cw = 0, ch = 0, px = 0, py = 0, nx, ny, minx = 0, miny = 0, pw = 0, ph = 0, x = *_x, y = *_y; ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE); if (!sid->pan_obj) return EINA_FALSE; elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); evas_object_geometry_get(sid->pan_obj, NULL, NULL, &pw, &ph); nx = x; if ((x > px) && (w < pw)) { if ((px + pw) < (x + w)) nx = x - pw + w; else nx = px; } ny = y; if ((y > py) && (h < ph)) { if ((py + ph) < (y + h)) ny = y - ph + h; else ny = py; } if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); if (sid->down.bounce_x_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); sid->bouncemex = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (sid->down.bounce_y_animator) { ELM_ANIMATOR_CONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); sid->bouncemey = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } _elm_scroll_anim_stop(sid); } if (sid->down.hold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); _elm_scroll_drag_stop(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del); if (sid->down.momentum_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); sid->down.bounce_x_hold = EINA_FALSE; sid->down.bounce_y_hold = EINA_FALSE; sid->down.ax = 0; sid->down.ay = 0; sid->down.pdx = 0; sid->down.pdy = 0; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (_paging_is_enabled(sid)) { x = _elm_scroll_page_x_get(sid, nx - px, EINA_FALSE); y = _elm_scroll_page_y_get(sid, ny - py, EINA_FALSE); } else { x = nx; y = ny; } if (!sid->loop_h) { if ((x + pw) > cw) x = cw - pw; if (x < minx) x = minx; } if (!sid->loop_v) { if ((y + ph) > ch) y = ch - ph; if (y < miny) y = miny; } if ((x == px) && (y == py)) return EINA_FALSE; *_x = x; *_y = y; return EINA_TRUE; } EOLIAN static void _elm_interface_scrollable_content_region_get(const Eo *obj, Elm_Scrollable_Smart_Interface_Data *_pd EINA_UNUSED, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h) { elm_interface_scrollable_content_pos_get(obj, x, y); elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, w, h); } /* Set should be used for calculated positions, for example, when we move * because of an animation or because this is the correct position after * constraints. */ EOLIAN static void _elm_interface_scrollable_content_region_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) { elm_interface_scrollable_content_pos_set(obj, x, y, EINA_FALSE); sid->down.sx = x; sid->down.sy = y; sid->down.x = sid->down.history[0].x; sid->down.y = sid->down.history[0].y; } } /* Set should be used for setting the wanted position, for example a * user scroll or moving the cursor in an entry. */ EOLIAN static void _elm_interface_scrollable_content_region_show(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { sid->wx = (sid->is_mirrored ? _elm_scroll_x_mirrored_get(sid->obj, x) : x); sid->wy = y; sid->ww = w; sid->wh = h; if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) { elm_interface_scrollable_content_pos_set(obj, x, y, EINA_TRUE); sid->down.sx = x; sid->down.sy = y; sid->down.x = sid->down.history[0].x; sid->down.y = sid->down.history[0].y; } } static void _elm_scroll_wanted_region_set(Evas_Object *obj) { Evas_Coord ww, wh, wx; Evas_Coord mx = 0, my = 0; ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); wx = sid->wx; if (sid->down.now || sid->down.momentum_animator || sid->down.bounce_x_animator || sid->down.bounce_y_animator || sid->down.hold_animator || sid->down.onhold_animator || sid->scrollto.x.animator || sid->scrollto.y.animator) return; sid->content_info.resized = EINA_FALSE; if (!sid->pan_obj) return; /* Flip to RTL cords only if init in RTL mode */ if (sid->is_mirrored) wx = _elm_scroll_x_mirrored_get(obj, sid->wx); if (sid->ww == -1) { elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &ww, &wh); } else { ww = sid->ww; wh = sid->wh; } elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); wx += (mx - sid->prev_cw) * sid->gravity_x; sid->wy += (my - sid->prev_ch) * sid->gravity_y; sid->prev_cw = mx; sid->prev_ch = my; elm_interface_scrollable_content_region_set(obj, wx, sid->wy, ww, wh); } static Eina_Value _scroll_wheel_post_event_job(void *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; // Animations are disabled if we are here elm_interface_scrollable_content_pos_set(sid->obj, sid->wx, sid->wy, EINA_TRUE); if (_paging_is_enabled(sid)) { sid->current_page.x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); sid->current_page.y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); } return v; } static inline void _scroll_wheel_post_event_go(Elm_Scrollable_Smart_Interface_Data *sid, int x, int y) { if (sid->hold || sid->freeze) return; _elm_scroll_wanted_coordinates_update(sid, x, y); if (_elm_config->scroll_animation_disable) { Eina_Future *f; f = eina_future_then(efl_loop_job(efl_loop_get(sid->obj)), _scroll_wheel_post_event_job, sid, NULL); efl_future_then(sid->obj, f); } else { _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x); _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y); } } static Eina_Bool _scroll_wheel_post_event_cb(void *data, Evas *e EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Event_Mouse_Wheel *ev = sid->event_info; Evas_Coord x = 0, y = 0, vw = 0, vh = 0, cw = 0, ch = 0; int pagenumber_h = 0, pagenumber_v = 0; int mx = 0, my = 0, minx = 0, miny = 0; Eina_Bool hold = EINA_FALSE; Evas_Coord pwx, pwy; double t; int direction; EINA_SAFETY_ON_NULL_RETURN_VAL(ev, EINA_TRUE); sid->event_info = NULL; direction = ev->direction; pwx = sid->wx; pwy = sid->wy; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE; if (evas_key_modifier_is_set(ev->modifiers, "Shift")) direction = !direction; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); if (sid->scrollto.x.animator) x = sid->scrollto.x.end; if (sid->scrollto.y.animator) y = sid->scrollto.y.end; elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); if (x < minx) x = minx; if (x > mx) x = mx; if (y < miny) y = miny; if (y > my) y = my; t = ecore_loop_time_get(); if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) { _elm_scroll_anim_stop(sid); } ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); if (sid->down.bounce_x_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); sid->bouncemex = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (sid->down.bounce_y_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); sid->bouncemey = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &vw, &vh); if (sid->pan_obj) elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); if (!_paging_is_enabled(sid)) { int d = ev->z; double delta_t = (double)(ev->timestamp - sid->last_wheel) / 1000.0; double mul; if (delta_t > 0.2) sid->last_wheel_mul = 0.0; if (delta_t > 0.2) delta_t = 0.2; mul = 1.0 + (_elm_config->scroll_accel_factor * ((0.2 - delta_t) / 0.2)); mul = mul * (1.0 + (0.15 * sid->last_wheel_mul)); d *= mul; sid->last_wheel = ev->timestamp; sid->last_wheel_mul = mul; if (!direction) { if ((ch > vh) || (cw <= vw)) y += d * sid->step.y; else { x += d * sid->step.x; direction = 1; } } else { if ((cw > vw) || (ch <= vh)) x += d * sid->step.x; else { y += d * sid->step.y; direction = 0; } } _scroll_wheel_post_event_go(sid, x, y); } else { int wx = x, wy = y; elm_interface_scrollable_current_page_get(sid->obj, &pagenumber_h, &pagenumber_v); if (!direction) { if ((ch > vh) || (cw <= vw)) wy = (pagenumber_v + (1 * ev->z)) * sid->pagesize_v; else { wx = (pagenumber_h + (1 * ev->z)) * sid->pagesize_h; direction = 1; } } else { if ((cw > vw) || (ch <= vh)) wx = (pagenumber_h + (1 * ev->z)) * sid->pagesize_h; else { wy = (pagenumber_v + (1 * ev->z)) * sid->pagesize_v; direction = 0; } } // Snap to first or last page before looping if not smooth if (_elm_config->scroll_animation_disable) { if (direction && sid->loop_h) { if (sid->page_snap_horiz) { if ((x == mx) && (wx > mx)) wx = minx; else if ((x == minx) && (wx < minx)) wx = mx; } else { if ((x < mx) && (wx > mx)) wx = mx; else if ((x > minx) && (wx < minx)) wx = minx; } } if (!direction && sid->loop_v) { if (sid->page_snap_vert) { if ((y == my) && (wy > my)) wy = miny; else if ((y == miny) && (wy < miny)) wy = my; } else { if ((y < my) && (wy > my)) wy = my; else if ((y > miny) && (wy < miny)) wy = miny; } } } _scroll_wheel_post_event_go(sid, wx, wy); } if (direction) { if ((pwx != sid->wx) || (((t - sid->down.last_time_x_wheel) < 0.5) && (sid->down.last_hold_x_wheel))) { sid->down.last_hold_x_wheel = EINA_TRUE; hold = EINA_TRUE; } else sid->down.last_hold_x_wheel = EINA_FALSE; sid->down.last_time_x_wheel = t; } else { if ((pwy != sid->wy) || (((t - sid->down.last_time_y_wheel) < 0.5) && (sid->down.last_hold_y_wheel))) { sid->down.last_hold_y_wheel = EINA_TRUE; hold = EINA_TRUE; } else sid->down.last_hold_y_wheel = EINA_FALSE; sid->down.last_time_y_wheel = t; } return !hold; } static void _elm_scroll_wheel_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Elm_Scrollable_Smart_Interface_Data *sid; Evas_Event_Mouse_Wheel *ev; int direction; sid = data; ev = event_info; sid->event_info = event_info; direction = ev->direction; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; if ((evas_key_modifier_is_set(ev->modifiers, "Control")) || (evas_key_modifier_is_set(ev->modifiers, "Alt")) || (evas_key_modifier_is_set(ev->modifiers, "Meta")) || (evas_key_modifier_is_set(ev->modifiers, "Hyper")) || (evas_key_modifier_is_set(ev->modifiers, "Super"))) return; if (direction) { if (sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL) return; } else { if (sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) return; } evas_post_event_callback_push(e, _scroll_wheel_post_event_cb, sid); } static Eina_Bool _elm_scroll_post_event_up(void *data, Evas *e EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->obj) { elm_widget_scroll_lock_set(sid->obj, EFL_UI_LAYOUT_ORIENTATION_DEFAULT); } return EINA_TRUE; } static Eina_Bool _paging_is_enabled(Elm_Scrollable_Smart_Interface_Data *sid) { if ((sid->pagerel_h == 0.0) && (!sid->pagesize_h) && (sid->pagerel_v == 0.0) && (!sid->pagesize_v)) return EINA_FALSE; return EINA_TRUE; } static void _elm_scroll_momentum_animator(void *data, const Efl_Event *event EINA_UNUSED) { double t, dt, p; Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord x, y, dx, dy, px, py, maxx, maxy, minx, miny; Eina_Bool no_bounce_x_end = EINA_FALSE, no_bounce_y_end = EINA_FALSE; if (!sid->pan_obj) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); return; } t = ecore_loop_time_get(); if (sid->down.anim_dur == 0) dt = 1.0; else dt = (t - sid->down.anim_start) / sid->down.anim_dur; if (dt >= 0.0) { if (dt > 1.0) dt = 1.0; p = 1.0 - ((1.0 - dt) * (1.0 - dt)); dx = sid->down.dx * p; dy = sid->down.dy * p; sid->down.ax = dx; sid->down.ay = dy; x = sid->down.sx - dx; y = sid->down.sy - dy; elm_interface_scrollable_content_pos_get(sid->obj, &px, &py); if ((sid->down.bounce_x_animator) || (sid->down.bounce_x_hold)) { sid->down.bx = sid->down.bx0 - dx + sid->down.b0x; x = px; } if ((sid->down.bounce_y_animator) || (sid->down.bounce_y_hold)) { sid->down.by = sid->down.by0 - dy + sid->down.b0y; y = py; } elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); _elm_scroll_wanted_coordinates_update(sid, x, y); elm_obj_pan_pos_max_get(sid->pan_obj, &maxx, &maxy); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); if (!_elm_config->thumbscroll_bounce_enable || !sid->bounce_horiz) { if (x <= minx) no_bounce_x_end = EINA_TRUE; if (!sid->loop_h && (x - minx) >= maxx) no_bounce_x_end = EINA_TRUE; } if (!_elm_config->thumbscroll_bounce_enable || !sid->bounce_vert) { if (y <= miny) no_bounce_y_end = EINA_TRUE; if (!sid->loop_v && (y - miny) >= maxy) no_bounce_y_end = EINA_TRUE; } if ((dt >= 1.0) || ((sid->down.bounce_x_hold) && (sid->down.bounce_y_hold)) || (no_bounce_x_end && no_bounce_y_end)) { _elm_scroll_anim_stop(sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); sid->down.bounce_x_hold = EINA_FALSE; sid->down.bounce_y_hold = EINA_FALSE; sid->down.ax = 0; sid->down.ay = 0; sid->down.pdx = 0; sid->down.pdy = 0; sid->down.anim_dur = 0; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } } } static Evas_Coord _elm_scroll_page_x_get(Elm_Scrollable_Smart_Interface_Data *sid, int offset, Eina_Bool limit) { Evas_Coord x, y, w, h, dx, cw, ch, minx = 0; if (!sid->pan_obj) return 0; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, NULL); if (sid->pagerel_h > 0.0) sid->pagesize_h = w * sid->pagerel_h; if (!limit) x += offset; else { dx = (sid->pagesize_h * ((double)sid->page_limit_h - 0.5)); if (offset > 0) x += (abs(offset) < dx ? offset : dx); else x += (abs(offset) < dx ? offset : -(dx + 1)); } if (sid->is_mirrored) x += w; if (sid->pagesize_h > 0) { if (x >= 0) x = x + (sid->pagesize_h * 0.5); else if (x < 0 && sid->loop_h) x = x - (sid->pagesize_h * 0.5); x = x / (sid->pagesize_h); x = x * (sid->pagesize_h); } if (sid->is_mirrored) x -= w; if (!sid->loop_h) { if ((x + w) > cw) x = cw - w; if (x < minx) x = minx; } return x; } static Evas_Coord _elm_scroll_page_y_get(Elm_Scrollable_Smart_Interface_Data *sid, int offset, Eina_Bool limit) { Evas_Coord x, y, w = 0, h = 0, dy, cw = 0, ch = 0, miny = 0; if (!sid->pan_obj) return 0; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); elm_obj_pan_pos_min_get(sid->pan_obj, NULL, &miny); if (sid->pagerel_v > 0.0) sid->pagesize_v = h * sid->pagerel_v; if (!limit) y += offset; else { dy = (sid->pagesize_v * ((double)sid->page_limit_v - 0.5)); if (offset > 0) y += (abs(offset) < dy ? offset : dy); else y += (abs(offset) < dy ? offset : -(dy + 1)); } if (sid->pagesize_v > 0) { if (y >= 0) y = y + (sid->pagesize_v * 0.5); else if (y < 0 && sid->loop_v) y = y - (sid->pagesize_v * 0.5); y = y / (sid->pagesize_v); y = y * (sid->pagesize_v); } if (!sid->loop_v) { if ((y + h) > ch) y = ch - h; if (y < miny) y = miny; } return y; } static void _elm_scroll_scroll_to_x_animator(void *data, const Efl_Event *event EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord px, py; double t, tt; if (!sid->pan_obj) goto on_end; t = ecore_loop_time_get(); tt = (t - sid->scrollto.x.t_start) / (sid->scrollto.x.t_end - sid->scrollto.x.t_start); tt = 1.0 - tt; tt = 1.0 - (tt * tt); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); px = (sid->scrollto.x.start * (1.0 - tt)) + (sid->scrollto.x.end * tt); if (t >= sid->scrollto.x.t_end) { px = sid->scrollto.x.end; elm_interface_scrollable_content_pos_set(sid->obj, px, py, EINA_TRUE); sid->down.sx = px; sid->down.x = sid->down.history[0].x; sid->down.pdx = 0; _elm_scroll_wanted_coordinates_update(sid, px, py); if ((!sid->scrollto.y.animator) && (!sid->down.bounce_y_animator)) _elm_scroll_anim_stop(sid); goto on_end; } elm_interface_scrollable_content_pos_set(sid->obj, px, py, EINA_TRUE); _elm_scroll_wanted_coordinates_update(sid, px, py); return; on_end: ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); } static void _elm_scroll_scroll_to_y_animator(void *data, const Efl_Event *event EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord px, py; double t, tt; if (!sid->pan_obj) goto on_end; t = ecore_loop_time_get(); tt = (t - sid->scrollto.y.t_start) / (sid->scrollto.y.t_end - sid->scrollto.y.t_start); tt = 1.0 - tt; tt = 1.0 - (tt * tt); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); py = (sid->scrollto.y.start * (1.0 - tt)) + (sid->scrollto.y.end * tt); if (t >= sid->scrollto.y.t_end) { py = sid->scrollto.y.end; elm_interface_scrollable_content_pos_set(sid->obj, px, py, EINA_TRUE); sid->down.sy = py; sid->down.y = sid->down.history[0].y; sid->down.pdy = 0; _elm_scroll_wanted_coordinates_update(sid, px, py); if ((!sid->scrollto.x.animator) && (!sid->down.bounce_x_animator)) _elm_scroll_anim_stop(sid); goto on_end; } elm_interface_scrollable_content_pos_set(sid->obj, px, py, EINA_TRUE); _elm_scroll_wanted_coordinates_update(sid, px, py); return; on_end: ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); } static void _elm_scroll_scroll_to_y(Elm_Scrollable_Smart_Interface_Data *sid, double t_in, Evas_Coord pos_y) { Evas_Coord px = 0, py = 0, x, y, w, h; double t; if (!sid->pan_obj) return; if (sid->freeze) return; if (t_in <= 0.0) { elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); y = pos_y; elm_interface_scrollable_content_region_set(sid->obj, x, y, w, h); return; } t = ecore_loop_time_get(); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); if (py == pos_y) return; sid->scrollto.y.start = py; sid->scrollto.y.end = pos_y; sid->scrollto.y.t_start = t; sid->scrollto.y.t_end = t + t_in; if (!sid->scrollto.y.animator) { ELM_ANIMATOR_CONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); if (!sid->scrollto.x.animator) _elm_scroll_anim_start(sid); } if (sid->down.bounce_y_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); _elm_scroll_momentum_end(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } sid->bouncemey = EINA_FALSE; } static void _elm_scroll_scroll_to_x(Elm_Scrollable_Smart_Interface_Data *sid, double t_in, Evas_Coord pos_x) { Evas_Coord px = 0, py = 0, x, y, w, h; double t; if (!sid->pan_obj) return; if (sid->freeze) return; if (t_in <= 0.0) { elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); x = pos_x; elm_interface_scrollable_content_region_set (sid->obj, x, y, w, h); return; } t = ecore_loop_time_get(); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); if (px == pos_x) return; sid->scrollto.x.start = px; sid->scrollto.x.end = pos_x; sid->scrollto.x.t_start = t; sid->scrollto.x.t_end = t + t_in; if (!sid->scrollto.x.animator) { ELM_ANIMATOR_CONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); if (!sid->scrollto.y.animator) _elm_scroll_anim_start(sid); } if (sid->down.bounce_x_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); _elm_scroll_momentum_end(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } sid->bouncemex = EINA_FALSE; } static Eina_Bool _elm_scroll_running_momentum_speed_get(Elm_Scrollable_Smart_Interface_Data *sid, double *vx, double *vy) { double p = 0; int remain_x = 0, remain_y = 0; double remain_dur = 0; p = (ecore_loop_time_get() - sid->down.anim_start) / sid->down.anim_dur; // if momentum animation is not running now if ( p > 1.0 || p < 0) { if (vx) *vx = 0; if (vy) *vy = 0; return EINA_FALSE; } // calculate current velecity from remain distance and time remain_x = sid->down.dx - sid->down.dx * p; remain_y = sid->down.dy - sid->down.dy * p; remain_dur = sid->down.anim_dur - sid->down.anim_dur * p; if (vx) *vx = remain_x / remain_dur; if (vy) *vy = remain_y / remain_dur; return EINA_TRUE; } static Eina_Bool _elm_scroll_momentum_calc(int dx, int dy, double dt, double *vx, double *vy, int *dist_x, int *dist_y, double *dur) { int n = 0; double vel = 0, vel_x = 0, vel_y = 0; double fvel = 0, fvel_x = 0, fvel_y = 0; int distance_x = 0 , distance_y = 0; int sign_dx = 0, sign_dy = 0; int sign_vx = 0, sign_vy = 0; double r = _elm_config->thumbscroll_momentum_friction; const int min_px = 3; if ( dt == 0 ) return EINA_FALSE; // store sign value of distance sign_dx = (dx > 0) - (dx < 0); sign_dy = (dy > 0) - (dy < 0); if (vx) sign_vx = (*vx > 0) - (*vx < 0); if (vy) sign_vy = (*vy > 0) - (*vy < 0); // scale factor must be below 1.0 if ( r >= 1 ) r = 0.99; if (vx && (sign_dx == sign_vx)) vel_x = *vx; if (vy && (sign_dy == sign_vy)) vel_y = *vy; // calculate time based velecity (unit : px/second) vel_x += dx / dt; vel_y += dy / dt; vel_x *= _elm_config->thumbscroll_sensitivity_friction; vel_y *= _elm_config->thumbscroll_sensitivity_friction; vel = sqrt((vel_x * vel_x) + (vel_y * vel_y)); // calculate frame based velecity (unit : px/frame) fvel_x = vel_x * (1/60.0); fvel_y = vel_y * (1/60.0); fvel = vel * (1/60.0); if (abs((int) fvel) < _elm_config->thumbscroll_threshold ) return EINA_FALSE; // calculate a number of frames to reach min_px when it follows a geometric sequence with scale factor r n = log(min_px/fvel) / log(r); distance_x = fvel_x * (( 1 - pow(r, n)) / (1 - r)); distance_y = fvel_y * (( 1 - pow(r, n)) / (1 - r)); // remove sign of distance distance_x = abs(distance_x); distance_y = abs(distance_y); // clamp distance by thumbscroll_momentum_distance_max distance_x = CLAMP(distance_x, 0, _elm_config->thumbscroll_momentum_distance_max); distance_y = CLAMP(distance_y, 0, _elm_config->thumbscroll_momentum_distance_max); // restore sign distance_x *= sign_dx; distance_y *= sign_dy; if (dist_x) *dist_x = distance_x; if (dist_y) *dist_y = distance_y; if (vx) *vx = vel_x; if (vy) *vy = vel_y; // convert to time based animation duration if (dur) *dur = CLAMP((n / 60.0), _elm_config->thumbscroll_momentum_animation_duration_min_limit, _elm_config->thumbscroll_momentum_animation_duration_max_limit); return EINA_TRUE; } static void _elm_scroll_mouse_up_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord x = 0, y = 0, ox = 0, oy = 0; Evas_Event_Mouse_Up *ev; if (!sid->pan_obj) return; if ((sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) && (sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) return; #ifdef SMOOTHDBG if (_elm_scroll_smooth_debug) _elm_scroll_smooth_debug_shutdown(); #endif ev = event_info; sid->down.hold_parent = EINA_FALSE; sid->down.dx = 0; sid->down.dy = 0; evas_post_event_callback_push(e, _elm_scroll_post_event_up, sid); // FIXME: respect elm_widget_scroll_hold_get of parent container if (!_elm_config->thumbscroll_enable) return; if (ev->button == 1) { if (sid->down.onhold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } x = ev->canvas.x - sid->down.x; y = ev->canvas.y - sid->down.y; if (sid->down.dragged) { _elm_scroll_drag_stop(sid); if ((!sid->hold) && (!sid->freeze)) { int i; double t, at, dt; Evas_Coord ax, ay, dx, dy, vel; double vel_x, vel_y, dur; Evas_Coord dist_x, dist_y; t = ev->timestamp / 1000.0; ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; ax = ev->canvas.x; ay = ev->canvas.y; at = 0.0; #ifdef SCROLLDBG DBG("------ %i %i\n", ev->canvas.x, ev->canvas.y); #endif for (i = 0; i < 60; i++) { dt = t - sid->down.history[i].timestamp; if (dt > 0.2) break; #ifdef SCROLLDBG DBG("H: %i %i @ %1.3f\n", sid->down.history[i].x, sid->down.history[i].y, dt); #endif at += dt; ax += sid->down.history[i].x; ay += sid->down.history[i].y; } ax /= (i + 1); ay /= (i + 1); at /= (i + 1); dx = ev->canvas.x - ax; dy = ev->canvas.y - ay; _elm_scroll_running_momentum_speed_get(sid, &vel_x, &vel_y); if (at > 0 && _elm_scroll_momentum_calc(dx, dy, at, &vel_x, &vel_y, &dist_x, &dist_y, &dur)) { vel = sqrt((vel_x * vel_x) + (vel_y * vel_y)); if ((_elm_config->thumbscroll_momentum_friction > 0.0) && (vel > _elm_config->thumbscroll_momentum_threshold)) { int minx, miny, mx, my, px, py; double dtt = 0.0; elm_obj_pan_pos_min_get (sid->pan_obj, &minx, &miny); elm_obj_pan_pos_max_get (sid->pan_obj, &mx, &my); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); sid->down.dx = dist_x; sid->down.dy = dist_y; sid->down.anim_dur = dur; if (abs(sid->down.dx) > _elm_config->thumbscroll_acceleration_threshold && (dtt < _elm_config->thumbscroll_acceleration_time_limit) && (((sid->down.dx > 0) && (sid->down.pdx > 0)) || ((sid->down.dx < 0) && (sid->down.pdx < 0)))) if (px > minx && px < mx) sid->down.dx += (double)sid->down.pdx * _elm_config->thumbscroll_acceleration_weight; if (abs(sid->down.dy) > _elm_config->thumbscroll_acceleration_threshold && (dtt < _elm_config->thumbscroll_acceleration_time_limit) && (((sid->down.dy > 0) && (sid->down.pdy > 0)) || ((sid->down.dy < 0) && (sid->down.pdy < 0)))) if (py > miny && py < my) { sid->down.dy += (double)sid->down.pdy * _elm_config->thumbscroll_acceleration_weight; } sid->down.pdx = sid->down.dx; sid->down.pdy = sid->down.dy; ox = -sid->down.dx; oy = -sid->down.dy; if (!_paging_is_enabled(sid)) { if ((!sid->down.momentum_animator) && (!sid->momentum_animator_disabled) && (sid->obj) && (!elm_widget_scroll_child_locked_y_get (sid->obj))) { ELM_ANIMATOR_CONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; _elm_scroll_anim_start(sid); } sid->down.anim_start = ecore_loop_time_get(); elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); sid->down.sx = x; sid->down.sy = y; sid->down.b0x = 0; sid->down.b0y = 0; } } } } else { sid->down.pdx = 0; sid->down.pdy = 0; } evas_event_feed_hold(e, 0, ev->timestamp, ev->data); if (_paging_is_enabled(sid)) { Evas_Coord pgx, pgy; elm_interface_scrollable_content_pos_get (sid->obj, &x, &y); if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get (sid->obj))) { pgx = _elm_scroll_page_x_get(sid, ox, EINA_TRUE); if (pgx != x && !(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; _elm_scroll_scroll_to_x (sid, _elm_config->page_scroll_friction, pgx); } } if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get (sid->obj))) { pgy = _elm_scroll_page_y_get(sid, oy, EINA_TRUE); if (pgy != y && !(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; _elm_scroll_scroll_to_y (sid, _elm_config->page_scroll_friction, pgy); } } } } else { sid->down.pdx = 0; sid->down.pdy = 0; if (_paging_is_enabled(sid)) { Evas_Coord pgx, pgy; elm_interface_scrollable_content_pos_get (sid->obj, &x, &y); if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get (sid->obj))) { pgx = _elm_scroll_page_x_get(sid, ox, EINA_TRUE); if (pgx != x) _elm_scroll_scroll_to_x (sid, _elm_config->page_scroll_friction, pgx); } if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get (sid->obj))) { pgy = _elm_scroll_page_y_get(sid, oy, EINA_TRUE); if (pgy != y) _elm_scroll_scroll_to_y (sid, _elm_config->page_scroll_friction, pgy); } } } if (sid->down.hold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del); if (sid->down.scroll) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; sid->down.scroll = EINA_FALSE; } if (sid->down.hold) { ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; sid->down.hold = EINA_FALSE; } sid->down.dragged_began = EINA_FALSE; sid->down.dir_x = EINA_FALSE; sid->down.dir_y = EINA_FALSE; sid->down.want_dragged = EINA_FALSE; sid->down.dragged = EINA_FALSE; sid->down.now = EINA_FALSE; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); _elm_scroll_wanted_coordinates_update(sid, x, y); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); if (!_paging_is_enabled(sid)) _elm_scroll_bounce_eval(sid); } } static void _elm_scroll_mouse_down_event_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Elm_Scrollable_Smart_Interface_Data *sid; Evas_Event_Mouse_Down *ev; Evas_Coord x = 0, y = 0; sid = data; ev = event_info; if ((sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) && (sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) return; #ifdef SMOOTHDBG if (getenv("ELS_SCROLLER_SMOOTH_DEBUG")) _elm_scroll_smooth_debug = 1; if (_elm_scroll_smooth_debug) _elm_scroll_smooth_debug_init(); #endif if (!_elm_config->thumbscroll_enable) return; sid->down.hold = EINA_FALSE; if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || (sid->down.momentum_animator) || (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL | EVAS_EVENT_FLAG_ON_HOLD; sid->down.scroll = EINA_TRUE; sid->down.hold = EINA_TRUE; _elm_scroll_anim_stop(sid); } ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); if (sid->down.bounce_x_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); sid->bouncemex = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (sid->down.bounce_y_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); sid->bouncemey = EINA_FALSE; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (sid->down.hold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); _elm_scroll_drag_stop(sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del); if (sid->down.momentum_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); sid->down.bounce_x_hold = EINA_FALSE; sid->down.bounce_y_hold = EINA_FALSE; sid->down.ax = 0; sid->down.ay = 0; if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } if (ev->button == 1) { sid->down.est_timestamp_diff = ecore_loop_time_get() - ((double)ev->timestamp / 1000.0); sid->down.now = EINA_TRUE; sid->down.dragged = EINA_FALSE; sid->down.dir_x = EINA_FALSE; sid->down.dir_y = EINA_FALSE; sid->down.x = ev->canvas.x; sid->down.y = ev->canvas.y; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); sid->down.sx = x; sid->down.sy = y; sid->down.locked = EINA_FALSE; memset(&(sid->down.history[0]), 0, sizeof(sid->down.history[0]) * 60); sid->down.history[0].timestamp = ev->timestamp / 1000.0; sid->down.dragged_began_timestamp = sid->down.history[0].timestamp; sid->down.history[0].x = ev->canvas.x; sid->down.history[0].y = ev->canvas.y; } sid->down.dragged_began = EINA_FALSE; sid->down.hold_parent = EINA_FALSE; sid->down.cancelled = EINA_FALSE; if (sid->hold || sid->freeze) sid->down.want_reset = EINA_TRUE; else sid->down.want_reset = EINA_FALSE; } static Eina_Bool _elm_scroll_can_scroll(Elm_Scrollable_Smart_Interface_Data *sid, int dir) { Evas_Coord mx = 0, my = 0, px = 0, py = 0, minx = 0, miny = 0; if (!sid->pan_obj) return EINA_FALSE; elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); switch (dir) { case LEFT: if (px > minx) return EINA_TRUE; break; case RIGHT: if ((px - minx) < mx) return EINA_TRUE; break; case UP: if (py > miny) return EINA_TRUE; break; case DOWN: if ((py - miny) < my) return EINA_TRUE; break; default: break; } return EINA_FALSE; } static inline void _elm_widget_parents_bounce_get(Eo *obj, Eina_Bool *horiz, Eina_Bool *vert) { Evas_Object *parent_obj = obj; Eina_Bool h = EINA_FALSE, v = EINA_FALSE; *horiz = EINA_FALSE; *vert = EINA_FALSE; do { parent_obj = elm_widget_parent_get(parent_obj); if ((!parent_obj) || (!efl_isa(parent_obj, EFL_UI_WIDGET_CLASS))) break; if (efl_isa(parent_obj, ELM_INTERFACE_SCROLLABLE_MIXIN)) { elm_interface_scrollable_bounce_allow_get(parent_obj, &h, &v); if (h) *horiz = EINA_TRUE; if (v) *vert = EINA_TRUE; } } while (parent_obj); } static Eina_Bool _elm_scroll_post_event_move(void *data, Evas *e EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; Efl_Ui_Layout_Orientation block; Eina_Bool horiz, vert; int start = 0; if (!sid->down.want_dragged) return EINA_TRUE; block = elm_widget_scroll_lock_get(sid->obj); _elm_widget_parents_bounce_get(sid->obj, &horiz, &vert); if (sid->down.hold_parent) { if ((sid->down.dir_x) && (horiz || !sid->bounce_horiz) && !_elm_scroll_can_scroll(sid, sid->down.hdir)) { sid->down.dir_x = EINA_FALSE; } if ((sid->down.dir_y) && (vert || !sid->bounce_vert) && !_elm_scroll_can_scroll(sid, sid->down.vdir)) { sid->down.dir_y = EINA_FALSE; } sid->down.dragged_began = EINA_TRUE; } if (sid->down.dir_x) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get(sid->obj))) { if (sid->down.dragged_began) { sid->down.want_dragged = EINA_FALSE; sid->down.dragged = EINA_TRUE; if (sid->obj) { block |= EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; elm_widget_scroll_lock_set(sid->obj, block); } start = 1; } } else { sid->down.dragged_began = EINA_TRUE; sid->down.dir_x = EINA_FALSE; } } if (sid->down.dir_y) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get(sid->obj))) { if (sid->down.dragged_began) { sid->down.want_dragged = EINA_FALSE; sid->down.dragged = EINA_TRUE; if (sid->obj) { block |= EFL_UI_LAYOUT_ORIENTATION_VERTICAL; elm_widget_scroll_lock_set(sid->obj, block); } start = 1; } } else { sid->down.dragged_began = EINA_TRUE; sid->down.dir_y = EINA_FALSE; } } if ((!sid->down.dir_x) && (!sid->down.dir_y)) { sid->down.cancelled = EINA_TRUE; } if (start) _elm_scroll_drag_start(sid); return EINA_TRUE; } static void _elm_scroll_down_coord_eval(Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y) { Evas_Coord minx, miny; if (!sid->pan_obj) return; if (sid->down.dir_x) *x = sid->down.sx - (*x - sid->down.x); else *x = sid->down.sx; if (sid->down.dir_y) *y = sid->down.sy - (*y - sid->down.y); else *y = sid->down.sy; if ((sid->down.dir_x) || (sid->down.dir_y)) { if (!((sid->down.dir_x) && (sid->down.dir_y))) { if (sid->down.dir_x) *y = sid->down.locked_y; else *x = sid->down.locked_x; } } elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); if (!sid->loop_h && *x < minx) *x += (minx - *x) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_h && sid->content_info.w <= sid->w) *x += (sid->down.sx - *x) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_h && (sid->content_info.w - sid->w + minx) < *x) *x += (sid->content_info.w - sid->w + minx - *x) * _elm_config->thumbscroll_border_friction; if (!sid->loop_v && *y < miny) *y += (miny - *y) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_v && sid->content_info.h <= sid->h) *y += (sid->down.sy - *y) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_v && (sid->content_info.h - sid->h + miny) < *y) *y += (sid->content_info.h - sid->h + miny - *y) * _elm_config->thumbscroll_border_friction; } static Eina_Bool _elm_scroll_hold_enterer(void *data) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord ox = 0, oy = 0, fx = 0, fy = 0; // Evas_Coord fy2; sid->down.hold_enterer = NULL; fx = sid->down.hold_x; fy = sid->down.hold_y; // fy2 = fy; if ((_elm_config->scroll_smooth_amount > 0.0) && (_elm_config->scroll_smooth_time_window > 0.0)) { int i, count = 0; Evas_Coord basex = 0, basey = 0, x, y; double dt, tdiff, tnow, twin, ttot; double xx, yy, tot; struct { Evas_Coord x, y; double t; } pos[100]; tdiff = sid->down.est_timestamp_diff; tnow = ecore_loop_time_get(); twin = _elm_config->scroll_smooth_time_window; for (i = 0; i < 60; i++) { if ((sid->down.history[i].timestamp - tdiff) > tnow) continue; if ((sid->down.history[i].timestamp > sid->down.dragged_began_timestamp) || (count == 0)) { x = sid->down.history[i].x; y = sid->down.history[i].y; _elm_scroll_down_coord_eval(sid, &x, &y); if (count == 0) { basex = x; basey = y; } dt = (tnow + tdiff) - sid->down.history[i].timestamp; if ((dt > twin) && (count > 0)) break; if ((dt > 0.0) && (count == 0)) { pos[count].x = x - basex; pos[count].y = y - basey; pos[count].t = 0.0; count++; } pos[count].x = x - basex; pos[count].y = y - basey; pos[count].t = dt; count++; } } if (count > 0) { xx = 0.0; yy = 0.0; tot = 0.0; ttot = pos[count - 1].t; for (i = 0; i < count; i++) { double wt; if (ttot > 0.0) { if (i < (count - 1)) wt = (ttot - pos[i].t) * (pos[i + 1].t - pos[i].t); else wt = 0.0; } else wt = 1.0; xx += ((double)(pos[i].x)) * wt; yy += ((double)(pos[i].y)) * wt; tot += wt; } if (tot > 0.0) { xx = basex + (xx / tot); yy = basey + (yy / tot); fx = (_elm_config->scroll_smooth_amount * xx) + ((1.0 - _elm_config->scroll_smooth_amount) * fx); fy = (_elm_config->scroll_smooth_amount * yy) + ((1.0 - _elm_config->scroll_smooth_amount) * fy); } } } // printf("%1.5f %i %i\n", // ecore_loop_time_get() - sid->down.dragged_began_timestamp, // fy, fy2); elm_interface_scrollable_content_pos_get(sid->obj, &ox, &oy); if (sid->down.dir_x) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get(sid->obj))) ox = fx; } if (sid->down.dir_y) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get(sid->obj))) oy = fy; } #ifdef SMOOTHDBG if (_elm_scroll_smooth_debug) _elm_scroll_smooth_debug_movetime_add(ox, oy); #endif elm_interface_scrollable_content_pos_set(sid->obj, ox, oy, EINA_TRUE); return EINA_FALSE; } static void _elm_scroll_hold_animator(void *data, const Efl_Event *event EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; ecore_idle_enterer_del(sid->down.hold_enterer); sid->down.hold_enterer = ecore_idle_enterer_before_add(_elm_scroll_hold_enterer, sid); } static void _elm_scroll_on_hold_animator(void *data, const Efl_Event *event EINA_UNUSED) { double t, td; double vx, vy; Evas_Coord x, y, ox = 0, oy = 0; Elm_Scrollable_Smart_Interface_Data *sid; sid = data; t = ecore_loop_time_get(); if (sid->down.onhold_tlast > 0.0) { td = t - sid->down.onhold_tlast; vx = sid->down.onhold_vx * td * (double)_elm_config->thumbscroll_hold_threshold * 2.0; vy = sid->down.onhold_vy * td * (double)_elm_config->thumbscroll_hold_threshold * 2.0; elm_interface_scrollable_content_pos_get(sid->obj, &ox, &oy); x = ox; y = oy; if (sid->down.dir_x) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_x_get(sid->obj))) { sid->down.onhold_vxe += vx; x = ox + (int)sid->down.onhold_vxe; sid->down.onhold_vxe -= (int)sid->down.onhold_vxe; } } if (sid->down.dir_y) { if ((!sid->obj) || (!elm_widget_scroll_child_locked_y_get(sid->obj))) { sid->down.onhold_vye += vy; y = oy + (int)sid->down.onhold_vye; sid->down.onhold_vye -= (int)sid->down.onhold_vye; } } elm_interface_scrollable_content_pos_set(sid->obj, x, y, EINA_TRUE); } sid->down.onhold_tlast = t; } static void _elm_scroll_mouse_move_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Event_Mouse_Move *ev; Evas_Coord x = 0, y = 0; if (!sid->pan_obj) return; if ((sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) && (sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) return; ev = event_info; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sid->down.hold_parent = EINA_TRUE; evas_post_event_callback_push(e, _elm_scroll_post_event_move, sid); // FIXME: respect elm_widget_scroll_hold_get of parent container if (!_elm_config->thumbscroll_enable) return; if (!sid->down.now) return; if ((sid->scrollto.x.animator) && (!sid->hold) && (!sid->freeze) && !(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { Evas_Coord px = 0; ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); elm_obj_pan_pos_get(sid->pan_obj, &px, NULL); sid->down.sx = px; sid->down.x = sid->down.history[0].x; } if ((sid->scrollto.y.animator) && (!sid->hold) && (!sid->freeze) && !(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { Evas_Coord py = 0; ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); elm_obj_pan_pos_get(sid->pan_obj, NULL, &py); sid->down.sy = py; sid->down.y = sid->down.history[0].y; } #ifdef SCROLLDBG DBG("::: %i %i\n", ev->cur.canvas.x, ev->cur.canvas.y); #endif memmove(&(sid->down.history[1]), &(sid->down.history[0]), sizeof(sid->down.history[0]) * (60 - 1)); sid->down.history[0].timestamp = ev->timestamp / 1000.0; sid->down.history[0].x = ev->cur.canvas.x; sid->down.history[0].y = ev->cur.canvas.y; if (!sid->down.dragged_began) { x = ev->cur.canvas.x - sid->down.x; y = ev->cur.canvas.y - sid->down.y; sid->down.hdir = -1; sid->down.vdir = -1; if (x > 0) sid->down.hdir = LEFT; else if (x < 0) sid->down.hdir = RIGHT; if (y > 0) sid->down.vdir = UP; else if (y < 0) sid->down.vdir = DOWN; if (x < 0) x = -x; if (y < 0) y = -y; if (sid->one_direction_at_a_time) { if (((x * x) + (y * y)) > (_elm_config->thumbscroll_threshold * _elm_config->thumbscroll_threshold)) { if (sid->one_direction_at_a_time == ELM_SCROLLER_SINGLE_DIRECTION_SOFT) { int dodir = 0; if (x > (y * 2)) { if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { sid->down.dir_x = EINA_TRUE; } sid->down.dir_y = EINA_FALSE; dodir++; } if (y > (x * 2)) { sid->down.dir_x = EINA_FALSE; if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { sid->down.dir_y = EINA_TRUE; } dodir++; } if (!dodir) { if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { sid->down.dir_x = EINA_TRUE; } if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { sid->down.dir_y = EINA_TRUE; } } } else if (sid->one_direction_at_a_time == ELM_SCROLLER_SINGLE_DIRECTION_HARD) { if (x > y) { if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { sid->down.dir_x = EINA_TRUE; } sid->down.dir_y = EINA_FALSE; } if (y > x) { sid->down.dir_x = EINA_FALSE; if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { sid->down.dir_y = EINA_TRUE; } } } } } else { if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) { sid->down.dir_x = EINA_TRUE; } if (!(sid->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) { sid->down.dir_y = EINA_TRUE; } } } if ((!sid->hold) && (!sid->freeze)) { if ((sid->down.dragged) || (((x * x) + (y * y)) > (_elm_config->thumbscroll_threshold * _elm_config->thumbscroll_threshold))) { if (!sid->down.dragged_began && _elm_config->scroll_smooth_start_enable) { sid->down.x = ev->cur.canvas.x; sid->down.y = ev->cur.canvas.y; sid->down.dragged_began_timestamp = ev->timestamp / 1000.0; } if (!sid->down.dragged) { sid->down.want_dragged = EINA_TRUE; } if ((((_elm_scroll_can_scroll(sid, LEFT) || _elm_scroll_can_scroll(sid, RIGHT)) && sid->down.dir_x) || ((_elm_scroll_can_scroll(sid, UP) || _elm_scroll_can_scroll(sid, DOWN)) && sid->down.dir_y)) && !sid->down.dragged_began) { ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; sid->down.dragged_began = EINA_TRUE; } else if (sid->down.dragged_began) { ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; } if (sid->down.dir_x) x = sid->down.sx - (ev->cur.canvas.x - sid->down.x); else x = sid->down.sx; if (sid->down.dir_y) y = sid->down.sy - (ev->cur.canvas.y - sid->down.y); else y = sid->down.sy; if (sid->down.want_reset) { sid->down.x = ev->cur.canvas.x; sid->down.y = ev->cur.canvas.y; sid->down.want_reset = EINA_FALSE; } if ((sid->down.dir_x) || (sid->down.dir_y)) { if (!sid->down.locked) { sid->down.locked_x = x; sid->down.locked_y = y; sid->down.locked = EINA_TRUE; } if (!((sid->down.dir_x) && (sid->down.dir_y))) { if (sid->down.dir_x) y = sid->down.locked_y; else x = sid->down.locked_x; } } { Evas_Coord minx = 0, miny = 0, mx, my; elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_max_get(sid->pan_obj, &mx, &my); if (!sid->loop_v && y < miny) y += (miny - y) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_v && my <= 0) y += (sid->down.sy - y) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_v && (my + miny) < y) y += (my + miny - y) * _elm_config->thumbscroll_border_friction; if (!sid->loop_h && x < minx) x += (minx - x) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_h && mx <= 0) x += (sid->down.sx - x) * _elm_config->thumbscroll_border_friction; else if (!sid->loop_h && (mx + minx) < x) x += (mx + minx - x) * _elm_config->thumbscroll_border_friction; } sid->down.hold_x = x; sid->down.hold_y = y; ELM_ANIMATOR_CONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); } else { if (sid->down.dragged_began) { if ((_elm_scroll_can_scroll(sid, sid->down.hdir) && sid->down.dir_x) || (_elm_scroll_can_scroll(sid, sid->down.vdir) && sid->down.dir_y)) { ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; } if (!sid->down.hold) { sid->down.hold = EINA_TRUE; evas_event_feed_hold (e, 1, ev->timestamp, ev->data); } } } } else if (!sid->freeze) { double vx = 0.0, vy = 0.0; x = ev->cur.canvas.x - sid->x; y = ev->cur.canvas.y - sid->y; if (x < _elm_config->thumbscroll_hold_threshold) { if (_elm_config->thumbscroll_hold_threshold > 0.0) vx = -(double)(_elm_config->thumbscroll_hold_threshold - x) / _elm_config->thumbscroll_hold_threshold; else vx = -1.0; } else if (x > (sid->w - _elm_config->thumbscroll_hold_threshold)) { if (_elm_config->thumbscroll_hold_threshold > 0.0) vx = (double)(_elm_config->thumbscroll_hold_threshold - (sid->w - x)) / _elm_config->thumbscroll_hold_threshold; else vx = 1.0; } if (y < _elm_config->thumbscroll_hold_threshold) { if (_elm_config->thumbscroll_hold_threshold > 0.0) vy = -(double)(_elm_config->thumbscroll_hold_threshold - y) / _elm_config->thumbscroll_hold_threshold; else vy = -1.0; } else if (y > (sid->h - _elm_config->thumbscroll_hold_threshold)) { if (_elm_config->thumbscroll_hold_threshold > 0.0) vy = (double)(_elm_config->thumbscroll_hold_threshold - (sid->h - y)) / _elm_config->thumbscroll_hold_threshold; else vy = 1.0; } if ((vx != 0.0) || (vy != 0.0)) { sid->down.onhold_vx = vx; sid->down.onhold_vy = vy; if (!sid->down.onhold_animator) { sid->down.onhold_vxe = 0.0; sid->down.onhold_vye = 0.0; sid->down.onhold_tlast = 0.0; ELM_ANIMATOR_CONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); } } else { if (sid->down.onhold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } } } } static void _elm_scroll_page_adjust(Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord x, y, w, h; if (!_paging_is_enabled(sid)) return; elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); x = _elm_scroll_page_x_get(sid, 0, EINA_TRUE); y = _elm_scroll_page_y_get(sid, 0, EINA_TRUE); elm_interface_scrollable_content_region_set(sid->obj, x, y, w, h); } static void _elm_scroll_reconfigure(Elm_Scrollable_Smart_Interface_Data *sid) { _elm_scroll_scroll_bar_size_adjust(sid); _elm_scroll_page_adjust(sid); } static void _on_edje_move(void *data, Evas *e EINA_UNUSED, Evas_Object *edje_obj, void *event_info EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; int x, y; evas_object_geometry_get(edje_obj, &x, &y, NULL, NULL); sid->x = x; sid->y = y; _elm_scroll_reconfigure(sid); } static void _on_edje_resize(void *data, Evas *e, Evas_Object *edje_obj, void *event_info EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; Evas_Coord w, h; int current_calc; Eina_Bool reconf_ok = EINA_TRUE; evas_object_geometry_get(edje_obj, NULL, NULL, &w, &h); sid->w = w; sid->h = h; current_calc = evas_smart_objects_calculate_count_get(e); if (current_calc == sid->current_calc) { sid->size_count++; if (sid->size_count > 3) reconf_ok = EINA_FALSE; } else { sid->current_calc = current_calc; sid->size_count = 0; } if (reconf_ok) _elm_scroll_reconfigure(sid); _elm_scroll_wanted_region_set(sid->obj); } static void _scroll_edje_object_attach(Evas_Object *obj) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); evas_object_event_callback_add (sid->edje_obj, EVAS_CALLBACK_RESIZE, _on_edje_resize, sid); evas_object_event_callback_add (sid->edje_obj, EVAS_CALLBACK_MOVE, _on_edje_move, sid); edje_object_signal_callback_add (sid->edje_obj, "reload", "elm", _elm_scroll_reload_cb, sid); if (!_elm_scroll_has_bars(sid)) return; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; else iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; edje_object_signal_callback_add (sid->edje_obj, "drag", iface_scr_dragable_vbar, _elm_scroll_vbar_drag_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,set", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,start", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_start_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,stop", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_stop_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,step", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,page", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "elm,vbar,press", "elm", _elm_scroll_vbar_press_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "elm,vbar,unpress", "elm", _elm_scroll_vbar_unpress_cb, sid); const char *iface_scr_dragable_hbar = NULL; if (elm_widget_is_legacy(sid->obj)) iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; else iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; edje_object_signal_callback_add (sid->edje_obj, "drag", iface_scr_dragable_hbar, _elm_scroll_hbar_drag_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,set", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,start", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_start_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,stop", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_stop_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,step", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "drag,page", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "elm,hbar,press", "elm", _elm_scroll_hbar_press_cb, sid); edje_object_signal_callback_add (sid->edje_obj, "elm,hbar,unpress", "elm", _elm_scroll_hbar_unpress_cb, sid); } static void _scroll_event_object_attach(Evas_Object *obj) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); evas_object_event_callback_add (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb, sid); evas_object_event_callback_add (sid->event_rect, EVAS_CALLBACK_MOUSE_DOWN, _elm_scroll_mouse_down_event_cb, sid); evas_object_event_callback_add (sid->event_rect, EVAS_CALLBACK_MOUSE_UP, _elm_scroll_mouse_up_event_cb, sid); evas_object_event_callback_add (sid->event_rect, EVAS_CALLBACK_MOUSE_MOVE, _elm_scroll_mouse_move_event_cb, sid); } static void _scroll_edje_object_detach(Evas_Object *obj) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); evas_object_event_callback_del_full (sid->edje_obj, EVAS_CALLBACK_RESIZE, _on_edje_resize, sid); evas_object_event_callback_del_full (sid->edje_obj, EVAS_CALLBACK_MOVE, _on_edje_move, sid); if (!_elm_scroll_has_bars(sid)) return; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; else iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; edje_object_signal_callback_del_full (sid->edje_obj, "drag", iface_scr_dragable_vbar, _elm_scroll_vbar_drag_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,set", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,start", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_start_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,stop", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_stop_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,step", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,page", iface_scr_dragable_vbar, _elm_scroll_edje_drag_v_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "elm,vbar,press", "elm", _elm_scroll_vbar_press_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "elm,vbar,unpress", "elm", _elm_scroll_vbar_unpress_cb, sid); const char *iface_scr_dragable_hbar = NULL; if (elm_widget_is_legacy(sid->obj)) iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; else iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; edje_object_signal_callback_del_full (sid->edje_obj, "drag", iface_scr_dragable_hbar, _elm_scroll_hbar_drag_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,set", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,start", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_start_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,stop", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_stop_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,step", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "drag,page", iface_scr_dragable_hbar, _elm_scroll_edje_drag_h_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "elm,hbar,press", "elm", _elm_scroll_hbar_press_cb, sid); edje_object_signal_callback_del_full (sid->edje_obj, "elm,hbar,unpress", "elm", _elm_scroll_hbar_unpress_cb, sid); } static void _scroll_event_object_detach(Evas_Object *obj) { ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); evas_object_event_callback_del_full (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb, sid); evas_object_event_callback_del_full (sid->event_rect, EVAS_CALLBACK_MOUSE_DOWN, _elm_scroll_mouse_down_event_cb, sid); evas_object_event_callback_del_full (sid->event_rect, EVAS_CALLBACK_MOUSE_UP, _elm_scroll_mouse_up_event_cb, sid); evas_object_event_callback_del_full (sid->event_rect, EVAS_CALLBACK_MOUSE_MOVE, _elm_scroll_mouse_move_event_cb, sid); } +EOLIAN static void +_elm_interface_scrollable_reset_signals(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) +{ + sid->go_up = sid->go_down = sid->go_right = sid->go_left = EINA_FALSE; + + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,up", "elm"); + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,down", "elm"); + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,right", "elm"); + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,left", "elm"); + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,vbar", "elm"); + edje_object_signal_emit(sid->edje_obj, "elm,action,hide,hbar", "elm"); + + _elm_scroll_scroll_bar_visibility_adjust(sid); +} + EOLIAN static void _elm_interface_scrollable_objects_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Object *edje_object, Evas_Object *hit_rectangle) { Evas_Coord mw, mh; if (!edje_object || !hit_rectangle) return; if (sid->edje_obj) _scroll_edje_object_detach(obj); sid->edje_obj = edje_object; - edje_object_signal_emit(sid->edje_obj, "elm,action,hide,up", "elm"); - edje_object_signal_emit(sid->edje_obj, "elm,action,hide,down", "elm"); - edje_object_signal_emit(sid->edje_obj, "elm,action,hide,right", "elm"); - edje_object_signal_emit(sid->edje_obj, "elm,action,hide,left", "elm"); + elm_interface_scrollable_reset_signals(obj); if (sid->event_rect) _scroll_event_object_detach(obj); sid->event_rect = hit_rectangle; evas_object_repeat_events_set(hit_rectangle, EINA_TRUE); _scroll_edje_object_attach(obj); _scroll_event_object_attach(obj); mw = mh = -1; elm_coords_finger_size_adjust(1, &mw, 1, &mh); if (elm_widget_is_legacy(sid->obj)) { if (edje_object_part_exists(sid->edje_obj, "elm.scrollbar.base")) { Evas_Object *base; base = edje_object_part_swallow_get (sid->edje_obj, "elm.scrollbar.base"); if (!base) { base = evas_object_rectangle_add (evas_object_evas_get(sid->edje_obj)); evas_object_color_set(base, 0, 0, 0, 0); edje_object_part_swallow (sid->edje_obj, "elm.scrollbar.base", base); } if (!_elm_config->thumbscroll_enable) evas_object_size_hint_min_set(base, mw, mh); } } else { if (edje_object_part_exists(sid->edje_obj, "efl.scrollbar.base")) { Evas_Object *base; base = edje_object_part_swallow_get (sid->edje_obj, "efl.scrollbar.base"); if (!base) { base = evas_object_rectangle_add (evas_object_evas_get(sid->edje_obj)); evas_object_color_set(base, 0, 0, 0, 0); edje_object_part_swallow (sid->edje_obj, "efl.scrollbar.base", base); } if (!_elm_config->thumbscroll_enable) evas_object_size_hint_min_set(base, mw, mh); } } _elm_scroll_scroll_bar_visibility_adjust(sid); } static void _elm_scroll_scroll_bar_reset(Elm_Scrollable_Smart_Interface_Data *sid) { Evas_Coord px = 0, py = 0, minx = 0, miny = 0; if (!sid->edje_obj || efl_invalidated_get(sid->edje_obj)) return; const char *iface_scr_dragable_hbar = NULL; const char *iface_scr_dragable_vbar = NULL; if (elm_widget_is_legacy(sid->obj)) { iface_scr_dragable_hbar = iface_scr_legacy_dragable_hbar; iface_scr_dragable_vbar = iface_scr_legacy_dragable_vbar; } else { iface_scr_dragable_hbar = iface_scr_efl_ui_dragable_hbar; iface_scr_dragable_vbar = iface_scr_efl_ui_dragable_vbar; } if (_elm_scroll_has_bars(sid)) { edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_vbar, 0.0, 0.0); edje_object_part_drag_value_set (sid->edje_obj, iface_scr_dragable_hbar, 0.0, 0.0); if ((!sid->content) && (!sid->extern_pan)) { edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_vbar, 1.0, 1.0); edje_object_part_drag_size_set (sid->edje_obj, iface_scr_dragable_hbar, 1.0, 1.0); } } if (sid->pan_obj) { elm_obj_pan_pos_min_get(sid->pan_obj, &minx, &miny); elm_obj_pan_pos_get(sid->pan_obj, &px, &py); elm_obj_pan_pos_set(sid->pan_obj, minx, miny); } if ((px != minx) || (py != miny)) edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); _elm_direction_arrows_eval(sid, EINA_TRUE); } static void _elm_scroll_pan_resized_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Evas_Coord w = 0, h = 0; Elm_Scrollable_Smart_Interface_Data *sid = data; if (sid->cb_func.content_viewport_resize) { elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); sid->cb_func.content_viewport_resize(sid->obj, w, h); } } /* even external pan objects get this */ static void _elm_scroll_pan_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Evas_Coord w = 0, h = 0; Elm_Scrollable_Smart_Interface_Data *sid = data; if (!sid->pan_obj) return; elm_obj_pan_content_size_get(sid->pan_obj, &w, &h); if ((w != sid->content_info.w) || (h != sid->content_info.h)) { sid->content_info.w = w; sid->content_info.h = h; _elm_scroll_scroll_bar_size_adjust(sid); evas_object_size_hint_min_set (sid->edje_obj, sid->content_info.w, sid->content_info.h); sid->content_info.resized = EINA_TRUE; _elm_scroll_wanted_region_set(sid->obj); } } static void _elm_scroll_content_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Scrollable_Smart_Interface_Data *sid = data; sid->content = NULL; _elm_scroll_scroll_bar_size_adjust(sid); _elm_scroll_scroll_bar_reset(sid); } EOLIAN static void _elm_interface_scrollable_scrollable_content_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Object *content) { Evas_Coord w = 0, h = 0; Evas_Object *o; if (!sid->edje_obj) return; if (sid->content) { /* if we had content, for sure we had a pan object */ _elm_pan_content_set(sid->pan_obj, NULL); evas_object_event_callback_del_full (sid->content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid); } sid->content = content; sid->wx = sid->wy = 0; /* (-1) means want viewports size */ sid->ww = sid->wh = -1; if (!content) return; if (!sid->pan_obj) { o = _elm_pan_add(evas_object_evas_get(obj)); ELM_PAN_DATA_GET_OR_RETURN(o, pd); pd->interface_object = obj; sid->pan_obj = o; efl_event_callback_add (o, ELM_PAN_EVENT_CHANGED, _elm_scroll_pan_changed_cb, sid); evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, _elm_scroll_pan_resized_cb, sid); if (elm_widget_is_legacy(obj)) edje_object_part_swallow(sid->edje_obj, "elm.swallow.content", o); else edje_object_part_swallow(sid->edje_obj, "efl.content", o); } evas_object_event_callback_add (content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid); _elm_pan_content_set(sid->pan_obj, content); elm_obj_pan_content_size_get(sid->pan_obj, &w, &h); sid->content_info.w = w; sid->content_info.h = h; _elm_scroll_scroll_bar_size_adjust(sid); _elm_scroll_scroll_bar_reset(sid); } EOLIAN static void _elm_interface_scrollable_extern_pan_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Object *pan) { if (!sid->edje_obj) return; elm_interface_scrollable_content_set(obj, NULL); if (sid->pan_obj) { efl_event_callback_del(sid->pan_obj, ELM_PAN_EVENT_CHANGED, _elm_scroll_pan_changed_cb, sid); evas_object_event_callback_del(sid->pan_obj, EVAS_CALLBACK_RESIZE, _elm_scroll_pan_resized_cb); } if (sid->extern_pan) { if (sid->pan_obj) { /* not owned by scroller, just leave (was external already) */ edje_object_part_unswallow(sid->edje_obj, sid->pan_obj); sid->pan_obj = NULL; } } else { ELM_SAFE_FREE(sid->pan_obj, evas_object_del); } if (!pan) { sid->extern_pan = EINA_FALSE; return; } sid->pan_obj = pan; sid->extern_pan = EINA_TRUE; efl_event_callback_add (sid->pan_obj, ELM_PAN_EVENT_CHANGED, _elm_scroll_pan_changed_cb, sid); evas_object_event_callback_add(sid->pan_obj, EVAS_CALLBACK_RESIZE, _elm_scroll_pan_resized_cb, sid); if (elm_widget_is_legacy(obj)) { edje_object_part_swallow (sid->edje_obj, "elm.swallow.content", sid->pan_obj); } else { edje_object_part_swallow (sid->edje_obj, "efl.content", sid->pan_obj); } } EOLIAN static void _elm_interface_scrollable_drag_start_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb drag_start_cb) { sid->cb_func.drag_start = drag_start_cb; } EOLIAN static void _elm_interface_scrollable_drag_stop_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb drag_stop_cb) { sid->cb_func.drag_stop = drag_stop_cb; } EOLIAN static void _elm_interface_scrollable_animate_start_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb animate_start_cb) { sid->cb_func.animate_start = animate_start_cb; } EOLIAN static void _elm_interface_scrollable_animate_stop_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb animate_stop_cb) { sid->cb_func.animate_stop = animate_stop_cb; } EOLIAN static void _elm_interface_scrollable_page_change_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb page_change_cb EINA_UNUSED) { sid->cb_func.page_change = page_change_cb; } EOLIAN static void _elm_interface_scrollable_scroll_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb scroll_cb) { sid->cb_func.scroll = scroll_cb; } EOLIAN static void _elm_interface_scrollable_scroll_left_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb scroll_left_cb) { sid->cb_func.scroll_left = scroll_left_cb; } EOLIAN static void _elm_interface_scrollable_scroll_right_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb scroll_right_cb) { sid->cb_func.scroll_right = scroll_right_cb; } EOLIAN static void _elm_interface_scrollable_scroll_up_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb scroll_up_cb) { sid->cb_func.scroll_up = scroll_up_cb; } EOLIAN static void _elm_interface_scrollable_scroll_down_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb scroll_down_cb) { sid->cb_func.scroll_down = scroll_down_cb; } EOLIAN static void _elm_interface_scrollable_edge_left_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb edje_left_cb) { sid->cb_func.edge_left = edje_left_cb; } EOLIAN static void _elm_interface_scrollable_edge_right_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb edje_right_cb) { sid->cb_func.edge_right = edje_right_cb; } EOLIAN static void _elm_interface_scrollable_edge_top_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb edje_top_cb) { sid->cb_func.edge_top = edje_top_cb; } EOLIAN static void _elm_interface_scrollable_edge_bottom_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb edje_bottom_cb) { sid->cb_func.edge_bottom = edje_bottom_cb; } EOLIAN static void _elm_interface_scrollable_vbar_drag_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb vbar_drag_cb) { sid->cb_func.vbar_drag = vbar_drag_cb; } EOLIAN static void _elm_interface_scrollable_vbar_press_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb vbar_press_cb) { sid->cb_func.vbar_press = vbar_press_cb; } EOLIAN static void _elm_interface_scrollable_vbar_unpress_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb vbar_unpress_cb) { sid->cb_func.vbar_unpress = vbar_unpress_cb; } EOLIAN static void _elm_interface_scrollable_hbar_drag_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb hbar_drag_cb) { sid->cb_func.hbar_drag = hbar_drag_cb; } EOLIAN static void _elm_interface_scrollable_hbar_press_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb hbar_press_cb) { sid->cb_func.hbar_press = hbar_press_cb; } EOLIAN static void _elm_interface_scrollable_hbar_unpress_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Cb hbar_unpress_cb) { sid->cb_func.hbar_unpress = hbar_unpress_cb; } EOLIAN static void _elm_interface_scrollable_content_min_limit_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Min_Limit_Cb min_limit_cb) { sid->cb_func.content_min_limit = min_limit_cb; } EOLIAN static void _elm_interface_scrollable_content_viewport_resize_cb_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Interface_Scrollable_Resize_Cb viewport_resize_cb) { sid->cb_func.content_viewport_resize = viewport_resize_cb; } EOLIAN static Eina_Bool _elm_interface_scrollable_momentum_animator_disabled_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { return sid->momentum_animator_disabled; } EOLIAN static void _elm_interface_scrollable_momentum_animator_disabled_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool disabled) { sid->momentum_animator_disabled = disabled; if (sid->momentum_animator_disabled) { if (sid->down.momentum_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } } } EOLIAN static Eina_Bool _elm_interface_scrollable_bounce_animator_disabled_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { return sid->bounce_animator_disabled; } EOLIAN static void _elm_interface_scrollable_bounce_animator_disabled_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool disabled) { sid->bounce_animator_disabled = disabled; if (sid->bounce_animator_disabled) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); } } EOLIAN static Eina_Bool _elm_interface_scrollable_wheel_disabled_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { return sid->wheel_disabled; } EOLIAN static void _elm_interface_scrollable_wheel_disabled_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool disabled) { if (!sid->event_rect) return; if ((!sid->wheel_disabled) && (disabled)) evas_object_event_callback_del_full (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb, sid); else if ((sid->wheel_disabled) && (!disabled)) evas_object_event_callback_add (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb, sid); sid->wheel_disabled = disabled; } EOLIAN static void _elm_interface_scrollable_step_size_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y) { if (x < 1) x = 1; if (y < 1) y = 1; sid->step.x = x; sid->step.y = y; _elm_scroll_scroll_bar_size_adjust(sid); } EOLIAN static void _elm_interface_scrollable_step_size_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y) { if (x) *x = sid->step.x; if (y) *y = sid->step.y; } EOLIAN static void _elm_interface_scrollable_page_size_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y) { sid->page.x = x; sid->page.y = y; _elm_scroll_scroll_bar_size_adjust(sid); } EOLIAN static void _elm_interface_scrollable_page_size_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y) { if (x) *x = sid->page.x; if (y) *y = sid->page.y; } EOLIAN static void _elm_interface_scrollable_policy_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroller_Policy hbar, Elm_Scroller_Policy vbar) { if (!sid->edje_obj) return; if ((sid->hbar_flags == hbar) && (sid->vbar_flags == vbar)) return; sid->hbar_flags = hbar; sid->vbar_flags = vbar; _elm_scroll_policy_signal_emit(sid); if (sid->cb_func.content_min_limit) sid->cb_func.content_min_limit(sid->obj, sid->min_w, sid->min_h); _elm_direction_arrows_eval(sid, EINA_TRUE); } EOLIAN static void _elm_interface_scrollable_policy_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroller_Policy *hbar, Elm_Scroller_Policy *vbar) { if (hbar) *hbar = sid->hbar_flags; if (vbar) *vbar = sid->vbar_flags; } EOLIAN static void _elm_interface_scrollable_single_direction_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroller_Single_Direction single_dir) { sid->one_direction_at_a_time = single_dir; } EOLIAN static Elm_Scroller_Single_Direction _elm_interface_scrollable_single_direction_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { return sid->one_direction_at_a_time; } EOLIAN static void _elm_interface_scrollable_content_events_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool repeat_events) { if (sid->event_rect) evas_object_repeat_events_set(sid->event_rect, repeat_events); } EOLIAN static Eina_Bool _elm_interface_scrollable_content_events_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { if (sid->event_rect) return evas_object_repeat_events_get(sid->event_rect); else return EINA_TRUE; } EOLIAN static void _elm_interface_scrollable_hold_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool hold) { sid->hold = hold; } EOLIAN static void _elm_interface_scrollable_freeze_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool freeze) { sid->freeze = freeze; sid->freeze_want = freeze; if (sid->freeze) { if (sid->down.onhold_animator) { ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); if (sid->content_info.resized) _elm_scroll_wanted_region_set(sid->obj); } } else _elm_scroll_bounce_eval(sid); } EOLIAN static void _elm_interface_scrollable_page_snap_allow_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool horiz, Eina_Bool vert) { sid->page_snap_horiz = !!horiz; sid->page_snap_vert = !!vert; } EOLIAN static void _elm_interface_scrollable_page_snap_allow_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool *horiz, Eina_Bool *vert) { if (horiz) *horiz = sid->page_snap_horiz; if (vert) *vert = sid->page_snap_vert; } EOLIAN static void _elm_interface_scrollable_bounce_allow_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool horiz, Eina_Bool vert) { sid->bounce_horiz = !!horiz; sid->bounce_vert = !!vert; } EOLIAN static void _elm_interface_scrollable_bounce_allow_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool *horiz, Eina_Bool *vert) { if (horiz) *horiz = sid->bounce_horiz; if (vert) *vert = sid->bounce_vert; } EOLIAN static void _elm_interface_scrollable_paging_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, double pagerel_h, double pagerel_v, Evas_Coord pagesize_h, Evas_Coord pagesize_v) { sid->pagerel_h = pagerel_h; sid->pagerel_v = pagerel_v; sid->pagesize_h = pagesize_h; sid->pagesize_v = pagesize_v; _elm_scroll_page_adjust(sid); } EOLIAN static void _elm_interface_scrollable_paging_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, double *pagerel_h, double *pagerel_v, Evas_Coord *pagesize_h, Evas_Coord *pagesize_v) { if (pagerel_h) *pagerel_h = sid->pagerel_h; if (pagerel_v) *pagerel_v = sid->pagerel_v; if (pagesize_h) *pagesize_h = sid->pagesize_h; if (pagesize_v) *pagesize_v = sid->pagesize_v; } EOLIAN static void _elm_interface_scrollable_page_relative_set(Eo *obj, Elm_Scrollable_Smart_Interface_Data *_pd EINA_UNUSED, double h_pagerel, double v_pagerel) { Evas_Coord pagesize_h, pagesize_v; elm_interface_scrollable_paging_get(obj, NULL, NULL, &pagesize_h, &pagesize_v); elm_interface_scrollable_paging_set(obj, h_pagerel, v_pagerel, pagesize_h, pagesize_v); } EOLIAN static void _elm_interface_scrollable_page_scroll_limit_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, int page_limit_h, int page_limit_v) { sid->page_limit_h = page_limit_h; sid->page_limit_v = page_limit_v; } EOLIAN static void _elm_interface_scrollable_page_scroll_limit_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, int *page_limit_h, int *page_limit_v) { if (page_limit_h) *page_limit_h = sid->page_limit_h; if (page_limit_v) *page_limit_v = sid->page_limit_v; } EOLIAN static void _elm_interface_scrollable_current_page_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, int *pagenumber_h, int *pagenumber_v) { Evas_Coord x, y; elm_interface_scrollable_content_pos_get(sid->obj, &x, &y); if (pagenumber_h) { if (sid->is_mirrored) x = _elm_scroll_x_mirrored_get(sid->obj, x); if (sid->pagesize_h > 0) { double result = (double)x / (double)sid->pagesize_h; double rest = result - (x / sid->pagesize_h); if (rest >= 0.5) *pagenumber_h = result + 1; else *pagenumber_h = result; } else *pagenumber_h = 0; } if (pagenumber_v) { if (sid->pagesize_v > 0) { double result = (double)y / (double)sid->pagesize_v; double rest = result - (y / sid->pagesize_v); if (rest >= 0.5) *pagenumber_v = result + 1; else *pagenumber_v = result; } else *pagenumber_v = 0; } } EOLIAN static void _elm_interface_scrollable_last_page_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, int *pagenumber_h, int *pagenumber_v) { Evas_Coord cw, ch; if (!sid->pan_obj) return; elm_obj_pan_content_size_get(sid->pan_obj, &cw, &ch); if (pagenumber_h) { if ((sid->pagesize_h > 0) && (cw > sid->pagesize_h)) *pagenumber_h = ceil((double)cw / (double)sid->pagesize_h) - 1; else *pagenumber_h = 0; } if (pagenumber_v) { if ((sid->pagesize_v > 0) && (ch > sid->pagesize_v)) *pagenumber_v = ceil((double)ch / (double)sid->pagesize_v) - 1; else *pagenumber_v = 0; } } EOLIAN static void _elm_interface_scrollable_page_show(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, int pagenumber_h, int pagenumber_v) { Evas_Coord w = 0, h = 0; Evas_Coord x = 0; Evas_Coord y = 0; sid->current_page.x = _elm_scroll_page_x_get(sid, 0, EINA_FALSE); sid->current_page.y = _elm_scroll_page_y_get(sid, 0, EINA_FALSE); elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); x = sid->pagesize_h * pagenumber_h; x = (sid->is_mirrored ? _elm_scroll_x_mirrored_get(sid->obj, x) : x); y = sid->pagesize_v * pagenumber_v; sid->wx = x; sid->wy = y; sid->ww = w; sid->wh = h; if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) elm_interface_scrollable_content_pos_set(obj, x, y, EINA_TRUE); if ((sid->current_page.x != x) || (sid->current_page.y != y)) { if (sid->cb_func.page_change) sid->cb_func.page_change(sid->obj, NULL); } } EOLIAN static void _elm_interface_scrollable_page_bring_in(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, int pagenumber_h, int pagenumber_v) { Evas_Coord w = 0, h = 0; Evas_Coord x = 0; Evas_Coord y = 0; elm_interface_scrollable_content_viewport_geometry_get (sid->obj, NULL, NULL, &w, &h); x = sid->pagesize_h * pagenumber_h; x = (sid->is_mirrored ? _elm_scroll_x_mirrored_get(sid->obj, x) : x); y = sid->pagesize_v * pagenumber_v; if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) { _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x); _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y); } } EOLIAN static void _elm_interface_scrollable_region_bring_in(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) { _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x); _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y); } } EOLIAN static void _elm_interface_scrollable_gravity_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, double x, double y) { sid->gravity_x = x; sid->gravity_y = y; elm_obj_pan_pos_max_get(sid->pan_obj, &sid->prev_cw, &sid->prev_ch); } EOLIAN static void _elm_interface_scrollable_gravity_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, double *x, double *y) { if (x) *x = sid->gravity_x; if (y) *y = sid->gravity_y; } EOLIAN static void _elm_interface_scrollable_movement_block_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Efl_Ui_Layout_Orientation block) { sid->block = block; } EOLIAN static Efl_Ui_Layout_Orientation _elm_interface_scrollable_movement_block_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid) { return sid->block; } EOLIAN static void _elm_interface_scrollable_content_loop_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool loop_h, Eina_Bool loop_v) { if (sid->loop_h == loop_h && sid->loop_v == loop_v) return; sid->loop_h = loop_h; sid->loop_v = loop_v; if (sid->loop_h) edje_object_signal_emit(sid->edje_obj, "elm,loop_x,set", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,loop_x,unset", "elm"); if (sid->loop_v) edje_object_signal_emit(sid->edje_obj, "elm,loop_y,set", "elm"); else edje_object_signal_emit(sid->edje_obj, "elm,loop_y,unset", "elm"); } EOLIAN static void _elm_interface_scrollable_content_loop_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Eina_Bool *loop_h, Eina_Bool *loop_v) { if (loop_h) *loop_h = sid->loop_h; if (loop_v) *loop_v = sid->loop_v; } EOLIAN static void _elm_interface_scrollable_efl_canvas_group_group_add(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid) { memset(sid, 0, sizeof(*sid)); sid->obj = obj; sid->x = 0; sid->y = 0; sid->w = 0; sid->h = 0; sid->step.x = 32 * elm_config_scale_get(); sid->step.y = 32 * elm_config_scale_get(); sid->page.x = -50; sid->page.y = -50; sid->page_limit_h = 9999; sid->page_limit_v = 9999; sid->hbar_flags = ELM_SCROLLER_POLICY_AUTO; sid->vbar_flags = ELM_SCROLLER_POLICY_AUTO; sid->hbar_visible = EINA_TRUE; sid->vbar_visible = EINA_TRUE; sid->loop_h = EINA_FALSE; sid->loop_v = EINA_FALSE; sid->bounce_horiz = EINA_TRUE; sid->bounce_vert = EINA_TRUE; sid->one_direction_at_a_time = ELM_SCROLLER_SINGLE_DIRECTION_SOFT; sid->momentum_animator_disabled = EINA_FALSE; sid->bounce_animator_disabled = EINA_FALSE; sid->block = EFL_UI_LAYOUT_ORIENTATION_DEFAULT; _elm_scroll_scroll_bar_reset(sid); efl_canvas_group_add(efl_super(obj, MY_SCROLLABLE_INTERFACE)); } EOLIAN static void _elm_interface_scrollable_efl_canvas_group_group_del(Eo *obj, Elm_Scrollable_Smart_Interface_Data *sid) { efl_canvas_group_del(efl_super(obj, MY_SCROLLABLE_INTERFACE)); elm_interface_scrollable_content_set(obj, NULL); if (!sid->extern_pan) evas_object_del(sid->pan_obj); ecore_idle_enterer_del(sid->down.hold_enterer); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.hold_animator, _elm_scroll_hold_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.onhold_animator, _elm_scroll_on_hold_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.momentum_animator, _elm_scroll_momentum_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_x_animator, _elm_scroll_bounce_x_animator, sid->obj); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->down.bounce_y_animator, _elm_scroll_bounce_y_animator, sid->obj); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.x.animator, _elm_scroll_scroll_to_x_animator, sid); ELM_ANIMATOR_DISCONNECT(sid->obj, sid->scrollto.y.animator, _elm_scroll_scroll_to_y_animator, sid); } EOLIAN static void _elm_interface_scrollable_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_SCROLLABLE_INTERFACE_NAME_LEGACY, klass); } /* Legacy ABI compatibility - APIs never worked and were hidden behind * EFL_EO_API_SUPPORT (from elm_interface.h) or inside internal headers. * Removed between 1.18 and 1.19. The symbols are kept purely for ABI * compatibility reasons. */ EAPI void elm_pan_gravity_set(Elm_Pan *obj EINA_UNUSED, double x EINA_UNUSED, double y EINA_UNUSED) {} EAPI void elm_pan_gravity_get(const Elm_Pan *obj EINA_UNUSED, double *x EINA_UNUSED, double *y EINA_UNUSED) {} EOLIAN static Efl_Ui_Focus_Manager* _elm_interface_scrollable_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root) { Efl_Ui_Focus_Manager *manager; manager = efl_add(EFL_UI_FOCUS_MANAGER_ROOT_FOCUS_CLASS, obj, efl_ui_focus_manager_root_set(efl_added, root) ); return manager; } EOLIAN static Efl_Object* _elm_interface_scrollable_efl_object_constructor(Eo *obj, Elm_Scrollable_Smart_Interface_Data *pd EINA_UNUSED) { Eo *o = efl_constructor(efl_super(obj, MY_SCROLLABLE_INTERFACE)); return o; } static Eina_Bool _filter_cb(const void *iterator EINA_UNUSED, void *data, void *fdata) { Eina_Rect geom; int min_x, max_x, min_y, max_y; geom = efl_ui_focus_object_focus_geometry_get(data); min_x = geom.rect.x; min_y = geom.rect.y; max_x = eina_rectangle_max_x(&geom.rect); max_y = eina_rectangle_max_y(&geom.rect); Eina_Bool inside = eina_rectangle_coords_inside(fdata, min_x, min_y) || eina_rectangle_coords_inside(fdata, min_x, max_y) || eina_rectangle_coords_inside(fdata, max_x, min_y) || eina_rectangle_coords_inside(fdata, max_x, max_y); return inside; } EOLIAN static Eina_Iterator* _elm_interface_scrollable_efl_ui_focus_manager_border_elements_get(const Eo *obj, Elm_Scrollable_Smart_Interface_Data *pd EINA_UNUSED) { Eina_Iterator *border_elements; Eina_Rectangle *rect = calloc(1, sizeof(Eina_Rectangle)); border_elements = efl_ui_focus_manager_border_elements_get(efl_super(obj, MY_SCROLLABLE_INTERFACE)); elm_interface_scrollable_content_viewport_geometry_get(obj, &rect->x, &rect->y, &rect->w, &rect->h); return eina_iterator_filter_new(border_elements, _filter_cb, free, rect); } EOLIAN static void _elm_interface_scrollable_item_loop_enabled_set(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *pd EINA_UNUSED, Eina_Bool enable EINA_UNUSED) { } EOLIAN static Eina_Bool _elm_interface_scrollable_item_loop_enabled_get(const Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *pd EINA_UNUSED) { return EINA_FALSE; } /* Internal EO APIs and hidden overrides */ #define ELM_PAN_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_pan) #define ELM_INTERFACE_SCROLLABLE_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_interface_scrollable) #include "elm_interface_scrollable.eo.c" #include "elm_pan_eo.c" diff --git a/src/lib/elementary/elm_interface_scrollable.eo b/src/lib/elementary/elm_interface_scrollable.eo index cec1536c10..2c911b8091 100644 --- a/src/lib/elementary/elm_interface_scrollable.eo +++ b/src/lib/elementary/elm_interface_scrollable.eo @@ -1,661 +1,664 @@ import efl_ui_layout_orientable; type @beta Elm_Interface_Scrollable_Cb: __undefined_type; [[Elementary interface scrollable callback type]] type @beta Elm_Interface_Scrollable_Resize_Cb: __undefined_type; [[Elementary interface scrollable resize callback type]] type @beta Elm_Interface_Scrollable_Min_Limit_Cb: __undefined_type; [[Elementary interface scrollable minimal limit callback type]] /* FIXME: Rename the namespace of the types. */ enum @beta Elm.Scroller.Policy { [[Type that controls when scrollbars should appear. See also @Elm.Interface_Scrollable.policy.set. ]] auto = 0, [[Show scrollbars as needed.]] on, [[Always show scrollbars.]] off, [[Never show scrollbars.]] last [[Internal. Sentinel value to indicate last enum field during iteration.]] } enum @beta Elm.Scroller.Single_Direction { [[Type that controls how the content is scrolled. See also @Elm.Interface_Scrollable.single_direction.set. ]] none = 0, [[Scroll every direction.]] soft, [[Scroll single direction if the direction is certain.]] hard, [[Scroll only single direction.]] last [[Internal. Sentinel value to indicate last enum field during iteration.]] } mixin @beta Elm.Interface_Scrollable requires Efl.Ui.Widget extends Efl.Ui.Focus.Manager_Sub, Efl.Ui.Widget_Focus_Manager { [[Elm scrollable mixin]] c_prefix: elm_interface_scrollable; data: Elm_Scrollable_Smart_Interface_Data; methods { @property gravity { [[Control scrolling gravity on the scrollable The gravity defines how the scroller will adjust its view when the size of the scroller contents increases. The scroller will adjust the view to glue itself as follows. x=0.0, for staying where it is relative to the left edge of the content x=1.0, for staying where it is relative to the right edge of the content y=0.0, for staying where it is relative to the top edge of the content y=1.0, for staying where it is relative to the bottom edge of the content Default values for x and y are 0.0]] set { } get { } values { x: double; [[Horizontal scrolling gravity]] y: double; [[Vertical scrolling gravity]] } } @property bounce_allow { [[Bouncing behavior When scrolling, the scroller may "bounce" when reaching an edge of the content object. This is a visual way to indicate the end has been reached. This is enabled by default for both axis. This API will set if it is enabled for the given axis with the boolean parameters for each axis.]] set { } get { } values { horiz: bool; [[Horizontal bounce policy.]] vert: bool; [[Vertical bounce policy.]] } } @property wheel_disabled { [[Control Wheel disable Enable or disable mouse wheel to be used to scroll the scroller content. heel is enabled by default.]] set { } get { } values { disabled: bool; [[$true if wheel is disabled, $false otherwise]] } } @property movement_block { [[Blocking of scrolling (per axis) This function will block scrolling movement (by input of a user) in a given direction. One can disable movements in the X axis, the Y axis or both. The default value is $none, where movements are allowed in both directions. What makes this function different from freeze_push(), hold_push() and lock_x_set() (or lock_y_set()) is that it doesn't propagate its effects to any parent or child widget of $obj. Only the target scrollable widget will be locked with regard to scrolling. ]] values { block: Efl.Ui.Layout_Orientation(Efl.Ui.Layout_Orientation.default); [[Which axis (or axes) to block]] } } @property momentum_animator_disabled { [[Momentum animator]] set { } get { } values { disabled: bool; [[$true if disabled, $false otherwise]] } } @property policy { [[Scrollbar visibility policy]] set { } get { } values { hbar: Elm.Scroller.Policy; [[Horizontal scrollbar policy]] vbar: Elm.Scroller.Policy; [[Vertical scrollbar policy]] } } @property content_region { [[Currently visible content region This gets the current region in the content object that is visible through the scroller. The region co-ordinates are returned in the $x, $y, $w, $h values pointed to. Note: All coordinates are relative to the content. See: @.content_region_show.]] set { } get { } values { x: int; [[X coordinate of the region]] y: int; [[Y coordinate of the region]] w: int; [[Width of the region]] h: int; [[Height of the region]] } } @property content_events { [[It decides whether the scrollable object propagates the events to content object or not.]] set { } get { } values { repeat_events: bool; [[$true if events are propagated, $false otherwise]] } } @property page_size { [[Scroll page size relative to viewport size. The scroller is capable of limiting scrolling by the user to "pages". That is to jump by and only show a "whole page" at a time as if the continuous area of the scroller content is split into page sized pieces. This sets the size of a page relative to the viewport of the scroller. 1.0 is "1 viewport" is size (horizontally or vertically). 0.0 turns it off in that axis. This is mutually exclusive with page size (see @.page_size for more information). Likewise 0.5 is "half a viewport". Sane usable values are normally between 0.0 and 1.0 including 1.0. If you only want 1 axis to be page "limited", use 0.0 for the other axis.]] set { } get { } values { x: int; [[The horizontal page relative size]] y: int; [[The vertical page relative size]] } } @property bounce_animator_disabled { [[Bounce animator]] set { } get { } values { disabled: bool; [[$true if bounce animation is disabled, $false otherwise]] } } @property page_scroll_limit { [[Page scroll limit]] set { } get { } values { page_limit_h: int; [[Page limit horizontal]] page_limit_v: int; [[Page limit vertical]] } } @property page_snap_allow { [[Page snapping behavior When scrolling, if a scroller is paged (see elm_scroller_page_size_set() and elm_scroller_page_relative_set()), the scroller may snap to pages when being scrolled, i.e., even if it had momentum to scroll further, it will stop at the next page boundaries. This is disabled, by default, for both axis. This function will set if it that is enabled or not, for each axis. Note: If $obj is not set to have pages, nothing will happen after this call. ]] set { } get { } values { horiz: bool; [[Allow snap horizontally]] vert: bool; [[Allow snap vertically]] } } @property paging { [[Paging property]] set { } get { } values { pagerel_h: double; [[Page relation horizontal]] pagerel_v: double; [[Page relation vertical]] pagesize_h: int; [[Page size horizontal]] pagesize_v: int; [[Page size vertical]] } } @property single_direction { [[Single direction scroll configuration This makes it possible to restrict scrolling to a single direction, with a "soft" or "hard" behavior. The hard behavior restricts the scrolling to a single direction all of the time while the soft one will restrict depending on factors such as the movement angle. If the user scrolls roughly in one direction only, it will only move according to it while if the move was clearly wanted on both axes, it will happen on both of them. ]] set { } get { } values { single_dir: Elm.Scroller.Single_Direction; [[The single direction scroll policy]] } } @property step_size { [[Step size]] set { } get { } values { x: int; [[X coordinate]] y: int; [[Y coordinate]] } } @property content_loop { [[Controls an infinite loop for a scroller. ]] set { } get { } values { loop_h: bool; [[The scrolling horizontal loop]] loop_v: bool; [[The scrolling vertical loop]] } } @property scroll_up_cb { [[Set the callback to run when the content has been moved up.]] set { } values { scroll_up_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property hbar_drag_cb { [[Set the callback to run when the horizontal scrollbar is dragged.]] set { } values { hbar_drag_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property drag_start_cb { [[Set the callback to run when dragging of the contents has started.]] set { } values { drag_start_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property freeze { [[Freeze property]] set { } values { freeze: bool; [[$true if freeze, $false otherwise]] } } @property content_viewport_resize_cb { [[When the viewport is resized, the callback is called.]] set { } values { viewport_resize_cb: Elm_Interface_Scrollable_Resize_Cb; [[The callback]] } } @property scroll_left_cb { [[Set the callback to run when the content has been moved to the left]] set { } values { scroll_left_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property vbar_press_cb { [[Set the callback to run when the vertical scrollbar is pressed.]] set { } values { vbar_press_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property hbar_press_cb { [[Set the callback to run when the horizontal scrollbar is pressed.]] set { } values { hbar_press_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property hbar_unpress_cb { [[Set the callback to run when the horizontal scrollbar is unpressed.]] set { } values { hbar_unpress_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property drag_stop_cb { [[Set the callback to run when dragging of the contents has stopped.]] set { } values { drag_stop_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property extern_pan { [[Extern pan]] set { } values { pan: Efl.Canvas.Object; [[Pan object]] } } @property page_change_cb { [[Set the callback to run when the visible page changes.]] set { } values { page_change_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property hold { [[Hold property]] set { } values { hold: bool; [[$true if hold, $false otherwise]] } } @property animate_start_cb { [[Set the callback to run when the scrolling animation has started.]] set { } values { animate_start_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property scroll_down_cb { [[Set the callback to run when the content has been moved down.]] set { } values { scroll_down_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property page_relative { [[Set scroll page size relative to viewport size.]] set { } values { h_pagerel: double; [[Page relation horizontal]] v_pagerel: double; [[Page relation vertical]] } } @property scroll_cb { [[Set the callback to run when the content has been moved.]] set { } values { scroll_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property animate_stop_cb { [[Set the callback to run when the scrolling animation has stopped.]] set { } values { animate_stop_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property content_min_limit_cb { [[set the callback to run on minimal limit content]] set { } values { min_limit_cb: Elm_Interface_Scrollable_Min_Limit_Cb; [[The callback]] } } @property scroll_right_cb { [[Set the callback to run when the content has been moved to the right.]] set { } values { scroll_right_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property scrollable_content { [[Content property]] set { } values { content: Efl.Canvas.Object; [[Content object]] } } @property edge_left_cb { [[Set the callback to run when the left edge of the content has been reached.]] set { } values { edge_left_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property vbar_drag_cb { [[Set the callback to run when the horizontal scrollbar is dragged.]] set { } values { vbar_drag_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property vbar_unpress_cb { [[Set the callback to run when the horizontal scrollbar is unpressed.]] set { } values { vbar_unpress_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property edge_bottom_cb { [[Set the callback to run when the bottom edge of the content has been reached.]] set { } values { edge_bottom_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property edge_right_cb { [[Set the callback to run when the right edge of the content has been reached.]] set { } values { edge_right_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property edge_top_cb { [[Set the callback to run when the top edge of the content has been reached.]] set { } values { edge_top_cb: Elm_Interface_Scrollable_Cb; [[The callback]] } } @property objects { [[Object property]] set { } values { edje_object: Efl.Canvas.Object; [[Edje object]] hit_rectangle: Efl.Canvas.Object; [[Evas object]] } } @property last_page { [[Scroll last page number. The page number starts from 0. 0 is the first page. This returns the last page number among the pages. See: @.current_page, @.page_show and @.page_bring_in.]] get { } values { pagenumber_h: int; [[The horizontal page number]] pagenumber_v: int; [[The vertical page number]] } } @property current_page { [[Scroll current page number. The page number starts from 0. 0 is the first page. Current page means the page which meets the top-left of the viewport. If there are two or more pages in the viewport, it returns the number of the page which meets the top-left of the viewport. See: @.last_page, @.page_show and @.page_bring_in.]] get { } values { pagenumber_h: int; [[The horizontal page number]] pagenumber_v: int; [[The vertical page number]] } } @property content_viewport_geometry { [[Content viewport geometry]] get { } values { x: int; [[X coordinate]] y: int; [[Y coordinate]] w: int; [[Width]] h: int; [[Height]] } } @property content_size { [[Get the size of the content object This gets the size of the content object of the scroller.]] get { } values { w: int; [[Width of the content object.]] h: int; [[Height of the content object.]] } } content_pos_set { [[Set the content position]] params { @in x: int; [[X coordinate]] @in y: int; [[Y coordinate]] @in sig: bool; [[Send signals to the theme corresponding to the scroll direction, or if an edge was reached.]] } } content_pos_get @const { [[Get content position]] params { @out x: int; [[X coordinate]] @out y: int; [[Y coordinate]] } } page_show { [[Show a specific virtual region within the scroller content object by page number. 0, 0 of the indicated page is located at the top-left of the viewport. This will jump to the page directly without animation. See @.page_bring_in.]] params { @in pagenumber_h: int; [[The horizontal page number]] @in pagenumber_v: int; [[The vertical page number]] } } region_bring_in { [[Show a specific virtual region within the scroller content object. This will ensure all (or part if it does not fit) of the designated region in the virtual content object (0, 0 starting at the top-left of the virtual content object) is shown within the scroller. Unlike elm_scroller_region_show(), this allows the scroller to "smoothly slide" to this location (if configuration in general calls for transitions). It may not jump immediately to the new location and make take a while and show other content along the way. See @.content_region_show]] params { @in x: int; [[X coordinate of the region]] @in y: int; [[Y coordinate of the region]] @in w: int; [[Width of the region]] @in h: int; [[Height of the region]] } } page_bring_in { [[Show a specific virtual region within the scroller content object by page number. 0, 0 of the indicated page is located at the top-left of the viewport. This will slide to the page with animation. @.page_show]] params { @in pagenumber_h: int; [[The horizontal page number]] @in pagenumber_v: int; [[The vertical page number]] } } content_region_show { [[Show a specific virtual region within the scroller content object This will ensure all (or part if it does not fit) of the designated region in the virtual content object (0, 0 starting at the top-left of the virtual content object) is shown within the scroller.]] params { @in x: int; [[X coordinate of the region]] @in y: int; [[Y coordinate of the region]] @in w: int; [[Width of the region]] @in h: int; [[Height of the region]] } } content_min_limit { [[Prevent the scrollable from being smaller than the minimum size of the content. By default the scroller will be as small as its design allows, irrespective of its content. This will make the scroller minimum size the right size horizontally and/or vertically to perfectly fit its content in that direction.]] params { @in w: bool; [[Whether to limit the minimum horizontal size]] @in h: bool; [[Whether to limit the minimum vertical size]] } } @property item_loop_enabled { [[Whether scrolling should loop around.]] values { enable: bool; [[True to enable looping.]] } } + reset_signals { + [[Call this if you have changed the group of the object passed to @.objects]] + } } implements { class.constructor; Efl.Object.constructor; Efl.Ui.Focus.Manager.border_elements { get; } Efl.Ui.Widget_Focus_Manager.focus_manager_create; Efl.Ui.I18n.mirrored { set; } } events { changed: void; [[Called when content changed]] } }