// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "NotificationsBox.hpp" #include "application-desktop/data/Style.hpp" #include "TextFixedSize.hpp" #include "RichTextParser.hpp" #include "FontManager.hpp" using namespace gui; using namespace style::desktop; NotificationsBox::NotificationsBox(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h) : VBox(parent, x, y, w, h) { this->setAlignment(Alignment(gui::Alignment::Horizontal::Center)); this->setPenWidth(style::window::default_border_no_focus_w); this->setPenFocusWidth(style::window::default_border_no_focus_w); auto getNextNotification = [this]() -> Item * { auto focusedItem = getFocusItem(); if (focusedItem == nullptr) { return nullptr; } auto nextItem = focusedItem->getNavigationItem(NavigationDirection::UP); if (nextItem == nullptr) { nextItem = focusedItem->getNavigationItem(NavigationDirection::DOWN); } return nextItem; }; auto setNextFocusedItemAfterErase = [this](Item *item) -> bool { if (item == nullptr) { setFocus(false); } else { item->clearNavigationItem(NavigationDirection::DOWN); item->clearNavigationItem(NavigationDirection::UP); setNavigation(); setFocusItem(item); } return true; }; inputCallback = [this, setNextFocusedItemAfterErase, getNextNotification](Item &, const InputEvent &event) -> bool { if (event.isShortPress() && event.is(KeyCode::KEY_RF)) { LOG_DEBUG("Removing single notification"); auto ptr = getFocusItem(); if (ptr == nullptr || focusItem == this) { return false; } auto nextItem = getNextNotification(); ptr->inputCallback(*this, event); erase(ptr); return setNextFocusedItemAfterErase(nextItem); } return false; }; } namespace { auto buildImageInactive(const UTF8 &img) -> gui::Image * { auto thumbnail = new gui::Image(img); thumbnail->activeItem = false; return thumbnail; } auto buildNotificationIcon(UTF8 icon) -> gui::Image * { auto thumbnail = buildImageInactive(icon); thumbnail->setMinimumWidth(notifications::IconWidth); thumbnail->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center)); thumbnail->setMargins( gui::Margins(style::window::default_left_margin, 0, style::window::default_right_margin, 0)); return thumbnail; } auto buildNotificationNameLabel(const UTF8 &name, uint32_t width) -> gui::TextFixedSize * { auto text = new gui::TextFixedSize( nullptr, 0, 0, style::desktop::notifications::TextMaxWidth, style::window::label::default_h); text->setMaximumSize(width, Axis::X); TextFormat format(FontManager::getInstance().getFont(style::window::font::medium)); text::RichTextParser rtParser; auto parsedText = rtParser.parse(name, &format); text->setText(std::move(parsedText)); text->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center)); text->setPenWidth(style::window::default_border_no_focus_w); text->setUnderline(false); text->activeItem = false; return text; } constexpr auto maxNotificationValue = "99+"; auto buildNotificationCountText(const UTF8 &indicator) -> gui::Text * { auto number = new gui::Text(); if (indicator.length() > 2) { number->setText(maxNotificationValue); } else { number->setText(indicator); } number->setMinimumWidth(number->getText().length() * notifications::DigitSize); number->setFont(style::window::font::mediumbold); number->setPenWidth(style::window::default_border_no_focus_w); number->setMargins(gui::Margins(0, 0, style::window::default_right_margin, 0)); number->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center)); number->activeItem = false; return number; } void setNotificationHboxLayoutProperties(gui::HBox *hbox) { hbox->setAlignment(Alignment(gui::Alignment::Vertical::Center)); hbox->setMargins(gui::Margins(0, 0, 0, 10)); hbox->setPenWidth(style::window::default_border_no_focus_w); hbox->setPenFocusWidth(style::window::default_border_focus_w); hbox->setEdges(RectangleEdge::Bottom | RectangleEdge::Top); } } // namespace auto NotificationsBox::addNotification(const UTF8 &icon, const UTF8 &name, const UTF8 &indicator, std::function showCallback, std::function clearCallback, std::function onFocus) -> bool { // 1. create hbox for all elements auto el = new gui::HBox(nullptr, 0, 0, style::window::default_body_width, style::window::label::default_h); // 2. Add all elements to hbox layout el->addWidget(buildNotificationIcon(icon)); el->addWidget(buildNotificationNameLabel(name, el->area().w)); el->addWidget(buildImageInactive("dot_12px_hard_alpha_W_G")); el->addWidget(buildNotificationCountText(indicator)); // 3. Set hbox layout properties setNotificationHboxLayoutProperties(el); el->focusChangedCallback = [el, onFocus](Item &) -> bool { onFocus(el->focus); return true; }; el->activatedCallback = [showCallback](Item &) { return showCallback(); }; el->inputCallback = [showCallback, clearCallback, this](Item &, const InputEvent &event) -> bool { if (event.isShortPress() && event.is(KeyCode::KEY_RF) && clearCallback) { return clearCallback(); } return false; }; this->addWidget(el); return el->visible; } auto NotificationsBox::addNotification(const UTF8 &icon, const UTF8 &name) -> bool { auto el = new gui::HBox(this, 0, 0, style::window::default_body_width, style::window::label::default_h); el->addWidget(buildNotificationIcon(icon)); el->addWidget(buildNotificationNameLabel(name, el->area().w)); setNotificationHboxLayoutProperties(el); return el->visible; } bool NotificationsBox::onInput(const InputEvent &inputEvent) { if (inputEvent.isShortPress() && (inputEvent.is(KeyCode::KEY_UP) || inputEvent.is(KeyCode::KEY_DOWN))) { auto handled = handleNavigation(inputEvent); if (!handled) { setFocus(false); } return true; } return VBox::onInput(inputEvent); } bool NotificationsBox::clearAll(const InputEvent &event) { while (!children.empty()) { inputCallback(*this, event); } return true; } void NotificationsBox::navigateToBottom() { setFocusItem(children.back()); }