From a84778f6ee0a865237b6b659e0882dcef19c1804 Mon Sep 17 00:00:00 2001 From: david072 Date: Fri, 17 Nov 2023 21:51:13 +0100 Subject: [PATCH] Calendar/AddEventDialog: Prevent inputting invalid date ranges The calendar now verifies that the user can't input invalid date ranges, i.e. date ranges where the end is before the start. The UI elements do this implicitly, by adjusting the values when changing the dates in an illegal way (e.g. when picking an end date that is before the start date, the end date will change to the start date). --- .../Applications/Calendar/AddEventDialog.cpp | 95 +++++++++++++------ .../Applications/Calendar/AddEventDialog.h | 13 ++- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/Userland/Applications/Calendar/AddEventDialog.cpp b/Userland/Applications/Calendar/AddEventDialog.cpp index a8b651a147..1a045dcf05 100644 --- a/Userland/Applications/Calendar/AddEventDialog.cpp +++ b/Userland/Applications/Calendar/AddEventDialog.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,8 @@ namespace Calendar { +static constexpr StringView DATE_FORMAT = "%Y-%m-%d"sv; + AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_manager, Window* parent_window) : Dialog(parent_window) , m_start_date_time(date_time) @@ -46,8 +49,7 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_man auto& event_title_textbox = *widget->find_descendant_of_type_named("event_title_textbox"); event_title_textbox.set_focus(true); - auto& start_date_box = *widget->find_descendant_of_type_named("start_date"); - start_date_box.set_text(MUST(m_start_date_time.to_string("%Y-%m-%d"sv))); + m_start_date_box = *widget->find_descendant_of_type_named("start_date"); auto calendar_date_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv).release_value_but_fixme_should_propagate_errors(); @@ -55,38 +57,43 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_man pick_start_date_button.set_icon(calendar_date_icon); pick_start_date_button.on_click = [&](auto) { if (auto new_date = GUI::DatePicker::show(this, "Pick Start Date"_string, m_start_date_time); new_date.has_value()) { - m_start_date_time.set_date(new_date.release_value()); - start_date_box.set_text(MUST(m_start_date_time.to_string("%Y-%m-%d"sv))); + m_start_date_time.set_date(new_date.value()); + if (m_end_date_time < m_start_date_time) { + m_end_date_time.set_date(new_date.value()); + update_end_date(); + } + + m_start_date_box->set_text(MUST(m_start_date_time.to_string(DATE_FORMAT))); } }; - auto& starting_hour_input = *widget->find_descendant_of_type_named("start_hour"); - starting_hour_input.set_value(m_start_date_time.hour()); + m_start_hour_box = *widget->find_descendant_of_type_named("start_hour"); + m_start_minute_box = *widget->find_descendant_of_type_named("start_minute"); - auto& starting_minute_input = *widget->find_descendant_of_type_named("start_minute"); - starting_minute_input.set_value(m_start_date_time.minute()); - - auto& end_date_box = *widget->find_descendant_of_type_named("end_date"); - end_date_box.set_text(MUST(m_start_date_time.to_string("%Y-%m-%d"sv))); + m_end_date_box = *widget->find_descendant_of_type_named("end_date"); auto& pick_end_date_button = *widget->find_descendant_of_type_named("pick_end_date"); pick_end_date_button.set_icon(calendar_date_icon); pick_end_date_button.on_click = [&](auto) { if (auto new_date = GUI::DatePicker::show(this, "Pick End Date"_string, m_end_date_time); new_date.has_value()) { - m_end_date_time.set_date(new_date.release_value()); - end_date_box.set_text(MUST(m_end_date_time.to_string("%Y-%m-%d"sv))); + m_end_date_time.set_date(new_date.value()); + if (m_end_date_time < m_start_date_time) { + m_start_date_time.set_date(new_date.value()); + update_start_date(); + } + + m_end_date_box->set_text(MUST(m_end_date_time.to_string(DATE_FORMAT))); } }; - auto& ending_hour_input = *widget->find_descendant_of_type_named("end_hour"); - ending_hour_input.set_value(m_end_date_time.hour()); - - auto& ending_minute_input = *widget->find_descendant_of_type_named("end_minute"); - ending_minute_input.set_value(m_end_date_time.minute()); + m_end_hour_box = *widget->find_descendant_of_type_named("end_hour"); + m_end_minute_box = *widget->find_descendant_of_type_named("end_minute"); auto& ok_button = *widget->find_descendant_of_type_named("ok_button"); ok_button.on_click = [&](auto) { - add_event_to_calendar().release_value_but_fixme_should_propagate_errors(); + auto successful = add_event_to_calendar().release_value_but_fixme_should_propagate_errors(); + if (!successful) + return; done(ExecResult::OK); }; @@ -94,24 +101,40 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_man cancel_button.on_click = [&](auto) { done(ExecResult::Cancel); }; auto update_starting_input_values = [&, this]() { - auto hour = starting_hour_input.value(); - auto minute = starting_minute_input.value(); + auto hour = m_start_hour_box->value(); + auto minute = m_start_minute_box->value(); m_start_date_time.set_time_only(hour, minute); + if (m_end_date_time < m_start_date_time) { + m_end_date_time.set_time_only(hour, minute); + update_end_date(); + } }; auto update_ending_input_values = [&, this]() { - auto hour = ending_hour_input.value(); - auto minute = ending_minute_input.value(); + auto hour = m_end_hour_box->value(); + auto minute = m_end_minute_box->value(); m_end_date_time.set_time_only(hour, minute); + if (m_end_date_time < m_start_date_time) { + m_start_date_time.set_time_only(hour, minute); + update_start_date(); + } }; - starting_hour_input.on_change = [update_starting_input_values](auto) { update_starting_input_values(); }; - starting_minute_input.on_change = [update_starting_input_values](auto) { update_starting_input_values(); }; - ending_hour_input.on_change = [update_ending_input_values](auto) { update_ending_input_values(); }; - ending_minute_input.on_change = [update_ending_input_values](auto) { update_ending_input_values(); }; + m_start_hour_box->on_change = [update_starting_input_values](auto) { update_starting_input_values(); }; + m_start_minute_box->on_change = [update_starting_input_values](auto) { update_starting_input_values(); }; + m_end_hour_box->on_change = [update_ending_input_values](auto) { update_ending_input_values(); }; + m_end_minute_box->on_change = [update_ending_input_values](auto) { update_ending_input_values(); }; + + update_start_date(); + update_end_date(); } -ErrorOr AddEventDialog::add_event_to_calendar() +ErrorOr AddEventDialog::add_event_to_calendar() { + if (m_end_date_time < m_start_date_time) { + GUI::MessageBox::show_error(this, "The end date has to be after the start date."sv); + return false; + } + auto summary = find_descendant_of_type_named("event_title_textbox")->get_text(); m_event_manager.add_event(Event { .summary = TRY(String::from_byte_string(summary)), @@ -119,7 +142,21 @@ ErrorOr AddEventDialog::add_event_to_calendar() .end = m_end_date_time, }); - return {}; + return true; +} + +void AddEventDialog::update_start_date() +{ + m_start_date_box->set_text(MUST(m_start_date_time.to_string(DATE_FORMAT))); + m_start_hour_box->set_value(m_start_date_time.hour(), GUI::AllowCallback::No); + m_start_minute_box->set_value(m_start_date_time.minute(), GUI::AllowCallback::No); +} + +void AddEventDialog::update_end_date() +{ + m_end_date_box->set_text(MUST(m_end_date_time.to_string(DATE_FORMAT))); + m_end_hour_box->set_value(m_end_date_time.hour(), GUI::AllowCallback::No); + m_end_minute_box->set_value(m_end_date_time.minute(), GUI::AllowCallback::No); } } diff --git a/Userland/Applications/Calendar/AddEventDialog.h b/Userland/Applications/Calendar/AddEventDialog.h index 0240a1b29f..c42b28275b 100644 --- a/Userland/Applications/Calendar/AddEventDialog.h +++ b/Userland/Applications/Calendar/AddEventDialog.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2020, Ryan Grieb * Copyright (c) 2022-2023, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -29,11 +30,21 @@ public: private: AddEventDialog(Core::DateTime date_time, EventManager& event_manager, Window* parent_window = nullptr); - ErrorOr add_event_to_calendar(); + ErrorOr add_event_to_calendar(); + + void update_start_date(); + void update_end_date(); Core::DateTime m_start_date_time; Core::DateTime m_end_date_time; EventManager& m_event_manager; + + RefPtr m_start_date_box; + RefPtr m_end_date_box; + RefPtr m_start_hour_box; + RefPtr m_start_minute_box; + RefPtr m_end_hour_box; + RefPtr m_end_minute_box; }; }