~aleteoryx/muditaos

123c95f9d46273c2b575a8c32036b3481e3090dd — KacperLewandowski 5 years ago 23533f8
[EGD-3809] Calendar changes after UX redesign (#745)

Add an additional field for date of the event.
Set 'all day' event as a default option when you add a new event.
Change in the design of AllEventsWindow and EventDetailWindow according to UX redesign.
M changelog.md => changelog.md +10 -0
@@ 1,5 1,15 @@
# MuditaOS changelog

## Current release 

## Added

* `[calendar]` Added a new field for date of the event when the user adds/edits event.

## Changed

* `[calendar]` Set 'all day' as a default option

## [0.45.1 2020-11-06]

### Added

A image/assets/images/Rectangle.vpi => image/assets/images/Rectangle.vpi +0 -0
M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +2 -1
@@ 174,7 174,8 @@ namespace app
            eventData->setDescription(style::window::calendar::new_event);
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = dateFilter;
            event->date_till = 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);
            eventData->setData(event);

            switchWindow(

M module-apps/application-calendar/ApplicationCalendar.hpp => module-apps/application-calendar/ApplicationCalendar.hpp +1 -1
@@ 26,7 26,7 @@ namespace app
        ApplicationCalendar(std::string name,
                            std::string parent,
                            StartInBackground startInBackground = {false},
                            uint32_t stackDepth                 = 4096,
                            uint32_t stackDepth                 = 8192,
                            sys::ServicePriority priority       = sys::ServicePriority::Idle);

        sys::Message_t DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;

M module-apps/application-calendar/CMakeLists.txt => module-apps/application-calendar/CMakeLists.txt +1 -0
@@ 25,6 25,7 @@ target_sources( ${PROJECT_NAME}
		"${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"

M module-apps/application-calendar/data/CalendarData.hpp => module-apps/application-calendar/data/CalendarData.hpp +0 -7
@@ 38,13 38,6 @@ class WeekDaysRepeatData : public gui::SwitchData
    virtual void setData(const uint32_t weekDay);
};

class DayEventsWindowMessage : public gui::SwitchData
{
  public:
    std::unique_ptr<std::vector<EventsRecord>> records;
    ~DayEventsWindowMessage() = default;
};

class DayMonthData : public gui::SwitchData
{
  protected:

M module-apps/application-calendar/models/AllEventsModel.cpp => module-apps/application-calendar/models/AllEventsModel.cpp +2 -1
@@ 76,7 76,8 @@ auto AllEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
    if (records.empty()) {

        if (app->getEquivalentToEmptyWindow() == EquivalentWindow::AllEventsWindow) {
            app->switchToNoEventsWindow(utils::localize.get("app_calendar_title_main"));
            auto filter = TimePointNow();
            app->switchToNoEventsWindow(utils::localize.get("app_calendar_title_main"), filter);
        }
    }
    auto eventShift = app->getEventShift();

M module-apps/application-calendar/models/NewEditEventModel.cpp => module-apps/application-calendar/models/NewEditEventModel.cpp +35 -6
@@ 72,6 72,8 @@ void NewEditEventModel::createData(bool allDayEvent)
    allDayEventCheckBox = new gui::NewEventCheckBoxWithLabel(
        application, utils::localize.get("app_calendar_new_edit_event_allday"), true, this);

    dateItem = new gui::EventDateItem();

    startTime = new gui::EventTimeItem(
        utils::localize.get("app_calendar_new_edit_event_start"),
        mode24H,


@@ 99,9 101,13 @@ void NewEditEventModel::createData(bool allDayEvent)
    endTime->setConnectionToSecondItem(startTime);
    startTime->setConnectionToSecondItem(endTime);

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

    internalData.push_back(eventNameInput);
    internalData.push_back(allDayEventCheckBox);
    if (!allDayEvent) {
        internalData.push_back(dateItem);
        internalData.push_back(startTime);
        internalData.push_back(endTime);
    }


@@ 133,6 139,23 @@ void NewEditEventModel::loadData(std::shared_ptr<EventsRecord> record)
        }
    }

    if (isAllDayEvent()) {
        record->date_from = record->date_from - TimePointToHourMinSec(record->date_from).hours() -
                            TimePointToHourMinSec(record->date_from).minutes() +
                            TimePointToHourMinSec(TimePointNow()).hours() +
                            TimePointToHourMinSec(TimePointNow()).minutes();
        record->date_till = record->date_from + std::chrono::hours(1);
        if (dateItem->onLoadCallback) {
            dateItem->onLoadCallback(record);
        }
        if (startTime->onLoadCallback) {
            startTime->onLoadCallback(record);
        }
        if (endTime->onLoadCallback) {
            endTime->onLoadCallback(record);
        }
    }

    list->rebuildList();
}



