efl_ui_relative_layout: introduce new relative container

Summary:
Efl.Ui.Relative_Layout is a container which allows you to position and size with
relation to each other.
it is possible to position and size using relation like edje even though
you don't know a edc script.
Position and size can be changed dynamically using widget APIs.

@feature

ref T5487

Test Plan:
make check
examples
elementary_test -to 'efl.ui.relative_layout'

Reviewers: cedric, Hermet, Jaehyun_Cho, zmike, bu5hm4n, jpeg, segfaultxavi

Reviewed By: Jaehyun_Cho, segfaultxavi

Subscribers: segfaultxavi, kimcinoo

Tags: #efl

Maniphest Tasks: T5487

Differential Revision: https://phab.enlightenment.org/D7524
This commit is contained in:
Yeongjong Lee 2019-02-22 19:47:47 +09:00 committed by Jaehyun Cho
parent 6716feb108
commit 3118bfc34b
18 changed files with 1463 additions and 4 deletions

View File

@ -133,6 +133,7 @@ elm_public_eolian_files = \
lib/elementary/efl_ui_text_part.eo \
lib/elementary/efl_ui_caching_factory.eo \
lib/elementary/efl_ui_widget_factory.eo \
lib/elementary/efl_ui_relative_layout.eo \
$(NULL)
# More public files -- FIXME
@ -455,7 +456,8 @@ includesunstable_HEADERS = \
lib/elementary/efl_page_indicator_icon.h \
lib/elementary/efl_ui_tab_pager_private.h \
lib/elementary/efl_ui_tab_bar_private.h \
lib/elementary/efl_ui_tab_page_private.h
lib/elementary/efl_ui_tab_page_private.h \
lib/elementary/efl_ui_relative_layout_private.h
includesunstabledir = $(includedir)/elementary-@VMAJ@
nodist_includesunstable_HEADERS = \
@ -893,6 +895,7 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/efl_ui_homogeneous_model.c \
lib/elementary/efl_ui_exact_model.c \
lib/elementary/efl_ui_average_model.c \
lib/elementary/efl_ui_relative_layout.c \
$(NULL)
@ -1111,6 +1114,7 @@ bin/elementary/test_gesture_framework.c \
bin/elementary/test_ui_tab_pager.c \
bin/elementary/test_ui_pager.c \
bin/elementary/test_ui_pager_scroll.c \
bin/elementary/test_ui_relative_layout.c \
bin/elementary/test.h
bin_elementary_elementary_test_LDADD = @USE_ELEMENTARY_LIBS@
@ -1620,6 +1624,7 @@ tests_elementary_efl_ui_suite_SOURCES = \
tests/elementary/efl_ui_test_focus.c \
tests/elementary/efl_ui_test_focus_sub.c \
tests/elementary/efl_ui_test_box.c \
tests/elementary/efl_ui_test_relative_layout.c \
tests/elementary/efl_ui_test_grid.c \
tests/elementary/efl_ui_test_image.c \
tests/elementary/efl_ui_test_image_zoomable.c \

View File

@ -155,6 +155,7 @@ elementary_test_src = [
'test_win_indicator.c',
'test_gesture_framework.c',
'test_ui_tab_pager.c',
'test_ui_relative_layout.c',
'test.h'
]

View File

@ -377,6 +377,8 @@ void test_ui_tab_pager(void *data, Evas_Object *obj, void *event_info);
void test_ui_pager(void *data, Evas_Object *obj, void *event_info);
void test_ui_pager_scroll(void *data, Evas_Object *obj, void *event_info);
void test_ui_relative_layout(void *data, Evas_Object *obj, void *event_info);
static void _list_udpate(void);
static Evas_Object *win, *tbx, *entry; // TODO: refactoring
@ -842,6 +844,7 @@ add_tests:
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table", test_ui_table);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table (Linear API)", test_ui_table_linear);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table_Static", test_ui_table_static);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Relative_Layout", test_ui_relative_layout);
//------------------------------//
ADD_TEST_EO(NULL, "Events", "Event Refeed", test_events);

View File

