M module-apps/application-bell-settings/models/TimeUnitsModel.cpp => module-apps/application-bell-settings/models/TimeUnitsModel.cpp +5 -5
@@ 3,7 3,7 @@
#include "TimeUnitsModel.hpp"
-#include <apps-common/widgets/TimeSetSpinner.hpp>
+#include <apps-common/widgets/TimeSetFmtSpinner.hpp>
#include <gui/widgets/ListViewEngine.hpp>
#include <gui/widgets/Style.hpp>
#include <gui/widgets/Text.hpp>
@@ 57,8 57,8 @@ namespace app::bell_settings
{
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
struct std::tm *newTime = std::localtime(&now);
- newTime->tm_hour = timeSetWidget->timeSetSpinner->getHour();
- newTime->tm_min = timeSetWidget->timeSetSpinner->getMinute();
+ newTime->tm_hour = timeSetWidget->timeSetFmtSpinner->getHour();
+ newTime->tm_min = timeSetWidget->timeSetFmtSpinner->getMinute();
LOG_INFO("Setting new time: %d:%d", newTime->tm_hour, newTime->tm_min);
sendRtcUpdateTimeMessage(std::mktime(newTime));
}
@@ 66,8 66,8 @@ namespace app::bell_settings
void TimeUnitsModel::loadData()
{
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
- timeSetWidget->timeSetSpinner->setHour(std::localtime(&now)->tm_hour);
- timeSetWidget->timeSetSpinner->setMinute(std::localtime(&now)->tm_min);
+ timeSetWidget->timeSetFmtSpinner->setHour(std::localtime(&now)->tm_hour);
+ timeSetWidget->timeSetFmtSpinner->setMinute(std::localtime(&now)->tm_min);
}
auto TimeUnitsModel::requestRecords(uint32_t offset, uint32_t limit) -> void
M module-apps/application-bell-settings/widgets/TimeSetSpinnerListItem.cpp => module-apps/application-bell-settings/widgets/TimeSetSpinnerListItem.cpp +13 -7
@@ 4,7 4,8 @@
#include "BellSettingsStyle.hpp"
#include "TimeSetSpinnerListItem.hpp"
-#include <widgets/TimeSetSpinner.hpp>
+#include <gui/input/InputEvent.hpp>
+#include <widgets/TimeSetFmtSpinner.hpp>
namespace gui
{
@@ 13,16 14,21 @@ namespace gui
: SideListItem(std::move(description))
{
setMinimumSize(style::sidelistview::list_item::w, style::sidelistview::list_item::h);
-
- timeSetSpinner = new TimeSetSpinner(body, 0, 0, 0, 0);
- timeSetSpinner->setMinimumSize(gui::bell_settings_style::time_set_spinner_list_item::w,
- gui::bell_settings_style::time_set_spinner_list_item::h);
- body->setFocusItem(timeSetSpinner);
+ timeSetFmtSpinner = new TimeSetFmtSpinner(body);
+ timeSetFmtSpinner->setMinimumSize(gui::bell_settings_style::time_set_spinner_list_item::w,
+ gui::bell_settings_style::time_set_spinner_list_item::h);
+ setFocusItem(body);
dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
body->setArea({0, 0, newDim.w, newDim.h});
return true;
};
- inputCallback = [&](gui::Item &item, const gui::InputEvent &event) { return body->onInput(event); };
+
+ focusChangedCallback = [&](Item &item) {
+ setFocusItem(focus ? body : nullptr);
+ return true;
+ };
+
+ inputCallback = [&](Item &, const InputEvent &inputEvent) -> bool { return body->onInput(inputEvent); };
}
} /* namespace gui */
M module-apps/application-bell-settings/widgets/TimeSetSpinnerListItem.hpp => module-apps/application-bell-settings/widgets/TimeSetSpinnerListItem.hpp +2 -2
@@ 9,12 9,12 @@
namespace gui
{
- class TimeSetSpinner;
+ class TimeSetFmtSpinner;
class TimeSetSpinnerListItem : public SideListItem
{
public:
- TimeSetSpinner *timeSetSpinner = nullptr;
+ TimeSetFmtSpinner *timeSetFmtSpinner = nullptr;
TimeSetSpinnerListItem(gui::Length x, gui::Length y, gui::Length w, gui::Length h, std::string description);
};
M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +3 -0
@@ 38,12 38,15 @@ target_sources(apps-common
widgets/TextWithIconsWidget.cpp
widgets/TimeSetSpinner.cpp
widgets/AlarmSetSpinner.cpp
+ widgets/TimeSetFmtSpinner.cpp
widgets/TimeWidget.cpp
widgets/WidgetsUtils.cpp
windows/AppWindow.cpp
windows/BrightnessWindow.cpp
windows/Dialog.cpp
windows/OptionWindow.cpp
+ PUBLIC
+ widgets/TimeSetFmtSpinner.hpp
)
add_subdirectory(popups)
A module-apps/apps-common/widgets/TimeSetFmtSpinner.cpp => module-apps/apps-common/widgets/TimeSetFmtSpinner.cpp +195 -0
@@ 0,0 1,195 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TimeSetFmtSpinner.hpp"
+
+#include "TimeSetSpinner.hpp"
+
+#include <date/date.h>
+#include <gui/core/FontManager.hpp>
+#include <gui/core/RawFont.hpp>
+#include <gui/widgets/Spinner.hpp>
+#include <gui/widgets/TextSpinner.hpp>
+
+namespace
+{
+ constexpr auto fmtSpinnerAMPos = 0U;
+ constexpr auto fmtSpinnerPMPos = 1U;
+} // namespace
+
+namespace gui
+{
+
+ TimeSetFmtSpinner::TimeSetFmtSpinner(
+ Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, utils::time::Locale::TimeFormat timeFormat)
+ : HBox{parent, x, y, w, h}
+ {
+ using namespace utils;
+
+ setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
+ setEdges(RectangleEdge::None);
+
+ timeSetSpinner = new TimeSetSpinner(this, 0, 0, 0, 0);
+ timeSetSpinner->setFont(fontName);
+ timeSetSpinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
+ timeSetSpinner->setMargins(Margins(0, 0, 0, 0));
+
+ auto minSize = getMinimumFmtSize();
+ auto textRange = TextSpinner::TextRange{time::Locale::getAM(), time::Locale::getPM()};
+ fmt = new TextSpinner(textRange, Boundaries::Continuous);
+ fmt->setMinimumSize(minSize.first, minSize.second);
+ fmt->setFont(fontName);
+ fmt->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
+ fmt->setEdges(RectangleEdge::None);
+ fmt->setVisible(false);
+ fmt->setPenFocusWidth(style::time_set_spinner::focus::size);
+ addWidget(fmt);
+ fmt->setEdges(RectangleEdge::Bottom);
+
+ focusChangedCallback = [&](Item &) {
+ setFocusItem(focus ? timeSetSpinner : nullptr);
+ return true;
+ };
+
+ setTimeFormat(timeFormat);
+ }
+
+ auto TimeSetFmtSpinner::setTimeFormat(utils::time::Locale::TimeFormat newFormat) noexcept -> void
+ {
+ using namespace utils;
+ timeFormat = newFormat;
+ switch (timeFormat) {
+ case utils::time::Locale::TimeFormat::FormatTime12H: {
+ fmt->setVisible(true);
+ timeSetSpinner->setHourMax(time::Locale::max_hour_12H_mode);
+ timeSetSpinner->setHourMin(time::Locale::min_hour_12H_mode);
+
+ auto hours = std::chrono::hours(timeSetSpinner->getHour());
+ timeSetSpinner->setHour(date::make12(hours).count());
+ if (date::is_pm(hours)) {
+ fmt->setCurrentPosition(fmtSpinnerPMPos);
+ }
+ else {
+ fmt->setCurrentPosition(fmtSpinnerAMPos);
+ }
+ } break;
+ case utils::time::Locale::TimeFormat::FormatTime24H: {
+ fmt->setVisible(false);
+ timeSetSpinner->setHourMax(time::Locale::max_hour_24H_mode);
+ timeSetSpinner->setHourMin(time::Locale::min_hour_24H_mode);
+
+ auto hours = std::chrono::hours(timeSetSpinner->getHour());
+ timeSetSpinner->setHour(date::make24(hours, isPm(fmt->getCurrentText())).count());
+ } break;
+ default:
+ break;
+ }
+
+ resizeItems();
+
+ // If we make 12->24 switch while focused on fmt then change focus to hour
+ if (focusItem == fmt) {
+ setFocusItem(timeSetSpinner);
+ }
+ }
+
+ auto TimeSetFmtSpinner::setMinute(int value) noexcept -> void
+ {
+ timeSetSpinner->setMinute(value);
+ }
+
+ auto TimeSetFmtSpinner::setEditMode(EditMode newEditMode) noexcept -> void
+ {
+ editMode = newEditMode;
+ if (editMode == EditMode::Edit) {
+ setFocusItem(timeSetSpinner);
+ }
+ else {
+ setFocusItem(nullptr);
+ }
+ }
+ auto TimeSetFmtSpinner::getHour() const noexcept -> int
+ {
+ return timeSetSpinner->getHour();
+ }
+ auto TimeSetFmtSpinner::getMinute() const noexcept -> int
+ {
+ return timeSetSpinner->getMinute();
+ }
+
+ auto TimeSetFmtSpinner::getFontHeight() const noexcept -> uint16_t
+ {
+ const auto font = FontManager::getInstance().getFont(fontName);
+ return font->info.line_height;
+ }
+ auto TimeSetFmtSpinner::getMinimumFmtSize() const noexcept -> std::pair<Length, Length>
+ {
+ constexpr auto spacer = 5U; // space between two chars
+ const auto font = FontManager::getInstance().getFont(fontName);
+ return {font->getPixelWidth(utils::time::Locale::getAM()) + spacer, font->info.line_height};
+ }
+ auto TimeSetFmtSpinner::setHour(int value) noexcept -> void
+ {
+ timeSetSpinner->setHour(value);
+ }
+ auto TimeSetFmtSpinner::setFont(std::string newFontName) noexcept -> void
+ {
+ fontName = std::move(newFontName);
+ auto fontHeight = getFontHeight();
+ auto minFmtSize = getMinimumFmtSize();
+
+ timeSetSpinner->setFont(fontName);
+ fmt->setFont(fontName);
+ fmt->setMinimumSize(minFmtSize.first, minFmtSize.second);
+ fmt->setText(fmt->getText());
+
+ setMinimumSize(timeSetSpinner->getMinimumSize().first + minFmtSize.first, fontHeight);
+ resizeItems();
+ }
+
+ auto TimeSetFmtSpinner::onInput(const InputEvent &inputEvent) -> bool
+ {
+ // Ignore input event when not in edit mode
+ if (editMode != EditMode::Edit) {
+ return false;
+ }
+
+ if (auto ret = this->focusItem->onInput(inputEvent)) {
+ return ret;
+ }
+
+ if (inputEvent.isShortRelease()) {
+ switch (inputEvent.getKeyCode()) {
+ case KeyCode::KEY_ENTER:
+ return handleEnterKey();
+ case KeyCode::KEY_RF:
+ return handleRightFunctionKey();
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ auto TimeSetFmtSpinner::handleEnterKey() -> bool
+ {
+ if (focusItem == timeSetSpinner) {
+ setFocusItem(fmt);
+ return true;
+ }
+ return false;
+ }
+ auto TimeSetFmtSpinner::handleRightFunctionKey() -> bool
+ {
+ if (focusItem == fmt) {
+ setFocusItem(timeSetSpinner);
+ return true;
+ }
+
+ return false;
+ }
+ auto TimeSetFmtSpinner::isPm(const std::string_view str) const noexcept -> bool
+ {
+ return str == utils::time::Locale::getPM().c_str();
+ }
+} // namespace gui<
\ No newline at end of file
A module-apps/apps-common/widgets/TimeSetFmtSpinner.hpp => module-apps/apps-common/widgets/TimeSetFmtSpinner.hpp +67 -0
@@ 0,0 1,67 @@
+// 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 <gui/widgets/BoxLayout.hpp>
+#include <gui/widgets/TextConstants.hpp>
+#include <time/time_locale.hpp>
+
+#include <string>
+
+namespace gui
+{
+ class TextSpinner;
+ class TimeSetSpinner;
+
+ /// Time set spinner widget class with option for dynamic switching between 24/12-hour format
+ /// Automatically recalculates hour upon switching format
+ /// Can be used as a basic time displaying widget when @ref EditMode set to 'Browse'
+ /// @ref KeyCode::KEY_ENTER switches hour -> minute -> time format
+ /// @ref KeyCode::KEY_RF switches back
+ /// Two time formats are supported:
+ /// utils::time::Locale::TimeFormat::FormatTime12H
+ /// utils::time::Locale::TimeFormat::FormatTime24H
+ class TimeSetFmtSpinner : public HBox
+ {
+ public:
+ explicit TimeSetFmtSpinner(
+ Item *parent = nullptr,
+ uint32_t x = 0U,
+ uint32_t y = 0U,
+ uint32_t w = 0U,
+ uint32_t h = 0U,
+ utils::time::Locale::TimeFormat timeFormat = utils::time::Locale::TimeFormat::FormatTime12H);
+
+ /// Switches currently displayed time format
+ auto setTimeFormat(utils::time::Locale::TimeFormat fmt) noexcept -> void;
+ auto setHour(int value) noexcept -> void;
+ auto setMinute(int value) noexcept -> void;
+ auto setEditMode(EditMode newEditMode) noexcept -> void;
+ auto setFont(std::string newFontName) noexcept -> void;
+ [[nodiscard]] auto getHour() const noexcept -> int;
+ [[nodiscard]] auto getMinute() const noexcept -> int;
+
+ private:
+ enum class TraverseDir : bool
+ {
+ Left,
+ Right
+ };
+
+ [[nodiscard]] auto getFontHeight() const noexcept -> uint16_t;
+ [[nodiscard]] auto getMinimumFmtSize() const noexcept -> std::pair<Length, Length>;
+
+ [[nodiscard]] auto isPm(std::string_view str) const noexcept -> bool;
+ auto onInput(const InputEvent &inputEvent) -> bool override;
+ auto handleEnterKey() -> bool;
+ auto handleRightFunctionKey() -> bool;
+
+ TimeSetSpinner *timeSetSpinner = nullptr;
+ TextSpinner *fmt = nullptr;
+ EditMode editMode = EditMode::Edit;
+ std::string fontName = style::window::font::supersizemelight;
+ utils::time::Locale::TimeFormat timeFormat = utils::time::Locale::TimeFormat::FormatTime12H;
+ };
+
+} // namespace gui
M module-apps/apps-common/widgets/TimeSetSpinner.cpp => module-apps/apps-common/widgets/TimeSetSpinner.cpp +34 -5
@@ 33,6 33,7 @@ namespace gui
hour->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
hour->setFixedFieldWidth(noOfDigits);
hour->setEdges(RectangleEdge::None);
+ hour->setPenFocusWidth(style::time_set_spinner::focus::size);
hour->setCurrentValue(0);
addWidget(hour);
@@ 48,6 49,7 @@ namespace gui
minute = new Spinner(minuteMin, minuteMax, minuteStep, Boundaries::Continuous);
minute->setMinimumSize(doubleCharWidth, fontHeight);
minute->setFont(fontName);
+ minute->setPenFocusWidth(style::time_set_spinner::focus::size);
minute->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
minute->setFixedFieldWidth(noOfDigits);
@@ 57,8 59,15 @@ namespace gui
resizeItems();
+ focusChangedCallback = [&](Item &) {
+ if (editMode != EditMode::Edit) {
+ return false;
+ }
+ setFocusItem(focus ? lastFocus : nullptr);
+ return true;
+ };
if (editMode == EditMode::Edit) {
- setFocusItem(hour);
+ updateFocus(hour);
}
}
@@ 113,7 122,7 @@ namespace gui
auto TimeSetSpinner::handleEnterKey() -> bool
{
if (focusItem == hour) {
- setFocusItem(minute);
+ updateFocus(minute);
return true;
}
return false;
@@ 122,7 131,7 @@ namespace gui
auto TimeSetSpinner::handleRightFunctionKey() -> bool
{
if (focusItem == minute) {
- setFocusItem(hour);
+ updateFocus(hour);
return true;
}
return false;
@@ 157,7 166,7 @@ namespace gui
minute->setMinimumSize(doubleCharWidth, fontHeight);
minute->setText(minute->getText());
- setMinimumSize(2 * doubleCharWidth + getColonWidth(), fontHeight);
+ setMinimumSize(noOfDigits * doubleCharWidth + getColonWidth(), fontHeight);
resizeItems();
}
@@ 165,7 174,7 @@ namespace gui
{
this->editMode = editMode;
if (editMode == EditMode::Edit) {
- setFocusItem(hour);
+ updateFocus(hour);
}
else {
setFocusItem(nullptr);
@@ 181,4 190,24 @@ namespace gui
{
return minute->getCurrentValue();
}
+ auto TimeSetSpinner::setHourMax(std::uint32_t newMax) noexcept -> void
+ {
+ hour->setMaxValue(newMax);
+ }
+ auto TimeSetSpinner::setHourMin(std::uint32_t newMin) noexcept -> void
+ {
+ hour->setMinValue(newMin);
+ }
+ void TimeSetSpinner::updateFocus(Item *newFocus)
+ {
+ setFocusItem(newFocus);
+ lastFocus = newFocus;
+ }
+ auto TimeSetSpinner::getMinimumSize() const noexcept -> std::pair<std::uint32_t, std::uint32_t>
+ {
+ constexpr auto spacer = 1U;
+ auto fontHeight = getFontHeight();
+ auto doubleCharWidth = (getWidestDigitWidth() * noOfDigits) + (spacer * noOfDigits);
+ return {noOfDigits * doubleCharWidth + getColonWidth(), fontHeight};
+ }
} /* namespace gui */
M module-apps/apps-common/widgets/TimeSetSpinner.hpp => module-apps/apps-common/widgets/TimeSetSpinner.hpp +14 -0
@@ 10,6 10,14 @@
#include <string>
+namespace style::time_set_spinner
+{
+ namespace focus
+ {
+ inline constexpr auto size = 3U;
+ } // namespace focus
+} // namespace style::time_set_spinner
+
namespace gui
{
class TimeSetSpinner : public HBox
@@ 21,16 29,22 @@ namespace gui
auto setMinute(int value) noexcept -> void;
auto setFont(std::string newFontName) noexcept -> void;
auto setEditMode(EditMode editMode) noexcept -> void;
+ auto setHourMax(std::uint32_t newMax) noexcept -> void;
+ auto setHourMin(std::uint32_t newMin) noexcept -> void;
[[nodiscard]] auto getHour() const noexcept -> int;
[[nodiscard]] auto getMinute() const noexcept -> int;
+ auto getMinimumSize() const noexcept -> std::pair<std::uint32_t, std::uint32_t>;
+
private:
Spinner *hour = nullptr;
Label *colon = nullptr;
Spinner *minute = nullptr;
EditMode editMode = EditMode::Edit;
+ Item *lastFocus = nullptr;
std::string fontName = style::window::font::supersizemelight;
+ void updateFocus(Item *newFocus);
auto handleEnterKey() -> bool;
auto handleRightFunctionKey() -> bool;
auto onInput(const InputEvent &inputEvent) -> bool override;
M module-gui/gui/widgets/Spinner.cpp => module-gui/gui/widgets/Spinner.cpp +9 -1
@@ 87,7 87,7 @@ namespace gui
bool Spinner::onFocus(bool state)
{
if (focus) {
- setEdges(RectangleEdge::Top | RectangleEdge::Bottom);
+ setEdges(RectangleEdge::Bottom);
}
else {
setEdges(RectangleEdge::None);
@@ 105,5 105,13 @@ namespace gui
outStream << currentValue;
setText(outStream.str());
}
+ void Spinner::setMinValue(int newMinValue)
+ {
+ minValue = newMinValue;
+ }
+ void Spinner::setMaxValue(int newMaxValue)
+ {
+ maxValue = newMaxValue;
+ }
} // namespace gui
M module-gui/gui/widgets/Spinner.hpp => module-gui/gui/widgets/Spinner.hpp +3 -0
@@ 15,6 15,9 @@ namespace gui
void setCurrentValue(int newCurrent);
void setFixedFieldWidth(unsigned char newFixedFieldWidth);
+ void setMinValue(int newMinValue);
+ void setMaxValue(int newMaxValue);
+
[[nodiscard]] int getCurrentValue() const noexcept;
[[nodiscard]] unsigned char getFixedFieldWidth() const noexcept;
M module-gui/gui/widgets/TextSpinner.cpp => module-gui/gui/widgets/TextSpinner.cpp +1 -1
@@ 82,7 82,7 @@ namespace gui
bool TextSpinner::onFocus(bool state)
{
if (focus) {
- setEdges(RectangleEdge::Top | RectangleEdge::Bottom);
+ setEdges(RectangleEdge::Bottom);
}
else {
setEdges(RectangleEdge::None);
M module-utils/time/time/time_locale.hpp => module-utils/time/time/time_locale.hpp +6 -3
@@ 65,9 65,12 @@ namespace utils
public:
static constexpr int max_hour_24H_mode = 23;
static constexpr int max_hour_12H_mode = 12;
- static constexpr int max_minutes = 59;
- static constexpr int max_years = 2038;
- static constexpr int min_years = 1970;
+ static constexpr int min_hour_24H_mode = 0;
+ static constexpr int min_hour_12H_mode = 1;
+
+ static constexpr int max_minutes = 59;
+ static constexpr int max_years = 2038;
+ static constexpr int min_years = 1970;
enum Day
{