@@ 145,6 168,7 @@ void NewEditEventModel::loadRepeat(const std::shared_ptr<EventsRecord> &record)

void NewEditEventModel::loadDataWithoutTimeItem()
{
    internalData.erase(std::find(internalData.begin(), internalData.end(), dateItem));
    internalData.erase(std::find(internalData.begin(), internalData.end(), startTime));
    internalData.erase(std::find(internalData.begin(), internalData.end(), endTime));
    list->rebuildList();


@@ 156,6 180,7 @@ void NewEditEventModel::reloadDataWithTimeItem()

    internalData.push_back(eventNameInput);
    internalData.push_back(allDayEventCheckBox);
    internalData.push_back(dateItem);
    internalData.push_back(startTime);
    internalData.push_back(endTime);
    internalData.push_back(reminder);


@@ 192,13 217,14 @@ void NewEditEventModel::saveData(std::shared_ptr<EventsRecord> event, bool edit)
            DBServiceAPI::GetQuery(
                application, db::Interface::Name::Events, std::make_unique<db::query::events::Add>(*record));

            if (application->getPrevWindow() == style::window::calendar::name::no_events_window) {
                auto data      = std::make_unique<DayMonthData>();
                auto startDate = TimePointToYearMonthDay(record->date_from);
                std::string monthStr =
                    utils::time::Locale::get_month(utils::time::Locale::Month(unsigned(startDate.month()) - 1));
                data->setData(std::to_string(unsigned(startDate.day())) + " " + monthStr, record->date_from);
            auto data       = std::make_unique<DayMonthData>();
            auto startDate  = TimePointToYearMonthDay(record->date_from);
            auto filterDate = TimePointFromYearMonthDay(startDate);
            std::string monthStr =
                utils::time::Locale::get_month(utils::time::Locale::Month(unsigned(startDate.month()) - 1));
            data->setData(std::to_string(unsigned(startDate.day())) + " " + monthStr, filterDate);

            if (application->getPrevWindow() == style::window::calendar::name::no_events_window) {
                auto app = dynamic_cast<app::ApplicationCalendar *>(application);
                assert(app != nullptr);
                if (app->getEquivalentToEmptyWindow() == EquivalentWindow::DayEventsWindow) {


@@ 211,6 237,9 @@ void NewEditEventModel::saveData(std::shared_ptr<EventsRecord> event, bool edit)
                }
            }
            else {
                if (application->getPrevWindow() == style::window::calendar::name::day_events_window) {
                    application->switchWindow(style::window::calendar::name::day_events_window, std::move(data));
                }
                application->returnToPreviousWindow();
            }
        }

M module-apps/application-calendar/models/NewEditEventModel.hpp => module-apps/application-calendar/models/NewEditEventModel.hpp +2 -0
@@ 9,6 9,7 @@
#include "application-calendar/widgets/EventTimeItem.hpp"
#include "application-calendar/widgets/SeveralOptionsItem.hpp"
#include "application-calendar/widgets/CheckBoxWithLabelItem.hpp"
#include "application-calendar/widgets/EventDateItem.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>



@@ 19,6 20,7 @@ class NewEditEventModel : public app::InternalModel<gui::CalendarListItem *>, pu

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

M module-apps/application-calendar/widgets/AllEventsItem.cpp => module-apps/application-calendar/widgets/AllEventsItem.cpp +18 -4
@@ 46,10 46,24 @@ namespace gui

    void AllEventsItem::setMarkerItem(UTF8 text)
    {
        description->setText("");
        startTime->setText(text);
        description->setMinimumSize(0, 0);
        startTime->setLineMode(true);
        hBox->erase();

        auto image = new gui::Image();
        image->set("Rectangle");
        image->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        auto dayMonthLabel = new gui::Label();
        dayMonthLabel->setMinimumSize(style::window::calendar::item::allEvents::start_time_min_w,
                                      style::window::label::big_h);
        dayMonthLabel->setMaximumSize(style::window::default_body_width, style::window::label::big_h);
        dayMonthLabel->setEdges(RectangleEdge::None);
        dayMonthLabel->setFont(style::window::font::small);
        dayMonthLabel->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        dayMonthLabel->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        dayMonthLabel->setText(text);

        hBox->addWidget(image);
        hBox->addWidget(dayMonthLabel);
        activeItem = false;
        setEdges(RectangleEdge::None);
    }

M module-apps/application-calendar/widgets/AllEventsItem.hpp => module-apps/application-calendar/widgets/AllEventsItem.hpp +1 -0
@@ 6,6 6,7 @@
#include <Label.hpp>
#include <BoxLayout.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <module-gui/gui/widgets/Image.hpp>

namespace gui
{

M module-apps/application-calendar/widgets/CalendarStyle.hpp => module-apps/application-calendar/widgets/CalendarStyle.hpp +3 -1
@@ 70,6 70,8 @@ namespace style
                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


@@ 92,7 94,7 @@ namespace style
                    inline constexpr auto height             = 150;
                    inline constexpr auto title_label_h      = 45;
                    inline constexpr auto title_label_margin = 15;
                    inline constexpr auto description_w      = style::window::default_body_width / 2 - 30;
                    inline constexpr auto description_w      = style::window::default_body_width / 2 - title_label_h;
                    inline constexpr auto description_h      = 30;
                } // namespace repeatAndReminder


A module-apps/application-calendar/widgets/EventDateItem.cpp => module-apps/application-calendar/widgets/EventDateItem.cpp +202 -0
@@ 0,0 1,202 @@
// 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 <ListView.hpp>
#include <Style.hpp>
#include <time/time_conversion.hpp>
#include <module-utils/date/include/date/date.h>

namespace gui
{

    EventDateItem::EventDateItem()
    {
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::eventTime::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(style::margins::small, style::window::calendar::item::eventTime::margin, 0, 0));

        buildInterface();
        applyCallbacks();
    }

    void EventDateItem::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,
                                   style::window::calendar::item::eventTime::separator);
        labelsHBox->setEdges(RectangleEdge::None);
        labelsHBox->activeItem = false;

        dayLabel = new gui::Label(labelsHBox, 0, 0, 0, 0);
        dayLabel->setMinimumSize(style::window::calendar::item::eventTime::time_input_12h_w,
                                 style::window::calendar::item::eventTime::separator);
        dayLabel->setEdges(RectangleEdge::None);
        dayLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        dayLabel->setFont(style::window::font::small);
        dayLabel->activeItem = false;
        dayLabel->setText(utils::localize.get("app_settings_title_day"));

        monthLabel = new gui::Label(labelsHBox, 0, 0, 0, 0);
        monthLabel->setMinimumSize(style::window::calendar::item::eventTime::time_input_12h_w,
                                   style::window::calendar::item::eventTime::separator);
        monthLabel->setMargins(gui::Margins(style::window::calendar::item::eventTime::separator, 0, 0, 0));
        monthLabel->setEdges(RectangleEdge::None);
        monthLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        monthLabel->setFont(style::window::font::small);
        monthLabel->activeItem = false;
        monthLabel->setText(utils::localize.get("app_settings_title_month"));

        yearLabel = new gui::Label(labelsHBox, 0, 0, 0, 0);
        yearLabel->setMinimumSize(style::window::calendar::item::eventTime::time_input_12h_w,
                                  style::window::calendar::item::eventTime::separator);
        yearLabel->setMargins(gui::Margins(style::window::calendar::item::eventTime::separator, 0, 0, 0));
        yearLabel->setEdges(RectangleEdge::None);
        yearLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        yearLabel->setFont(style::window::font::small);
        yearLabel->activeItem = false;
        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,
                                 style::window::calendar::item::eventTime::height -
                                     style::window::calendar::item::eventTime::separator);
        dateHBox->setEdges(RectangleEdge::None);
        dateHBox->activeItem = false;

        dayInput = new gui::Text(dateHBox, 0, 0, 0, 0);

        monthInput = new gui::Text(dateHBox, 0, 0, 0, 0);
        monthInput->setMargins(gui::Margins(style::window::calendar::item::eventTime::separator, 0, 0, 0));

        yearInput = new gui::Text(dateHBox, 0, 0, 0, 0);
        yearInput->setMargins(gui::Margins(style::window::calendar::item::eventTime::separator, 0, 0, 0));

        applyItemSpecificProperties(dayInput);
        applyItemSpecificProperties(monthInput);
        applyItemSpecificProperties(yearInput);
    }

    void EventDateItem::applyItemSpecificProperties(gui::Text *item)
    {
        item->setMinimumSize(style::window::calendar::item::eventTime::time_input_12h_w,
                             style::window::calendar::item::eventTime::height -
                                 style::window::calendar::item::eventTime::separator);
        item->setEdges(RectangleEdge::Bottom);
        item->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        item->setFont(style::window::font::largelight);
        item->setInputMode(new InputMode({InputMode::digit}));
        item->setPenFocusWidth(style::window::default_border_focus_w);
        item->setPenWidth(style::window::default_border_rect_no_focus);
        item->setEditMode(gui::EditMode::EDIT);
    }

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

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

        yearInput->focusChangedCallback = [&](Item &item) {
            yearInput->setText(std::to_string(static_cast<int>(validateDate().year())));
            return true;
        };
        monthInput->focusChangedCallback = [&](Item &item) {
            monthInput->setText(std::to_string(static_cast<unsigned>(validateDate().month())));
            return true;
        };
        dayInput->focusChangedCallback = [&](Item &item) {
            dayInput->setText(std::to_string(static_cast<unsigned>(validateDate().day())));
            return true;
        };

        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())));
        };
    }

    bool EventDateItem::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);
        return true;
    }

    YearMonthDay EventDateItem::validateDate()
    {
        auto actualDate = TimePointToYearMonthDay(TimePointNow());
        uint32_t day;
        try {
            day = std::stoi(dayInput->getText().c_str());
        }
        catch (std::exception &) {
            day = static_cast<unsigned>(actualDate.day());
        }
        uint32_t month;
        try {
            month = std::stoi(monthInput->getText().c_str());
        }
        catch (std::exception &) {
            month = static_cast<unsigned>(actualDate.month());
        }
        int year;
        try {
            year = std::stoi(yearInput->getText().c_str());
        }
        catch (std::exception &) {
            year = static_cast<int>(actualDate.year());
        }

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

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

        YearMonthDayLast max_date = date::year(year) / date::month(month) / date::last;
        if (day > static_cast<unsigned>(max_date.day())) {
            dayInput->setText(std::to_string(static_cast<unsigned>(max_date.day())));
        }
        day = std::clamp(static_cast<unsigned>(day), 1u, static_cast<unsigned>(max_date.day()));

        return date::year(year) / date::month(month) / date::day(day);
    }

    const YearMonthDay EventDateItem::getChosenDate()
    {
        return validateDate();
    }

} /* namespace gui */

