~aleteoryx/muditaos

ac17db13ea4aff2ac0606fd07fe1f2318d8bf8ca — Lefucjusz 1 year, 11 months ago bc9982c
[BH-1897] Add tick icon to currently selected sound

* Added mechanics showing tick icon
next to currently selected sound
in Bedtime and Pre-wakeup.
* Removed no longer used LabelOption
widget.
29 files changed, 280 insertions(+), 197 deletions(-)

M module-apps/application-settings/windows/apps/PhoneWindow.cpp
M module-gui/gui/widgets/Alignment.cpp
M module-gui/gui/widgets/Alignment.hpp
M module-gui/gui/widgets/Image.cpp
M module-gui/gui/widgets/Image.hpp
M module-gui/gui/widgets/Item.cpp
M module-gui/gui/widgets/ListItem.cpp
M products/BellHybrid/BellHybridMain.cpp
M products/BellHybrid/CMakeLists.txt
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.cpp
M products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.hpp
M products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.cpp
M products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.hpp
M products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.cpp
M products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.hpp
M products/BellHybrid/apps/common/CMakeLists.txt
M products/BellHybrid/apps/common/include/common/models/SongsModel.hpp
M products/BellHybrid/apps/common/include/common/options/OptionBellMenu.hpp
R products/BellHybrid/apps/common/include/common/widgets/{LabelOption => LabelOptionWithTick}.hpp
M products/BellHybrid/apps/common/include/common/widgets/ListViewWithLabels.hpp
M products/BellHybrid/apps/common/include/common/widgets/list_items/details.hpp
M products/BellHybrid/apps/common/src/models/SongsModel.cpp
M products/BellHybrid/apps/common/src/options/OptionBellMenu.cpp
M products/BellHybrid/apps/common/src/widgets/LabelListItem.cpp
D products/BellHybrid/apps/common/src/widgets/LabelOption.cpp
A products/BellHybrid/apps/common/src/widgets/LabelOptionWithTick.cpp
M products/BellHybrid/apps/common/src/widgets/ListViewWithLabels.cpp
M products/BellHybrid/assets/assets_common.json
M module-apps/application-settings/windows/apps/PhoneWindow.cpp => module-apps/application-settings/windows/apps/PhoneWindow.cpp +1 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PhoneWindow.hpp"


@@ 76,5 76,4 @@ namespace gui
        info.audioModel  = mAudioModel.get();
        application->switchWindow(gui::window::name::sound_select, std::make_unique<SoundSelectData>(info));
    }

} // namespace gui

M module-gui/gui/widgets/Alignment.cpp => module-gui/gui/widgets/Alignment.cpp +1 -3
@@ 1,11 1,10 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Alignment.hpp"

namespace gui
{

    bool Alignment::operator==(const Alignment &alignment) const
    {
        return !(horizontal != alignment.horizontal || vertical != alignment.vertical);


@@ 43,5 42,4 @@ namespace gui
            return 0;
        }
    }

} /* namespace gui */

M module-gui/gui/widgets/Alignment.hpp => module-gui/gui/widgets/Alignment.hpp +1 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 47,5 47,4 @@ namespace gui
        bool operator==(const Alignment &alignment) const;
        bool operator!=(const Alignment &alignment) const;
    };

} /* namespace gui */

M module-gui/gui/widgets/Image.cpp => module-gui/gui/widgets/Image.cpp +8 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Image.hpp"


@@ 11,13 11,13 @@

namespace gui
{

    Image::Image() : Rect(), imageMap{nullptr}
    {
        type = ItemType::IMAGE;
    }

    Image::Image(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, const UTF8 imageName)
    Image::Image(
        Item *parent, std::uint32_t x, std::uint32_t y, std::uint32_t w, std::uint32_t h, const UTF8 &imageName)
        : Rect(parent, x, y, w, h), imageMap{nullptr}
    {
        type = ItemType::IMAGE;


@@ 38,17 38,17 @@ namespace gui
        set(imageName, specifier);
    }

