summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibGUI/Calendar.cpp911
-rw-r--r--Userland/Libraries/LibGUI/Calendar.h163
2 files changed, 750 insertions, 324 deletions
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 <ryan.m.grieb@gmail.com>
- * 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 <LibCore/DateTime.h>
-#include <LibGUI/BoxLayout.h>
-#include <LibGUI/Button.h>
#include <LibGUI/Calendar.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Window.h>
-#include <LibGfx/Font.h>
#include <LibGfx/FontDatabase.h>
#include <LibGfx/Palette.h>
+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<GUI::VerticalBoxLayout>();
- layout()->set_spacing(0);
-
- m_day_name_container = add<GUI::Widget>();
- m_day_name_container->set_layout<GUI::HorizontalBoxLayout>();
- 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<GUI::Label>();
- day->set_font(Gfx::FontDatabase::default_bold_font());
- }
+ set_background_role(Gfx::ColorRole::ThreedShadow2);
- m_calendar_tile_container = add<GUI::Widget>();
- m_calendar_tile_container->set_layout<GUI::VerticalBoxLayout>();
- m_calendar_tile_container->layout()->set_spacing(0);
-
- for (auto& row : m_week_rows) {
- row = m_calendar_tile_container->add<GUI::Widget>();
- row->set_layout<GUI::HorizontalBoxLayout>();
- row->layout()->set_spacing(0);
+ for (int i = 0; i < 7; i++) {
+ Day day;
+ m_days.append(move(day));
}
-
- 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<CalendarTile>(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<GUI::Widget>();
- m_month_tile_container->set_visible(false);
- m_month_tile_container->set_layout<GUI::VerticalBoxLayout>();
- 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<GUI::Widget>();
- row->set_layout<GUI::HorizontalBoxLayout>();
- 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<MonthTile>(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());
+ invalidate_layout();
}
-void Calendar::set_grid(bool grid)
+void Calendar::resize_event(GUI::ResizeEvent& event)
{
- if (m_grid == grid)
- return;
+ m_event_size.set_width(event.size().width());
+ m_event_size.set_height(event.size().height());
- m_grid = grid;
+ 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 (int i = 0; i < 42; i++) {
- m_calendar_tiles[i]->set_grid(grid);
- m_calendar_tiles[i]->update();
- }
-}
+ for (auto& day : m_days)
+ day.width = tile_width;
-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]);
+ 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;
+ }
}
- }
- 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]);
+ 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);
}
- }
- (event.size().width() < 200) ? set_grid(false) : set_grid(true);
-}
+ 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];
+ }
-void Calendar::update_tiles(unsigned int target_year, unsigned int target_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;
-
- 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);
+ 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;
+ }
}
+ }
- 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();
- i++;
+ if (height_remainder) {
+ for (int i = 0; i < height_remainder * 4; i++) {
+ m_months[i].height = month_tile_height + 1;
+ }
}
- } else {
+ return;
+ }
+
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);
+ 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;
+ }
+ }
}
}
}
-const String Calendar::selected_calendar_text(bool long_names)
+void Calendar::update_tiles(unsigned view_year, unsigned view_month)
{
- 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);
+ set_view_date(view_year, view_month);
+
+ 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;
+
+ 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();
}
-void Calendar::set_selected_calendar(unsigned int year, unsigned int month)
+String Calendar::formatted_date(Format format)
{
- m_selected_year = year;
- m_selected_month = month;
+ 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();
+ }
}
-Calendar::MonthTile::MonthTile(int index, Core::DateTime date_time)
- : m_index(index)
- , m_date_time(date_time)
+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;
+ }
-Calendar::MonthTile::~MonthTile()
-{
-}
+ 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;
+ }
-void Calendar::MonthTile::mouseup_event(GUI::MouseEvent& event)
-{
- if (on_indexed_click)
- on_indexed_click(m_index);
+ 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;
+ }
- GUI::Button::mouseup_event(event);
-}
+ 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);
+ }
-Calendar::CalendarTile::CalendarTile(int index, Core::DateTime date_time)
-{
- set_frame_thickness(0);
- update_values(index, date_time);
-}
+ 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;
+ }
-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());
+ 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++;
+ }
+ }
+ }
+ }
}
-Calendar::CalendarTile::~CalendarTile()
+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::CalendarTile::doubleclick_event(GUI::MouseEvent&)
+void Calendar::mousemove_event(GUI::MouseEvent& event)
{
- if (on_doubleclick)
- on_doubleclick(m_index);
-}
+ 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);
+ }
+ }
-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();
+ 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::CalendarTile::leave_event(Core::Event&)
+void Calendar::mouseup_event(GUI::MouseEvent& event)
{
- m_hovered = false;
+ 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();
}
-bool Calendar::CalendarTile::is_today() const
+void Calendar::mousedown_event(GUI::MouseEvent& event)
{
- 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();
+ if (mode() == Year && m_show_month_tiles) {
+ for (int i = 0; i < 12; i++) {
+ 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;
+ }
+ }
+ }
}
-void Calendar::CalendarTile::paint_event(GUI::PaintEvent& event)
+void Calendar::doubleclick_event(GUI::MouseEvent& 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 <ryan.m.grieb@gmail.com>
- * 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 <AK/String.h>
#include <LibCore/DateTime.h>
-#include <LibGUI/Button.h>
#include <LibGUI/Frame.h>
-#include <LibGUI/Label.h>
#include <LibGUI/Widget.h>
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; }
+
+ 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();
- Function<void()> on_calendar_tile_click;
- Function<void()> on_calendar_tile_doubleclick;
- Function<void()> on_month_tile_click;
+ void update_tiles(unsigned year, unsigned month);
-private:
- virtual void resize_event(GUI::ResizeEvent&) override;
+ void set_grid(bool);
+ bool has_grid() const { return m_grid; }
- 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<void(int index)> on_doubleclick;
- Function<void(int index)> 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;
- };
+ void set_show_year(bool b) { m_show_year = b; }
+ bool is_showing_year() const { return m_show_year; }
- 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<void(int index)> on_indexed_click;
+ 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; }
- private:
- virtual void mouseup_event(GUI::MouseEvent&) override;
+ 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; }
- int m_index { 0 };
- Core::DateTime m_date_time;
- };
+ 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);
+ }
- RefPtr<MonthTile> m_month_tiles[12];
- RefPtr<CalendarTile> m_calendar_tiles[42];
- RefPtr<GUI::Label> m_day_names[7];
- RefPtr<GUI::Widget> m_week_rows[6];
- RefPtr<GUI::Widget> m_month_rows[3];
- RefPtr<GUI::Widget> m_month_tile_container;
- RefPtr<GUI::Widget> m_calendar_tile_container;
- RefPtr<GUI::Widget> m_day_name_container;
+ Function<void()> on_tile_click;
+ Function<void()> on_tile_doubleclick;
+ Function<void()> 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;
+
+ struct Day {
+ String name;
+ int width { 0 };
+ int height { 16 };
+ };
+ Vector<Day> m_days;
+
+ struct MonthTile {
+ String name;
+ Gfx::IntRect rect;
+ int width { 0 };
+ int height { 0 };
+ bool is_hovered { false };
+ bool is_being_pressed { false };
+ };
+ Vector<MonthTile> m_months;
+
+ 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<Tile> 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 };
};