@ -0,0 +1,319 @@
#include "test.h"
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
static Eo *layout, *btn1, *btn2, *btn3;
typedef enum {
LEFT,
RIGHT,
TOP,
BOTTOM
} Options;
static void
_btn_clicked_to_cb(void *data, const Efl_Event *event)
{
Eo *to, *btn, *obj = event->object;
Options opt = (Options)data;
double relative;
btn = efl_key_wref_get(obj, "btn");
to = efl_key_wref_get(obj, "to");
do
{
if (to == layout)
to = btn1;
else if (to == btn1)
to = btn2;
else if (to == btn2)
to = btn3;
else if (to == btn3)
to = layout;
}
while (btn == to);
efl_key_wref_set(obj, "to", to);
switch (opt)
{
case LEFT:
efl_ui_relative_layout_relation_left_get(layout, btn, NULL, &relative);
efl_ui_relative_layout_relation_left_set(layout, btn, to, relative);
break;
case RIGHT:
efl_ui_relative_layout_relation_right_get(layout, btn, NULL, &relative);
efl_ui_relative_layout_relation_right_set(layout, btn, to, relative);
break;
case TOP:
efl_ui_relative_layout_relation_top_get(layout, btn, NULL, &relative);
efl_ui_relative_layout_relation_top_set(layout, btn, to, relative);
break;
case BOTTOM:
efl_ui_relative_layout_relation_bottom_get(layout, btn, NULL, &relative);
efl_ui_relative_layout_relation_bottom_set(layout, btn, to, relative);
break;
}
efl_text_set(obj, ((to == layout) ? "parent" : (char *)efl_text_get(to)));
efl_pack_layout_request(layout);
}
static void
_slider_changed_relative_cb(void *data, const Efl_Event *event)
{
Options opt = (Options)data;
Eo *btn, *slider = event->object;
double val;
btn = efl_key_wref_get(slider, "btn");
val = efl_ui_range_value_get(slider);
switch (opt)
{
case LEFT:
efl_ui_relative_layout_relation_left_set(layout, btn, NULL, val);
break;
case RIGHT:
efl_ui_relative_layout_relation_right_set(layout, btn, NULL, val);
break;
case TOP:
efl_ui_relative_layout_relation_top_set(layout, btn, NULL, val);
break;
case BOTTOM:
efl_ui_relative_layout_relation_bottom_set(layout, btn, NULL, val);
break;
}
efl_pack_layout_request(layout);
}
static void
_slider_changed_align_cb(void *data, const Efl_Event *event)
{
char opt = (char)(uintptr_t)data;
Eo *btn, *slider = event->object;
double val, x, y;
btn = efl_key_wref_get(slider, "btn");
val = efl_ui_range_value_get(slider);
efl_gfx_hint_align_get(btn, &x, &y);
if (opt == 'x')
efl_gfx_hint_align_set(btn, val, y);
else if (opt == 'y')
efl_gfx_hint_align_set(btn, x, val);
efl_pack_layout_request(layout);
}
static void
_setter_add(Eo *vbox, Eo *btn, Options option)
{
Eo *to, *hbox;
char *text, *btn_text;
double relative;
switch (option)
{
case LEFT:
text = "left";
efl_ui_relative_layout_relation_left_get(layout, btn, &to, &relative);
break;
case RIGHT:
text = "right";
efl_ui_relative_layout_relation_right_get(layout, btn, &to, &relative);
break;
case TOP:
text = "top";
efl_ui_relative_layout_relation_top_get(layout, btn, &to, &relative);
break;
case BOTTOM:
text = "bottom";
efl_ui_relative_layout_relation_bottom_get(layout, btn, &to, &relative);
break;
}
btn_text = ((to == layout) ? "parent" : (char *)efl_text_get(to));
hbox = efl_add(EFL_UI_BOX_CLASS, vbox,
efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL),
efl_pack_padding_set(efl_added, 2, 2, EINA_TRUE),
efl_pack(vbox, efl_added));
efl_add(EFL_UI_TEXT_CLASS, hbox,
efl_text_set(efl_added, text),
efl_text_interactive_editable_set(efl_added, EINA_FALSE),
efl_text_valign_set(efl_added, 0.5),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(40, 0)),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_BUTTON_CLASS, hbox,
efl_text_set(efl_added, btn_text),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(60, 0)),
efl_key_wref_set(efl_added, "to", to),
efl_key_wref_set(efl_added, "btn", btn),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED, _btn_clicked_to_cb, (void *)option),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_SLIDER_CLASS, hbox,
efl_ui_range_min_max_set(efl_added, 0.0, 1.0),
efl_ui_range_step_set(efl_added, 0.1),
efl_ui_range_value_set(efl_added, relative),
efl_key_wref_set(efl_added, "btn", btn),
efl_event_callback_add(efl_added, EFL_UI_SLIDER_EVENT_CHANGED, _slider_changed_relative_cb, (void *)option),
efl_pack(hbox, efl_added));
}
static void
_button_frame_add(Eo *box, Eo *btn)
{
Eo *f, *vbox, *hbox;
double align_x, align_y;
f = efl_add(EFL_UI_FRAME_CLASS, box,
efl_text_set(efl_added, efl_text_get(btn)),
efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0),
efl_pack(box, efl_added));
vbox = efl_add(EFL_UI_BOX_CLASS, f,
efl_ui_direction_set(efl_added, EFL_UI_DIR_VERTICAL),
efl_pack_padding_set(efl_added, 2, 2, EINA_TRUE),
efl_gfx_hint_margin_set(efl_added, 2, 2, 2, 2),
efl_content_set(f, efl_added));
hbox = efl_add(EFL_UI_BOX_CLASS, vbox,
efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL),
efl_pack_padding_set(efl_added, 2, 2, EINA_TRUE),
efl_pack(vbox, efl_added));
efl_add(EFL_CANVAS_RECTANGLE_CLASS, hbox,
efl_gfx_color_set(efl_added, 0, 0, 0, 0),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(40, 0)),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_TEXT_CLASS, hbox,
efl_text_set(efl_added, "to"),
efl_text_interactive_editable_set(efl_added, EINA_FALSE),
efl_text_halign_set(efl_added, 0.5),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(60, 0)),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_TEXT_CLASS, hbox,
efl_text_set(efl_added, "relative"),
efl_text_halign_set(efl_added, 0.5),
efl_text_interactive_editable_set(efl_added, EINA_FALSE),
efl_pack(hbox, efl_added));
_setter_add(vbox, btn, LEFT);
_setter_add(vbox, btn, RIGHT);
_setter_add(vbox, btn, TOP);
_setter_add(vbox, btn, BOTTOM);
/* align setter */
efl_gfx_hint_align_get(btn, &align_x, &align_y);
hbox = efl_add(EFL_UI_BOX_CLASS, vbox,
efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL),
efl_pack_padding_set(efl_added, 2, 2, EINA_TRUE),
efl_pack(vbox, efl_added));
efl_add(EFL_UI_TEXT_CLASS, hbox,
efl_text_set(efl_added, "align_x"),
efl_text_interactive_editable_set(efl_added, EINA_FALSE),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(40, 0)),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_SLIDER_CLASS, hbox,
efl_ui_range_min_max_set(efl_added, 0.0, 1.0),
efl_ui_range_step_set(efl_added, 0.1),
efl_ui_range_value_set(efl_added, align_x),
efl_key_wref_set(efl_added, "btn", btn),
efl_event_callback_add(efl_added, EFL_UI_SLIDER_EVENT_CHANGED, _slider_changed_align_cb, (void *)'x'),
efl_pack(hbox, efl_added));
hbox = efl_add(EFL_UI_BOX_CLASS, vbox,
efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL),
efl_pack_padding_set(efl_added, 2, 2, EINA_TRUE),
efl_pack(vbox, efl_added));
efl_add(EFL_UI_TEXT_CLASS, hbox,
efl_text_set(efl_added, "align_y"),
efl_text_interactive_editable_set(efl_added, EINA_FALSE),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(40, 0)),
efl_gfx_hint_weight_set(efl_added, 0, EFL_GFX_HINT_EXPAND),
efl_pack(hbox, efl_added));
efl_add(EFL_UI_SLIDER_CLASS, hbox,
efl_ui_range_min_max_set(efl_added, 0.0, 1.0),
efl_ui_range_step_set(efl_added, 0.1),
efl_ui_range_value_set(efl_added, align_y),
efl_key_wref_set(efl_added, "btn", btn),
efl_event_callback_add(efl_added, EFL_UI_SLIDER_EVENT_CHANGED, _slider_changed_align_cb, (void *)'y'),
efl_pack(hbox, efl_added));
}
void
test_ui_relative_layout(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
Eo *win, *vbox, *f, *hbox;
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC),
efl_text_set(efl_added, "Efl.Ui.Relative_Layout"),
efl_ui_win_autodel_set(efl_added, EINA_TRUE));
vbox = efl_add(EFL_UI_BOX_CLASS, win,
efl_ui_direction_set(efl_added, EFL_UI_DIR_VERTICAL),
efl_pack_padding_set(efl_added, 10, 10, EINA_TRUE),
efl_gfx_hint_margin_set(efl_added, 5, 5, 5, 5),
efl_content_set(win, efl_added));
/* controls */
f = efl_add(EFL_UI_FRAME_CLASS, vbox,
efl_text_set(efl_added, "Controls"),
efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0),
efl_pack(vbox, efl_added));
hbox = efl_add(EFL_UI_BOX_CLASS, f,
efl_ui_direction_set(efl_added, EFL_UI_DIR_HORIZONTAL),
efl_pack_padding_set(efl_added, 10, 0, EINA_TRUE),
efl_content_set(f, efl_added));
/* contents */
f = efl_add(EFL_UI_FRAME_CLASS, vbox,
efl_text_set(efl_added, "Contents"),
efl_pack(vbox, efl_added));
layout = efl_add(EFL_UI_RELATIVE_LAYOUT_CLASS, f,
efl_content_set(f, efl_added));
btn1 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "button1"),
efl_gfx_hint_align_set(efl_added, 0.0, 0.0),
efl_ui_relative_layout_relation_right_set(layout, efl_added, layout, 0.0),
efl_ui_relative_layout_relation_bottom_set(layout, efl_added, layout, 0.0));
btn2 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "button2"),
efl_gfx_hint_align_set(efl_added, 0.5, 0.0),
efl_ui_relative_layout_relation_left_set(layout, efl_added, btn1, 1.0),
efl_ui_relative_layout_relation_bottom_set(layout, efl_added, layout, 0.0));
btn3 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "button3"),
efl_ui_relative_layout_relation_left_set(layout, efl_added, btn2, 0.0),
efl_ui_relative_layout_relation_top_set(layout, efl_added, btn2, 1.0));
_button_frame_add(hbox, btn1);
_button_frame_add(hbox, btn2);
_button_frame_add(hbox, btn3);
efl_gfx_entity_size_set(win, EINA_SIZE2D(600, 400));
efl_gfx_entity_visible_set(win, EINA_TRUE);
}

