~aleteoryx/muditaos

8f0797218f2defa9266704a93cfff94322a38f51 — Mateusz Grzegorzek 5 years ago 3fd296c
[EGD-5312] Add Time selection window

- add ChangeDateAndTimeWindow,
- extract EventTimeItem to common widgets folder
  and rename it to TimeWidget,
- extract EventDateItem to common widgets folder
  and rename it to DateWidget,
- replace timeWidget with common TimeWidget
  in NightshiftWindow,
- refactor time setting in
  DesktopMainWindow and TopBar,
- Remove dead code from EventManager
  (GetNextAlarmTimestamp and HandleAlarmTrigger)
73 files changed, 1264 insertions(+), 919 deletions(-)

M module-apps/Application.cpp
M module-apps/Application.hpp
M module-apps/CMakeLists.txt
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/CMakeLists.txt
M module-apps/application-calendar/data/dateCommon.hpp
M module-apps/application-calendar/models/CustomRepeatModel.hpp
M module-apps/application-calendar/models/EventDetailModel.hpp
M module-apps/application-calendar/models/NewEditEventModel.cpp
M module-apps/application-calendar/models/NewEditEventModel.hpp
A module-apps/application-calendar/widgets/CalendarDateItem.cpp
A module-apps/application-calendar/widgets/CalendarDateItem.hpp
M module-apps/application-calendar/widgets/CalendarListItem.hpp
M module-apps/application-calendar/widgets/CalendarStyle.hpp
A module-apps/application-calendar/widgets/CalendarTimeItem.cpp
A module-apps/application-calendar/widgets/CalendarTimeItem.hpp
M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp
D module-apps/application-calendar/widgets/EventDateItem.hpp
M module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp
M module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp
M module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp
M module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp
M module-apps/application-calendar/widgets/SeveralOptionsItem.cpp
M module-apps/application-calendar/widgets/SeveralOptionsItem.hpp
M module-apps/application-calendar/widgets/TextWithLabelItem.cpp
M module-apps/application-calendar/widgets/TextWithLabelItem.hpp
M module-apps/application-calendar/windows/AllEventsWindow.cpp
M module-apps/application-calendar/windows/DayEventsWindow.cpp
M module-apps/application-calllog/CalllogModel.cpp
M module-apps/application-desktop/windows/DesktopMainWindow.cpp
M module-apps/application-desktop/windows/DesktopMainWindow.hpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/CMakeLists.txt
A module-apps/application-settings-new/models/DateAndTimeModel.cpp
A module-apps/application-settings-new/models/DateAndTimeModel.hpp
A module-apps/application-settings-new/models/FromTimeToTimeModel.cpp
A module-apps/application-settings-new/models/FromTimeToTimeModel.hpp
A module-apps/application-settings-new/widgets/SettingsDateItem.cpp
A module-apps/application-settings-new/widgets/SettingsDateItem.hpp
A module-apps/application-settings-new/widgets/SettingsTimeItem.cpp
A module-apps/application-settings-new/widgets/SettingsTimeItem.hpp
D module-apps/application-settings-new/widgets/timeWidget.cpp
D module-apps/application-settings-new/widgets/timeWidget.hpp
A module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp
A module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.hpp
M module-apps/application-settings-new/windows/NightshiftWindow.cpp
M module-apps/application-settings-new/windows/NightshiftWindow.hpp
M module-apps/application-settings-new/windows/TorchWindow.cpp
M module-apps/application-settings/windows/DateTimeWindow.cpp
A module-apps/widgets/DateAndTimeStyle.hpp
A module-apps/widgets/DateOrTimeListItem.hpp
R module-apps/{application-calendar/widgets/EventDateItem => widgets/DateWidget}.cpp
A module-apps/widgets/DateWidget.hpp
R module-apps/{application-calendar/widgets/EventTimeItem => widgets/TimeWidget}.cpp
R module-apps/{application-calendar/widgets/EventTimeItem => widgets/TimeWidget}.hpp
A module-apps/widgets/WidgetsUtils.cpp
A module-apps/widgets/WidgetsUtils.hpp
M module-apps/windows/AppWindow.cpp
M module-apps/windows/AppWindow.hpp
M module-gui/gui/widgets/ListItem.hpp
M module-gui/gui/widgets/TopBar.cpp
M module-gui/gui/widgets/TopBar.hpp
M module-services/service-evtmgr/CMakeLists.txt
M module-services/service-evtmgr/EventManager.cpp
M module-services/service-evtmgr/WorkerEvent.cpp
D module-services/service-evtmgr/alarm/EventManagerAlarm.cpp
M module-services/service-evtmgr/service-evtmgr/EVMessages.hpp
M module-services/service-evtmgr/service-evtmgr/EventManager.hpp
M module-utils/time/DateAndTimeSettings.hpp
A module-utils/time/FromTillDate.hpp
M module-utils/time/time_conversion.cpp
M module-utils/time/time_conversion.hpp
M module-utils/time/time_locale.hpp
M module-apps/Application.cpp => module-apps/Application.cpp +0 -7
@@ 308,8 308,6 @@ namespace app

    sys::MessagePointer Application::handleMinuteUpdated(sys::Message *msgl)
    {
        auto *msg = static_cast<sevm::RtcMinuteAlarmMessage *>(msgl);
        getCurrentWindow()->updateTime(msg->timestamp, !isTimeFormat12());
        if (state == State::ACTIVE_FORGROUND) {
            refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
        }


@@ 709,11 707,6 @@ namespace app
        timeFormat12 = utils::getNumericValue<bool>(value);
    }

    bool Application::isTimeFormat12() const noexcept
    {
        return utils::dateAndTimeSettings.getTimeFormat() == utils::time::Locale::TimeFormat::FormatTime12H;
    }

    void Application::cancelCallbacks(AsyncCallbackReceiver::Ptr receiver)
    {
        callbackStorage->removeAll(receiver);

M module-apps/Application.hpp => module-apps/Application.hpp +0 -1
@@ 379,7 379,6 @@ namespace app
        bool timeFormat12 = false;

      public:
        bool isTimeFormat12() const noexcept;
        void setLockScreenPasscodeOn(bool screenPasscodeOn) noexcept;
        bool isLockScreenPasscodeOn() const noexcept;
        const gui::top_bar::Configuration &getTopBarConfiguration() const noexcept;

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +3 -0
@@ 36,6 36,9 @@ set( SOURCES
    "widgets/BarGraph.cpp"
    "widgets/ActiveIconFactory.cpp"
    "widgets/TextWithIconsWidget.cpp"
    "widgets/DateWidget.cpp"
    "widgets/TimeWidget.cpp"
    "widgets/WidgetsUtils.cpp"
    "options/OptionsModel.cpp"
    "options/type/OptionSimple.cpp"
    "options/type/OptionCall.cpp"

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationCalendar.hpp"


@@ 165,8 165,8 @@ namespace app
            eventData->setDescription(style::window::calendar::new_event);
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = dateFilter;
            event->date_till = dateFilter + std::chrono::hours(style::window::calendar::time::max_hour_24H_mode) +
                               std::chrono::minutes(style::window::calendar::time::max_minutes);
            event->date_till = dateFilter + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                               std::chrono::minutes(utils::time::Locale::max_minutes);
            eventData->setData(event);

            switchWindow(

M module-apps/application-calendar/CMakeLists.txt => module-apps/application-calendar/CMakeLists.txt +2 -2
@@ 24,13 24,13 @@
		"${CMAKE_CURRENT_LIST_DIR}/widgets/EventDetailDescriptionItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/RepeatAndReminderItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CheckBoxWithLabelItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/EventTimeItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/EventDateItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/SeveralOptionsItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/TextWithLabelItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/NewEventCheckBoxWithLabel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/DayLabel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/MonthBox.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarDateItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarTimeItem.cpp"
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationCalendar.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/MonthModel.hpp"

M module-apps/application-calendar/data/dateCommon.hpp => module-apps/application-calendar/data/dateCommon.hpp +23 -1
@@ 1,10 1,11 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef DATECOMMON_H
#define DATECOMMON_H

#include <module-utils/date/include/date/date.h>
#include <module-utils/time/DateAndTimeSettings.hpp>
#include <time/time_conversion.hpp>
#include <Utils.hpp>
#include <random>


@@ 19,6 20,7 @@ namespace calendar
} // namespace calendar

inline constexpr auto max_month_day = 31;
inline constexpr auto unix_epoch_year = 1900;

enum class Reminder
{


@@ 124,6 126,14 @@ inline uint32_t TimePointToHour24H(const TimePoint &tp)
    return hour;
}

inline auto LocalizedHoursToUtcHours(int hour = 0)
{
    std::tm tm           = CreateTmStruct(unix_epoch_year, 1, 1, hour, 0, 0);
    std::time_t basetime = std::mktime(&tm);
    basetime -= utils::time::Time::getTimeZoneOffset();
    return TimePointToHour24H(TimePointFromTimeT(basetime));
}

inline uint32_t TimePointToMinutes(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);


@@ 204,6 214,12 @@ inline std::string TimePointToLocalizedTimeString(const TimePoint &tp, const std
    return timestamp.str(format);
}

inline std::string TimePointToLocalizedHourMinString(const TimePoint &tp)
{
    return utils::dateAndTimeSettings.isTimeFormat12() ? TimePointToLocalizedDateString(tp, "%I:%M")
                                                       : TimePointToLocalizedDateString(tp, "%H:%M");
}

inline TimePoint TimePointFromString(const char *s1)
{
    TimePoint tp;


@@ 248,6 264,12 @@ inline std::string TimePointToHourString12H(const TimePoint &tp)
    return utils::to_string(hour12h);
}

inline std::string TimePointToLocalizedHourString(const TimePoint &tp)
{
    return utils::dateAndTimeSettings.isTimeFormat12() ? TimePointToLocalizedDateString(tp, "%I")
                                                       : TimePointToLocalizedDateString(tp, "%H");
}

inline std::string TimePointToHourString24H(const TimePoint &tp)
{
    auto hour =

M module-apps/application-calendar/models/CustomRepeatModel.hpp => module-apps/application-calendar/models/CustomRepeatModel.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/widgets/CalendarListItem.hpp"

M module-apps/application-calendar/models/EventDetailModel.hpp => module-apps/application-calendar/models/EventDetailModel.hpp +1 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

M module-apps/application-calendar/models/NewEditEventModel.cpp => module-apps/application-calendar/models/NewEditEventModel.cpp +11 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewEditEventModel.hpp"


@@ 72,17 72,17 @@ void NewEditEventModel::createData(bool allDayEvent)
    allDayEventCheckBox = new gui::NewEventCheckBoxWithLabel(
        application, utils::localize.get("app_calendar_new_edit_event_allday"), this);

    dateItem = new gui::EventDateItem();
    dateItem = new gui::CalendarDateItem();

    startTime = new gui::EventTimeItem(
    startTime = new gui::CalendarTimeItem(
        utils::localize.get("app_calendar_new_edit_event_start"),
        mode24H,
        gui::TimeWidget::Type::Start,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    endTime = new gui::EventTimeItem(
    endTime = new gui::CalendarTimeItem(
        utils::localize.get("app_calendar_new_edit_event_end"),
        mode24H,
        gui::TimeWidget::Type::End,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });



@@ 98,13 98,13 @@ void NewEditEventModel::createData(bool allDayEvent)
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    endTime->setConnectionToSecondItem(startTime);
    startTime->setConnectionToSecondItem(endTime);
    endTime->setConnectionToSecondItem(startTime);

    startTime->setConnectionToDateItem(dateItem);
    endTime->setConnectionToDateItem(dateItem);

    allDayEventCheckBox->setConnectionToDateItem(dateItem);
    allDayEventCheckBox->setConnectionToDateItem(dateItem->getDateWidget());

    internalData.push_back(eventNameInput);
    internalData.push_back(allDayEventCheckBox);


@@ 129,8 129,8 @@ void NewEditEventModel::loadData(std::shared_ptr<EventsRecord> record)
    auto end_time      = TimePointToHourMinSec(record->date_till);
    auto isAllDayEvent = [&]() -> bool {
        return start_time.hours().count() == 0 && start_time.minutes().count() == 0 &&
               end_time.hours().count() == style::window::calendar::time::max_hour_24H_mode &&
               end_time.minutes().count() == style::window::calendar::time::max_minutes;
               end_time.hours().count() == utils::time::Locale::max_hour_24H_mode &&
               end_time.minutes().count() == utils::time::Locale::max_minutes;
    };

    createData(isAllDayEvent());


@@ 243,6 243,7 @@ void NewEditEventModel::saveData(std::shared_ptr<EventsRecord> event, EventActio
            }
        }
        else {
            LOG_WARN("Title event is empty! Returning to previous window.");
            application->returnToPreviousWindow();
        }
    }

M module-apps/application-calendar/models/NewEditEventModel.hpp => module-apps/application-calendar/models/NewEditEventModel.hpp +11 -11
@@ 1,14 1,13 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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.hpp"
#include <application-calendar/widgets/CalendarDateItem.hpp>
#include <application-calendar/widgets/CalendarTimeItem.hpp>
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/widgets/CalendarListItem.hpp"
#include "application-calendar/widgets/TextWithLabelItem.hpp"
#include "application-calendar/widgets/EventTimeItem.hpp"
#include "application-calendar/widgets/SeveralOptionsItem.hpp"
#include "application-calendar/widgets/EventDateItem.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>



@@ 22,13 21,15 @@ class NewEditEventModel : public app::InternalModel<gui::CalendarListItem *>, pu
    app::Application *application = nullptr;
    bool mode24H                  = false;

    gui::TextWithLabelItem *eventNameInput          = nullptr;
    gui::NewEventCheckBoxWithLabel *allDayEventCheckBox = nullptr;
    gui::EventDateItem *dateItem                    = nullptr;
    gui::EventTimeItem *startTime                   = nullptr;
    gui::EventTimeItem *endTime                     = nullptr;
    gui::SeveralOptionsItem *reminder               = nullptr;
    gui::SeveralOptionsItem *repeat                 = nullptr;

    gui::CalendarDateItem *dateItem  = nullptr;
    gui::CalendarTimeItem *startTime = nullptr;
    gui::CalendarTimeItem *endTime   = nullptr;

    gui::SeveralOptionsItem *reminder      = nullptr;
    gui::SeveralOptionsItem *repeat        = nullptr;
    gui::TextWithLabelItem *eventNameInput = nullptr;

  public:
    NewEditEventModel(app::Application *app, bool mode24H = false);


@@ 38,7 39,6 @@ class NewEditEventModel : public app::InternalModel<gui::CalendarListItem *>, pu
    void loadDataWithoutTimeItem();
    void reloadDataWithTimeItem();
    void saveData(std::shared_ptr<EventsRecord> event, EventAction action);
    void addReapetedRecords(std::shared_ptr<EventsRecord> event);

    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    [[nodiscard]] unsigned int requestRecordsCount() override;

A module-apps/application-calendar/widgets/CalendarDateItem.cpp => module-apps/application-calendar/widgets/CalendarDateItem.cpp +30 -0
@@ 0,0 1,30 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarDateItem.hpp"
#include <module-apps/widgets/DateAndTimeStyle.hpp>
#include <module-apps/widgets/DateWidget.hpp>
#include <module-apps/widgets/WidgetsUtils.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    CalendarDateItem::CalendarDateItem()
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        dateWidget = new DateWidget(vBox);

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            dateWidget->loadData(TimePointToYearMonthDay(event->date_from));
        };

        passDefaultCallbacksFromListItem(this, vBox);
    }
} /* namespace gui */

