В этом уроке рассмотрим всплывающие окна, цель и способ их использования.
В конце урока, в качестве практики, добавим одно всплывающее окно для подтверждения запроса на закрытие приложения пользователем.
Часто в приложениях возникает необходимость отображать важную информацию поверх всего пользовательского интерфейса, или запрашивать подтверждение и/или отмену действия. Для этого можно использовать всплывающее окно (Popup).
Для создания виджета используется следующая функция:
Evas_Object *elm_popup_add(Evas_Object *parent);
Как и с другими виджетами, что бы отобразить всплывающее окно необходимо вызвать функцию evas_object_show().
Этот виджет поддерживает исчезновение с задержкой, таким образом можно отобразить информацию и через несколько секунд автоматически свернуть popup. Для установки такой задержки и ее величины используется следующая функция.
void elm_popup_timeout_set(Evas_Object *obj, double timeout);
Здесь первым параметром передается указатель на объект всплывающего окна, а вторым – количество секунд через которое необходимо свернуть указанный виджет.
В некоторых стилях всплывающего окна может быть реализовано сворачивание с анимацией. Для того что бы свернуть такой виджет с таким эффектом, нужно использовать следующую функцию.
void elm_popup_dismiss(Evas_Object *obj);
Заметьте, после вызова, всплывающее окно лишь станет невидимым, но не удалится. Поэтому если вы хотите его удалить, вы должны дождаться конца анимации, а затем в обработчике этого события удалить виджет функцией evas_object_del(), мы рассмотрим события для всплывающих окон дальше в этом уроке.
Если же вы вызовите функцию evas_object_hide(), то окно исчезнет мгновенно без всякого эфекта.
Поскольку мы используем круглый интерфейс нам необходимо указывать это для виджета явно следующим вызовом.
elm_object_style_set(popup, "circle");
Стили для всплывающих окон реализовываются немного иначе чем в других виджетах, поскольку popup является в некоторой степени контейнером. В общем случае процесс установки стиля происходит следующим образом: создается объект макета, затем ему устанавливается один из стандартных стилей предусмотренных для всплывающих окон. И потом в зависимости от выбранного стиля, всплывающее окно наполняется содержимым.
Рассмотрим стили макетов для использования во всплывающих окнах.
Стиль "layout/popup/content/circle":
Имя стиля макета составное, оно состоит из класса, группы и стиля. У каждого представленного стиля класс - это "layout", далее следуют группа "popup" и стиль "content/circle". Установить данный стиль для макета можно с помощью следующей функции:
elm_layout_theme_set(layout, "layout", "popup", "content/circle");
Мы уже делали эту процедуру в 12-м уроке для макетов.
Данный стиль представляет собой две области в которые можно установить текст ("elm.text" – имя области для основного текста, "elm.text.title" – имя области для текста заголовка), а также одну область для установки кнопки, которая будет находится внизу экрана.

Если текст слишком длинный, внутренняя реализация всплывающего окна добавляет скроллер. Теперь, если размер текста превышает высоту виджета, текст начнет прокручиваться.
Установка кнопок во всплывающее окно происходит не напрямую в макет, как было с текстом, а уже задается прямо в popup следующим вызовом.
elm_object_part_content_set(popup, "button1", button);
В версии виджета для носимых устройств с круглым экраном, бывает две такие области для кнопок. Как вы могли догадаться их названия "button1" и "button2".
Стиль "layout/popup/content/circle/buttons1":
Это расширенный за счет анимаций области кнопки стиль "layout/popup/content/circle". Структурно эти два стиля ничем не отличаются.
Стиль "layout/popup/content/circle/buttons2":
Как и макет "content/circle" и "content/circle/buttons1" этот стиль имеет текстовую зону и заголовок. Но отличается тем, что имеет две кнопки слева (button1) и справа (button2), вместо одной внизу. Мы уже рассматривали в уроке 14 стили кнопок ("popup/circle/left" и "popup/circle/right") предусмотренные специально для этих областей.