A module-apps/application-calendar/widgets/EventDateItem.hpp => module-apps/application-calendar/widgets/EventDateItem.hpp +37 -0
@@ 0,0 1,37 @@
// 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::Text *dayInput    = nullptr;
        gui::Text *monthInput  = nullptr;
        gui::Text *yearInput   = nullptr;

        void buildInterface();
        void applyItemSpecificProperties(gui::Text *item);
        void applyCallbacks();
        YearMonthDay validateDate();

      public:
        EventDateItem();

        const YearMonthDay getChosenDate();
        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp +13 -3
@@ 21,12 21,22 @@ namespace gui
        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        title = new gui::Label(vBox, 0, 0, 0, 0);
        title->setMinimumSize(style::window::default_body_width, style::window::calendar::item::eventDetail::title_h);
        hBox = new HBox(vBox, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, style::window::calendar::item::eventDetail::title_h);
        hBox->setEdges(RectangleEdge::None);

        squareImage = new gui::Image();
        squareImage->set("Rectangle");
        squareImage->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        hBox->addWidget(squareImage);

        title = new gui::Label(hBox, 0, 0, 0, 0);
        title->setMinimumSize(style::window::default_body_width - 40,
                              style::window::calendar::item::eventDetail::title_h);
        title->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        title->setEdges(RectangleEdge::None);
        title->setFont(style::window::font::small);
        title->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        title->setLineMode(true);
        title->activeItem = false;

        eventTime = new gui::Label(vBox, 0, 0, 0, 0);