A module-apps/application-calendar/widgets/CalendarDateItem.hpp => module-apps/application-calendar/widgets/CalendarDateItem.hpp +26 -0
@@ 0,0 1,26 @@
// 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 "CalendarListItem.hpp"

namespace gui
{
    class DateWidget;
    class VBox;
    class CalendarDateItem : public CalendarListItem
    {
      public:
        CalendarDateItem();

        [[nodiscard]] DateWidget *getDateWidget() const noexcept
        {
            return dateWidget;
        }

      private:
        DateWidget *dateWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

M module-apps/application-calendar/widgets/CalendarListItem.hpp => module-apps/application-calendar/widgets/CalendarListItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 <module-db/Interface/EventsRecord.hpp>
#include <ListItem.hpp>


M module-apps/application-calendar/widgets/CalendarStyle.hpp => module-apps/application-calendar/widgets/CalendarStyle.hpp +1 -21
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 65,16 65,6 @@ namespace style
                inline constexpr auto date_text_3   = "March 2019";
            } // namespace test

            namespace time
            {
                inline constexpr auto max_time_length   = 2;
                inline constexpr auto max_hour_24H_mode = 23;
                inline constexpr auto max_hour_12H_mode = 12;
                inline constexpr auto max_minutes       = 59;
                inline constexpr auto max_years         = 2038;
                inline constexpr auto min_years         = 1970;
            } // namespace time

            namespace item
            {
                namespace dayEvents


@@ 109,16 99,6 @@ namespace style
                    inline constexpr auto label_h           = 35;
                } // namespace eventDetail

                namespace eventTime
                {
                    inline constexpr auto height           = 107;
                    inline constexpr auto margin           = 20;
                    inline constexpr auto separator        = 30;
                    inline constexpr auto time_input_12h_w = 120;
                    inline constexpr auto time_input_24h_w = 195;
                    inline constexpr auto hBox_h           = height - 1.25 * margin;
                } // namespace eventTime

                namespace checkBox
                {
                    inline constexpr auto height              = 44;

A module-apps/application-calendar/widgets/CalendarTimeItem.cpp => module-apps/application-calendar/widgets/CalendarTimeItem.cpp +56 -0
@@ 0,0 1,56 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarDateItem.hpp"
#include "CalendarTimeItem.hpp"
#include <module-apps/widgets/DateAndTimeStyle.hpp>
#include <module-apps/widgets/WidgetsUtils.hpp>
#include <module-gui/gui/input/InputEvent.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    CalendarTimeItem::CalendarTimeItem(const std::string &description,
                                       TimeWidget::Type type,
                                       const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                                       const std::function<void()> &bottomBarRestoreFromTemporaryMode)
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        timeWidget = new TimeWidget(vBox, description, type, bottomBarTemporaryMode, bottomBarRestoreFromTemporaryMode);

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            timeWidget->loadData(std::chrono::hours(TimePointToHour24H(event->date_from)),
                                 std::chrono::minutes(TimePointToMin(event->date_from)),
                                 std::chrono::hours(TimePointToHour24H(event->date_till)),
                                 std::chrono::minutes(TimePointToMin(event->date_till)));
        };

        onSaveCallback = [&](std::shared_ptr<EventsRecord> record) -> bool {
            auto fromTillDate = std::make_shared<utils::time::FromTillDate>(record->date_from, record->date_till);
            if (timeWidget->saveData(fromTillDate)) {
                record->date_from = fromTillDate->from;
                record->date_till = fromTillDate->till;
                return true;
            }
            return false;
        };
        passDefaultCallbacksFromListItem(this, vBox);
    }

    void CalendarTimeItem::setConnectionToSecondItem(CalendarTimeItem *item)
    {
        timeWidget->setConnectionToSecondItem(item->timeWidget);
    }

    void CalendarTimeItem::setConnectionToDateItem(CalendarDateItem *item)
    {
        timeWidget->setConnectionToDateItem(item->getDateWidget());
    }
} /* namespace gui */

A module-apps/application-calendar/widgets/CalendarTimeItem.hpp => module-apps/application-calendar/widgets/CalendarTimeItem.hpp +27 -0
@@ 0,0 1,27 @@
// 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 "CalendarListItem.hpp"
#include <module-apps/widgets/TimeWidget.hpp>

namespace gui
{
    class VBox;
    class CalendarTimeItem : public CalendarListItem
    {
      public:
        CalendarTimeItem(const std::string &description,
                         TimeWidget::Type type,
                         const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                         const std::function<void()> &bottomBarRestoreFromTemporaryMode);

        void setConnectionToSecondItem(CalendarTimeItem *item);
        void setConnectionToDateItem(CalendarDateItem *item);

      private:
        TimeWidget *timeWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

M module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp => module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "CalendarListItem.hpp"
#include "Application.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"

D module-apps/application-calendar/widgets/EventDateItem.hpp => module-apps/application-calendar/widgets/EventDateItem.hpp +0 -39
@@ 1,39 0,0 @@
// 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 "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <BoxLayout.hpp>

namespace gui
{
    class EventDateItem : public CalendarListItem
    {
        gui::VBox *vBox        = nullptr;
        gui::HBox *labelsHBox  = nullptr;
        gui::HBox *dateHBox    = nullptr;
        gui::Label *dayLabel   = nullptr;
        gui::Label *monthLabel = nullptr;
        gui::Label *yearLabel  = nullptr;
        gui::Label *dayInput   = nullptr;
        gui::Label *monthInput = nullptr;
        gui::Label *yearInput  = nullptr;

        void buildInterface();
        void applyItemSpecificProperties(gui::Label *item);
        void applyLabelSpecificProperties(gui::Label *label);
        void applyCallbacks();
        calendar::YearMonthDay validateDate();
        void setDate(int keyValue, gui::Label &item);
        void setOnInputCallback(gui::Label &dateInput);
        void clearInput(gui::Label &dateInput);

      public:
        EventDateItem();

        const calendar::YearMonthDay getChosenDate();
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>

M module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp => module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp +6 -7
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewEventCheckBoxWithLabel.hpp"


@@ 60,22 60,21 @@ namespace gui
            auto start_time = TimePointToHourMinSec(event->date_from);
            auto end_time   = TimePointToHourMinSec(event->date_till);
            if (start_time.hours().count() == 0 && start_time.minutes().count() == 0 &&
                end_time.hours().count() == style::window::calendar::time::max_hour_24H_mode &&
                end_time.minutes().count() == style::window::calendar::time::max_minutes) {
                end_time.hours().count() == utils::time::Locale::max_hour_24H_mode &&
                end_time.minutes().count() == utils::time::Locale::max_minutes) {
                checkBox->setImageVisible(true);
            }
        };
        onSaveCallback = [&](std::shared_ptr<EventsRecord> event) {
            if (checkBox->isChecked()) {
                event->date_from = TimePointFromYearMonthDay(dateItem->getChosenDate());
                event->date_till = event->date_from +
                                   std::chrono::hours(style::window::calendar::time::max_hour_24H_mode) +
                                   std::chrono::minutes(style::window::calendar::time::max_minutes);
                event->date_till = event->date_from + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                                   std::chrono::minutes(utils::time::Locale::max_minutes);
            }
        };
    }

    void NewEventCheckBoxWithLabel::setConnectionToDateItem(gui::EventDateItem *item)
    void NewEventCheckBoxWithLabel::setConnectionToDateItem(gui::DateWidget *item)
    {
        dateItem = item;
    }

M module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp => module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,7 11,7 @@ namespace gui
    {
        NewEditEventModel *model = nullptr;
        app::Application *app    = nullptr;
        gui::EventDateItem *dateItem = nullptr;
        gui::DateWidget *dateItem = nullptr;
        void applyCallbacks() override;

      public:


@@ 20,7 20,7 @@ namespace gui
                                  NewEditEventModel *model  = nullptr);
        virtual ~NewEventCheckBoxWithLabel() override = default;

        void setConnectionToDateItem(gui::EventDateItem *item);
        void setConnectionToDateItem(gui::DateWidget *item);
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>

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

#include "SeveralOptionsItem.hpp"

M module-apps/application-calendar/widgets/SeveralOptionsItem.hpp => module-apps/application-calendar/widgets/SeveralOptionsItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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.hpp"
#include "CalendarListItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"

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

#include "TextWithLabelItem.hpp"

M module-apps/application-calendar/widgets/TextWithLabelItem.hpp => module-apps/application-calendar/widgets/TextWithLabelItem.hpp +2 -1
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>

M module-apps/application-calendar/windows/AllEventsWindow.cpp => module-apps/application-calendar/windows/AllEventsWindow.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AllEventsWindow.hpp"


@@ 94,8 94,8 @@ namespace gui
            data->setDescription(style::window::calendar::new_event);
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = dateFilter;
            event->date_till = dateFilter + std::chrono::hours(style::window::calendar::time::max_hour_24H_mode) +
                               std::chrono::minutes(style::window::calendar::time::max_minutes);
            event->date_till = dateFilter + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                               std::chrono::minutes(utils::time::Locale::max_minutes);
            data->setData(event);
            application->switchWindow(
                style::window::calendar::name::new_edit_event, gui::ShowMode::GUI_SHOW_INIT, std::move(data));

M module-apps/application-calendar/windows/DayEventsWindow.cpp => module-apps/application-calendar/windows/DayEventsWindow.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DayEventsWindow.hpp"


@@ 100,8 100,8 @@ namespace gui
            data->setDescription(style::window::calendar::new_event);
            auto rec       = new EventsRecord();
            rec->date_from = filterFrom;
            rec->date_till = filterFrom + std::chrono::hours(style::window::calendar::time::max_hour_24H_mode) +
                             std::chrono::minutes(style::window::calendar::time::max_minutes);
            rec->date_till = filterFrom + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                             std::chrono::minutes(utils::time::Locale::max_minutes);
            auto event = std::make_shared<EventsRecord>(*rec);
            data->setData(event);
            application->switchWindow(

M module-apps/application-calllog/CalllogModel.cpp => module-apps/application-calllog/CalllogModel.cpp +2 -1
@@ 7,6 7,7 @@
#include "data/CallLogInternals.hpp"
#include "data/CallLogSwitchData.hpp"
#include "widgets/CalllogItem.hpp"
#include <module-utils/time/DateAndTimeSettings.hpp>
#include <module-utils/Utils.hpp>

#include <service-db/DBServiceAPI.hpp>


@@ 59,7 60,7 @@ gui::ListItem *CalllogModel::getItem(gui::Order order)
        return nullptr;
    }

    auto item = new gui::CalllogItem(this, !(application->isTimeFormat12()));
    auto item = new gui::CalllogItem(this, !(utils::dateAndTimeSettings.isTimeFormat12()));

    auto callCallback = [this, item](gui::Item &, const gui::InputEvent &event) {
        if (event.state != gui::InputEvent::State::keyReleasedShort) {

M module-apps/application-desktop/windows/DesktopMainWindow.cpp => module-apps/application-desktop/windows/DesktopMainWindow.cpp +9 -21
@@ 31,7 31,6 @@ namespace gui
{
    void DesktopMainWindow::buildInterface()
    {
        auto ttime = utils::time::Time();
        AppWindow::buildInterface();

        bottomBar->setActive(BottomBar::Side::CENTER, true);


@@ 42,14 41,12 @@ namespace gui
        time->setFilled(false);
        time->setBorderColor(gui::ColorNoColor);
        time->setFont(style::window::font::supersizemelight);
        time->setText(ttime);
        time->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));

        dayText = new gui::Label(this, dayLabel::X, dayLabel::Y, dayLabel::Width, dayLabel::Height);
        dayText->setFilled(false);
        dayText->setBorderColor(gui::ColorNoColor);
        dayText->setFont(style::window::font::biglight);
        dayText->setText(ttime.day() + ", " + ttime.str("%d %b"));
        dayText->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));

        activatedCallback = [this](Item &) {


@@ 86,9 83,7 @@ namespace gui
        buildInterface();

        preBuildDrawListHook = [this](std::list<Command> &cmd) {
            if (time != nullptr) {
                time->setText(topBar->getTimeString());
            }
            time->setText(TimePointToLocalizedHourMinString(TimePointNow()));
        };
    }



@@ 128,8 123,7 @@ namespace gui

    void DesktopMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        // update time
        time->setText(topBar->getTimeString());
        updateTime();
        // check if there was a signal to lock the pone due to inactivity.
        if ((data != nullptr) && (data->getDescription() == "LockPhoneData")) {
            auto app = getAppDesktop();


@@ 239,19 233,6 @@ namespace gui
        buildInterface();
    }

