summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorm.zielonka <m.zielonka@samsung.com>2014-03-24 16:38:27 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2014-03-24 16:38:27 +0900
commit4fe82cc740eda0c6b50ee09aedcf173aac1e775d (patch)
tree65a83e288ed3d3741ae4fcdc568edfe57b8c59ac /src/modules
parentda77baab86477e741b49d1e7338e32ab402849f5 (diff)
adding focus direction for gengrid
Summary: It is implementation of focus_direction method for gengrid widget. We need it in our project. We must service focus by directions and we want to use gengrid widget. Additionally we must support changing focus by keyboard (TV remote). Unfortunately gengrid hasn't default implementation to present its sub-objects as candidates for focus direction. Our solution can search for focusable sub-objects (from realized items) and change then focused and last selected variables in gengrid. If this candidate wins then object will receive focus. Reviewers: cedric, raster CC: raster, seoz Differential Revision: https://phab.enlightenment.org/D449
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/Makefile.am3
-rw-r--r--src/modules/gengrid_focus_hook/Makefile.am38
-rw-r--r--src/modules/gengrid_focus_hook/gengrid_focus_hook.c537
3 files changed, 577 insertions, 1 deletions
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index 00fbcc05c..7b9697058 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -6,4 +6,5 @@ prefs \
6test_entry \ 6test_entry \
7test_map \ 7test_map \
8access_output \ 8access_output \
9datetime_input_ctxpopup 9datetime_input_ctxpopup \
10gengrid_focus_hook
diff --git a/src/modules/gengrid_focus_hook/Makefile.am b/src/modules/gengrid_focus_hook/Makefile.am
new file mode 100644
index 000000000..54d5b48dd
--- /dev/null
+++ b/src/modules/gengrid_focus_hook/Makefile.am
@@ -0,0 +1,38 @@
1
2MAINTAINERCLEANFILES = Makefile.in
3
4AM_CPPFLAGS = \
5-DELM_INTERNAL_API_ARGESFSDFEFC=1 \
6-I. \
7-I$(top_builddir) \
8-I$(top_srcdir) \
9-I$(top_srcdir)/src/lib \
10-I$(top_builddir)/src/lib \
11-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
12-DPACKAGE_LIB_DIR=\"$(libdir)\" \
13@ELEMENTARY_CFLAGS@ \
14@ELEMENTARY_X_CFLAGS@ \
15@ELEMENTARY_FB_CFLAGS@ \
16@ELEMENTARY_SDL_CFLAGS@ \
17@ELEMENTARY_WIN32_CFLAGS@ \
18@ELEMENTARY_WINCE_CFLAGS@ \
19@ELEMENTARY_ELOCATION_CFLAGS@ \
20@ELEMENTARY_EWEATHER_CFLAGS@ \
21@ELEMENTARY_WEB_CFLAGS@ \
22@ELEMENTARY_EMAP_CFLAGS@ \
23@ELEMENTARY_WAYLAND_CFLAGS@ \
24@ELEMENTARY_EMAP_CFLAGS@ \
25@EVIL_CFLAGS@
26
27if ELEMENTARY_WINDOWS_BUILD
28AM_CPPFLAGS += -DELEMENTARY_BUILD
29endif
30
31pkgdir = $(libdir)/elementary/modules/gengrid_focus_hook/$(MODULE_ARCH)
32pkg_LTLIBRARIES = module.la
33
34module_la_SOURCES = gengrid_focus_hook.c
35
36module_la_LIBADD = @ELEMENTARY_LIBS@ $(top_builddir)/src/lib/libelementary.la
37module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version
38module_la_LIBTOOLFLAGS = --tag=disable-static
diff --git a/src/modules/gengrid_focus_hook/gengrid_focus_hook.c b/src/modules/gengrid_focus_hook/gengrid_focus_hook.c
new file mode 100644
index 000000000..8df840041
--- /dev/null
+++ b/src/modules/gengrid_focus_hook/gengrid_focus_hook.c
@@ -0,0 +1,537 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4#include <Elementary.h>
5#include <Elementary_Cursor.h>
6#include "elm_priv.h"
7#include "elm_widget_gengrid.h"
8
9#include <limits.h>
10
11#define GG_IT(_it) (_it->item)
12
13#define EINA_INLIST_REVERSE_FOREACH_INSIDE(list, it) \
14 for (it = NULL, it = (list ? _EINA_INLIST_CONTAINER(it, list) : NULL); it; \
15 it = (EINA_INLIST_GET(it)->prev \
16 ? _EINA_INLIST_CONTAINER(it, EINA_INLIST_GET(it)->prev) \
17 : NULL))
18
19#define EINA_LIST_REVERSE_FOREACH_INSIDE(list, l, data) \
20 for (l = list, data = eina_list_data_get(l); l; \
21 l = eina_list_prev(l), data = eina_list_data_get(l))
22
23#define WEIGHT_MAX ((double)INT_MAX / (double)1000000)
24
25/**
26 * Find the first parent of object.
27 * Parent must be type of elm_gengrid.
28 *
29 * @param base
30 *
31 * @return
32 */
33static const Evas_Object *
34_find_gengrid_parent_item(const Evas_Object *base)
35{
36 const Evas_Object *res = NULL;
37 if (base)
38 {
39 const Evas_Object *parent = NULL;
40 parent = elm_widget_parent_get(base);
41
42 while (parent)
43 {
44 if (evas_object_smart_type_check(parent, "elm_gengrid"))
45 {
46 res = parent;
47 break;
48 }
49 parent = elm_widget_parent_get(parent);
50 }
51 }
52 return res;
53}
54
55static Eina_Bool
56_can_focus(const Evas_Object *obj)
57{
58 if (obj && elm_object_focus_allow_get(obj) &&
59 elm_widget_can_focus_get(obj))
60 return EINA_TRUE;
61 return EINA_FALSE;
62}
63
64
65static Eina_List *
66_get_contents(const Evas_Object *obj)
67{
68 Eina_List *res = NULL;
69 if (!obj)
70 return NULL;
71
72 if (evas_object_smart_type_check(obj, "elm_layout"))
73 {
74 Evas_Object *edje = elm_layout_edje_get(obj);
75 if (edje)
76 {
77 const Eina_List *l = NULL;
78 const char *key;
79 const Eina_List *contents =
80 elm_widget_stringlist_get(
81 edje_object_data_get(edje, "contents"));
82
83 EINA_LIST_FOREACH(contents, l, key)
84 {
85 if (key)
86 {
87 const Evas_Object *child =
88 edje_object_part_swallow_get(edje, key);
89 if (child)
90 {
91 Eina_List *l1 = NULL;
92 res = eina_list_append(res, child);
93
94 l1 = _get_contents(child);
95 if (l1)
96 {
97 res = eina_list_merge(res, l1);
98 }
99 }
100 }
101 }
102 }
103 }
104 return res;
105}
106
107static Eina_List *
108_gengrid_item_contents_get(const Elm_Gen_Item *it)
109{
110 Eina_List *res = NULL;
111 Eina_List *l = NULL;
112 Evas_Object *child = NULL;
113 EINA_LIST_FOREACH(it->content_objs, l, child)
114 {
115 if (child)
116 {
117 Eina_List *l1 = NULL;
118 res = eina_list_append(res, child);
119
120 l1 = _get_contents(child);
121 if (l1)
122 {
123 res = eina_list_merge(res, l1);
124 }
125 }
126 }
127 return res;
128}
129
130static const Evas_Object *
131_find_focusable_object(const Elm_Gen_Item *it,
132 const Evas_Object *base,
133 Elm_Focus_Direction dir)
134{
135 const Evas_Object *obj = NULL;
136 Eina_List *l = NULL;
137 Eina_List *contents = NULL;
138
139 if (!it) return NULL;
140
141 contents = _gengrid_item_contents_get(it);
142
143 if (contents)
144 {
145 if (base)
146 {
147 l = eina_list_data_find_list(contents, base);
148 obj = base;
149 }
150
151 if (dir == ELM_FOCUS_LEFT || dir == ELM_FOCUS_UP)
152 {
153 if (l)
154 l = eina_list_prev(l);
155 else
156 l = eina_list_last(contents);
157
158 while (l)
159 {
160 obj = eina_list_data_get(l);
161 if (_can_focus(obj))
162 break;
163 obj = NULL;
164 l = eina_list_prev(l);
165 }
166 }
167 else if (dir == ELM_FOCUS_RIGHT || dir == ELM_FOCUS_DOWN)
168 {
169 if (l)
170 l = eina_list_next(l);
171 else
172 l = contents;
173 while (l)
174 {
175 obj = eina_list_data_get(l);
176 if (_can_focus(obj))
177 break;
178 obj = NULL;
179 l = eina_list_next(l);
180 }
181 }
182 eina_list_free(contents);
183 }
184 return obj;
185}
186
187
188
189static Eina_Bool
190_check_item_contains(Elm_Gen_Item *it, const Evas_Object *base)
191{
192 Eina_Bool res = EINA_FALSE;
193 Eina_List *contents = NULL;
194 contents = _gengrid_item_contents_get(it);
195
196 if (contents)
197 {
198 const Eina_List *l = NULL;
199 Evas_Object *content = NULL;
200
201 // loop contents (Evas_Object) of item
202 EINA_LIST_FOREACH(contents, l, content)
203 {
204 if (content == base)
205 {
206 res = EINA_TRUE;
207 break;
208 }
209 }
210 eina_list_free(contents);
211 }
212 return res;
213}
214
215static Elm_Gen_Item *
216_find_item_for_base(const Evas_Object *obj,
217 const Evas_Object *base)
218{
219 ELM_GENGRID_CHECK(obj) EINA_FALSE;
220 ELM_GENGRID_DATA_GET(obj, sd);
221 Elm_Gen_Item *res = NULL;
222 Elm_Gen_Item *it = (Elm_Gen_Item *)(sd->last_selected_item);
223
224 if (it && _check_item_contains(it, base))
225 {
226 res = it;
227 }
228 else
229 { // try find in all
230 EINA_INLIST_FOREACH(sd->items, it)
231 {
232 if (it && _check_item_contains(it, base))
233 {
234 res = it;
235 break;
236 }
237 }
238 }
239 return res;
240}
241
242static Eina_Bool
243_gengrid_self_focus_item_get(
244 const Evas_Object *obj, const Evas_Object *base,
245 // list of Elm_Gen_Items
246 const Eina_List *items, void *(*list_data_get)(const Eina_List *l),
247 double degree, Evas_Object **direction, double *weight)
248{
249 Evas_Coord ox, oy;
250 Evas_Coord vw, vh;
251 const Evas_Object *res_obj = NULL;
252 Elm_Focus_Direction dir = ELM_FOCUS_UP;
253
254 unsigned int items_count = 0;
255 unsigned int columns = 0, items_visible = 0;
256 unsigned int items_row = 0, items_col = 0, rows = 0;
257 int new_position = 0;
258 int cx = 0;
259 int cy = 0;
260
261 int focused_pos = 0;
262
263 Elm_Gen_Item *it = NULL;
264 Elm_Gen_Item *it_res = NULL;
265 Eina_List *list = NULL;
266 Eina_List *l = NULL;
267
268 Elm_Gen_Item *focused_item = NULL;
269
270 (void)list_data_get;
271
272 ELM_GENGRID_CHECK(obj) EINA_FALSE;
273 ELM_GENGRID_DATA_GET(obj, sd);
274
275 if (!direction || !weight || !base || !items) return EINA_FALSE;
276
277 evas_object_geometry_get(sd->pan_obj, &ox, &oy, &vw, &vh);
278
279 focused_item = _find_item_for_base(obj, base);
280
281 if (!focused_item) return EINA_FALSE;
282
283
284 if (degree == 0)
285 dir = ELM_FOCUS_UP;
286 else if (degree == 90)
287 dir = ELM_FOCUS_RIGHT;
288 else if (degree == 180)
289 dir = ELM_FOCUS_DOWN;
290 else if (degree == 270)
291 dir = ELM_FOCUS_LEFT;
292 else
293 return EINA_FALSE;
294
295 res_obj = _find_focusable_object(focused_item, base, dir);
296 if (res_obj && res_obj != base)
297 {
298 *direction = (Evas_Object *)res_obj;
299 *weight = WEIGHT_MAX;
300 return EINA_TRUE;
301 }
302
303 focused_pos = focused_item->position - 1;
304
305 items_count =
306 sd->item_count - eina_list_count(sd->group_items) + sd->items_lost;
307 if (sd->horizontal)
308 {
309 if (sd->item_height > 0) items_visible = vh / sd->item_height;
310 if (items_visible < 1) items_visible = 1;
311
312 columns = items_count / items_visible;
313 if (items_count % items_visible) columns++;
314
315 items_row = items_visible;
316 if (items_row > sd->item_count) items_row = sd->item_count;
317
318 cx = focused_pos / items_row;
319 cy = focused_pos % items_row;
320 }
321 else
322 {
323 if (sd->item_width > 0) items_visible = vw / sd->item_width;
324 if (items_visible < 1) items_visible = 1;
325
326 rows = items_count / items_visible;
327 if (items_count % items_visible) rows++;
328
329 items_col = items_visible;
330 if (items_col > sd->item_count) items_col = sd->item_count;
331
332 cy = focused_pos / items_col;
333 cx = focused_pos % items_col;
334 }
335
336
337 if (dir == ELM_FOCUS_UP)
338 cy--;
339 else if (dir == ELM_FOCUS_RIGHT)
340 cx++;
341 else if (dir == ELM_FOCUS_DOWN)
342 cy++;
343 else if (dir == ELM_FOCUS_LEFT)
344 cx--;
345
346 if (cx < 0 || cy < 0) return EINA_FALSE;
347
348 if (sd->horizontal)
349 {
350 if ((cy > (items_row - 1)) || (cx > (columns - 1))) return EINA_FALSE;
351 new_position = items_row * cx + cy;
352 }
353 else
354 {
355 if ((cx > (items_col - 1)) || (cy > (rows - 1))) return EINA_FALSE;
356 new_position = cx + items_col * cy;
357 }
358
359 if (new_position > (items_count - 1)) return EINA_FALSE;
360
361 focused_pos++;
362 new_position++;
363
364 list = eina_list_data_find_list(items, focused_item);
365
366 if (!list) return EINA_FALSE;
367
368 if (new_position > focused_pos)
369 {
370 /// New position should be after focused
371 EINA_LIST_FOREACH(list, l, it)
372 {
373 if (it->position == new_position)
374 {
375 it_res = it;
376 break;
377 }
378 }
379 }
380 else if (new_position < focused_pos)
381 {
382 /// New position should be before focused
383 EINA_LIST_REVERSE_FOREACH_INSIDE(list, l, it)
384 {
385 if (it->position == new_position)
386 {
387 it_res = it;
388 break;
389 }
390 }
391 }
392 else
393 {
394 return EINA_FALSE;
395 }
396
397 if (it_res)
398 {
399 res_obj = _find_focusable_object(it_res, base, dir);
400 *direction = (Evas_Object *)res_obj;
401 *weight = WEIGHT_MAX;
402 return EINA_TRUE;
403 }
404 return EINA_FALSE;
405}
406
407static Eina_Bool
408_gengrid_focus_list_direction_get(
409 const Evas_Object *obj, const Evas_Object *base,
410 // list of Elm_Gen_Items
411 const Eina_List *items, void *(*list_data_get)(const Eina_List *l),
412 double degree, Evas_Object **direction, double *weight)
413{
414 const Eina_List *l = NULL;
415 Evas_Object *current_best = NULL;
416 (void)list_data_get;
417 ELM_GENGRID_CHECK(obj) EINA_FALSE;
418 if (!direction || !weight || !base || !items) return EINA_FALSE;
419
420 l = items;
421 current_best = *direction;
422
423 // loop items Elm_Gen_Item
424 for (; l; l = eina_list_next(l))
425 {
426 Eina_List *contents = NULL;
427 Elm_Gen_Item *it = list_data_get(l);
428 contents = _gengrid_item_contents_get(it);
429 if (contents)
430 {
431 const Eina_List *l2 = NULL;
432 Evas_Object *content = NULL;
433
434 // loop contents (Evas_Object) of item
435 EINA_LIST_FOREACH(contents, l2, content)
436 {
437 // if better element than set new sd->focused and sd->selected
438 elm_widget_focus_direction_get(content, base, degree, direction,
439 weight);
440 }
441 eina_list_free(contents);
442 }
443 }
444 if (current_best != *direction)
445 return EINA_TRUE;
446 else
447 return EINA_FALSE;
448}
449
450EAPI void
451gen_focus_direction(Eo *obj, void *_pd, va_list *list)
452{
453 Eina_List *items = NULL;
454 const Evas_Object *parent = NULL;
455
456 void *(*list_data_get)(const Eina_List * list);
457 Eina_List *(*list_free)(Eina_List * list);
458
459 Elm_Gengrid_Smart_Data *sd = _pd;
460
461 Evas_Object *base = va_arg(*list, Evas_Object *);
462 double degree = va_arg(*list, double);
463 Evas_Object **direction = va_arg(*list, Evas_Object **);
464 double *weight = va_arg(*list, double *);
465 Eina_Bool *ret = va_arg(*list, Eina_Bool *);
466
467 if (!sd)
468 {
469 *ret = EINA_FALSE;
470 return;
471 }
472
473
474 *ret = EINA_FALSE;
475 list_data_get = NULL;
476
477 Eina_Bool (*list_direction_get)(
478 const Evas_Object * obj, const Evas_Object * base,
479 const Eina_List * items, void * (*list_data_get)(const Eina_List * l),
480 double degree, Evas_Object * *direction, double * weight);
481
482 list_direction_get = NULL;
483 list_free = NULL;
484 parent = _find_gengrid_parent_item(base);
485
486 /// If focused is subobject of this gengrid then we selected next
487 /// in direction
488 if (obj == parent)
489 {
490 items = elm_gengrid_realized_items_get(obj);
491 list_data_get = NULL;
492 list_direction_get = _gengrid_self_focus_item_get;
493 list_free = eina_list_free;
494 }
495
496 if (!items)
497 {
498 items = (Eina_List *)(elm_object_focus_custom_chain_get(obj));
499 list_data_get = eina_list_data_get;
500 list_direction_get = elm_widget_focus_list_direction_get;
501 list_free = NULL;
502 }
503
504 if (!items)
505 {
506 items = elm_gengrid_realized_items_get(obj);
507 list_data_get = eina_list_data_get;
508 list_direction_get = _gengrid_focus_list_direction_get;
509 list_free = eina_list_free;
510 }
511
512 if (!items)
513 {
514 *ret = EINA_FALSE;
515 return;
516 }
517
518 *ret = list_direction_get(obj, base, items, list_data_get, degree, direction,
519 weight);
520
521 if (list_free) list_free(items);
522}
523
524// module api funcs needed
525EAPI int
526elm_modapi_init(void *m)
527{
528 (void) m;
529 return 1; // succeed always
530}
531
532EAPI int
533elm_modapi_shutdown(void *m)
534{
535 (void) m;
536 return 1; // succeed always
537}