Entry - Example of simple editing

As a general overview of Entry we are going to write an, albeit simple, functional editor.

Although intended to show how elm_entry works, this example also makes extensive use of several other widgets. The full code can be found in entry_example.c and in the following lines we'll go through the parts especific to the Entry widget.

The program itself is a simple editor, with a file already set to it, that can be set to autosave or not and allows insertion of emoticons and some formatted text. As of this writing, the capabilities of format edition in the entry are very limited, so a lot of manual work is required to change the current text.

In any case, the program allows some changes by using the buttons on the top of the window and returning focus back to the main entry afterwards.

We'll begin by showing a few structures used throught the program. First, the application owns data that holds the main window and the main entry where the editting happens. Then, an auxiliary structure we'll use later when inserting icons in our text.

typedef struct
{
Evas_Object *edit_buffer;
} App_Data;
typedef struct
{
Evas_Object *inwin;
Evas_Object *naviframe;
Evas_Object *grid;
Evas_Object *settings;
int size;
int vsize;
int width, height;
const char *emo;
App_Data *ad;
} App_Inwin_Data;

A little convenience function will insert whatever text we need in the buffer at the current cursor's position and set focus back to this entry. This is done mostly because clicking on any button will make them steal focus, which makes writing text more cumbersome.

static void
_edit_buffer_insert(Evas_Object *e, const char *text)
{
elm_entry_entry_insert(e, text);
}

One of the buttons on the top will trigger an Inwin to open and show us several icons we can insert into the text. We'll jump over most of these functions, but when all the options are chosen, we insert the special markup text that will show the chosen icon in place.

static void
_btn_insert_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
App_Inwin_Data *aid = data;
const char *size[] = {
"size",
"absize",
"relsize"
};
const char *vsize[] = {
"full",
"ascent"
};
char buf[512];
snprintf(buf, sizeof(buf), "<item %s=%dx%d vsize=%s href=emoticon/%s>"
"</item>", size[aid->size], aid->width, aid->height,
vsize[aid->vsize], aid->emo);
_edit_buffer_insert(aid->ad->edit_buffer, buf);
evas_object_del(aid->inwin);
}

As can be seen in that function, the program lets us add icons to our entry using all the possible configurations for them. That should help to clarify how the different combinations work out by actually seeing them in action.

The same popup window has a page to set the settings of the chosen icon, that is, the size and how the item will be placed within the line.

The size is done with two entries, limitted to accept numbers and a fixed size of characters. Changing the value in this entries will update the icon size in our struct as seen in the next two callbacks.

static void
_width_changed_cb(void *data, Evas_Object *obj, void *event EINA_UNUSED)
{
App_Inwin_Data *aid = data;
aid->width = atoi(elm_object_text_get(obj));
}
static void
_height_changed_cb(void *data, Evas_Object *obj, void *event EINA_UNUSED)
{
App_Inwin_Data *aid = data;
aid->height = atoi(elm_object_text_get(obj));
}

The rest of the options are handled with radio buttons, since only one type of size can be used (size, absize or relsize) and for the vertical sizing it needs to choose between ascent and full. Depending on which is chosen, the item tag is formed accordingly as seen before.