    bool DesktopMainWindow::updateTime(const UTF8 &timeStr)
    {
        auto ret = AppWindow::updateTime(timeStr);
        time->setText(topBar->getTimeString());
        return ret;
    }
    bool DesktopMainWindow::updateTime(const uint32_t &timestamp, bool mode24H)
    {
        auto ret = AppWindow::updateTime(timestamp, mode24H);
        time->setText(topBar->getTimeString());
        return ret;
    }

    auto DesktopMainWindow::buildNotifications(app::ApplicationDesktop *app) -> bool
    {
        erase(notifications);


@@ 344,4 325,11 @@ namespace gui
        assert(app);
        return app;
    }

    void DesktopMainWindow::updateTime()
    {
        auto timeNow = TimePointNow();
        time->setText(TimePointToLocalizedHourMinString(timeNow));
        dayText->setText(TimePointToLocalizedDateString(timeNow, "%A, %d %b"));
    }
} /* namespace gui */

M module-apps/application-desktop/windows/DesktopMainWindow.hpp => module-apps/application-desktop/windows/DesktopMainWindow.hpp +2 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 83,11 83,10 @@ namespace gui
        void destroyInterface() override;
        top_bar::Configuration configureTopBar(top_bar::Configuration appConfiguration) override;

        bool updateTime(const UTF8 &timeStr) override;
        bool updateTime(const uint32_t &timestamp, bool mode24H) override;

      private:
        void invalidate() noexcept;
        void updateTime();

        gui::KeyInputMappedTranslation translator;
    };

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +4 -0
@@ 33,6 33,7 @@
#include "windows/LanguagesWindow.hpp"
#include "windows/DateAndTimeMainWindow.hpp"
#include "windows/ChangeTimeZone.hpp"
#include "windows/ChangeDateAndTimeWindow.hpp"

#include "Dialog.hpp"



@@ 318,6 319,9 @@ namespace app
        windowsFactory.attach(gui::window::name::change_time_zone, [](Application *app, const std::string &name) {
            return std::make_unique<gui::ChangeTimeZone>(app);
        });
        windowsFactory.attach(gui::window::name::change_date_and_time, [](Application *app, const std::string &name) {
            return std::make_unique<gui::ChangeDateAndTimeWindow>(app);
        });
    }

    void ApplicationSettingsNew::destroyUserInterface()

