[elm_conformant] Autoscroll: Scroll using Top level Scroller done.
[framework/uifw/elementary.git] / src / lib / elm_conform.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Conformant Conformant
6  * @ingroup Elementary
7  * 
8  * The aim is to provide a widget that can be used in elementary apps to 
9  * account for space taken up by the indicator & softkey windows when running 
10  * the illume2 module of E17.
11  */
12
13 typedef struct _Widget_Data Widget_Data;
14 struct _Widget_Data 
15 {
16    Evas_Object *base;
17    Evas_Object *shelf, *panel;
18    Evas_Object *content;
19 #ifdef HAVE_CONFORMANT_AUTOSCROLL
20    Evas_Object *virtualkeypad;
21    Evas_Object *focus_obj;
22    Evas_Object *scroller;
23    Evas_Coord frelx, frely;
24    int vkeypad_height;
25    Ecore_X_Virtual_Keyboard_State vkeypad_state;
26 #endif
27    Ecore_Event_Handler *prop_hdl;
28    struct {
29       Ecore_Animator *animator; // animaton timer
30       double start; // time started
31       Evas_Coord auto_x, auto_y; // desired delta
32       Evas_Coord x, y; // current delta
33    } delta;
34 };
35
36 /* local function prototypes */
37 static const char *widtype = NULL;
38 static void _del_hook(Evas_Object *obj);
39 static void _theme_hook(Evas_Object *obj);
40 static void _swallow_conformant_parts(Evas_Object *obj);
41 static void _sizing_eval(Evas_Object *obj);
42 static Eina_Bool _prop_change(void *data, int type, void *event);
43 #ifdef HAVE_CONFORMANT_AUTOSCROLL
44 static const char SIG_IMPREGION_CHANGED[] = "impregion,changed";
45 #endif
46
47 /* local functions */
48 static void 
49 _del_hook(Evas_Object *obj) 
50 {
51    Widget_Data *wd = elm_widget_data_get(obj);
52    if (!wd) return;
53    if (wd->prop_hdl) ecore_event_handler_del(wd->prop_hdl);
54    free(wd);
55 }
56
57 static void 
58 _theme_hook(Evas_Object *obj) 
59 {
60    Widget_Data *wd = elm_widget_data_get(obj);
61    if (!wd) return;
62    _elm_theme_object_set(obj, wd->base, "conformant", "base", elm_widget_style_get(obj));
63    _swallow_conformant_parts(obj);
64
65    if (wd->content)
66      edje_object_part_swallow(wd->base, "elm.swallow.content", wd->content);
67    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
68    _sizing_eval(obj);
69 }
70
71 static void 
72 _sizing_eval(Evas_Object *obj) 
73 {
74    Widget_Data *wd = elm_widget_data_get(obj);
75    Evas_Coord mw = -1, mh = -1;
76    if (!wd) return;
77    edje_object_size_min_calc(wd->base, &mw, &mh);
78    evas_object_size_hint_min_set(obj, mw, mh);
79    evas_object_size_hint_max_set(obj, -1, -1);
80 }
81
82 static void
83 _swallow_conformant_parts(Evas_Object *obj)
84 {
85 #ifdef HAVE_ELEMENTARY_X
86    Widget_Data *wd = elm_widget_data_get(obj);
87
88    Ecore_X_Window zone, xwin;
89    int sh = -1;
90
91    xwin = elm_win_xwindow_get(obj);
92    zone = ecore_x_e_illume_zone_get(xwin);
93
94    ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
95    if (sh < 0) sh = 0;
96    if(!wd->shelf)
97       wd->shelf = evas_object_rectangle_add(evas_object_evas_get(obj));
98    evas_object_color_set(wd->shelf, 0, 0, 0, 0);
99    evas_object_size_hint_min_set(wd->shelf, -1, sh);
100    evas_object_size_hint_max_set(wd->shelf, -1, sh);
101    edje_object_part_swallow(wd->base, "elm.swallow.shelf", wd->shelf);
102
103 #ifdef HAVE_CONFORMANT_AUTOSCROLL
104    wd->scroller = NULL;
105    wd->focus_obj = NULL;
106    sh = -1;
107    ecore_x_e_illume_keyboard_geometry_get(zone, NULL, NULL, NULL, &sh);
108    if (sh < 0) sh = 0;
109    wd->vkeypad_height = sh;
110    if(!wd->virtualkeypad)
111       wd->virtualkeypad= evas_object_rectangle_add(evas_object_evas_get(obj));
112    evas_object_color_set(wd->virtualkeypad, 0, 0, 0, 0);
113    evas_object_size_hint_min_set(wd->virtualkeypad, -1, sh);
114    evas_object_size_hint_max_set(wd->virtualkeypad, -1, sh);
115    edje_object_part_swallow(wd->base, "elm.swallow.virtualkeypad", wd->virtualkeypad);
116 #endif
117
118    sh = -1;
119    ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
120    if (sh < 0) sh = 0;
121    if(!wd->panel)
122       wd->panel = evas_object_rectangle_add(evas_object_evas_get(obj));
123    evas_object_color_set(wd->panel, 0, 0, 0, 0);
124    evas_object_size_hint_min_set(wd->panel, -1, sh);
125    evas_object_size_hint_max_set(wd->panel, -1, sh);
126    edje_object_part_swallow(wd->base, "elm.swallow.panel", wd->panel);
127 #endif
128 }
129
130 static void
131 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
132 {
133    Widget_Data *wd = elm_widget_data_get(data);
134    if (!wd) return;
135    _sizing_eval(data);
136 }
137
138 static void
139 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
140 {
141    Widget_Data *wd = elm_widget_data_get(obj);
142    Evas_Object *sub = event_info;
143    if (!wd) return;
144    if (sub == wd->content)
145      {
146         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
147                                             _changed_size_hints, obj);
148         wd->content = NULL;
149         _sizing_eval(obj);
150      }
151 }
152
153 #ifdef HAVE_CONFORMANT_AUTOSCROLL
154 static Evas_Object *
155 _focus_object_get(const Evas_Object *obj)
156 {
157    Evas_Object *parent = NULL, *foc = NULL;
158    parent = elm_widget_top_get(obj);
159    if (!parent) return NULL;
160    foc = elm_widget_focused_object_get(parent);
161    return foc;
162 }
163
164 static void 
165 _imp_region_show(void *data, Evas_Object *obj)
166 {
167    Evas_Coord x = 0, y = 0, w = 0, h = 0;
168    Evas_Object * immed_scroll = NULL;
169    Widget_Data *wd = elm_widget_data_get(data);
170    if (!wd) return;   
171    if (!wd->scroller) return;
172    if (!wd->focus_obj) return;
173    immed_scroll = elm_widget_imp_region_get(wd->focus_obj, &x, &y, &w, &h);
174    if(x < 0) x = 0;
175    if(y < 0) y = 0;
176    x = x + wd->frelx;
177    y = y + wd->frely;
178    //if(wd->vkeypad_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
179    elm_scroller_region_bring_in(wd->scroller, x, y, w, h);
180 }
181
182 static void
183 _imp_region_changed_cb(void *data, Evas_Object *obj, void *event_info)
184 {
185    _imp_region_show(data, obj);
186 }
187
188 static void 
189 _scroller_resize_event_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
190
191    _imp_region_show(data, obj);
192 }
193
194 static Eina_Bool
195 _focus_object_region_calc(void *data, int *kh, Eina_Bool reg_scrol_resz_evnt)
196 {
197    Evas_Coord fx = 0, fy = 0, fw = 0, fh = 0;
198    Evas_Coord sx = 0, sy = 0, sw = 0, sh = 0;
199    Evas_Coord tsx = 0, tsy = 0, tsw = 0, tsh = 0;
200
201    Widget_Data *wd = elm_widget_data_get(data);
202    if (!wd) return EINA_FALSE;
203    if (!wd->scroller) return EINA_FALSE;
204    if (!wd->focus_obj) return EINA_FALSE;
205    evas_object_geometry_get(wd->focus_obj, &fx, &fy, &fw, &fh);
206    evas_object_geometry_get(wd->scroller, &sx, &sy, &sw, &sh); 
207    elm_scroller_region_get(wd->scroller, &tsx, &tsy, &tsw, &tsh);
208    if(sy > 0)
209     {
210       fy = fy - sy;
211     }
212    wd->frelx = tsx + fx;
213    wd->frely = tsy + fy;
214 }
215
216 static Eina_Bool
217 _get_scroller(void *data, Evas_Object * foc_obj, Eina_Bool reg_scrol_resz_evnt)
218 {
219    Evas_Coord x = 0, y = 0, w = 0, h = 0;
220    Evas_Object * parent = NULL;
221    Evas_Object * immed_scroll=NULL;
222    Evas_Object * root_scroller = NULL;
223    Evas_Object *win=NULL;
224    const char * type=NULL;
225    Widget_Data *wd = elm_widget_data_get(data);
226    if (!wd) return EINA_FALSE;
227    win = elm_widget_top_get(foc_obj);
228    if (!win) return EINA_FALSE;
229    immed_scroll = elm_widget_imp_region_get(foc_obj, &x, &y, &w, &h);
230    parent = elm_object_parent_widget_get( foc_obj );  
231    do
232     {
233       type=elm_widget_type_get(parent);
234       if(!strcmp(type,"scroller"))
235         {
236           root_scroller = parent;
237          // break;
238         }
239       parent = elm_object_parent_widget_get( parent );
240     }while(parent && (parent !=win));
241    if(root_scroller)
242      wd->scroller = root_scroller;
243    else if(immed_scroll)
244      wd->scroller = immed_scroll;
245    else
246     {
247       wd->scroller = NULL;
248       return EINA_FALSE;
249     }
250    return EINA_TRUE;
251 }
252
253 static void
254 _autoscroll_mode_enable(void *data)
255 {
256    Evas_Object * focused_object=NULL;
257    Eina_Bool ret = EINA_FALSE;
258    
259    Widget_Data *wd = elm_widget_data_get(data);
260    if (!wd) return;
261    focused_object=_focus_object_get(data);
262    if(focused_object)
263     {
264       wd->focus_obj = focused_object;
265       ret = _get_scroller(data, focused_object, EINA_TRUE);
266     }
267    if(ret == EINA_TRUE)
268     {
269       ret = _focus_object_region_calc(data, NULL, EINA_TRUE);
270       evas_object_event_callback_add(wd->scroller, EVAS_CALLBACK_RESIZE, 
271                                      _scroller_resize_event_cb, data);
272       evas_object_smart_callback_add(wd->focus_obj, SIG_IMPREGION_CHANGED,
273                                       _imp_region_changed_cb, data);
274    }
275 }
276
277 static void
278 _autoscroll_mode_disable(void *data)
279 {
280    Widget_Data *wd = elm_widget_data_get(data);
281    if (!wd) return;
282    evas_object_event_callback_del(wd->scroller, EVAS_CALLBACK_RESIZE, _scroller_resize_event_cb);
283    evas_object_smart_callback_del(wd->focus_obj, SIG_IMPREGION_CHANGED, _imp_region_changed_cb);
284    wd->scroller = NULL;
285    wd->focus_obj = NULL;
286 }
287 #endif
288
289
290 static Eina_Bool
291 _prop_change(void *data, int type __UNUSED__, void *event) 
292 {
293 #ifdef HAVE_ELEMENTARY_X
294 #ifdef HAVE_CONFORMANT_AUTOSCROLL
295    int indicator_height=57;
296    Ecore_X_Virtual_Keyboard_State virt_keypad_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_UNKNOWN;
297 #endif
298    Ecore_X_Event_Window_Property *ev;
299    Widget_Data *wd = elm_widget_data_get(data);
300    if (!wd) return ECORE_CALLBACK_PASS_ON;
301    ev = event;
302    if (ev->atom == ECORE_X_ATOM_E_ILLUME_ZONE) 
303      {
304         Ecore_X_Window zone;
305         int sh = -1;
306
307         zone = ecore_x_e_illume_zone_get(ev->win);
308         ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
309 #ifdef HAVE_CONFORMANT_AUTOSCROLL
310         if (sh < 0) sh = indicator_height;
311 #else
312         if (sh < 0) sh = 0;
313 #endif
314         evas_object_size_hint_min_set(wd->shelf, -1, sh);
315         evas_object_size_hint_max_set(wd->shelf, -1, sh);
316 #ifdef HAVE_CONFORMANT_AUTOSCROLL
317         sh = -1;
318         zone = ecore_x_e_illume_zone_get(ev->win);
319         ecore_x_e_illume_keyboard_geometry_get(zone, NULL, NULL, NULL, &sh);
320         if (sh < 0) sh = 0;
321         evas_object_size_hint_min_set(wd->virtualkeypad, -1, sh);
322         evas_object_size_hint_max_set(wd->virtualkeypad, -1, sh);
323 #endif
324         sh = -1;
325         ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
326         if (sh < 0) sh = 0;
327         evas_object_size_hint_min_set(wd->panel, -1, sh);
328         evas_object_size_hint_max_set(wd->panel, -1, sh);
329      }
330    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_INDICATOR_GEOMETRY) 
331      {
332         Ecore_X_Window zone;
333         int sh = -1;
334
335         zone = ecore_x_e_illume_zone_get(ev->win);
336         ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
337         if (sh < 0) sh = 0;
338         evas_object_size_hint_min_set(wd->shelf, -1, sh);
339         evas_object_size_hint_max_set(wd->shelf, -1, sh);
340      }
341    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_SOFTKEY_GEOMETRY) 
342      {
343         Ecore_X_Window zone;
344         int sh = -1;
345
346         zone = ecore_x_e_illume_zone_get(ev->win);
347         ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
348         if (sh < 0) sh = 0;
349         evas_object_size_hint_min_set(wd->panel, -1, sh);
350         evas_object_size_hint_max_set(wd->panel, -1, sh);
351      }
352 #ifdef HAVE_CONFORMANT_AUTOSCROLL
353    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_KEYBOARD_GEOMETRY) 
354      {
355         Ecore_X_Window zone;
356         int ky = -1, kh = -1;
357
358         printf("Keyboard Geometry Changed\n");
359         zone = ecore_x_e_illume_zone_get(ev->win);
360         ecore_x_e_illume_keyboard_geometry_get(zone, NULL, &ky, NULL, &kh);
361         printf("\tGeom: %d\n", ky);
362         if (kh < 0) kh = 0;
363         if (kh == wd->vkeypad_height) return ECORE_CALLBACK_PASS_ON;
364         wd->vkeypad_height = kh;
365         evas_object_size_hint_min_set(wd->virtualkeypad, -1, kh);
366         evas_object_size_hint_max_set(wd->virtualkeypad, -1, kh);
367      }
368    else if (ev->atom == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE)
369      {
370         Ecore_X_Window zone;
371         zone = ecore_x_e_illume_zone_get(ev->win);
372         virt_keypad_state = ecore_x_e_virtual_keyboard_state_get(zone);
373         if (virt_keypad_state == wd->vkeypad_state)   return ECORE_CALLBACK_PASS_ON;
374         wd->vkeypad_state = virt_keypad_state;
375         if(wd->vkeypad_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
376           {
377              _autoscroll_mode_enable(data);
378           }
379         else if(wd->vkeypad_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF)
380           {
381              _autoscroll_mode_disable(data);
382           }
383      }
384 #endif
385 #endif
386
387    return ECORE_CALLBACK_PASS_ON;
388 }
389
390 /**
391  * Add a new Conformant object
392  * 
393  * @param parent The parent object
394  * @return The new conformant object or NULL if it cannot be created
395  * 
396  * @ingroup Conformant
397  */
398 EAPI Evas_Object *
399 elm_conformant_add(Evas_Object *parent) 
400 {
401    Evas_Object *obj;
402    Evas *evas;
403    Widget_Data *wd;
404
405    wd = ELM_NEW(Widget_Data);
406
407    evas = evas_object_evas_get(parent);
408
409    obj = elm_widget_add(evas);
410    ELM_SET_WIDTYPE(widtype, "conformant");
411    elm_widget_type_set(obj, "conformant");
412    elm_widget_sub_object_add(parent, obj);
413    elm_widget_data_set(obj, wd);
414    elm_widget_del_hook_set(obj, _del_hook);
415    elm_widget_theme_hook_set(obj, _theme_hook);
416
417    wd->base = edje_object_add(evas);
418    _elm_theme_object_set(obj, wd->base, "conformant", "base", "default");
419    elm_widget_resize_object_set(obj, wd->base);
420    _swallow_conformant_parts(obj);
421
422 #ifdef HAVE_ELEMENTARY_X
423    wd->prop_hdl = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY,
424                                           _prop_change, obj);
425 #ifdef HAVE_CONFORMANT_AUTOSCROLL
426    wd->vkeypad_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
427 #endif
428 #endif
429
430    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
431
432    _sizing_eval(obj);
433    return obj;
434 }
435
436 /**
437  * Set the content of the conformant widget
438  *
439  * Once the content object is set, a previously set one will be deleted.
440  * If you want to keep that old content object, use the
441  * elm_conformat_content_unset() function.
442  *
443  * @param obj The conformant object
444  * @return The content that was being used
445  *
446  * @ingroup Conformant
447  */
448 EAPI void
449 elm_conformant_content_set(Evas_Object *obj, Evas_Object *content)
450 {
451    ELM_CHECK_WIDTYPE(obj, widtype);
452    Widget_Data *wd = elm_widget_data_get(obj);
453    if (!wd) return;
454    if (wd->content == content) return;
455    if (wd->content) evas_object_del(wd->content);
456    wd->content = content;
457    if (content)
458      {
459         elm_widget_sub_object_add(obj, content);
460         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
461                                        _changed_size_hints, obj);
462         edje_object_part_swallow(wd->base, "elm.swallow.content", content);
463      }
464    _sizing_eval(obj);
465 }
466
467 /**
468  * Unset the content of the conformant widget
469  *
470  * Unparent and return the content object which was set for this widget;
471  *
472  * @param obj The conformant object
473  * @return The content that was being used
474  *
475  * @ingroup Conformant
476  */
477 EAPI Evas_Object *
478 elm_conformant_content_unset(Evas_Object *obj)
479 {
480    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
481    Widget_Data *wd = elm_widget_data_get(obj);
482    Evas_Object *content;
483    if (!wd) return NULL;
484    if (!wd->content) return NULL;
485    content = wd->content;
486    elm_widget_sub_object_del(obj, wd->content);
487    edje_object_part_unswallow(wd->base, wd->content);
488    wd->content = NULL;
489    return content;
490 }