Вслывающее окно с таким стилем мы и добавим в наше демо приложение для подтверждения завершения его работы.
Виджет поддерживает следующие смарт события:
"timeout" – событие срабатывает по истечению времени заданного вызовом функции elm_popup_timeout_set() когда всплывающее окно автоматически скрывается.
"block,clicked". Некоторые стили всплывающих окон, имеют область которая перекрывает основной пользовательский интерфейс во избежании действий с ним. Когда пользователь нажимает на эту область данное событие и срабатывает.
Так во всплывающем окне со стилем "circle/toast" такая область размещена поверх всего пользовательского интерфейса и поверх самого виджета. Таким образом если пользователь нажмет в любом месте всплывающего окна, сработает данное событие. Обычно при таком действии стоит закрывать popup.
"show,finished" – после указания отобразить всплывающее окно, происходит некоторая анимация появления, данное событие происходит после завершения такой анимации.
"dismissed" – данное событие подобно предыдущему, только срабатывает после анимации исчезновения всплывающего окна, в результате вызова функции elm_popup_dismiss() которую мы рассмотрели ранее в этом уроке.
"language,changed" – поскольку всплывающее окно наследуюется от макета, этот сигнал также поддерживается. При изменении языка в системе вы сможете обработать зарегестрировав колбек на данный сигнал.
Перейдем к практике и закончим наше демо-приложение. Мы будем создавать всплывающее окно с двумя кнопками: отмена и подтверждение. Также будет отображаться вопрос о закрытии приложения. В случае подтверждения мы будем закрывать приложение, иначе просто удалять всплывающее окно по нажатию кнопки отмена или аппаратной кнопки назад.
Добавим указатель на всплывающее окно в нашу структуру с виджетами.
Evas_Object *popup;
Теперь получилась такая структура.
typedef struct _UIData {
Evas_Object *win;
Evas_Object *conform;
Evas_Object *layout;
Evas_Object *button;
Evas_Object *label;
Evas_Object *popup;
int click_count;
} UIData;
Подготовим новую функцию с помощью которой мы будем создавать всплывающие окна.
static void
_exit_popup_create(UIData *ui)
{
}
Создаем объект всплывающего окна и устанавливаем его круглый стиль.
ui->popup = elm_popup_add(ui->layout);
elm_object_style_set(ui->popup, "circle");
Теперь создадим макет для содержимого со стилем "content/circle/buttons2" и установим его как содержимое для popup.
Evas_Object *popup_layout = elm_layout_add(ui->popup);
elm_layout_theme_set(popup_layout, "layout", "popup", "content/circle/buttons2");
elm_object_content_set(ui->popup, popup_layout);
После создания макета можно установить основной текст и заголовок.
elm_object_part_text_set(popup_layout, "elm.text", "Закрыть приложение?");
elm_object_part_text_set(popup_layout, "elm.text.title", "Подтверждение");
Теперь необходимо разобраться с кнопками для всплывающего окна.
Будем использовать две кнопки со стилями "popup/circle/left" и "popup/circle/right". Нам нужно будет добавить иконки (крестик и галочку) в эти кнопки, посредством виджета-изображения.
Можете воспользоваться нашими ресурсами скачав эти картинки ниже и поместив их в папку res/ вашего проекта.