static Evas_Object *
_page_settings_add(Evas_Object *parent, App_Inwin_Data *aid)
{
Evas_Object *box, *sizeopts, *box2, *sizebox, *vsizebox,
*rsize, *rabsize, *rrelsize, *rvfull, *rvascent,
*fwidth, *ewidth, *fheight, *eheight,
*binsert;
char buf[100];
static Elm_Entry_Filter_Accept_Set accept_set = {
.accepted = "0123456789",
.rejected = NULL
};
static Elm_Entry_Filter_Limit_Size limit_size = {
.max_byte_count = 0
};
box = elm_box_add(parent);
sizeopts = elm_frame_add(parent);
elm_object_text_set(sizeopts, "Size");
elm_box_pack_end(box, sizeopts);
evas_object_show(sizeopts);
box2 = elm_box_add(parent);
elm_object_content_set(sizeopts, box2);
sizebox = elm_box_add(parent);
elm_box_horizontal_set(sizebox, EINA_TRUE);
elm_box_pack_end(box2, sizebox);
evas_object_show(sizebox);
rsize = elm_radio_add(parent);
elm_object_text_set(rsize, "Scale adjusted (size)");
elm_radio_state_value_set(rsize, 0);
elm_radio_value_pointer_set(rsize, &aid->size);
elm_box_pack_end(sizebox, rsize);
rabsize = elm_radio_add(parent);
elm_object_text_set(rabsize, "Absolute size (absize)");
elm_radio_state_value_set(rabsize, 1);
elm_radio_value_pointer_set(rabsize, &aid->size);
elm_radio_group_add(rabsize, rsize);
elm_box_pack_end(sizebox, rabsize);
evas_object_show(rabsize);
rrelsize = elm_radio_add(parent);
elm_object_text_set(rrelsize, "Relative to line (relsize)");
elm_radio_state_value_set(rrelsize, 2);
elm_radio_value_pointer_set(rrelsize, &aid->size);
elm_radio_group_add(rrelsize, rsize);
elm_box_pack_end(sizebox, rrelsize);
evas_object_show(rrelsize);
vsizebox = elm_box_add(parent);
elm_box_horizontal_set(vsizebox, EINA_TRUE);
elm_box_pack_end(box2, vsizebox);
evas_object_show(vsizebox);
rvfull = elm_radio_add(parent);
elm_object_text_set(rvfull, "Full height (vsize=full)");
elm_radio_state_value_set(rvfull, 0);
elm_radio_value_pointer_set(rvfull, &aid->vsize);
elm_box_pack_end(vsizebox, rvfull);
rvascent = elm_radio_add(parent);
elm_object_text_set(rvascent, "Ascent only (vsize=ascent)");
elm_radio_state_value_set(rvascent, 1);
elm_radio_value_pointer_set(rvascent, &aid->vsize);
elm_radio_group_add(rvascent, rvfull);
elm_box_pack_end(vsizebox, rvascent);
evas_object_show(rvascent);

The first of our entries is here. There's something worth mentioning about the way we'll create this one. Normally, any entry regardless of whether is single line or not, will be set to scrollable, but in this case, since we are limitting how many characters can fit in them and we know we don't need scrolling, we are not setting this flag. This makes the entry have virtually no appearance on screen, other than its text. This is because an entry is just that, a box that holds text, and in order to have some frame around it or a background color, another widget needs to provide this. When an entry is scrollable, the same scroller used internally does this. We are using frames here to provide some decoration around, then creating our entries, set them to single line, add our two filters and the callback for when their value change.

fwidth = elm_frame_add(parent);
elm_object_text_set(fwidth, "Width");
elm_box_pack_end(box2, fwidth);
snprintf(buf, sizeof(buf), "%d", aid->width);
ewidth = elm_entry_add(parent);
elm_entry_single_line_set(ewidth, EINA_TRUE);
elm_entry_markup_filter_append(ewidth, elm_entry_filter_accept_set,
&accept_set);
elm_entry_markup_filter_append(ewidth, elm_entry_filter_limit_size,
&limit_size);
elm_object_text_set(ewidth, buf);
elm_object_content_set(fwidth, ewidth);
evas_object_smart_callback_add(ewidth, "changed", _width_changed_cb, aid);
fheight = elm_frame_add(parent);
elm_object_text_set(fheight, "Height");
elm_box_pack_end(box2, fheight);
evas_object_show(fheight);
snprintf(buf, sizeof(buf), "%d", aid->height);
eheight = elm_entry_add(parent);
elm_entry_single_line_set(eheight, EINA_TRUE);
elm_entry_markup_filter_append(eheight, elm_entry_filter_accept_set,
&accept_set);
elm_entry_markup_filter_append(eheight, elm_entry_filter_limit_size,
&limit_size);
elm_object_text_set(eheight, buf);
elm_object_content_set(fheight, eheight);
evas_object_show(eheight);
evas_object_smart_callback_add(eheight, "changed", _height_changed_cb, aid);

This function ends with the button that will finally call the item into our editting string.

binsert = elm_button_add(parent);
elm_object_text_set(binsert, "Insert");
elm_box_pack_end(box, binsert);
evas_object_show(binsert);
evas_object_smart_callback_add(binsert, "clicked", _btn_insert_cb, aid);
return box;
}

Then we get to the format edition. Here we can add the bold and emphasis tags to parts of our text. There's a lot of manual work to know what to do here, since we are not implementing an entire state manager and the entry itself doesn't, yet, support all the needed capabilities to make this simpler. We begin by getting the format we are using in our function from the button pressed.

Next we need to find out if we need to insert an opening or a closing tag. For this, we store the current cursor position and create a selection from this point until the beginning of our text, and then get the selected text to look for any existing format tags in it. This is currently the only way in which we can find out what formats is being used in the entry.

Once we know what tag to insert, we need a second check in the case it was a closing tag. This is because any other closing tag that comes after would be left dangling alone, so we need to remove it to keep the text consistent.

Finally, we clear our fake selections and return the cursor back to the position it had at first, since there is where we want to insert our format.