    bool Image::set(int id)
    bool Image::set(std::uint32_t id)
    {
        auto map = ImageManager::getInstance().getImageMap(id);
        const auto map = ImageManager::getInstance().getImageMap(id);
        if (map == nullptr) {
            LOG_ERROR("Unable to get an image map for id: %d", id);
            LOG_ERROR("Unable to get an image map for id: %" PRIu32, id);
            return false;
        }

        imageMap = map;
        auto w   = imageMap->getWidth();
        auto h   = imageMap->getHeight();
        const auto w = imageMap->getWidth();
        const auto h = imageMap->getHeight();
        setMinimumWidth(w);
        setMinimumHeight(h);
        setArea(BoundingBox(getX(), getY(), w, h));

M module-gui/gui/widgets/Image.hpp => module-gui/gui/widgets/Image.hpp +15 -12
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 12,30 12,33 @@

namespace gui
{

    /// image element on screen, it will automatically set it's size to fit space of selected image
    class Image : public Rect
    {
      protected:
        /// internal representation of the image
        ImageMap *imageMap = nullptr;

      public:
        Image();
        /// create new Image element and resize it to size needed by selected Image
        Image(const UTF8 &imageName, ImageTypeSpecifier specifier = ImageTypeSpecifier::None);
        explicit Image(const UTF8 &imageName, ImageTypeSpecifier specifier = ImageTypeSpecifier::None);
        Image(Item *parent, const UTF8 &imageName, ImageTypeSpecifier specifier = ImageTypeSpecifier::None);
        /// Create new Image element on position x,y w&h will be *ignored*
        Image(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, const UTF8 = UTF8{""});
        Image(Item *parent, uint32_t x, uint32_t y, const UTF8 imgName = UTF8{""})
            : Image(parent, x, y, 0u, 0u, imgName)
        Image(Item *parent,
              std::uint32_t x,
              std::uint32_t y,
              std::uint32_t w,
              std::uint32_t h,
              const UTF8 &imageName = UTF8{""});
        Image(Item *parent, std::uint32_t x, std::uint32_t y, const UTF8 &imageName = UTF8{""})
            : Image(parent, x, y, 0U, 0U, imageName)
        {}

        bool set(int id);
        bool set(std::uint32_t id);
        void set(const UTF8 &name, ImageTypeSpecifier specifier = ImageTypeSpecifier::None);

        void buildDrawListImplementation(std::list<Command> &commands) override;
        void accept(GuiVisitor &visitor) override;
    };

      protected:
        /// internal representation of the image
        ImageMap *imageMap = nullptr;
    };
} /* namespace gui */

M module-gui/gui/widgets/Item.cpp => module-gui/gui/widgets/Item.cpp +2 -4
@@ 345,11 345,9 @@ namespace gui
    Alignment Item::getAlignment(Axis axis)
    {
        if (axis == Axis::X) {
            return Alignment(alignment.horizontal, Alignment::Vertical::None);
        }
        else {
            return Alignment(Alignment::Horizontal::None, alignment.vertical);
            return {alignment.horizontal, Alignment::Vertical::None};
        }
        return {Alignment::Horizontal::None, alignment.vertical};
    }

    Alignment &Item::getAlignment()

M module-gui/gui/widgets/ListItem.cpp => module-gui/gui/widgets/ListItem.cpp +1 -4
@@ 1,13 1,11 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ListItem.hpp"
#include <log/log.hpp>
#include <Style.hpp>

namespace gui
{