M module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp +3 -0
@@ 5,15 5,18 @@
#include "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <module-gui/gui/widgets/Image.hpp>

namespace gui
{
    class EventDetailDescriptionItem : public CalendarListItem
    {
        gui::VBox *vBox        = nullptr;
        gui::HBox *hBox         = nullptr;
        gui::Label *title      = nullptr;
        gui::Label *eventTime  = nullptr;
        gui::Text *description = nullptr;
        gui::Image *squareImage = nullptr;

      public:
        EventDetailDescriptionItem();

M module-apps/application-calendar/widgets/EventTimeItem.cpp => module-apps/application-calendar/widgets/EventTimeItem.cpp +14 -4
@@ 181,12 181,10 @@ namespace gui
                hours = date::make24(hours, isPm(mode12hInput->getText()));
            }
            if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_end")) {
                auto time         = TimePointToHourMinSec(record->date_till);
                record->date_till = record->date_till - time.hours() - time.minutes() + hours + minutes;
                record->date_till = calculateEventTime(dateItem->getChosenDate(), hours, minutes);
            }
            else if (this->descriptionLabel->getText() == utils::localize.get("app_calendar_new_edit_event_start")) {
                auto time         = TimePointToHourMinSec(record->date_from);
                record->date_from = record->date_from - time.hours() - time.minutes() + hours + minutes;
                record->date_from = calculateEventTime(dateItem->getChosenDate(), hours, minutes);
            }
        };