View File

@ -125,7 +125,9 @@ elementary/efl_ui_list_view_example_2.c \
elementary/efl_ui_list_view_example_3.c \
elementary/efl_canvas_layout_text.c \
elementary/efl_ui_theme_example_01.c \
elementary/efl_ui_theme_example_02.c
elementary/efl_ui_theme_example_02.c \
elementary/efl_ui_relative_layout_example_01.c \
elementary/efl_ui_relative_layout_example_02.c
ELM_SRCS += \
elementary/bg_cxx_example_01.cc \
@ -344,7 +346,9 @@ elementary/efl_ui_list_view_example_2 \
elementary/efl_ui_list_view_example_3 \
elementary/efl_canvas_layout_text \
elementary/efl_ui_theme_example_01 \
elementary/efl_ui_theme_example_02
elementary/efl_ui_theme_example_02 \
elementary/efl_ui_relative_layout_example_01 \
elementary/efl_ui_relative_layout_example_02
#benchmark3d
#sphere-hunter

View File

@ -169,3 +169,5 @@
/efl_ui_list_view_example_3
/efl_ui_theme_example_01
/efl_ui_theme_example_02
/efl_ui_relative_layout_example_01
/efl_ui_relative_layout_example_02

View File

@ -0,0 +1,51 @@
// gcc -o efl_ui_relative_layout_example_01 efl_ui_relative_layout_example_01.c `pkg-config --cflags --libs elementary`
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#else
#define EFL_BETA_API_SUPPORT 1
#define EFL_EO_API_SUPPORT 1
#endif
#include <Elementary.h>
#include <Efl.h>
EAPI_MAIN int
elm_main(int argc, char **argv)
{
Eo *win, *layout, *btn1, *btn2, *btn3;
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC),
efl_text_set(efl_added, "Efl.Ui.Relative_Layout"),
efl_ui_win_autodel_set(efl_added, EINA_TRUE));
layout = efl_add(EFL_UI_RELATIVE_LAYOUT_CLASS, win,
efl_content_set(win, efl_added));
btn1 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "btn1"),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(100, 100)),
efl_gfx_hint_align_set(efl_added, 0, 0),
efl_gfx_hint_margin_set(efl_added, 10, 30, 20, 40),
efl_ui_relative_layout_relation_right_set(layout, efl_added, layout, 0.0),
efl_ui_relative_layout_relation_bottom_set(layout, efl_added, layout, 0.0));
btn2 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "btn2"),
efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(0, 200)),
efl_gfx_hint_align_set(efl_added, 0.5, 0),
efl_ui_relative_layout_relation_left_set(layout, efl_added, btn1, 1.0),
efl_ui_relative_layout_relation_bottom_set(layout, efl_added, layout, 0.0));
btn3 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "btn3"),
efl_ui_relative_layout_relation_left_set(layout, efl_added, btn2, 0.0),
efl_ui_relative_layout_relation_top_set(layout, efl_added, btn2, 1.0));
efl_gfx_entity_size_set(win, EINA_SIZE2D(300, 300));
elm_run();
return 0;
}
ELM_MAIN()

View File

@ -0,0 +1,45 @@
// gcc -o efl_ui_relative_layout_example_02 efl_ui_relative_layout_example_02.c `pkg-config --cflags --libs elementary`
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#else
#define EFL_BETA_API_SUPPORT 1
#define EFL_EO_API_SUPPORT 1
#endif
#include <Elementary.h>
#include <Efl.h>
EAPI_MAIN int
elm_main(int argc, char **argv)
{
Eo *win, *layout, *btn1, *btn2;
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC),
efl_text_set(efl_added, "Efl.Ui.Relative_Layout"),
efl_ui_win_autodel_set(efl_added, EINA_TRUE));
layout = efl_add(EFL_UI_RELATIVE_LAYOUT_CLASS, win,
efl_content_set(win, efl_added));
btn1 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "btn1"),
efl_gfx_size_hint_min_set(efl_added, EINA_SIZE2D(100, 100)));
btn2 = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_text_set(efl_added, "btn2"),
efl_gfx_size_hint_min_set(efl_added, EINA_SIZE2D(0, 100)));
efl_ui_relative_layout_relation_right_set(layout, btn1, btn2, 0.0);
efl_ui_relative_layout_relation_bottom_set(layout, btn1, btn2, 0.0);
efl_ui_relative_layout_relation_left_set(layout, btn2, btn1, 1.0);
efl_ui_relative_layout_relation_top_set(layout, btn2, btn1, 1.0);
efl_gfx_entity_size_set(win, EINA_SIZE2D(300, 300));
elm_run();
return 0;
}
ELM_MAIN()

View File

@ -188,6 +188,7 @@ EAPI void efl_ui_focus_relation_free(Efl_Ui_Focus_Relations *rel);
# include <efl_ui_win.eo.h>
# include <efl_ui_win_inlined.eo.h>
# include <efl_ui_win_socket.eo.h>
# include <efl_ui_relative_layout.eo.h>
/* FIXME: Efl.Ui.Text must not use elm_general.h */
# warning Efl.Ui.Text is not available yet without Elementary.h

View File

@ -360,6 +360,7 @@ EAPI void efl_ui_focus_relation_free(Efl_Ui_Focus_Relations *rel);
# include <efl_ui_navigation_bar_part_back_button.eo.h>
# include <efl_ui_navigation_layout.eo.h>
# include <efl_ui_stack.eo.h>
# include <efl_ui_relative_layout.eo.h>
# ifndef _EFL_UI_PAGER_EO_CLASS_TYPE
# define _EFL_UI_PAGER_EO_CLASS_TYPE

View File

