combobox: add Multiple selection feature

Summary:
This patch focuses on Combobox widget customization,
Multibuttonentry widget is used instead of entry for taking user input.
The idea is to make the widget look like  {F28112} {F28115} when the multiple_selection is set.
To-DO:
1) Need to add scrollable interface to combobox when MBE is used (need some suggestions on it).
2) focus cycle is still buggy as genlist requires focus otherwise selected item will return NULL (sometimes)
Signed-off-by: divyesh purohit <div.purohit@samsung.com>
@feature

Test Plan: Please run combobox multiple selection example from elementart_test.

Reviewers: raster, shilpasingh, cedric

Subscribers: govi, rajeshps

Projects: #elementary

Differential Revision: https://phab.enlightenment.org/D3570
This commit is contained in:
divyesh purohit 2016-03-10 14:24:50 -08:00 committed by Cedric BAIL
parent 7078261b44
commit 07404215a9
7 changed files with 312 additions and 10 deletions

View File

@ -7,7 +7,7 @@ EDJE_CC_FLAGS += -id $(top_srcdir)/data/objects -fd $(top_srcdir)/data/objects
filesdir = $(datadir)/elementary/objects
files_DATA = test.edj test_external.edj multip.edj cursors.edj font_preview.edj postit_ent.edj multibuttonentry.edj test_prefs.edj test_prefs.epb test_focus_style.edj
files_DATA = test.edj test_external.edj multip.edj cursors.edj combobox_multiple.edj font_preview.edj postit_ent.edj multibuttonentry.edj test_prefs.edj test_prefs.epb test_focus_style.edj
EXTRA_DIST = \
test.edc \
@ -16,6 +16,7 @@ test_prefs.edc \
test_prefs.epc \
multip.edc \
cursors.edc \
combobox_multiple.edc \
font_preview.edc \
postit_ent.edc \
multibuttonentry.edc \
@ -57,6 +58,11 @@ cursors.edj: Makefile $(EXTRA_DIST)
$(top_srcdir)/data/objects/cursors.edc \
$(top_builddir)/data/objects/cursors.edj
combobox_multiple.edj: Makefile combobox_multiple.edc
$(AM_V_EDJ)$(EDJE_CC) $(EDJE_CC_FLAGS) \
$(top_srcdir)/data/objects/combobox_multiple.edc \
$(top_builddir)/data/objects/combobox_multiple.edj
font_preview.edj: Makefile $(EXTRA_DIST)
$(AM_V_EDJ)$(EDJE_CC) $(EDJE_CC_FLAGS) \
$(top_srcdir)/data/objects/font_preview.edc \

View File

@ -0,0 +1,61 @@
collections {
group {
name: "combobox_multiple_test";
parts{
part {
name: "bg";
type: RECT;
mouse_events: 1;
scale:1;
description {
state: "default" 0.0;
color: 0 0 0 0;
rel1.relative: 0.0 0.0;
rel2.relative: 1.0 1.0;
}
}
part{
name: "top.left";
type: RECT;
scale: 1;
description {
state: "default" 0.0;
min : 0 0;
fixed: 1 1;
rel1 { relative: 0.0 0.0; to: bg; }
rel2 { relative: 0.0 0.0; to: bg; }
align: 0.0 0.0;
color: 0 0 0 0;
}
}
part{
name: "bottom.right";
type: RECT;
scale: 1;
description {
state: "default" 0.0;
min : 0 0;
fixed: 1 1;
rel1 { relative: 1.0 1.0; to: bg; }
rel2 { relative: 1.0 1.0; to: bg; }
align: 1.0 1.0;
color: 0 0 0 0;
}
}
part {
name: "combobox";
type: SWALLOW;
mouse_events: 1;
scale:1;
description {
state: "default" 0.0;
min : 0 0;
max : -1 300;
rel1 { relative: 0.0 1.0; to: top.left; }
rel2 { relative: 0.0 0.0; to: bottom.right; }
align: 0.0 0.0;
}
}
}
}
}

View File

@ -46,6 +46,7 @@ void test_clock_edit(void *data, Evas_Object *obj, void *event_info);
void test_clock_edit2(void *data, Evas_Object *obj, void *event_info);
void test_clock_pause(void *data, Evas_Object *obj, void *event_info);
void test_combobox(void *data, Evas_Object *obj, void *event_info);
void test_combobox2(void *data, Evas_Object *obj, void *event_info);
void test_check(void *data, Evas_Object *obj, void *event_info);
void test_check_toggle(void *data, Evas_Object *obj, void *event_info);
void test_radio(void *data, Evas_Object *obj, void *event_info);
@ -755,6 +756,7 @@ add_tests:
ADD_TEST(NULL, "Selectors", "DaySelector", test_dayselector);
ADD_TEST(NULL, "Selectors", "Main menu", test_main_menu);
ADD_TEST(NULL, "Selectors", "Combobox", test_combobox);
ADD_TEST(NULL, "Selectors", "Combobox Multiple Selection", test_combobox2);
//------------------------------//
ADD_TEST(NULL, "Cursors", "Cursor", test_cursor);

