From 4465b37897a24e910c2c76e4da090c1c424ee8a2 Mon Sep 17 00:00:00 2001 From: thankyouverycool <66646555+thankyouverycool@users.noreply.github.com> Date: Mon, 29 Mar 2021 20:43:40 -0400 Subject: [PATCH] LibGUI+Calendar: Add new month and year views to Calendar And overhaul resize and paint events to fix layout edge cases in which Calendar wasn't filling its parent widget completely. Ensures month views always display prior month days for click navigation. Converts Calendar app layout to GML. --- Base/res/icons/16x16/calendar-month-view.png | Bin 0 -> 175 bytes Userland/Applications/Calendar/CMakeLists.txt | 3 + .../Applications/Calendar/CalendarWindow.gml | 24 + Userland/Applications/Calendar/main.cpp | 134 ++- Userland/Libraries/LibGUI/Calendar.cpp | 963 +++++++++++++----- Userland/Libraries/LibGUI/Calendar.h | 161 +-- 6 files changed, 865 insertions(+), 420 deletions(-) create mode 100644 Base/res/icons/16x16/calendar-month-view.png create mode 100644 Userland/Applications/Calendar/CalendarWindow.gml diff --git a/Base/res/icons/16x16/calendar-month-view.png b/Base/res/icons/16x16/calendar-month-view.png new file mode 100644 index 0000000000000000000000000000000000000000..af19dc5fd37634aef26fe6e620e6c0be8dfa0d04 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7$~;{hLo80;4L-@+V8Bzn zI%Wf7w*gD_243L}Ih9kqY;G+II>KC^A9NkMnK|F4fvs fFS{rDfZ@>zW5MHFw^lJQFfe$!`njxgN@xNAu^L8x literal 0 HcmV?d00001 diff --git a/Userland/Applications/Calendar/CMakeLists.txt b/Userland/Applications/Calendar/CMakeLists.txt index c23a6743cc..fc0628fec4 100644 --- a/Userland/Applications/Calendar/CMakeLists.txt +++ b/Userland/Applications/Calendar/CMakeLists.txt @@ -1,5 +1,8 @@ +compile_gml(CalendarWindow.gml CalendarWindowGML.h calendar_window_gml) + set(SOURCES AddEventDialog.cpp + CalendarWindowGML.h main.cpp ) diff --git a/Userland/Applications/Calendar/CalendarWindow.gml b/Userland/Applications/Calendar/CalendarWindow.gml new file mode 100644 index 0000000000..f55e7372da --- /dev/null +++ b/Userland/Applications/Calendar/CalendarWindow.gml @@ -0,0 +1,24 @@ +@GUI::Widget { + fill_with_background_color: true + layout: @GUI::VerticalBoxLayout { + } + + @GUI::ToolBarContainer { + name: "toolbar_container" + + @GUI::ToolBar { + name: "toolbar" + } + } + + @GUI::Frame { + name: "calendar_frame" + layout: @GUI::VerticalBoxLayout { + margins: [2, 2, 2, 2] + } + + @GUI::Calendar { + name: "calendar" + } + } +} diff --git a/Userland/Applications/Calendar/main.cpp b/Userland/Applications/Calendar/main.cpp index 57c734c298..2843b6de2b 100644 --- a/Userland/Applications/Calendar/main.cpp +++ b/Userland/Applications/Calendar/main.cpp @@ -25,7 +25,9 @@ */ #include "AddEventDialog.h" +#include #include +#include #include #include #include @@ -36,15 +38,10 @@ #include #include #include -#include -#include -#include -#include #include int main(int argc, char** argv) { - if (pledge("stdio recvfd sendfd rpath accept unix cpath fattr", nullptr) < 0) { perror("pledge"); return 1; @@ -68,101 +65,92 @@ int main(int argc, char** argv) auto window = GUI::Window::construct(); window->set_title("Calendar"); window->resize(600, 480); + window->set_minimum_size(171, 141); window->set_icon(app_icon.bitmap_for_size(16)); - auto& root_container = window->set_main_widget(); - root_container.set_fill_with_background_color(true); - root_container.set_layout(); + auto& main_widget = window->set_main_widget(); + main_widget.load_from_gml(calendar_window_gml); - auto& toolbar_container = root_container.add(); - auto& toolbar = toolbar_container.add(); + auto toolbar = main_widget.find_descendant_of_type_named("toolbar"); + auto calendar = main_widget.find_descendant_of_type_named("calendar"); - auto& calendar_container = root_container.add(); - calendar_container.set_layout(); - calendar_container.layout()->set_margins({ 2, 2, 2, 2 }); - auto& calendar_widget = calendar_container.add(Core::DateTime::now()); - - RefPtr selected_calendar_button; - - auto prev_date_action = GUI::Action::create("Previous date", { Mod_Alt, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), [&](const GUI::Action&) { - unsigned int target_month = calendar_widget.selected_month(); - unsigned int target_year = calendar_widget.selected_year(); - - if (calendar_widget.mode() == GUI::Calendar::Month) { - target_month--; - if (calendar_widget.selected_month() <= 1) { - target_month = 12; - target_year--; + auto prev_date_action = GUI::Action::create({}, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), [&](const GUI::Action&) { + unsigned view_month = calendar->view_month(); + unsigned view_year = calendar->view_year(); + if (calendar->mode() == GUI::Calendar::Month) { + view_month--; + if (calendar->view_month() == 1) { + view_month = 12; + view_year--; } } else { - target_year--; + view_year--; } - - calendar_widget.update_tiles(target_year, target_month); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); + calendar->update_tiles(view_year, view_month); }); - auto next_date_action = GUI::Action::create("Next date", { Mod_Alt, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) { - unsigned int target_month = calendar_widget.selected_month(); - unsigned int target_year = calendar_widget.selected_year(); - - if (calendar_widget.mode() == GUI::Calendar::Month) { - target_month++; - if (calendar_widget.selected_month() >= 12) { - target_month = 1; - target_year++; + auto next_date_action = GUI::Action::create({}, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) { + unsigned view_month = calendar->view_month(); + unsigned view_year = calendar->view_year(); + if (calendar->mode() == GUI::Calendar::Month) { + view_month++; + if (calendar->view_month() == 12) { + view_month = 1; + view_year++; } } else { - target_year++; + view_year++; } - - calendar_widget.update_tiles(target_year, target_month); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); + calendar->update_tiles(view_year, view_month); }); auto add_event_action = GUI::Action::create("Add event", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"), [&](const GUI::Action&) { - AddEventDialog::show(calendar_widget.selected_date(), window); + AddEventDialog::show(calendar->selected_date(), window); }); auto jump_to_action = GUI::Action::create("Jump to today", {}, Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"), [&](const GUI::Action&) { - if (calendar_widget.mode() == GUI::Calendar::Year) - calendar_widget.toggle_mode(); - calendar_widget.set_selected_date(Core::DateTime::now()); - calendar_widget.update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month()); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); + calendar->set_selected_date(Core::DateTime::now()); + calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month()); }); - toolbar.add_action(prev_date_action); - selected_calendar_button = toolbar.add(calendar_widget.selected_calendar_text()); - selected_calendar_button->set_fixed_width(70); - selected_calendar_button->set_button_style(Gfx::ButtonStyle::CoolBar); - selected_calendar_button->set_font(Gfx::FontDatabase::default_bold_fixed_width_font()); - selected_calendar_button->on_click = [&](auto) { - calendar_widget.toggle_mode(); - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); - }; - toolbar.add_action(next_date_action); - toolbar.add_separator(); - toolbar.add_action(jump_to_action); - toolbar.add_action(add_event_action); + auto view_month_action = GUI::Action::create_checkable("Month view", { Mod_Ctrl, KeyCode::Key_1 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-month-view.png"), [&](const GUI::Action&) { + if (calendar->mode() == GUI::Calendar::Year) + calendar->toggle_mode(); + }); + view_month_action->set_checked(true); - calendar_widget.on_calendar_tile_click = [&] { - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); + auto view_year_action = GUI::Action::create_checkable("Year view", { Mod_Ctrl, KeyCode::Key_2 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GUI::Action&) { + if (calendar->mode() == GUI::Calendar::Month) + calendar->toggle_mode(); + }); + + auto view_type_action_group = make(); + view_type_action_group->set_exclusive(true); + view_type_action_group->add_action(*view_month_action); + view_type_action_group->add_action(*view_year_action); + + toolbar->add_action(prev_date_action); + toolbar->add_action(next_date_action); + toolbar->add_separator(); + toolbar->add_action(jump_to_action); + toolbar->add_action(add_event_action); + toolbar->add_separator(); + toolbar->add_action(view_month_action); + toolbar->add_action(view_year_action); + + calendar->on_tile_doubleclick = [&] { + AddEventDialog::show(calendar->selected_date(), window); }; - calendar_widget.on_calendar_tile_doubleclick = [&] { - AddEventDialog::show(calendar_widget.selected_date(), window); - }; - - calendar_widget.on_month_tile_click = [&] { - selected_calendar_button->set_text(calendar_widget.selected_calendar_text()); + calendar->on_month_click = [&] { + view_month_action->set_checked(true); }; auto menubar = GUI::MenuBar::construct(); auto& app_menu = menubar->add_menu("File"); app_menu.add_action(GUI::Action::create("Add Event", { Mod_Ctrl | Mod_Shift, Key_E }, Gfx::Bitmap::load_from_file("/res/icons/16x16/add-event.png"), [&](const GUI::Action&) { - AddEventDialog::show(calendar_widget.selected_date(), window); + AddEventDialog::show(calendar->selected_date(), window); return; })); @@ -173,6 +161,10 @@ int main(int argc, char** argv) return; })); + auto& view_menu = menubar->add_menu("View"); + view_menu.add_action(*view_month_action); + view_menu.add_action(*view_year_action); + auto& help_menu = menubar->add_menu("Help"); help_menu.add_action(GUI::CommonActions::make_about_action("Calendar", app_icon, window)); diff --git a/Userland/Libraries/LibGUI/Calendar.cpp b/Userland/Libraries/LibGUI/Calendar.cpp index 6ca093e397..1201848610 100644 --- a/Userland/Libraries/LibGUI/Calendar.cpp +++ b/Userland/Libraries/LibGUI/Calendar.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2020, Ryan Grieb - * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2020-2021, the SerenityOS developers * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,15 +26,14 @@ */ #include -#include -#include #include #include #include -#include #include #include +REGISTER_WIDGET(GUI, Calendar); + namespace GUI { static const char* long_day_names[] = { @@ -56,316 +55,734 @@ static const char* short_month_names[] = { "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -Calendar::Calendar(Core::DateTime date_time) +static const auto extra_large_font = Gfx::BitmapFont::load_from_file("/res/fonts/LizaRegular36.font"); +static const auto large_font = Gfx::BitmapFont::load_from_file("/res/fonts/LizaRegular24.font"); +static const auto medium_font = Gfx::BitmapFont::load_from_file("/res/fonts/PebbletonRegular14.font"); +static const auto small_font = Gfx::BitmapFont::load_from_file("/res/fonts/KaticaRegular10.font"); + +Calendar::Calendar(Core::DateTime date_time, Mode mode) : m_selected_date(date_time) - , m_selected_year(date_time.year()) - , m_selected_month(date_time.month()) + , m_mode(mode) { set_fill_with_background_color(true); - set_layout(); - layout()->set_spacing(0); + set_background_role(Gfx::ColorRole::ThreedShadow2); - m_day_name_container = add(); - m_day_name_container->set_layout(); - m_day_name_container->set_fixed_height(16); - m_day_name_container->layout()->set_spacing(0); - m_day_name_container->set_fill_with_background_color(true); - m_day_name_container->set_background_role(Gfx::ColorRole::HoverHighlight); - for (auto& day : m_day_names) { - day = m_day_name_container->add(); - day->set_font(Gfx::FontDatabase::default_bold_font()); + for (int i = 0; i < 7; i++) { + Day day; + m_days.append(move(day)); } - - m_calendar_tile_container = add(); - m_calendar_tile_container->set_layout(); - m_calendar_tile_container->layout()->set_spacing(0); - - for (auto& row : m_week_rows) { - row = m_calendar_tile_container->add(); - row->set_layout(); - row->layout()->set_spacing(0); - } - - int i = 0; - for (int j = 0; j < 6; j++) - for (int k = 0; k < 7; k++) { - m_calendar_tiles[i] = m_week_rows[j]->add(i, date_time); - m_calendar_tiles[i]->on_click = [this](int index) { - m_previous_selected_date = m_selected_date; - m_selected_date = m_calendar_tiles[index]->get_date_time(); - update_tiles(m_selected_date.year(), m_selected_date.month()); - if (on_calendar_tile_click) - on_calendar_tile_click(); - }; - m_calendar_tiles[i]->on_doubleclick = [this](int index) { - if (m_calendar_tiles[index]->get_date_time().day() != m_previous_selected_date.day()) - return; - if (on_calendar_tile_doubleclick) - on_calendar_tile_doubleclick(); - }; - i++; + for (int i = 0; i < 12; i++) { + MonthTile month; + m_months.append(move(month)); + for (int j = 0; j < 42; j++) { + Tile tile; + m_tiles[i].append(move(tile)); } - - m_month_tile_container = add(); - m_month_tile_container->set_visible(false); - m_month_tile_container->set_layout(); - m_month_tile_container->set_fill_with_background_color(true); - m_month_tile_container->set_background_role(Gfx::ColorRole::HoverHighlight); - m_month_tile_container->layout()->set_spacing(0); - - for (auto& row : m_month_rows) { - row = m_month_tile_container->add(); - row->set_layout(); - row->layout()->set_spacing(0); } - i = 0; - for (int j = 0; j < 3; j++) - for (int k = 0; k < 4; k++) { - m_month_tiles[i] = m_month_rows[j]->add(i, date_time); - m_month_tiles[i]->set_button_style(Gfx::ButtonStyle::CoolBar); - m_month_tiles[i]->on_indexed_click = [this](int index) { - toggle_mode(); - update_tiles(m_month_tiles[index]->get_date_time().year(), m_month_tiles[index]->get_date_time().month()); - if (on_month_tile_click) - on_month_tile_click(); - }; - i++; - } - - update_tiles(selected_year(), selected_month()); + update_tiles(m_selected_date.year(), m_selected_date.month()); } Calendar::~Calendar() { } +void Calendar::set_grid(bool show) +{ + m_grid = show; + set_background_role(has_grid() + ? Gfx::ColorRole::ThreedShadow2 + : Gfx::ColorRole::Base); +} + void Calendar::toggle_mode() { m_mode == Month ? m_mode = Year : m_mode = Month; - - if (mode() == Month) { - m_day_name_container->set_visible(true); - m_calendar_tile_container->set_visible(true); - m_month_tile_container->set_visible(false); - } else { - m_day_name_container->set_visible(false); - m_calendar_tile_container->set_visible(false); - m_month_tile_container->set_visible(true); - } - + set_show_days_of_the_week(!m_show_days); + set_show_year(!m_show_year); + set_show_month_and_year(!m_show_month_year); + update_tiles(this->view_year(), this->view_month()); this->resize(this->height(), this->width()); - update_tiles(selected_year(), selected_month()); -} - -void Calendar::set_grid(bool grid) -{ - if (m_grid == grid) - return; - - m_grid = grid; - - for (int i = 0; i < 42; i++) { - m_calendar_tiles[i]->set_grid(grid); - m_calendar_tiles[i]->update(); - } + invalidate_layout(); } void Calendar::resize_event(GUI::ResizeEvent& event) { - if (m_day_name_container->is_visible()) { - for (int i = 0; i < 7; i++) { - if (event.size().width() < 120) - m_day_names[i]->set_text(micro_day_names[i]); - else if (event.size().width() < 200) - m_day_names[i]->set_text(mini_day_names[i]); - else if (event.size().width() < 480) - m_day_names[i]->set_text(short_day_names[i]); - else - m_day_names[i]->set_text(long_day_names[i]); - } - } + m_event_size.set_width(event.size().width()); + m_event_size.set_height(event.size().height()); + + if (mode() == Month) { + if (m_event_size.width() < 160 || m_event_size.height() < 130) + set_show_month_and_year(false); + else if (m_event_size.width() >= 160 && m_event_size.height() >= 130) + set_show_month_and_year(true); + + set_show_year(false); + + const int GRID_LINES = 6; + int tile_width = (m_event_size.width() - GRID_LINES) / 7; + int width_remainder = (m_event_size.width() - GRID_LINES) % 7; + int y_offset = is_showing_days_of_the_week() ? 16 : 0; + y_offset += is_showing_month_and_year() ? 24 : 0; + int tile_height = (m_event_size.height() - y_offset - GRID_LINES) / 6; + int height_remainder = (m_event_size.height() - y_offset - GRID_LINES) % 6; + + set_unadjusted_tile_size(tile_width, tile_height); + tile_width < 30 || tile_height < 30 ? set_grid(false) : set_grid(true); + + for (int i = 0; i < 42; i++) { + m_tiles[0][i].width = tile_width; + m_tiles[0][i].height = tile_height; + } + + for (auto& day : m_days) + day.width = tile_width; + + for (int i = 0; i < width_remainder; i++) { + m_days[i].width = (tile_width + 1); + for (int j = i; j < i + 36; j += 7) { + m_tiles[0][j].width = tile_width + 1; + } + } + + for (int j = 0; j < height_remainder * 7; j++) + m_tiles[0][j].height = tile_height + 1; + + if (is_showing_days_of_the_week()) { + for (int i = 0; i < 7; i++) { + if (m_event_size.width() < 138) + m_days[i].name = micro_day_names[i]; + else if (m_event_size.width() < 200) + m_days[i].name = mini_day_names[i]; + else if (m_event_size.width() < 480) + m_days[i].name = short_day_names[i]; + else + m_days[i].name = long_day_names[i]; + } + } + } else { + if (m_event_size.width() < 140 && m_event_size.height() < 120) + set_show_year(false); + else if (m_event_size.width() >= 140 && m_event_size.height() >= 120) + set_show_year(true); + + set_show_month_and_year(false); + + const int VERT_GRID_LINES = 27; + const int HORI_GRID_LINES = 15; + const int THREADING = 3; + const int MONTH_TITLE = 19; + int tile_width = (m_event_size.width() - VERT_GRID_LINES) / 28; + int width_remainder = (m_event_size.width() - VERT_GRID_LINES) % 28; + int y_offset = is_showing_year() ? 22 : 0; + y_offset += (MONTH_TITLE * 3) + (THREADING * 3); + int tile_height = (m_event_size.height() - y_offset - HORI_GRID_LINES) / 18; + int height_remainder = (m_event_size.height() - y_offset - HORI_GRID_LINES) % 18; + + set_grid(false); + set_unadjusted_tile_size(tile_width, tile_height); + if (unadjusted_tile_size().width() < 17 || unadjusted_tile_size().height() < 13) { + m_show_month_tiles = true; + set_background_role(Gfx::ColorRole::Window); + } else { + m_show_month_tiles = false; + set_background_role(Gfx::ColorRole::Base); + } + + if (m_show_month_tiles) { + int month_tile_width = m_event_size.width() / 4; + int width_remainder = m_event_size.width() % 4; + int y_offset = is_showing_year() ? 23 : 0; + int month_tile_height = (m_event_size.height() - y_offset) / 3; + int height_remainder = (m_event_size.height() - y_offset) % 3; + + for (int i = 0; i < 12; i++) { + m_months[i].width = month_tile_width; + m_months[i].height = month_tile_height; + if (m_event_size.width() < 250) + m_months[i].name = short_month_names[i]; + else + m_months[i].name = long_month_names[i]; + } + + if (width_remainder) { + for (int i = 0; i < width_remainder; i++) { + for (int j = i; j < 12; j += 4) { + m_months[j].width = month_tile_width + 1; + } + } + } + + if (height_remainder) { + for (int i = 0; i < height_remainder * 4; i++) { + m_months[i].height = month_tile_height + 1; + } + } + return; + } - if (m_month_tile_container->is_visible()) { for (int i = 0; i < 12; i++) { - if (event.size().width() < 250) - m_month_tiles[i]->set_text(short_month_names[i]); - else - m_month_tiles[i]->set_text(long_month_names[i]); + int remainder = 0; + if (i == 0 || i == 4 || i == 8) + remainder = min(width_remainder, 7); + if (i == 1 || i == 5 || i == 9) + width_remainder > 7 ? remainder = min(width_remainder - 7, 7) : remainder = 0; + if (i == 2 || i == 6 || i == 10) + width_remainder > 14 ? remainder = min(width_remainder - 14, 7) : remainder = 0; + if (i == 3 || i == 7 || i == 11) + width_remainder > 21 ? remainder = width_remainder - 21 : remainder = 0; + m_month_size[i].set_width(remainder + 6 + tile_width * 7); + + if (i >= 0 && i <= 3) + remainder = min(height_remainder, 6); + if (i >= 4 && i <= 7) + height_remainder > 6 ? remainder = min(height_remainder - 6, 6) : remainder = 0; + if (i >= 8 && i <= 12) + height_remainder > 12 ? remainder = height_remainder - 12 : remainder = 0; + m_month_size[i].set_height(remainder + 5 + tile_height * 6); + + for (int j = 0; j < 42; j++) { + m_tiles[i][j].width = tile_width; + m_tiles[i][j].height = tile_height; + } + } + + if (width_remainder) { + for (int i = 0; i < 12; i += 4) { + for (int j = 0; j < min(width_remainder, 7); j++) { + for (int k = j; k < j + 36; k += 7) { + m_tiles[i][k].width = tile_width + 1; + } + } + } + } + if (width_remainder > 7) { + for (int i = 1; i < 12; i += 4) { + for (int j = 0; j < min(width_remainder - 7, 7); j++) { + for (int k = j; k < j + 36; k += 7) { + m_tiles[i][k].width = tile_width + 1; + } + } + } + } + if (width_remainder > 14) { + for (int i = 2; i < 12; i += 4) { + for (int j = 0; j < min(width_remainder - 14, 7); j++) { + for (int k = j; k < j + 36; k += 7) { + m_tiles[i][k].width = tile_width + 1; + } + } + } + } + if (width_remainder > 21) { + for (int i = 3; i < 12; i += 4) { + for (int j = 0; j < width_remainder - 21; j++) { + for (int k = j; k < j + 36; k += 7) { + m_tiles[i][k].width = tile_width + 1; + } + } + } + } + if (height_remainder) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < min(height_remainder, 6) * 7; j++) { + m_tiles[i][j].height = tile_height + 1; + } + } + } + if (height_remainder > 6) { + for (int i = 4; i < 8; i++) { + for (int j = 0; j < min(height_remainder - 6, 6) * 7; j++) { + m_tiles[i][j].height = tile_height + 1; + } + } + } + if (height_remainder > 12) { + for (int i = 8; i < 12; i++) { + for (int j = 0; j < (height_remainder - 12) * 7; j++) { + m_tiles[i][j].height = tile_height + 1; + } + } } } - - (event.size().width() < 200) ? set_grid(false) : set_grid(true); } -void Calendar::update_tiles(unsigned int target_year, unsigned int target_month) +void Calendar::update_tiles(unsigned view_year, unsigned view_month) { - set_selected_calendar(target_year, target_month); - if (mode() == Month) { - unsigned int i = 0; - for (int y = 0; y < 6; y++) - for (int x = 0; x < 7; x++) { - auto date_time = Core::DateTime::create(target_year, target_month, 1); - unsigned int start_of_month = date_time.weekday(); - unsigned int year; - unsigned int month; - unsigned int day; + set_view_date(view_year, view_month); - if (start_of_month > i) { - month = (target_month - 1 == 0) ? 12 : target_month - 1; - year = (month == 12) ? target_year - 1 : target_year; - date_time.set_time(year, month, 1); - day = (date_time.days_in_month() - (start_of_month) + i) + 1; - date_time.set_time(year, month, day); - } else if ((i - start_of_month) + 1 > date_time.days_in_month()) { - month = (target_month + 1) > 12 ? 1 : target_month + 1; - year = (month == 1) ? target_year + 1 : target_year; - day = ((i - start_of_month) + 1) - date_time.days_in_month(); - date_time.set_time(year, month, day); - } else { - month = target_month; - year = target_year; - day = (i - start_of_month) + 1; - date_time.set_time(year, month, day); - } + unsigned months; + mode() == Month ? months = 1 : months = 12; + for (unsigned i = 0; i < months; i++) { + if (mode() == Year) + view_month = i + 1; + for (unsigned j = 0; j < 42; j++) { + auto date_time = Core::DateTime::create(view_year, view_month, 1); + unsigned start_of_month = date_time.weekday(); + unsigned year; + unsigned month; + unsigned day; - m_calendar_tiles[i]->update_values(i, date_time); - m_calendar_tiles[i]->set_selected(date_time.year() == m_selected_date.year() && date_time.month() == m_selected_date.month() && date_time.day() == m_selected_date.day()); - m_calendar_tiles[i]->set_outside_selection(date_time.month() != selected_month() || date_time.year() != selected_year()); - m_calendar_tiles[i]->update(); + if (start_of_month == 0 && mode() != Year) { + month = (view_month - 1 == 0) ? 12 : view_month - 1; + year = (month == 12) ? view_year - 1 : view_year; + date_time.set_time(year, month, 1); + day = (date_time.days_in_month() - 6 + j); + } else if (start_of_month > j) { + month = (view_month - 1 == 0) ? 12 : view_month - 1; + year = (month == 12) ? view_year - 1 : view_year; + date_time.set_time(year, month, 1); + day = (date_time.days_in_month() - (start_of_month) + j) + 1; + } else if ((j - start_of_month) + 1 > date_time.days_in_month()) { + month = (view_month + 1) > 12 ? 1 : view_month + 1; + year = (month == 1) ? view_year + 1 : view_year; + day = ((j - start_of_month) + 1) - date_time.days_in_month(); + } else { + month = view_month; + year = view_year; + day = (j - start_of_month) + 1; + } + date_time.set_time(year, month, day); + + m_tiles[i][j].date_time = date_time; + m_tiles[i][j].is_outside_selected_month = (date_time.month() != view_month + || date_time.year() != view_year); + m_tiles[i][j].is_selected = (date_time.year() == m_selected_date.year() + && date_time.month() == m_selected_date.month() + && date_time.day() == m_selected_date.day() + && (mode() == Year ? !m_tiles[i][j].is_outside_selected_month : true)); + m_tiles[i][j].is_today = (date_time.day() == Core::DateTime::now().day() + && date_time.month() == Core::DateTime::now().month() + && date_time.year() == Core::DateTime::now().year()); + } + } + update(); +} + +String Calendar::formatted_date(Format format) +{ + switch (format) { + case ShortMonthYear: + return String::formatted("{} {}", short_month_names[view_month() - 1], view_year()); + case LongMonthYear: + return String::formatted("{} {}", long_month_names[view_month() - 1], view_year()); + case MonthOnly: + return String::formatted("{}", long_month_names[view_month() - 1]); + case YearOnly: + return String::number(view_year()); + default: + VERIFY_NOT_REACHED(); + } +} + +void Calendar::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + int width = unadjusted_tile_size().width(); + int height = unadjusted_tile_size().height(); + int x_offset = 0; + int y_offset = 0; + + if (is_showing_year()) { + auto year_only_rect = Gfx::IntRect( + 0, + 0, + event.rect().width(), + 22); + y_offset += year_only_rect.height(); + painter.fill_rect(year_only_rect, palette().hover_highlight()); + painter.draw_text(year_only_rect, formatted_date(YearOnly), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, (!m_show_month_tiles ? palette().threed_shadow1() : palette().threed_shadow2()), 1); + y_offset += 1; + if (!m_show_month_tiles) { + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, palette().threed_highlight(), 1); + y_offset += 1; + } + } else if (is_showing_month_and_year()) { + auto month_year_rect = Gfx::IntRect( + 0, + 0, + event.rect().width(), + 22); + painter.fill_rect(month_year_rect, palette().hover_highlight()); + auto month_rect = Gfx::IntRect( + 0, + 0, + event.rect().width() / 2, + 22); + auto year_rect = Gfx::IntRect( + event.rect().width() / 2, + 0, + event.rect().width() / 2 + ((event.rect().width() % 2) ? 1 : 0), + 22); + painter.fill_rect(month_rect, palette().hover_highlight()); + painter.fill_rect(year_rect, palette().hover_highlight()); + painter.draw_text(month_rect, formatted_date(MonthOnly), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + painter.draw_text(year_rect, formatted_date(YearOnly), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + y_offset += year_rect.height(); + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, palette().threed_shadow1(), 1); + y_offset += 1; + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, palette().threed_highlight(), 1); + y_offset += 1; + } + + if (mode() == Year && m_show_month_tiles) { + int i = 0; + for (int j = 0; j < 3; j++) { + x_offset = 0; + for (int k = 0; k < 4; k++) { + if (k > 0) + x_offset += m_months[i - 1].width; + auto month_tile_rect = Gfx::IntRect( + x_offset, + y_offset, + m_months[i].width, + m_months[i].height); + m_months[i].rect = month_tile_rect; + Gfx::StylePainter::paint_button( + painter, month_tile_rect, palette(), + Gfx::ButtonStyle::Normal, + m_months[i].is_being_pressed, + m_months[i].is_hovered, + false, true, false); + set_font(small_font); + painter.draw_text(month_tile_rect, m_months[i].name, font(), Gfx::TextAlignment::Center, palette().base_text()); i++; } + y_offset += m_months[i - 1].height; + } + return; + } + + if (is_showing_days_of_the_week()) { + auto days_of_the_week_rect = Gfx::IntRect( + 0, + y_offset, + event.rect().width(), + 16); + painter.fill_rect(days_of_the_week_rect, palette().hover_highlight()); + for (int i = 0; i < 7; i++) { + if (i > 0) + x_offset += m_days[i - 1].width + 1; + Gfx::IntRect day_rect = Gfx::IntRect( + event.rect().x() + x_offset, + y_offset, + m_days[i].width, + 16); + painter.draw_text(day_rect, m_days[i].name, small_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + } + y_offset += days_of_the_week_rect.height(); + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, palette().threed_shadow2(), 1); + y_offset += 1; + } + + if (mode() == Month) { + int i = 0; + for (int j = 0; j < 6; j++) { + x_offset = 0; + if (j > 0) + y_offset += m_tiles[0][(j - 1) * 7].height + 1; + for (int k = 0; k < 7; k++) { + if (k > 0) + x_offset += m_tiles[0][k - 1].width + 1; + auto tile_rect = Gfx::IntRect( + x_offset, + y_offset, + m_tiles[0][i].width, + m_tiles[0][i].height); + m_tiles[0][i].rect = tile_rect; + if (m_tiles[0][i].is_hovered || m_tiles[0][i].is_selected) + painter.fill_rect(tile_rect, palette().hover_highlight()); + else + painter.fill_rect(tile_rect, palette().base()); + + auto text_alignment = Gfx::TextAlignment::TopRight; + auto text_rect = Gfx::IntRect( + x_offset, + y_offset + 4, + m_tiles[0][i].width, + font().glyph_height() + 4); + + if (width > 150 && height > 150) { + set_font(extra_large_font); + } else if (width > 100 && height > 100) { + set_font(large_font); + } else if (width > 50 && height > 50) { + set_font(medium_font); + text_rect.set_width(m_tiles[0][i].width - 4); + } else if (width >= 30 && height >= 30) { + set_font(small_font); + text_rect.set_width(m_tiles[0][i].width - 4); + } else { + set_font(small_font); + text_alignment = Gfx::TextAlignment::Center; + text_rect = Gfx::IntRect(tile_rect); + } + + auto display_date = String::number(m_tiles[0][i].date_time.day()); + if (m_tiles[0][i].is_today) { + if (m_tiles[0][i].is_selected && width < 30) + painter.draw_rect(tile_rect, palette().base_text()); + painter.draw_text(text_rect, display_date, font().bold_variant(), text_alignment, palette().base_text()); + } else if (m_tiles[0][i].is_outside_selected_month) { + painter.draw_text(text_rect, display_date, font(), text_alignment, Color::LightGray); + } else { + if ((width < 30 || height < 30) && m_tiles[0][i].is_selected) + painter.draw_rect(tile_rect, palette().base_text()); + painter.draw_text(text_rect, display_date, font(), text_alignment, palette().base_text()); + } + i++; + } + } } else { + for (int i = 0; i < 4; i++) { + static int x_month_offset; + x_month_offset += (i > 0 ? m_month_size[i - 1].width() + 1 : 0); + auto month_rect = Gfx::IntRect( + event.rect().x() + x_month_offset, + y_offset, + m_month_size[i].width(), + 19); + painter.fill_rect(month_rect, palette().hover_highlight()); + painter.draw_text(month_rect, long_month_names[i], medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + if (i > 0 && i < 4) { + painter.draw_line({ x_month_offset - 1, y_offset - 1 }, { x_month_offset - 1, y_offset + 18 }, palette().threed_shadow2(), 1); + painter.draw_line({ x_month_offset, y_offset - 1 }, { x_month_offset, y_offset + 18 }, palette().threed_highlight(), 1); + } + if (i == 3) + x_month_offset = 0; + } + y_offset += 19; + painter.draw_line({ 0, y_offset }, { event.rect().width(), y_offset }, palette().threed_shadow2(), 1); + y_offset += 1; + + int x_translation = 0; + int y_translation = y_offset; + for (int l = 0; l < 12; l++) { + if ((l > 0 && l < 4) || (l > 4 && l < 8) || (l > 8)) { + x_translation += m_month_size[l - 1].width() + 1; + } else if (l % 4 == 0) { + x_translation = 0; + } + if (l < 4 || (l > 4 && l < 8) || l > 8) { + y_offset = y_translation; + } else if (l == 4 || l == 8) { + y_translation += m_month_size[l - 1].height(); + painter.draw_line({ 0, y_translation }, { event.rect().width(), y_translation }, palette().threed_shadow1(), 1); + y_translation += 1; + painter.draw_line({ 0, y_translation }, { event.rect().width(), y_translation }, palette().threed_highlight(), 1); + y_translation += 1; + y_offset = y_translation; + for (int i = l; i < (l == 4 ? 8 : 12); i++) { + static int x_month_offset; + x_month_offset += (i > (l == 4 ? 4 : 8) ? m_month_size[i - 1].width() + 1 : 0); + auto month_rect = Gfx::IntRect( + event.rect().x() + x_month_offset, + y_offset, + m_month_size[i].width(), + 19); + painter.fill_rect(month_rect, palette().hover_highlight()); + painter.draw_text(month_rect, long_month_names[i], medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + if (i > (l == 4 ? 4 : 8) && i < (l == 4 ? 8 : 12)) { + painter.draw_line({ x_month_offset - 1, y_offset - 1 }, { x_month_offset - 1, y_offset + 18 }, palette().threed_shadow2(), 1); + painter.draw_line({ x_month_offset, y_offset - 1 }, { x_month_offset, y_offset + 18 }, palette().threed_highlight(), 1); + } + if (i == 7 || i == 11) + x_month_offset = 0; + } + y_translation += 19; + painter.draw_line({ 0, y_translation }, { event.rect().width(), y_translation }, palette().threed_shadow2(), 1); + y_translation += 1; + y_offset = y_translation; + } + + int i = 0; + for (int j = 0; j < 6; j++) { + x_offset = 0; + if (j > 0) + y_offset += m_tiles[l][(j - 1) * 7].height + (j < 6 ? 1 : 0); + if (j == 0 && l != 3 && l != 7 && l != 11) { + painter.draw_line( + { m_month_size[l].width() + x_translation, y_offset }, + { m_month_size[l].width() + x_translation, y_offset + m_month_size[l].height() }, + palette().threed_shadow2(), + 1); + } + for (int k = 0; k < 7; k++) { + if (k > 0) + x_offset += m_tiles[l][k - 1].width + 1; + auto tile_rect = Gfx::IntRect( + x_offset + x_translation, + y_offset, + m_tiles[l][i].width, + m_tiles[l][i].height); + m_tiles[l][i].rect = tile_rect; + + if (m_tiles[l][i].is_hovered || m_tiles[l][i].is_selected) + painter.fill_rect(tile_rect, palette().hover_highlight()); + else + painter.fill_rect(tile_rect, palette().base()); + + if (width > 50 && height > 50) { + set_font(medium_font); + } else { + set_font(small_font); + } + + auto display_date = String::number(m_tiles[l][i].date_time.day()); + if (m_tiles[l][i].is_today && !m_tiles[l][i].is_outside_selected_month) { + if (m_tiles[l][i].is_selected) + painter.draw_rect(tile_rect, palette().base_text()); + painter.draw_text(tile_rect, display_date, font().bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); + } else if (!m_tiles[l][i].is_outside_selected_month) { + if (m_tiles[l][i].is_selected) + painter.draw_rect(tile_rect, palette().base_text()); + painter.draw_text(tile_rect, display_date, font(), Gfx::TextAlignment::Center, palette().base_text()); + } + i++; + } + } + } + } +} + +void Calendar::leave_event(Core::Event&) +{ + int months; + mode() == Month ? months = 1 : months = 12; + for (int i = 0; i < months; i++) { + if (mode() == Year && m_show_month_tiles) { + m_months[i].is_hovered = false; + continue; + } else { + for (int j = 0; j < 42; j++) { + m_tiles[i][j].is_hovered = false; + } + } + } + update(); +} + +void Calendar::mousemove_event(GUI::MouseEvent& event) +{ + static int last_index_i; + static int last_index_j; + + if (mode() == Year && m_show_month_tiles) { + if (m_months[last_index_i].rect.contains(event.position()) && (m_months[last_index_i].is_hovered || m_months[last_index_i].is_being_pressed)) { + return; + } else { + m_months[last_index_i].is_hovered = false; + m_months[last_index_i].is_being_pressed = false; + update(m_months[last_index_i].rect); + } + } else { + if (m_tiles[last_index_i][last_index_j].rect.contains(event.position()) && m_tiles[last_index_i][last_index_j].is_hovered) { + return; + } else { + m_tiles[last_index_i][last_index_j].is_hovered = false; + update(m_tiles[last_index_i][last_index_j].rect); + } + } + + int months; + mode() == Month ? months = 1 : months = 12; + for (int i = 0; i < months; i++) { + if (mode() == Year && m_show_month_tiles) { + if (m_months[i].rect.contains(event.position())) { + if (m_currently_pressed_index == -1 || m_currently_pressed_index == i) + m_months[i].is_hovered = true; + if (m_currently_pressed_index == i) + m_months[i].is_being_pressed = true; + update(m_months[last_index_i].rect); + if (m_months[i].is_being_pressed == true) + m_currently_pressed_index = i; + last_index_i = i; + update(m_months[i].rect); + break; + } + } else { + for (int j = 0; j < 42; j++) { + if (mode() == Year && m_tiles[i][j].is_outside_selected_month) + continue; + if (m_tiles[i][j].rect.contains(event.position())) { + m_tiles[i][j].is_hovered = true; + update(m_tiles[last_index_i][last_index_j].rect); + last_index_i = i; + last_index_j = j; + update(m_tiles[i][j].rect); + break; + } + } + } + } +} + +void Calendar::mouseup_event(GUI::MouseEvent& event) +{ + int months; + mode() == Month ? months = 1 : months = 12; + for (int i = 0; i < months; i++) { + if (mode() == Year && m_show_month_tiles) { + if (m_months[i].rect.contains(event.position()) && m_months[i].is_being_pressed) { + set_view_date(view_year(), (unsigned)i + 1); + toggle_mode(); + if (on_month_click) + on_month_click(); + } + } else { + for (int j = 0; j < 42; j++) { + if (mode() == Year && m_tiles[i][j].is_outside_selected_month) + continue; + if (m_tiles[i][j].rect.contains(event.position())) { + m_previous_selected_date = m_selected_date; + m_selected_date = m_tiles[i][j].date_time; + update_tiles(m_selected_date.year(), m_selected_date.month()); + if (on_tile_click) + on_tile_click(); + } + } + } + if (months == 12) { + m_months[i].is_being_pressed = false; + m_months[i].is_hovered = false; + } + } + m_currently_pressed_index = -1; + update(); +} + +void Calendar::mousedown_event(GUI::MouseEvent& event) +{ + if (mode() == Year && m_show_month_tiles) { for (int i = 0; i < 12; i++) { - auto date_time = Core::DateTime::create(target_year, i + 1, 1); - m_month_tiles[i]->update_values(date_time); + if (m_months[i].rect.contains(event.position())) { + m_months[i].is_being_pressed = true; + m_currently_pressed_index = i; + update(m_months[i].rect); + break; + } } } } -const String Calendar::selected_calendar_text(bool long_names) +void Calendar::doubleclick_event(GUI::MouseEvent& event) { - if (mode() == Month) - return String::formatted("{} {}", long_names ? long_month_names[m_selected_month - 1] : short_month_names[m_selected_month - 1], m_selected_year); - else - return String::number(m_selected_year); -} - -void Calendar::set_selected_calendar(unsigned int year, unsigned int month) -{ - m_selected_year = year; - m_selected_month = month; -} - -Calendar::MonthTile::MonthTile(int index, Core::DateTime date_time) - : m_index(index) - , m_date_time(date_time) -{ -} - -Calendar::MonthTile::~MonthTile() -{ -} - -void Calendar::MonthTile::mouseup_event(GUI::MouseEvent& event) -{ - if (on_indexed_click) - on_indexed_click(m_index); - - GUI::Button::mouseup_event(event); -} - -Calendar::CalendarTile::CalendarTile(int index, Core::DateTime date_time) -{ - set_frame_thickness(0); - update_values(index, date_time); -} - -void Calendar::CalendarTile::update_values(int index, Core::DateTime date_time) -{ - m_index = index; - m_date_time = date_time; - m_display_date = (m_date_time.day() == 1) ? String::formatted("{} {}", short_month_names[m_date_time.month() - 1], m_date_time.day()) : String::number(m_date_time.day()); -} - -Calendar::CalendarTile::~CalendarTile() -{ -} - -void Calendar::CalendarTile::doubleclick_event(GUI::MouseEvent&) -{ - if (on_doubleclick) - on_doubleclick(m_index); -} - -void Calendar::CalendarTile::mousedown_event(GUI::MouseEvent&) -{ - if (on_click) - on_click(m_index); -} -void Calendar::CalendarTile::enter_event(Core::Event&) -{ - m_hovered = true; - update(); -} - -void Calendar::CalendarTile::leave_event(Core::Event&) -{ - m_hovered = false; - update(); -} - -bool Calendar::CalendarTile::is_today() const -{ - auto current_date_time = Core::DateTime::now(); - return m_date_time.day() == current_date_time.day() && m_date_time.month() == current_date_time.month() && m_date_time.year() == current_date_time.year(); -} - -void Calendar::CalendarTile::paint_event(GUI::PaintEvent& event) -{ - GUI::Frame::paint_event(event); - - GUI::Painter painter(*this); - painter.add_clip_rect(frame_inner_rect()); - - if (is_hovered() || is_selected()) - painter.fill_rect(frame_inner_rect(), palette().hover_highlight()); - else - painter.fill_rect(frame_inner_rect(), palette().base()); - - if (m_index < 7) - painter.draw_line(frame_inner_rect().top_left(), frame_inner_rect().top_right(), Color::NamedColor::Black); - if (!((m_index + 1) % 7 == 0) && has_grid()) - painter.draw_line(frame_inner_rect().top_right(), frame_inner_rect().bottom_right(), Color::NamedColor::Black); - if (m_index < 35 && has_grid()) - painter.draw_line(frame_inner_rect().bottom_left(), frame_inner_rect().bottom_right(), Color::NamedColor::Black); - - Gfx::IntRect day_rect; - if (has_grid()) { - day_rect = Gfx::IntRect(frame_inner_rect().x(), frame_inner_rect().y(), frame_inner_rect().width(), font().glyph_height() + 4); - day_rect.set_y(frame_inner_rect().y() + 4); - } else { - day_rect = Gfx::IntRect(frame_inner_rect()); - } - - int highlight_rect_width = (font().glyph_width('0') * (m_display_date.length() + 1)) + 2; - auto display_date = (m_date_time.day() == 1 && frame_inner_rect().width() > highlight_rect_width) ? m_display_date : String::number(m_date_time.day()); - - if (is_today()) { - if (has_grid()) { - auto highlight_rect = Gfx::IntRect(day_rect.width() / 2 - (highlight_rect_width / 2), day_rect.y(), highlight_rect_width, font().glyph_height() + 4); - painter.draw_rect(highlight_rect, palette().base_text()); - } else if (is_selected()) { - painter.draw_rect(frame_inner_rect(), palette().base_text()); + int months; + mode() == Month ? months = 1 : months = 12; + for (int i = 0; i < months; i++) { + for (int j = 0; j < 42; j++) { + if (m_tiles[i][j].date_time.day() != m_previous_selected_date.day()) + continue; + if (mode() == Year && m_tiles[i][j].is_outside_selected_month) + continue; + if (m_tiles[i][j].rect.contains(event.position())) { + if (on_tile_doubleclick) + on_tile_doubleclick(); + } } - painter.draw_text(day_rect, display_date, Gfx::FontDatabase::default_bold_font(), Gfx::TextAlignment::Center, palette().base_text()); - } else if (is_outside_selection()) { - painter.draw_text(day_rect, display_date, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::Center, Color::LightGray); - } else { - if (!has_grid() && is_selected()) - painter.draw_rect(frame_inner_rect(), palette().base_text()); - painter.draw_text(day_rect, display_date, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::Center, palette().base_text()); } } - } diff --git a/Userland/Libraries/LibGUI/Calendar.h b/Userland/Libraries/LibGUI/Calendar.h index a160665591..0cc84bf3df 100644 --- a/Userland/Libraries/LibGUI/Calendar.h +++ b/Userland/Libraries/LibGUI/Calendar.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2020, Ryan Grieb - * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2020-2021, the SerenityOS developers * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,9 +29,7 @@ #include #include -#include #include -#include #include namespace GUI { @@ -45,97 +43,108 @@ public: Year }; - enum { - ShortNames, - LongNames + enum Format { + ShortMonthYear, + LongMonthYear, + MonthOnly, + YearOnly }; - Calendar(Core::DateTime); + Calendar(Core::DateTime date_time = Core::DateTime::now(), Mode mode = Month); virtual ~Calendar() override; - unsigned int selected_year() const { return m_selected_year; } - unsigned int selected_month() const { return m_selected_month; } - const String selected_calendar_text(bool long_names = ShortNames); - void update_tiles(unsigned int target_year, unsigned int target_month); - void set_selected_calendar(unsigned int year, unsigned int month); void set_selected_date(Core::DateTime date_time) { m_selected_date = date_time; } Core::DateTime selected_date() const { return m_selected_date; } - void toggle_mode(); - void set_grid(bool grid); - bool has_grid() { return m_grid; } - Mode mode() const { return m_mode; } - Function on_calendar_tile_click; - Function on_calendar_tile_doubleclick; - Function on_month_tile_click; + void set_view_date(unsigned year, unsigned month) + { + m_view_year = year; + m_view_month = month; + } + unsigned view_year() const { return m_view_year; } + unsigned view_month() const { return m_view_month; } + + String formatted_date(Format format = LongMonthYear); + + Mode mode() const { return m_mode; } + void toggle_mode(); + + void update_tiles(unsigned year, unsigned month); + + void set_grid(bool); + bool has_grid() const { return m_grid; } + + void set_show_year(bool b) { m_show_year = b; } + bool is_showing_year() const { return m_show_year; } + + void set_show_month_and_year(bool b) { m_show_month_year = b; } + bool is_showing_month_and_year() const { return m_show_month_year; } + + void set_show_days_of_the_week(bool b) { m_show_days = b; } + bool is_showing_days_of_the_week() const { return m_show_days; } + + Gfx::IntSize unadjusted_tile_size() const { return m_unadjusted_tile_size; } + void set_unadjusted_tile_size(int width, int height) + { + m_unadjusted_tile_size.set_width(width); + m_unadjusted_tile_size.set_height(height); + } + + Function on_tile_click; + Function on_tile_doubleclick; + Function on_month_click; private: virtual void resize_event(GUI::ResizeEvent&) override; + virtual void paint_event(GUI::PaintEvent&) override; + virtual void mousemove_event(GUI::MouseEvent&) override; + virtual void mousedown_event(MouseEvent&) override; + virtual void mouseup_event(MouseEvent&) override; + virtual void doubleclick_event(MouseEvent&); + virtual void leave_event(Core::Event&) override; - class CalendarTile final : public GUI::Frame { - C_OBJECT(CalendarTile) - public: - CalendarTile(int index, Core::DateTime m_date_time); - void update_values(int index, Core::DateTime date_time); - virtual ~CalendarTile() override; - bool is_today() const; - bool is_hovered() const { return m_hovered; } - bool is_selected() const { return m_selected; } - void set_selected(bool b) { m_selected = b; } - bool is_outside_selection() const { return m_outside_selection; } - void set_outside_selection(bool b) { m_outside_selection = b; } - bool has_grid() const { return m_grid; } - void set_grid(bool b) { m_grid = b; } - Core::DateTime get_date_time() { return m_date_time; } - Function on_doubleclick; - Function on_click; - - private: - virtual void doubleclick_event(GUI::MouseEvent&) override; - virtual void mousedown_event(GUI::MouseEvent&) override; - virtual void enter_event(Core::Event&) override; - virtual void leave_event(Core::Event&) override; - virtual void paint_event(GUI::PaintEvent&) override; - - int m_index { 0 }; - bool m_outside_selection { false }; - bool m_hovered { false }; - bool m_selected { false }; - bool m_grid { true }; - String m_display_date; - Core::DateTime m_date_time; + struct Day { + String name; + int width { 0 }; + int height { 16 }; }; + Vector m_days; - class MonthTile final : public GUI::Button { - C_OBJECT(MonthTile) - public: - MonthTile(int index, Core::DateTime m_date_time); - virtual ~MonthTile() override; - void update_values(Core::DateTime date_time) { m_date_time = date_time; } - Core::DateTime get_date_time() { return m_date_time; } - Function on_indexed_click; - - private: - virtual void mouseup_event(GUI::MouseEvent&) override; - - int m_index { 0 }; - Core::DateTime m_date_time; + struct MonthTile { + String name; + Gfx::IntRect rect; + int width { 0 }; + int height { 0 }; + bool is_hovered { false }; + bool is_being_pressed { false }; }; + Vector m_months; - RefPtr m_month_tiles[12]; - RefPtr m_calendar_tiles[42]; - RefPtr m_day_names[7]; - RefPtr m_week_rows[6]; - RefPtr m_month_rows[3]; - RefPtr m_month_tile_container; - RefPtr m_calendar_tile_container; - RefPtr m_day_name_container; + struct Tile { + Core::DateTime date_time; + Gfx::IntRect rect; + int width { 0 }; + int height { 0 }; + bool is_today { false }; + bool is_selected { false }; + bool is_hovered { false }; + bool is_outside_selected_month { false }; + }; + Vector m_tiles[12]; + bool m_grid { true }; + bool m_show_month_year { true }; + bool m_show_days { true }; + bool m_show_year { false }; + bool m_show_month_tiles { false }; + int m_currently_pressed_index { -1 }; + unsigned m_view_year; + unsigned m_view_month; Core::DateTime m_selected_date; Core::DateTime m_previous_selected_date; - unsigned int m_selected_year { 0 }; - unsigned int m_selected_month { 0 }; - bool m_grid { true }; + Gfx::IntSize m_unadjusted_tile_size; + Gfx::IntSize m_event_size; + Gfx::IntSize m_month_size[12]; Mode m_mode { Month }; };