diff options
author | Eugene Kurzberg <i.kurtsberg@samsung.com> | 2017-07-21 17:59:51 +0300 |
---|---|---|
committer | Eugene Kurzberg <i.kurtsberg@samsung.com> | 2017-07-24 10:00:47 +0300 |
commit | 9a17c5c733e5b15a5909d03a189300e50252ae2a (patch) | |
tree | 0ed2f6acd545cefe19c97806044a9c4860207af1 | |
parent | ba10d486e51671cac51a6fa8f5562354e87c891a (diff) | |
download | alarm-9a17c5c733e5b15a5909d03a189300e50252ae2a.tar.gz alarm-9a17c5c733e5b15a5909d03a189300e50252ae2a.tar.bz2 alarm-9a17c5c733e5b15a5909d03a189300e50252ae2a.zip |
Merge and integrate new ListView and SelectView.
Change-Id: I3f9c03d883f4e18da5b50c3aac357f4f3428f137
Signed-off-by: Eugene Kurzberg <i.kurtsberg@samsung.com>
23 files changed, 979 insertions, 1105 deletions
diff --git a/alarm-app/inc/List/AlarmItem.h b/alarm-app/inc/List/AlarmItem.h index f95647a..1ee83b3 100644 --- a/alarm-app/inc/List/AlarmItem.h +++ b/alarm-app/inc/List/AlarmItem.h @@ -29,7 +29,7 @@ namespace Common namespace List { - class AlarmItem: public Ux::SelectItem + class AlarmItem : public Ux::SelectItem { public: /** @@ -38,19 +38,7 @@ namespace List */ explicit AlarmItem(Common::Model::Alarm &alarm); - /** - * @return Alarm associated with the item. - */ - const Common::Model::Alarm &getAlarm() const; - - /** - * @brief Update the item according to the changes. - * @param[in] changes Mask specifying which data has changed - */ - void update(int changes); - private: - virtual Ux::SelectResult getDefaultResult() const override; virtual Elm_Gen_Item_Class *getItemClass() const override; virtual char *getText(Evas_Object *parent, const char *part) override; virtual Evas_Object *getContent(Evas_Object *parent, const char *part) override; @@ -58,9 +46,8 @@ namespace List virtual void onInserted() override; virtual void onSelected() override; + virtual void onUpdate(int changes) override; void onAlarmEnabled(Evas_Object *check, void *eventInfo); - - Common::Model::Alarm &m_Alarm; }; } diff --git a/alarm-app/inc/List/AlarmsView.h b/alarm-app/inc/List/AlarmsView.h index 9586d3f..da1081c 100644 --- a/alarm-app/inc/List/AlarmsView.h +++ b/alarm-app/inc/List/AlarmsView.h @@ -21,11 +21,6 @@ #include "Ux/SelectView.h" #include <system_settings.h> -namespace Ui -{ - class Genlist; -} - namespace Ux { class ActionButtonsItem; @@ -33,8 +28,7 @@ namespace Ux namespace List { - class AlarmItem; - class AlarmsView: public Ux::SelectView + class AlarmsView : public Ux::SelectView { public: /** @@ -53,43 +47,20 @@ namespace List void setAddCallback(AddCallback callback); private: - virtual Evas_Object *onCreate(Evas_Object *parent) override; - virtual void onCreated() override; - virtual void onPageAttached(Ui::NavigatorPage *page) override; - virtual void onNavigation(bool isCurrent) override; - virtual void onSelectModeChanged(Ux::SelectMode selectMode) override; - virtual void onSelectCountChanged(size_t selectCount) override; - virtual Evas_Object *createDoneButton() override; - virtual Ux::MultiSelector *createMultiSelector() override; - - Evas_Object *createContentLayout(Evas_Object *parent); - Evas_Object *createNoContents(Evas_Object *parent); - void updateEmptyState(); + virtual Evas_Object *createContent(Evas_Object *parent) override; + virtual Evas_Object *createNoContents(Evas_Object *parent) override; + virtual Ux::ListItem *createItem(::Model::DataItem &dataItem) override; - AlarmItem *createItem(::Model::DataItem &dataItem); - AlarmItem *insertItem(AlarmItem *alarmItem); - - Ui::GenItem *getCenterItem(); - Elm_Interface_Atspi_Accessible *getNextItem(); - Elm_Interface_Atspi_Accessible *getPrevItem(); - - Evas_Point getWindowCenter(); - - void onAlarmInserted(::Model::DataItem &dataItem); - void onAlarmUpdated(AlarmItem *item, int changes); - void onAlarmDeleted(AlarmItem *item); + virtual void onFilled() override; + virtual void onItemInserted(Ux::ListItem *item) override; + virtual void onSelectModeChanged(Ux::SelectMode selectMode) override; void onAddPressed(); void onFormatChanged(system_settings_key_e key); void onItemLongpressed(Evas_Object *genlist, Elm_Object_Item *item); bool onSelectFinished(); Model::AlarmProvider m_Provider; - Evas_Object *m_NoContents; - Evas_Object *m_ContentLayout; - Ui::Genlist *m_Genlist; Ux::ActionButtonsItem *m_AddAlarmItem; - Evas_Object *m_DeleteButton; - AddCallback m_OnAlarmAdded; }; } diff --git a/alarm-app/inc/OperationPickController.h b/alarm-app/inc/OperationPickController.h index 9edc855..23d7101 100644 --- a/alarm-app/inc/OperationPickController.h +++ b/alarm-app/inc/OperationPickController.h @@ -18,12 +18,7 @@ #define OPERATION_PICK_CONTROLLER_H #include "App/OperationController.h" -#include "Ux/SelectTypes.h" - -namespace Ui -{ - class View; -} +#include "Ux/SelectView.h" class OperationPickController : public App::OperationController { diff --git a/alarm-app/src/List/AlarmItem.cpp b/alarm-app/src/List/AlarmItem.cpp index 9704753..6573d36 100644 --- a/alarm-app/src/List/AlarmItem.cpp +++ b/alarm-app/src/List/AlarmItem.cpp @@ -39,37 +39,10 @@ using namespace Common::Model; using namespace List; AlarmItem::AlarmItem(Alarm &alarm) - : m_Alarm(alarm) + : ListItem(alarm), SelectItem(alarm) { } -const Common::Model::Alarm &AlarmItem::getAlarm() const -{ - return m_Alarm; -} - -void AlarmItem::update(int changes) -{ - if (changes & Alarm::ChangedDate) { - GenItem::update(PART_TIME, ELM_GENLIST_ITEM_FIELD_TEXT); - if (!m_Alarm.getRepeat()) { - GenItem::update(PART_DATE, ELM_GENLIST_ITEM_FIELD_TEXT); - } - } - if (changes & Alarm::ChangedRepeat) { - GenItem::update(PART_DATE, ELM_GENLIST_ITEM_FIELD_TEXT); - } - if (changes & Alarm::ChangedEnabled) { - Evas_Object *check = elm_object_item_part_content_get(getObjectItem(), PART_ON_OFF); - elm_check_state_set(check, m_Alarm.isEnabled()); - } -} - -Ux::SelectResult AlarmItem::getDefaultResult() const -{ - return { 0, &m_Alarm }; -} - Elm_Gen_Item_Class *AlarmItem::getItemClass() const { static Elm_Gen_Item_Class itc = createItemClass("2text.1icon.1"); @@ -78,13 +51,14 @@ Elm_Gen_Item_Class *AlarmItem::getItemClass() const char *AlarmItem::getText(Evas_Object *parent, const char *part) { + auto &alarm = getDataItem<Alarm>(); if (strcmp(part, PART_TIME) == 0) { - return strdup(Common::formatTime(m_Alarm.getDate(), AM_PM_FONT_SIZE)); + return strdup(Common::formatTime(alarm.getDate(), AM_PM_FONT_SIZE)); } else if (strcmp(part, PART_DATE) == 0) { - if (m_Alarm.getRepeat()) { - return strdup(Common::formatRepeat(m_Alarm.getRepeat())); + if (alarm.getRepeat()) { + return strdup(Common::formatRepeat(alarm.getRepeat())); } else { - return strdup(Common::formatDate(m_Alarm.getDate()).c_str()); + return strdup(Common::formatDate(alarm.getDate()).c_str()); } } @@ -96,7 +70,7 @@ Evas_Object *AlarmItem::getContent(Evas_Object *parent, const char *part) if (strcmp(part, PART_ON_OFF) == 0) { Evas_Object *check = elm_check_add(parent); elm_object_style_set(check, STYLE_CHECK_ALARM_ON_OFF); - elm_check_state_set(check, m_Alarm.isEnabled()); + elm_check_state_set(check, getDataItem<Alarm>().isEnabled()); evas_object_propagate_events_set(check, EINA_FALSE); evas_object_size_hint_min_set(check, ON_OFF_WH, ON_OFF_WH); @@ -119,20 +93,23 @@ Evas_Object *AlarmItem::getContent(Evas_Object *parent, const char *part) char *AlarmItem::getAccessibleName(Evas_Object *obj) { + auto &alarm = getDataItem<Alarm>(); + std::string name; - name.append(Common::formatTime(m_Alarm.getDate())); + name.append(Common::formatTime(alarm.getDate())); name.append(", "); - if (m_Alarm.getRepeat()) { - name.append(Common::formatVerbalRepeat(m_Alarm.getRepeat())); + if (alarm.getRepeat()) { + name.append(Common::formatVerbalRepeat(alarm.getRepeat())); } else { - name.append(Common::formatVerbalDate(m_Alarm.getDate())); + name.append(Common::formatVerbalDate(alarm.getDate())); } return strdup(name.c_str()); } void AlarmItem::onInserted() { + SelectItem::onInserted(); elm_atspi_accessible_name_cb_set(getObjectItem(), makeCallback(&AlarmItem::getAccessibleName), this); } @@ -145,16 +122,36 @@ void AlarmItem::onSelected() } if (auto navigator = getParent()->findParent<Ui::Navigator>()) { - navigator->navigateTo(new Input::InputView(m_Alarm)); + navigator->navigateTo(new Input::InputView(getDataItem<Alarm>())); + } +} + +void AlarmItem::onUpdate(int changes) +{ + auto &alarm = getDataItem<Alarm>(); + if (changes & Alarm::ChangedDate) { + GenItem::update(PART_TIME, ELM_GENLIST_ITEM_FIELD_TEXT); + if (!alarm.getRepeat()) { + GenItem::update(PART_DATE, ELM_GENLIST_ITEM_FIELD_TEXT); + } + } + if (changes & Alarm::ChangedRepeat) { + GenItem::update(PART_DATE, ELM_GENLIST_ITEM_FIELD_TEXT); + } + if (changes & Alarm::ChangedEnabled) { + Evas_Object *check = elm_object_item_part_content_get(getObjectItem(), PART_ON_OFF); + elm_check_state_set(check, alarm.isEnabled()); } } void AlarmItem::onAlarmEnabled(Evas_Object *check, void *eventInfo) { - m_Alarm.setEnabled(elm_check_state_get(check)); - AlarmConsumer::getInstance().updateDataItem(m_Alarm, [this](bool isSuccess, int alarmId) { - if (isSuccess && m_Alarm.isEnabled()) { - auto popup = new Common::AlarmSetPopup(m_Alarm); + auto &alarm = getDataItem<Alarm>(); + alarm.setEnabled(elm_check_state_get(check)); + AlarmConsumer::getInstance().updateDataItem(alarm, [this](bool isSuccess, int alarmId) { + auto &alarm = getDataItem<Alarm>(); + if (isSuccess && alarm.isEnabled()) { + auto popup = new Common::AlarmSetPopup(alarm); popup->create(getParent()->getEvasObject()); popup->show(); } diff --git a/alarm-app/src/List/AlarmsView.cpp b/alarm-app/src/List/AlarmsView.cpp index 7656e0b..c8c668e 100644 --- a/alarm-app/src/List/AlarmsView.cpp +++ b/alarm-app/src/List/AlarmsView.cpp @@ -14,23 +14,17 @@ * limitations under the License. */ -#include "Common/Model/AlarmConsumer.h" -#include "List/AlarmItem.h" #include "List/AlarmsView.h" +#include "List/AlarmItem.h" #include "Input/InputView.h" +#include "Common/Model/AlarmConsumer.h" #include "App/Path.h" #include "System/Settings.h" #include "Ui/Accessibility.h" -#include "Ui/CircleMenu.h" -#include "Ui/Genlist.h" -#include "Ui/GenGroupItem.h" -#include "Ui/PaddingItem.h" #include "Ui/Toast.h" -#include "Ui/Window.h" -#include "Utils/Callback.h" #include "Ux/ActionButtonsItem.h" -#include "Ux/CircleSelector.h" +#include "Utils/Callback.h" #include "AppsCommonList.h" #include "ListPath.h" @@ -45,17 +39,24 @@ using namespace List; using namespace std::placeholders; AlarmsView::AlarmsView() - : m_Provider(AlarmConsumer::getInstance()), - m_NoContents(nullptr), m_ContentLayout(nullptr), m_Genlist(nullptr), - m_AddAlarmItem(nullptr), m_DeleteButton(nullptr) + : SelectView(m_Provider), + m_Provider(AlarmConsumer::getInstance()), + m_AddAlarmItem(nullptr) { - Strings strings{}; - strings.selectAll = "WDS_MSG_OPT_SELECT_ALL_ABB"; - strings.deselectAll = "WDS_MSG_OPT_DESELECT_ALL_ABB"; + Strings strings = { }; + strings.selectorStrings.selectAll = "WDS_MSG_OPT_SELECT_ALL_ABB"; + strings.selectorStrings.deselectAll = "WDS_MSG_OPT_DESELECT_ALL_ABB"; strings.buttonDone = "WDS_ALM_ACBUTTON_DELETE_ABB"; - strings.titleMulti = "0"; strings.titleWithCount = "%d"; setStrings(strings); + setAccessibleStrings({ { + "WDS_TTS_TBOPT_SELECT_MODE_POP_UP", + "WDS_TTS_TBBODY_DOUBLE_TAP_TO_CLOSE_THE_POP_UP", + "WDS_TTS_TBBODY_DOUBLE_TAP_TO_SELECT_ALL", + "WDS_TTS_TBBODY_DOUBLE_TAP_TO_DESELECT_ALL" }, + "WDS_GALLERY_HEADER_PD_SELECTED_ABB", + nullptr + }); System::Settings::addCallback(SYSTEM_SETTINGS_KEY_LOCALE_TIMEFORMAT_24HOUR, { std::bind(&AlarmsView::onFormatChanged, this, _1), this }); @@ -71,128 +72,15 @@ void AlarmsView::setAddCallback(AddCallback callback) m_OnAlarmAdded = std::move(callback); } -Evas_Object *AlarmsView::onCreate(Evas_Object *parent) -{ - Evas_Object *layout = elm_layout_add(parent); - elm_layout_theme_set(layout, "layout", "bottom_button", "default"); - - m_ContentLayout = createContentLayout(layout); - m_NoContents = createNoContents(layout); - - return layout; -} - -void AlarmsView::onCreated() -{ - m_Provider.onUpdated() += { std::bind(&AlarmsView::updateEmptyState, this), this }; - m_Provider.onInserted() += { std::bind(&AlarmsView::onAlarmInserted, this, _1), this }; - m_Provider.initialize({ [this] { - for (auto &&dataItem : m_Provider.getDataList()) { - insertItem(createItem(*dataItem)); - } - m_AddAlarmItem->getNextItem()->scrollTo(ELM_GENLIST_ITEM_SCROLLTO_MIDDLE); - updateEmptyState(); - }, this }); -} - -void AlarmsView::onPageAttached(Ui::NavigatorPage *page) -{ - page->setStyle("empty"); -} - -void AlarmsView::onNavigation(bool isCurrent) -{ - eext_rotary_object_event_activated_set(m_Genlist->getEvasObject(), isCurrent); -} - -void AlarmsView::onSelectModeChanged(Ux::SelectMode selectMode) -{ - if (!getEvasObject()) { - return; - } - - if (selectMode != Ux::SelectMulti) { - elm_layout_signal_emit(m_ContentLayout, "select_mode,button,hide", ""); - m_AddAlarmItem->getControl()->setEnabled(true); - - for (auto &&selectItem : getSelectItems()) { - elm_atspi_accessible_relationships_clear(selectItem->getObjectItem()); - } - } else { - elm_layout_signal_emit(m_ContentLayout, "select_mode,button,show", ""); - m_AddAlarmItem->getControl()->setEnabled(false); - - if (auto selector = getMultiSelector().lock()) { - for (auto &&selectItem : getSelectItems()) { - elm_atspi_accessible_relationship_append(selectItem->getObjectItem(), ELM_ATSPI_RELATION_FLOWS_TO, selector->getEvasObject()); - elm_atspi_accessible_relationship_append(selectItem->getObjectItem(), ELM_ATSPI_RELATION_FLOWS_FROM, m_DeleteButton); - } - } - } -} - -void AlarmsView::onSelectCountChanged(size_t selectCount) -{ - if (auto multiSelector = getPtr<Ux::CircleSelector>(getMultiSelector())) { - multiSelector->setCount(selectCount); - } -} - -Evas_Object *AlarmsView::createDoneButton() -{ - m_DeleteButton = elm_button_add(getEvasObject()); - elm_object_style_set(m_DeleteButton, "bottom"); - elm_object_translatable_text_set(m_DeleteButton, "WDS_ALM_ACBUTTON_DELETE_ABB"); - elm_object_part_content_set(getEvasObject(), "elm.swallow.button", m_DeleteButton); - evas_object_smart_callback_add(m_DeleteButton, "atspi,highlighted", - [](void *data, Evas_Object *button, void *) { - auto view = (AlarmsView *)data; - elm_atspi_accessible_relationships_clear(button); - elm_atspi_accessible_relationship_append(button, ELM_ATSPI_RELATION_FLOWS_TO, view->getNextItem()); - }, this); - - return m_DeleteButton; -} - -Ux::MultiSelector *AlarmsView::createMultiSelector() +Evas_Object *AlarmsView::createContent(Evas_Object *parent) { - auto multiSelector = new Ux::CircleSelector(); - multiSelector->create(m_ContentLayout); - multiSelector->setAccessibilityStrings({ - "WDS_TTS_TBOPT_SELECT_MODE_POP_UP", - "WDS_TTS_TBBODY_DOUBLE_TAP_TO_CLOSE_THE_POP_UP", - "WDS_GALLERY_HEADER_PD_SELECTED_ABB", - "WDS_TTS_TBBODY_DOUBLE_TAP_TO_SELECT_ALL", - "WDS_TTS_TBBODY_DOUBLE_TAP_TO_DESELECT_ALL" }); - elm_object_part_content_set(m_ContentLayout, "elm.swallow.icon", multiSelector->getEvasObject()); + Evas_Object *layout = SelectView::createContent(parent); - evas_object_smart_callback_add(multiSelector->getEvasObject(), "atspi,highlighted", - [](void *data, Evas_Object *selector, void *) { - auto view = (AlarmsView *)data; - elm_atspi_accessible_relationships_clear(selector); - elm_atspi_accessible_relationship_append(selector, ELM_ATSPI_RELATION_FLOWS_FROM, view->getPrevItem()); - }, this); - - return multiSelector; -} - -Evas_Object *AlarmsView::createContentLayout(Evas_Object *parent) -{ - Evas_Object *layout = elm_layout_add(parent); - elm_layout_theme_set(layout, "layout", "select_mode", "default"); - - auto surface = findParent<Ui::Window>(parent)->getCircleConformant(); - m_Genlist = new Ui::Genlist(); - m_Genlist->create(parent); - eext_circle_object_genlist_add(m_Genlist->getEvasObject(), surface); - evas_object_smart_callback_add(m_Genlist->getEvasObject(), "longpressed", + Ui::GenContainer *container = getContainer(); + evas_object_smart_callback_add(container->getEvasObject(), "longpressed", (Evas_Smart_Cb)makeCallback(&AlarmsView::onItemLongpressed), this); - elm_object_content_set(layout, m_Genlist->getEvasObject()); - - m_Genlist->insert(new Ui::PaddingItem()); - m_Genlist->insert(m_AddAlarmItem = new Ux::ActionButtonsItem()); - m_Genlist->insert(new Ui::PaddingItem()); + container->insert(m_AddAlarmItem = new Ux::ActionButtonsItem(), nullptr, container->getLastItem()); m_AddAlarmItem->getControl()->addButton("WDS_ALM_BUTTON_ADD_ABB", PATH_ICON_ADD_ALARM, std::bind(&AlarmsView::onAddPressed, this)); @@ -226,112 +114,29 @@ Evas_Object *AlarmsView::createNoContents(Evas_Object *parent) return layout; } -void AlarmsView::updateEmptyState() -{ - Evas_Object *content = m_Provider.getDataList().size() > 0 - ? m_ContentLayout : m_NoContents; - - if (content != elm_object_content_get(getEvasObject())) { - evas_object_hide(elm_object_content_unset(getEvasObject())); - elm_object_content_set(getEvasObject(), content); - } -} - -AlarmItem *AlarmsView::createItem(::Model::DataItem &dataItem) +Ux::ListItem *AlarmsView::createItem(::Model::DataItem &dataItem) { - auto item = new AlarmItem(static_cast<Alarm &>(dataItem)); - dataItem.onUpdated() += { std::bind(&AlarmsView::onAlarmUpdated, this, item, _1), item }; - dataItem.onDeleted() += { std::bind(&AlarmsView::onAlarmDeleted, this, item), item }; - addSelectItem(item); - return item; + return new AlarmItem(static_cast<Alarm &>(dataItem)); } -AlarmItem *AlarmsView::insertItem(AlarmItem *alarmItem) +void AlarmsView::onFilled() { - Ui::GenItem *firstItem = m_AddAlarmItem->getNextItem(); - Ui::GenItem *lastItem = m_Genlist->getLastItem(); - Ui::GenItem *nextItem = lastItem; - - for (auto item = firstItem; item != lastItem; item = item->getNextItem()) { - if (alarmItem->getAlarm() < static_cast<AlarmItem *>(item)->getAlarm()) { - nextItem = item; - break; - } - } - - m_Genlist->insert(alarmItem, nullptr, nextItem); - return alarmItem; + m_AddAlarmItem->scrollTo(ELM_GENLIST_ITEM_SCROLLTO_TOP); } -Ui::GenItem *AlarmsView::getCenterItem() +void AlarmsView::onItemInserted(Ux::ListItem *item) { - static auto center = getWindowCenter(); - if (auto item = elm_genlist_at_xy_item_get(m_Genlist->getEvasObject(), center.x, center.y, nullptr)) { - return (Ui::GenItem *)elm_object_item_data_get(item); - } - - return nullptr; + SelectView::onItemInserted(item); + item->scrollTo(); } -Elm_Interface_Atspi_Accessible *AlarmsView::getNextItem() -{ - if (auto item = getCenterItem()) { - if (auto nextItem = item->getNextItem()) { - if (nextItem != m_Genlist->getLastItem()) { - return nextItem->getObjectItem(); - } - } - } - - return nullptr; -} - -Elm_Interface_Atspi_Accessible *AlarmsView::getPrevItem() -{ - if (auto item = getCenterItem()) { - if (auto prevItem = item->getPrevItem()) { - if (prevItem != m_Genlist->getFirstItem()) { - return prevItem->getObjectItem(); - } - } - } - - return nullptr; -} - -Evas_Point AlarmsView::getWindowCenter() -{ - Evas_Point point { 0 }; - if (auto window = findParent<Ui::Window>()) { - evas_object_geometry_get(window->getEvasObject(), nullptr, nullptr, &point.x, &point.y); - point.x /= 2; - point.y /= 2; - } - - return point; -} - -void AlarmsView::onAlarmInserted(::Model::DataItem &dataItem) -{ - insertItem(createItem(dataItem))->scrollTo(ELM_GENLIST_ITEM_SCROLLTO_MIDDLE); -} - -void AlarmsView::onAlarmUpdated(AlarmItem *item, int changes) +void AlarmsView::onSelectModeChanged(Ux::SelectMode selectMode) { - if (changes & Alarm::ChangedDate) { - item->pop(); - insertItem(item); - } else { - item->update(changes); + if (m_AddAlarmItem) { + m_AddAlarmItem->getControl()->setEnabled(selectMode != Ux::SelectMulti); } } -void AlarmsView::onAlarmDeleted(AlarmItem *item) -{ - removeSelectItem(item); - delete item; -} - void AlarmsView::onAddPressed() { if (m_Provider.getDataList().size() < ALARM_MAX_COUNT) { @@ -351,7 +156,7 @@ void AlarmsView::onAddPressed() void AlarmsView::onFormatChanged(system_settings_key_e key) { - m_Genlist->update("elm.text", ELM_GENLIST_ITEM_FIELD_TEXT); + getContainer()->update("elm.text", ELM_GENLIST_ITEM_FIELD_TEXT); } void AlarmsView::onItemLongpressed(Evas_Object *genlist, Elm_Object_Item *item) @@ -365,8 +170,7 @@ void AlarmsView::onItemLongpressed(Evas_Object *genlist, Elm_Object_Item *item) setCancelCallback(std::bind(&AlarmsView::onSelectFinished, this)); setSelectCallback([this](Ux::SelectResults results) { for (auto &&result : results) { - auto alarm = (Alarm *)result.value.data; - AlarmConsumer::getInstance().deleteDataItem(alarm->getId(), nullptr); + AlarmConsumer::getInstance().deleteDataItem(result->getDataItem<Alarm>().getId(), nullptr); } return onSelectFinished(); diff --git a/alarm-app/src/OperationPickController.cpp b/alarm-app/src/OperationPickController.cpp index 5294fdb..6585af6 100644 --- a/alarm-app/src/OperationPickController.cpp +++ b/alarm-app/src/OperationPickController.cpp @@ -35,8 +35,8 @@ void OperationPickController::onRequest(const char *operation, app_control_h req bool OperationPickController::onAlarmSelected(Ux::SelectResults results) { - Alarm *alarm = (Alarm *) results.begin()->value.data; - sendReply(alarm->getId()); + auto &alarm = results.front()->getDataItem<Alarm>(); + sendReply(alarm.getId()); return true; } diff --git a/lib-apps-common/inc/Ui/CheckItem.h b/lib-apps-common/inc/Ui/CheckItem.h deleted file mode 100644 index f54c2aa..0000000 --- a/lib-apps-common/inc/Ui/CheckItem.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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_CHECK_ITEM -#define UI_CHECK_ITEM - -#include "Ui/GenItem.h" - -namespace Ui -{ - /** - * @brief GenContainer check item - */ - class EXPORT_API CheckItem : public Ui::GenItem - { - public: - /** - * @brief Item check callback. - * @param[in] Whether item is checked - * @return Whether item's state should be changed. - */ - typedef std::function<bool(bool)> CheckCallback; - - CheckItem(GenContainer::Type type = GenContainer::TypeGenlist); - virtual ~CheckItem() override; - - /** - * @return Whether the item is checked. - */ - bool isChecked() const; - - /** - * @brief Set item check state. - * @param[in] isChecked Whether item is checked - * @return Whether the state was changed successfully. - */ - bool setChecked(bool isChecked); - - /** - * @brief Set item check callback. - * @param[in] callback Callback to be called when item is checked/unchecked - */ - void setCheckCallback(CheckCallback callback); - - /** - * @brief Set item which "checked" state should be synchronized with this item. - * @param[in] item Item to link with - */ - void setLinkedItem(CheckItem *item); - - /** - * @brief Unset linked item. - */ - void unsetLinkedItem(); - - protected: - /** - * @brief Update the part containing check component. - */ - void updateCheckPart(); - - /** - * @see GenItem::getContent() - * @remark Use it in derived class to create check component - */ - virtual Evas_Object *getContent(Evas_Object *parent, const char *part) override; - - /** - * @see GenItem::onSelected() - */ - virtual void onSelected() override; - - /** - * @brief Called when item's "checked" state changes. - * @param[in] isChecked Whether item is checked - * @return Whether item's state should be changed. - */ - virtual bool onChecked(bool isChecked) { return true; } - - private: - void onCheckChanged(Evas_Object *check, void *eventInfo); - bool notifyCheck(); - - std::string m_CheckPart; - Eina_Bool m_IsChecked; - bool m_IsChecking; - CheckCallback m_OnChecked; - CheckItem *m_LinkedItem; - }; -} - -#endif /* UI_CHECK_ITEM */ diff --git a/lib-apps-common/inc/Ux/CircleSelector.h b/lib-apps-common/inc/Ux/CircleSelector.h index 3adfe35..591e102 100644 --- a/lib-apps-common/inc/Ux/CircleSelector.h +++ b/lib-apps-common/inc/Ux/CircleSelector.h @@ -23,43 +23,12 @@ namespace Ux { class EXPORT_API CircleSelector : public Ux::MultiSelector { - public: - /** - * @brief Structure with information, that should be pronounced by accessibility engine. - */ - struct AccessibilityStrings - { - const char *name; /**< Selector name. */ - const char *description; /**< Selector description. */ - const char *title; /**< Selector title. */ - const char *selectAllDescription; /**< Description for "Select all" item. */ - const char *deselectAllDescription; /**< Description for "Deselect all" item. */ - }; - - CircleSelector(); - - /** - * @brief Set count of selected items. - */ - void setCount(size_t count); - - /** - * @brief Set accessibility strings. - * @param[in] strings Accessibility strings. - * @see AccessibilityStrings. - */ - void setAccessibilityStrings(const AccessibilityStrings &strings); - private: virtual Evas_Object *onCreate(Evas_Object *parent) override; void onButtonClicked(Evas_Object *button, void *eventInfo); void makeAccessible(Evas_Object *menu); void makeAccessible(Elm_Object_Item *item, const char *description); - char *getAccessibleName(Evas_Object *button); - - AccessibilityStrings m_AccessibilityStrings; - size_t m_Count; }; } diff --git a/lib-apps-common/inc/Ux/ListItem.h b/lib-apps-common/inc/Ux/ListItem.h new file mode 100644 index 0000000..46a23f6 --- /dev/null +++ b/lib-apps-common/inc/Ux/ListItem.h @@ -0,0 +1,74 @@ +/* + * 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 UX_LIST_ITEM_H +#define UX_LIST_ITEM_H + +#include "Ui/GenItem.h" + +namespace Model +{ + class DataItem; +} + +namespace Ux +{ + /** + * @brief GenContainer item representing DataItem in ListView. + */ + class EXPORT_API ListItem : virtual public Ui::GenItem + { + public: + /** + * @brief Create list item. + * @param[in] dataItem Data item associated with list item + * @param[in] type Parent container type + */ + explicit ListItem(Model::DataItem &dataItem, + Ui::GenContainer::Type type = Ui::GenContainer::TypeGenlist); + virtual ~ListItem() override; + + /**@{*/ + /** + * @return Data item associated with list item. + */ + template <typename ItemType> + const ItemType &getDataItem() const { return static_cast<const ItemType &>(m_DataItem); } + template <typename ItemType> + ItemType &getDataItem() { return static_cast<ItemType &>(m_DataItem); } + /**@}*/ + + protected: + /** + * @brief Called after associated DataItem was updated. + * @param[in] changes DataItem changes mask + */ + virtual void onUpdate(int changes) { update(); } + + /** + * @brief Called before associated DataItem is deleted. + */ + virtual void onDelete() { } + + private: + void onDataItemUpdated(int changes, Model::DataItem *nextDataItem); + void onDataItemDeleted(); + + Model::DataItem &m_DataItem; + }; +} + +#endif /* UX_LIST_ITEM_H */ diff --git a/lib-apps-common/inc/Ux/ListView.h b/lib-apps-common/inc/Ux/ListView.h new file mode 100644 index 0000000..e2b1c92 --- /dev/null +++ b/lib-apps-common/inc/Ux/ListView.h @@ -0,0 +1,160 @@ +/* + * 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 UX_LIST_VIEW_H +#define UX_LIST_VIEW_H + +#include "Ui/View.h" +#include <functional> + +namespace Model +{ + class DataItem; + class DataProvider; +} + +namespace Ui +{ + class GenContainer; +} + +namespace Ux +{ + class ListItem; + class EXPORT_API ListView : public Ui::View + { + public: + /** + * @brief Called once view is filled. + */ + typedef std::function<void()> FillCallback; + + /** + * @brief Create list view. + * @param[in] provider Data provider for the view + * @param[in] container Item container (default is Genlist) + */ + explicit ListView(Model::DataProvider &provider, Ui::GenContainer *container = nullptr); + + /** + * @brief Set fill callback. + * @param[in] callback Callback to be called when view is filled + */ + void setFillCallback(FillCallback callback); + + protected: + /** + * @brief Creates main layout and calls createContent() and createNoContents(). + * @see Control::onCreate() + */ + virtual Evas_Object *onCreate(Evas_Object *parent) override; + + /** + * @brief Initializes the provider and fills the container. + * @see Control::onCreated() + */ + virtual void onCreated() override; + + /** + * @brief Changes provider's update mode according to view's state. + * @see View::onNavigation() + */ + virtual void onNavigation(bool isCurrent) override; + + /** + * @brief Create layout with item container. + * @param[in] parent Parent Evas_Object + * @return Layout that displays item container. + */ + virtual Evas_Object *createContent(Evas_Object *parent); + + /** + * @brief Create "No Contents" layout. + * @param[in] parent Parent Evas_Object + * @return Layout to display when there is no content. + */ + virtual Evas_Object *createNoContents(Evas_Object *parent); + + /** + * @brief Create "More" menu. + * @param[in] parent Parent Evas_Object + * @return Menu object to be displayed in the view. + */ + virtual Evas_Object *createMoreMenu(Evas_Object *parent) { return nullptr; } + + /** + * @brief Create list item for specified dataItem. + * @param[in] dataItem Data item to be represented by ListItem + * @return New list item. + */ + virtual ListItem *createItem(Model::DataItem &dataItem) = 0; + + /** + * @brief Update main layout according to content's empty state. + * @details Switches between "Content" and "No Contents" layouts. + */ + virtual void updateEmptyState(); + + /** + * @brief Called once view is filled. + */ + virtual void onFilled() { } + + /** + * @brief Called when new list item is inserted. + * @param[in] item New list item + */ + virtual void onItemInserted(ListItem *item) { } + + /** + * @return Data provider. + */ + Model::DataProvider &getProvider() const; + + /** + * @return Item container. + */ + Ui::GenContainer *getContainer() const; + + /** + * @return Content layout. + */ + Evas_Object *getContent() const; + + /** + * @return "No Contents" layout. + */ + Evas_Object *getNoContents() const; + + /** + * @return "More" menu. + */ + Evas_Object *getMoreMenu() const; + + private: + ListItem *addItem(Model::DataItem &dataItem, Model::DataItem *nextDataItem = nullptr); + + Ui::GenContainer *m_Container; + Evas_Object *m_Content; + Evas_Object *m_NoContents; + Evas_Object *m_MoreMenu; + + Model::DataProvider &m_Provider; + FillCallback m_OnFilled; + }; +} + +#endif /* UX_LIST_VIEW_H */ diff --git a/lib-apps-common/inc/Ux/MultiSelector.h b/lib-apps-common/inc/Ux/MultiSelector.h index feb2e66..c69597c 100644 --- a/lib-apps-common/inc/Ux/MultiSelector.h +++ b/lib-apps-common/inc/Ux/MultiSelector.h @@ -35,17 +35,28 @@ namespace Ux }; /** - * @brief Translatable strings for selector menu items. + * @brief Translatable strings table for selector elements. */ struct Strings { - const char *selectAll; /**< "Select all" item text. */ - const char *deselectAll; /**< "Deselect all" item text. */ + const char *selectAll; /**< "Select all" text. */ + const char *deselectAll; /**< "Deselect all" text. */ + }; + + /** + * @brief Translatable strings table for accessibility feature. + */ + struct AccessibleStrings + { + const char *name; /**< Selector control name. */ + const char *desc; /**< Selector control description. */ + const char *selectAll; /**< "Select all" description. */ + const char *deselectAll; /**< "Deselect all" description. */ }; /** * @brief Change callback. - * @param[in] Selector state. + * @param[in] Selector state * @return Whether contol's state should be changed. */ typedef std::function<bool(State)> ChangeCallback; @@ -54,26 +65,32 @@ namespace Ux /** * @brief Set change callback. - * @param[in] callback Change callback. + * @param[in] callback Change callback */ void setChangeCallback(ChangeCallback callback); /** * @brief Set state. - * @param[in] state MultiSelector state. + * @param[in] state MultiSelector state */ void setState(State state); /** - * @brief Set translatable strings for menu. - * @param[in] strings Translatable strings table. + * @brief Set translatable strings for selector elements. + * @param[in] strings Translatable strings table */ void setStrings(const Strings &strings); + /** + * @brief Set translatable strings for accessibility. + * @param[in] strings Translatable strings table + */ + void setAccessibleStrings(const AccessibleStrings &strings); + protected: /** * @brief Called when inner state was changed by public @setState method. - * @param[in] state Selector state. + * @param[in] state Selector state */ virtual void onStateChanged(State state) { } @@ -88,16 +105,23 @@ namespace Ux const Strings &getStrings() const; /** + * @return Translatable strings for accessibility. + */ + const AccessibleStrings &getAccessibleStrings() const; + + /** * @brief Called when MultiSelector state was changed. * @remark MUST be called only when change was performed by user interaction. - * @param[in] state Selector state. + * @param[in] state Selector state */ bool notifyChanged(State state); private: ChangeCallback m_OnChanged; State m_State; + Strings m_Strings; + AccessibleStrings m_AccessStrings; }; } diff --git a/lib-apps-common/inc/Ux/SelectAllItem.h b/lib-apps-common/inc/Ux/SelectAllItem.h deleted file mode 100644 index c2babc6..0000000 --- a/lib-apps-common/inc/Ux/SelectAllItem.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 UX_SELECT_ALL_ITEM_H -#define UX_SELECT_ALL_ITEM_H - -#include "Ui/CheckItem.h" - -namespace Ux -{ - /** - * @brief "Select all" genlist item - */ - class EXPORT_API SelectAllItem : public Ui::CheckItem - { - public: - /** - * @brief Create "Select all" item. - * @param[in] text Item text - */ - SelectAllItem(const char *text); - - protected: - /** - * @see GenItem::getItemClass() - */ - virtual Elm_Gen_Item_Class *getItemClass() const override; - - /** - * @see GenItem::getText() - */ - virtual char *getText(Evas_Object *parent, const char *part) override; - - /** - * @see GenItem::getContent() - */ - virtual Evas_Object *getContent(Evas_Object *parent, const char *part) override; - - /** - * @see GenItem::onInserted() - */ - virtual void onInserted() override; - - private: - std::string m_Text; - }; -} - -#endif /* UX_SELECT_ALL_ITEM_H */ diff --git a/lib-apps-common/inc/Ux/SelectItem.h b/lib-apps-common/inc/Ux/SelectItem.h index 7f3fb71..4a39f7a 100644 --- a/lib-apps-common/inc/Ux/SelectItem.h +++ b/lib-apps-common/inc/Ux/SelectItem.h @@ -17,72 +17,82 @@ #ifndef UX_SELECT_ITEM_H #define UX_SELECT_ITEM_H -#include "Ui/CheckItem.h" -#include "Ux/SelectTypes.h" +#include "Ux/ListItem.h" namespace Ux { class SelectView; /** + * @brief Determines how items can be selected. + */ + enum SelectMode + { + SelectNone, /**< Selection is disabled */ + SelectSingle, /**< Only one item can be selected */ + SelectMulti /**< Multiple items can be selected */ + }; + + /** * @brief GenContainer item for SelectView that supports selection mode switching. */ - class EXPORT_API SelectItem : public Ui::CheckItem + class EXPORT_API SelectItem : virtual public ListItem { public: - SelectItem(Ui::GenContainer::Type type = Ui::GenContainer::TypeGenlist); - /** - * @return Whether item is excluded from multiple selection. + * @brief Create select item. + * @param[in] checkPart Part which should contain "check" widget + * @see ListItem::ListItem() */ - bool isExcluded() const; + explicit SelectItem(Model::DataItem &dataItem, + const char *checkPart = "elm.swallow.center_check", + Ui::GenContainer::Type type = Ui::GenContainer::TypeGenlist); /** - * @brief Set item exclusion. - * @param[in] isExcluded Whether item is excluded from multiple selection - */ - void setExcluded(bool isExcluded); - - /** - * @return Item selection mode. + * @return Whether the item is checked. */ - SelectMode getSelectMode() const; + bool isChecked() const; /** - * @brief Set item selection mode. + * @brief Set item check state. + * @param[in] isChecked Whether item is checked + * @return Whether the state was changed successfully. */ - void setSelectMode(SelectMode selectMode); + bool setChecked(bool isChecked); /** - * @return Selection result associated with the item. + * @return Whether item is excluded from multiple selection. */ - SelectResult getSelectResult() const; + bool isExcluded() const; /** - * @return Whether item has custom selection result. + * @brief Set item exclusion. + * @param[in] isExcluded Whether item is excluded from multiple selection */ - bool hasCustomResult() const; + void setExcluded(bool isExcluded); /** - * @brief Set custom selection result to override the default result. + * @return Item selection mode. */ - void setCustomResult(SelectResult result); + SelectMode getSelectMode() const; + protected: /** - * @brief Unset custom selection result to use default result. + * @see GenItem::getContent() */ - void unsetCustomResult(); + virtual Evas_Object *getContent(Evas_Object *parent, const char *part) override; - protected: /** - * @return Default selection result associated with the item. + * @brief Adds this item to the parent SelectView. + * @see GenItem::onInserted() */ - virtual SelectResult getDefaultResult() const = 0; + virtual void onInserted() override; /** - * @see GenItem::getContent() + * @brief Removes this item from the parent SelectView. + * @see ListItem::onDelete() */ - virtual Evas_Object *getContent(Evas_Object *parent, const char *part) override; + virtual void onDelete() override; /** * @GenItem::onVisibilityChanged() @@ -95,9 +105,11 @@ namespace Ux virtual void onSelected() override; /** - * @see CheckItem::onChecked() + * @brief Called when item's "checked" state changes. + * @param[in] isChecked Whether item is checked + * @return Whether item's state should be changed. */ - virtual bool onChecked(bool isChecked) override; + virtual bool onChecked(bool isChecked) { return true; } /** * @brief Called when selection mode was changed. @@ -107,11 +119,17 @@ namespace Ux private: friend class SelectView; + void setSelectMode(SelectMode selectMode); + + void onCheckChanged(Evas_Object *check, void *eventInfo); + bool isCheckAllowed(); + + const char *m_CheckPart; + Eina_Bool m_IsChecked; + bool m_IsChecking; SelectView *m_SelectView; SelectMode m_SelectMode; - SelectResult m_CustomResult; - bool m_HasCustomResult; bool m_IsExcluded; }; } diff --git a/lib-apps-common/inc/Ux/SelectTypes.h b/lib-apps-common/inc/Ux/SelectTypes.h deleted file mode 100644 index 0221525..0000000 --- a/lib-apps-common/inc/Ux/SelectTypes.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 UX_SELECT_TYPES_H -#define UX_SELECT_TYPES_H - -#include <functional> -#include <vector> - -namespace Ux -{ - /** - * @brief Determines how items can be selected. - */ - enum SelectMode - { - SelectNone, /**< Selection is disabled */ - SelectSingle, /**< Only one item can be selected */ - SelectMulti /**< Multiple items can be selected */ - }; - - /** - * @brief Selection result. - */ - struct SelectResult - { - unsigned type; /**< Result type (depends on result source) */ - union Value - { - Value(void *data) : data(data) { } - Value(int id) : id(id) { } - void *data; /**< Result data */ - int id; /**< Result data ID */ - } value; /**< Result value (depends on type) */ - }; - - /** - * @brief Range of consecutive selection results. - */ - typedef std::vector<SelectResult> SelectResults; - - /** - * @brief Callback to be called when selection is done. - * @param[in] Selection results - * @return Whether the component that provided the selection should be destroyed. - */ - typedef std::function<bool(SelectResults)> SelectCallback; - - /** - * @brief Callback to be called when cancel is pressed. - * @return Whether the component that provided the selection should be destroyed. - */ - typedef std::function<bool()> CancelCallback; -} - -#endif /* UX_SELECT_TYPES_H */ diff --git a/lib-apps-common/inc/Ux/SelectView.h b/lib-apps-common/inc/Ux/SelectView.h index ed0915f..03bb6da 100644 --- a/lib-apps-common/inc/Ux/SelectView.h +++ b/lib-apps-common/inc/Ux/SelectView.h @@ -17,9 +17,9 @@ #ifndef UX_SELECT_VIEW_H #define UX_SELECT_VIEW_H -#include "Ui/View.h" +#include "Ux/ListView.h" #include "Ux/MultiSelector.h" -#include "Ux/SelectTypes.h" +#include "Ux/SelectItem.h" #include <vector> @@ -30,21 +30,25 @@ namespace Ui namespace Ux { - class SelectItem; + /** + * @brief List of selected items. + */ + typedef std::vector<SelectItem *> SelectResults; /** * @brief Base class for a view that support single and multiple selection modes. */ - class EXPORT_API SelectView : public Ui::View + class EXPORT_API SelectView : public ListView { public: + DEFINE_CLASS_TYPE(SelectView, ListView) + /** * @brief Translatable strings table for view elements. */ struct Strings { - const char *selectAll; /**< "Select all" text. */ - const char *deselectAll; /**< "Deselect all" text. */ + MultiSelector::Strings selectorStrings; const char *buttonDone; /**< "Done" button text. */ const char *buttonCancel; /**< "Cancel" button text. */ const char *titleDefault; /**< Title for #SelectNone mode. */ @@ -59,6 +63,31 @@ namespace Ux }; /** + * @brief Translatable strings table for accessibility feature. + */ + struct AccessibleStrings + { + MultiSelector::AccessibleStrings selectorStrings; + const char *titleWithCount; /**< Title for #SelectMulti mode with selection count. + Can contain one integer format specifier. */ + const char *titleWithLimit; /**< Title for #SelectMulti mode with limit. + Can contain two integer format specifiers. */ + }; + + /** + * @brief Callback to be called when selection is done. + * @param[in] Selection results + * @return Whether the view should be closed. + */ + typedef std::function<bool(SelectResults)> SelectCallback; + + /** + * @brief Callback to be called when cancel is pressed. + * @return Whether the view should be closed. + */ + typedef std::function<bool()> CancelCallback; + + /** * @brief Called when item's "checked" state changed in #SelectMulti mode. * @param[in] item Changed item * @param[in] isChecked Whether item is checked @@ -78,8 +107,10 @@ namespace Ux */ typedef std::vector<SelectItem *> SelectItems; - SelectView(); - virtual ~SelectView() override; + /** + * @see ListView::ListView() + */ + explicit SelectView(Model::DataProvider &provider, Ui::GenContainer *container = nullptr); /** * @return View selection mode. @@ -102,16 +133,18 @@ namespace Ux const SelectItems &getSelectItems() const; /** - * @return Whether all items are selected. + * @brief Set translatable strings for the view. + * @remark Should be called before create(). + * @param[in] strings Translatable strings table */ - bool isMaxSelected() const; + void setStrings(const Strings &strings); /** - * @brief Set translatable strings for the view. + * @brief Set translatable strings for accessibility. * @remark Should be called before create(). * @param[in] strings Translatable strings table */ - void setStrings(const Strings &strings); + void setAccessibleStrings(const AccessibleStrings &strings); /** * @brief Set selection mode. @@ -157,10 +190,9 @@ namespace Ux protected: /** - * @brief Creates "Done" and "Cancel" buttons in #SelectMulti mode. - * @see View::onPageAttached() + * @see ListView::createContent() */ - virtual void onPageAttached(Ui::NavigatorPage *page) override; + virtual Evas_Object *createContent(Evas_Object *parent) override; /** * @brief Calls cancel callback if it exists. @@ -169,10 +201,10 @@ namespace Ux virtual bool onBackPressed() override; /** - * @brief Called when title was changed. - * @param[in] title Title + * @brief Registers accessible siblings for the item when in #SelectMulti mode. + * @see ListView::onItemInserted() */ - virtual void onTitleChanged(const char *title); + virtual void onItemInserted(ListItem *item) override; /** * @brief Called when selection mode was changed. @@ -192,38 +224,6 @@ namespace Ux */ virtual void onSelectCountChanged(size_t selectCount) { } - /** - * @return Done button. - */ - virtual Evas_Object *createDoneButton(); - - /** - * @return Cancel button. - */ - virtual Evas_Object *createCancelButton(); - - /** - * @return MultiSelector control. - */ - virtual Ux::MultiSelector *createMultiSelector() = 0; - - /** - * @brief Add selectable item to be managed by the view. - * @param[in] item Item to add - */ - void addSelectItem(SelectItem *item); - - /** - * @brief Remove selectable item. - * @param[in] item Item to remove - */ - void removeSelectItem(SelectItem *item); - - /** - * @return MultiSelector - */ - Ui::ControlPtr getMultiSelector(); - private: friend class SelectItem; @@ -233,11 +233,18 @@ namespace Ux CountDecrement }; + Evas_Object *createMultiSelector(Evas_Object *parent); + Evas_Object *createDoneButton(Evas_Object *parent); + void setAccessSiblings(ListItem *item); + void unsetAccessSiblings(ListItem *item); + void onScrolled(Evas_Object *obj, void *eventInfo); + size_t getSelectMax() const; bool isLimitReached() const; + bool isMaxSelected() const; - void updatePageTitle(); - void updatePageButtons(); + char *getAccessibleTitle(Evas_Object *obj); + void updateTitle(); void updateDoneButtonState(); void updateMultiSelector(); void updateMultiSelectorState(); @@ -248,8 +255,8 @@ namespace Ux void updateVisibleCount(CountChange change, SelectItem *item); void updateVisibleSelectCount(CountChange change, SelectItem *item); - void createPageButtons(); - void destroyPageButtons(); + void addSelectItem(SelectItem *item); + void removeSelectItem(SelectItem *item); void onItemExcluded(SelectItem *item, bool isExcluded); void onItemVisibilityChanged(SelectItem *item, bool isVisible); @@ -261,19 +268,18 @@ namespace Ux void onCancelPressed(Evas_Object *button, void *eventInfo); void onLimitReached(); - Ui::ControlPtr m_MultiSelector; SelectItems m_Items; - + MultiSelector *m_MultiSelector; Evas_Object *m_DoneButton; - Evas_Object *m_CancelButton; + bool m_IsEmptyResultAllowed; bool m_IsMultiChecking; + size_t m_TotalCount; size_t m_TotalSelectCount; size_t m_VisibleCount; size_t m_VisibleSelectCount; size_t m_SelectLimit; - bool m_IsEmptyResultAllowed; SelectMode m_SelectMode; SelectCallback m_OnSelected; @@ -282,6 +288,7 @@ namespace Ux LimitCallback m_OnLimitReached; Strings m_Strings; + AccessibleStrings m_AccessStrings; }; } diff --git a/lib-apps-common/src/Ui/CheckItem.cpp b/lib-apps-common/src/Ui/CheckItem.cpp deleted file mode 100644 index 72bf193..0000000 --- a/lib-apps-common/src/Ui/CheckItem.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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/CheckItem.h" -#include "Utils/Callback.h" - -using namespace Ui; - -CheckItem::CheckItem(GenContainer::Type type) - : GenItem(type), - m_CheckPart("*"), m_IsChecked(false), m_IsChecking(false), - m_LinkedItem(nullptr) -{ -} - -CheckItem::~CheckItem() -{ - unsetLinkedItem(); -} - -bool CheckItem::isChecked() const -{ - return m_IsChecked; -} - -bool CheckItem::setChecked(bool isChecked) -{ - if (isChecked == m_IsChecked) { - return true; - } - - m_IsChecked = isChecked; - if (!notifyCheck()) { - m_IsChecked = !m_IsChecked; - return false; - } - - Evas_Object *check = elm_object_item_part_content_get(getObjectItem(), m_CheckPart.c_str()); - if (check) { - /* Enable animation */ - elm_object_signal_emit(check, m_IsChecked - ? "elm,activate,check,on" : "elm,activate,check,off", "elm"); - elm_check_state_set(check, m_IsChecked); - } - - return true; -} - -void CheckItem::setCheckCallback(CheckCallback callback) -{ - m_OnChecked = std::move(callback); -} - -void CheckItem::setLinkedItem(CheckItem *item) -{ - if (!item) { - return; - } - - unsetLinkedItem(); - item->unsetLinkedItem(); - - item->setChecked(m_IsChecked); - item->m_LinkedItem = this; - m_LinkedItem = item; -} - -void CheckItem::unsetLinkedItem() -{ - if (m_LinkedItem) { - m_LinkedItem->m_LinkedItem = nullptr; - m_LinkedItem = nullptr; - } -} - -void CheckItem::updateCheckPart() -{ - update(m_CheckPart.c_str(), ELM_GENLIST_ITEM_FIELD_CONTENT); -} - -Evas_Object *CheckItem::getContent(Evas_Object *parent, const char *part) -{ - m_CheckPart = part; - - Elm_Check *check = elm_check_add(parent); - elm_check_state_set(check, m_IsChecked); - elm_check_state_pointer_set(check, &m_IsChecked); - evas_object_propagate_events_set(check, EINA_FALSE); - evas_object_smart_callback_add(check, "changed", - makeCallback(&CheckItem::onCheckChanged), this); - - elm_atspi_accessible_relationship_append(check, ELM_ATSPI_RELATION_CONTROLLED_BY, getObjectItem()); - elm_atspi_accessible_relationship_append(getObjectItem(), ELM_ATSPI_RELATION_CONTROLLER_FOR, check); - elm_atspi_accessible_relationship_append(getObjectItem(), ELM_ATSPI_RELATION_DESCRIBED_BY, check); - - return check; -} - -void CheckItem::onSelected() -{ - setChecked(!m_IsChecked); -} - -void CheckItem::onCheckChanged(Evas_Object *check, void *eventInfo) -{ - if (!notifyCheck()) { - elm_check_state_set(check, !m_IsChecked); - } -} - -bool CheckItem::notifyCheck() -{ - if (m_IsChecking) { - return false; - } - - bool isAllowed = false; - m_IsChecking = true; - - if (onChecked(m_IsChecked)) { - if (!m_OnChecked || m_OnChecked(m_IsChecked)) { - if (!m_LinkedItem || m_LinkedItem->setChecked(m_IsChecked)) { - isAllowed = true; - } - } - } - - m_IsChecking = false; - return isAllowed; -} diff --git a/lib-apps-common/src/Ux/CircleSelector.cpp b/lib-apps-common/src/Ux/CircleSelector.cpp index d12a547..4dcca40 100644 --- a/lib-apps-common/src/Ux/CircleSelector.cpp +++ b/lib-apps-common/src/Ux/CircleSelector.cpp @@ -18,43 +18,17 @@ #include "Ui/Accessibility.h" #include "Ui/CircleMenu.h" #include "Utils/Callback.h" -#include <app_i18n.h> - -#define BUF_SIZE 8 -#define BTN_BUF_SIZE 32 using namespace Ui; using namespace Ux; -CircleSelector::CircleSelector() - : m_AccessibilityStrings{ nullptr }, - m_Count(0) -{ -} - -void CircleSelector::setCount(size_t count) -{ - m_Count = count; - char buf[BUF_SIZE]; - snprintf(buf, sizeof(buf), "%zu", count); - elm_object_text_set(getEvasObject(), buf); -} - -void CircleSelector::setAccessibilityStrings(const AccessibilityStrings &strings) -{ - m_AccessibilityStrings = strings; -} - Evas_Object *CircleSelector::onCreate(Evas_Object *parent) { auto button = elm_button_add(parent); elm_object_style_set(button, "select_mode"); - elm_object_text_set(button, "0"); evas_object_smart_callback_add(button, "clicked", makeCallback(&CircleSelector::onButtonClicked), this); - elm_atspi_accessible_name_cb_set(button, makeCallback(&CircleSelector::getAccessibleName), this); - return button; } @@ -67,13 +41,13 @@ void CircleSelector::onButtonClicked(Evas_Object *button, void *eventInfo) auto item = menu->addItem(getStrings().selectAll, [this] { notifyChanged(SelectedAll); }); - makeAccessible(item, m_AccessibilityStrings.selectAllDescription); + makeAccessible(item, getAccessibleStrings().selectAll); } if (getState() != SelectedNone) { auto item = menu->addItem(getStrings().deselectAll, [this] { notifyChanged(SelectedNone); }); - makeAccessible(item, m_AccessibilityStrings.deselectAllDescription); + makeAccessible(item, getAccessibleStrings().deselectAll); } menu->show(); @@ -85,8 +59,8 @@ void CircleSelector::makeAccessible(Evas_Object *menu) elm_atspi_accessible_reading_info_type_set(menu, Elm_Accessible_Reading_Info_Type( ELM_ACCESSIBLE_READING_INFO_TYPE_NAME | ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION)); - elm_atspi_accessible_name_cb_set(menu, getTranslatableAccessText, m_AccessibilityStrings.name); - elm_atspi_accessible_description_cb_set(menu, getTranslatableAccessText, m_AccessibilityStrings.description); + elm_atspi_accessible_name_cb_set(menu, getTranslatableAccessText, getAccessibleStrings().name); + elm_atspi_accessible_description_cb_set(menu, getTranslatableAccessText, getAccessibleStrings().desc); elm_access_action_cb_set(menu, ELM_ACCESS_ACTION_ACTIVATE, [](void *, Evas_Object *obj, Elm_Access_Action_Info *) { @@ -100,10 +74,3 @@ void CircleSelector::makeAccessible(Elm_Object_Item *item, const char *descripti elm_atspi_accessible_reading_info_type_set(item, ELM_ACCESSIBLE_READING_INFO_TYPE_DESCRIPTION); elm_atspi_accessible_description_cb_set(item, getTranslatableAccessText, description); } - -char *CircleSelector::getAccessibleName(Evas_Object *button) -{ - char buf[BTN_BUF_SIZE]; - snprintf(buf, sizeof(buf), _(m_AccessibilityStrings.title), (int)m_Count); - return strdup(buf); -} diff --git a/lib-apps-common/src/Ux/ListItem.cpp b/lib-apps-common/src/Ux/ListItem.cpp new file mode 100644 index 0000000..9842c79 --- /dev/null +++ b/lib-apps-common/src/Ux/ListItem.cpp @@ -0,0 +1,63 @@ +/* + * 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 "Ux/ListItem.h" +#include "Model/DataItem.h" + +using namespace Ux; +using namespace std::placeholders; + +ListItem::ListItem(Model::DataItem &dataItem, Ui::GenContainer::Type type) + : GenItem(type), m_DataItem(dataItem) +{ + m_DataItem.setUserData(this); + m_DataItem.onUpdated() += { std::bind(&ListItem::onDataItemUpdated, this, _1, _2), this }; + m_DataItem.onDeleted() += { std::bind(&ListItem::onDataItemDeleted, this), this }; +} + +ListItem::~ListItem() +{ + m_DataItem.onUpdated() -= this; + m_DataItem.onDeleted() -= this; + m_DataItem.setUserData(nullptr); +} + +void ListItem::onDataItemUpdated(int changes, Model::DataItem *nextDataItem) +{ + if (!isInserted()) { + return; + } + + Ui::GenItem *nextItem = getParent()->getLastItem(); + if (nextDataItem && nextDataItem->getUserData()) { + nextItem = (ListItem *) nextDataItem->getUserData(); + } + if (nextItem != getNextItem()) { + Ui::GenGroupItem *parentItem = getParentItem(); + if (parentItem != nextItem->getParentItem()) { + nextItem = nullptr; + } + getParent()->insert(this, parentItem, nextItem); + } else { + onUpdate(changes); + } +} + +void ListItem::onDataItemDeleted() +{ + onDelete(); + delete this; +} diff --git a/lib-apps-common/src/Ux/ListView.cpp b/lib-apps-common/src/Ux/ListView.cpp new file mode 100644 index 0000000..76c5351 --- /dev/null +++ b/lib-apps-common/src/Ux/ListView.cpp @@ -0,0 +1,180 @@ +/* + * 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 "Ux/ListView.h" +#include "Ux/ListItem.h" + +#include "Ui/Accessibility.h" +#include "Ui/GenGroupItem.h" +#include "Ui/Genlist.h" +#include "Ui/PaddingItem.h" +#include "Model/DataProvider.h" + +#include <efl_extension.h> + +using namespace Model; +using namespace Ux; +using namespace std::placeholders; + +ListView::ListView(DataProvider &provider, Ui::GenContainer *container) + : m_Container(container), m_Content(nullptr), m_NoContents(nullptr), m_MoreMenu(nullptr), + m_Provider(provider) +{ +} + +void ListView::setFillCallback(FillCallback callback) +{ + m_OnFilled = std::move(callback); +} + +Evas_Object *ListView::onCreate(Evas_Object *parent) +{ + Evas_Object *layout = elm_layout_add(parent); + elm_layout_theme_set(layout, "layout", "application", "default"); + + m_Content = createContent(layout); + m_NoContents = createNoContents(layout); + return layout; +} + +void ListView::onCreated() +{ + m_Provider.onInserted() += { std::bind(&ListView::addItem, this, _1, _2), this }; + m_Provider.onUpdated() += { std::bind(&ListView::updateEmptyState, this), this }; + m_Provider.initialize({ [this] { + for (auto &&dataItem : m_Provider.getDataList()) { + addItem(*dataItem); + } + updateEmptyState(); + + onFilled(); + if (m_OnFilled) { + m_OnFilled(); + } + }, this }); +} + +void ListView::onNavigation(bool isCurrent) +{ + eext_rotary_object_event_activated_set(m_Container->getEvasObject(), isCurrent); + m_Provider.setUpdateEnabled(isCurrent); +} + +Evas_Object *ListView::createContent(Evas_Object *parent) +{ + Evas_Object *layout = elm_layout_add(parent); + elm_layout_theme_set(layout, "layout", "list", "default"); + + if (!m_Container) { + m_Container = new Ui::Genlist(); + } + m_Container->create(layout); + m_Container->insert(new Ui::PaddingItem()); + m_Container->insert(new Ui::PaddingItem()); + elm_object_content_set(layout, m_Container->getEvasObject()); + + m_MoreMenu = createMoreMenu(layout); + if (m_MoreMenu) { + /* FIXME: Remove this workaround once eext_more_option stops repeating events */ + evas_object_repeat_events_set(elm_object_part_content_get(m_MoreMenu, "elm.swallow.right"), EINA_FALSE); + evas_object_smart_callback_add(m_MoreMenu, "more,option,closed", + [](void *data, Evas_Object *menu, void *) { + auto view = (ListView *) data; + eext_rotary_object_event_activated_set(view->m_Container->getEvasObject(), EINA_TRUE); + evas_object_repeat_events_set(elm_object_part_content_get(menu, "elm.swallow.right"), EINA_FALSE); + }, this); + + evas_object_smart_callback_add(getContainer()->getEvasObject(), "scroll", + [](void *data, Evas_Object *, void *) { + auto view = (ListView *) data; + elm_atspi_accessible_relationships_clear(view->m_MoreMenu); + elm_atspi_accessible_relationship_append(view->m_MoreMenu, + ELM_ATSPI_RELATION_FLOWS_FROM, view->m_Container->getMiddleItem()->getObjectItem()); + }, this); + elm_object_part_content_set(layout, "elm.swallow.more_option", m_MoreMenu); + } + + return layout; +} + +Evas_Object *ListView::createNoContents(Evas_Object *parent) +{ + Evas_Object *layout = elm_layout_add(parent); + elm_layout_theme_set(layout, "layout", "nocontents", "default"); + Ui::createTextAccessObject(layout, "elm.text.title"); + return layout; +} + +void ListView::updateEmptyState() +{ + Evas_Object *content = m_Provider.getDataList().empty() ? m_NoContents : m_Content; + if (content != elm_object_content_get(getEvasObject())) { + evas_object_hide(elm_object_content_unset(getEvasObject())); + elm_object_content_set(getEvasObject(), content); + } +} + +Model::DataProvider &ListView::getProvider() const +{ + return m_Provider; +} + +Ui::GenContainer *ListView::getContainer() const +{ + return m_Container; +} + +Evas_Object *ListView::getContent() const +{ + return m_Content; +} + +Evas_Object *ListView::getNoContents() const +{ + return m_NoContents; +} + +Evas_Object *ListView::getMoreMenu() const +{ + return m_MoreMenu; +} + +ListItem *ListView::addItem(DataItem &dataItem, DataItem *nextDataItem) +{ + Ui::GenItem *nextItem = m_Container->getLastItem(); + if (nextDataItem && nextDataItem->getUserData()) { + nextItem = (ListItem *) nextDataItem->getUserData(); + } + + ListItem *item = createItem(dataItem); + m_Container->insert(item, nullptr, nextItem); + + if (m_MoreMenu) { + if (item->isGroupItem()) { + for (auto &&subItem : *dynamic_cast<Ui::GenGroupItem *>(item)) { + elm_atspi_accessible_relationship_append(subItem->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_TO, m_MoreMenu); + onItemInserted(dynamic_cast<ListItem *>(subItem)); + } + } else { + elm_atspi_accessible_relationship_append(item->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_TO, m_MoreMenu); + onItemInserted(item); + } + } + + return item; +} diff --git a/lib-apps-common/src/Ux/MultiSelector.cpp b/lib-apps-common/src/Ux/MultiSelector.cpp index ea64ce4..246fcfe 100644 --- a/lib-apps-common/src/Ux/MultiSelector.cpp +++ b/lib-apps-common/src/Ux/MultiSelector.cpp @@ -20,7 +20,7 @@ using namespace Ux; MultiSelector::MultiSelector() : m_State(SelectedNone), - m_Strings{ nullptr } + m_Strings{ }, m_AccessStrings{ } { } @@ -40,6 +40,11 @@ void MultiSelector::setStrings(const Strings &strings) m_Strings = strings; } +void MultiSelector::setAccessibleStrings(const AccessibleStrings &strings) +{ + m_AccessStrings = strings; +} + MultiSelector::State MultiSelector::getState() const { return m_State; @@ -50,6 +55,11 @@ const MultiSelector::Strings &MultiSelector::getStrings() const return m_Strings; } +const MultiSelector::AccessibleStrings &MultiSelector::getAccessibleStrings() const +{ + return m_AccessStrings; +} + bool MultiSelector::notifyChanged(State state) { return !m_OnChanged || m_OnChanged(state); diff --git a/lib-apps-common/src/Ux/SelectAllItem.cpp b/lib-apps-common/src/Ux/SelectAllItem.cpp deleted file mode 100644 index ec6d8cb..0000000 --- a/lib-apps-common/src/Ux/SelectAllItem.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 "Ux/SelectAllItem.h" -#include <app_i18n.h> - -#define TEXT_COLOR 61, 185, 204, 255 - -using namespace Ux; - -SelectAllItem::SelectAllItem(const char *text) -{ - if (text) { - m_Text = text; - } -} - -Elm_Gen_Item_Class *SelectAllItem::getItemClass() const -{ - static Elm_Gen_Item_Class itc = createItemClass("end_icon"); - return &itc; -} - -char *SelectAllItem::getText(Evas_Object *parent, const char *part) -{ - if (strcmp(part, "elm.text") == 0) { - return strdup(_(m_Text.c_str())); - } - - return nullptr; -} - -Evas_Object *SelectAllItem::getContent(Evas_Object *parent, const char *part) -{ - if (strcmp(part, "elm.swallow.end") == 0) { - return CheckItem::getContent(parent, part); - } - - return nullptr; -} - -void SelectAllItem::onInserted() -{ - elm_object_item_color_class_color_set(getObjectItem(), "text", TEXT_COLOR); - elm_object_item_color_class_color_set(getObjectItem(), "text_pressed", TEXT_COLOR); -} diff --git a/lib-apps-common/src/Ux/SelectItem.cpp b/lib-apps-common/src/Ux/SelectItem.cpp index 746d389..9d6e10d 100644 --- a/lib-apps-common/src/Ux/SelectItem.cpp +++ b/lib-apps-common/src/Ux/SelectItem.cpp @@ -16,18 +16,46 @@ #include "Ux/SelectItem.h" #include "Ux/SelectView.h" +#include "Utils/Callback.h" using namespace Ux; -using namespace Ui; -SelectItem::SelectItem(GenContainer::Type type) - : CheckItem(type), +SelectItem::SelectItem(Model::DataItem &dataItem, const char *checkPart, Ui::GenContainer::Type type) + : ListItem(dataItem, type), + m_CheckPart(checkPart), m_IsChecked(false), m_IsChecking(false), m_SelectView(nullptr), m_SelectMode(SelectNone), - m_CustomResult{ 0, 0 }, m_HasCustomResult(false), m_IsExcluded(false) { } +bool SelectItem::isChecked() const +{ + return m_IsChecked; +} + +bool SelectItem::setChecked(bool isChecked) +{ + if (isChecked == m_IsChecked) { + return true; + } + + m_IsChecked = isChecked; + if (!isCheckAllowed()) { + m_IsChecked = !m_IsChecked; + return false; + } + + Evas_Object *check = elm_object_item_part_content_get(getObjectItem(), m_CheckPart); + if (check) { + /* Enable animation */ + elm_object_signal_emit(check, m_IsChecked + ? "elm,activate,check,on" : "elm,activate,check,off", "elm"); + elm_check_state_set(check, m_IsChecked); + } + + return true; +} + bool SelectItem::isExcluded() const { return m_IsExcluded; @@ -50,49 +78,50 @@ SelectMode SelectItem::getSelectMode() const return m_SelectMode; } -void SelectItem::setSelectMode(SelectMode selectMode) -{ - m_SelectMode = selectMode; - - setChecked(false); - updateCheckPart(); - onSelectModeChanged(m_SelectMode); -} - -SelectResult SelectItem::getSelectResult() const -{ - return m_HasCustomResult ? m_CustomResult : getDefaultResult(); -} - -bool SelectItem::hasCustomResult() const +Evas_Object *SelectItem::getContent(Evas_Object *parent, const char *part) { - return m_HasCustomResult; -} + if (m_SelectMode == SelectMulti) { + if (strcmp(part, m_CheckPart) == 0) { + Elm_Check *check = elm_check_add(parent); + elm_check_state_set(check, m_IsChecked); + elm_check_state_pointer_set(check, &m_IsChecked); + elm_object_style_set(check, "genlist/select_mode"); + evas_object_propagate_events_set(check, EINA_FALSE); + evas_object_smart_callback_add(check, "changed", + makeCallback(&SelectItem::onCheckChanged), this); + + elm_atspi_accessible_relationship_append(check, ELM_ATSPI_RELATION_CONTROLLED_BY, getObjectItem()); + elm_atspi_accessible_relationship_append(getObjectItem(), ELM_ATSPI_RELATION_CONTROLLER_FOR, check); + elm_atspi_accessible_relationship_append(getObjectItem(), ELM_ATSPI_RELATION_DESCRIBED_BY, check); + + return check; + } + } -void SelectItem::setCustomResult(SelectResult result) -{ - m_CustomResult = result; - m_HasCustomResult = true; + return nullptr; } -void SelectItem::unsetCustomResult() +void SelectItem::onInserted() { - m_HasCustomResult = false; + if (!m_SelectView) { + m_SelectView = getParent()->findParent<SelectView>(); + if (m_SelectView) { + m_SelectView->addSelectItem(this); + } + } } -Evas_Object *SelectItem::getContent(Evas_Object *parent, const char *part) +void SelectItem::onDelete() { - if (m_SelectMode == SelectMulti) { - return CheckItem::getContent(parent, part); + if (m_SelectView) { + m_SelectView->removeSelectItem(this); } - - return nullptr; } void SelectItem::onVisibilityChanged(bool isVisible) { if (m_SelectView && !m_IsExcluded) { - return m_SelectView->onItemVisibilityChanged(this, isVisible); + m_SelectView->onItemVisibilityChanged(this, isVisible); } } @@ -103,15 +132,41 @@ void SelectItem::onSelected() m_SelectView->onItemSelected(this); } } else if (m_SelectMode == SelectMulti) { - CheckItem::onSelected(); + setChecked(!m_IsChecked); } } -bool SelectItem::onChecked(bool isChecked) +void SelectItem::setSelectMode(SelectMode selectMode) { - if (m_SelectView && !m_IsExcluded) { - return m_SelectView->onItemChecked(this, isChecked); + m_SelectMode = selectMode; + + setChecked(false); + update(m_CheckPart, ELM_GENLIST_ITEM_FIELD_CONTENT); + onSelectModeChanged(m_SelectMode); +} + +void SelectItem::onCheckChanged(Evas_Object *check, void *eventInfo) +{ + if (!isCheckAllowed()) { + elm_check_state_set(check, !m_IsChecked); } +} - return true; +bool SelectItem::isCheckAllowed() +{ + if (m_IsChecking) { + return false; + } + + bool isAllowed = false; + m_IsChecking = true; + + if (onChecked(m_IsChecked)) { + if (m_IsExcluded || !m_SelectView || m_SelectView->onItemChecked(this, m_IsChecked)) { + isAllowed = true; + } + } + + m_IsChecking = false; + return isAllowed; } diff --git a/lib-apps-common/src/Ux/SelectView.cpp b/lib-apps-common/src/Ux/SelectView.cpp index 2b52467..dfb2f85 100644 --- a/lib-apps-common/src/Ux/SelectView.cpp +++ b/lib-apps-common/src/Ux/SelectView.cpp @@ -15,14 +15,14 @@ */ #include "Ux/SelectView.h" -#include "Ux/SelectItem.h" - +#include "Ux/CircleSelector.h" #include "Ui/Genlist.h" +#include "Ui/GenGroupItem.h" +#include "Ui/Toast.h" #include "Utils/Callback.h" #include <app_i18n.h> #include <algorithm> -#include <notification.h> #define TITLE_BUFFER_SIZE 64 #define POPUP_BUFFER_SIZE 256 @@ -30,22 +30,17 @@ using namespace Ux; using namespace std::placeholders; -SelectView::SelectView() - : m_DoneButton(nullptr), m_CancelButton(nullptr), m_IsMultiChecking(false), +SelectView::SelectView(Model::DataProvider &provider, Ui::GenContainer *container) + : ListView(provider, container), + m_MultiSelector(nullptr), m_DoneButton(nullptr), + m_IsEmptyResultAllowed(false), m_IsMultiChecking(false), m_TotalCount(0), m_TotalSelectCount(0), m_VisibleCount(0), m_VisibleSelectCount(0), - m_SelectLimit(0), m_IsEmptyResultAllowed(false), m_SelectMode(SelectNone), - m_Strings{ nullptr } + m_SelectLimit(0), m_SelectMode(SelectNone), + m_Strings{ }, m_AccessStrings{ } { } -SelectView::~SelectView() -{ - if (auto multiSelector = m_MultiSelector.lock()) { - delete multiSelector.get(); - } -} - SelectMode SelectView::getSelectMode() const { return m_SelectMode; @@ -66,14 +61,14 @@ const SelectView::SelectItems &SelectView::getSelectItems() const return m_Items; } -bool SelectView::isMaxSelected() const +void SelectView::setStrings(const Strings &strings) { - return m_VisibleSelectCount == getSelectMax(); + m_Strings = strings; } -void SelectView::setStrings(const Strings &strings) +void SelectView::setAccessibleStrings(const AccessibleStrings &strings) { - m_Strings = strings; + m_AccessStrings = strings; } void SelectView::setSelectMode(SelectMode selectMode) @@ -81,12 +76,16 @@ void SelectView::setSelectMode(SelectMode selectMode) if (m_SelectMode != selectMode) { m_SelectMode = selectMode; - updatePageTitle(); - updatePageButtons(); + updateTitle(); updateMultiSelector(); for (auto &&item : m_Items) { item->setSelectMode(m_SelectMode); + if (m_SelectMode == SelectMulti) { + setAccessSiblings(item); + } else { + unsetAccessSiblings(item); + } } onSelectModeChanged(m_SelectMode); @@ -111,7 +110,7 @@ void SelectView::setSelectLimit(size_t selectLimit) updateMultiSelectorState(); updateDoneButtonState(); - updatePageTitle(); + updateTitle(); onSelectLimitChanged(m_SelectLimit); } @@ -145,15 +144,24 @@ void SelectView::setLimitCallback(LimitCallback callback) m_OnLimitReached = std::move(callback); } -void SelectView::onPageAttached(Ui::NavigatorPage *page) +Evas_Object *SelectView::createContent(Evas_Object *parent) { - updatePageTitle(); - updatePageButtons(); + Evas_Object *layout = elm_layout_add(parent); + elm_layout_theme_set(layout, "layout", "select_mode", "default"); + elm_object_content_set(layout, ListView::createContent(layout)); + elm_object_part_content_set(layout, "elm.swallow.icon", createMultiSelector(layout)); + elm_object_part_content_set(layout, "elm.swallow.button", createDoneButton(layout)); - evas_object_smart_callback_add(getEvasObject(), "language,changed", + evas_object_smart_callback_add(getContainer()->getEvasObject(), "scroll", + makeCallback(&SelectView::onScrolled), this); + evas_object_smart_callback_add(layout, "language,changed", [](void *data, Evas_Object *, void *) { - ((SelectView *) data)->updatePageTitle(); + auto view = (SelectView *) data; + view->updateTitle(); }, this); + updateTitle(); + + return layout; } bool SelectView::onBackPressed() @@ -167,59 +175,66 @@ bool SelectView::onBackPressed() return true; } -void SelectView::onTitleChanged(const char *title) +void SelectView::onItemInserted(ListItem *item) { - if (auto page = getPage()) { - page->setTitle(title); + if (m_SelectMode == SelectMulti) { + setAccessSiblings(item); } } -Evas_Object *SelectView::createDoneButton() +Evas_Object *SelectView::createMultiSelector(Evas_Object *parent) { - if (auto page = getPage()) { - return page->addTitleButton(Ui::ButtonRight, m_Strings.buttonDone, nullptr, nullptr); - } - - return nullptr; + m_MultiSelector = new CircleSelector(); + m_MultiSelector->setStrings(m_Strings.selectorStrings); + m_MultiSelector->setAccessibleStrings(m_AccessStrings.selectorStrings); + m_MultiSelector->setChangeCallback(std::bind(&SelectView::onMultiSelectorChanged, this, _1)); + m_MultiSelector->create(parent); + elm_atspi_accessible_name_cb_set(m_MultiSelector->getEvasObject(), + makeCallback(&SelectView::getAccessibleTitle), this); + return m_MultiSelector->getEvasObject(); } -Evas_Object *SelectView::createCancelButton() +Evas_Object *SelectView::createDoneButton(Evas_Object *parent) { - if (auto page = getPage()) { - return page->addTitleButton(Ui::ButtonLeft, m_Strings.buttonCancel, nullptr, nullptr); - } - - return nullptr; + m_DoneButton = elm_button_add(parent); + elm_object_style_set(m_DoneButton, "bottom"); + elm_object_translatable_text_set(m_DoneButton, m_Strings.buttonDone); + evas_object_smart_callback_add(m_DoneButton, "clicked", makeCallback(&SelectView::onDonePressed), this); + elm_atspi_accessible_relationship_append(m_DoneButton, ELM_ATSPI_RELATION_FLOWS_TO, getMoreMenu()); + return m_DoneButton; } -void SelectView::addSelectItem(SelectItem *item) +void SelectView::setAccessSiblings(ListItem *item) { - item->m_SelectView = this; - item->setSelectMode(m_SelectMode); - m_Items.push_back(item); + elm_atspi_accessible_relationship_append(item->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_FROM, m_MultiSelector->getEvasObject()); + elm_atspi_accessible_relationship_append(item->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_TO, m_DoneButton); +} - if (!item->isExcluded()) { - updateTotalCount(CountIncrement, item); - } +void SelectView::unsetAccessSiblings(ListItem *item) +{ + elm_atspi_accessible_relationship_remove(item->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_FROM, m_MultiSelector->getEvasObject()); + elm_atspi_accessible_relationship_remove(item->getObjectItem(), + ELM_ATSPI_RELATION_FLOWS_TO, m_DoneButton); } -void SelectView::removeSelectItem(SelectItem *item) +void SelectView::onScrolled(Evas_Object *obj, void *eventInfo) { - auto it = std::find(m_Items.begin(), m_Items.end(), item); - if (it == m_Items.end()) { - return; + Ui::GenItem *middleItem = getContainer()->getMiddleItem(); + Ui::GenGroupItem *groupItem = middleItem->getParentItem(); + if (!groupItem || groupItem->getFirstItem() != middleItem) { + groupItem = nullptr; } - m_Items.erase(it); - if (!item->isExcluded()) { - updateTotalCount(CountDecrement, item); - } - item->m_SelectView = nullptr; -} + elm_atspi_accessible_relationships_clear(m_MultiSelector->getEvasObject()); + elm_atspi_accessible_relationship_append(m_MultiSelector->getEvasObject(), + ELM_ATSPI_RELATION_FLOWS_TO, groupItem ? groupItem->getObjectItem() : middleItem->getObjectItem()); -Ui::ControlPtr SelectView::getMultiSelector() -{ - return m_MultiSelector; + elm_atspi_accessible_relationships_clear(m_DoneButton); + elm_atspi_accessible_relationship_append(m_DoneButton, + ELM_ATSPI_RELATION_FLOWS_FROM, middleItem->getObjectItem()); } size_t SelectView::getSelectMax() const @@ -236,8 +251,37 @@ bool SelectView::isLimitReached() const return m_SelectLimit && m_TotalSelectCount == m_SelectLimit; } -void SelectView::updatePageTitle() +bool SelectView::isMaxSelected() const +{ + return m_VisibleSelectCount == getSelectMax(); +} + +char *SelectView::getAccessibleTitle(Evas_Object *obj) +{ + char buffer[TITLE_BUFFER_SIZE]; + if (m_SelectMode == SelectMulti) { + if (m_SelectLimit) { + int len = snprintf(buffer, sizeof(buffer), _(m_AccessStrings.titleWithLimit), m_TotalSelectCount, m_SelectLimit); + if (len > 0) { + return strdup(buffer); + } + } else if (m_TotalSelectCount || !m_Strings.titleMulti) { + int len = snprintf(buffer, sizeof(buffer), _(m_AccessStrings.titleWithCount), m_TotalSelectCount); + if (len > 0) { + return strdup(buffer); + } + } + } + + return Utils::safeDup(elm_object_text_get(obj)); +} + +void SelectView::updateTitle() { + if (!m_MultiSelector) { + return; + } + char buffer[TITLE_BUFFER_SIZE]; const char *title = nullptr; @@ -250,37 +294,22 @@ void SelectView::updatePageTitle() break; case SelectMulti: if (m_SelectLimit) { - snprintf(buffer, sizeof(buffer), _(m_Strings.titleWithLimit), m_TotalSelectCount, m_SelectLimit); - title = buffer; - } else if (m_TotalSelectCount) { - snprintf(buffer, sizeof(buffer), _(m_Strings.titleWithCount), m_TotalSelectCount); - title = buffer; + int len = snprintf(buffer, sizeof(buffer), _(m_Strings.titleWithLimit), m_TotalSelectCount, m_SelectLimit); + if (len > 0) { + title = buffer; + } + } else if (m_TotalSelectCount || !m_Strings.titleMulti) { + int len = snprintf(buffer, sizeof(buffer), _(m_Strings.titleWithCount), m_TotalSelectCount); + if (len > 0) { + title = buffer; + } } else { title = m_Strings.titleMulti; } break; } - onTitleChanged(title); -} - -void SelectView::updatePageButtons() -{ - switch (m_SelectMode) { - case SelectNone: - case SelectSingle: - if (m_DoneButton) { - destroyPageButtons(); - } - break; - - case SelectMulti: - if (!m_DoneButton) { - createPageButtons(); - } - updateDoneButtonState(); - break; - } + elm_object_translatable_text_set(m_MultiSelector->getEvasObject(), title); } void SelectView::updateDoneButtonState() @@ -291,35 +320,29 @@ void SelectView::updateDoneButtonState() void SelectView::updateMultiSelector() { - if (m_SelectMode == SelectMulti && m_VisibleCount) { - if (m_MultiSelector.expired()) { - auto multiSelector = createMultiSelector(); - multiSelector->setStrings({ m_Strings.selectAll, m_Strings.deselectAll }); - multiSelector->setChangeCallback(std::bind(&SelectView::onMultiSelectorChanged, this, _1)); - - m_MultiSelector = multiSelector->getWeakPtr(); - } - - updateMultiSelectorState(); - } else { - if (auto multiSelector = m_MultiSelector.lock()) { - delete multiSelector.get(); - } + if (!m_MultiSelector) { + return; } + + Evas_Object *layout = elm_object_parent_widget_get(m_MultiSelector->getEvasObject()); + elm_layout_signal_emit(layout, m_SelectMode == SelectMulti && m_VisibleCount + ? "select_mode,button,show" : "select_mode,button,hide", ""); } void SelectView::updateMultiSelectorState() { - if (auto multiSelector = getPtr<MultiSelector>(m_MultiSelector)) { - auto state = MultiSelector::SelectedNone; - if (isMaxSelected()) { - state = MultiSelector::SelectedAll; - } else if (getSelectCount()) { - state = MultiSelector::SelectedPartially; - } + if (!m_MultiSelector) { + return; + } - multiSelector->setState(state); + auto state = MultiSelector::SelectedNone; + if (isMaxSelected()) { + state = MultiSelector::SelectedAll; + } else if (getSelectCount()) { + state = MultiSelector::SelectedPartially; } + + m_MultiSelector->setState(state); } void SelectView::updateTotalCount(CountChange change, SelectItem *item) @@ -348,7 +371,7 @@ void SelectView::updateTotalSelectCount(CountChange change, SelectItem *item) updateDoneButtonState(); /* Prevent updating if multiple checking is in progress (performance optimization) */ if (!m_IsMultiChecking) { - updatePageTitle(); + updateTitle(); } } @@ -369,22 +392,27 @@ void SelectView::updateVisibleSelectCount(CountChange change, SelectItem *item) updateMultiSelectorState(); } -void SelectView::createPageButtons() +void SelectView::addSelectItem(SelectItem *item) { - m_DoneButton = createDoneButton(); - evas_object_smart_callback_add(m_DoneButton, "clicked", makeCallback(&SelectView::onDonePressed), this); + item->setSelectMode(m_SelectMode); + m_Items.push_back(item); - m_CancelButton = createCancelButton(); - evas_object_smart_callback_add(m_CancelButton, "clicked", makeCallback(&SelectView::onCancelPressed), this); + if (!item->isExcluded()) { + updateTotalCount(CountIncrement, item); + } } -void SelectView::destroyPageButtons() +void SelectView::removeSelectItem(SelectItem *item) { - evas_object_del(m_DoneButton); - evas_object_del(m_CancelButton); + auto it = std::find(m_Items.begin(), m_Items.end(), item); + if (it == m_Items.end()) { + return; + } - m_DoneButton = nullptr; - m_CancelButton = nullptr; + m_Items.erase(it); + if (!item->isExcluded()) { + updateTotalCount(CountDecrement, item); + } } void SelectView::onItemExcluded(SelectItem *item, bool isExcluded) @@ -399,9 +427,8 @@ void SelectView::onItemVisibilityChanged(SelectItem *item, bool isVisible) void SelectView::onItemSelected(SelectItem *item) { - if (m_SelectMode == SelectSingle) { - SelectResult result = item->getSelectResult(); - if (m_OnSelected && m_OnSelected({ result })) { + if (m_SelectMode == SelectSingle) { + if (m_OnSelected && m_OnSelected({ item })) { getPage()->close(); } } @@ -440,16 +467,16 @@ bool SelectView::onMultiSelectorChanged(MultiSelector::State state) } m_IsMultiChecking = false; - updatePageTitle(); + updateTitle(); return isAllSelected == isMaxSelected(); } void SelectView::onDonePressed(Evas_Object *button, void *eventInfo) { - std::vector<SelectResult> results; + std::vector<SelectItem *> results; for (auto &&item : m_Items) { if (!item->isExcluded() && item->isChecked()) { - results.push_back(item->getSelectResult()); + results.push_back(item); } } @@ -470,6 +497,10 @@ void SelectView::onLimitReached() if (!m_OnLimitReached || m_OnLimitReached()) { char buffer[POPUP_BUFFER_SIZE]; snprintf(buffer, sizeof(buffer), _(m_Strings.popupLimit), m_SelectLimit); - notification_status_message_post(buffer); + + auto toast = new Ui::Toast(); + toast->create(getEvasObject()); + toast->setText(buffer); + toast->show(); } } |