ladybird/Userland/Libraries/LibGUI/Calendar.cpp
Michael Manganiello 56081cdb6e LibGUI: Remove Tile.date_time member from Calendar
Currently, navigating through different years in the Year view of the
Calendar app or the taskbar is very slow.
Profiling results show that almost all the time is spent in
`Calendar::update_tiles`, and specifically, in `DateTime::create` and
`DateTime::set_time`.

Performance can improve substantially if the `TZ` environment variable
is set [0], but we can improve the current code to perform better
nevertheless :^)

This diff focuses on removing the need of the `Tile` struct to require
the instantiation of a `DateTime` object, which avoids _at least_ 365
object instantiations in the Year view, on each `update_tiles` call.
Instead, as the `date_time` isn't used often, we can instantiate it on
demand when a particular date is selected.

[0] https://blog.packagecloud.io/set-environment-variable-save-thousands-of-system-calls/
2022-04-20 18:37:56 +02:00

738 lines
29 KiB
C++

/*
* Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com>
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/DateConstants.h>
#include <LibCore/DateTime.h>
#include <LibGUI/Calendar.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Window.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Palette.h>
REGISTER_WIDGET(GUI, Calendar);
namespace GUI {
static auto const extra_large_font = Gfx::BitmapFont::load_from_file("/res/fonts/MarietaRegular36.font");
static auto const large_font = Gfx::BitmapFont::load_from_file("/res/fonts/MarietaRegular24.font");
static auto const medium_font = Gfx::BitmapFont::load_from_file("/res/fonts/PebbletonRegular14.font");
static auto const 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_mode(mode)
{
set_fill_with_background_color(true);
for (int i = 0; i < 7; i++) {
Day day;
m_days.append(move(day));
}
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));
}
}
update_tiles(m_selected_date.year(), m_selected_date.month());
}
void Calendar::set_grid(bool show)
{
if (m_grid == show)
return;
m_grid = show;
}
void Calendar::toggle_mode()
{
m_mode == Month ? m_mode = Year : m_mode = Month;
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());
invalidate_layout();
}
void Calendar::resize_event(GUI::ResizeEvent& event)
{
m_event_size.set_width(event.size().width() - (frame_thickness() * 2));
m_event_size.set_height(event.size().height() - (frame_thickness() * 2));
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);
int const 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);
int const VERT_GRID_LINES = 27;
int const HORI_GRID_LINES = 15;
int const THREADING = 3;
int const 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;
else
m_show_month_tiles = false;
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;
}
for (int i = 0; i < 12; 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;
}
}
}
}
}
void Calendar::update_tiles(unsigned view_year, unsigned view_month)
{
set_view_date(view_year, view_month);
auto now = Core::DateTime::now();
unsigned months = mode() == Month ? 1 : 12;
for (unsigned i = 0; i < months; i++) {
if (mode() == Year)
view_month = i + 1;
auto first_day_of_current_month = Core::DateTime::create(view_year, view_month, 1);
unsigned start_of_month = first_day_of_current_month.weekday();
unsigned days_from_previous_month_to_show = start_of_month == 0 ? 7 : start_of_month;
for (unsigned j = 0; j < 42; j++) {
unsigned year;
unsigned month;
unsigned day;
if (j + 1 <= days_from_previous_month_to_show) {
// Day from previous month.
month = (view_month - 1 == 0) ? 12 : view_month - 1;
year = (month == 12) ? view_year - 1 : view_year;
day = days_in_month(year, month) + j + 1 - days_from_previous_month_to_show;
} else if (j + 1 > days_from_previous_month_to_show + first_day_of_current_month.days_in_month()) {
// Day from next month.
month = (view_month + 1) > 12 ? 1 : view_month + 1;
year = (month == 1) ? view_year + 1 : view_year;
day = j + 1 - days_from_previous_month_to_show - first_day_of_current_month.days_in_month();
} else {
// Day from current month.
month = view_month;
year = view_year;
day = j + 1 - days_from_previous_month_to_show;
}
m_tiles[i][j].year = year;
m_tiles[i][j].month = month;
m_tiles[i][j].day = day;
m_tiles[i][j].is_outside_selected_month = (month != view_month
|| year != view_year);
m_tiles[i][j].is_selected = (year == m_selected_date.year()
&& month == m_selected_date.month()
&& day == m_selected_date.day()
&& (mode() == Year ? !m_tiles[i][j].is_outside_selected_month : true));
m_tiles[i][j].is_today = (day == now.day()
&& month == now.month()
&& year == 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::Frame::paint_event(event);
GUI::Painter painter(*this);
painter.add_clip_rect(frame_inner_rect());
painter.add_clip_rect(event.rect());
if (has_grid())
painter.fill_rect(frame_inner_rect(), palette().threed_shadow2());
else
painter.fill_rect(frame_inner_rect(), palette().base());
painter.translate(frame_thickness(), frame_thickness());
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,
frame_inner_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 }, { frame_inner_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 }, { frame_inner_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,
frame_inner_rect().width(),
22);
painter.fill_rect(month_year_rect, palette().hover_highlight());
month_year_rect.set_width(frame_inner_rect().width() / 2);
painter.draw_text(month_year_rect, formatted_date(MonthOnly), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text());
month_year_rect.set_x(month_year_rect.width() + (frame_inner_rect().width() % 2 ? 1 : 0));
painter.draw_text(month_year_rect, formatted_date(YearOnly), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text());
y_offset += 22;
painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_shadow1(), 1);
y_offset += 1;
painter.draw_line({ 0, y_offset }, { frame_inner_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.translated(frame_thickness(), frame_thickness());
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,
frame_inner_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(
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 }, { frame_inner_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.translated(frame_thickness(), frame_thickness());
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 - 4,
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);
} else if (width >= 30 && height >= 30) {
set_font(small_font);
} 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].day);
if (m_tiles[0][i].is_selected && (width < 30 || height < 30))
painter.draw_rect(tile_rect, palette().base_text());
if (m_tiles[0][i].is_today && !m_tiles[0][i].is_outside_selected_month) {
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, m_tiles[0][i].is_today ? font().bold_variant() : font(), text_alignment, Color::LightGray);
} else {
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(
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 }, { frame_inner_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 }, { frame_inner_rect().width(), y_translation }, palette().threed_shadow1(), 1);
y_translation += 1;
painter.draw_line({ 0, y_translation }, { frame_inner_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(
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 }, { frame_inner_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.translated(frame_thickness(), frame_thickness());
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].day);
if (m_tiles[l][i].is_selected)
painter.draw_rect(tile_rect, palette().base_text());
if (m_tiles[l][i].is_today && !m_tiles[l][i].is_outside_selected_month) {
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) {
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 = Core::DateTime::create(m_tiles[i][j].year, m_tiles[i][j].month, m_tiles[i][j].day);
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++) {
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::doubleclick_event(GUI::MouseEvent& event)
{
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].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();
}
}
}
}
}