@@ 325,6 323,11 @@ namespace gui
        this->secondItem = item;
    }

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

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


@@ 430,4 433,11 @@ namespace gui
        }
    }

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

} /* namespace gui */

M module-apps/application-calendar/widgets/EventTimeItem.hpp => module-apps/application-calendar/widgets/EventTimeItem.hpp +4 -0
@@ 3,6 3,7 @@

#pragma once
#include "CalendarListItem.hpp"
#include "EventDateItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <BoxLayout.hpp>


@@ 25,6 26,7 @@ namespace gui
        gui::Label *mode12hInput       = nullptr;
        bool mode24H                   = false;
        gui::EventTimeItem *secondItem = nullptr;
        gui::EventDateItem *dateItem   = nullptr;

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


@@ 41,6 43,7 @@ namespace gui
                                    std::chrono::minutes end_hour,
                                    uint32_t start_minutes,
                                    uint32_t end_minutes);
        TimePoint calculateEventTime(YearMonthDay date, std::chrono::hours hours, std::chrono::minutes minutes);

      public:
        EventTimeItem(const std::string &description,


@@ 50,6 53,7 @@ namespace gui
        virtual ~EventTimeItem() override = default;

        void setConnectionToSecondItem(gui::EventTimeItem *item);
        void setConnectionToDateItem(gui::EventDateItem *item);
        // virtual methods from Item
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
    };