And finish by calling our convenience function from before, to insert the text at the current cursor and give focus back to the entry.

A checkbox on the top of our program tells us if the text we are editing will autosave or not. In it's "changed" callback we get the value from the checkbox and call the elm_entry_autosave_set() function with it. If autosave is set, we also call elm_entry_file_save(). This is so the internal timer used to periodically store to disk our changes is started.

Two more functions to show some cursor playing. Whenever we double click anywhere on our entry, we'll find what word is the cursor placed at and select it. Likewise, for triple clicking, we select the entire line.

And finally, the main window of the program contains the entry where we do all the edition and some helping widgets to change format, add icons or change the autosave flag.

And the main entry of the program. Set to scroll, by default we disable autosave and we'll begin with a file set to it because no file selector is being used here. The file is loaded with ELM_TEXT_FORMAT_MARKUP_UTF8 so that any format contained in it is interpreted, otherwise the entry would load it as just text, escaping any tags found and no format or icons would be shown. Then we connect to the double and triple click signals and set focus on the entry so we can start typing right away.

_Elm_Entry_Filter_Limit_Size
Data for the elm_entry_filter_limit_size() entry filter.
Definition: elm_entry_common.h:184
elm_radio_add
Evas_Object * elm_radio_add(Evas_Object *parent)
Add a new radio to the parent.
Definition: efl_ui_radio.c:420
elm_entry_filter_limit_size
void elm_entry_filter_limit_size(void *data, Evas_Object *entry, char **text)
Filter inserted text based on user defined character and byte limits.
Definition: elm_entry.c:4834
elm_frame_add
Evas_Object * elm_frame_add(Evas_Object *parent)
Add a new frame to the parent.
Definition: efl_ui_frame.c:262
elm_box_add
Evas_Object * elm_box_add(Evas_Object *parent)
Add a new box to the parent.
Definition: elm_box.c:363
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition: eina_types.h:339
_Elm_Entry_Filter_Accept_Set::accepted
const char * accepted
Set of characters accepted in the entry.
Definition: elm_entry_common.h:229
_Elm_Entry_Filter_Accept_Set
Data for the elm_entry_filter_accept_set() entry filter.
Definition: elm_entry_common.h:228
elm_button_add
Evas_Object * elm_button_add(Evas_Object *parent)
Add a new button to the parent's canvas.
Definition: efl_ui_button.c:477
EVAS_HINT_EXPAND
#define EVAS_HINT_EXPAND
Use with evas_object_size_hint_weight_set(), evas_object_size_hint_weight_get(), evas_object_size_hin...
Definition: Evas_Common.h:292
evas_object_smart_callback_add
void evas_object_smart_callback_add(Evas_Object *eo_obj, const char *event, Evas_Smart_Cb func, const void *data)
Add (register) a callback function to the smart event specified by event on the smart object obj.
Definition: evas_object_smart.c:1040
Evas_Object
Efl_Canvas_Object Evas_Object
An Evas Object handle.
Definition: Evas_Common.h:180
evas_object_size_hint_weight_set
void evas_object_size_hint_weight_set(Evas_Object *obj, double x, double y)
Sets the hints for an object's weight.
Definition: evas_object_main.c:2638
evas_object_size_hint_align_set
void evas_object_size_hint_align_set(Evas_Object *obj, double x, double y)
Sets the hints for an object's alignment.
Definition: evas_object_main.c:2650
_Elm_Entry_Filter_Limit_Size::max_char_count
int max_char_count
The maximum number of characters allowed.
Definition: elm_entry_common.h:185
evas_object_show
void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition: evas_object_main.c:1814
EVAS_HINT_FILL
#define EVAS_HINT_FILL
Use with evas_object_size_hint_align_set(), evas_object_size_hint_align_get(), evas_object_size_hint_...
Definition: Evas_Common.h:293
elm_entry_filter_accept_set
void elm_entry_filter_accept_set(void *data, Evas_Object *entry, char **text)
Filter inserted text based on accepted or rejected sets of characters.
Definition: elm_entry.c:4894
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition: eina_types.h:539
evas_object_del
void evas_object_del(Evas_Object *obj)
Marks the given Evas object for deletion (when Evas will free its memory).
Definition: evas_object_main.c:928
elm_entry_add
Evas_Object * elm_entry_add(Evas_Object *parent)
This adds an entry to parent object.
Definition: elm_entry.c:4150
elm_object_focus_set
void elm_object_focus_set(Evas_Object *obj, Eina_Bool focus)
Set/unset focus to a given Elementary object.
Definition: elm_focus_legacy.c:374