--- /dev/null
+/*
+ * Copyright 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_CIRCLE_MENU_H
+#define UI_CIRCLE_MENU_H
+
+#include "Ui/Menu.h"
+#include <vector>
+
+namespace Ui
+{
+ class EXPORT_API CircleMenu : public Menu
+ {
+ public:
+ /**
+ * @see Menu::addItem().
+ */
+ Elm_Object_Item *addItem(const char *text, ItemCallback callback);
+
+ /**
+ * @see Menu::show().
+ */
+ void show();
+
+ private:
+ virtual Evas_Object *onCreate(Evas_Object *parent) override;
+ void updateItemsStyle();
+
+ std::vector<Elm_Object_Item *> m_Items;
+ };
+}
+
+#endif /* UI_CIRCLE_MENU_H */
--- /dev/null
+/*
+ * Copyright 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UI_MENU_H
+#define UI_MENU_H
+
+#include "Ui/Control.h"
+#include <functional>
+
+namespace Ui
+{
+ class View;
+ class Window;
+
+ class EXPORT_API Menu : public Control
+ {
+ public:
+ /**
+ * @brief Item selection callback
+ */
+ typedef std::function<void()> ItemCallback;
+
+ Menu();
+ virtual ~Menu() override;
+
+ /**
+ * @brief Add menu item
+ * @param[in] text Item text
+ * @param[in] callback Item selection callback
+ * @return Added item on success, otherwise nullptr
+ */
+ Elm_Object_Item *addItem(const char *text, ItemCallback callback);
+
+ /**
+ * @brief Show menu at the bottom of application window.
+ */
+ void show();
+
+ protected:
+ virtual Evas_Object *onCreate(Evas_Object *parent) override;
+
+ private:
+ void onViewNavigation(Evas_Object *obj, void *eventInfo);
+ void onWindowResized(Evas *e, Evas_Object *obj, void *eventInfo);
+
+ static void onItemSelect(void *data, Evas_Object *obj, void *item);
+ static void onItemDestroy(void *data, Evas_Object *obj, void *item);
+ void onDismissed(Evas_Object *obj, void *eventInfo);
+
+ View *m_View;
+ Window *m_Window;
+ };
+}
+
+#endif /* UI_MENU_H */
--- /dev/null
+/*
+ * Copyright 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ui/CircleMenu.h"
+
+using namespace Ui;
+
+Elm_Object_Item *CircleMenu::addItem(const char *text, ItemCallback callback)
+{
+ m_Items.push_back(Menu::addItem(text, std::move(callback)));
+ return m_Items.back();
+}
+
+void CircleMenu::show()
+{
+ Evas_Coord x = 0, y = 0, w = 0, h = 0;
+ Evas_Object *parent = elm_object_parent_widget_get(getEvasObject());
+ evas_object_geometry_get(parent , &x, &y, &w, &h);
+ evas_object_move(getEvasObject(), x + (w / 2), y + (h / 2));
+ evas_object_show(getEvasObject());
+
+ updateItemsStyle();
+}
+
+Evas_Object *CircleMenu::onCreate(Evas_Object *parent)
+{
+ auto menu = Menu::onCreate(parent);
+ elm_object_style_set(menu, "select_mode");
+ elm_ctxpopup_direction_priority_set(menu,
+ ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_DOWN,
+ ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_DOWN);
+
+ return menu;
+}
+
+void CircleMenu::updateItemsStyle()
+{
+ if (m_Items.size() >= 2) {
+ elm_object_item_style_set(m_Items.front(), "select_mode/top");
+ elm_object_item_style_set(m_Items.back(), "select_mode/bottom");
+ }
+}
--- /dev/null
+/*
+ * Copyright 2017 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ui/Menu.h"
+#include "Ui/View.h"
+#include "Ui/Window.h"
+#include "Utils/Callback.h"
+
+#include <efl_extension.h>
+
+using namespace Ui;
+
+Menu::Menu()
+ : m_View(nullptr), m_Window(nullptr)
+{
+}
+
+Menu::~Menu()
+{
+ if (m_View) {
+ evas_object_smart_callback_del_full(m_View->getEvasObject(), EVENT_VIEW_NAVIGATION,
+ makeCallback(&Menu::onViewNavigation), this);
+ }
+ if (m_Window) {
+ evas_object_event_callback_del_full(m_Window->getEvasObject(), EVAS_CALLBACK_RESIZE,
+ makeCallback(&Menu::onWindowResized), this);
+ }
+}
+
+Elm_Object_Item *Menu::addItem(const char *text, ItemCallback callback)
+{
+ Elm_Object_Item *item = elm_ctxpopup_item_append(getEvasObject(), text, nullptr,
+ &Menu::onItemSelect, new ItemCallback(std::move(callback)));
+ elm_object_item_text_translatable_set(item, EINA_TRUE);
+ elm_object_item_del_cb_set(item, &Menu::onItemDestroy);
+ return item;
+}
+
+void Menu::show()
+{
+ Evas_Coord y = 0, w = 0, h = 0;
+ Evas_Object *parent = elm_ctxpopup_hover_parent_get(getEvasObject());
+ evas_object_geometry_get(parent, nullptr, &y, &w, &h);
+
+ Evas_Object *menu = getEvasObject();
+ int menuWidth = 0;
+ evas_object_geometry_get(menu, nullptr, nullptr, &menuWidth, nullptr);
+ evas_object_move(menu, w / 2 - menuWidth / 2, y + h);
+ evas_object_show(menu);
+}
+
+Evas_Object *Menu::onCreate(Evas_Object *parent)
+{
+ Evas_Object *menu = elm_ctxpopup_add(parent);
+ elm_object_style_set(menu, "more/default");
+
+ evas_object_smart_callback_add(menu, "dismissed",
+ makeCallback(&Menu::onDismissed), this);
+ eext_object_event_callback_add(menu, EEXT_CALLBACK_BACK,
+ eext_ctxpopup_back_cb, nullptr);
+ eext_object_event_callback_add(menu, EEXT_CALLBACK_MORE,
+ eext_ctxpopup_back_cb, nullptr);
+
+ m_View = findParent<View>(parent);
+ if (m_View) {
+ evas_object_smart_callback_add(m_View->getEvasObject(), EVENT_VIEW_NAVIGATION,
+ makeCallback(&Menu::onViewNavigation), this);
+ }
+
+ m_Window = findParent<Window>(parent);
+ if (m_Window) {
+ elm_ctxpopup_hover_parent_set(menu, m_Window->getEvasObject());
+ evas_object_event_callback_add(m_Window->getEvasObject(), EVAS_CALLBACK_RESIZE,
+ makeCallback(&Menu::onWindowResized), this);
+ }
+
+ return menu;
+}
+
+void Menu::onViewNavigation(Evas_Object *obj, void *eventInfo)
+{
+ if (!eventInfo) {
+ elm_ctxpopup_dismiss(getEvasObject());
+ }
+}
+
+void Menu::onWindowResized(Evas *e, Evas_Object *obj, void *eventInfo)
+{
+ show();
+}
+
+void Menu::onItemSelect(void *data, Evas_Object *obj, void *item)
+{
+ ItemCallback &callback = *(ItemCallback *) data;
+ if (callback) {
+ callback();
+ }
+
+ elm_ctxpopup_dismiss(obj);
+}
+
+void Menu::onItemDestroy(void *data, Evas_Object *obj, void *item)
+{
+ delete (ItemCallback *) data;
+}
+
+void Menu::onDismissed(Evas_Object *obj, void *eventInfo)
+{
+ delete this;
+}