M module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp +32 -11
@@ 15,29 15,44 @@ namespace gui
        activeItem = false;
        setEdges(RectangleEdge::None);
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::repeatAndReminder::height);
        setMargins(gui::Margins(style::margins::small, 0, 0, 0));

        std::vector<gui::Image *> squareImages;
        for (int i = 0; i < 2; i++) {
            auto square = new gui::Image();
            square->set("Rectangle");
            square->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
            squareImages.push_back(square);
        }

        hBox = new HBox(this, 0, 0, 0, 0);
        hBox->setEdges(RectangleEdge::None);

        repeatVBox = new VBox(hBox, 0, 0, 0, 0);
        repeatVBox->setMinimumSize(style::window::default_body_width / 2,
        repeatVBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                   style::window::calendar::item::repeatAndReminder::height);
        repeatVBox->setEdges(RectangleEdge::None);

        reminderVBox = new VBox(hBox, 0, 0, 0, 0);
        reminderVBox->setMinimumSize(style::window::default_body_width / 2,
        reminderVBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                     style::window::calendar::item::repeatAndReminder::height);
        reminderVBox->setEdges(RectangleEdge::None);

        repeatTitle = new gui::Label(repeatVBox, 0, 0, 0, 0);
        repeatHBox = new HBox(repeatVBox, 0, 0, 0, 0);
        repeatHBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                   style::window::calendar::item::repeatAndReminder::title_label_h);
        repeatHBox->setMargins(
            gui::Margins(0, 0, 0, style::window::calendar::item::repeatAndReminder::title_label_margin));
        repeatHBox->setEdges(RectangleEdge::None);
        repeatHBox->addWidget(squareImages[0]);

        repeatTitle = new gui::Label(repeatHBox, 0, 0, 0, 0);
        repeatTitle->setMinimumSize(style::window::calendar::item::repeatAndReminder::description_w,
                                    style::window::calendar::item::repeatAndReminder::title_label_h);
        repeatTitle->setMargins(
            gui::Margins(0, 0, 0, style::window::calendar::item::repeatAndReminder::title_label_margin));
        repeatTitle->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        repeatTitle->setEdges(RectangleEdge::None);
        repeatTitle->setFont(style::window::font::small);
        repeatTitle->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        repeatTitle->setLineMode(true);
        repeatTitle->activeItem = false;

        repeat = new gui::Label(repeatVBox, 0, 0, 0, 0);


@@ 48,15 63,21 @@ namespace gui
        repeat->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        repeat->activeItem = false;

        reminderTitle = new gui::Label(reminderVBox, 0, 0, 0, 0);
        reminderTitle->setMinimumSize(style::window::default_body_width / 2,
                                      style::window::calendar::item::repeatAndReminder::title_label_h);
        reminderTitle->setMargins(
        reminderHBox = new HBox(reminderVBox, 0, 0, 0, 0);
        reminderHBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                     style::window::calendar::item::repeatAndReminder::title_label_h);
        reminderHBox->setMargins(
            gui::Margins(0, 0, 0, style::window::calendar::item::repeatAndReminder::title_label_margin));
        reminderHBox->setEdges(RectangleEdge::None);
        reminderHBox->addWidget(squareImages[1]);

        reminderTitle = new gui::Label(reminderHBox, 0, 0, 0, 0);
        reminderTitle->setMinimumSize(style::window::calendar::item::repeatAndReminder::description_w,
                                      style::window::calendar::item::repeatAndReminder::title_label_h);
        reminderTitle->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        reminderTitle->setEdges(RectangleEdge::None);
        reminderTitle->setFont(style::window::font::small);
        reminderTitle->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        reminderTitle->setLineMode(true);
        reminderTitle->activeItem = false;

        reminder = new gui::Label(reminderVBox, 0, 0, 0, 0);