M module-apps/application-settings-new/CMakeLists.txt => module-apps/application-settings-new/CMakeLists.txt +6 -1
@@ 18,10 18,13 @@ target_sources( ${PROJECT_NAME}
        models/ApnSettingsModel.cpp
        models/BluetoothSettingsModel.cpp
        models/NewApnModel.cpp
        widgets/timeWidget.cpp
        models/DateAndTimeModel.cpp
        models/FromTimeToTimeModel.cpp
        widgets/ChangePasscodeLockHandler.cpp
        widgets/QuoteWidget.cpp
        widgets/ApnInputWidget.cpp
        widgets/SettingsDateItem.cpp
        widgets/SettingsTimeItem.cpp
        windows/SettingsMainWindow.cpp
        windows/AddDeviceWindow.cpp
        windows/AllDevicesWindow.cpp


@@ 54,6 57,7 @@ target_sources( ${PROJECT_NAME}
        windows/LanguagesWindow.cpp
        windows/DateAndTimeMainWindow.cpp
        windows/ChangeTimeZone.cpp
        windows/ChangeDateAndTimeWindow.cpp
        windows/BluetoothCheckPasskeyWindow.cpp

    PUBLIC


@@ 78,6 82,7 @@ target_sources( ${PROJECT_NAME}
        windows/SystemMainWindow.hpp
        windows/LanguagesWindow.hpp
        windows/DateAndTimeMainWindow.hpp
        windows/ChangeDateAndTimeWindow.hpp
)

add_dependencies(${PROJECT_NAME} version)

A module-apps/application-settings-new/models/DateAndTimeModel.cpp => module-apps/application-settings-new/models/DateAndTimeModel.cpp +78 -0
@@ 0,0 1,78 @@
// 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/widgets/SettingsDateItem.hpp>
#include <application-settings-new/widgets/SettingsTimeItem.hpp>
#include "DateAndTimeModel.hpp"
#include <ListView.hpp>
#include <module-apps/widgets/DateAndTimeStyle.hpp>
#include <module-utils/time/DateAndTimeSettings.hpp>

DateAndTimeModel::DateAndTimeModel(app::Application *application) : app(application)
{}

void DateAndTimeModel::loadData(std::shared_ptr<utils::time::FromTillDate> fromTillDate)
{
    list->clear();
    eraseInternalData();

    createData();

    for (auto &item : internalData) {
        if (item->onLoadCallback) {
            item->onLoadCallback(fromTillDate);
        }
    }

    list->rebuildList();
}

void DateAndTimeModel::saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate)
{
    for (auto &item : internalData) {
        if (item->onSaveCallback) {
            item->onSaveCallback(fromTillDate);
        }
    }
}

gui::ListItem *DateAndTimeModel::getItem(gui::Order order)
{
    return getRecord(order);
}

unsigned int DateAndTimeModel::getMinimalItemHeight() const
{
    return style::window::date_and_time::height;
}

void DateAndTimeModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

unsigned int DateAndTimeModel::requestRecordsCount()
{
    return internalData.size();
}

void DateAndTimeModel::createData()
{
    dateItem = new gui::SettingsDateItem();

    timeItem = new gui::SettingsTimeItem(
        utils::localize.get("app_settings_title_time"),
        gui::TimeWidget::Type::Start,
        [&](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [&]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    timeItem->setConnectionToDateItem(dateItem);

    internalData.push_back(dateItem);
    internalData.push_back(timeItem);

    for (auto &item : internalData) {
        item->deleteByList = false;
    }
}

A module-apps/application-settings-new/models/DateAndTimeModel.hpp => module-apps/application-settings-new/models/DateAndTimeModel.hpp +36 -0
@@ 0,0 1,36 @@
// 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.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>
#include <widgets/DateOrTimeListItem.hpp>

namespace gui
{
    class SettingsDateItem;
    class SettingsTimeItem;
}; // namespace gui

class DateAndTimeModel : public app::InternalModel<gui::DateOrTimeListItem *>, public gui::ListItemProvider
{
  public:
    DateAndTimeModel(app::Application *application);

    void loadData(std::shared_ptr<utils::time::FromTillDate> fromTillDate);
    void saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate);

    gui::ListItem *getItem(gui::Order order) override;
    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    [[nodiscard]] unsigned int requestRecordsCount() override;

  private:
    app::Application *app           = nullptr;
    gui::SettingsDateItem *dateItem = nullptr;
    gui::SettingsTimeItem *timeItem = nullptr;

    void createData();
};

A module-apps/application-settings-new/models/FromTimeToTimeModel.cpp => module-apps/application-settings-new/models/FromTimeToTimeModel.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 "FromTimeToTimeModel.hpp"
#include <application-settings-new/widgets/SettingsDateItem.hpp>
#include <application-settings-new/widgets/SettingsTimeItem.hpp>
#include <ListView.hpp>
#include <time/DateAndTimeSettings.hpp>
#include <widgets/DateAndTimeStyle.hpp>

FromTimeToTimeModel::FromTimeToTimeModel(app::Application *application) : app(application)
{}

void FromTimeToTimeModel::loadData(std::shared_ptr<utils::time::FromTillDate> fromTillDate)
{
    list->clear();
    eraseInternalData();

    createData();

    for (auto &item : internalData) {
        if (item->onLoadCallback) {
            item->onLoadCallback(fromTillDate);
        }
    }

    list->rebuildList();
}

void FromTimeToTimeModel::saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate)
{
    for (auto &item : internalData) {
        if (item->onSaveCallback) {
            item->onSaveCallback(fromTillDate);
        }
    }
}

gui::ListItem *FromTimeToTimeModel::getItem(gui::Order order)
{
    return getRecord(order);
}

unsigned int FromTimeToTimeModel::getMinimalItemHeight() const
{
    return style::window::date_and_time::height;
}

void FromTimeToTimeModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

unsigned int FromTimeToTimeModel::requestRecordsCount()
{
    return internalData.size();
}

void FromTimeToTimeModel::createData()
{
    fromTimeItem = new gui::SettingsTimeItem(
        utils::localize.get("app_settings_nightshift_from"),
        gui::TimeWidget::Type::Start,
        [&](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [&]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    toTimeItem = new gui::SettingsTimeItem(
        utils::localize.get("app_settings_nightshift_to"),
        gui::TimeWidget::Type::End,
        [&](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [&]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    fromTimeItem->setConnectionToSecondItem(toTimeItem);
    toTimeItem->setConnectionToSecondItem(fromTimeItem);

    internalData.push_back(fromTimeItem);
    internalData.push_back(toTimeItem);

    for (auto &item : internalData) {
        item->deleteByList = false;
    }
}

A module-apps/application-settings-new/models/FromTimeToTimeModel.hpp => module-apps/application-settings-new/models/FromTimeToTimeModel.hpp +35 -0
@@ 0,0 1,35 @@
// 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.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>
#include <widgets/DateOrTimeListItem.hpp>

namespace gui
{
    class SettingsTimeItem;
};

class FromTimeToTimeModel : public app::InternalModel<gui::DateOrTimeListItem *>, public gui::ListItemProvider
{
  public:
    FromTimeToTimeModel(app::Application *application);

    void loadData(std::shared_ptr<utils::time::FromTillDate> fromTillDate);
    void saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate);

    gui::ListItem *getItem(gui::Order order) override;
    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    [[nodiscard]] unsigned int requestRecordsCount() override;

  private:
    app::Application *app               = nullptr;
    gui::SettingsTimeItem *fromTimeItem = nullptr;
    gui::SettingsTimeItem *toTimeItem   = nullptr;

    void createData();
};

A module-apps/application-settings-new/widgets/SettingsDateItem.cpp => module-apps/application-settings-new/widgets/SettingsDateItem.cpp +36 -0
@@ 0,0 1,36 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SettingsDateItem.hpp"
#include <module-apps/widgets/DateAndTimeStyle.hpp>
#include <module-apps/widgets/WidgetsUtils.hpp>
#include <module-gui/gui/input/InputEvent.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    SettingsDateItem::SettingsDateItem()
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        dateWidget = new DateWidget(vBox);

        onLoadCallback = [&](std::shared_ptr<utils::time::FromTillDate> fromTillDate) {
            dateWidget->loadData(TimePointToYearMonthDay(fromTillDate->from));
        };

        inputCallback = [&](Item &item, const InputEvent &event) {
            if (event.state != InputEvent::State::keyReleasedShort) {
                return false;
            }
            return vBox->onInput(event);
        };
        passDefaultCallbacksFromListItem(this, vBox);
    }
} /* namespace gui */

A module-apps/application-settings-new/widgets/SettingsDateItem.hpp => module-apps/application-settings-new/widgets/SettingsDateItem.hpp +25 -0
@@ 0,0 1,25 @@
// 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 <module-apps/widgets/DateWidget.hpp>
#include <widgets/DateOrTimeListItem.hpp>

namespace gui
{
    class SettingsDateItem : public DateOrTimeListItem
    {
      public:
        SettingsDateItem();

        [[nodiscard]] DateWidget *getDateWidget() const noexcept
        {
            return dateWidget;
        }

      private:
        DateWidget *dateWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

A module-apps/application-settings-new/widgets/SettingsTimeItem.cpp => module-apps/application-settings-new/widgets/SettingsTimeItem.cpp +50 -0
@@ 0,0 1,50 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SettingsDateItem.hpp"
#include "SettingsTimeItem.hpp"
#include <module-apps/widgets/DateAndTimeStyle.hpp>
#include <module-apps/widgets/WidgetsUtils.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    SettingsTimeItem::SettingsTimeItem(const std::string &description,
                                       TimeWidget::Type type,
                                       const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                                       const std::function<void()> &bottomBarRestoreFromTemporaryMode)
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        timeWidget = new TimeWidget(vBox, description, type, bottomBarTemporaryMode, bottomBarRestoreFromTemporaryMode);

        onLoadCallback = [&](std::shared_ptr<utils::time::FromTillDate> fromTillDate) {
            timeWidget->loadData(std::chrono::hours(TimePointToHour24H(fromTillDate->from)),
                                 std::chrono::minutes(TimePointToMin(fromTillDate->from)),
                                 std::chrono::hours(TimePointToHour24H(fromTillDate->till)),
                                 std::chrono::minutes(TimePointToMin(fromTillDate->till)));
        };

        onSaveCallback = [&](std::shared_ptr<utils::time::FromTillDate> fromTillDate) -> bool {
            return timeWidget->saveData(fromTillDate);
        };

        passDefaultCallbacksFromListItem(this, vBox);
    }

    void SettingsTimeItem::setConnectionToSecondItem(SettingsTimeItem *item)
    {
        timeWidget->setConnectionToSecondItem(item->timeWidget);
    }

    void SettingsTimeItem::setConnectionToDateItem(SettingsDateItem *item)
    {
        timeWidget->setConnectionToDateItem(item->getDateWidget());
    }
} /* namespace gui */

A module-apps/application-settings-new/widgets/SettingsTimeItem.hpp => module-apps/application-settings-new/widgets/SettingsTimeItem.hpp +27 -0
@@ 0,0 1,27 @@
// 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 <widgets/DateOrTimeListItem.hpp>
#include <widgets/TimeWidget.hpp>

namespace gui
{
    class SettingsDateItem;
    class SettingsTimeItem : public DateOrTimeListItem
    {
      public:
        SettingsTimeItem(const std::string &description,
                         TimeWidget::Type type,
                         const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                         const std::function<void()> &bottomBarRestoreFromTemporaryMode);

        void setConnectionToSecondItem(SettingsTimeItem *item);
        void setConnectionToDateItem(SettingsDateItem *item);

      private:
        TimeWidget *timeWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

D module-apps/application-settings-new/widgets/timeWidget.cpp => module-apps/application-settings-new/widgets/timeWidget.cpp +0 -265
@@ 1,265 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "timeWidget.hpp"
#include "SettingsStyle.hpp"

#include <Style.hpp>
#include <i18n/i18n.hpp>

namespace gui
{
    TimeWidget::TimeWidget(Item *parent,
                           const std::string &description,
                           std::function<void(const UTF8 &text)> bottomBarTemporaryMode,
                           std::function<void()> bottomBarRestoreFromTemporaryMode)
        : VBox(parent, 0, 0, style::window::default_body_width, style::settings::widget::time::h),
          bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
          bottomBarRestoreFromTemporaryMode(std::move(bottomBarRestoreFromTemporaryMode))
    {
        setEdges(RectangleEdge::None);

        descriptionLabel = new gui::Label(
            this, 0, 0, style::window::default_body_width, style::settings::widget::time::description_label_h);
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        descriptionLabel->setFont(style::window::font::small);
        descriptionLabel->activeItem = false;
        descriptionLabel->setText(description);

        hBox = new gui::HBox(this, 0, 0, style::window::default_body_width, style::settings::widget::time::body_h);
        hBox->setEdges(gui::RectangleEdge::None);
        hBox->activeItem = false;

        hourInput =
            new gui::Text(hBox, 0, 0, style::settings::widget::time::input_w, style::settings::widget::time::body_h);
        hourInput->setEdges(gui::RectangleEdge::Bottom);
        hourInput->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        hourInput->setFont(style::window::font::largelight);
        hourInput->setInputMode(new InputMode({InputMode::digit}));
        hourInput->setPenFocusWidth(style::window::default_border_focus_w);
        hourInput->setPenWidth(style::window::default_border_rect_no_focus);
        hourInput->setEditMode(gui::EditMode::Edit);

        colonLabel = new gui::Label(
            hBox, 0, 0, style::settings::widget::time::separator_w, style::settings::widget::time::body_h);
        colonLabel->setEdges(gui::RectangleEdge::None);
        colonLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        colonLabel->setFont(style::window::font::medium);
        colonLabel->setText(":");
        colonLabel->activeItem = false;

        minuteInput =
            new gui::Text(hBox, 0, 0, style::settings::widget::time::input_w, style::settings::widget::time::body_h);
        minuteInput->setEdges(gui::RectangleEdge::Bottom);
        minuteInput->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        minuteInput->setFont(style::window::font::largelight);
        minuteInput->setInputMode(new InputMode({InputMode::digit}));
        minuteInput->setPenFocusWidth(style::window::default_border_focus_w);
        minuteInput->setPenWidth(style::window::default_border_rect_no_focus);
        minuteInput->setEditMode(gui::EditMode::Edit);

        separatorLabel = new gui::Label(
            hBox, 0, 0, style::settings::widget::time::separator_w, style::settings::widget::time::body_h);
        separatorLabel->setEdges(gui::RectangleEdge::None);
        separatorLabel->activeItem = false;

        meridiemInput =
            new gui::Label(hBox, 0, 0, style::settings::widget::time::input_w, style::settings::widget::time::body_h);
        meridiemInput->setEdges(gui::RectangleEdge::Bottom);
        meridiemInput->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        meridiemInput->setFont(style::window::font::largelight);
        meridiemInput->setPenFocusWidth(style::window::default_border_focus_w);
        meridiemInput->setPenWidth(style::window::default_border_rect_no_focus);

        applyFocusCallbacks();
        applyInputCallbacks();
        setTime(std::chrono::minutes(style::settings::widget::time::default_time_in_minutes));
    }

    auto TimeWidget::getTime() const noexcept -> std::chrono::minutes
    {
        std::chrono::minutes currentTimeDuration;
        int currentHourRaw;
        int currentMinutesRaw;

        try {
            currentHourRaw = std::stoi(hourInput->getText());
        }
        catch (const std::exception &e) {
            LOG_ERROR("TimeWidget::getTime::currentHourRaw %s", e.what());
            currentHourRaw = 0;
        }
        if (meridiemInput->getText() == style::settings::widget::time::after_noon) {
            currentHourRaw += style::settings::widget::time::max_hour;
        }
        try {
            currentMinutesRaw = std::stoi(minuteInput->getText());
        }
        catch (const std::exception &e) {
            LOG_ERROR("TimeWidget::getTime::currentMinutesRaw %s", e.what());
            currentMinutesRaw = 0;
        }
        currentTimeDuration = std::chrono::hours(currentHourRaw);
        currentTimeDuration += std::chrono::minutes(currentMinutesRaw);

        return currentTimeDuration;
    }

    void TimeWidget::setTime(std::chrono::minutes newTimeMinutes)
    {
        auto isPm = false;

        auto newTimeHours = std::chrono::duration_cast<std::chrono::hours>(newTimeMinutes);
        newTimeMinutes -= std::chrono::duration_cast<std::chrono::minutes>(newTimeHours);

        auto newHourRaw   = newTimeHours.count();
        auto newMinuteRaw = newTimeMinutes.count();
        if (newHourRaw > style::settings::widget::time::max_hour) {
            newHourRaw -= style::settings::widget::time::max_hour;
            isPm = true;
        }

        hourInput->setText(std::to_string(newHourRaw));
        minuteInput->setText(std::to_string(newMinuteRaw));
        if (isPm) {
            meridiemInput->setText(style::settings::widget::time::after_noon);
        }
        else {
            meridiemInput->setText(style::settings::widget::time::before_noon);
        }
    }

    void TimeWidget::applyFocusCallbacks()
    {
        focusChangedCallback = [&](Item &item) {
            setFocusItem(focus ? hBox : nullptr);
            return true;
        };

        hourInput->focusChangedCallback = [&](Item &item) {
            if (!item.focus) {
                if (hourInput->isEmpty()) {
                    hourInput->setText(style::settings::widget::time::default_input_string);
                }
            }
            return true;
        };

        minuteInput->focusChangedCallback = [&](Item &item) {
            if (!item.focus) {
                if (minuteInput->isEmpty()) {
                    minuteInput->setText(style::settings::widget::time::default_input_string);
                }
            }
            return true;
        };

        meridiemInput->focusChangedCallback = [&](Item &item) {
            if (item.focus) {
                bottomBarTemporaryMode(utils::localize.get("common_switch"));
            }
            else {
                bottomBarRestoreFromTemporaryMode();
            }
            return true;
        };
    }

    void TimeWidget::applyInputCallbacks()
    {
        inputCallback = [&](Item &item, const InputEvent &event) {
            auto focusedItem = getFocusItem();
            if (!event.isShortPress()) {
                return false;
            }
            if (event.is(gui::KeyCode::KEY_ENTER) || event.is(gui::KeyCode::KEY_RF)) {
                return false;
            }

            if (focusedItem->onInput(event)) {
                return readTimeFromInputFields();
            }
            else if (hBox->onInput(event)) {
                return true;
            }

            return false;
        };

        auto isMoveDeleteSaveEvent = [](auto inputEvent) {
            return inputEvent.is(gui::KeyCode::KEY_LEFT) || inputEvent.is(gui::KeyCode::KEY_RIGHT) ||
                   inputEvent.is(gui::KeyCode::KEY_PND) || inputEvent.is(gui::KeyCode::KEY_UP) ||
                   inputEvent.is(gui::KeyCode::KEY_DOWN) || inputEvent.is(gui::KeyCode::KEY_RF) ||
                   inputEvent.is(gui::KeyCode::KEY_ENTER);
        };

        hourInput->inputCallback = [&](Item &item, const InputEvent &event) {
            if (!event.isShortPress()) {
                return false;
            }
            if (hourInput->getText().length() > 1 && !isMoveDeleteSaveEvent(event)) {
                return true;
            }
            return false;
        };

        minuteInput->inputCallback = [&](Item &item, const InputEvent &event) {
            if (!event.isShortPress()) {
                return false;
            }
            if (minuteInput->getText().length() > 1 && !isMoveDeleteSaveEvent(event)) {
                return true;
            }
            return false;
        };

        meridiemInput->inputCallback = [&](Item &item, const InputEvent &event) {
            if (!event.isShortPress()) {
                return false;
            }
            if (event.is(gui::KeyCode::KEY_LF)) {
                if (meridiemInput->getText() == style::settings::widget::time::before_noon) {
                    meridiemInput->setText(style::settings::widget::time::after_noon);
                }
                else {
                    meridiemInput->setText(style::settings::widget::time::before_noon);
                }
                return true;
            }
            return false;
        };
    }

    auto TimeWidget::readTimeFromInputFields() const -> bool
    {
        uint32_t hours;
        uint32_t minutes;

        try {
            hours = std::stoi(hourInput->getText().c_str());
        }
        catch (const std::exception &e) {
            LOG_ERROR("EventTimeItem::applyInputCallbacks hours: %s", e.what());
            return false;
        }

        try {
            minutes = std::stoi(minuteInput->getText().c_str());
        }
        catch (const std::exception &e) {
            LOG_ERROR("EventTimeItem::applyInputCallbacks minutes: %s", e.what());
            return false;
        }

        if (hours > style::settings::widget::time::max_hour) {
            hourInput->setText(style::settings::widget::time::max_hour_string);
        }
        if (minutes > style::settings::widget::time::max_minutes) {
            minuteInput->setText(style::settings::widget::time::max_minutes_string);
        }

        return true;
    }
} /* namespace gui */

D module-apps/application-settings-new/widgets/timeWidget.hpp => module-apps/application-settings-new/widgets/timeWidget.hpp +0 -40
@@ 1,40 0,0 @@
// 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 <Label.hpp>
#include <Text.hpp>
#include <chrono>

namespace gui
{
    class TimeWidget : public VBox
    {
      public:
        TimeWidget(Item *parent,
                   const std::string &description,
                   std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                   std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);

        [[nodiscard]] auto getTime() const noexcept -> std::chrono::minutes;
        void setTime(std::chrono::minutes newTime);

      private:
        gui::HBox *hBox         = nullptr;
        Label *descriptionLabel = nullptr;
        Text *hourInput         = nullptr;
        Label *colonLabel       = nullptr;
        Text *minuteInput       = nullptr;
        Label *separatorLabel   = nullptr;
        Label *meridiemInput    = nullptr;

        std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
        std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr;

        void applyFocusCallbacks();
        void applyInputCallbacks();
        [[nodiscard]] auto readTimeFromInputFields() const -> bool;
    };

} /* namespace gui */

A module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp => module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.cpp +75 -0
@@ 0,0 1,75 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ChangeDateAndTimeWindow.hpp"
#include <application-settings-new/ApplicationSettings.hpp>
#include <gui/input/InputEvent.hpp>
#include <ListView.hpp>
#include <service-evtmgr/service-evtmgr/Constants.hpp>
#include <service-evtmgr/service-evtmgr/EVMessages.hpp>
#include <widgets/DateAndTimeStyle.hpp>

namespace gui
{
    ChangeDateAndTimeWindow::ChangeDateAndTimeWindow(app::Application *app)
        : AppWindow(app, gui::window::name::change_date_and_time), dateAndTimeModel{std::make_shared<DateAndTimeModel>(
                                                                       this->application)}
    {
        buildInterface();
    }

    void ChangeDateAndTimeWindow::buildInterface()
    {
        AppWindow::buildInterface();
        setTitle(utils::localize.get("app_settings_date_and_time"));

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));

        list = new gui::ListView(this,
                                 style::window::date_and_time::listView_x,
                                 style::window::date_and_time::listView_y,
                                 style::window::date_and_time::listView_w,
                                 style::window::date_and_time::listView_h,
                                 dateAndTimeModel,
                                 style::listview::ScrollBarType::PreRendered);
        setFocusItem(list);

        fromTillDate       = std::make_shared<utils::time::FromTillDate>();
        fromTillDate->from = TimePointNow();
        dateAndTimeModel->loadData(fromTillDate);
    }

    bool ChangeDateAndTimeWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (!inputEvent.isShortPress()) {
            return false;
        }

        if (inputEvent.keyCode == gui::KeyCode::KEY_ENTER) {
            dateAndTimeModel->saveData(fromTillDate);
            sendRtcUpdateTimeMessage();
            return true;
        }

        return false;
    }

    void ChangeDateAndTimeWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    void ChangeDateAndTimeWindow::sendRtcUpdateTimeMessage()
    {
        auto msg = std::make_shared<sevm::RtcUpdateTimeMessage>(TimePointToTimeT(fromTillDate->from));
        application->bus.sendUnicast(std::move(msg), service::name::evt_manager);
    }
} /* namespace gui */

A module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.hpp => module-apps/application-settings-new/windows/ChangeDateAndTimeWindow.hpp +28 -0
@@ 0,0 1,28 @@
// 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 <AppWindow.hpp>
#include "application-settings-new/models/DateAndTimeModel.hpp"

namespace gui
{
    class ListView;
    class ChangeDateAndTimeWindow : public AppWindow
    {
      public:
        explicit ChangeDateAndTimeWindow(app::Application *app);

        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;
        void rebuild() override;

      private:
        void sendRtcUpdateTimeMessage();

        std::shared_ptr<DateAndTimeModel> dateAndTimeModel;
        std::shared_ptr<utils::time::FromTillDate> fromTillDate;
        ListView *list = nullptr;
    };
} /* namespace gui */

M module-apps/application-settings-new/windows/NightshiftWindow.cpp => module-apps/application-settings-new/windows/NightshiftWindow.cpp +24 -37
@@ 1,13 1,18 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NightshiftWindow.hpp"
#include "application-settings-new/ApplicationSettings.hpp"
#include <module-apps/application-settings-new/widgets/SettingsStyle.hpp>
#include <application-settings-new/ApplicationSettings.hpp>
#include <application-settings-new/widgets/SettingsStyle.hpp>
#include <gui/input/InputEvent.hpp>
#include <ListView.hpp>
#include <widgets/DateAndTimeStyle.hpp>

