mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-04-19 22:49:06 -04:00
Updated AlarmOptionsItem to use UTF8Spinner. Created specialized widgets to cover options sets. Updated GenericSpinner to handle Pure navigation and content swap. Updated Alarm RRule code to work with Custom Days selection and new widgets. Added Tests. General GUI stylistic fixes. Increased app Alarm Clock and service Time stack sizes.
232 lines
8.1 KiB
C++
232 lines
8.1 KiB
C++
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "DateWidget.hpp"
|
|
#include "DateAndTimeStyle.hpp"
|
|
#include <ListView.hpp>
|
|
#include <Style.hpp>
|
|
#include <time/time_conversion.hpp>
|
|
#include <time/time_date_validation.hpp>
|
|
#include <module-gui/gui/input/InputEvent.hpp>
|
|
#include <time/dateCommon.hpp>
|
|
|
|
namespace date_and_time = style::window::date_and_time;
|
|
|
|
namespace gui
|
|
{
|
|
DateWidget::DateWidget(Item *parent) : VBox(parent)
|
|
{
|
|
setMinimumSize(style::window::default_body_width, date_and_time::height);
|
|
setEdges(RectangleEdge::None);
|
|
|
|
buildInterface();
|
|
applyCallbacks();
|
|
}
|
|
|
|
void DateWidget::buildInterface()
|
|
{
|
|
labelsHBox = new HBox(this, 0, 0, 0, 0);
|
|
labelsHBox->setMinimumSize(style::window::default_body_width, date_and_time::topMargin);
|
|
labelsHBox->setMargins(Margins(0, 0, 0, date_and_time::topMargin / 4));
|
|
labelsHBox->setEdges(RectangleEdge::None);
|
|
labelsHBox->activeItem = false;
|
|
|
|
dayLabel = new Label(labelsHBox, 0, 0, 0, 0);
|
|
applyLabelSpecificProperties(dayLabel);
|
|
dayLabel->setText(utils::translate("app_settings_title_day"));
|
|
|
|
monthLabel = new Label(labelsHBox, 0, 0, 0, 0);
|
|
applyLabelSpecificProperties(monthLabel);
|
|
monthLabel->setMargins(Margins(date_and_time::separator, 0, 0, 0));
|
|
monthLabel->setText(utils::translate("app_settings_title_month"));
|
|
|
|
yearLabel = new Label(labelsHBox, 0, 0, 0, 0);
|
|
applyLabelSpecificProperties(yearLabel);
|
|
yearLabel->setMargins(Margins(date_and_time::separator, 0, 0, 0));
|
|
yearLabel->setText(utils::translate("app_settings_title_year"));
|
|
|
|
dateHBox = new HBox(this, 0, 0, 0, 0);
|
|
dateHBox->setMinimumSize(style::window::default_body_width, date_and_time::hBox_h);
|
|
dateHBox->setEdges(RectangleEdge::None);
|
|
dateHBox->activeItem = false;
|
|
|
|
dayInput = new Label(dateHBox, 0, 0, 0, 0);
|
|
|
|
monthInput = new Label(dateHBox, 0, 0, 0, 0);
|
|
monthInput->setMargins(Margins(date_and_time::separator, 0, 0, 0));
|
|
|
|
yearInput = new Label(dateHBox, 0, 0, 0, 0);
|
|
yearInput->setMargins(Margins(date_and_time::separator, 0, 0, 0));
|
|
|
|
applyItemSpecificProperties(dayInput);
|
|
applyItemSpecificProperties(monthInput);
|
|
applyItemSpecificProperties(yearInput);
|
|
|
|
resizeItems();
|
|
}
|
|
|
|
void DateWidget::applyItemSpecificProperties(Label *item)
|
|
{
|
|
item->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
|
|
item->setEdges(RectangleEdge::Bottom);
|
|
item->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
|
|
item->setFont(style::window::font::largelight);
|
|
item->setPenFocusWidth(style::window::default_border_focus_w);
|
|
item->setPenWidth(style::window::default_border_rect_no_focus);
|
|
}
|
|
|
|
void DateWidget::applyLabelSpecificProperties(Label *label)
|
|
{
|
|
label->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::topMargin);
|
|
label->setEdges(RectangleEdge::None);
|
|
label->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
|
|
label->setFont(style::window::font::small);
|
|
label->activeItem = false;
|
|
}
|
|
|
|
void DateWidget::applyCallbacks()
|
|
{
|
|
focusChangedCallback = [&]([[maybe_unused]] Item &item) {
|
|
setFocusItem(focus ? dateHBox : nullptr);
|
|
return true;
|
|
};
|
|
|
|
inputCallback = [&](Item &item, const InputEvent &event) {
|
|
auto focusedItem = getFocusItem();
|
|
if (!event.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (event.is(KeyCode::KEY_ENTER) || event.is(KeyCode::KEY_RF)) {
|
|
return false;
|
|
}
|
|
if (focusedItem->onInput(event)) {
|
|
validateDate();
|
|
return true;
|
|
}
|
|
else if (dateHBox->onInput(event)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
yearInput->focusChangedCallback = [&](Item &item) {
|
|
yearInput->setText(std::to_string(static_cast<int>(validateDate().year())));
|
|
return true;
|
|
};
|
|
monthInput->focusChangedCallback = [&](Item &item) {
|
|
monthInput->setText(std::to_string(static_cast<unsigned>(validateDate().month())));
|
|
return true;
|
|
};
|
|
dayInput->focusChangedCallback = [&](Item &item) {
|
|
dayInput->setText(std::to_string(static_cast<unsigned>(validateDate().day())));
|
|
return true;
|
|
};
|
|
|
|
setOnInputCallback(*dayInput);
|
|
setOnInputCallback(*monthInput);
|
|
setOnInputCallback(*yearInput);
|
|
}
|
|
|
|
date::year_month_day DateWidget::validateDate()
|
|
{
|
|
auto actualDate = TimePointToYearMonthDay(TimePointNow());
|
|
uint32_t day;
|
|
try {
|
|
day = std::stoi(dayInput->getText().c_str());
|
|
}
|
|
catch (std::exception &) {
|
|
day = static_cast<unsigned>(actualDate.day());
|
|
}
|
|
uint32_t month;
|
|
try {
|
|
month = std::stoi(monthInput->getText().c_str());
|
|
}
|
|
catch (std::exception &) {
|
|
month = static_cast<unsigned>(actualDate.month());
|
|
}
|
|
int year;
|
|
try {
|
|
year = std::stoi(yearInput->getText().c_str());
|
|
}
|
|
catch (std::exception &) {
|
|
year = static_cast<int>(actualDate.year());
|
|
}
|
|
|
|
if (year > utils::time::Locale::max_years) {
|
|
yearInput->setText(std::to_string(utils::time::Locale::max_years));
|
|
}
|
|
|
|
year = std::clamp(year, utils::time::Locale::min_years, utils::time::Locale::max_years);
|
|
|
|
if (month > static_cast<unsigned>(date::dec)) {
|
|
monthInput->setText(std::to_string(static_cast<unsigned>(date::dec)));
|
|
}
|
|
month = std::clamp(static_cast<unsigned>(month), 1u, static_cast<unsigned>(date::dec));
|
|
|
|
date::year_month_day_last max_date = date::year(year) / date::month(month) / date::last;
|
|
if (day > static_cast<unsigned>(max_date.day())) {
|
|
dayInput->setText(std::to_string(static_cast<unsigned>(max_date.day())));
|
|
}
|
|
day = std::clamp(static_cast<unsigned>(day), 1u, static_cast<unsigned>(max_date.day()));
|
|
|
|
return date::year(year) / date::month(month) / date::day(day);
|
|
}
|
|
|
|
const date::year_month_day DateWidget::getChosenDate()
|
|
{
|
|
return validateDate();
|
|
}
|
|
|
|
void DateWidget::setDate(int keyValue, Label &item)
|
|
{
|
|
auto itemValue = item.getText();
|
|
auto key = std::to_string(keyValue);
|
|
if (itemValue == "0") {
|
|
itemValue = key;
|
|
}
|
|
else {
|
|
itemValue += key;
|
|
}
|
|
item.setText(itemValue);
|
|
|
|
if (!utils::time::validateDate(dayInput->getText(), monthInput->getText(), yearInput->getText())) {
|
|
item.setText(key);
|
|
}
|
|
}
|
|
|
|
void DateWidget::setOnInputCallback(Label &dateInput)
|
|
{
|
|
dateInput.inputCallback = [&](Item &item, const InputEvent &event) {
|
|
if (!event.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (event.isDigit()) {
|
|
setDate(event.numericValue(), dateInput);
|
|
return true;
|
|
}
|
|
else if (event.is(KeyCode::KEY_PND)) {
|
|
clearInput(dateInput);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
|
|
void DateWidget::clearInput(Label &dateInput)
|
|
{
|
|
auto value = dateInput.getText();
|
|
if (auto length = value.length(); length > 0) {
|
|
value.removeChar(length - 1);
|
|
dateInput.setText(value);
|
|
}
|
|
}
|
|
|
|
void DateWidget::loadData(const date::year_month_day &yearMonthDay)
|
|
{
|
|
dayInput->setText(std::to_string(static_cast<unsigned>(yearMonthDay.day())));
|
|
monthInput->setText(std::to_string(static_cast<unsigned>(yearMonthDay.month())));
|
|
yearInput->setText(std::to_string(static_cast<int>(yearMonthDay.year())));
|
|
}
|
|
|
|
} /* namespace gui */
|