File diff suppressed because one or more lines are too long

View File

@ -150,7 +150,8 @@ _table_resize(void *data)
evas_object_geometry_get(elm_object_item_track(sd->item), NULL, NULL,
NULL, &h);
if (h) sd->item_height = h;
evas_object_geometry_get(sd->entry, NULL, NULL, &obj_w, NULL);
evas_object_geometry_get(elm_object_part_content_get(data, "elm.swallow.content"),
NULL, NULL, &obj_w, NULL);
evas_object_geometry_get(data, NULL, &obj_y, NULL, &obj_h);
evas_object_geometry_get(sd->hover_parent, NULL, NULL, &hover_parent_w,
&hover_parent_h);
@ -197,7 +198,13 @@ static void
_on_item_selected(void *data , Evas_Object *obj EINA_UNUSED, void *event)
{
ELM_COMBOBOX_DATA_GET(data, sd);
elm_object_focus_set(sd->entry, EINA_TRUE);
if (!sd->multiple_selection) elm_object_focus_set(sd->entry, EINA_TRUE);
else
{
elm_genlist_item_bring_in(sd->item, ELM_GENLIST_ITEM_SCROLLTO_TOP);
elm_object_focus_set(sd->mbe, EINA_TRUE);
}
eo_event_callback_call(data, ELM_COMBOBOX_EVENT_ITEM_SELECTED, event);
}
@ -305,6 +312,107 @@ _elm_combobox_elm_button_admits_autorepeat_get(Eo *obj EINA_UNUSED,
return EINA_FALSE;
}
EOLIAN static Eina_Bool
_elm_combobox_multiple_selection_get(Eo *obj EINA_UNUSED, Elm_Combobox_Data *pd)
{
return pd->multiple_selection;
}
static Eina_Bool
_mbe_clicked_cb(void *data EINA_UNUSED, Eo *obj,
const Eo_Event_Description *desc EINA_UNUSED,
void *event_info EINA_UNUSED)
{
//Unset the multibuttonentry to contracted mode of single line
elm_multibuttonentry_expanded_set(obj, EINA_TRUE);
return EINA_TRUE;
}
static Eina_Bool
_mbe_focused_cb(void *data EINA_UNUSED, Eo *obj EINA_UNUSED,
const Eo_Event_Description *desc EINA_UNUSED,
void *event_info EINA_UNUSED)
{
return EINA_TRUE;
}
static Eina_Bool
_mbe_unfocused_cb(void *data EINA_UNUSED, Eo *obj,
const Eo_Event_Description *desc EINA_UNUSED,
void *event_info EINA_UNUSED)
{
//Set the multibuttonentry to contracted mode of single line
elm_multibuttonentry_expanded_set(obj, EINA_FALSE);
return EINA_TRUE;
}
static Eina_Bool
_mbe_item_added(void *data, Eo *obj EINA_UNUSED,
const Eo_Event_Description *desc EINA_UNUSED,
void *event_info EINA_UNUSED)
{
ELM_COMBOBOX_DATA_GET(data, sd);
elm_genlist_filter_set(sd->genlist, NULL);
return EINA_TRUE;
}
EO_CALLBACKS_ARRAY_DEFINE(mbe_callbacks,
{ EVAS_CLICKABLE_INTERFACE_EVENT_CLICKED, _mbe_clicked_cb },
{ ELM_WIDGET_EVENT_FOCUSED, _mbe_focused_cb },
{ ELM_WIDGET_EVENT_UNFOCUSED, _mbe_unfocused_cb },
{ ELM_MULTIBUTTONENTRY_EVENT_ITEM_ADDED , _mbe_item_added });
EO_CALLBACKS_ARRAY_DEFINE(entry_callbacks,
{ ELM_ENTRY_EVENT_CHANGED_USER, _on_changed },
{ ELM_ENTRY_EVENT_ABORTED, _on_aborted });
EOLIAN static void
_elm_combobox_multiple_selection_set(Eo *obj, Elm_Combobox_Data *pd,
Eina_Bool enabled)
{
Evas_Object* scr;
pd->multiple_selection = enabled;
if (enabled)
{
// This is multibuttonentry object that will take over the MBE call
eo_add(&pd->mbe,ELM_MULTIBUTTONENTRY_CLASS, obj);
evas_object_size_hint_weight_set(pd->mbe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(pd->mbe, EVAS_HINT_FILL, EVAS_HINT_FILL);
eo_event_callback_array_add(elm_multibuttonentry_entry_get(pd->mbe), entry_callbacks(), obj);
eo_event_callback_array_add(pd->mbe, mbe_callbacks(), obj);
pd->entry = elm_object_part_content_unset(obj, "elm.swallow.content");
elm_object_text_set(pd->mbe, elm_object_part_text_get(pd->entry, NULL));
elm_object_part_text_set(pd->mbe, "guide", elm_object_part_text_get(pd->entry,
"guide"));
evas_object_hide(pd->entry);
eo_composite_attach(obj, pd->mbe);
scr = elm_scroller_add(obj);
elm_scroller_bounce_set(scr, EINA_FALSE, EINA_TRUE);
elm_scroller_policy_set(scr, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
evas_object_size_hint_weight_set(scr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(scr);
elm_object_content_set(scr, pd->mbe);
elm_object_part_content_set(obj, "elm.swallow.content", scr);
elm_widget_can_focus_set(pd->genlist, EINA_FALSE);
}
else
{
scr = elm_object_part_content_unset(obj, "elm.swallow.content");
elm_object_part_content_set(obj, "elm.swallow.content", pd->entry);
elm_object_text_set(pd->entry, elm_object_part_text_get(pd->mbe, NULL));
elm_object_part_text_set(pd->entry, "guide",
elm_object_part_text_get(pd->mbe, "guide"));
elm_widget_can_focus_set(pd->genlist, EINA_TRUE);
elm_genlist_item_bring_in(pd->item, ELM_GENLIST_ITEM_SCROLLTO_NONE);
evas_object_hide(scr);
}
}
EAPI Evas_Object *
elm_combobox_add(Evas_Object *parent)
{
@ -336,7 +444,6 @@ _elm_combobox_eo_base_constructor(Eo *obj, Elm_Combobox_Data *sd)
eo_add(&sd->hover, ELM_HOVER_CLASS, sd->hover_parent);
elm_widget_mirrored_automatic_set(sd->hover, EINA_FALSE);
elm_hover_target_set(sd->hover, obj);
elm_widget_sub_object_add(obj, sd->hover);
snprintf(buf, sizeof(buf), "combobox_vertical/%s",
elm_widget_style_get(obj));
elm_object_style_set(sd->hover, buf);
@ -382,8 +489,7 @@ _elm_combobox_eo_base_constructor(Eo *obj, Elm_Combobox_Data *sd)
ELM_SCROLLER_POLICY_OFF);
elm_entry_scrollable_set(entry, EINA_TRUE);
elm_entry_single_line_set(entry, EINA_TRUE);
eo_event_callback_add(entry, ELM_ENTRY_EVENT_CHANGED_USER, _on_changed, obj);
eo_event_callback_add(entry, ELM_ENTRY_EVENT_ABORTED, _on_aborted, obj);
eo_event_callback_array_add(entry, entry_callbacks(), obj);
evas_object_show(entry);
eo_composite_attach(obj, gl);
@ -397,7 +503,11 @@ EOLIAN static void
_elm_combobox_hover_begin(Eo *obj, Elm_Combobox_Data *sd)
{
if (!sd->hover) return;
elm_object_focus_set(sd->entry, EINA_TRUE);
if (sd->multiple_selection)
elm_object_focus_set(sd->mbe, EINA_TRUE);
else elm_object_focus_set(sd->entry, EINA_TRUE);
_activate(obj);
}
@ -514,13 +624,15 @@ EOLIAN void
_elm_combobox_elm_widget_part_text_set(Eo *obj EINA_UNUSED, Elm_Combobox_Data *pd,
const char * part, const char *label)
{
elm_object_part_text_set(pd->entry, part, label);
if (pd->multiple_selection) elm_object_part_text_set(pd->mbe, part, label);
else elm_object_part_text_set(pd->entry, part, label);
}
EOLIAN const char *
_elm_combobox_elm_widget_part_text_get(Eo *obj EINA_UNUSED, Elm_Combobox_Data *pd,
const char * part)
{
if (pd->multiple_selection) return elm_object_part_text_get(pd->mbe, part);
return elm_object_part_text_get(pd->entry, part);
}

View File

@ -1,6 +1,6 @@
class Elm.Combobox (Elm.Button, Evas.Selectable_Interface,
Elm.Interface_Atspi_Widget_Action,
Elm.Entry, Elm.Genlist, Elm.Hover)
Elm.Entry, Elm.Genlist, Elm.Hover, Elm.Multibuttonentry)
{
eo_prefix: elm_obj_combobox;
methods {
@ -16,6 +16,25 @@ class Elm.Combobox (Elm.Button, Evas.Selectable_Interface,
return: bool;
}
}
@property multiple_selection {
get {
[[Returns whether the combobox allows multiple selection.
@since 1.18
]]
}
set {
[[Enables or disables multiple selection in combobox.
Warning: This API should be set before any other API on
combobox, if you wish to avoid complications.
@since 1.18
]]
}
values {
enabled: bool; [[$true if multiple selection is enabled,
$false otherwise.]]
}
}
hover_begin {
[[This triggers the combobox popup from code, the same as if the user
had clicked the button.

View File

@ -33,6 +33,7 @@ struct _Elm_Combobox_Data
Evas_Object *entry;
Evas_Object *tbl;
Evas_Object *spacer;
Evas_Object *mbe;
Elm_Object_Item *item;
const char *style;
const char *best_location;
@ -40,6 +41,7 @@ struct _Elm_Combobox_Data
int item_height;
Eina_Bool expanded:1;
Eina_Bool first_filter:1;
Eina_Bool multiple_selection:1;
};
/**