M image/assets/lang/English.json => image/assets/lang/English.json +8 -3
@@ 299,12 299,17 @@
"app_settings_display_input_language": "Input language",
"app_settings_display_locked_screen_autolock": "Autolock",
"app_settings_display_locked_screen_wallpaper": "Wallpaper",
- "app_settings_display_locked_screen_quotes": "Quotes",
- "app_settings_display_locked_screen_new_quote": "New quote",
"app_settings_display_wallpaper_logo": "Mudita logo",
"app_settings_display_wallpaper_clock": "Clock",
"app_settings_display_wallpaper_quotes": "Quotes",
- "app_settings_display_wallpaper_select_quotes": "Select Quotes",
+ "app_settings_display_wallpaper_quotes_options": "Options",
+ "app_settings_display_wallpaper_quotes_edit": "Edit quote",
+ "app_settings_display_wallpaper_quotes_delete": "Delete quote",
+ "app_settings_display_wallpaper_quotes_new": "New quote",
+ "app_settings_display_wallpaper_quotes_select": "Select quotes",
+ "app_settings_display_wallpaper_quotes_delete_confirmation": "Delete quote?",
+ "app_settings_display_wallpaper_quotes_quote": "Quote text",
+ "app_settings_display_wallpaper_quotes_author": "Quote author",
"app_settings_system": "System",
"app_settings_apps_tools": "Apps and tools",
"app_settings_apps_messages": "Messages",
A image/user/data/applications/settings/quotes.json => image/user/data/applications/settings/quotes.json +27 -0
@@ 0,0 1,27 @@
+[
+ {
+ "lang":"english",
+ "author": "Buddha",
+ "quote": "Do not dwell in the past, do not dream of the future, concentrate the mind on the present moment."
+ },
+ {
+ "lang":"english",
+ "author": "Tara Brach",
+ "quote": "The only way to live is by accepting each minute as an unrepeatable miracle."
+ },
+ {
+ "lang":"english",
+ "author": "Richar Bach",
+ "quote": "The simplest things are often the truest"
+ },
+ {
+ "lang":"english",
+ "author": "Eckhart Tolle",
+ "quote": "Always say yes to the present moment. Say yes to life."
+ },
+ {
+ "lang":"english",
+ "author": "Naomi Judd",
+ "quote": "Slow down, simplify and be kind"
+ }
+]
M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +6 -12
@@ 25,6 25,7 @@
#include "windows/QuotesMainWindow.hpp"
#include "windows/QuotesAddWindow.hpp"
#include "windows/SecurityMainWindow.hpp"
+#include "windows/QuotesOptionsWindow.hpp"
#include "windows/ChangePasscodeWindow.hpp"
#include "Dialog.hpp"
@@ 51,7 52,9 @@ namespace app
namespace settings
{
constexpr inline auto operators_on = "operators_on";
- }
+ const std::string quotesPath =
+ purefs::createPath(purefs::dir::getUserDiskPath(), "data/applications/settings/quotes.json");
+ } // namespace settings
ApplicationSettingsNew::ApplicationSettingsNew(std::string name,
std::string parent,
@@ 223,21 226,12 @@ namespace app
windowsFactory.attach(gui::window::name::wallpaper, [](Application *app, const std::string &name) {
return std::make_unique<gui::WallpaperWindow>(app);
});
- windowsFactory.attach(gui::window::name::quotes, [](Application *app, const std::string &name) {
- return std::make_unique<gui::QuotesMainWindow>(app);
- });
- windowsFactory.attach(gui::window::name::new_quote, [](Application *app, const std::string &name) {
- return std::make_unique<gui::QuotesAddWindow>(app);
+ windowsFactory.attach(gui::window::name::quotes_dialog_yes_no, [](Application *app, const std::string &name) {
+ return std::make_unique<gui::DialogYesNo>(app, name);
});
windowsFactory.attach(gui::window::name::security, [](Application *app, const std::string &name) {
return std::make_unique<gui::SecurityMainWindow>(app);
});
- windowsFactory.attach(gui::window::name::change_passcode, [](Application *app, const std::string &name) {
- return std::make_unique<gui::ChangePasscodeWindow>(app);
- });
- windowsFactory.attach(gui::window::name::dialog_confirm, [](Application *app, const std::string &name) {
- return std::make_unique<gui::DialogConfirm>(app, gui::window::name::dialog_confirm);
- });
}
void ApplicationSettingsNew::destroyUserInterface()
M module-apps/application-settings-new/ApplicationSettings.hpp => module-apps/application-settings-new/ApplicationSettings.hpp +10 -4
@@ 33,10 33,14 @@ namespace gui::window::name
inline constexpr auto nightshift = "Nightshift";
inline constexpr auto templates = "Templates";
- inline constexpr auto autolock = "Autolock";
- inline constexpr auto wallpaper = "Wallpaper";
- inline constexpr auto quotes = "Quotes";
- inline constexpr auto new_quote = "NewQuote";
+ inline constexpr auto autolock = "Autolock";
+ inline constexpr auto wallpaper = "Wallpaper";
+ inline constexpr auto quotes = "Quotes";
+ inline constexpr auto new_quote = "NewQuote";
+ inline constexpr auto edit_quote = "EditQuote";
+ inline constexpr auto options_quote = "OptionsQuote";
+ inline constexpr auto delete_quote = "DeleteQuote";
+ inline constexpr auto quotes_dialog_yes_no = "DialogYesNo";
inline constexpr auto display_and_keypad = "DisplayAndKeypad";
inline constexpr auto change_settings = "ChangeSettings";
@@ 130,6 134,8 @@ namespace app
void setStatus(bool isDisplayLightSwitchOn) override;
private:
+ void attachQuotesWindows();
+
Store::GSM::SIM selectedSim = Store::GSM::get()->selected;
std::string selectedSimNumber = {};
bsp::Board board = bsp::Board::none;
M module-apps/application-settings-new/CMakeLists.txt => module-apps/application-settings-new/CMakeLists.txt +1 -2
@@ 17,6 17,7 @@ target_sources( ${PROJECT_NAME}
ApplicationSettings.cpp
widgets/timeWidget.cpp
widgets/ChangePasscodeLockHandler.cpp
+ widgets/QuoteWidget.cpp
windows/SettingsMainWindow.cpp
windows/AddDeviceWindow.cpp
windows/AllDevicesWindow.cpp
@@ 46,7 47,6 @@ target_sources( ${PROJECT_NAME}
PUBLIC
ApplicationSettings.hpp
- widgets/ChangePasscodeLockHandler.hpp
windows/SettingsMainWindow.hpp
windows/BaseSettingsWindow.hpp
windows/FontSizeWindow.hpp
@@ 60,7 60,6 @@ target_sources( ${PROJECT_NAME}
widgets/SettingsStyle.hpp
windows/AutolockWindow.hpp
windows/WallpaperWindow.hpp
- windows/ChangePasscodeWindow.hpp
)
add_dependencies(${PROJECT_NAME} version)
A module-apps/application-settings-new/data/QuoteSwitchData.hpp => module-apps/application-settings-new/data/QuoteSwitchData.hpp +45 -0
@@ 0,0 1,45 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "application-settings-new/model/QuotesModel.hpp"
+
+#include <module-gui/gui/SwitchData.hpp>
+#include <json/json11.hpp>
+#include <utility>
+
+namespace app
+{
+ class QuotesModel;
+};
+
+namespace gui
+{
+ enum class QuoteAction
+ {
+ None,
+ Add,
+ Edit
+ };
+
+ class QuoteSwitchData : public gui::SwitchData
+ {
+ public:
+ QuoteSwitchData(QuoteAction action, app::QuoteRecord quote = {}) : action(action), quote(std::move(quote))
+ {}
+
+ [[nodiscard]] auto getQuote() const
+ {
+ return quote;
+ }
+ [[nodiscard]] auto getAction() const
+ {
+ return action;
+ }
+
+ private:
+ QuoteAction action;
+ app::QuoteRecord quote;
+ };
+} // namespace gui
A module-apps/application-settings-new/model/QuotesModel.cpp => module-apps/application-settings-new/model/QuotesModel.cpp +100 -0
@@ 0,0 1,100 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "application-settings-new/windows/QuotesMainWindow.hpp"
+#include "application-settings-new/ApplicationSettings.hpp"
+#include "application-settings-new/model/QuotesRepository.hpp"
+#include "application-settings-new/model/QuotesModel.hpp"
+#include "application-settings-new/model/QuotesModel.hpp"
+
+#include <InputEvent.hpp>
+#include <i18n/i18n.hpp>
+#include <json/json11.hpp>
+#include <Utils.hpp>
+#include <string>
+#include <utility>
+
+namespace style::quotes::list
+{
+ constexpr auto item_height = 63;
+ constexpr auto max_quotes = 100;
+} // namespace style::quotes::list
+
+namespace app
+{
+ QuotesModel::QuotesModel(app::Application *app, std::unique_ptr<QuotesRepository> repository)
+ : application(app), repository(std::move(repository))
+ {}
+
+ auto QuotesModel::requestRecordsCount() -> unsigned int
+ {
+ return internalData.size();
+ }
+
+ auto QuotesModel::getMinimalItemHeight() const -> unsigned int
+ {
+ return style::quotes::list::item_height;
+ }
+
+ void QuotesModel::requestRecords(const uint32_t offset, const uint32_t limit)
+ {
+ setupModel(offset, limit);
+ list->onProviderDataUpdate();
+ }
+
+ auto QuotesModel::getItem(gui::Order order) -> gui::ListItem *
+ {
+ auto app = application;
+ auto *item = dynamic_cast<gui::QuoteWidget *>(getRecord(order));
+
+ if (item != nullptr) {
+ item->inputCallback = [app, item](gui::Item &, const gui::InputEvent &event) {
+ if (event.isShortPress() && event.is(gui::KeyCode::KEY_LF)) {
+ app->switchWindow(
+ gui::window::name::options_quote,
+ std::make_unique<gui::QuoteSwitchData>(gui::QuoteAction::None, item->getQuoteData()));
+ }
+ return false;
+ };
+ }
+ return item;
+ }
+
+ void QuotesModel::rebuild()
+ {
+ list->clear();
+ eraseInternalData();
+ createData();
+ list->rebuildList();
+ }
+
+ void QuotesModel::createData()
+ {
+ repository->get(0, style::quotes::list::max_quotes, [this](const std::list<QuoteRecord> "es, unsigned int) {
+ auto app = application;
+ for (const auto "e : quotes) {
+ auto item = new gui::QuoteWidget(
+ quote,
+ [app](const UTF8 &text) {
+ app->getCurrentWindow()->bottomBarTemporaryMode(text, gui::BottomBar::Side::CENTER, false);
+ },
+ [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });
+
+ item->deleteByList = false;
+ internalData.push_back(item);
+ }
+ return true;
+ });
+ }
+
+ void QuotesModel::remove(const app::QuoteRecord "e)
+ {
+ repository->remove(quote);
+ }
+
+ void QuotesModel::save(const app::QuoteRecord "e)
+ {
+ repository->save(quote);
+ }
+
+} // namespace app
A module-apps/application-settings-new/model/QuotesModel.hpp => module-apps/application-settings-new/model/QuotesModel.hpp +42 -0
@@ 0,0 1,42 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "QuotesRepository.hpp"
+
+#include <purefs/filesystem_paths.hpp>
+#include <module-gui/gui/widgets/ListView.hpp>
+#include <module-apps/InternalModel.hpp>
+
+namespace gui
+{
+ class QuoteWidget;
+}
+
+namespace app
+{
+ class QuotesModel : public app::InternalModel<gui::QuoteWidget *>, public gui::ListItemProvider
+ {
+ public:
+ QuotesModel(app::Application *app, std::unique_ptr<QuotesRepository> repository);
+
+ [[nodiscard]] auto requestRecordsCount() -> unsigned int final;
+ [[nodiscard]] auto getMinimalItemHeight() const -> unsigned int final;
+
+ auto getItem(gui::Order order) -> gui::ListItem * final;
+ void requestRecords(const uint32_t offset, const uint32_t limit) final;
+
+ void rebuild();
+
+ void remove(const app::QuoteRecord "e);
+ void save(const app::QuoteRecord "e);
+
+ private:
+ void createData();
+
+ app::Application *application = nullptr;
+ std::unique_ptr<app::QuotesRepository> repository = nullptr;
+ };
+
+} // namespace app
A module-apps/application-settings-new/model/QuotesRepository.cpp => module-apps/application-settings-new/model/QuotesRepository.cpp +103 -0
@@ 0,0 1,103 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "QuotesRepository.hpp"
+
+#include <module-utils/gsl/gsl_util>
+#include <algorithm>
+#include <utility>
+
+namespace app
+{
+ QuotesJsonRepository::QuotesJsonRepository(const std::string &path) : repositoryPath{std::move(path)}
+ {}
+
+ void QuotesJsonRepository::get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback)
+ {
+ if (quotes.empty()) {
+ readQuotes(repositoryPath);
+ }
+
+ if (callback) {
+ std::uint32_t size = quotes.size();
+ auto start = std::next(quotes.begin(), std::min(offset, size));
+ auto end = std::next(quotes.begin(), std::min(offset + limit, size));
+ std::list<QuoteRecord> result{start, end};
+ callback(result, result.size());
+ }
+ }
+
+ void QuotesJsonRepository::save(const QuoteRecord "e)
+ {
+ auto toEdit = std::find_if(quotes.begin(), quotes.end(), [quote](auto &&d) { return d.id == quote.id; });
+
+ if (toEdit != quotes.end()) {
+ toEdit->quote = quote.quote;
+ toEdit->author = quote.author;
+ }
+ else if (quote.id == 0) {
+ quotes.push_back(quote);
+ }
+
+ writeQuotes(repositoryPath);
+ }
+
+ void QuotesJsonRepository::remove(const QuoteRecord "e)
+ {
+ quotes.remove_if([quote](auto &&d) { return d.id == quote.id; });
+ writeQuotes(repositoryPath);
+ }
+
+ void QuotesJsonRepository::writeQuotes(const fs::path "esFilename)
+ {
+ if (auto file = std::fopen(repositoryPath.c_str(), "w"); file != nullptr) {
+ auto _ = gsl::finally([file] { std::fclose(file); });
+ auto body = json11::Json{quotes};
+ auto text = body.dump();
+ std::fwrite(text.c_str(), 1, text.length(), file);
+ }
+ }
+
+ void QuotesJsonRepository::readQuotes(const fs::path "esFilename)
+ {
+ std::string err;
+
+ const auto fileContents = readFileToString(quotesFilename);
+ auto obj = json11::Json::parse(fileContents.c_str(), err).array_items();
+
+ if (!err.empty()) {
+ LOG_ERROR("Error while parsing quotes from file: %s error: %s ", quotesFilename.c_str(), err.c_str());
+ return;
+ }
+
+ quotes.clear();
+
+ auto id = 1;
+ std::transform(obj.begin(), obj.end(), std::back_inserter(quotes), [&id](auto item) {
+ return QuoteRecord{id++, item["quote"].string_value(), item["author"].string_value()};
+ });
+ }
+
+ auto QuotesJsonRepository::readFileToString(const fs::path &filename) -> std::string
+ {
+ constexpr auto tar_buf = 8192 * 4;
+ auto file = std::fopen(filename.c_str(), "r");
+ if (file == nullptr) {
+ return {};
+ }
+ auto _ = gsl::finally([file] { std::fclose(file); });
+ const auto length = utils::filesystem::filelength(file);
+
+ if (length >= tar_buf) {
+ LOG_ERROR("File %s length is too high!", filename.c_str());
+ return {};
+ }
+ LOG_INFO("file length: %ld", length);
+ auto buffer = std::make_unique<char[]>(length + 1);
+ std::fread(buffer.get(), 1, length, file);
+ return std::string(buffer.get());
+ }
+} // namespace app
A module-apps/application-settings-new/model/QuotesRepository.hpp => module-apps/application-settings-new/model/QuotesRepository.hpp +58 -0
@@ 0,0 1,58 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <json/json11.hpp>
+
+#include <list>
+#include <functional>
+#include <module-apps/Application.hpp>
+
+namespace app
+{
+ struct QuoteRecord
+ {
+ int id = 0;
+ std::string quote;
+ std::string author;
+
+ [[nodiscard]] auto to_json() const -> json11::Json
+ {
+ return json11::Json::object{{"quote", quote}, {"author", author}};
+ }
+ };
+
+ class QuotesRepository
+ {
+ public:
+ using OnGetCallback = std::function<bool(const std::list<QuoteRecord> &, unsigned int)>;
+
+ virtual ~QuotesRepository() noexcept = default;
+
+ virtual void get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback) = 0;
+ virtual void save(const QuoteRecord "e) = 0;
+ virtual void remove(const QuoteRecord "e) = 0;
+ };
+
+ class QuotesJsonRepository : public QuotesRepository
+ {
+ public:
+ QuotesJsonRepository(const std::string &path);
+
+ void get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback) override;
+ void save(const QuoteRecord "e) override;
+ void remove(const QuoteRecord "e) override;
+
+ private:
+ void writeQuotes(const fs::path &path);
+ void readQuotes(const fs::path &fn);
+ std::string readFileToString(const fs::path &fn);
+
+ std::list<QuoteRecord> quotes;
+ std::string repositoryPath;
+ };
+} // namespace app
A module-apps/application-settings-new/widgets/QuoteWidget.cpp => module-apps/application-settings-new/widgets/QuoteWidget.cpp +117 -0
@@ 0,0 1,117 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "QuoteWidget.hpp"
+
+#include <BottomBar.hpp>
+#include <InputEvent.hpp>
+#include <utility>
+#include <i18n/i18n.hpp>
+
+namespace style::quotes
+{
+ namespace widget
+ {
+ inline constexpr uint32_t w = style::window::default_body_width;
+ inline constexpr uint32_t h = 50;
+
+ inline constexpr uint32_t input_box_w = 55;
+ inline constexpr uint32_t input_box_h = h;
+ inline constexpr int32_t input_box_right_margin = 20;
+
+ inline constexpr uint32_t description_label_w = 280;
+ inline constexpr uint32_t description_label_h = 33;
+ inline constexpr int32_t description_label_right_margin = 40;
+
+ inline constexpr int32_t tick_image_left_margin = -64;
+ inline constexpr int32_t tick_image_right_margin = 32;
+
+ } // namespace widget
+
+} // namespace style::quotes
+
+namespace gui
+{
+
+ QuoteWidget::QuoteWidget(const app::QuoteRecord "e,
+ std::function<void(const UTF8 &)> bottomBarTemporaryMode,
+ std::function<void()> bottomBarRestoreFromTemporaryMode)
+ : bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
+ bottomBarRestoreFromTemporaryMode(std::move(bottomBarRestoreFromTemporaryMode)), quote(quote)
+ {
+
+ setMinimumSize(style::quotes::widget::w, style::quotes::widget::h);
+
+ setMargins(gui::Margins(0, style::margins::big, 0, 0));
+
+ hBox = new gui::HBox(this, 0, 0, this->getWidth(), 80);
+ hBox->setEdges(gui::RectangleEdge::None);
+ hBox->setPenFocusWidth(style::window::default_border_focus_w);
+ hBox->setPenWidth(style::window::default_border_rect_no_focus);
+
+ inputBoxLabel = new gui::Label(hBox, 0, 0, 0, 0);
+ inputBoxLabel->setMinimumSize(style::quotes::widget::input_box_w, style::quotes::widget::input_box_h);
+
+ inputBoxLabel->setMargins(gui::Margins(0, 0, style::quotes::widget::input_box_right_margin, 0));
+ inputBoxLabel->setEdges(gui::RectangleEdge::Bottom);
+ inputBoxLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
+ inputBoxLabel->setFont(style::window::font::medium);
+ inputBoxLabel->activeItem = false;
+
+ tickImage = new gui::Image(hBox, 0, 0, 0, 0);
+ tickImage->setAlignment(Alignment(gui::Alignment::Vertical::Center));
+ // Not ideal -> best solution would be to create separate widget with image inside box.
+ tickImage->setMargins(gui::Margins(
+ style::quotes::widget::tick_image_left_margin, 0, style::quotes::widget::tick_image_right_margin, 0));
+ tickImage->set("small_tick_W_M");
+ tickImage->setVisible(true);
+ tickImage->activeItem = false;
+
+ descriptionLabel = new gui::Label(hBox, 0, 0, 0, 0);
+ descriptionLabel->setMinimumSize(style::quotes::widget::description_label_w,
+ style::quotes::widget::description_label_h);
+ descriptionLabel->setMargins(gui::Margins(0, 0, style::quotes::widget::description_label_right_margin, 0));
+ descriptionLabel->setEdges(gui::RectangleEdge::None);
+ descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
+ descriptionLabel->setFont(style::window::font::medium);
+ descriptionLabel->activeItem = false;
+
+ descriptionLabel->setText(quote.quote);
+
+ focusChangedCallback = [&](gui::Item &item) {
+ if (item.focus) {
+ descriptionLabel->setFont(style::footer::font::bold);
+ setFocusItem(inputBoxLabel);
+ auto bottorBarText =
+ tickImage->visible ? utils::localize.get("common_uncheck") : utils::localize.get("common_check");
+ this->bottomBarTemporaryMode(bottorBarText);
+ }
+ else {
+ descriptionLabel->setFont(style::footer::font::medium);
+ setFocusItem(nullptr);
+ this->bottomBarRestoreFromTemporaryMode();
+ }
+ return true;
+ };
+
+ activatedCallback = [&](gui::Item &item) {
+ tickImage->setVisible(!tickImage->visible);
+ auto bottorBarText =
+ tickImage->visible ? utils::localize.get("common_uncheck") : utils::localize.get("common_check");
+ this->bottomBarTemporaryMode(bottorBarText);
+ hBox->resizeItems();
+ return true;
+ };
+
+ setEdges(gui::RectangleEdge::None);
+ }
+
+ auto QuoteWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
+ {
+ hBox->setPosition(0, 0);
+ hBox->setSize(newDim.w, newDim.h);
+
+ return true;
+ }
+
+} /* namespace gui */
A module-apps/application-settings-new/widgets/QuoteWidget.hpp => module-apps/application-settings-new/widgets/QuoteWidget.hpp +42 -0
@@ 0,0 1,42 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "application-settings-new/data/QuoteSwitchData.hpp"
+
+#include <BoxLayout.hpp>
+#include <Image.hpp>
+#include <Label.hpp>
+#include <ListItem.hpp>
+#include <json/json11.hpp>
+
+namespace gui
+{
+ class QuoteWidget : public ListItem
+ {
+ public:
+ QuoteWidget(const app::QuoteRecord "e,
+ std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
+ std::function<void()> bottomBarRestoreFromTemporaryMode = nullptr);
+
+ auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
+
+ [[nodiscard]] auto getQuoteData() const -> app::QuoteRecord
+ {
+ return quote;
+ }
+
+ private:
+ gui::HBox *hBox = nullptr;
+ gui::Label *inputBoxLabel = nullptr;
+ gui::Label *descriptionLabel = nullptr;
+ gui::Image *tickImage = nullptr;
+
+ std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
+ std::function<void()> bottomBarRestoreFromTemporaryMode = nullptr;
+
+ app::QuoteRecord quote;
+ };
+
+} /* namespace gui */
M module-apps/application-settings-new/windows/QuotesAddWindow.cpp => module-apps/application-settings-new/windows/QuotesAddWindow.cpp +165 -10
@@ 2,32 2,187 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "QuotesAddWindow.hpp"
+#include "QuotesMainWindow.hpp"
#include "application-settings-new/ApplicationSettings.hpp"
+#include "application-settings-new/data/QuoteSwitchData.hpp"
+#include "application-settings-new/model/QuotesRepository.hpp"
#include <i18n/i18n.hpp>
#include <widgets/Text.hpp>
+namespace style
+{
+ constexpr auto counterWidth = 70;
+ constexpr auto headerWidth = style::window::default_body_width - counterWidth;
+} // namespace style
+
namespace gui
{
- QuotesAddWindow::QuotesAddWindow(app::Application *app) : AppWindow(app, gui::window::name::quotes)
+ namespace
+ {
+ constexpr auto maxQuoteCharactersCount = 150U;
+ constexpr auto maxQuoteLinesCount = 4;
+ constexpr auto maxAuthorCharactersCount = 30U;
+
+ auto formatCounterText(uint32_t counter, uint32_t maxValue) -> std::string
+ {
+ std::ostringstream counterText;
+ counterText << counter << '/' << maxValue;
+ return counterText.str();
+ }
+ } // namespace
+
+ QuoteAddEditWindow::QuoteAddEditWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model)
+ : AppWindow(app, gui::window::name::quotes), quoteModel(std::move(model))
{
- setTitle(utils::localize.get("app_settings_display_locked_screen_new_quote"));
+ buildInterface();
}
- void QuotesAddWindow::buildInterface()
+ void QuoteAddEditWindow::buildInterface()
{
+ AppWindow::buildInterface();
+
bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));
bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
- auto text = new gui::Text(nullptr,
- style::window::default_left_margin,
- title->offset_h() + style::margins::big,
- style::window::default_body_width,
- style::window::default_body_height);
+ auto vBox = new VBox(this,
+ style::window::default_left_margin,
+ style::header::height + style::margins::very_big,
+ style::window::default_body_width,
+ style::window::default_body_height);
+
+ vBox->setEdges(RectangleEdge::None);
+ vBox->setPenFocusWidth(::style::window::default_border_focus_w);
+ vBox->setPenWidth(::style::window::default_border_rect_no_focus);
+
+ auto quoteHeader = new HBox(vBox, 0, 0, 0, 0);
+ quoteHeader->setMinimumSize(style::window::default_body_width, style::window::label::default_h);
+ quoteHeader->activeItem = false;
+ quoteHeader->setEdges(gui::RectangleEdge::None);
+
+ auto quoteLabel = new Label(quoteHeader, 0, 0, 0, 0);
+ quoteLabel->setMinimumSize(style::headerWidth, style::window::label::default_h);
+ quoteLabel->setEdges(RectangleEdge::None);
+ quoteLabel->setPenFocusWidth(::style::window::default_border_focus_w);
+ quoteLabel->setText(utils::localize.get("app_settings_display_wallpaper_quotes_quote"));
+ quoteLabel->setFont(::style::window::font::verysmall);
+ quoteLabel->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left});
+ quoteLabel->activeItem = false;
+
+ quoteCharCounter = new gui::Label(quoteHeader, 0, 0, 0, 0);
+ quoteCharCounter->setMinimumSize(style::counterWidth, style::window::label::default_h);
+ quoteCharCounter->setEdges(gui::RectangleEdge::None);
+ quoteCharCounter->setFont(::style::window::font::verysmall);
+ quoteCharCounter->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Right});
+
+ quoteText = new gui::Text(vBox, 0, 0, 0, 0);
+ quoteText->setAlignment(gui::Alignment{gui::Alignment::Vertical::Top});
+ quoteText->setMinimumSize(style::window::default_body_width,
+ style::window::label::default_h * maxQuoteLinesCount);
+ quoteText->setPenFocusWidth(::style::window::default_border_focus_w);
+ quoteText->setPenWidth(::style::window::default_border_rect_no_focus);
+ quoteText->setEdges(gui::RectangleEdge::None);
+ quoteText->setFont(::style::window::font::medium);
+ quoteText->setInputMode(new InputMode(
+ {InputMode::ABC, InputMode::abc, InputMode::digit},
+ [=](const UTF8 &text) { bottomBarTemporaryMode(text); },
+ [=]() { bottomBarRestoreFromTemporaryMode(); },
+ [=]() { selectSpecialCharacter(); }));
+ quoteText->setTextLimitType(gui::TextLimitType::MaxSignsCount, maxQuoteCharactersCount);
+ quoteText->setTextChangedCallback([this](Item &, const UTF8 &text) { setQuoteCharactersCount(text.length()); });
+
+ auto authorHeader = new HBox(vBox, 0, 0, 0, 0);
+ authorHeader->setMinimumSize(style::window::default_body_width, style::window::label::default_h);
+ authorHeader->setEdges(gui::RectangleEdge::None);
+ authorHeader->activeItem = false;
+
+ auto authorLabel = new Label(authorHeader, 0, 0, 0, 0);
+ authorLabel->setMinimumSize(style::headerWidth, style::window::label::default_h);
+ authorLabel->setEdges(RectangleEdge::None);
+ authorLabel->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left});
+ authorLabel->setText(utils::localize.get("app_settings_display_wallpaper_quotes_author"));
+ authorLabel->setPenFocusWidth(::style::window::default_border_focus_w);
+ authorLabel->setFont(::style::window::font::verysmall);
+ authorLabel->setPadding(gui::Padding(0, 0, 0, 0));
+ authorLabel->activeItem = false;
+
+ authorCharCounter = new gui::Label(authorHeader, 0, 0, 0, 0);
+ authorCharCounter->setMinimumSize(style::counterWidth, style::window::label::default_h);
+ authorCharCounter->setEdges(gui::RectangleEdge::None);
+ authorCharCounter->setFont(::style::window::font::verysmall);
+ authorCharCounter->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Right});
+
+ authorText = new gui::Text(vBox, 0, 0, 0, 0);
+ authorText->setMinimumSize(style::window::default_body_width, style::window::label::default_h);
+ authorText->setAlignment(gui::Alignment{gui::Alignment::Vertical::Top});
+ authorText->setPenFocusWidth(::style::window::default_border_focus_w);
+ authorText->setPenWidth(::style::window::default_border_rect_no_focus);
+ authorText->setEdges(gui::RectangleEdge::None);
+ authorText->setFont(::style::window::font::medium);
+ authorText->setInputMode(new InputMode(
+ {InputMode::ABC, InputMode::abc, InputMode::digit},
+ [=](const UTF8 &text) { bottomBarTemporaryMode(text); },
+ [=]() { bottomBarRestoreFromTemporaryMode(); },
+ [=]() { selectSpecialCharacter(); }));
+ authorText->setTextLimitType(gui::TextLimitType::MaxSignsCount, maxAuthorCharactersCount);
+ authorText->setTextChangedCallback(
+ [this](Item &, const UTF8 &text) { setAuthorCharactersCount(text.length()); });
+
+ setTitle(utils::localize.get("app_settings_display_wallpaper_quotes_new"));
+ vBox->resizeItems();
+ setFocusItem(quoteText);
+ }
- addWidget(text);
- setFocusItem(text);
+ void QuoteAddEditWindow::onBeforeShow(ShowMode mode, SwitchData *data)
+ {
+ auto *quotedata = dynamic_cast<QuoteSwitchData *>(data);
+ if (quotedata == nullptr) {
+ return;
+ }
+
+ quoteAction = quotedata->getAction();
+ quoteData = quotedata->getQuote();
+
+ if (quoteAction == QuoteAction::Edit) {
+ setTitle(utils::localize.get("app_settings_display_wallpaper_quotes_edit"));
+ quoteText->setText(quoteData.quote);
+ authorText->setText(quoteData.author);
+ }
+ else {
+ setTitle(utils::localize.get("app_settings_display_wallpaper_quotes_new"));
+ }
+
+ setAuthorCharactersCount(authorText->getText().length());
+ setQuoteCharactersCount(quoteText->getText().length());
+ }
+
+ bool QuoteAddEditWindow::onInput(const gui::InputEvent &inputEvent)
+ {
+ if (inputEvent.isShortPress() && inputEvent.is(gui::KeyCode::KEY_ENTER)) {
+ LOG_DEBUG("Save Quote: %s", quoteText->getText().c_str());
+ quoteData.quote = quoteText->getText();
+ quoteData.author = authorText->getText();
+ quoteModel->save(quoteData);
+
+ auto backToOptionWindow = 1;
+ auto backToMainWindow = 2;
+
+ auto windowToBack = quoteAction == QuoteAction::Add ? backToOptionWindow : backToMainWindow;
+ application->returnToPreviousWindow(windowToBack);
+ }
+
+ return AppWindow::onInput(inputEvent);
+ }
+
+ void QuoteAddEditWindow::setAuthorCharactersCount(std::uint32_t count)
+ {
+ authorCharCounter->setText(formatCounterText(count, maxAuthorCharactersCount));
+ }
+
+ void QuoteAddEditWindow::setQuoteCharactersCount(std::uint32_t count)
+ {
+ quoteCharCounter->setText(formatCounterText(count, maxQuoteCharactersCount));
}
} // namespace gui
M module-apps/application-settings-new/windows/QuotesAddWindow.hpp => module-apps/application-settings-new/windows/QuotesAddWindow.hpp +17 -3
@@ 4,18 4,32 @@
#pragma once
#include "BaseSettingsWindow.hpp"
+#include "QuotesMainWindow.hpp"
+#include "application-settings-new/model/QuotesModel.hpp"
namespace gui
{
class CheckBoxWithLabel;
- class QuotesAddWindow : public AppWindow
+ class QuoteAddEditWindow : public AppWindow
{
public:
- QuotesAddWindow(app::Application *app);
+ QuoteAddEditWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model);
void buildInterface() override;
private:
- std::vector<CheckBoxWithLabel *> boxes;
+ auto onInput(const InputEvent &inputEvent) -> bool override;
+ void onBeforeShow(ShowMode mode, SwitchData *data) override;
+ void setAuthorCharactersCount(uint32_t count);
+ void setQuoteCharactersCount(uint32_t count);
+
+ gui::Text *quoteText = nullptr;
+ gui::Text *authorText = nullptr;
+ gui::Label *authorCharCounter = nullptr;
+ gui::Label *quoteCharCounter = nullptr;
+
+ QuoteAction quoteAction;
+ app::QuoteRecord quoteData;
+ std::shared_ptr<app::QuotesModel> quoteModel;
};
} // namespace gui
M module-apps/application-settings-new/windows/QuotesMainWindow.cpp => module-apps/application-settings-new/windows/QuotesMainWindow.cpp +52 -74
@@ 2,7 2,8 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "QuotesMainWindow.hpp"
-
+#include "application-settings-new/model/QuotesRepository.hpp"
+#include "application-settings-new/model/QuotesModel.hpp"
#include "application-settings-new/ApplicationSettings.hpp"
#include "application-settings-new/widgets/SettingsStyle.hpp"
#include "OptionSetting.hpp"
@@ 10,17 11,54 @@
#include <InputEvent.hpp>
#include <i18n/i18n.hpp>
#include <json/json11.hpp>
-#include <purefs/filesystem_paths.hpp>
-#include <Utils.hpp>
-#include <string>
+#include <utility>
+
+namespace style::quotes
+{
+ namespace list
+ {
+ constexpr auto X = style::window::default_left_margin;
+ constexpr auto Y = style::header::height;
+ constexpr auto Width = style::listview::body_width_with_scroll;
+ constexpr auto Height = style::window_height - Y - style::footer::height;
+ } // namespace list
+
+ inline constexpr auto cross_x = 48;
+ inline constexpr auto cross_y = 55;
+ inline constexpr auto arrow_x = 30;
+ inline constexpr auto arrow_y = 62;
+
+} // namespace style::quotes
namespace gui
{
- QuotesMainWindow::QuotesMainWindow(app::Application *app) : BaseSettingsWindow(app, gui::window::name::quotes)
+ QuotesMainWindow::QuotesMainWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model)
+ : AppWindow(app, gui::window::name::quotes), quotesModel(std::move(model))
{
- const auto quotesPath = purefs::dir::getCurrentOSPath() / "data/applications/settings/quotes.json";
- setTitle(utils::localize.get("app_settings_display_locked_screen_quotes"));
- readQuotes(quotesPath);
+ buildInterface();
+ }
+
+ void QuotesMainWindow::buildInterface()
+ {
+ AppWindow::buildInterface();
+
+ setTitle(utils::localize.get("app_settings_display_wallpaper_quotes"));
+
+ bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::check));
+ bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
+ bottomBar->setText(BottomBar::Side::LEFT, utils::localize.get(style::strings::common::options));
+
+ new gui::Image(this, style::quotes::arrow_x, style::quotes::arrow_y, 0, 0, "arrow_left");
+ new gui::Image(this, style::quotes::cross_x, style::quotes::cross_y, 0, 0, "cross");
+
+ list = new gui::ListView(this,
+ style::quotes::list::X,
+ style::quotes::list::Y,
+ style::quotes::list::Width,
+ style::quotes::list::Height,
+ quotesModel);
+
+ setFocusItem(list);
}
auto QuotesMainWindow::onInput(const InputEvent &inputEvent) -> bool
@@ 29,10 67,12 @@ namespace gui
if (AppWindow::onInput(inputEvent)) {
return true;
}
- if (inputEvent.state == InputEvent::State::keyReleasedShort) {
+
+ if (inputEvent.isShortPress()) {
switch (inputEvent.keyCode) {
case gui::KeyCode::KEY_LEFT:
- application->switchWindow(gui::window::name::new_quote, nullptr);
+ application->switchWindow(gui::window::name::new_quote,
+ std::make_unique<QuoteSwitchData>(QuoteAction::Add));
return true;
default:
break;
@@ 41,70 81,8 @@ namespace gui
return false;
}
- void QuotesMainWindow::readQuotes(fs::path fn)
- {
- std::string err;
-
- const auto fileContents = readFileToString(fn);
- auto obj = json11::Json::parse(fileContents.c_str(), err).array_items();
-
- if (!err.empty()) {
- LOG_ERROR("Error while parsing quotes: %s", err.c_str());
- }
-
- std::transform(obj.begin(), obj.end(), std::back_inserter(quotes), [](auto item) {
- return std::pair{item["quote"].string_value(), false};
- });
- }
-
- auto QuotesMainWindow::buildOptionsList() -> std::list<gui::Option>
+ void QuotesMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)
{
- std::list<gui::Option> optionsList;
-
- for (auto "e : quotes) {
- optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
- utils::translateI18(quote.first),
- ["e, this](gui::Item &item) {
- switchHandler(quote.second);
- return true;
- },
- [=](gui::Item &item) {
- if (item.focus) {
- this->setBottomBarText(utils::translateI18(style::strings::common::Switch),
- BottomBar::Side::CENTER);
- }
- return true;
- },
- this,
- quote.second ? gui::option::SettingRightItem::Checked : gui::option::SettingRightItem::Disabled));
- }
-
- return optionsList;
+ quotesModel->rebuild();
}
-
- void QuotesMainWindow::switchHandler(bool &optionSwitch)
- {
- optionSwitch = !optionSwitch;
- rebuildOptionList();
- }
-
- std::string QuotesMainWindow::readFileToString(const fs::path &fn)
- {
- constexpr auto tar_buf = 8192 * 4;
- auto file = std::fopen(fn.c_str(), "r");
- if (!file) {
- return {};
- }
- const auto length = utils::filesystem::filelength(file);
- if (length >= tar_buf) {
- LOG_ERROR("File %s length is too high!", fn.c_str());
- std::fclose(file);
- return {};
- }
- auto buffer = std::make_unique<char[]>(length + 1);
- std::fread(buffer.get(), 1, length, file);
- std::fclose(file);
- return std::string(buffer.get());
- }
-
} // namespace gui
M module-apps/application-settings-new/windows/QuotesMainWindow.hpp => module-apps/application-settings-new/windows/QuotesMainWindow.hpp +14 -13
@@ 4,27 4,28 @@
#pragma once
#include "BaseSettingsWindow.hpp"
+#include "application-settings-new/widgets/QuoteWidget.hpp"
+#include "application-settings-new/model/QuotesModel.hpp"
+#include <purefs/filesystem_paths.hpp>
+#include <module-gui/gui/widgets/ListView.hpp>
+#include <module-apps/InternalModel.hpp>
+#include <utility>
namespace gui
{
- class CheckBoxWithLabel;
-
- class QuotesMainWindow : public BaseSettingsWindow
+ class QuotesMainWindow : public AppWindow
{
public:
- QuotesMainWindow(app::Application *app);
-
- auto onInput(const InputEvent &inputEvent) -> bool override;
-
- protected:
- auto buildOptionsList() -> std::list<Option> override;
+ QuotesMainWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model);
private:
- void readQuotes(fs::path fn);
- void switchHandler(bool &optionSwitch);
- [[nodiscard]] static std::string readFileToString(const fs::path &fn);
+ void buildInterface() override;
+ auto onInput(const InputEvent &inputEvent) -> bool override;
+ void onBeforeShow(ShowMode mode, SwitchData *data) override;
- std::list<std::pair<std::string, bool>> quotes;
+ std::shared_ptr<app::QuotesModel> quotesModel = nullptr;
+ gui::ListView *list = nullptr;
};
+
} // namespace gui
A module-apps/application-settings-new/windows/QuotesOptionsWindow.cpp => module-apps/application-settings-new/windows/QuotesOptionsWindow.cpp +83 -0
@@ 0,0 1,83 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "QuotesOptionsWindow.hpp"
+#include "OptionSetting.hpp"
+#include "DialogMetadataMessage.hpp"
+#include "QuotesMainWindow.hpp"
+#include "application-settings-new/model/QuotesRepository.hpp"
+#include "application-settings-new/ApplicationSettings.hpp"
+#include "application-settings-new/widgets/QuoteWidget.hpp"
+
+#include <i18n/i18n.hpp>
+#include <widgets/Text.hpp>
+
+namespace gui
+{
+
+ QuotesOptionsWindow::QuotesOptionsWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model)
+ : BaseSettingsWindow(app, gui::window::name::quotes), quotesModel(std::move(model))
+ {
+ setTitle(utils::localize.get("app_settings_display_wallpaper_quotes_options"));
+ }
+
+ auto QuotesOptionsWindow::buildOptionsList() -> std::list<gui::Option>
+ {
+ std::list<gui::Option> optionsList;
+
+ optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
+ utils::translateI18("app_settings_display_wallpaper_quotes_edit"),
+ [=](gui::Item &item) {
+ application->switchWindow(gui::window::name::new_quote,
+ std::make_unique<QuoteSwitchData>(QuoteAction::Edit, quote));
+ return true;
+ },
+ [=](gui::Item &item) {
+ if (item.focus) {
+ this->setBottomBarText(utils::translateI18(style::strings::common::select),
+ BottomBar::Side::CENTER);
+ }
+ return true;
+ },
+ this));
+
+ optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
+ utils::translateI18("app_settings_display_wallpaper_quotes_delete"),
+ [=](gui::Item &item) {
+ gui::DialogMetadata meta;
+ meta.text = utils::localize.get("app_settings_display_wallpaper_quotes_delete_confirmation");
+ meta.title = quote.quote;
+ meta.icon = "phonebook_contact_delete_trashcan";
+ meta.action = [this]() {
+ auto backToQuotesMainWindow = 2;
+ quotesModel->remove(quote);
+ application->returnToPreviousWindow(backToQuotesMainWindow);
+ return true;
+ };
+
+ application->switchWindow(gui::window::name::quotes_dialog_yes_no,
+ std::make_unique<gui::DialogMetadataMessage>(meta));
+ return true;
+ },
+ [=](gui::Item &item) {
+ if (item.focus) {
+ this->setBottomBarText(utils::translateI18(style::strings::common::select),
+ BottomBar::Side::CENTER);
+ }
+ return true;
+ },
+ this));
+
+ return optionsList;
+ } // namespace gui
+
+ void QuotesOptionsWindow::onBeforeShow(ShowMode mode, SwitchData *data)
+ {
+ auto *quoteSwitchData = dynamic_cast<QuoteSwitchData *>(data);
+ if (quoteSwitchData != nullptr) {
+ quote = quoteSwitchData->getQuote();
+ }
+
+ BaseSettingsWindow::onBeforeShow(mode, data);
+ }
+} // namespace gui
A module-apps/application-settings-new/windows/QuotesOptionsWindow.hpp => module-apps/application-settings-new/windows/QuotesOptionsWindow.hpp +24 -0
@@ 0,0 1,24 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "BaseSettingsWindow.hpp"
+#include "QuotesMainWindow.hpp"
+#include "application-settings-new/widgets/QuoteWidget.hpp"
+
+namespace gui
+{
+ class QuotesOptionsWindow : public BaseSettingsWindow
+ {
+ public:
+ QuotesOptionsWindow(app::Application *app, std::shared_ptr<app::QuotesModel> model);
+
+ private:
+ std::list<Option> buildOptionsList() override;
+ void onBeforeShow(ShowMode mode, SwitchData *data) override;
+
+ std::shared_ptr<app::QuotesModel> quotesModel;
+ app::QuoteRecord quote;
+ };
+} // namespace gui
M module-gui/gui/widgets/Style.hpp => module-gui/gui/widgets/Style.hpp +1 -0
@@ 138,6 138,7 @@ namespace style
inline constexpr auto set = "common_set";
inline constexpr auto yes = "common_yes";
inline constexpr auto no = "common_no";
+ inline constexpr auto check = "common_check";
inline constexpr auto Switch = "common_switch";
inline constexpr auto options = "common_options";
inline constexpr auto information = "common_information";