Begins highlight on focused object.

There's still work to do here, particularly in the theme, but it has
something nice and fun to see the code working.
The idea behind this:
Window tracks focused object and sends the highlight object(s) to it. These
are simple edje objects, one on top, one below the focused widget for nice
effects. Widgets can choose to ignore the highlight and this will be sent to
the parent object, if it doesn't ignore it as well.
About the bottom object, it doesn't work now. For the most part, focused
widget will always be a member of some smart object, so stacking won't work
and the desired effect is nowhere to be seen. This will be worked out later.
To be done now:
 - Let the theme for a widget define its own highlight, disabling if needed
 the standard one for those objects.
 - Needed base in code to allow animations when switching focus. All in theme.
 - Properly test all widgets and fix some things that will most likely work
 in weird ways, given the nature of Evas/Edje and how Elementary makes use
 of them.
 - Forgot the rest, stay tuned, test, report, give ideas, plant a tree.

Work started by glima, continued with some refactors by me when he
decided he needed vacations.


SVN revision: 52524
This commit is contained in:
Iván Briano 2010-09-20 22:18:58 +00:00
parent 66707ffab4
commit 6879d7fdad
7 changed files with 492 additions and 0 deletions

View File

@ -8,6 +8,140 @@
collections {
///////////////////////////////////////////////////////////////////////////////
group { name: "elm/focus_highlight/top/default";
images {
image: "emo-unhappy.png" COMP;
}
parts {
part { name: "base";
type: RECT;
repeat_events: 1;
description { state: "default" 0.0;
rel1.relative: 0.0 0.0;
rel2.relative: 1.0 1.0;
visible: 0;
}
}
part { name: "shine";
type: IMAGE;
mouse_events: 1;
repeat_events: 1;
ignore_flags: ON_HOLD;
description { state: "default" 0.0;
image {
normal: "emo-unhappy.png";
}
rel1.to: "base";
rel1.relative: 1.0 0.0;
rel1.offset: -15 -15;
rel2.to: "base";
rel2.relative: 1.0 0.0;
rel2.offset: 14 14;
}
description { state: "disabled" 0.0;
inherit: "default" 0.0;
color: 0 0 0 0;
}
}
program { name: "show";
signal: "elm,action,focus,show";
source: "elm";
action: ACTION_STOP;
target: "hide";
target: "hide_start";
target: "hide_end";
after: "show_start";
}
program { name: "show_start";
action: STATE_SET "default" 0.0;
transition: LINEAR 0.2;
target: "shine";
after: "show_end";
}
program { name: "show_end";
action: SIGNAL_EMIT "elm,action,focus,show,end" "";
}
program { name: "hide";
signal: "elm,action,focus,hide";
source: "elm";
action: ACTION_STOP;
target: "show";
target: "show_start";
target: "show_end";
after: "hide_start";
}
program { name: "hide_start";
action: STATE_SET "disabled" 0.0;
transition: LINEAR 0.2;
target: "shine";
after: "hide_end";
}
program { name: "hide_end";
action: SIGNAL_EMIT "elm,action,focus,hide,end" "";
}
}
}
///////////////////////////////////////////////////////////////////////////////
group { name: "elm/focus_highlight/bottom/default";
parts {
part { name: "shine";
type: RECT;
mouse_events: 1;
repeat_events: 1;
ignore_flags: ON_HOLD;
description { state: "default" 0.0;
color: 0 255 0 50;
rel1.offset: 0 0;
rel2.offset: 0 0;
}
description { state: "disabled" 0.0;
inherit: "default" 0.0;
color: 0 0 0 0;
}
}
program { name: "show";
signal: "elm,action,focus,show";
source: "elm";
action: ACTION_STOP;
target: "hide";
target: "hide_start";
target: "hide_end";
after: "show_start";
}
program { name: "show_start";
action: STATE_SET "default" 0.0;
transition: LINEAR 0.2;
target: "shine";
after: "show_end";
}
program { name: "show_end";
action: SIGNAL_EMIT "elm,action,focus,show,end" "";
}
program { name: "hide";
signal: "elm,action,focus,hide";
source: "elm";
action: ACTION_STOP;
target: "show";
target: "show_start";
target: "show_end";
after: "hide_start";
}
program { name: "hide_start";
action: STATE_SET "disabled" 0.0;
transition: LINEAR 0.2;
target: "shine";
after: "hide_end";
}
program { name: "hide_end";
action: SIGNAL_EMIT "elm,action,focus,hide,end" "";
}
}
}
///////////////////////////////////////////////////////////////////////////////
group { name: "elm/bg/base/default";
images {

View File

@ -387,6 +387,8 @@ extern "C" {
EAPI void elm_win_quickpanel_priority_minor_set(Evas_Object *obj, int priority);
EAPI int elm_win_quickpanel_priority_minor_get(const Evas_Object *obj);
EAPI void elm_win_quickpanel_zone_set(Evas_Object *obj, int zone);
EAPI void elm_win_focus_highlight_enabled_set(Evas_Object *obj, Eina_Bool enabled);
EAPI Eina_Bool elm_win_focus_highlight_enabled_get(const Evas_Object *obj);
/*...
* ecore_x_icccm_hints_set -> accepts_focus (add to ecore_evas)

View File

@ -115,6 +115,8 @@ elm_box_add(Evas_Object *parent)
elm_widget_data_set(obj, wd);
elm_widget_del_hook_set(obj, _del_hook);
elm_widget_del_pre_hook_set(obj, _del_pre_hook);
elm_widget_can_focus_set(obj, 0);
elm_widget_highlight_ignore_set(obj, 1);
wd->box = evas_object_box_add(e);
/*evas_object_box_layout_set(wd->box, evas_object_box_layout_vertical,

View File

@ -1351,6 +1351,7 @@ elm_entry_add(Evas_Object *parent)
elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
elm_object_cursor_set(obj, ELM_CURSOR_XTERM);
elm_widget_can_focus_set(obj, 1);
elm_widget_highlight_ignore_set(obj, 1);
wd->linewrap = EINA_TRUE;
wd->char_linewrap= EINA_FALSE;

View File

@ -62,6 +62,7 @@ struct _Smart_Data
Eina_Bool can_focus : 1;
Eina_Bool child_can_focus : 1;
Eina_Bool focused : 1;
Eina_Bool highlight_ignore : 1;
Eina_Bool disabled : 1;
};
@ -468,6 +469,20 @@ elm_widget_can_focus_get(const Evas_Object *obj)
return 0;
}
EAPI void
elm_widget_highlight_ignore_set(Evas_Object *obj, Eina_Bool ignore)
{
API_ENTRY return;
sd->highlight_ignore = !!ignore;
}
EAPI Eina_Bool
elm_widget_highlight_ignore_get(const Evas_Object *obj)
{
API_ENTRY return EINA_FALSE;
return sd->highlight_ignore;
}
EAPI int
elm_widget_focus_get(const Evas_Object *obj)
{

View File

@ -232,6 +232,8 @@ EAPI void elm_widget_signal_callback_add(Evas_Object *obj, const cha
EAPI void *elm_widget_signal_callback_del(Evas_Object *obj, const char *emission, const char *source, void (*func) (void *data, Evas_Object *o, const char *emission, const char *source));
EAPI void elm_widget_can_focus_set(Evas_Object *obj, int can_focus);
EAPI int elm_widget_can_focus_get(const Evas_Object *obj);
EAPI void elm_widget_highlight_ignore_set(Evas_Object *obj, Eina_Bool ignore);
EAPI Eina_Bool elm_widget_highlight_ignore_get(const Evas_Object *obj);
EAPI int elm_widget_focus_get(const Evas_Object *obj);
EAPI Evas_Object *elm_widget_focused_object_get(const Evas_Object *obj);
EAPI Evas_Object *elm_widget_top_get(const Evas_Object *obj);

View File

@ -30,6 +30,17 @@ struct _Elm_Win
struct {
int x, y;
} screen;
struct {
Evas_Object *bottom, *top;
Evas_Object *target;
Ecore_Job *reconf_job;
Eina_Bool enabled : 1;
Eina_Bool changed_theme : 1;
Eina_Bool visible : 1;
} focus_highlight;
};
static const char *widtype = NULL;
@ -46,6 +57,12 @@ static void _elm_win_xwin_update(Elm_Win *win);
static void _elm_win_eval_subobjs(Evas_Object *obj);
static void _elm_win_subobj_callback_del(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _elm_win_subobj_callback_changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _elm_win_focus_highlight_init(Elm_Win *win);
static void _elm_win_focus_highlight_shutdown(Elm_Win *win);
static void _elm_win_focus_highlight_visible_set(Elm_Win *win, Eina_Bool visible);
static void _elm_win_focus_highlight_reconfigure_job_start(Elm_Win *win);
static void _elm_win_focus_highlight_reconfigure_job_stop(Elm_Win *win);
static void _elm_win_focus_highlight_reconfigure(Elm_Win *win);
Eina_List *_elm_win_list = NULL;
@ -90,6 +107,8 @@ _elm_win_focus_in(Ecore_Evas *ee)
/*NB: Why two different "focus signals" here ??? */
evas_object_smart_callback_call(win->win_obj, "focus-in", NULL); // FIXME: remove me
evas_object_smart_callback_call(win->win_obj, "focus,in", NULL);
win->focus_highlight.visible = EINA_TRUE;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
@ -103,6 +122,8 @@ _elm_win_focus_out(Ecore_Evas *ee)
if (!win) return;
evas_object_smart_callback_call(win->win_obj, "focus-out", NULL); // FIXME: remove me
evas_object_smart_callback_call(win->win_obj, "focus,out", NULL);
win->focus_highlight.visible = EINA_FALSE;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
@ -153,6 +174,9 @@ _elm_win_obj_callback_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void
// that lives under it from the handler... nasty. deferring doesn't help either
ecore_job_add(_deferred_ecore_evas_free, win->ee);
// ecore_evas_free(win->ee);
_elm_win_focus_highlight_shutdown(win);
free(win);
if ((!_elm_win_list) &&
@ -426,6 +450,267 @@ _elm_win_client_message(void *data, int type __UNUSED__, void *event)
}
#endif
static void
_elm_win_focus_target_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
Elm_Win *win = data;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_focus_target_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
Elm_Win *win = data;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_focus_target_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
Elm_Win *win = data;
win->focus_highlight.target = NULL;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_focus_target_callbacks_add(Elm_Win *win)
{
Evas_Object *obj = win->focus_highlight.target;
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
_elm_win_focus_target_move, win);
evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
_elm_win_focus_target_resize, win);
evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
_elm_win_focus_target_del, win);
}
static void
_elm_win_focus_target_callbacks_del(Elm_Win *win)
{
Evas_Object *obj = win->focus_highlight.target;
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE,
_elm_win_focus_target_move, win);
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE,
_elm_win_focus_target_resize, win);
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
_elm_win_focus_target_del, win);
}
static Evas_Object *
_elm_win_focus_target_get(Evas_Object *obj)
{
Evas_Object *o = obj;
do
{
if (elm_widget_is(o))
{
if (!elm_widget_highlight_ignore_get(o))
break;
o = elm_widget_parent_get(o);
if (!o)
o = evas_object_smart_parent_get(o);
}
else
{
o = elm_widget_parent_widget_get(o);
if (!o)
o = evas_object_smart_parent_get(o);
}
}
while (o);
return o;
}
static void
_elm_win_object_focus_in(void *data, Evas *e, void *event_info)
{
Evas_Object *obj = event_info;
Elm_Win *win = data;
if (win->focus_highlight.target == obj)
return;
win->focus_highlight.target = _elm_win_focus_target_get(obj);
_elm_win_focus_target_callbacks_add(win);
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_object_focus_out(void *data, Evas *e, void *event_info)
{
Elm_Win *win = data;
Evas_Object *obj = event_info;
if (win->focus_highlight.target != obj)
return;
_elm_win_focus_target_callbacks_del(win);
win->focus_highlight.target = NULL;
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_focus_highlight_hide(void *data __UNUSED__, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__)
{
evas_object_hide(obj);
}
static void
_elm_win_focus_highlight_init(Elm_Win *win)
{
evas_event_callback_add(win->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN,
_elm_win_object_focus_in, win);
evas_event_callback_add(win->evas,
EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT,
_elm_win_object_focus_out, win);
win->focus_highlight.target = evas_focus_get(win->evas);
win->focus_highlight.bottom = edje_object_add(win->evas);
win->focus_highlight.top = edje_object_add(win->evas);
win->focus_highlight.changed_theme = EINA_TRUE;
edje_object_signal_callback_add(win->focus_highlight.bottom,
"elm,action,focus,hide,end", "",
_elm_win_focus_highlight_hide, NULL);
edje_object_signal_callback_add(win->focus_highlight.top,
"elm,action,focus,hide,end", "",
_elm_win_focus_highlight_hide, NULL);
_elm_win_focus_highlight_reconfigure_job_start(win);
}
static void
_elm_win_focus_highlight_shutdown(Elm_Win *win)
{
if (win->focus_highlight.target)
{
_elm_win_focus_target_callbacks_del(win);
win->focus_highlight.target = NULL;
}
if (win->focus_highlight.bottom)
{
evas_object_del(win->focus_highlight.bottom);
win->focus_highlight.bottom = NULL;
}
if (win->focus_highlight.top)
{
evas_object_del(win->focus_highlight.top);
win->focus_highlight.top = NULL;
}
evas_event_callback_del_full(win->evas,
EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN,
_elm_win_object_focus_in, win);
evas_event_callback_del_full(win->evas,
EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT,
_elm_win_object_focus_out, win);
}
static void
_elm_win_focus_highlight_visible_set(Elm_Win *win, Eina_Bool visible)
{
Evas_Object *bottom, *top;
bottom = win->focus_highlight.bottom;
top = win->focus_highlight.top;
if (visible)
{
if (bottom)
{
evas_object_show(bottom);
edje_object_signal_emit(bottom, "elm,action,focus,show", "elm");
}
if (top)
{
evas_object_show(top);
edje_object_signal_emit(top, "elm,action,focus,show", "elm");
}
}
else
{
if (bottom)
edje_object_signal_emit(bottom, "elm,action,focus,hide", "elm");
if (top)
edje_object_signal_emit(top, "elm,action,focus,hide", "elm");
}
}
static void
_elm_win_focus_highlight_reconfigure_job(void *data)
{
_elm_win_focus_highlight_reconfigure((Elm_Win *)data);
}
static void
_elm_win_focus_highlight_reconfigure_job_start(Elm_Win *win)
{
if (win->focus_highlight.reconf_job)
ecore_job_del(win->focus_highlight.reconf_job);
win->focus_highlight.reconf_job = ecore_job_add(
_elm_win_focus_highlight_reconfigure_job, win);
}
static void
_elm_win_focus_highlight_reconfigure_job_stop(Elm_Win *win)
{
if (win->focus_highlight.reconf_job)
ecore_job_del(win->focus_highlight.reconf_job);
win->focus_highlight.reconf_job = NULL;
}
static void
_elm_win_focus_highlight_reconfigure(Elm_Win *win)
{
Evas_Coord tx, ty, tw, th;
Evas_Object *target = win->focus_highlight.target;
Evas_Object *bottom, *top, *clip;
_elm_win_focus_highlight_reconfigure_job_stop(win);
bottom = win->focus_highlight.bottom;
top = win->focus_highlight.top;
if (!target || !win->focus_highlight.visible)
{
_elm_win_focus_highlight_visible_set(win, EINA_FALSE);
return;
}
if (win->focus_highlight.changed_theme)
{
_elm_theme_object_set(win->win_obj, bottom, "focus_highlight", "bottom",
"default"); /* FIXME: use style */
_elm_theme_object_set(win->win_obj, top, "focus_highlight", "top",
"default");
win->focus_highlight.changed_theme = EINA_FALSE;
}
evas_object_geometry_get(target, &tx, &ty, &tw, &th);
clip = evas_object_clip_get(target);
evas_object_move(bottom, tx, ty);
evas_object_resize(bottom, tw, th);
evas_object_lower(bottom);
evas_object_clip_set(bottom, clip);
evas_object_move(top, tx, ty);
evas_object_resize(top, tw, th);
evas_object_raise(top);
evas_object_clip_set(top, clip);
_elm_win_focus_highlight_visible_set(win, EINA_TRUE);
}
/**
* Adds a window object. If this is the first window created, pass NULL as
* @p parent.
@ -1565,6 +1850,57 @@ elm_win_quickpanel_zone_set(Evas_Object *obj, int zone)
#endif
}
/**
* Set the enabled status for the focus highlight in a window
*
* This function will enable or disable the focus highlight only for the
* given window, regardless of the global setting for it
*
* @param obj The window where to enable the highlight
* @param enabled The enabled value for the highlight
*
* @ingroup Win
*/
EAPI void
elm_win_focus_highlight_enabled_set(Evas_Object *obj, Eina_Bool enabled)
{
Elm_Win *win;
ELM_CHECK_WIDTYPE(obj, widtype);
win = elm_widget_data_get(obj);
enabled = !!enabled;
if (win->focus_highlight.enabled == enabled)
return;
win->focus_highlight.enabled = enabled;
if (win->focus_highlight.enabled)
_elm_win_focus_highlight_init(win);
else
_elm_win_focus_highlight_shutdown(win);
}
/**
* Get the enabled value of the focus highlight for this window
*
* @param obj The window in which to check if the focus highlight is enabled
*
* @return EINA_TRUE if enabled, EINA_FALSE otherwise
*
* @ingroup Win
*/
EAPI Eina_Bool
elm_win_focus_highlight_enabled_get(const Evas_Object *obj)
{
Elm_Win *win;
ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
win = elm_widget_data_get(obj);
return win->focus_highlight.enabled;
}
typedef struct _Widget_Data Widget_Data;
struct _Widget_Data