namespace gui
{
    NightshiftWindow::NightshiftWindow(app::Application *app) : AppWindow(app, gui::window::name::nightshift)
    NightshiftWindow::NightshiftWindow(app::Application *app)
        : AppWindow(app, gui::window::name::nightshift), timeModel{
                                                             std::make_shared<FromTimeToTimeModel>(this->application)}
    {
        buildInterface();
    }


@@ 15,42 20,26 @@ namespace gui
    void NightshiftWindow::buildInterface()
    {
        AppWindow::buildInterface();

        setTitle(utils::localize.get("app_settings_title_nightshift"));

        body = new gui::VBox(this,
                             style::window::default_left_margin,
                             style::settings::window::nightshift::body_offset,
                             style::window::default_body_width,
                             style::window::default_body_height);
        body->setEdges(gui::RectangleEdge::None);

        fromTimeWidget = new TimeWidget(
            body,
            utils::localize.get("app_settings_nightshift_from"),
            [this](const UTF8 &text) { bottomBarTemporaryMode(text, false); },
            [this]() { bottomBarRestoreFromTemporaryMode(); });
        fromTimeWidget->activeItem = true;

        separatorLabel = new gui::Label(
            body, 0, 0, style::window::default_body_width, style::settings::window::nightshift::separator_h);
        separatorLabel->setEdges(gui::RectangleEdge::None);
        separatorLabel->activeItem = false;

        toTimeWidget = new TimeWidget(
            body,
            utils::localize.get("app_settings_nightshift_to"),
            [this](const UTF8 &text) { bottomBarTemporaryMode(text, false); },
            [this]() { bottomBarRestoreFromTemporaryMode(); });
        toTimeWidget->activeItem = true;

        bottomBar->setActive(BottomBar::Side::LEFT, false);
        bottomBar->setActive(BottomBar::Side::CENTER, true);
        bottomBar->setActive(BottomBar::Side::RIGHT, true);
        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));

        setFocusItem(fromTimeWidget);
        list = new gui::ListView(this,
                                 style::window::date_and_time::listView_x,
                                 style::window::date_and_time::listView_y,
                                 style::window::date_and_time::listView_w,
                                 style::window::date_and_time::listView_h,
                                 timeModel,
                                 style::listview::ScrollBarType::PreRendered);
        setFocusItem(list);

        fromTillDate       = std::make_shared<utils::time::FromTillDate>();
        fromTillDate->from = TimePointNow();
        timeModel->loadData(fromTillDate);
    }

    auto NightshiftWindow::onInput(const InputEvent &inputEvent) -> bool


@@ 64,11 53,9 @@ namespace gui
        }

        if (inputEvent.is(gui::KeyCode::KEY_ENTER)) {
            auto timeFrom = fromTimeWidget->getTime();
            LOG_DEBUG("timeFrom in minutes: %d", static_cast<int>(timeFrom.count()));
            auto timeTo = toTimeWidget->getTime();
            LOG_DEBUG("timeTo in minutes: %d", static_cast<int>(timeTo.count()));
            application->switchWindow(gui::window::name::torch);
            timeModel->saveData(fromTillDate);
            LOG_DEBUG("Date from: %s", TimePointToLocalizedHourMinString(fromTillDate->from).c_str());
            LOG_DEBUG("Date to: %s", TimePointToLocalizedHourMinString(fromTillDate->till).c_str());
            return true;
        }


M module-apps/application-settings-new/windows/NightshiftWindow.hpp => module-apps/application-settings-new/windows/NightshiftWindow.hpp +8 -7
@@ 1,13 1,15 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "AppWindow.hpp"
#include "application-settings-new/widgets/timeWidget.hpp"
#include <application-settings-new/models/FromTimeToTimeModel.hpp>

namespace gui
{
    class ListView;
    class VBox;
    class NightshiftWindow : public AppWindow
    {
      public:


@@ 17,10 19,9 @@ namespace gui
        void buildInterface() override;
        auto onInput(const InputEvent &inputEvent) -> bool override;

        VBox *body                 = nullptr;
        TimeWidget *fromTimeWidget = nullptr;
        TimeWidget *toTimeWidget   = nullptr;
        Label *separatorLabel      = nullptr;
        VBox *body = nullptr;
        std::shared_ptr<utils::time::FromTillDate> fromTillDate;
        gui::ListView *list = nullptr;
        std::shared_ptr<FromTimeToTimeModel> timeModel;
    };

} /* namespace gui */

M module-apps/application-settings-new/windows/TorchWindow.cpp => module-apps/application-settings-new/windows/TorchWindow.cpp +1 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "TorchWindow.hpp"


