The (Circle) Dialer sample application demonstrates how you can create a circular dialer and use the Entry UI component to show the digits entered by the user. It shows how to hijack upper layer touch events to determine, without a button, which digits the user has touched.
With this application, you can understand a simple circular dialer view and touch event control techniques. The application does not include call functions or companion mode operation.
For information on creating the sample application project in the IDE, see Creating Sample Applications.
The following figure illustrates the main screen of the (Circle) Dialer.
Figure: (Circle) Dialer screen
On the application screen, the user can dial a phone number by touching the digits at the edge of the screen.
Source Files
You can create and view the sample application project including the source files in the IDE.
File name | Description |
---|---|
edje/images | This file contains the image files used in the main.edc file. |
inc/data.h | This file contains information and definition of the variables and functions used in the C files, especially in the data.c file. |
inc/dialer.h | This file contains information and definition of the variables and functions used in the C files, especially in the main.c file. |
inc/view.h | This file contains information and definition of the variables and functions used in the C files, especially in the view.c file. |
res/edje/main.edc | This file is for the UI and contains style, image, and position of the sample application. |
res/images | This directory contains the image files used in the C files. |
src/data.c | This file contains the functions for retrieving and creating data for the application. |
src/main.c | This file contains the functions related to the application life-cycle, callback functions, and view control. |
src/view.c | This file contains the functions for implementing the views and handling events. |
Implementation
Application Layout
To create the basic application layout, use the view_create() function. The window and conformant components are essential parts of the application layout.
void view_create(void) { // Create a window s_info.win = view_create_win(PACKAGE); if (s_info.win == NULL) { dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window."); return; } // Create a conformant s_info.conform = view_create_conformant_without_indicator(s_info.win); if (s_info.conform == NULL) { dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant"); return; } // Show the window after the main view is set up evas_object_show(s_info.win); }
To create a layout with the EDJ file, use the view_dialer_create() function:
void view_dialer_create(const char *file_path) { s_info.layout = view_create_layout_for_conformant(s_info.conform, file_path, GRP_MAIN, _dialer_layout_cb, NULL); if (s_info.layout == NULL) { evas_object_del(s_info.win); dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a content."); return; } }
Main View
The main view contains digit buttons, function image buttons (Call and Delete), and a digit input field showing the selected digits.
Figure: Main view
The structure of the main view is very simple. The circular positioning text and digits are merged with a background image. The result is a screen where the user feels like they are touching a button at the edge of the dial, while, in the actual implementation, the button is just an image and text. For the details of this kind of trick button implementation, see Making a Digit Button.
Figure: Main view structure
To implement the main view:
- Set the background image and the image buttons using the view_set_image() and view_set_color() functions:
// main.c // Set the background image to the "sw.button.bg" part of EDC image = data_get_image_path("sw.button.bg"); view_set_image(view_dialer_get_layout_object(), "sw.button.bg", image); view_set_color(view_dialer_get_layout_object(), "sw.button.bg", 8, 36, 61, 255); image = data_get_image_path("sw.button.call"); view_set_image(view_dialer_get_layout_object(), "sw.button.call", image); view_set_color(view_dialer_get_layout_object(), "sw.button.call", 0, 214, 46, 255); image = data_get_image_path("sw.button.delete"); view_set_button(view_dialer_get_layout_object(), "sw.button.delete", "focus", image, NULL, _btn_down_cb, _btn_up_cb, NULL, NULL); view_set_color(view_dialer_get_layout_object(), "sw.button.delete", 250, 250, 250, 255);
- To do something when the button goes down or up, customize the _btn_down_cb() and _btn_up_cb() callbacks, or create another function for the action.
In this example, the _btn_down_cb() and _btn_up_cb() callbacks handle the touch events of the Delete button. When the user touches the Delete button, the _btn_down_cb() callback changes the color of the button and removes a digit in the digit input field, while the _btn_up_cb() callback returns the button color to normal.
static void _btn_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) { dlog_print(DLOG_DEBUG, LOG_TAG, "button is pressed."); // Delete an input character view_dialer_set_entry_text(ENTRY_TEXT_BACKSPACE, NULL); // Change the button color evas_object_color_set(obj, 250, 250, 250, 102); } static void _btn_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) { dlog_print(DLOG_DEBUG, LOG_TAG, "button is released."); // Change the button color evas_object_color_set(obj, 250, 250, 250, 255); }
- To implement the digit input field in the center of the screen, use the EFL Entry component. The EFL entry is a convenient UI component that shows a box in which the user can enter text.
The view_create_entry() function creates an entry component and returns the component object. The object is needed to control the entry component, for example, to delete its text or change the text style.
void view_dialer_set_entry(const char *part_name) { if (s_info.layout == NULL) { dlog_print(DLOG_ERROR, LOG_TAG, "parent layout is NULL."); return; } if (part_name == NULL) { dlog_print(DLOG_ERROR, LOG_TAG, "part name is NULL."); return; } s_info.entry = view_create_entry(s_info.layout, part_name, NULL, NULL); }
Making a Digit Button
The trick digit button in the dialer is implemented touch event hijacking. The button is actually just images and texts as shown in the main view structure. You can transform those images and text into a trick button by hijacking user touch events.
Figure: Transparent layer for touch event hijacking
To implement touch event hijacking:
- Create a transparent layer for touch event handling
- The evas_object_rectangle_add() function creates a rectangle object on the canvas.
- The evas_object_repeat_events_set() function enables the touch events to be repeated on the lower layers. Event repetition is very important to correctly handle touch events for each layer.
- Register callback functions using the evas_object_event_callback_add() function.
// Create a rectangle object to the target window for hijacking touch events Evas_Object *view_dialer_create_rectangle() { Evas_Object *rect = NULL; // Add the rectangle object to parent rect = evas_object_rectangle_add(evas_object_evas_get(s_info.layout)); if (rect == NULL) { dlog_print(DLOG_ERROR, LOG_TAG, "failed to add a rectangle"); return NULL; } // Set the color to transparent // You can change the color for other purposes, such as a color filter layer evas_object_color_set(rect, 255, 255, 255, 0); // Set the event repeat mode evas_object_repeat_events_set(rect, EINA_TRUE); // Set the size of the object evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(s_info.win, rect); evas_object_show(rect); // Set a callback for object events evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_DOWN, _rectangle_mouse_down_cb, NULL); evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_UP, _rectangle_mouse_up_cb, NULL); evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_MOVE, _rectangle_mouse_move_cb, NULL); return rect; }
-
You must calculate the position of the touch events that the transparent layer catches on the canvas. Events are handled or discarded according to the position of the touch event.
In the calculation, check the radius and slope. The radius from the center of the screen to the position of the touch event must be longer than the distance from the center to the button image. If the radius is shorter than the threshold, the touch event is ignored and just repeated on the lower layers.
To determine the correct digit being touched, use the slope of the touch event position. For example, in the following figure, if the slope of some event position is higher than line 1's slope and lower than line 2's slope, you can determine that the digit 1 button is being touched.
The _rectangle_mouse_down_cb() callback is called with the touch event information from the transparent layer. The _get_btn_dial_number() function is used to calculate the position of the digit by checking the radius and slope.
static void _rectangle_mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down*) event_info; s_info.mouse_down_dial_num = _get_btn_dial_number(ev->output.x, ev->output.y); } // Get the dial number of the touch event position by calculating distance and slope static int _get_btn_dial_number(int evt_x, int evt_y) { int result = -1; // Calculate x and y from the CENTER_REF point int x = evt_x - CENTER_REF_X; int y = CENTER_REF_Y - evt_y; // Calculate the slope and radius from the CENTER_REF point float slope = (float)y / (float)x; float radius = sqrt(x*x + y*y); if (radius > RADIUS_MIN) { if (x == 0) { if (y >= 0) result = 0; else result = 5; } else if (y == 0) { if (x >= 0) result = 2; else result = 8; } else { if (slope > SLOPE_72_DEGREE) { if (y > 0) result = 0; else result = 5; } else if (slope > SLOPE_36_DEGREE) { if (y > 0) result = 1; else result = 6; } else if (slope > SLOPE_180_DEGREE) { if (y > 0) result = 2; else result = 7; } else if (slope > SLOPE_144_DEGREE) { if (y > 0) result = 8; else result = 3; } else if (slope > SLOPE_108_DEGREE) { if (y > 0) result = 9; else result = 4; } else { if (y > 0) result = 0; else result = 5; } } } else { dlog_print(DLOG_DEBUG, LOG_TAG, "Ignore touch event under min radius"); } return result; }