M module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp +2 -0
@@ 17,6 17,8 @@ namespace gui
        gui::HBox *hBox           = nullptr;
        gui::VBox *repeatVBox     = nullptr;
        gui::VBox *reminderVBox   = nullptr;
        gui::HBox *repeatHBox         = nullptr;
        gui::HBox *reminderHBox       = nullptr;
        gui::Label *repeatTitle   = nullptr;
        gui::Label *repeat        = nullptr;
        gui::Label *reminderTitle = nullptr;

M module-apps/application-calendar/windows/AllEventsWindow.cpp => module-apps/application-calendar/windows/AllEventsWindow.cpp +18 -5
@@ 3,8 3,8 @@

#include "AllEventsWindow.hpp"
#include "InputEvent.hpp"
#include "module-apps/application-calendar/ApplicationCalendar.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include <gui/widgets/BottomBar.hpp>
#include <gui/widgets/TopBar.hpp>
#include <gui/widgets/Window.hpp>


@@ 12,7 12,6 @@

#include <module-services/service-db/messages/QueryMessage.hpp>
#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <module-services/service-db/api/DBServiceAPI.hpp>
#include <time/time_conversion.hpp>
#include <module-services/service-db/messages/DBNotificationMessage.hpp>



@@ 61,6 60,19 @@ namespace gui
        allEventsList->rebuildList();
    }

    auto AllEventsWindow::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }
        auto *item = dynamic_cast<DayMonthData *>(data);
        if (item == nullptr) {
            return false;
        }
        dateFilter = item->getDateFilter();
        return true;
    }

    bool AllEventsWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (inputEvent.keyCode == gui::KeyCode::KEY_RF &&


@@ 82,8 94,9 @@ namespace gui
            std::unique_ptr<EventRecordData> data = std::make_unique<EventRecordData>();
            data->setDescription(style::window::calendar::new_event);
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = TimePointNow();
            event->date_till = TimePointNow();
            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);
            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/AllEventsWindow.hpp => module-apps/application-calendar/windows/AllEventsWindow.hpp +2 -1
@@ 16,6 16,7 @@ namespace gui
        gui::Image *leftArrowImage   = nullptr;
        gui::Image *newDayEventImage = nullptr;

        TimePoint dateFilter                           = TimePointNow();
        gui::ListView *allEventsList                   = nullptr;
        std::shared_ptr<AllEventsModel> allEventsModel = nullptr;



@@ 27,7 28,7 @@ namespace gui
        bool onInput(const gui::InputEvent &inputEvent) override;
        void rebuild() override;
        void buildInterface() override;

        auto handleSwitchData(SwitchData *data) -> bool override;
    };

} // namespace gui

M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +5 -2
@@ 221,13 221,16 @@ namespace gui
        }
        if (auto response = dynamic_cast<db::query::events::GetAllResult *>(queryResult)) {
            const auto records = response->getResult();
            auto day           = monthBox->getFocusItemIndex() + 1;
            auto filter = TimePointFromYearMonthDay(monthModel->getYear() / monthModel->getMonth() / date::day(day));
            if (!records->empty()) {
                application->switchWindow(style::window::calendar::name::all_events_window);
                auto data = std::make_unique<DayMonthData>();
                data->setData("", filter);
                application->switchWindow(style::window::calendar::name::all_events_window, std::move(data));
            }
            else {
                auto appCalendar = dynamic_cast<app::ApplicationCalendar *>(application);
                assert(appCalendar != nullptr);
                auto filter = TimePointFromYearMonthDay(actualDate);
                appCalendar->switchToNoEventsWindow(utils::localize.get("app_calendar_title_main"), filter);
            }
            return true;

M module-apps/application-calendar/windows/DayEventsWindow.cpp => module-apps/application-calendar/windows/DayEventsWindow.cpp +2 -1
@@ 100,7 100,8 @@ namespace gui
            data->setDescription(style::window::calendar::new_event);
            auto rec       = new EventsRecord();
            rec->date_from = filterFrom;
            rec->date_till = 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);
            auto event     = std::make_shared<EventsRecord>(*rec);
            data->setData(event);
            application->switchWindow(