@@ 19,7 19,6 @@ namespace gui

    void TorchWindow::buildInterface()
    {
        BaseSettingsWindow::buildInterface();
        setTitle(utils::translateI18("app_settings_title_torch"));
        optionsList->setSize(optionsList->getWidth(),
                             optionsList->getHeight() - style::settings::window::torch::body_offset);

M module-apps/application-settings/windows/DateTimeWindow.cpp => module-apps/application-settings/windows/DateTimeWindow.cpp +6 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*


@@ 26,6 26,7 @@
#include <Style.hpp>
#include <widgets/BoxLayout.hpp>

#include <module-utils/time/DateAndTimeSettings.hpp>
#include <module-utils/Utils.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>



@@ 116,7 117,7 @@ namespace gui

        auto hourValue = time.get_date_time_sub_value(utils::time::GetParameters::Hour);

        if (application->isTimeFormat12()) {
        if (utils::dateAndTimeSettings.isTimeFormat12()) {
            if (hourValue > 12) {
                hourValue -= 12;
                dayPeriod = true;


@@ 139,7 140,7 @@ namespace gui
        timeBody->addWidget(addSpacer(""));

        item = addDateTimeItem(nullptr, (""), (""));
        if (application->isTimeFormat12()) {
        if (utils::dateAndTimeSettings.isTimeFormat12()) {
            if (dayPeriod) {
                item->setText("PM");
            }


@@ 338,7 339,7 @@ namespace gui

            if (utils::time::validateTime(getDateTimeItemValue(DateTimeItems::Hour),
                                          getDateTimeItemValue(DateTimeItems::Minute),
                                          application->isTimeFormat12())) {
                                          utils::dateAndTimeSettings.isTimeFormat12())) {
                application->refreshWindow(RefreshModes::GUI_REFRESH_FAST);
            }
            else {


@@ 361,7 362,7 @@ namespace gui
            timeinfo.tm_mday = std::stoi(getDateTimeItemValue(DateTimeItems::Day));

            auto hourValue = std::stoi(getDateTimeItemValue(DateTimeItems::Hour));
            if (application->isTimeFormat12()) {
            if (utils::dateAndTimeSettings.isTimeFormat12()) {
                if (dayPeriod) {
                    hourValue += 12;
                }

A module-apps/widgets/DateAndTimeStyle.hpp => module-apps/widgets/DateAndTimeStyle.hpp +19 -0
@@ 0,0 1,19 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

namespace style::window::date_and_time
{
    inline constexpr auto leftMargin       = 10;
    inline constexpr auto height           = 107;
    inline constexpr auto topMargin        = 20;
    inline constexpr auto separator        = 30;
    inline constexpr auto time_input_12h_w = 120;
    inline constexpr auto time_input_24h_w = 195;
    inline constexpr auto hBox_h           = height - 1.25 * topMargin;
    inline constexpr auto listView_x       = style::window::default_left_margin;
    inline constexpr auto listView_y       = style::header::height;
    inline constexpr auto listView_w       = style::listview::body_width_with_scroll;
    inline constexpr auto listView_h       = style::window_height - listView_y - style::footer::height;
} // namespace style::window::date_and_time

A module-apps/widgets/DateOrTimeListItem.hpp => module-apps/widgets/DateOrTimeListItem.hpp +12 -0
@@ 0,0 1,12 @@
// 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 <module-utils/time/FromTillDate.hpp>
#include <module-gui/gui/widgets/ListItem.hpp>

namespace gui
{
    using DateOrTimeListItem = ListItemWithCallbacks<utils::time::FromTillDate>;
} /* namespace gui */

R module-apps/application-calendar/widgets/EventDateItem.cpp => module-apps/widgets/DateWidget.cpp +54 -54
@@ 1,90 1,90 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventDateItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#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 <application-calendar/data/dateCommon.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    namespace dateItem = style::window::calendar::item::eventTime;
    EventDateItem::EventDateItem()
    DateWidget::DateWidget(Item *parent) : VBox(parent)
    {
        setMinimumSize(style::window::default_body_width, dateItem::height);
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(style::window::calendar::leftMargin, dateItem::margin, 0, 0));

        buildInterface();
        applyCallbacks();
    }

    void EventDateItem::buildInterface()
    void DateWidget::buildInterface()
    {
        vBox = new gui::VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);
        vBox->activeItem = false;

        labelsHBox = new gui::HBox(vBox, 0, 0, 0, 0);
        labelsHBox->setMinimumSize(style::window::default_body_width, dateItem::margin);
        labelsHBox->setMargins(gui::Margins(0, 0, 0, dateItem::margin / 4));
        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 gui::Label(labelsHBox, 0, 0, 0, 0);
        dayLabel = new Label(labelsHBox, 0, 0, 0, 0);
        applyLabelSpecificProperties(dayLabel);
        dayLabel->setText(utils::localize.get("app_settings_title_day"));

        monthLabel = new gui::Label(labelsHBox, 0, 0, 0, 0);
        monthLabel = new Label(labelsHBox, 0, 0, 0, 0);
        applyLabelSpecificProperties(monthLabel);
        monthLabel->setMargins(gui::Margins(dateItem::separator, 0, 0, 0));
        monthLabel->setMargins(Margins(date_and_time::separator, 0, 0, 0));
        monthLabel->setText(utils::localize.get("app_settings_title_month"));

        yearLabel = new gui::Label(labelsHBox, 0, 0, 0, 0);
        yearLabel = new Label(labelsHBox, 0, 0, 0, 0);
        applyLabelSpecificProperties(yearLabel);
        yearLabel->setMargins(gui::Margins(dateItem::separator, 0, 0, 0));
        yearLabel->setMargins(Margins(date_and_time::separator, 0, 0, 0));
        yearLabel->setText(utils::localize.get("app_settings_title_year"));

        dateHBox = new gui::HBox(vBox, 0, 0, 0, 0);
        dateHBox->setMinimumSize(style::window::default_body_width, dateItem::hBox_h);
        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 gui::Label(dateHBox, 0, 0, 0, 0);
        dayInput = new Label(dateHBox, 0, 0, 0, 0);

        monthInput = new gui::Label(dateHBox, 0, 0, 0, 0);
        monthInput->setMargins(gui::Margins(dateItem::separator, 0, 0, 0));
        monthInput = new Label(dateHBox, 0, 0, 0, 0);
        monthInput->setMargins(Margins(date_and_time::separator, 0, 0, 0));

        yearInput = new gui::Label(dateHBox, 0, 0, 0, 0);
        yearInput->setMargins(gui::Margins(dateItem::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 EventDateItem::applyItemSpecificProperties(gui::Label *item)
    void DateWidget::applyItemSpecificProperties(Label *item)
    {
        item->setMinimumSize(dateItem::time_input_12h_w, dateItem::hBox_h);
        item->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
        item->setEdges(RectangleEdge::Bottom);
        item->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        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 EventDateItem::applyLabelSpecificProperties(gui::Label *label)
    void DateWidget::applyLabelSpecificProperties(Label *label)
    {
        label->setMinimumSize(dateItem::time_input_12h_w, dateItem::margin);
        label->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::topMargin);
        label->setEdges(RectangleEdge::None);
        label->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        label->setAlignment(Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Center));
        label->setFont(style::window::font::small);
        label->activeItem = false;
    }

    void EventDateItem::applyCallbacks()
    void DateWidget::applyCallbacks()
    {
        focusChangedCallback = [&]([[maybe_unused]] Item &item) {
            setFocusItem(focus ? dateHBox : nullptr);


@@ 93,10 93,10 @@ namespace gui

        inputCallback = [&](Item &item, const InputEvent &event) {
            auto focusedItem = getFocusItem();
            if (event.state != gui::InputEvent::State::keyReleasedShort) {
            if (event.state != InputEvent::State::keyReleasedShort) {
                return false;
            }
            if (event.keyCode == gui::KeyCode::KEY_ENTER || event.keyCode == gui::KeyCode::KEY_RF) {
            if (event.keyCode == KeyCode::KEY_ENTER || event.keyCode == KeyCode::KEY_RF) {
                return false;
            }
            if (focusedItem->onInput(event)) {


@@ 122,13 122,6 @@ namespace gui
            return true;
        };

        onLoadCallback = [&](std::shared_ptr<EventsRecord> record) {
            auto date = TimePointToYearMonthDay(record->date_from);
            dayInput->setText(std::to_string(static_cast<unsigned>(date.day())));
            monthInput->setText(std::to_string(static_cast<unsigned>(date.month())));
            yearInput->setText(std::to_string(static_cast<int>(date.year())));
        };

        setOnInputCallback(*dayInput);
        setOnInputCallback(*monthInput);
        setOnInputCallback(*yearInput);


@@ 139,7 132,7 @@ namespace gui
        };
    }

    calendar::YearMonthDay EventDateItem::validateDate()
    date::year_month_day DateWidget::validateDate()
    {
        auto actualDate = TimePointToYearMonthDay(TimePointNow());
        uint32_t day;


@@ 164,18 157,18 @@ namespace gui
            year = static_cast<int>(actualDate.year());
        }

        if (year > style::window::calendar::time::max_years) {
            yearInput->setText(std::to_string(style::window::calendar::time::max_years));
        if (year > utils::time::Locale::max_years) {
            yearInput->setText(std::to_string(utils::time::Locale::max_years));
        }

        year = std::clamp(year, style::window::calendar::time::min_years, style::window::calendar::time::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));

        calendar::YearMonthDayLast max_date = date::year(year) / date::month(month) / date::last;
        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())));
        }


@@ 184,12 177,12 @@ namespace gui
        return date::year(year) / date::month(month) / date::day(day);
    }

    const calendar::YearMonthDay EventDateItem::getChosenDate()
    const date::year_month_day DateWidget::getChosenDate()
    {
        return validateDate();
    }

    void EventDateItem::setDate(int keyValue, gui::Label &item)
    void DateWidget::setDate(int keyValue, Label &item)
    {
        auto itemValue = item.getText();
        auto key       = std::to_string(keyValue);


@@ 206,17 199,17 @@ namespace gui
        }
    }

    void EventDateItem::setOnInputCallback(gui::Label &dateInput)
    void DateWidget::setOnInputCallback(Label &dateInput)
    {
        dateInput.inputCallback = [&](Item &item, const InputEvent &event) {
            if (event.state != gui::InputEvent::State::keyReleasedShort) {
            if (event.state != InputEvent::State::keyReleasedShort) {
                return false;
            }
            if (auto value = gui::toNumeric(event.keyCode); value >= 0) {
            if (auto value = toNumeric(event.keyCode); value >= 0) {
                setDate(value, dateInput);
                return true;
            }
            else if (event.is(gui::KeyCode::KEY_PND)) {
            else if (event.is(KeyCode::KEY_PND)) {
                clearInput(dateInput);
                return true;
            }


@@ 224,7 217,7 @@ namespace gui
        };
    }

    void EventDateItem::clearInput(gui::Label &dateInput)
    void DateWidget::clearInput(Label &dateInput)
    {
        auto value = dateInput.getText();
        if (auto length = value.length(); length > 0) {


@@ 233,4 226,11 @@ namespace gui
        }
    }

    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 */

A module-apps/widgets/DateWidget.hpp => module-apps/widgets/DateWidget.hpp +41 -0
@@ 0,0 1,41 @@
// 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 <BoxLayout.hpp>
#include <Label.hpp>
#include <module-utils/date/include/date/date.h>

namespace gui
{
    class DateWidget : public VBox
    {
      public:
        DateWidget(Item *parent);
        void loadData(const date::year_month_day &yearMonthDay);

        const date::year_month_day getChosenDate();

      private:
        VBox *vBox        = nullptr;
        HBox *labelsHBox  = nullptr;
        HBox *dateHBox    = nullptr;
        Label *dayLabel   = nullptr;
        Label *monthLabel = nullptr;
        Label *yearLabel  = nullptr;
        Label *dayInput   = nullptr;
        Label *monthInput = nullptr;
        Label *yearInput  = nullptr;

        void buildInterface();
        void applyItemSpecificProperties(Label *item);
        void applyLabelSpecificProperties(Label *label);
        void applyCallbacks();
        date::year_month_day validateDate();
        void setDate(int keyValue, Label &item);
        void setOnInputCallback(Label &dateInput);
        void clearInput(Label &dateInput);
    };

} /* namespace gui */

R module-apps/application-calendar/widgets/EventTimeItem.cpp => module-apps/widgets/TimeWidget.cpp +213 -202
@@ 1,43 1,41 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventTimeItem.hpp"
#include "TimeWidget.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include <ListView.hpp>
#include <Style.hpp>
#include <time/time_conversion.hpp>
#include <time/time_date_validation.hpp>
#include "DateAndTimeStyle.hpp"
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace gui
{
    namespace timeItem = style::window::calendar::item::eventTime;

    EventTimeItem::EventTimeItem(const std::string &description,
                                 bool mode24H,
                                 std::function<void(const UTF8 &text)> bottomBarTemporaryMode,
                                 std::function<void()> bottomBarRestoreFromTemporaryMode)
        : mode24H{mode24H}, bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
    namespace date_and_time = style::window::date_and_time;

    TimeWidget::TimeWidget(Item *parent,
                           const std::string &description,
                           Type type,
                           std::function<void(const UTF8 &text)> bottomBarTemporaryMode,
                           std::function<void()> bottomBarRestoreFromTemporaryMode)
        : VBox(parent), mode24H{!utils::dateAndTimeSettings.isTimeFormat12()}, type{type},
          bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
          bottomBarRestoreFromTemporaryMode(std::move(bottomBarRestoreFromTemporaryMode))
    {
        setMinimumSize(style::window::default_body_width, timeItem::height);

        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(style::window::calendar::leftMargin, timeItem::margin, 0, 0));

        vBox = new gui::VBox(this, 0, 0, 0, 0);
        vBox->setEdges(gui::RectangleEdge::None);
        vBox->activeItem = false;

        descriptionLabel = new gui::Label(vBox, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::window::default_body_width, timeItem::margin);
        descriptionLabel->setMargins(gui::Margins(0, 0, 0, timeItem::margin / 4));
        descriptionLabel = new gui::Label(this, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::window::default_body_width, date_and_time::topMargin);
        descriptionLabel->setMargins(gui::Margins(0, 0, 0, date_and_time::topMargin / 4));
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        descriptionLabel->setFont(style::window::font::small);
        descriptionLabel->activeItem = false;

        hBox = new gui::HBox(vBox, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, timeItem::hBox_h);
        hBox = new gui::HBox(this, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, date_and_time::hBox_h);
        hBox->setEdges(gui::RectangleEdge::None);
        hBox->activeItem = false;



@@ 49,7 47,7 @@ namespace gui
        hourInput->setPenWidth(style::window::default_border_rect_no_focus);

        colonLabel = new gui::Label(hBox, 0, 0, 0, 0);
        colonLabel->setMinimumSize(timeItem::separator, timeItem::hBox_h);
        colonLabel->setMinimumSize(date_and_time::separator, date_and_time::hBox_h);
        colonLabel->setEdges(gui::RectangleEdge::None);
        colonLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        colonLabel->setFont(style::window::font::medium);


@@ 73,16 71,11 @@ namespace gui
            return true;
        };

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

        applyInputCallbacks();
        prepareForTimeMode();
    }

    void EventTimeItem::applyInputCallbacks()
    void TimeWidget::applyInputCallbacks()
    {
        inputCallback = [&](Item &item, const InputEvent &event) {
            auto focusedItem = getFocusItem();


@@ 94,53 87,52 @@ namespace gui
            }

            if (focusedItem->onInput(event)) {
                uint32_t hours;
                if (secondItem != nullptr) {
                    uint32_t hours;

                try {
                    hours = std::stoi(hourInput->getText().c_str());
                }
                catch (std::exception &e) {
                    LOG_ERROR("EventTimeItem::applyInputCallbacks hours: %s", e.what());
                    return true;
                }
                    try {
                        hours = std::stoi(hourInput->getText().c_str());
                    }
                    catch (std::exception &e) {
                        LOG_ERROR("applyInputCallbacks hours: %s", e.what());
                        return true;
                    }

                auto autofill_hour = hours + 1;
                if (this->descriptionLabel->getText() ==
                        utils::localize.get("app_calendar_new_edit_event_start").c_str() &&
                    !mode24H) {
                    if (mode12hInput->getText() == timeConstants::after_noon) {
                        if (autofill_hour == style::window::calendar::time::max_hour_12H_mode) {
                            autofill_hour = style::window::calendar::time::max_hour_12H_mode - 1;
                            secondItem->minuteInput->setText(
                                std::to_string(style::window::calendar::time::max_minutes));
                    auto autofill_hour = hours + 1;
                    if (type == Type::Start && !mode24H) {
                        if (mode12hInput->getText() == timeConstants::after_noon) {
                            if (autofill_hour == utils::time::Locale::max_hour_12H_mode) {
                                autofill_hour = utils::time::Locale::max_hour_12H_mode - 1;
                                secondItem->minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
                            }
                            else {
                                secondItem->minuteInput->setText(minuteInput->getText());
                            }
                            secondItem->mode12hInput->setText(mode12hInput->getText());
                        }
                        else {
                            if (autofill_hour == utils::time::Locale::max_hour_12H_mode) {
                                secondItem->mode12hInput->setText(timeConstants::after_noon);
                            }
                            secondItem->minuteInput->setText(minuteInput->getText());
                        }
                        secondItem->mode12hInput->setText(mode12hInput->getText());
                    }
                    else {
                        if (autofill_hour == style::window::calendar::time::max_hour_12H_mode) {
                            secondItem->mode12hInput->setText(timeConstants::after_noon);
                        if (autofill_hour > utils::time::Locale::max_hour_12H_mode) {
                            autofill_hour = 1;
                            secondItem->mode12hInput->setText(mode12hInput->getText());
                            secondItem->minuteInput->setText(minuteInput->getText());
                        }
                        secondItem->minuteInput->setText(minuteInput->getText());
                    }
                    if (autofill_hour > style::window::calendar::time::max_hour_12H_mode) {
                        autofill_hour = 1;
                        secondItem->mode12hInput->setText(mode12hInput->getText());
                        secondItem->minuteInput->setText(minuteInput->getText());
                        secondItem->hourInput->setText(std::to_string(autofill_hour));
                    }
                    secondItem->hourInput->setText(std::to_string(autofill_hour));
                }
                else if (this->descriptionLabel->getText() ==
                             utils::localize.get("app_calendar_new_edit_event_start").c_str() &&
                         mode24H) {
                    secondItem->minuteInput->setText(minuteInput->getText());
                    if (autofill_hour > style::window::calendar::time::max_hour_24H_mode) {
                        autofill_hour = style::window::calendar::time::max_hour_24H_mode;
                        secondItem->minuteInput->setText(minuteInput->getText());
                    else if (type == Type::Start && mode24H) {
                        if (secondItem != nullptr) {
                            secondItem->minuteInput->setText(minuteInput->getText());
                            if (autofill_hour > utils::time::Locale::max_hour_24H_mode) {
                                autofill_hour = utils::time::Locale::max_hour_24H_mode;
                                secondItem->minuteInput->setText(minuteInput->getText());
                            }
                            secondItem->hourInput->setText(std::to_string(autofill_hour));
                        }
                    }
                    secondItem->hourInput->setText(std::to_string(autofill_hour));
                }
                return true;
            }


@@ 151,167 143,130 @@ namespace gui
            return false;
        };

        onSaveCallback = [&](std::shared_ptr<EventsRecord> record) {
            validateHour();
            auto hours   = std::chrono::hours(std::stoi(hourInput->getText().c_str()));
            auto minutes = std::chrono::minutes(std::stoi(minuteInput->getText().c_str()));
            if (!mode24H) {
                hours = date::make24(hours, isPm(mode12hInput->getText()));
            }
            if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_end")) {
                record->date_till = calculateEventTime(dateItem->getChosenDate(), hours, minutes);
            }
            else if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_start")) {
                record->date_from = calculateEventTime(dateItem->getChosenDate(), hours, minutes);
            }
        };

        onInputCallback(*hourInput);
        onInputCallback(*minuteInput);
    }

    void EventTimeItem::prepareForTimeMode()
    void TimeWidget::prepareForTimeMode()
    {
        if (!mode24H) {
            mode12hInput = new gui::Label(hBox, 0, 0, 0, 0);
            mode12hInput->setEdges(gui::RectangleEdge::Bottom);
            mode12hInput->setAlignment(
                gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
            mode12hInput->setFont(style::window::font::largelight);
            mode12hInput->setPenFocusWidth(style::window::default_border_focus_w);
            mode12hInput->setPenWidth(style::window::default_border_rect_no_focus);
            mode12hInput->setText(timeConstants::before_noon);
            mode12hInput->inputCallback = [&](Item &item, const InputEvent &event) {
                if (event.state != gui::InputEvent::State::keyReleasedShort) {
                    return false;
                }
                if (event.keyCode == gui::KeyCode::KEY_LF) {
                    if (mode12hInput->getText() == timeConstants::before_noon) {
                        mode12hInput->setText(timeConstants::after_noon);
                    }
                    else {
                        mode12hInput->setText(timeConstants::before_noon);
                    }
                    return true;
                }
            prepareMode12HInputLabel();
            hourInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
            minuteInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
        }
        else {
            hourInput->setMinimumSize(date_and_time::time_input_24h_w, date_and_time::hBox_h);
            minuteInput->setMinimumSize(date_and_time::time_input_24h_w, date_and_time::hBox_h);
        }
    }

    void TimeWidget::prepareMode12HInputLabel()
    {
        mode12hInput = new gui::Label(hBox, 0, 0, 0, 0);
        mode12hInput->setEdges(gui::RectangleEdge::Bottom);
        mode12hInput->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        mode12hInput->setFont(style::window::font::largelight);
        mode12hInput->setPenFocusWidth(style::window::default_border_focus_w);
        mode12hInput->setPenWidth(style::window::default_border_rect_no_focus);
        mode12hInput->setText(timeConstants::before_noon);
        mode12hInput->inputCallback = [&](Item &item, const InputEvent &event) {
            if (event.state != gui::InputEvent::State::keyReleasedShort) {
                return false;
            };
            mode12hInput->focusChangedCallback = [&](Item &item) {
                if (item.focus) {
                    bottomBarTemporaryMode(utils::localize.get("common_switch"));
            }
            if (event.keyCode == gui::KeyCode::KEY_LF) {
                if (mode12hInput->getText() == timeConstants::before_noon) {
                    mode12hInput->setText(timeConstants::after_noon);
                }
                else {
                    bottomBarRestoreFromTemporaryMode();
                    mode12hInput->setText(timeConstants::before_noon);
                }
                return true;
            };

            mode12hInput->setMinimumSize(timeItem::time_input_12h_w, timeItem::hBox_h);
            mode12hInput->setMargins(gui::Margins(timeItem::separator, 0, 0, 0));
            hourInput->setMinimumSize(timeItem::time_input_12h_w, timeItem::hBox_h);
            minuteInput->setMinimumSize(timeItem::time_input_12h_w, timeItem::hBox_h);

            onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
                if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_start")) {
                    auto start_time = TimePointToHourMinSec(event->date_from);

                    hourInput->setText(TimePointToHourString12H(event->date_from));
                    minuteInput->setText(TimePointToMinutesString(event->date_from));

                    if (date::is_am(start_time.hours())) {
                        mode12hInput->setText(timeConstants::before_noon);
                    }
                    else {
                        mode12hInput->setText(timeConstants::after_noon);
                    }
                }
                else if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_end")) {
                    auto end_time = TimePointToHourMinSec(event->date_till);
            }
            return false;
        };
        mode12hInput->focusChangedCallback = [&](Item &item) {
            if (item.focus) {
                bottomBarTemporaryMode(utils::localize.get("common_switch"));
            }
            else {
                bottomBarRestoreFromTemporaryMode();
            }
            return true;
        };

                    hourInput->setText(TimePointToHourString12H(event->date_till));
                    minuteInput->setText(TimePointToMinutesString(event->date_till));
                    if (date::is_am(end_time.hours())) {
                        mode12hInput->setText(timeConstants::before_noon);
                    }
                    else {
                        mode12hInput->setText(timeConstants::after_noon);
                    }
                }
            };
        }
        else {
            hourInput->setMinimumSize(timeItem::time_input_24h_w, timeItem::hBox_h);
            minuteInput->setMinimumSize(timeItem::time_input_24h_w, timeItem::hBox_h);

            onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
                if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_start")) {
                    auto start_time = TimePointToHourMinSec(event->date_from);
                    hourInput->setText(std::to_string(start_time.hours().count()));
                    minuteInput->setText(std::to_string(start_time.minutes().count()));
                }
                else if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_end")) {
                    auto end_time = TimePointToHourMinSec(event->date_till);
                    hourInput->setText(std::to_string(end_time.hours().count()));
                    minuteInput->setText(std::to_string(end_time.minutes().count()));
                }
            };
        }
        mode12hInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
        mode12hInput->setMargins(gui::Margins(date_and_time::separator, 0, 0, 0));
    }

    void EventTimeItem::setConnectionToSecondItem(gui::EventTimeItem *item)
    void TimeWidget::setConnectionToSecondItem(gui::TimeWidget *item)
    {
        this->secondItem = item;
    }

    void EventTimeItem::setConnectionToDateItem(gui::EventDateItem *item)
    void TimeWidget::setConnectionToDateItem(gui::DateWidget *item)
    {
        this->dateItem = item;
    }

    bool EventTimeItem::isPm(const std::string &text)
    bool TimeWidget::isPm(const std::string &text)
    {
        return !(text == timeConstants::before_noon);
    }

    void EventTimeItem::validateHour()
    bool TimeWidget::validateHour()
    {
        if (descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_end")) {
        if (type == Type::End) {
            if (secondItem == nullptr) {
                LOG_ERROR("secondItem not connected!");
                return false;
            }
            std::chrono::hours start_hour;
            std::chrono::hours end_hour;
            uint32_t start_minutes;
            uint32_t end_minutes;
            try {
                start_hour = date::make24(std::chrono::hours(std::stoi(secondItem->hourInput->getText().c_str())),
                                          isPm(secondItem->mode12hInput->getText()));
                auto hours = std::chrono::hours(std::stoi(secondItem->hourInput->getText().c_str()));
                if (!mode24H) {
                    start_hour = date::make24(hours, isPm(secondItem->mode12hInput->getText()));
                }
                else {
                    start_hour = hours;
                }
            }
            catch (std::exception &e) {
                LOG_ERROR("EventTimeItem::validateHour start_hour: %s", e.what());
                start_hour = TimePointToHourMinSec(TimePointNow()).hours();
                LOG_WARN("start_hour: %s", e.what());
                return false;
            }

            try {
                end_hour = date::make24(std::chrono::hours(std::stoi(hourInput->getText().c_str())),
                                        isPm(mode12hInput->getText()));
                auto hours = std::chrono::hours(std::stoi(hourInput->getText().c_str()));
                if (!mode24H) {
                    end_hour = date::make24(hours, isPm(mode12hInput->getText()));
                }
                else {
                    end_hour = hours;
                }
            }
            catch (std::exception &e) {
                LOG_ERROR("EventTimeItem::validateHour end_hour: %s", e.what());
                end_hour = start_hour + std::chrono::hours{1};
                LOG_ERROR("end_hour: %s", e.what());
                return false;
            }

            try {
                start_minutes = std::stoi(secondItem->minuteInput->getText().c_str());
            }
            catch (std::exception &e) {
                LOG_ERROR("EventTimeItem::validateHour start_minutes: %s", e.what());
                start_minutes = TimePointToHourMinSec(TimePointNow()).minutes().count();
                LOG_ERROR("start_minutes: %s", e.what());
                return false;
            }

            try {
                end_minutes = std::stoi(minuteInput->getText().c_str());
            }
            catch (std::exception &e) {
                LOG_ERROR("EventTimeItem::validateHour end_minutes: %s", e.what());
                end_minutes = start_minutes;
                LOG_ERROR("end_minutes: %s", e.what());
                return false;
            }

            if (!mode24H) {


@@ 321,19 276,20 @@ namespace gui
                validateHourFor24hMode(start_hour, end_hour, start_minutes, end_minutes);
            }
        }
        return true;
    }

    void EventTimeItem::validateHourFor12hMode(std::chrono::hours start_hour,
                                               std::chrono::minutes end_hour,
                                               uint32_t start_minutes,
                                               uint32_t end_minutes)
    void TimeWidget::validateHourFor12hMode(std::chrono::hours start_hour,
                                            std::chrono::minutes end_hour,
                                            uint32_t start_minutes,
                                            uint32_t end_minutes)
    {
        if (start_hour > end_hour || (start_hour == end_hour && start_minutes > end_minutes)) {
            auto hour = start_hour.count() + 1;
            if (secondItem->mode12hInput->getText() == timeConstants::after_noon) {
                if (hour == style::window::calendar::time::max_hour_12H_mode) {
                    hour = style::window::calendar::time::max_hour_12H_mode - 1;
                    minuteInput->setText(std::to_string(style::window::calendar::time::max_minutes));
                if (hour == utils::time::Locale::max_hour_12H_mode) {
                    hour = utils::time::Locale::max_hour_12H_mode - 1;
                    minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
                }
                else {
                    minuteInput->setText(secondItem->minuteInput->getText());


@@ 341,12 297,12 @@ namespace gui
                mode12hInput->setText(secondItem->mode12hInput->getText());
            }
            else {
                if (hour == style::window::calendar::time::max_hour_12H_mode) {
                if (hour == utils::time::Locale::max_hour_12H_mode) {
                    mode12hInput->setText(timeConstants::after_noon);
                }
                minuteInput->setText(minuteInput->getText());
            }
            if (hour > style::window::calendar::time::max_hour_12H_mode) {
            if (hour > utils::time::Locale::max_hour_12H_mode) {
                hour = 1;
                mode12hInput->setText(secondItem->mode12hInput->getText());
                minuteInput->setText(secondItem->minuteInput->getText());


@@ 355,16 311,16 @@ namespace gui
        }
    }

    void EventTimeItem::validateHourFor24hMode(std::chrono::hours start_hour,
                                               std::chrono::minutes end_hour,
                                               uint32_t start_minutes,
                                               uint32_t end_minutes)
    void TimeWidget::validateHourFor24hMode(std::chrono::hours start_hour,
                                            std::chrono::minutes end_hour,
                                            uint32_t start_minutes,
                                            uint32_t end_minutes)
    {
        if (start_hour > end_hour || (start_hour == end_hour && start_minutes > end_minutes)) {
            auto hour = start_hour.count() + 1;
            if (hour > style::window::calendar::time::max_hour_24H_mode) {
                hour = style::window::calendar::time::max_hour_24H_mode;
                minuteInput->setText(std::to_string(style::window::calendar::time::max_minutes));
            if (hour > utils::time::Locale::max_hour_24H_mode) {
                hour = utils::time::Locale::max_hour_24H_mode;
                minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
            }
            else {
                minuteInput->setText(secondItem->minuteInput->getText());


@@ 373,14 329,7 @@ namespace gui
        }
    }

    TimePoint EventTimeItem::calculateEventTime(calendar::YearMonthDay date,
                                                std::chrono::hours hours,
                                                std::chrono::minutes minutes)
    {
        return TimePointFromYearMonthDay(date) + hours + minutes;
    }

    void EventTimeItem::setTime(int keyValue, gui::Label &item)
    void TimeWidget::setTime(int keyValue, gui::Label &item)
    {
        auto itemValue = item.getText();
        auto key       = std::to_string(keyValue);


@@ 397,7 346,7 @@ namespace gui
        }
    }

    void EventTimeItem::onInputCallback(gui::Label &timeInput)
    void TimeWidget::onInputCallback(gui::Label &timeInput)
    {
        timeInput.inputCallback = [&](Item &item, const InputEvent &event) {
            if (event.state != gui::InputEvent::State::keyReleasedShort) {


@@ 415,7 364,7 @@ namespace gui
        };
    }

    void EventTimeItem::clearInput(gui::Label &timeInput)
    void TimeWidget::clearInput(gui::Label &timeInput)
    {
        if (auto length = timeInput.getText().length(); length > 0) {
            auto value = timeInput.getText();


@@ 427,4 376,66 @@ namespace gui
        }
    }

    void TimeWidget::loadData(const std::chrono::hours &hoursFrom,
                              const std::chrono::minutes &minutesFrom,
                              const std::chrono::hours &hoursTill,
                              const std::chrono::minutes &minutesTill)
    {
        if (!mode24H) {
            if (type == Type::Start) {
                const auto hours12H = date::make12(hoursFrom);
                hourInput->setText(std::to_string(hours12H.count()));
                minuteInput->setText(std::to_string(minutesFrom.count()));
                if (date::is_am(hoursFrom)) {
                    mode12hInput->setText(timeConstants::before_noon);
                }
                else {
                    mode12hInput->setText(timeConstants::after_noon);
                }
            }
            else if (type == Type::End) {
                const auto hours12H = date::make12(hoursTill);
                hourInput->setText(std::to_string(hours12H.count()));
                minuteInput->setText(std::to_string(minutesTill.count()));
                if (date::is_am(hoursTill)) {
                    mode12hInput->setText(timeConstants::before_noon);
                }
                else {
                    mode12hInput->setText(timeConstants::after_noon);
                }
            }
        }
        else {
            if (type == Type::Start) {
                hourInput->setText(std::to_string(hoursFrom.count()));
                minuteInput->setText(std::to_string(minutesFrom.count()));
            }
            else if (type == Type::End) {
                hourInput->setText(std::to_string(hoursTill.count()));
                minuteInput->setText(std::to_string(minutesTill.count()));
            }
        }
    }

    bool TimeWidget::saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate)
    {
        if (!validateHour()) {
            return false;
        }
        auto hours   = std::chrono::hours(LocalizedHoursToUtcHours(std::stoi(hourInput->getText().c_str())));
        auto minutes = std::chrono::minutes(std::stoi(minuteInput->getText().c_str()));
        if (!mode24H) {
            hours = date::make24(hours, isPm(mode12hInput->getText()));
        }
        auto date = (dateItem != nullptr) ? TimePointFromYearMonthDay(dateItem->getChosenDate())
                                          : TimePointFromYearMonthDay(TimePointToYearMonthDay(TimePointNow()));
        if (type == Type::Start) {
            fromTillDate->from = date + hours + minutes;
        }
        else if (type == Type::End) {
            fromTillDate->till = date + hours + minutes;
        }
        return true;
    }

} /* namespace gui */

R module-apps/application-calendar/widgets/EventTimeItem.hpp => module-apps/widgets/TimeWidget.hpp +44 -31
@@ 2,11 2,12 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CalendarListItem.hpp"
#include "EventDateItem.hpp"

#include <Label.hpp>
#include <Text.hpp>
#include <BoxLayout.hpp>
#include "widgets/DateWidget.hpp"
#include <module-utils/time/FromTillDate.hpp>

namespace gui
{


@@ 15,29 16,55 @@ namespace gui
        inline constexpr auto before_noon = "AM";
        inline constexpr auto after_noon  = "PM";
    } // namespace timeConstants
    class EventTimeItem : public CalendarListItem

    class TimeWidget : public VBox
    {
        gui::VBox *vBox                = nullptr;
        gui::HBox *hBox                = nullptr;
        gui::Label *colonLabel         = nullptr;
        gui::Label *descriptionLabel   = nullptr;
        gui::Label *hourInput          = nullptr;
        gui::Label *minuteInput        = nullptr;
        gui::Label *mode12hInput       = nullptr;
        bool mode24H                   = false;
        gui::EventTimeItem *secondItem = nullptr;
        gui::EventDateItem *dateItem   = nullptr;
      public:
        enum class Type
        {
            Start,
            End
        };

        TimeWidget(Item *parent,
                   const std::string &description,
                   Type type,
                   std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                   std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);
        void loadData(const std::chrono::hours &hoursFrom,
                      const std::chrono::minutes &minutesFrom,
                      const std::chrono::hours &hoursTill,
                      const std::chrono::minutes &minutesTill);
        bool saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate);
        virtual ~TimeWidget() override = default;

        void setConnectionToSecondItem(TimeWidget *item);
        void setConnectionToDateItem(DateWidget *item);

      private:
        VBox *vBox              = nullptr;
        HBox *hBox              = nullptr;
        Label *colonLabel       = nullptr;
        Label *descriptionLabel = nullptr;
        Label *hourInput        = nullptr;
        Label *minuteInput      = nullptr;
        Label *mode12hInput     = nullptr;
        bool mode24H            = false;
        TimeWidget *secondItem  = nullptr;
        DateWidget *dateItem    = nullptr;

        Type type;
        std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
        std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr;

        void applyInputCallbacks();
        void prepareForTimeMode();
        void setTime(int keyValue, gui::Label &item);
        void onInputCallback(gui::Label &timeInput);
        void clearInput(gui::Label &timeInput);
        void prepareMode12HInputLabel();
        void setTime(int keyValue, Label &item);
        void onInputCallback(Label &timeInput);
        void clearInput(Label &timeInput);
        bool isPm(const std::string &text);
        void validateHour();
        bool validateHour();
        void validateHourFor12hMode(std::chrono::hours start_hour,
                                    std::chrono::minutes end_hour,
                                    uint32_t start_minutes,


@@ 46,19 73,5 @@ namespace gui
                                    std::chrono::minutes end_hour,
                                    uint32_t start_minutes,
                                    uint32_t end_minutes);
        TimePoint calculateEventTime(calendar::YearMonthDay date,
                                     std::chrono::hours hours,
                                     std::chrono::minutes minutes);

      public:
        EventTimeItem(const std::string &description,
                      bool mode24H,
                      std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                      std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);
        virtual ~EventTimeItem() override = default;

        void setConnectionToSecondItem(gui::EventTimeItem *item);
        void setConnectionToDateItem(gui::EventDateItem *item);
    };

} /* namespace gui */

A module-apps/widgets/WidgetsUtils.cpp => module-apps/widgets/WidgetsUtils.cpp +31 -0
@@ 0,0 1,31 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "WidgetsUtils.hpp"

#include <module-gui/gui/core/BoundingBox.hpp>
#include <module-gui/gui/input/InputEvent.hpp>
#include <module-gui/gui/widgets/Item.hpp>

namespace gui
{
    void passDefaultCallbacksFromListItem(Item *parent, Item *child)
    {
        parent->focusChangedCallback = [=](Item &) {
            parent->setFocusItem(parent->focus ? child : nullptr);
            return true;
        };

        parent->inputCallback = [=](Item &, const InputEvent &event) {
            if (event.state != InputEvent::State::keyReleasedShort) {
                return false;
            }
            return child->onInput(event);
        };

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

A module-apps/widgets/WidgetsUtils.hpp => module-apps/widgets/WidgetsUtils.hpp +10 -0
@@ 0,0 1,10 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

namespace gui
{
    class Item;
    void passDefaultCallbacksFromListItem(Item *parent, Item *child);
} // namespace gui

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

#include "AppWindow.hpp"


@@ 91,18 91,6 @@ namespace gui
        return topBar->updateNetworkAccessTechnology();
    }

    bool AppWindow::updateTime(const UTF8 &timeStr)
    {
        topBar->setTime(timeStr);
        return true;
    }

    bool AppWindow::updateTime(const uint32_t &timestamp, bool mode24H)
    {
        topBar->setTime(timestamp, mode24H);
        return true;
    }

    void AppWindow::setTitle(const UTF8 &text)
    {
        if (title != nullptr) {

M module-apps/windows/AppWindow.hpp => module-apps/windows/AppWindow.hpp +0 -2
@@ 67,8 67,6 @@ namespace gui
        bool updateBatteryStatus();
        bool updateSignalStrength();
        bool updateNetworkAccessTechnology();
        virtual bool updateTime(const UTF8 &timeStr);
        virtual bool updateTime(const uint32_t &timestamp, bool mode24H);
        void setTitle(const UTF8 &text);

        void rebuild() override;

M module-gui/gui/widgets/ListItem.hpp => module-gui/gui/widgets/ListItem.hpp +2 -2
@@ 22,7 22,7 @@ namespace gui
      public:
        std::function<bool()> onEmptyCallback                          = nullptr;
        std::function<bool()> onContentChangedCallback                 = nullptr;
        std::function<void(std::shared_ptr<T> contact)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<T> contact)> onLoadCallback = nullptr;
        std::function<void(std::shared_ptr<T> record)> onSaveCallback  = nullptr;
        std::function<void(std::shared_ptr<T> record)> onLoadCallback  = nullptr;
    };
} /* namespace gui */

M module-gui/gui/widgets/TopBar.cpp => module-gui/gui/widgets/TopBar.cpp +1 -24
@@ 34,9 34,6 @@ namespace gui::top_bar
    static constexpr uint32_t signalOffset  = 20;
    static constexpr uint32_t batteryOffset = 413;

    TopBar::TimeMode TopBar::timeMode = TimeMode::TIME_24H;
    uint32_t TopBar::time             = 0;

    void Configuration::enable(Indicator indicator)
    {
        set(indicator, true);


@@ 79,9 76,7 @@ namespace gui::top_bar
        setSize(480, 50);
        updateDrawArea();

        preBuildDrawListHook = [this](std::list<Command> &) {
            setTime(time, (timeMode == TimeMode::TIME_24H) ? true : false);
        };
        preBuildDrawListHook = [this](std::list<Command> &) { setTime(utils::time::getHoursMinInCurrentTimeFormat()); };
    }

    void TopBar::prepareWidget()


@@ 209,24 204,6 @@ namespace gui::top_bar
        timeLabel->setText(value);
    }

    void TopBar::setTime(uint32_t value, bool mode24H)
    {
        setTime(utils::time::Time());
        timeMode = (mode24H ? TimeMode::TIME_24H : TimeMode::TIME_12H);
        time     = value;
    }

    UTF8 TopBar::getTimeString()
    {
        setTime(time, (timeMode == TimeMode::TIME_24H) ? true : false);
        return timeLabel->getText();
    }

    uint32_t TopBar::getTime() const noexcept
    {
        return time;
    }

    void TopBar::simSet()
    {
        if (sim == nullptr) {

M module-gui/gui/widgets/TopBar.hpp => module-gui/gui/widgets/TopBar.hpp +0 -10
@@ 72,11 72,6 @@ namespace gui::top_bar
        static constexpr uint32_t signalImgCount   = 6;

      public:
        enum class TimeMode
        {
            TIME_12H,
            TIME_24H
        };
        static uint32_t time;

      protected:


@@ 87,7 82,6 @@ namespace gui::top_bar
        gui::SIM *sim                    = nullptr;
        BatteryWidgetBase *batteryWidget = nullptr;
        Configuration configuration;
        static TimeMode timeMode;

        void prepareWidget();



@@ 126,10 120,6 @@ namespace gui::top_bar
        void simSet();

        void setTime(const UTF8 &value);
        void setTime(uint32_t value, bool mode24H);

        UTF8 getTimeString();
        uint32_t getTime() const noexcept;

        void accept(GuiVisitor &visitor) override;
    };

M module-services/service-evtmgr/CMakeLists.txt => module-services/service-evtmgr/CMakeLists.txt +0 -1
@@ 4,7 4,6 @@ message( "${PROJECT_NAME}  ${CMAKE_CURRENT_LIST_DIR}" )
set(SOURCES
        EventManager.cpp
        WorkerEvent.cpp
        alarm/EventManagerAlarm.cpp
        api/EventManagerServiceAPI.cpp
        messages/Message.cpp
        battery-level-check/BatteryLevelCheck.cpp

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +18 -6
@@ 135,9 135,8 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
        }
    }
    else if (msgl->messageType == MessageType::EVMMinuteUpdated && msgl->sender == this->GetName()) {

        HandleAlarmTrigger(msgl);

        auto msg = static_cast<sevm::RtcMinuteAlarmMessage *>(msgl);
        handleMinuteUpdate(msg->timestamp);
        handled = true;
    }
    else if (auto msg = dynamic_cast<AudioEventRequest *>(msgl); msg /*&& msgl->sender == this->GetName()*/) {


@@ 272,6 271,13 @@ sys::ReturnCodes EventManager::InitHandler()
            screenLightControl->getLightState(), screenLightControl->getAutoModeState(), params);
        return msg;
    });
    connect(sevm::RtcUpdateTimeMessage(0), [&](sys::Message *msgl) {
        auto msg = static_cast<sevm::RtcUpdateTimeMessage *>(msgl);
        bsp::rtc_SetDateTimeFromTimestamp(msg->getTime());
        bsp::rtc_SetMinuteAlarm(msg->getTime());
        handleMinuteUpdate(msg->getTime());
        return app::msgHandled();
    });

    connect(sevm::BatteryStatusChangeMessage(), [&](sys::Message *msgl) {
        if (msgl->sender == this->GetName()) {


@@ 367,6 373,15 @@ bool EventManager::messageSetApplication(sys::Service *sender, const std::string
    return sender->bus.sendUnicast(msg, service::name::evt_manager);
}

void EventManager::handleMinuteUpdate(time_t timestamp)
{
    if (!targetApplication.empty()) {
        auto message       = std::make_shared<sevm::RtcMinuteAlarmMessage>(MessageType::EVMMinuteUpdated);
        message->timestamp = timestamp;
        bus.sendUnicast(message, targetApplication);
    }
}

bool EventManager::processKeypadBacklightRequest(bsp::keypad_backlight::Action act)
{
    bool response = false;


@@ 402,6 417,3 @@ bool EventManager::processVibraRequest(bsp::vibrator::Action act, sys::ms Repeti
    }
    return true;
}

void EventManager::GetNextAlarmTimestamp(time_t timestamp)
{}

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +0 -4
@@ 136,10 136,6 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        bsp::rtc_GetCurrentTimestamp(&timestamp);
        bsp::rtc_SetMinuteAlarm(timestamp);

        struct tm time;

        bsp::rtc_GetCurrentDateTime(&time);

        auto message       = std::make_shared<sevm::RtcMinuteAlarmMessage>(MessageType::EVMMinuteUpdated);
        message->timestamp = timestamp;
        service->bus.sendUnicast(message, service::name::evt_manager);

D module-services/service-evtmgr/alarm/EventManagerAlarm.cpp => module-services/service-evtmgr/alarm/EventManagerAlarm.cpp +0 -64
@@ 1,64 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-evtmgr/EventManager.hpp>
#include <service-evtmgr/EVMessages.hpp>

#include <AlarmsRecord.hpp>
#include <MessageType.hpp>
#include <SwitchData.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-db/DBServiceAPI.hpp>     // for DBServiceAPI

#include <memory>
#include <string>
#include <sys/types.h>
#include <utility>

namespace sys
{
    class DataMessage;
} // namespace sys

void EventManager::HandleAlarmTrigger(sys::DataMessage *msgl)
{
    sevm::RtcMinuteAlarmMessage *msg = reinterpret_cast<sevm::RtcMinuteAlarmMessage *>(msgl);

    // Send message to update time in application
    auto message       = std::make_shared<sevm::RtcMinuteAlarmMessage>(MessageType::EVMMinuteUpdated);
    message->timestamp = msg->timestamp;

    if (targetApplication.empty() == false) {
        bus.sendUnicast(message, targetApplication);
    }

    time_t currentTime = message->timestamp;

    // it there is no valid alarm and DB is not empty
    if (alarmIsValid == false) {
        // check if its midnight
        if (currentTime % 86400 == 0) {
            alarmDBEmpty = false;
        }

        if (alarmDBEmpty == false)
            // get next alarm
            GetNextAlarmTimestamp(currentTime);
    }

    // there is valid alarm
    if (alarmIsValid == true) {
        // check if its time to trigger alarm
        // round time and compare to alaram timestamp
        if (alarmTimestamp == (currentTime % 86400)) {
            alarmIsValid = false;
            // Run "alarm" application.
            // std::unique_ptr<gui::SwitchData> switchMessage = std::make_unique<sevm::EVMAlarmSwitchData>(alarmID);
            // app::manager::Controller::sendAction(this, app::manager::actions::ShowAlarm, std::move(switchMessage));
        }
        // check if alarm is not obsolete
        else if ((alarmTimestamp < (currentTime % 86400)) && ((currentTime + 60) % 86400) > (currentTime % 86400)) {
            alarmIsValid = false;
        }
    }
}

M module-services/service-evtmgr/service-evtmgr/EVMessages.hpp => module-services/service-evtmgr/service-evtmgr/EVMessages.hpp +15 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 43,6 43,20 @@ namespace sevm
        uint32_t timestamp = 0;
    };

    class RtcUpdateTimeMessage : public sys::Message
    {
      public:
        explicit RtcUpdateTimeMessage(time_t time) : time(time)
        {}
        [[nodiscard]] time_t getTime() const noexcept
        {
            return time;
        }

      private:
        const time_t time = 0;
    };

    class SIMMessage : public sys::DataMessage
    {
      public:

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +1 -2
@@ 28,8 28,7 @@ class WorkerEvent;
class EventManager : public sys::Service
{
  private:
    void HandleAlarmTrigger(sys::DataMessage *msgl);
    void GetNextAlarmTimestamp(time_t timestamp);
    void handleMinuteUpdate(time_t timestamp);
    bool processKeypadBacklightRequest(bsp::keypad_backlight::Action act);
    bool processVibraRequest(bsp::vibrator::Action act, sys::ms RepetitionTime = static_cast<sys::ms>(1000));


M module-utils/time/DateAndTimeSettings.hpp => module-utils/time/DateAndTimeSettings.hpp +4 -0
@@ 26,6 26,10 @@ namespace utils
        {
            return dateFormat;
        }
        [[nodiscard]] bool isTimeFormat12() const noexcept
        {
            return timeFormat == utils::time::Locale::TimeFormat::FormatTime12H;
        }
        void setAutomaticDateAndTimeOn(bool value);
        void setAutomaticTimeZoneOn(bool value);
        void setTimeFormat(time::Locale::TimeFormat format);

A module-utils/time/FromTillDate.hpp => module-utils/time/FromTillDate.hpp +21 -0
@@ 0,0 1,21 @@
// 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-calendar/data/dateCommon.hpp>

namespace utils
{
    namespace time
    {
        struct FromTillDate
        {
            FromTillDate() = default;
            explicit FromTillDate(TimePoint from, TimePoint till) : from(std::move(from)), till(std::move(till))
            {}
            TimePoint from;
            TimePoint till;
        };
    } // namespace time
} // namespace utils

M module-utils/time/time_conversion.cpp => module-utils/time/time_conversion.cpp +10 -0
@@ 17,6 17,7 @@
#include "i18n/i18n.hpp"

#include "time_locale.hpp"
#include "DateAndTimeSettings.hpp"
#include <Utils.hpp>

namespace utils::time


@@ 33,6 34,8 @@ namespace utils::time
                                                                 "%b",  // month abbrew
                                                                 "%B",  // month long
                                                                 "%Z"}; // timezone
        constexpr auto hoursMinFormat12H                      = "%I:%M";
        constexpr auto hoursMinFormat24H                      = "%H:%M";

        struct Format
        {


@@ 344,4 347,11 @@ namespace utils::time
    {
        return Timestamp{};
    }

    std::string getHoursMinInCurrentTimeFormat()
    {
        auto timestamp = getCurrentTimestamp();
        return utils::dateAndTimeSettings.isTimeFormat12() ? timestamp.str(hoursMinFormat12H)
                                                           : timestamp.str(hoursMinFormat24H);
    }
}; // namespace utils::time

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +1 -0
@@ 288,5 288,6 @@ namespace utils
        };

        Timestamp getCurrentTimestamp();
        std::string getHoursMinInCurrentTimeFormat();
    } // namespace time
} // namespace utils

M module-utils/time/time_locale.hpp => module-utils/time/time_locale.hpp +6 -0
@@ 61,6 61,12 @@ namespace utils
            const std::string ltimezone  = "common_timezone";

          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;

            enum Day
            {
                Sun = 0,