    ListItem::ListItem()
    {
        setRadius(0);


@@ 23,5 21,4 @@ namespace gui
    {
        visitor.visit(*this);
    }

} /* namespace gui */

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +1 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PlatformFactory.hpp"

M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +6 -6
@@ 135,27 135,27 @@ include(DownloadAsset)
# copy all assets required to build catalog under current folder as in json recipe
download_asset_json(json-proprietary-target
                    ${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_proprietary.json
                   ${SYSROOT_PATH}/system_a/
                    ${SYSROOT_PATH}/system_a/
                    MuditaOSAssets
                    ${MUDITA_CACHE_DIR}
    )
download_asset_release_json(json-common-target
                            ${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_common.json
                           ${SYSROOT_PATH}/system_a/
                            ${SYSROOT_PATH}/system_a/
                            MuditaOSPublicAssets
                            0.0.22
                            0.0.23
                            ${MUDITA_CACHE_DIR}
    )
download_asset_release_json(json-community-target
                            ${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_community.json
                           ${SYSROOT_PATH}/system_a/
                            ${SYSROOT_PATH}/system_a/
                            MuditaOSPublicAssets
                            0.0.22
                            0.0.23
                            ${MUDITA_CACHE_DIR}
    )
download_asset_json(json-rt1051-target
                            ${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_rt1051.json
                           ${SYSROOT_PATH}/system_a/assets
                            ${SYSROOT_PATH}/system_a/assets
                            MuditaOSAssets
                            ${MUDITA_CACHE_DIR}
    )

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp +3 -0
@@ 15,6 15,7 @@ namespace app::relaxation
        songsModel->createData(activateCallback);
        updateViewState();
    }

    void RelaxationMainWindowPresenter::updateViewState()
    {
        auto view = getView();


@@ 22,10 23,12 @@ namespace app::relaxation
            view->updateViewState();
        }
    }

    void RelaxationMainWindowPresenter::updateRecordsCount()
    {
        songsModel->updateRecordsCount();
    }

    std::shared_ptr<SongsModel> RelaxationMainWindowPresenter::getSongsModel()
    {
        return songsModel;

M products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.cpp => products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.cpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FrontlightListItemProvider.hpp"


@@ 39,6 39,7 @@ namespace app::bell_settings
        };
        internalData.emplace_back(mode);
    }

    FrontlightListItemProvider::FrontlightListItemProvider(AbstractFrontlightModel &model) : model{model}
    {
        buildListItems();

M products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.hpp => products/BellHybrid/apps/application-bell-settings/models/FrontlightListItemProvider.hpp +2 -4
@@ 1,15 1,14 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <common/BellListItemProvider.hpp>
#include <common/models/AbstractSettingsModel.hpp>
#include <functional>

namespace app::bell_settings
{
    class AbstractFrontlightModel;

    class FrontlightListItemProvider : public app::BellListItemProvider
    {
      public:


@@ 20,5 19,4 @@ namespace app::bell_settings

        AbstractFrontlightModel &model;
    };

} // namespace app::bell_settings

M products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.cpp => products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.cpp +5 -4
@@ 53,19 53,20 @@ namespace app::bell_settings
                                  songsModel);

        chimeTone->set_on_value_change_cb([this](const auto &val) {
            currentSoundPath = val;
            if (onToneChange) {
                onToneChange(val);
            }
        });

        chimeTone->onEnter = [this, chimeTone]() {
        chimeTone->onEnter = [this]() {
            if (onToneEnter) {
                onToneEnter(chimeTone->value());
                onToneEnter(currentSoundPath);
            }
        };
        chimeTone->onExit = [this, chimeTone]() {
        chimeTone->onExit = [this]() {
            if (onToneExit) {
                onToneExit(chimeTone->value());
                onToneExit(currentSoundPath);
            }
        };
        internalData.emplace_back(chimeTone);

M products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.hpp => products/BellHybrid/apps/application-bell-settings/models/alarm_settings/PrewakeUpListItemProvider.hpp +1 -0
@@ 34,5 34,6 @@ namespace app::bell_settings
        AbstractPrewakeUpSettingsModel &settingsModel;
        app::list_items::NumericWithBar *prewakeUpVolume{nullptr};
        std::shared_ptr<SongsModel> songsModel;
        UTF8 currentSoundPath;
    };
} // namespace app::bell_settings

M products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.cpp => products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.cpp +12 -20
@@ 13,11 13,11 @@ namespace gui
                                         AbstractSettingsModel<UTF8> &settingsModel,
                                         std::shared_ptr<app::SongsModel> songsModel)
        : BellSideListItemWithCallbacks({}, BellBaseLayout::LayoutType::WithoutArrows), settingsModel{settingsModel},
          songsModel{songsModel}
          songsModel{std::move(songsModel)}
    {
        rearrangeBaseLayout();

        list = new ListViewWithLabels(body, 0, 0, style::window_width, style::window_height, songsModel);
        list = new ListViewWithLabels(body, 0, 0, style::window_width, style::window_height, this->songsModel);
        list->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        list->applySizeRestrictions(style::bell_options_list::w,
                                    style::bell_options_list::h,


@@ 31,40 31,32 @@ namespace gui
            return storedCallback(item, invertNavigationDirection(event));
        };

        focusChangedCallback = [this]([[maybe_unused]] Item &item) {
            if (focus) {
                set_value(focusedRecordPath);
            }
            OnFocusChangedCallback();
            return true;
        };
        getValue = [this]() { this->settingsModel.setValue(this->value()); };
        setValue = [this]() { set_value(this->settingsModel.getValue()); };

        auto onListItemActivate = []([[maybe_unused]] const db::multimedia_files::MultimediaFilesRecord &record) {
        auto onListItemActivate = [this]([[maybe_unused]] const db::multimedia_files::MultimediaFilesRecord &record) {
            set_value(record.fileInfo.path);
            list->rebuildList(listview::RebuildType::InPlace);
            return false;
        };
        auto onListItemFocusAcquire = [this](const db::multimedia_files::MultimediaFilesRecord &record) {
            focusedRecordPath = record.fileInfo.path;
            onValueChange(focusedRecordPath);
            onValueChange(record.fileInfo.path);
            return true;
        };

        songsModel->createData(onListItemActivate, onListItemFocusAcquire);
        setValue();
        this->songsModel->createData(onListItemActivate, onListItemFocusAcquire);
        list->rebuildList(listview::RebuildType::Full);

        getValue = [this]() {
            this->settingsModel.setValue(focusedRecordPath);
        };
        setValue = [this]() { focusedRecordPath = this->settingsModel.getValue(); };
    }

    auto SongsListViewItem::value() const -> UTF8
    {
        return focusedRecordPath;
        return songsModel->getCurrentlyChosenRecordPath();
    }

    auto SongsListViewItem::set_value(const UTF8 &value) -> void
    {
        focusedRecordPath = value;
        songsModel->setCurrentlyChosenRecordPath(value);
    }

    auto SongsListViewItem::set_on_value_change_cb(std::function<void(const UTF8 &)> &&cb) -> void

M products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.hpp => products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.hpp +0 -1
@@ 33,6 33,5 @@ namespace gui
        std::shared_ptr<app::SongsModel> songsModel;
        ListViewWithLabels *list{nullptr};
        std::function<void(const UTF8 &)> onValueChange;
        std::string focusedRecordPath;
    };
} // namespace gui

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +14 -5
@@ 25,6 25,9 @@ target_sources(application-bell-common
        src/SoundsRepository.cpp
        src/BellListItemProvider.cpp
        src/SoundsProvider.cpp
        src/BellSideListItemWithCallbacks.cpp
        src/TimeUtils.cpp

        src/windows/BellFactoryReset.cpp
        src/windows/BellFinishedWindow.cpp
        src/windows/BellTurnOffWindow.cpp


@@ 33,14 36,13 @@ target_sources(application-bell-common
        src/windows/ShortcutsWindow.cpp
        src/windows/BellBatteryStatusWindow.cpp
        src/windows/AppsBatteryStatusWindow.cpp
        src/BellSideListItemWithCallbacks.cpp
        src/TimeUtils.cpp
        src/TimeUtils.cpp

        src/models/SettingsModel.cpp
        src/models/BedtimeModel.cpp
        src/models/LowBatteryInfoModel.cpp
        src/models/SongsModel.cpp
        src/models/PreWakeUpModel.cpp

        src/popups/AlarmActivatedWindow.cpp
        src/popups/AlarmActivatedWindow.cpp
        src/popups/BedtimeNotificationWindow.cpp


@@ 49,6 51,7 @@ target_sources(application-bell-common
        src/popups/BellRebootWindow.cpp
        src/popups/ChargingNotificationWindow.cpp
        src/popups/presenter/AlarmActivatedPresenter.cpp

        src/widgets/AlarmIcon.cpp
        src/widgets/ListItems.cpp
        src/widgets/BellBattery.cpp


@@ 61,7 64,7 @@ target_sources(application-bell-common
        src/widgets/ClockVertical.cpp
        src/widgets/ListViewWithLabels.cpp
        src/widgets/LabelListItem.cpp
        src/widgets/LabelOption.cpp
        src/widgets/LabelOptionWithTick.cpp

        src/options/BellOptionWindow.cpp
        src/options/BellShortOptionWindow.cpp


@@ 100,9 103,11 @@ target_sources(application-bell-common
        include/common/windows/BellBatteryStatusWindow.hpp
        include/common/windows/AppsBatteryStatusWindow.hpp
        include/common/TimeUtils.hpp

        include/common/data/BatteryUtils.hpp
        include/common/data/FrontlightUtils.hpp
        include/common/data/BatteryStatusSwitchData.hpp

        include/common/models/AbstractAlarmModel.hpp
        include/common/models/AbstractBedtimeModel.hpp
        include/common/models/AbstractSettingsModel.hpp


@@ 121,6 126,7 @@ target_sources(application-bell-common
        include/common/models/QuoteModel.hpp
        include/common/models/SongsModel.hpp
        include/common/models/PreWakeUpModel.hpp

        include/common/popups/presenter/AlarmActivatedPresenter.hpp
        include/common/popups/AlarmActivatedWindow.hpp
        include/common/popups/AlarmDeactivatedWindow.hpp


@@ 128,6 134,7 @@ target_sources(application-bell-common
        include/common/popups/BellTurnOffOptionWindow.hpp
        include/common/popups/BellRebootWindow.hpp
        include/common/popups/ChargingNotificationWindow.hpp

        include/common/widgets/AlarmIcon.hpp
        include/common/widgets/BellBattery.hpp
        include/common/widgets/BellConnectionStatus.hpp


@@ 141,10 148,12 @@ target_sources(application-bell-common
        include/common/widgets/ClockVertical.hpp
        include/common/widgets/ListViewWithLabels.hpp
        include/common/widgets/LabelListItem.hpp
        include/common/widgets/LabelOption.hpp
        include/common/widgets/LabelOptionWithTick.hpp

        include/common/options/BellOptionWindow.hpp
        include/common/options/BellShortOptionWindow.hpp
        include/common/options/OptionBellMenu.hpp

        include/common/layouts/HomeScreenLayouts.hpp
        include/common/layouts/BaseHomeScreenLayoutProvider.hpp
        include/common/layouts/HomeScreenLayoutClassic.hpp

M products/BellHybrid/apps/common/include/common/models/SongsModel.hpp => products/BellHybrid/apps/common/include/common/models/SongsModel.hpp +18 -14
@@ 36,30 36,34 @@ namespace app
                   const LabelsWithPaths &pathPrefixes = {});
        virtual ~SongsModel() = default;

        unsigned int requestRecordsCount() override;
        auto requestRecordsCount() -> unsigned override;

        [[nodiscard]] unsigned int getMinimalItemSpaceRequired() const override;
        [[nodiscard]] auto getMinimalItemSpaceRequired() const -> unsigned override;

        gui::ListItem *getItem(gui::Order order) override;
        auto getItem(gui::Order order) -> gui::ListItem * override;

        void requestRecords(std::uint32_t offset, std::uint32_t limit) override;
        auto requestRecords(std::uint32_t offset, std::uint32_t limit) -> void override;

        void createData(OnActivateCallback activateCallback        = nullptr,
                        OnFocusAcquireCallback focusChangeCallback = nullptr) override;
        void updateRecordsCount();
        bool nextRecordExist(gui::Order order);
        auto createData(OnActivateCallback activateCallback        = nullptr,
                        OnFocusAcquireCallback focusChangeCallback = nullptr) -> void override;
        auto updateRecordsCount() -> void;
        auto nextRecordExists(gui::Order order) -> bool;

        auto setCurrentlyChosenRecordPath(const std::string &path) -> void;
        [[nodiscard]] auto getCurrentlyChosenRecordPath() const -> std::string;

      private:
        bool onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  unsigned int repoRecordsCount);
        [[nodiscard]] bool updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records) override;
        gui::ListLabel getLabelFromPath(const std::string &path);
        auto onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  unsigned repoRecordsCount) -> bool;
        [[nodiscard]] auto updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records)
            -> bool override;
        auto getLabelFromPath(const std::string &path) -> gui::ListLabel;

        ApplicationCommon *application;
        ApplicationCommon *application{nullptr};
        std::unique_ptr<AbstractSoundsRepository> songsRepository;
        LabelsWithPaths pathPrefixes;
        std::string currentRecordPath;
        OnActivateCallback activateCallback{nullptr};
        OnFocusAcquireCallback focusAcquireCallback{nullptr};
        std::string currentlyChosenRecordPath{};
    };
} // namespace app

M products/BellHybrid/apps/common/include/common/options/OptionBellMenu.hpp => products/BellHybrid/apps/common/include/common/options/OptionBellMenu.hpp +10 -8
@@ 16,14 16,6 @@ namespace gui::option
{
    class OptionBellMenu : public option::Base
    {
      protected:
        UTF8 text;
        std::function<bool(Item &)> activatedCallback    = nullptr;
        std::function<bool(Item &)> focusChangedCallback = nullptr;
        AppWindow *app                                   = nullptr;

        void prepareListItem(ListItem *item) const;

      public:
        OptionBellMenu(const UTF8 &text,
                       std::function<bool(Item &)> activatedCallback,


@@ 32,10 24,20 @@ namespace gui::option
            : text(text), activatedCallback(std::move(activatedCallback)),
              focusChangedCallback(std::move(focusChangedCallback)), app(app)
        {}

        [[nodiscard]] auto build() const -> ListItem * override;

        [[nodiscard]] auto str() const -> std::string override
        {
            return text;
        }

      protected:
        UTF8 text;
        std::function<bool(Item &)> activatedCallback{nullptr};
        std::function<bool(Item &)> focusChangedCallback{nullptr};
        AppWindow *app{nullptr};

        void prepareListItem(ListItem *item) const;
    };
} // namespace gui::option

R products/BellHybrid/apps/common/include/common/widgets/LabelOption.hpp => products/BellHybrid/apps/common/include/common/widgets/LabelOptionWithTick.hpp +21 -11
@@ 3,24 3,34 @@

#pragma once

#include <common/widgets/LabelListItem.hpp>
#include <common/widgets/ListViewWithLabels.hpp>
#include <common/options/OptionBellMenu.hpp>

namespace gui::option
{
    class LabelOption : public OptionBellMenu
    class LabelOptionWithTick : public OptionBellMenu
    {
      private:
        ListLabel label{};

      public:
        LabelOption(ListLabel label,
                    const UTF8 &text,
                    std::function<bool(Item &)> activatedCallback,
                    std::function<bool(Item &)> focusChangedCallback,
                    AppWindow *app);
        enum class TickState
        {
            Show,
            Hide
        };

        LabelOptionWithTick(ListLabel label,
                            const UTF8 &text,
                            TickState tickState,
                            std::function<bool(Item &)> activatedCallback,
                            std::function<bool(Item &)> focusChangedCallback,
                            AppWindow *app);

        auto build() const -> ListItem * override;

      private:
        auto prepareLabelOption(ListItem *item) const -> void;
        auto getAdjustedText(TextFixedSize *textItem) const -> UTF8;

        [[nodiscard]] auto build() const -> ListItem * override;
        ListLabel label;
        TickState tickState;
    };
} // namespace gui::option

M products/BellHybrid/apps/common/include/common/widgets/ListViewWithLabels.hpp => products/BellHybrid/apps/common/include/common/widgets/ListViewWithLabels.hpp +1 -1
@@ 28,7 28,7 @@ namespace gui
        void reset() override;

      private:
        std::size_t getSlotsLeft();
        [[nodiscard]] std::size_t getSlotsLeft() const;
        void addItemsOnPage() override;
        void addLabelMarker(ListItem *item);
        void updateState(ListLabel newMarker);

M products/BellHybrid/apps/common/include/common/widgets/list_items/details.hpp => products/BellHybrid/apps/common/include/common/widgets/list_items/details.hpp +0 -1
@@ 8,7 8,6 @@
#include <common/data/StyleCommon.hpp>

#include <apps-common/widgets/spinners/Spinners.hpp>
#include <apps-common/widgets/TimeSetFmtSpinner.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <apps-common/widgets/BarGraph.hpp>
#include "common/data/ListItemBarStyle.hpp"

M products/BellHybrid/apps/common/src/models/SongsModel.cpp => products/BellHybrid/apps/common/src/models/SongsModel.cpp +57 -42
@@ 2,7 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <common/models/SongsModel.hpp>
#include <common/widgets/LabelOption.hpp>
#include <common/widgets/LabelOptionWithTick.hpp>

namespace app
{


@@ 16,69 16,74 @@ namespace app
          application(application), songsRepository{std::move(soundsRepository)}, pathPrefixes{pathPrefixes}
    {}

    void SongsModel::createData(OnActivateCallback onActivate, OnFocusAcquireCallback onFocusAcquire)
    {
        activateCallback    = std::move(onActivate);
        focusAcquireCallback = std::move(onFocusAcquire);
        songsRepository->init();
    }

    void SongsModel::updateRecordsCount()
    {
        songsRepository->updateFilesCount();
    }

    unsigned int SongsModel::requestRecordsCount()
    auto SongsModel::requestRecordsCount() -> unsigned
    {
        return songsRepository->getFilesCount();
    }

    unsigned int SongsModel::getMinimalItemSpaceRequired() const
    auto SongsModel::getMinimalItemSpaceRequired() const -> unsigned
    {
        return style::bell_options::h + 2 * style::bell_options::option_margin;
    }

    bool SongsModel::nextRecordExist(gui::Order order)
    auto SongsModel::getItem(gui::Order order) -> gui::ListItem *
    {
        auto exist = getRecord(order) != nullptr;
        getRecord(~order);
        return exist;
    }
        using TickState = gui::option::LabelOptionWithTick::TickState;

    gui::ListItem *SongsModel::getItem(gui::Order order)
    {
        const auto sound = getRecord(order);
        if (sound == nullptr) {
            return nullptr;
        }

        auto item = gui::option::LabelOption{getLabelFromPath(sound->fileInfo.path),
                                             sound->tags.title,
                                             [this, sound]([[maybe_unused]] gui::Item &item) {
                                                 if (activateCallback) {
                                                     return activateCallback(*sound);
                                                 }
                                                 return true;
                                             },
                                             [this, sound](gui::Item &item) {
                                                 if (focusAcquireCallback && item.focus) {
                                                     return focusAcquireCallback(*sound);
                                                 }
                                                 return true;
                                             },
                                             nullptr};
        const auto &filePath = sound->fileInfo.path;
        const auto tickState = (filePath == currentlyChosenRecordPath) ? TickState::Show : TickState::Hide;
        auto item            = gui::option::LabelOptionWithTick{getLabelFromPath(filePath),
                                                     sound->tags.title,
                                                     tickState,
                                                     [this, sound]([[maybe_unused]] gui::Item &item) {
                                                         if (activateCallback) {
                                                             return activateCallback(*sound);
                                                         }
                                                         return true;
                                                     },
                                                     [this, sound](gui::Item &item) {
                                                         if (focusAcquireCallback && item.focus) {
                                                             return focusAcquireCallback(*sound);
                                                         }
                                                         return true;
                                                     },
                                                     nullptr};
        return item.build();
    }

    void SongsModel::requestRecords(std::uint32_t offset, std::uint32_t limit)
    auto SongsModel::requestRecords(std::uint32_t offset, std::uint32_t limit) -> void
    {
        songsRepository->getMusicFiles(offset, limit, [this](const auto &records, const auto repoRecordsCount) {
            return onMusicListRetrieved(records, repoRecordsCount);
        });
    }

    bool SongsModel::onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                          unsigned int repoRecordsCount)
    auto SongsModel::createData(OnActivateCallback onActivate, OnFocusAcquireCallback onFocusAcquire) -> void
    {
        activateCallback     = std::move(onActivate);
        focusAcquireCallback = std::move(onFocusAcquire);
        songsRepository->init();
    }

    auto SongsModel::updateRecordsCount() -> void
    {
        songsRepository->updateFilesCount();
    }

    auto SongsModel::nextRecordExists(gui::Order order) -> bool
    {
        auto exists = getRecord(order) != nullptr;
        getRecord(~order);
        return exists;
    }

    auto SongsModel::onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                          unsigned int repoRecordsCount) -> bool
    {
        if (list != nullptr && recordsCount != repoRecordsCount) {
            recordsCount = repoRecordsCount;


@@ 88,14 93,14 @@ namespace app
        return updateRecords(records);
    }

    bool SongsModel::updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records)
    auto SongsModel::updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records) -> bool
    {
        DatabaseModel::updateRecords(std::move(records));
        list->onProviderDataUpdate();
        return true;
    }

