Entry - Example of simple editing

As a general overview of Elm_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 Elm_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 *win;
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;
Evas_Object
Efl_Canvas_Object Evas_Object
An Evas Object handle.
Definition Evas_Common.h:185

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);
elm_object_focus_set(e, EINA_TRUE);
}
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition eina_types.h:539
elm_entry_entry_insert
void elm_entry_entry_insert(Elm_Entry *obj, const char *entry)
Inserts the given text into the entry at the current cursor position.
Definition elm_entry_eo.legacy.c:465
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

One of the buttons on the top will trigger an Elm_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);
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition eina_types.h:339
evas_object_del
EVAS_API 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
}

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_char_count = 5,
.max_byte_count = 0
};
box = elm_box_add(parent);
evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL);
sizeopts = elm_frame_add(parent);
elm_object_text_set(sizeopts, "Size");
evas_object_size_hint_weight_set(sizeopts, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(sizeopts, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(box, sizeopts);
evas_object_show(sizeopts);
box2 = elm_box_add(parent);
evas_object_size_hint_weight_set(box2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(box2, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_content_set(sizeopts, box2);
evas_object_show(box2);
sizebox = elm_box_add(parent);
elm_box_horizontal_set(sizebox, EINA_TRUE);
evas_object_size_hint_weight_set(sizebox, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(sizebox, EVAS_HINT_FILL, EVAS_HINT_FILL);
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);
evas_object_show(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);
evas_object_size_hint_weight_set(vsizebox, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(vsizebox, EVAS_HINT_FILL, EVAS_HINT_FILL);
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);
evas_object_show(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);
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:297
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:298
elm_box_horizontal_set
void elm_box_horizontal_set(Elm_Box *obj, Eina_Bool horizontal)
Set the horizontal orientation.
Definition elm_box_eo.legacy.c:27
elm_box_add
Evas_Object * elm_box_add(Evas_Object *parent)
Add a new box to the parent.
Definition elm_box.c:363
elm_box_pack_end
void elm_box_pack_end(Elm_Box *obj, Efl_Canvas_Object *subobj)
Add an object at the end of the pack list.
Definition elm_box_eo.legacy.c:57
Elm_Entry_Filter_Limit_Size
struct _Elm_Entry_Filter_Limit_Size Elm_Entry_Filter_Limit_Size
Data for the elm_entry_filter_limit_size() entry filter.
Definition elm_entry_common.h:178
Elm_Entry_Filter_Accept_Set
struct _Elm_Entry_Filter_Accept_Set Elm_Entry_Filter_Accept_Set
Data for the elm_entry_filter_accept_set() entry filter.
Definition elm_entry_common.h:222
elm_frame_add
Evas_Object * elm_frame_add(Evas_Object *parent)
Add a new frame to the parent.
Definition efl_ui_frame.c:274
elm_radio_add
Evas_Object * elm_radio_add(Evas_Object *parent)
Add a new radio to the parent.
Definition efl_ui_radio.c:401
elm_radio_group_add
void elm_radio_group_add(Efl_Ui_Radio *obj, Efl_Ui_Radio *group)
Add this radio to a group of other radio objects.
Definition efl_ui_radio.c:465
elm_radio_state_value_set
void elm_radio_state_value_set(Efl_Ui_Radio *obj, int value)
Set the integer value that this radio object represents.
Definition efl_ui_radio_eo.legacy.c:3
elm_radio_value_pointer_set
void elm_radio_value_pointer_set(Efl_Ui_Radio *obj, int *valuep)
Set a convenience pointer to an integer, which changes when radio group value changes.
Definition efl_ui_radio.c:428
evas_object_show
EVAS_API void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition evas_object_main.c:1814
evas_object_size_hint_weight_set
EVAS_API 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
EVAS_API 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

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");
evas_object_size_hint_weight_set(fwidth, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(fwidth, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(box2, fwidth);
evas_object_show(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);
evas_object_size_hint_weight_set(ewidth, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(ewidth, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_content_set(fwidth, ewidth);
evas_object_show(ewidth);
evas_object_smart_callback_add(ewidth, "changed", _width_changed_cb, aid);
fheight = elm_frame_add(parent);
elm_object_text_set(fheight, "Height");
evas_object_size_hint_weight_set(fheight, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(fheight, EVAS_HINT_FILL, EVAS_HINT_FILL);
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);
evas_object_size_hint_weight_set(eheight, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(eheight, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_content_set(fheight, eheight);
evas_object_show(eheight);
evas_object_smart_callback_add(eheight, "changed", _height_changed_cb, aid);
elm_entry_markup_filter_append
void elm_entry_markup_filter_append(Elm_Entry *obj, Elm_Entry_Filter_Cb func, void *data)
Append a markup filter function for text inserted in the entry.
Definition elm_entry_eo.legacy.c:543
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:4925
elm_entry_single_line_set
void elm_entry_single_line_set(Elm_Entry *obj, Eina_Bool single_line)
Sets the entry to single line mode.
Definition elm_entry_eo.legacy.c:123
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:4865
elm_entry_add
Evas_Object * elm_entry_add(Evas_Object *parent)
This adds an entry to parent object.
Definition elm_entry.c:4183
evas_object_smart_callback_add
EVAS_API 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

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;
}
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:459

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.