Мы сейчас не будем останавливаться подробно на виджете для отображения изображений, можете просто скопировать две следующие функции для создания виджета с указанным изображением в ваш код.
static void
_file_abs_resource_path_get(char *res_file_path, char *abs_path, int buf_size)
{
char *res_dir_path = app_get_resource_path();
if (res_dir_path)
{
snprintf(abs_path, buf_size, "%s%s", res_dir_path, res_file_path);
free(res_dir_path);
}
}
static Evas_Object *
_image_create(Evas_Object *parent, char *image_name)
{
Evas_Object *icon = elm_image_add(parent);
char image_path[PATH_MAX] = {0,};
_file_abs_resource_path_get(image_name, image_path, PATH_MAX);
elm_image_file_set(icon, image_path, NULL);
evas_object_show(icon);
return icon;
}
Вызов последней функции создаст и вернет объект виджета-изображения на основании переданного родительского виджета и названия изображения находящегося в папке res/ вашего проекта.
Создадим функцию которой будем пользоваться для создания кнопок для всплывающего окна, будем передавать родительский виджет, стиль для кнопки и имя иконки которую необходимо использовать в кнопке.
static Evas_Object *
_popup_button_create(Evas_Object *parent, char *style, char *icon_name)
{
Evas_Object *button = elm_button_add(parent);
elm_object_style_set(button, style);
Evas_Object *btn_icon = _image_create(button, icon_name);
elm_object_content_set(button, btn_icon);
evas_object_show(button);
return button;
}
Мы подробно рассматривали кнопку и ее создание в 14-м уроке.
Вспомогательные функции мы реализовали, теперь воспользуемся ими для продолжения создания всплывающего окна.
Создаем левую кнопку для отмены выхода из приложения и помещаем ее в область "button1" всплывающего окна. И сразу зарегистрируем обработчик нажатия этой кнопки.
static void
_exit_popup_create(UIData *ui)
{
...
Evas_Object *left_button = _popup_button_create(popup_layout, "popup/circle/left", "cancel.png");
elm_object_part_content_set(ui->popup, "button1", left_button);
evas_object_smart_callback_add(left_button, "clicked", _cancel_exit_cb, ui);
...
}
Создаем правую кнопку для подтверждения выхода из приложения и помещаем ее в область "button2" всплывающего окна. И сразу зарегистрируем обработчик нажатия этой кнопки.
static void
_exit_popup_create(UIData *ui)
{
...
Evas_Object *right_button = _popup_button_create(popup_layout, "popup/circle/right", "apply.png");
elm_object_part_content_set(ui->popup, "button2", right_button);
evas_object_smart_callback_add(right_button, "clicked", _apply_exit_cb, ui);
...
}
Реализуем небольшую функцию для удаления всплывающего окна.
static void
_remove_popup(UIData *ui)
{
evas_object_del(ui->popup);
ui->popup = NULL;
}
Мы устанавливаем указателю значение NULL после удаления объекта, поскольку функционал метода evas_object_del() не позволяет это сделать автоматически, а нам далее предстоит следить создано ли всплывающее окно как-раз по состоянию указателя на виджет.
Теперь реализуем функционал самих обработчиков для созданных кнопок.
static void
_cancel_exit_cb(void *data, Evas_Object *obj, void *info)
{
UIData *ui = data;
_remove_popup(ui);
}
static void
_apply_exit_cb(void *data, Evas_Object *obj, void *info)
{
ui_app_exit();
}
Здесь в обработчике отмены выхода из приложения мы и воспользовались нашей функцией удаления и сброса указателя всплывающего окна. В обработчике подтверждения выхода, вызываем функцию для завершения приложения.
Последним шагом отображаем всплывающее окно уже знакомой функцией.
static void
_exit_popup_create(UIData *ui)
{
...
evas_object_show(ui->popup);
}
Теперь переделаем функцию обработчик нажатия аппаратной кнопки назад. Нам нужно создавать и отображать popup если он еще не был создан, в противном случае удалять виджет. Обработчик должен выглядеть таким образом.
static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
UIData *ui = data;
if (!ui->popup)
{
_exit_popup_create(ui);
}
else
{
_remove_popup(ui);
}
}
Запустим наше приложение и посмотрим как оно работает.
На этом создание всплывающего окна и нашего демо-приложения завершено.
Полный исходный код этого урока вы можете скачать здесь WearLesson017.