    gui::ListLabel SongsModel::getLabelFromPath(const std::string &path)
    auto SongsModel::getLabelFromPath(const std::string &path) -> gui::ListLabel
    {
        for (const auto &[label, pathPrefix] : pathPrefixes) {
            if (path.find(pathPrefix) != std::string::npos) {


@@ 104,4 109,14 @@ namespace app
        }
        return std::nullopt;
    }

    auto SongsModel::setCurrentlyChosenRecordPath(const std::string &path) -> void
    {
        currentlyChosenRecordPath = path;
    }

    auto SongsModel::getCurrentlyChosenRecordPath() const -> std::string
    {
        return currentlyChosenRecordPath;
    }
} // namespace app

M products/BellHybrid/apps/common/src/options/OptionBellMenu.cpp => products/BellHybrid/apps/common/src/options/OptionBellMenu.cpp +5 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <options/OptionBellMenu.hpp>


@@ 6,9 6,9 @@

namespace
{
    constexpr auto ellipsis       = "...";
    constexpr auto ellipsisSpace  = 2u;
    constexpr auto linesMaxNumber = 1u;
    constexpr auto ellipsis{"..."};
    constexpr auto ellipsisSpace{2U};
    constexpr auto linesMaxNumber{1U};

    UTF8 adjustTextLength(const UTF8 &textToDisplay, std::uint32_t maxCharsInLine)
    {


@@ 17,7 17,6 @@ namespace
        }
        return textToDisplay;
    }

} // namespace