@ -0,0 +1,552 @@
#include "efl_ui_relative_layout_private.h"
#define MY_CLASS EFL_UI_RELATIVE_LAYOUT_CLASS
#define MY_CLASS_NAME "Efl.Ui.Relative_Layout"
#define LEFT 0
#define RIGHT 1
#define TOP 2
#define BOTTOM 3
#define START (axis ? TOP : LEFT)
#define END (axis ? BOTTOM : RIGHT)
static void _child_calc(Efl_Ui_Relative_Layout_Child *child, Eina_Bool axis);
static int
_chain_sort_cb(const void *l1, const void *l2)
{
Efl_Ui_Relative_Layout_Calc *calc1, *calc2;
calc1 = EINA_INLIST_CONTAINER_GET(l1, Efl_Ui_Relative_Layout_Calc);
calc2 = EINA_INLIST_CONTAINER_GET(l2, Efl_Ui_Relative_Layout_Calc);
return calc2->comp_factor <= calc1->comp_factor ? -1 : 1;
}
static Efl_Ui_Relative_Layout_Child *
_efl_ui_relative_layout_register(Efl_Ui_Relative_Layout_Data *pd, Eo *child)
{
Efl_Ui_Relative_Layout_Child *rc;
rc = calloc(1, sizeof(Efl_Ui_Relative_Layout_Child));
if (!rc) return NULL;
rc->obj = child;
rc->layout = pd->obj;
rc->rel[LEFT].to = rc->layout;
rc->rel[LEFT].relative = 0.0;
rc->rel[RIGHT].to = rc->layout;
rc->rel[RIGHT].relative = 1.0;
rc->rel[TOP].to = rc->layout;
rc->rel[TOP].relative = 0.0;
rc->rel[BOTTOM].to = rc->layout;
rc->rel[BOTTOM].relative = 1.0;
if (pd->obj == child)
{
rc->calc.state[0] = RELATIVE_CALC_DONE;
rc->calc.state[1] = RELATIVE_CALC_DONE;
rc->calc.chain_state[0] = RELATIVE_CALC_DONE;
rc->calc.chain_state[1] = RELATIVE_CALC_DONE;
}
else
{
efl_ui_widget_sub_object_add(pd->obj, child);
efl_canvas_group_member_add(pd->obj, child);
efl_canvas_group_change(pd->obj);
}
eina_hash_add(pd->children, &child, rc);
return rc;
}
static Efl_Ui_Relative_Layout_Child *
_relative_child_get(Efl_Ui_Relative_Layout_Data *pd, Eo *child)
{
Efl_Ui_Relative_Layout_Child *rc;
rc = eina_hash_find(pd->children, &child);
if (!rc)
rc = _efl_ui_relative_layout_register(pd, child);
return rc;
}
static Efl_Ui_Relative_Layout_Child *
_relative_child_find(const Eina_Hash *children, Eo *target)
{
Efl_Ui_Relative_Layout_Child *child;
child = eina_hash_find(children, &target);
if (!child)
ERR("target(%p(%s)) is not registered", target, efl_class_name_get(target));
return child;
}
static void
_child_aspect_calc(Efl_Ui_Relative_Layout_Child *child, Eina_Bool axis)
{
Efl_Ui_Relative_Layout_Calc *calc = &child->calc;
int temph;
if ((calc->aspect[0] <= 0) || (calc->aspect[1] <= 0))
{
ERR("Invalid aspect parameter for obj(%p), aspect(%d, %d) ",
child->obj, calc->aspect[0], calc->aspect[1]);
return;
}
switch (calc->aspect_type)
{
case EFL_GFX_HINT_ASPECT_HORIZONTAL:
if (axis) _child_calc(child, !axis);
calc->want[1].length = calc->want[0].length * calc->aspect[1] / calc->aspect[0];
break;
case EFL_GFX_HINT_ASPECT_VERTICAL:
if (!axis) _child_calc(child, !axis);
calc->want[0].length = calc->want[1].length * calc->aspect[0] / calc->aspect[1];
break;
case EFL_GFX_HINT_ASPECT_BOTH:
if (calc->state[!axis] != RELATIVE_CALC_ON)
_child_calc(child, !axis);
temph = calc->want[axis].length * calc->aspect[!axis] / calc->aspect[axis];
if (temph > calc->want[!axis].length)
{
temph = calc->want[!axis].length;
calc->want[axis].length = temph * calc->aspect[axis] / calc->aspect[!axis];
}
else
calc->want[!axis].length = temph;
break;
default:
if (calc->state[!axis] != RELATIVE_CALC_ON)
_child_calc(child, !axis);
temph = calc->want[axis].length * calc->aspect[!axis] / calc->aspect[axis];
if (temph < calc->want[!axis].length)
{
temph = calc->want[!axis].length;
calc->want[axis].length = temph * calc->aspect[axis] / calc->aspect[!axis];
}
else
calc->want[!axis].length = temph;
}
//calculate max size
if (calc->want[0].length > calc->max[0])
{
calc->want[0].length = calc->max[0];
calc->want[1].length = calc->want[0].length * calc->aspect[1] / calc->aspect[0];
}
if (calc->want[1].length > calc->max[1])
{
calc->want[1].length = calc->max[1];
calc->want[0].length = calc->want[1].length * calc->aspect[0] / calc->aspect[1];
}
//calculate min size
if (calc->want[0].length < calc->min[0])
{
calc->want[0].length = calc->min[0];
calc->want[1].length = calc->want[0].length * calc->aspect[1] / calc->aspect[0];
}
if (calc->want[1].length < calc->min[1])
{
calc->want[1].length = calc->min[1];
calc->want[0].length = calc->want[1].length * calc->aspect[0] / calc->aspect[1];
}
//calculate align
calc->want[!axis].position =
calc->space[!axis].position +
(calc->space[!axis].length - calc->want[!axis].length) * calc->align[!axis];
}
static Eina_Bool
_child_chain_calc(Efl_Ui_Relative_Layout_Child *child, Eina_Bool axis)
{
Efl_Ui_Relative_Layout_Child *head, *tail, *o;
Efl_Gfx_Hint_Aspect aspect_type;
int space, min_sum = 0;
double weight_sum = 0, cur_pos;
Eina_Inlist *chain = NULL;
if (child->calc.chain_state[axis] == RELATIVE_CALC_DONE)
return EINA_TRUE;
if ((child != child->calc.to[START]->calc.to[END]) &&
(child != child->calc.to[END]->calc.to[START]))
return EINA_FALSE;
// find head
head = child;
while (head == head->calc.to[START]->calc.to[END])
head = head->calc.to[START];
//calculate weight_sum
aspect_type = !axis ? EFL_GFX_HINT_ASPECT_VERTICAL : EFL_GFX_HINT_ASPECT_HORIZONTAL;
o = head;
do
{
if ((o->calc.aspect[0] > 0) && (o->calc.aspect[1] > 0) &&
(o->calc.aspect_type == aspect_type))
{
_child_calc(o, !axis);
if (o->calc.want[axis].length > o->calc.min[axis])
o->calc.min[axis] = o->calc.want[axis].length;
}
else if ((o->calc.aspect[0] <= 0) ^ (o->calc.aspect[1] <= 0))
{
ERR("Invalid aspect parameter for obj(%p), aspect(%d, %d) ",
o->obj, o->calc.aspect[0], o->calc.aspect[1]);
}
o->calc.space[axis].length = o->calc.min[axis] +
o->calc.margin[START] + o->calc.margin[END];
min_sum += o->calc.space[axis].length;
weight_sum += o->calc.weight[axis];
tail = o;
o = o->calc.to[END];
}
while (o->calc.to[START] == tail);
_child_calc(head->calc.to[START], axis);
_child_calc(tail->calc.to[END], axis);
cur_pos = head->calc.to[START]->calc.want[axis].position +
(head->calc.to[START]->calc.want[axis].length * head->rel[START].relative);
space = tail->calc.to[END]->calc.want[axis].position +
(tail->calc.to[END]->calc.want[axis].length * tail->rel[END].relative) - cur_pos;
if ((space <= min_sum) || EINA_DBL_EQ(weight_sum, 0.0))
cur_pos += (space - min_sum) * head->calc.align[axis];
else
{
Efl_Ui_Relative_Layout_Calc *calc;
double weight_len, orig_space = space, orig_weight = weight_sum;
// Calculate compare factor
for (o = head; o != tail->calc.to[END]; o = o->calc.to[END])
{
double denom;
calc = &o->calc;
denom = (calc->weight[axis] * orig_space) - (orig_weight * calc->min[axis]);
if (denom > 0)
{
calc->comp_factor = (calc->weight[axis] * orig_space) / denom;
chain = eina_inlist_sorted_insert(chain, EINA_INLIST_GET(calc),
_chain_sort_cb);
}
else
{
space -= calc->space[axis].length;
weight_sum -= calc->weight[axis];
}
}
EINA_INLIST_FOREACH(chain, calc)
{
weight_len = (space * calc->weight[axis]) / weight_sum;
if (calc->space[axis].length < weight_len)
calc->space[axis].length = weight_len;
else
{
weight_sum -= calc->weight[axis];
space -= calc->space[axis].length;
}
}
}
for (o = head; o != tail->calc.to[END]; o = o->calc.to[END])
{
o->calc.space[axis].position = cur_pos + o->calc.margin[START] + 0.5;
cur_pos += o->calc.space[axis].length;
o->calc.space[axis].length -= o->calc.margin[START] + o->calc.margin[END];
o->calc.chain_state[axis] = RELATIVE_CALC_DONE;
}
return EINA_TRUE;
}
static void
_child_calc(Efl_Ui_Relative_Layout_Child *child, Eina_Bool axis)
{
Efl_Ui_Relative_Layout_Calc *calc = &child->calc;
if (calc->state[axis] == RELATIVE_CALC_DONE)
return;
if (calc->state[axis] == RELATIVE_CALC_ON)
{
ERR("%c-axis circular dependency when calculating part \"%s\"(%p).",
axis ? 'Y' : 'X', efl_class_name_get(child->obj), child->obj);
return;
}
calc->state[axis] = RELATIVE_CALC_ON;
if (!_child_chain_calc(child, axis))
{
_child_calc(calc->to[START], axis);
_child_calc(calc->to[END], axis);
calc->space[axis].position = calc->to[START]->calc.want[axis].position
+ (calc->to[START]->calc.want[axis].length * child->rel[START].relative)
+ calc->margin[START];
calc->space[axis].length = calc->to[END]->calc.want[axis].position
+ (calc->to[END]->calc.want[axis].length * child->rel[END].relative)
- calc->margin[END] - calc->space[axis].position;
}
if (calc->fill[axis] && (calc->weight[axis] > 0))
calc->want[axis].length = calc->space[axis].length;
if (!calc->aspect[0] && !calc->aspect[1])
{
if (calc->want[axis].length > calc->max[axis])
calc->want[axis].length = calc->max[axis];
if (calc->want[axis].length < calc->min[axis])
calc->want[axis].length = calc->min[axis];
}
else
{
_child_aspect_calc(child, axis);
}
//calculate align
calc->want[axis].position =
calc->space[axis].position +
(calc->space[axis].length - calc->want[axis].length) * calc->align[axis];
child->calc.state[axis] = RELATIVE_CALC_DONE;
}
static void
_hash_free_cb(void *data)
{
Efl_Ui_Relative_Layout_Child *child = data;
free(child);
}
static Eina_Bool
_hash_free_foreach_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED,
void *data, void *fdata EINA_UNUSED)
{
Efl_Ui_Relative_Layout_Child *child = data;
_elm_widget_sub_object_redirect_to_top(child->layout, child->obj);
_hash_free_cb(child);
return EINA_TRUE;
}
static Eina_Bool
_hash_child_calc_foreach_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED,
void *data, void *fdata EINA_UNUSED)
{
Efl_Ui_Relative_Layout_Child *child = data;
Eina_Rect want;
if (child->obj == child->layout)
return EINA_TRUE;
_child_calc(child, 0);
_child_calc(child, 1);
want.x = child->calc.want[0].position;
want.w = child->calc.want[0].length;
want.y = child->calc.want[1].position;
want.h = child->calc.want[1].length;
efl_gfx_entity_geometry_set(child->obj, want);
return EINA_TRUE;
}
static Eina_Bool
_hash_child_init_foreach_cb(const Eina_Hash *hash, const void *key EINA_UNUSED,
void *data, void *fdata EINA_UNUSED)
{
Eina_Size2D max, min, aspect;
Efl_Ui_Relative_Layout_Child *child = data;
Efl_Ui_Relative_Layout_Calc *calc = &(child->calc);
calc->to[LEFT] = _relative_child_find(hash, child->rel[LEFT].to);
if (!calc->to[LEFT]) calc->to[LEFT] = eina_hash_find(hash, &child->layout);
calc->to[RIGHT] = _relative_child_find(hash, child->rel[RIGHT].to);
if (!calc->to[RIGHT]) calc->to[RIGHT] = eina_hash_find(hash, &child->layout);
calc->to[TOP] = _relative_child_find(hash, child->rel[TOP].to);
if (!calc->to[TOP]) calc->to[TOP] = eina_hash_find(hash, &child->layout);
calc->to[BOTTOM] = _relative_child_find(hash, child->rel[BOTTOM].to);
if (!calc->to[BOTTOM]) calc->to[BOTTOM] = eina_hash_find(hash, &child->layout);
if (child->obj == child->layout)
{
Eina_Rect want = efl_gfx_entity_geometry_get(child->obj);
calc->want[0].position = want.x;
calc->want[0].length = want.w;
calc->want[1].position = want.y;
calc->want[1].length = want.h;
calc->state[0] = RELATIVE_CALC_DONE;
calc->state[1] = RELATIVE_CALC_DONE;
calc->chain_state[0] = RELATIVE_CALC_DONE;
calc->chain_state[1] = RELATIVE_CALC_DONE;
return EINA_TRUE;
}
calc->state[0] = RELATIVE_CALC_NONE;
calc->state[1] = RELATIVE_CALC_NONE;
calc->chain_state[0] = RELATIVE_CALC_NONE;
calc->chain_state[1] = RELATIVE_CALC_NONE;
efl_gfx_hint_weight_get(child->obj, &calc->weight[0], &calc->weight[1]);
efl_gfx_hint_align_get(child->obj, &calc->align[0], &calc->align[1]);
efl_gfx_hint_fill_get(child->obj, &calc->fill[0], &calc->fill[1]);
efl_gfx_hint_aspect_get(child->obj, &calc->aspect_type, &aspect);
calc->aspect[0] = aspect.w;
calc->aspect[1] = aspect.h;
efl_gfx_hint_margin_get(child->obj, &calc->margin[LEFT], &calc->margin[RIGHT],
&calc->margin[TOP], &calc->margin[BOTTOM]);
max = efl_gfx_hint_size_max_get(child->obj);
min = efl_gfx_hint_size_combined_min_get(child->obj);
calc->max[0] = max.w;
calc->max[1] = max.h;
calc->min[0] = min.w;
calc->min[1] = min.h;
calc->want[0].position = 0;
calc->want[0].length = 0;
calc->want[1].position = 0;
calc->want[1].length = 0;
calc->space[0].position = 0;
calc->space[0].length = 0;
calc->space[1].position = 0;
calc->space[1].length = 0;
if (calc->weight[0] < 0) calc->weight[0] = 0;
if (calc->weight[1] < 0) calc->weight[1] = 0;
if (calc->align[0] < 0) calc->align[0] = 0;
if (calc->align[1] < 0) calc->align[1] = 0;
if (calc->align[0] > 1) calc->align[0] = 1;
if (calc->align[1] > 1) calc->align[1] = 1;
if (calc->max[0] < 0) calc->max[0] = INT_MAX;
if (calc->max[1] < 0) calc->max[1] = INT_MAX;
if (calc->aspect[0] < 0) calc->aspect[0] = 0;
if (calc->aspect[1] < 0) calc->aspect[1] = 0;
return EINA_TRUE;
}
static void
_on_size_hints_changed(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
Evas_Object *obj, void *event_info EINA_UNUSED)
{
efl_pack_layout_request(obj);
}
EOLIAN static void
_efl_ui_relative_layout_efl_pack_layout_layout_update(Eo *obj, Efl_Ui_Relative_Layout_Data *pd)
{
eina_hash_foreach(pd->children, _hash_child_init_foreach_cb, NULL);
eina_hash_foreach(pd->children, _hash_child_calc_foreach_cb, NULL);
efl_event_callback_call(obj, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL);
}
EOLIAN static void
_efl_ui_relative_layout_efl_pack_layout_layout_request(Eo *obj, Efl_Ui_Relative_Layout_Data *pd EINA_UNUSED)
{
efl_canvas_group_need_recalculate_set(obj, EINA_TRUE);
}
EOLIAN static void
_efl_ui_relative_layout_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Relative_Layout_Data *pd EINA_UNUSED)
{
efl_pack_layout_update(obj);
}
EOLIAN static void
_efl_ui_relative_layout_efl_gfx_entity_size_set(Eo *obj, Efl_Ui_Relative_Layout_Data *pd EINA_UNUSED, Eina_Size2D sz)
{
efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz);
efl_canvas_group_change(obj);
}
EOLIAN static void
_efl_ui_relative_layout_efl_gfx_entity_position_set(Eo *obj, Efl_Ui_Relative_Layout_Data *pd EINA_UNUSED, Eina_Position2D pos)
{
efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);
efl_canvas_group_change(obj);
}
EOLIAN static void
_efl_ui_relative_layout_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Relative_Layout_Data *pd EINA_UNUSED)
{
evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_size_hints_changed, NULL);
efl_canvas_group_add(efl_super(obj, MY_CLASS));
elm_widget_sub_object_parent_add(obj);
elm_widget_highlight_ignore_set(obj, EINA_TRUE);
}
EOLIAN static Eo *
_efl_ui_relative_layout_efl_object_constructor(Eo *obj, Efl_Ui_Relative_Layout_Data *pd)
{
obj = efl_constructor(efl_super(obj, MY_CLASS));
efl_canvas_object_type_set(obj, MY_CLASS_NAME);
efl_access_object_access_type_set(obj, EFL_ACCESS_TYPE_SKIPPED);
efl_access_object_role_set(obj, EFL_ACCESS_ROLE_FILLER);
pd->obj = obj;
pd->children = eina_hash_pointer_new(_hash_free_cb);
_efl_ui_relative_layout_register(pd, obj);
return obj;
}
EOLIAN static void
_efl_ui_relative_layout_efl_object_destructor(Eo *obj, Efl_Ui_Relative_Layout_Data *pd)
{
eina_hash_free(pd->children);
efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static void
_efl_ui_relative_layout_unregister(Eo *obj, Efl_Ui_Relative_Layout_Data *pd, Efl_Object *child)
{
_elm_widget_sub_object_redirect_to_top(obj, child);
if (!eina_hash_del_by_key(pd->children, &child))
ERR("child(%p(%s)) is not registered", child, efl_class_name_get(child));
}
EOLIAN static void
_efl_ui_relative_layout_unregister_all(Eo *obj EINA_UNUSED, Efl_Ui_Relative_Layout_Data *pd)
{
eina_hash_foreach(pd->children, _hash_free_foreach_cb, NULL);
}
EOLIAN static Eina_Iterator *
_efl_ui_relative_layout_children_iterate(Eo *obj EINA_UNUSED, Efl_Ui_Relative_Layout_Data *pd)
{
return eina_hash_iterator_data_new(pd->children);
}
EFL_UI_RELATIVE_LAYOUT_RELATION_SET_GET(left, LEFT);
EFL_UI_RELATIVE_LAYOUT_RELATION_SET_GET(right, RIGHT);
EFL_UI_RELATIVE_LAYOUT_RELATION_SET_GET(top, TOP);
EFL_UI_RELATIVE_LAYOUT_RELATION_SET_GET(bottom, BOTTOM);
/* Internal EO APIs and hidden overrides */
#define EFL_UI_RELATIVE_LAYOUT_EXTRA_OPS \
EFL_CANVAS_GROUP_ADD_OPS(efl_ui_relative_layout)
#include "efl_ui_relative_layout.eo.c"

View File

@ -0,0 +1,79 @@
class Efl.Ui.Relative_Layout extends Efl.Ui.Widget implements Efl.Pack_Layout
{
[[The relative layout class.
A relative layout calculates the size and position of all the children
based on their relationship to each other.]]
methods {
@property relation_left {
[[Specifies the left side edge of the child relative to the target.
By default, target is parent and relative is 0.0.]]
keys {
child: Efl.Object; [[The child to specify relation.]]
}
values {
target: Efl.Object; [[The relative target.]]
relative: double; [[The ratio between left and right of the target,
ranging from 0.0 to 1.0.]]
}
}
@property relation_right {
[[Specifies the right side edge of the child relative to the target.
By default, target is parent and relative is 1.0.]]
keys {
child: Efl.Object; [[The child to specify relation.]]
}
values {
target: Efl.Object; [[The relative target.]]
relative: double; [[The ratio between left and right of the target,
ranging from 0.0 to 1.0.]]
}
}
@property relation_top {
[[Specifies the top side edge of the child relative to the target.
By default, target is parent and relative is 0.0.]]
keys {
child: Efl.Object; [[The child to specify relation.]]
}
values {
target: Efl.Object; [[The relative target.]]
relative: double; [[The ratio between top and bottom of the target,
ranging from 0.0 to 1.0.]]
}
}
@property relation_bottom {
[[Specifies the bottom side edge of the child relative to the target.
By default, target is parent and relative is 1.0.]]
keys {
child: Efl.Object; [[The child to specify relation.]]
}
values {
target: Efl.Object; [[The relative target.]]
relative: double; [[The ratio between top and bottom of the target,
ranging from 0.0 to 1.0.]]
}
}
unregister {
[[Remove all relations of the child.]]
params {
@in child: Efl.Object; [[The child to unregister]]
}
}
unregister_all {
[[Remove all relations from the registered children. ]]
}
children_iterate {
[[Begin iterating over this object's children.]]
return: iterator<Efl.Object> @owned @warn_unused; [[Iterator to object children.]]
}
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Canvas.Group.group_calculate;
Efl.Gfx.Entity.position { set; }
Efl.Gfx.Entity.size { set; }
Efl.Pack_Layout.layout_update;
Efl.Pack_Layout.layout_request;
}
}

View File

@ -0,0 +1,90 @@
#ifndef EFL_UI_RELATIVE_LAYOUT_PRIVATE_H
#define EFL_UI_RELATIVE_LAYOUT_PRIVATE_H
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#define EFL_PACK_LAYOUT_PROTECTED
#include <Elementary.h>
#include "elm_priv.h"
typedef enum _Efl_Ui_Relative_Layout_Calc_State
{
RELATIVE_CALC_NONE,
RELATIVE_CALC_DONE,
RELATIVE_CALC_ON
} Efl_Ui_Relative_Layout_Calc_State;
typedef struct _Efl_Ui_Relative_Layout_Data Efl_Ui_Relative_Layout_Data;
typedef struct _Efl_Ui_Relative_Layout_Child Efl_Ui_Relative_Layout_Child;
typedef struct _Efl_Ui_Relative_Layout_Calc Efl_Ui_Relative_Layout_Calc;
typedef struct _Efl_Ui_Relative_Layout_Relation Efl_Ui_Relative_Layout_Relation;
struct _Efl_Ui_Relative_Layout_Calc
{
EINA_INLIST;
int max[2];
int min[2];
int aspect[2];
int margin[4];
Efl_Gfx_Hint_Aspect aspect_type;
Eina_Bool fill[2];
double weight[2];
double align[2];
double comp_factor;
struct {
int position;
double length;
} space[2], want[2];
Efl_Ui_Relative_Layout_Calc_State state[2];
Efl_Ui_Relative_Layout_Calc_State chain_state[2];
Efl_Ui_Relative_Layout_Child *to[4];
};
struct _Efl_Ui_Relative_Layout_Data
{
Eo *obj;
Eina_Hash *children;
};
struct _Efl_Ui_Relative_Layout_Relation
{
Efl_Object *to;
double relative;
};
struct _Efl_Ui_Relative_Layout_Child
{
Eo *obj;
Eo *layout;
Efl_Ui_Relative_Layout_Relation rel[4];
Efl_Ui_Relative_Layout_Calc calc;
};
#define EFL_UI_RELATIVE_LAYOUT_RELATION_SET_GET(direction, DIRECTION) \
EOLIAN static void \
_efl_ui_relative_layout_relation_ ## direction ## _set(Eo *obj EINA_UNUSED, Efl_Ui_Relative_Layout_Data *pd, Eo *child, Eo *target, double relative) \
{ \
Efl_Ui_Relative_Layout_Child *rc; \
rc = _relative_child_get(pd, child); \
if (target) rc->rel[DIRECTION].to = target; \
if (relative < 0) relative = 0; \
else if (relative > 1) relative = 1; \
rc->rel[DIRECTION].relative = relative; \
} \
\
EOLIAN static void \
_efl_ui_relative_layout_relation_ ## direction ## _get(const Eo *obj EINA_UNUSED, Efl_Ui_Relative_Layout_Data *pd, Eo *child, Eo **target, double *relative) \
{ \
Efl_Ui_Relative_Layout_Child *rc; \
rc = _relative_child_get(pd, child); \
if (target) *target = rc->rel[DIRECTION].to; \
if (relative) *relative = rc->rel[DIRECTION].relative; \
}
#endif

View File

@ -276,6 +276,7 @@ pub_eo_files = [
'efl_ui_text_part.eo',
'efl_ui_caching_factory.eo',
'efl_ui_widget_factory.eo',
'efl_ui_relative_layout.eo',
]
foreach eo_file : pub_eo_files
@ -480,7 +481,8 @@ elementary_headers_unstable = [
'efl_page_indicator_icon.h',
'efl_ui_tab_pager_private.h',
'efl_ui_tab_bar_private.h',
'efl_ui_tab_page_private.h'
'efl_ui_tab_page_private.h',
'efl_ui_relative_layout_private.h'
]
elementary_pub_headers = [
@ -912,6 +914,7 @@ elementary_src = [
'efl_ui_homogeneous_model.c',
'efl_ui_exact_model.c',
'efl_ui_average_model.c'
'efl_ui_relative_layout.c'
]
elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl]

View File

@ -14,6 +14,7 @@ static const Efl_Test_Case etc[] = {
{ "efl_ui_focus_sub", efl_ui_test_focus_sub},
{ "efl_ui_box", efl_ui_test_box},
{ "efl_ui_grid", efl_ui_test_grid},
{ "efl_ui_relative_layout", efl_ui_test_relative_layout},
{ "efl_ui_image", efl_ui_test_image},
{ "efl_ui_image_zoomable", efl_ui_test_image_zoomable},
{ "efl_ui_layout", efl_ui_test_layout},

View File

@ -21,6 +21,7 @@
void efl_ui_test_box(TCase *tc);
void efl_ui_test_grid(TCase *tc);
void efl_ui_test_relative_layout(TCase *tc);
void efl_ui_test_atspi(TCase *tc);
void efl_ui_test_image_zoomable(TCase *tc);
void efl_ui_test_layout(TCase *tc);

View File

@ -0,0 +1,300 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#include "elm_suite.h"
#define COORD_EQ(a, b) (!!(abs(a - b) < 2))
#define GEOMETRY_EQ(a, b) (COORD_EQ(a.x, b.x) && COORD_EQ(a.y, b.y) && \
COORD_EQ(a.w, b.w) && COORD_EQ(a.h, b.h))
typedef struct {
Eina_Size2D max;
Eina_Size2D min;
double weightx;
double weighty;
double alignx;
double aligny;
int marginl;
int marginr;
int margint;
int marginb;
Efl_Gfx_Hint_Aspect mode;
Eina_Size2D aspect;
Eina_Bool fillx;
Eina_Bool filly;
Eina_Size2D layout_size;
Eina_Size2D layout_expected;
Eina_Rect expected;
char testname[1024];
} Hint;
static Hint hints[] = {
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(0, 0), 1, 1, 0.5, 0.5, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT(0, 0, 200, 200), "[0]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.5, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT(0, 0, 200, 200), "[1]" },
{ EINA_SIZE2D(50, 150), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, (200 - 150) * 0.7, 70, 150), "[2]" },
{ EINA_SIZE2D(150, -1), EINA_SIZE2D(70, 70), 0, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, (200 - 70) * 0.7, 70, 70), "[3]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_TRUE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, (200 - 70) * 0.7, 70, 70), "[4]" },
{ EINA_SIZE2D(150, 150), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_TRUE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, (200 - 70) * 0.7, 70, 70), "[5]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 0, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, -7, 70, 70 * 3), "[6]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 0, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(300, 300), EINA_SIZE2D(300, 300),
EINA_RECT((300 - 70) * 0.3, (300 - 70 * 3) * 0.7, 70, 70 * 3), "[7]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, -7, 70, 70 * 3), "[8]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(300, 300), EINA_SIZE2D(300, 300),
EINA_RECT((300 - 70) * 0.3, (300 - 70 * 3) * 0.7, 70, 70 * 3), "[9]" },
{ EINA_SIZE2D(-1, 150), EINA_SIZE2D(70, 70), 0, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, -7, 70, 70 * 3), "[10]" },
{ EINA_SIZE2D(-1, 150), EINA_SIZE2D(70, 70), 0, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(300, 300), EINA_SIZE2D(300, 300),
EINA_RECT((300 - 70) * 0.3, (300 - 70 * 3) * 0.7, 70, 70 * 3), "[11]" },
{ EINA_SIZE2D(-1, 150), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(200, 200), EINA_SIZE2D(200, 200),
EINA_RECT((200 - 70) * 0.3, -7, 70, 70 * 3), "[12]" },
{ EINA_SIZE2D(-1, 150), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_BOTH, EINA_SIZE2D(1, 3), EINA_TRUE, EINA_FALSE,
EINA_SIZE2D(300, 300), EINA_SIZE2D(300, 300),
EINA_RECT((300 - 70) * 0.3, (300 - 70 * 3) * 0.7, 70, 70 * 3), "[13]" },
};
static Hint hints2[][2] = {
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.3, (150 - 70) * 0.7, 70, 70), "[1/1 weight btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.8, (150 - 70) * 0.2 + 150, 70, 70), "[1/1 weight btn2]" }
},
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.3, 0, 70, 70), "[0/1 weight btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.8, (300 - 140) * 0.2 + 70, 70, 70), "[0/1 weight btn2]" }
},
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.3, (300 - 140) * 0.7, 70, 70), "[0/0 weight btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 0, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_FALSE, EINA_FALSE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT((150 - 70) * 0.8, (300 - 140) * 0.7 + 70, 70, 70), "[0/0 weight btn2]" }
},
};
static Hint hints3[][3] = {
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT(0, 0, 150, 100), "[1/1/1 weight_l btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 100), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT(0, 100, 150, 100), "[1/1/1 weight_l btn2]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 300), EINA_SIZE2D(150, 300),
EINA_RECT(0, 100 + 100, 150, 100), "[1/1/1 weight_l btn2]" }
},
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 270), EINA_SIZE2D(150, 270),
EINA_RECT(0, 0, 150, 85), "[1/1/1 weight_m btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 100), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 270), EINA_SIZE2D(150, 270),
EINA_RECT(0, 85, 150, 100), "[1/1/1 weight_m btn2]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 270), EINA_SIZE2D(150, 270),
EINA_RECT(0, 100 + 85, 150, 85), "[1/1/1 weight_m btn2]" }
},
{
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.3, 0.7, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 200), EINA_SIZE2D(150, 200),
EINA_RECT(0, -28, 150, 70), "[1/1/1 weight_s btn]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 100), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 200), EINA_SIZE2D(150, 200),
EINA_RECT(0, 42, 150, 100), "[1/1/1 weight_s btn2]" },
{ EINA_SIZE2D(-1, -1), EINA_SIZE2D(70, 70), 1, 1, 0.8, 0.2, 0, 0, 0, 0,
EFL_GFX_HINT_ASPECT_NONE, EINA_SIZE2D(0, 0), EINA_TRUE, EINA_TRUE,
EINA_SIZE2D(150, 200), EINA_SIZE2D(150, 200),
EINA_RECT(0, 142, 150, 70), "[1/1/1 weight_s btn2]" }
},
};
static Eo *win, *layout;
static void
btn_hint_set(Eo *btn, Hint *hint)
{
efl_gfx_entity_size_set(layout, hint->layout_size);
efl_gfx_hint_size_max_set(btn, hint->max);
efl_gfx_hint_size_min_set(btn, hint->min);
efl_gfx_hint_margin_set(btn, hint->marginl, hint->marginr, hint->margint,
hint->marginb);
efl_gfx_hint_weight_set(btn, hint->weightx, hint->weighty);
efl_gfx_hint_align_set(btn, hint->alignx, hint->aligny);
efl_gfx_hint_fill_set(btn, hint->fillx, hint->filly);
efl_gfx_hint_aspect_set(btn, hint->mode, hint->aspect);
efl_canvas_group_calculate(layout);
}
static void
btn_geom_assert(Hint *hint, Eina_Rect btn_geom)
{
Eina_Size2D layout_size, layout_min;
layout_size = efl_gfx_entity_size_get(layout);
layout_min = efl_gfx_hint_size_min_get(layout);
layout_size.w = layout_size.w > layout_min.w ? layout_size.w : layout_min.w;
layout_size.h = layout_size.h > layout_min.h ? layout_size.h : layout_min.h;
ck_assert_msg(GEOMETRY_EQ(btn_geom, hint->expected),
"Case %s failed... button geometry: (%d, %d, %d, %d) expected geometry: (%d, %d, %d, %d)",
hint->testname, btn_geom.x, btn_geom.y, btn_geom.w, btn_geom.h,
hint->expected.x, hint->expected.y, hint->expected.w, hint->expected.h);
ck_assert_msg(COORD_EQ(layout_size.w, hint->layout_expected.w) &&
COORD_EQ(layout_size.h, hint->layout_expected.h),
"Case %s failed... layout size: (%d, %d) expected size: (%d, %d)",
hint->testname, layout_size.w, layout_size.h,
hint->layout_expected.w, hint->layout_expected.h);
}
static void
layout_setup()
{
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC));
layout = efl_add(EFL_UI_RELATIVE_LAYOUT_CLASS, win,
efl_gfx_entity_size_set(efl_added, EINA_SIZE2D(200, 200)));
efl_gfx_entity_size_set(win, EINA_SIZE2D(200, 200));
}
static void
layout_teardown()
{
if (win)
{
efl_del(win);
win = NULL;
}
}
EFL_START_TEST (efl_ui_relative_layout_class_check)
{
const char *class;
class = efl_class_name_get(layout);
ck_assert(class != NULL);
ck_assert(!strcmp(class, "Efl.Ui.Relative_Layout"));
}
EFL_END_TEST
EFL_START_TEST (efl_ui_relative_layout_layout_update)
{
int i, max_index = (sizeof(hints) / sizeof(Hint));
Eo *btn = efl_add(EFL_UI_BUTTON_CLASS, layout,
efl_ui_relative_layout_relation_left_set(layout, efl_added, NULL, 0.0));
for (i = 0; i < max_index; i++)
{
btn_hint_set(btn, &hints[i]);
btn_geom_assert(&hints[i], efl_gfx_entity_geometry_get(btn));
}
}
EFL_END_TEST
EFL_START_TEST (efl_ui_relative_layout_layout_update_chain)
{
int i, max_index2, max_index3;
Eo *btn, *btn2, *btn3;
btn = efl_add(EFL_UI_BUTTON_CLASS, layout);
btn2 = efl_add(EFL_UI_BUTTON_CLASS, layout);
max_index2 = ((sizeof(hints2) / sizeof(Hint)) / 2);
max_index3 = ((sizeof(hints3) / sizeof(Hint)) / 3);
efl_ui_relative_layout_relation_bottom_set(layout, btn, btn2, 0.0);
efl_ui_relative_layout_relation_top_set(layout, btn2, btn, 1.0);
for (i = 0; i < max_index2; i++)
{
btn_hint_set(btn, &hints2[i][0]);
btn_hint_set(btn2, &hints2[i][1]);
btn_geom_assert(&hints2[i][0], efl_gfx_entity_geometry_get(btn));
btn_geom_assert(&hints2[i][1], efl_gfx_entity_geometry_get(btn2));
}
btn3 = efl_add(EFL_UI_BUTTON_CLASS, layout);
efl_ui_relative_layout_relation_bottom_set(layout, btn2, btn3, 0.0);
efl_ui_relative_layout_relation_top_set(layout, btn3, btn2, 1.0);
for (i = 0; i < max_index3; i++)
{
btn_hint_set(btn, &hints3[i][0]);
btn_hint_set(btn2, &hints3[i][1]);
btn_hint_set(btn3, &hints3[i][2]);
btn_geom_assert(&hints3[i][0], efl_gfx_entity_geometry_get(btn));
btn_geom_assert(&hints3[i][1], efl_gfx_entity_geometry_get(btn2));
btn_geom_assert(&hints3[i][2], efl_gfx_entity_geometry_get(btn3));
}
}
EFL_END_TEST
void efl_ui_test_relative_layout(TCase *tc)
{
tcase_add_checked_fixture(tc, layout_setup, layout_teardown);
tcase_add_test(tc, efl_ui_relative_layout_class_check);
tcase_add_test(tc, efl_ui_relative_layout_layout_update);
tcase_add_test(tc, efl_ui_relative_layout_layout_update_chain);
}

View File

@ -124,6 +124,7 @@ efl_ui_suite_src = [
'efl_ui_test_focus_sub.c',
'efl_ui_test_box.c',
'efl_ui_test_grid.c',
'efl_ui_test_relative_layout.c',
'efl_ui_test_image.c',
'efl_ui_test_image_zoomable.c',
'efl_ui_test_layout.c',