namespace gui::option


@@ 51,6 50,7 @@ namespace gui::option
        const auto maxTextCharsToDisplay =
            optionText->getTextFormat().getFont()->getCharCountInSpace(text, style::bell_options::default_text_width);
        optionText->setRichText(adjustTextLength(text, maxTextCharsToDisplay));

        optionItem->dimensionChangedCallback = [optionBodyHBox](gui::Item &, const BoundingBox &newDim) -> bool {
            optionBodyHBox->setArea({0, 0, newDim.w, newDim.h});
            return true;

M products/BellHybrid/apps/common/src/widgets/LabelListItem.cpp => products/BellHybrid/apps/common/src/widgets/LabelListItem.cpp +2 -5
@@ 4,11 4,6 @@
#include <common/widgets/LabelListItem.hpp>
#include "common/options/OptionBellMenu.hpp"

namespace
{
    constexpr auto linesMaxNumber = 1U;
}

namespace gui
{
    LabelListItem::LabelListItem(ListLabel label) : label{std::move(label)}


@@ 21,6 16,8 @@ namespace gui

    LabelMarkerItem::LabelMarkerItem(const UTF8 &labelText)
    {
        constexpr auto linesMaxNumber{1U};

        setMinimumSize(style::bell_options::default_text_width, style::bell_options::h);
        setMargins(gui::Margins(0, style::bell_options::option_margin, 0, style::bell_options::option_margin));
        setAlignment(gui::Alignment::Horizontal::Center);

D products/BellHybrid/apps/common/src/widgets/LabelOption.cpp => products/BellHybrid/apps/common/src/widgets/LabelOption.cpp +0 -25
@@ 1,25 0,0 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <common/widgets/LabelOption.hpp>

#include <utility>

namespace gui::option
{
    LabelOption::LabelOption(ListLabel label,
                             const UTF8 &text,
                             std::function<bool(Item &)> activatedCallback,
                             std::function<bool(Item &)> focusChangedCallback,
                             gui::AppWindow *app)
        : OptionBellMenu(text, std::move(activatedCallback), std::move(focusChangedCallback), app),
          label(std::move(label))
    {}

    auto LabelOption::build() const -> ListItem *
    {
        auto labelItem = new LabelListItem(label);
        OptionBellMenu::prepareListItem(labelItem);
        return labelItem;
    }
} // namespace gui::option

A products/BellHybrid/apps/common/src/widgets/LabelOptionWithTick.cpp => products/BellHybrid/apps/common/src/widgets/LabelOptionWithTick.cpp +86 -0
@@ 0,0 1,86 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <common/widgets/LabelOptionWithTick.hpp>
#include <common/widgets/LabelListItem.hpp>

namespace gui::option
{
    LabelOptionWithTick::LabelOptionWithTick(ListLabel label,
                                             const UTF8 &text,
                                             TickState tickState,
                                             std::function<bool(Item &)> activatedCallback,
                                             std::function<bool(Item &)> focusChangedCallback,
                                             gui::AppWindow *app)
        : OptionBellMenu(text, std::move(activatedCallback), std::move(focusChangedCallback), app),
          label{std::move(label)}, tickState{tickState}
    {}

    auto LabelOptionWithTick::build() const -> ListItem *
    {
        auto labelOption = new LabelListItem(label);
        prepareLabelOption(labelOption);
        return labelOption;
    }

    auto LabelOptionWithTick::prepareLabelOption(ListItem *labelOption) const -> void
    {
        constexpr auto linesMaxNumber{1U};
        constexpr auto tickMarginRight{12U};

        labelOption->setMinimumSize(style::bell_options::default_text_width, style::bell_options::h);
        labelOption->setMargins(Margins(0, style::bell_options::option_margin, 0, style::bell_options::option_margin));
        labelOption->setAlignment(gui::Alignment::Horizontal::Center);
        labelOption->activatedCallback    = activatedCallback;
        labelOption->focusChangedCallback = focusChangedCallback;

        auto labelOptionBody = new HBox(labelOption, 0, 0, 0, 0);
        labelOptionBody->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        style::window::decorate(labelOptionBody);

        Image *tickImage      = nullptr;
        auto optionTextMargin = 0;
        auto optionTextWidth  = style::bell_options::default_text_width;
        if (tickState == TickState::Show) {
            tickImage                  = new Image(nullptr, 0, 0, 0, 0, "bell_small_tick_W_M");
            const auto imageTotalSpace = static_cast<int>(tickImage->getWidth() + tickMarginRight);
            optionTextMargin           = imageTotalSpace;
            optionTextWidth -= imageTotalSpace * 2; // Option text has to be centered, so clip space from both sides
        }
        auto labelOptionText = new TextFixedSize(labelOptionBody, 0, 0, 0, 0);
        labelOptionText->setLines(linesMaxNumber);
        labelOptionText->setTextType(gui::TextType::SingleLine);
        labelOptionText->drawUnderline(false);
        labelOptionText->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        labelOptionText->setMargins(Margins{optionTextMargin, 0, 0, 0});
        labelOptionText->setMaximumSize(optionTextWidth, style::bell_options::h);
        labelOptionText->setFont(style::window::font::largelight);
        labelOptionText->setRichText(getAdjustedText(labelOptionText));

        /* Add tick after option text to resolve weird alignment issues */
        if (tickImage != nullptr) {
            tickImage->setAlignment(Alignment{Alignment::Horizontal::Right, Alignment::Vertical::Center});
            tickImage->setMargins(Margins{0, 0, tickMarginRight, 0});
            tickImage->setVisible(true);
            labelOptionBody->addWidget(tickImage);
        }

        labelOption->dimensionChangedCallback = [labelOptionBody](gui::Item &, const BoundingBox &newDim) -> bool {
            labelOptionBody->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    auto LabelOptionWithTick::getAdjustedText(TextFixedSize *textItem) const -> UTF8
    {
        constexpr auto ellipsis{"..."};
        constexpr auto ellipsisSpace{2U};

        const auto maxCharsInLine =
            textItem->getTextFormat().getFont()->getCharCountInSpace(text, textItem->widgetMaximumArea.w);
        if (maxCharsInLine < text.length()) {
            return text.substr(0, maxCharsInLine - ellipsisSpace) + ellipsis;
        }
        return text;
    }
} // namespace gui::option

M products/BellHybrid/apps/common/src/widgets/ListViewWithLabels.cpp => products/BellHybrid/apps/common/src/widgets/ListViewWithLabels.cpp +4 -8
@@ 5,11 5,6 @@
#include <common/widgets/LabelListItem.hpp>
#include <common/models/SongsModel.hpp>

namespace
{
    constexpr auto maxItemDisplayed{4U};
} // namespace

namespace gui
{
    ListViewWithLabels::ListViewWithLabels(Item *parent,


@@ 111,8 106,8 @@ namespace gui
                if (songsProvider == nullptr) {
                    break;
                }
                const auto nextItemExist = songsProvider->nextRecordExist(getOrderFromDirection());
                if (!nextItemExist && getSlotsLeft() == 1) {
                const auto nextItemExists = songsProvider->nextRecordExists(getOrderFromDirection());
                if (!nextItemExists && getSlotsLeft() == 1) {
                    body->addWidget(createMarkerItem(current));
                    updateState(current);
                }


@@ 121,8 116,9 @@ namespace gui
        }
    }

    std::size_t ListViewWithLabels::getSlotsLeft()
    std::size_t ListViewWithLabels::getSlotsLeft() const
    {
        constexpr auto maxItemDisplayed{4U};
        if (itemsOnPage > maxItemDisplayed) {
            return 0;
        }

M products/BellHybrid/assets/assets_common.json => products/BellHybrid/assets/assets_common.json +1 -0
@@ 66,6 66,7 @@
        {"name": "release.tgz", "tarfile" :"image/assets/images/bell/big_information_W_G.vpi", "output": "assets/images/big_information_W_G.vpi"},
        {"name": "release.tgz", "tarfile" :"image/assets/images/bell/big_bell_battery_charging_W_G.vpi", "output": "assets/images/big_bell_battery_charging_W_G.vpi"},
        {"name": "release.tgz", "tarfile" :"image/assets/images/bell/bell_quote_W_M.vpi", "output": "assets/images/bell_quote_W_M.vpi"},
        {"name": "release.tgz", "tarfile" :"image/assets/images/bell/bell_small_tick_W_M.vpi", "output": "assets/images/bell_small_tick_W_M.vpi"},

        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/chimes/Blissful_Dream.mp3", "output": "assets/audio/chimes/Blissful_Dream.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/chimes/Gentle_Chime.mp3", "output": "assets/audio/chimes/Gentle_Chime.mp3"},