Serenity Operating System
at master 803 lines 33 kB view raw
1/* 2 * Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com> 3 * Copyright (c) 2020-2022, the SerenityOS developers. 4 * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <AK/DateConstants.h> 10#include <AK/String.h> 11#include <LibConfig/Client.h> 12#include <LibCore/DateTime.h> 13#include <LibGUI/Calendar.h> 14#include <LibGUI/Painter.h> 15#include <LibGUI/Window.h> 16#include <LibGfx/Font/FontDatabase.h> 17#include <LibGfx/Palette.h> 18 19REGISTER_WIDGET(GUI, Calendar); 20 21namespace GUI { 22 23static auto const extra_large_font = Gfx::BitmapFont::load_from_file("/res/fonts/MarietaRegular36.font"); 24static auto const large_font = Gfx::BitmapFont::load_from_file("/res/fonts/MarietaRegular24.font"); 25static auto const medium_font = Gfx::BitmapFont::load_from_file("/res/fonts/PebbletonRegular14.font"); 26static auto const small_font = Gfx::BitmapFont::load_from_file("/res/fonts/KaticaRegular10.font"); 27 28Calendar::Calendar(Core::DateTime date_time, Mode mode) 29 : m_selected_date(date_time) 30 , m_mode(mode) 31{ 32 auto first_day_of_week = Config::read_string("Calendar"sv, "View"sv, "FirstDayOfWeek"sv, "Sunday"sv); 33 m_first_day_of_week = static_cast<DayOfWeek>(day_of_week_index(first_day_of_week)); 34 35 auto first_day_of_weekend = Config::read_string("Calendar"sv, "View"sv, "FirstDayOfWeekend"sv, "Saturday"sv); 36 m_first_day_of_weekend = static_cast<DayOfWeek>(day_of_week_index(first_day_of_weekend)); 37 38 auto weekend_length = Config::read_i32("Calendar"sv, "View"sv, "WeekendLength"sv, 2); 39 m_weekend_length = weekend_length; 40 41 set_fill_with_background_color(true); 42 43 for (int i = 0; i < 7; i++) { 44 Day day; 45 m_days.append(move(day)); 46 } 47 for (int i = 0; i < 12; i++) { 48 MonthTile month; 49 m_months.append(move(month)); 50 for (int j = 0; j < 42; j++) { 51 Tile tile; 52 m_tiles[i].append(move(tile)); 53 } 54 } 55 56 auto default_view = Config::read_string("Calendar"sv, "View"sv, "DefaultView"sv, "Month"sv); 57 if (default_view == "Year") { 58 m_mode = Year; 59 m_show_days = false; 60 m_show_year = true; 61 m_show_month_year = true; 62 } 63 64 update_tiles(m_selected_date.year(), m_selected_date.month()); 65} 66 67void Calendar::set_grid(bool show) 68{ 69 if (m_grid == show) 70 return; 71 m_grid = show; 72} 73 74void Calendar::toggle_mode() 75{ 76 m_mode == Month ? m_mode = Year : m_mode = Month; 77 set_show_days_of_the_week(!m_show_days); 78 set_show_year(!m_show_year); 79 set_show_month_and_year(!m_show_month_year); 80 update_tiles(this->view_year(), this->view_month()); 81 this->resize(this->height(), this->width()); 82 invalidate_layout(); 83} 84 85void Calendar::resize_event(GUI::ResizeEvent& event) 86{ 87 m_event_size.set_width(event.size().width() - (frame_thickness() * 2)); 88 m_event_size.set_height(event.size().height() - (frame_thickness() * 2)); 89 90 if (mode() == Month) { 91 if (m_event_size.width() < 160 || m_event_size.height() < 130) 92 set_show_month_and_year(false); 93 else if (m_event_size.width() >= 160 && m_event_size.height() >= 130) 94 set_show_month_and_year(true); 95 96 set_show_year(false); 97 98 int const GRID_LINES = 6; 99 int tile_width = (m_event_size.width() - GRID_LINES) / 7; 100 int width_remainder = (m_event_size.width() - GRID_LINES) % 7; 101 int y_offset = is_showing_days_of_the_week() ? 16 : 0; 102 y_offset += is_showing_month_and_year() ? 24 : 0; 103 int tile_height = (m_event_size.height() - y_offset - GRID_LINES) / 6; 104 int height_remainder = (m_event_size.height() - y_offset - GRID_LINES) % 6; 105 106 set_unadjusted_tile_size(tile_width, tile_height); 107 tile_width < 30 || tile_height < 30 ? set_grid(false) : set_grid(true); 108 109 for (int i = 0; i < 42; i++) { 110 m_tiles[0][i].width = tile_width; 111 m_tiles[0][i].height = tile_height; 112 } 113 114 for (auto& day : m_days) 115 day.width = tile_width; 116 117 for (int i = 0; i < width_remainder; i++) { 118 m_days[i].width = (tile_width + 1); 119 for (int j = i; j < i + 36; j += 7) { 120 m_tiles[0][j].width = tile_width + 1; 121 } 122 } 123 124 for (int j = 0; j < height_remainder * 7; j++) 125 m_tiles[0][j].height = tile_height + 1; 126 127 if (is_showing_days_of_the_week()) { 128 for (int i = 0; i < 7; i++) { 129 if (m_event_size.width() < 138) 130 m_days[i].name = micro_day_names[i]; 131 else if (m_event_size.width() < 200) 132 m_days[i].name = mini_day_names[i]; 133 else if (m_event_size.width() < 480) 134 m_days[i].name = short_day_names[i]; 135 else 136 m_days[i].name = long_day_names[i]; 137 } 138 } 139 } else { 140 if (m_event_size.width() < 140 && m_event_size.height() < 120) 141 set_show_year(false); 142 else if (m_event_size.width() >= 140 && m_event_size.height() >= 120) 143 set_show_year(true); 144 145 set_show_month_and_year(false); 146 147 int const VERT_GRID_LINES = 27; 148 int const HORI_GRID_LINES = 15; 149 int const THREADING = 3; 150 int const MONTH_TITLE = 19; 151 int tile_width = (m_event_size.width() - VERT_GRID_LINES) / 28; 152 int width_remainder = (m_event_size.width() - VERT_GRID_LINES) % 28; 153 int y_offset = is_showing_year() ? 22 : 0; 154 y_offset += (MONTH_TITLE * 3) + (THREADING * 3); 155 int tile_height = (m_event_size.height() - y_offset - HORI_GRID_LINES) / 18; 156 int height_remainder = (m_event_size.height() - y_offset - HORI_GRID_LINES) % 18; 157 158 set_grid(false); 159 set_unadjusted_tile_size(tile_width, tile_height); 160 if (unadjusted_tile_size().width() < 17 || unadjusted_tile_size().height() < 13) 161 m_show_month_tiles = true; 162 else 163 m_show_month_tiles = false; 164 165 if (m_show_month_tiles) { 166 int month_tile_width = m_event_size.width() / 4; 167 int width_remainder = m_event_size.width() % 4; 168 int y_offset = is_showing_year() ? 23 : 0; 169 int month_tile_height = (m_event_size.height() - y_offset) / 3; 170 int height_remainder = (m_event_size.height() - y_offset) % 3; 171 172 for (int i = 0; i < 12; i++) { 173 m_months[i].width = month_tile_width; 174 m_months[i].height = month_tile_height; 175 if (m_event_size.width() < 250) 176 m_months[i].name = short_month_names[i]; 177 else 178 m_months[i].name = long_month_names[i]; 179 } 180 181 if (width_remainder) { 182 for (int i = 0; i < width_remainder; i++) { 183 for (int j = i; j < 12; j += 4) { 184 m_months[j].width = month_tile_width + 1; 185 } 186 } 187 } 188 189 if (height_remainder) { 190 for (int i = 0; i < height_remainder * 4; i++) { 191 m_months[i].height = month_tile_height + 1; 192 } 193 } 194 return; 195 } 196 197 for (int i = 0; i < 12; i++) { 198 int remainder = 0; 199 if (i == 0 || i == 4 || i == 8) 200 remainder = min(width_remainder, 7); 201 if (i == 1 || i == 5 || i == 9) 202 width_remainder > 7 ? remainder = min(width_remainder - 7, 7) : remainder = 0; 203 if (i == 2 || i == 6 || i == 10) 204 width_remainder > 14 ? remainder = min(width_remainder - 14, 7) : remainder = 0; 205 if (i == 3 || i == 7 || i == 11) 206 width_remainder > 21 ? remainder = width_remainder - 21 : remainder = 0; 207 m_month_size[i].set_width(remainder + 6 + tile_width * 7); 208 209 if (i >= 0 && i <= 3) 210 remainder = min(height_remainder, 6); 211 if (i >= 4 && i <= 7) 212 height_remainder > 6 ? remainder = min(height_remainder - 6, 6) : remainder = 0; 213 if (i >= 8 && i <= 12) 214 height_remainder > 12 ? remainder = height_remainder - 12 : remainder = 0; 215 m_month_size[i].set_height(remainder + 5 + tile_height * 6); 216 217 for (int j = 0; j < 42; j++) { 218 m_tiles[i][j].width = tile_width; 219 m_tiles[i][j].height = tile_height; 220 } 221 } 222 223 if (width_remainder) { 224 for (int i = 0; i < 12; i += 4) { 225 for (int j = 0; j < min(width_remainder, 7); j++) { 226 for (int k = j; k < j + 36; k += 7) { 227 m_tiles[i][k].width = tile_width + 1; 228 } 229 } 230 } 231 } 232 if (width_remainder > 7) { 233 for (int i = 1; i < 12; i += 4) { 234 for (int j = 0; j < min(width_remainder - 7, 7); j++) { 235 for (int k = j; k < j + 36; k += 7) { 236 m_tiles[i][k].width = tile_width + 1; 237 } 238 } 239 } 240 } 241 if (width_remainder > 14) { 242 for (int i = 2; i < 12; i += 4) { 243 for (int j = 0; j < min(width_remainder - 14, 7); j++) { 244 for (int k = j; k < j + 36; k += 7) { 245 m_tiles[i][k].width = tile_width + 1; 246 } 247 } 248 } 249 } 250 if (width_remainder > 21) { 251 for (int i = 3; i < 12; i += 4) { 252 for (int j = 0; j < width_remainder - 21; j++) { 253 for (int k = j; k < j + 36; k += 7) { 254 m_tiles[i][k].width = tile_width + 1; 255 } 256 } 257 } 258 } 259 if (height_remainder) { 260 for (int i = 0; i < 4; i++) { 261 for (int j = 0; j < min(height_remainder, 6) * 7; j++) { 262 m_tiles[i][j].height = tile_height + 1; 263 } 264 } 265 } 266 if (height_remainder > 6) { 267 for (int i = 4; i < 8; i++) { 268 for (int j = 0; j < min(height_remainder - 6, 6) * 7; j++) { 269 m_tiles[i][j].height = tile_height + 1; 270 } 271 } 272 } 273 if (height_remainder > 12) { 274 for (int i = 8; i < 12; i++) { 275 for (int j = 0; j < (height_remainder - 12) * 7; j++) { 276 m_tiles[i][j].height = tile_height + 1; 277 } 278 } 279 } 280 } 281} 282 283void Calendar::update_tiles(unsigned view_year, unsigned view_month) 284{ 285 set_view_date(view_year, view_month); 286 287 auto now = Core::DateTime::now(); 288 unsigned months = mode() == Month ? 1 : 12; 289 for (unsigned i = 0; i < months; i++) { 290 if (mode() == Year) 291 view_month = i + 1; 292 293 auto first_day_of_current_month = Core::DateTime::create(view_year, view_month, 1); 294 unsigned start_of_month = (first_day_of_current_month.weekday() - to_underlying(m_first_day_of_week) + 7) % 7; 295 unsigned days_from_previous_month_to_show = start_of_month == 0 ? 7 : start_of_month; 296 297 for (unsigned j = 0; j < 42; j++) { 298 unsigned year; 299 unsigned month; 300 unsigned day; 301 302 if (j + 1 <= days_from_previous_month_to_show) { 303 // Day from previous month. 304 month = (view_month - 1 == 0) ? 12 : view_month - 1; 305 year = (month == 12) ? view_year - 1 : view_year; 306 day = days_in_month(year, month) + j + 1 - days_from_previous_month_to_show; 307 } else if (j + 1 > days_from_previous_month_to_show + first_day_of_current_month.days_in_month()) { 308 // Day from next month. 309 month = (view_month + 1) > 12 ? 1 : view_month + 1; 310 year = (month == 1) ? view_year + 1 : view_year; 311 day = j + 1 - days_from_previous_month_to_show - first_day_of_current_month.days_in_month(); 312 } else { 313 // Day from current month. 314 month = view_month; 315 year = view_year; 316 day = j + 1 - days_from_previous_month_to_show; 317 } 318 319 m_tiles[i][j].year = year; 320 m_tiles[i][j].month = month; 321 m_tiles[i][j].day = day; 322 m_tiles[i][j].is_outside_selected_month = (month != view_month 323 || year != view_year); 324 m_tiles[i][j].is_selected = (year == m_selected_date.year() 325 && month == m_selected_date.month() 326 && day == m_selected_date.day() 327 && (mode() == Year ? !m_tiles[i][j].is_outside_selected_month : true)); 328 m_tiles[i][j].is_today = (day == now.day() 329 && month == now.month() 330 && year == now.year()); 331 } 332 } 333 update(); 334} 335 336ErrorOr<String> Calendar::formatted_date(Format format) 337{ 338 switch (format) { 339 case ShortMonthYear: 340 return String::formatted("{} {}", short_month_names[view_month() - 1], view_year()); 341 case LongMonthYear: 342 return String::formatted("{} {}", long_month_names[view_month() - 1], view_year()); 343 case MonthOnly: 344 return String::formatted("{}", long_month_names[view_month() - 1]); 345 case YearOnly: 346 return String::number(view_year()); 347 } 348 VERIFY_NOT_REACHED(); 349} 350 351void Calendar::paint_event(GUI::PaintEvent& event) 352{ 353 GUI::Frame::paint_event(event); 354 355 GUI::Painter painter(*this); 356 painter.add_clip_rect(frame_inner_rect()); 357 painter.add_clip_rect(event.rect()); 358 359 if (has_grid()) 360 painter.fill_rect(frame_inner_rect(), palette().threed_shadow2()); 361 else 362 painter.fill_rect(frame_inner_rect(), palette().base()); 363 364 painter.translate(frame_thickness(), frame_thickness()); 365 366 int width = unadjusted_tile_size().width(); 367 int height = unadjusted_tile_size().height(); 368 int x_offset = 0; 369 int y_offset = 0; 370 371 if (is_showing_year()) { 372 auto year_only_rect = Gfx::IntRect( 373 0, 374 0, 375 frame_inner_rect().width(), 376 22); 377 y_offset += year_only_rect.height(); 378 painter.fill_rect(year_only_rect, palette().hover_highlight()); 379 painter.draw_text(year_only_rect, formatted_date(YearOnly).release_value_but_fixme_should_propagate_errors(), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 380 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, (!m_show_month_tiles ? palette().threed_shadow1() : palette().threed_shadow2()), 1); 381 y_offset += 1; 382 if (!m_show_month_tiles) { 383 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_highlight(), 1); 384 y_offset += 1; 385 } 386 } else if (is_showing_month_and_year()) { 387 auto month_year_rect = Gfx::IntRect( 388 0, 389 0, 390 frame_inner_rect().width(), 391 22); 392 painter.fill_rect(month_year_rect, palette().hover_highlight()); 393 month_year_rect.set_width(frame_inner_rect().width() / 2); 394 painter.draw_text(month_year_rect, formatted_date(MonthOnly).release_value_but_fixme_should_propagate_errors(), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 395 month_year_rect.set_x(month_year_rect.width() + (frame_inner_rect().width() % 2 ? 1 : 0)); 396 painter.draw_text(month_year_rect, formatted_date(YearOnly).release_value_but_fixme_should_propagate_errors(), medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 397 y_offset += 22; 398 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_shadow1(), 1); 399 y_offset += 1; 400 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_highlight(), 1); 401 y_offset += 1; 402 } 403 404 if (mode() == Year && m_show_month_tiles) { 405 int i = 0; 406 for (int j = 0; j < 3; j++) { 407 x_offset = 0; 408 for (int k = 0; k < 4; k++) { 409 if (k > 0) 410 x_offset += m_months[i - 1].width; 411 auto month_tile_rect = Gfx::IntRect( 412 x_offset, 413 y_offset, 414 m_months[i].width, 415 m_months[i].height); 416 m_months[i].rect = month_tile_rect.translated(frame_thickness(), frame_thickness()); 417 Gfx::StylePainter::paint_button( 418 painter, month_tile_rect, palette(), 419 Gfx::ButtonStyle::Normal, 420 m_months[i].is_being_pressed, 421 m_months[i].is_hovered, 422 false, true, false); 423 set_font(small_font); 424 painter.draw_text(month_tile_rect, m_months[i].name, font(), Gfx::TextAlignment::Center, palette().base_text()); 425 i++; 426 } 427 y_offset += m_months[i - 1].height; 428 } 429 return; 430 } 431 432 if (is_showing_days_of_the_week()) { 433 auto days_of_the_week_rect = Gfx::IntRect( 434 0, 435 y_offset, 436 frame_inner_rect().width(), 437 16); 438 painter.fill_rect(days_of_the_week_rect, palette().hover_highlight()); 439 for (int i = 0; i < 7; i++) { 440 if (i > 0) 441 x_offset += m_days[i - 1].width + 1; 442 Gfx::IntRect day_rect = Gfx::IntRect( 443 x_offset, 444 y_offset, 445 m_days[i].width, 446 16); 447 auto const& day_name = m_days[(i + to_underlying(m_first_day_of_week)) % 7].name; 448 painter.draw_text(day_rect, day_name, small_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 449 } 450 y_offset += days_of_the_week_rect.height(); 451 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_shadow2(), 1); 452 y_offset += 1; 453 } 454 455 if (mode() == Month) { 456 int i = 0; 457 for (int j = 0; j < 6; j++) { 458 x_offset = 0; 459 if (j > 0) 460 y_offset += m_tiles[0][(j - 1) * 7].height + 1; 461 for (int k = 0; k < 7; k++) { 462 bool is_weekend = is_day_in_weekend((DayOfWeek)((k + to_underlying(m_first_day_of_week)) % 7)); 463 if (k > 0) 464 x_offset += m_tiles[0][k - 1].width + 1; 465 auto tile_rect = Gfx::IntRect( 466 x_offset, 467 y_offset, 468 m_tiles[0][i].width, 469 m_tiles[0][i].height); 470 m_tiles[0][i].rect = tile_rect.translated(frame_thickness(), frame_thickness()); 471 472 Color background_color = palette().base(); 473 474 if (m_tiles[0][i].is_hovered || m_tiles[0][i].is_selected) { 475 background_color = palette().hover_highlight(); 476 } else if (is_weekend) { 477 background_color = palette().gutter(); 478 } 479 480 painter.fill_rect(tile_rect, background_color); 481 482 auto text_alignment = Gfx::TextAlignment::TopRight; 483 auto text_rect = Gfx::IntRect( 484 x_offset, 485 y_offset + 4, 486 m_tiles[0][i].width - 4, 487 font().pixel_size_rounded_up() + 4); 488 489 if (width > 150 && height > 150) { 490 set_font(extra_large_font); 491 } else if (width > 100 && height > 100) { 492 set_font(large_font); 493 } else if (width > 50 && height > 50) { 494 set_font(medium_font); 495 } else if (width >= 30 && height >= 30) { 496 set_font(small_font); 497 } else { 498 set_font(small_font); 499 text_alignment = Gfx::TextAlignment::Center; 500 text_rect = Gfx::IntRect(tile_rect); 501 } 502 503 auto display_date = DeprecatedString::number(m_tiles[0][i].day); 504 if (m_tiles[0][i].is_selected && (width < 30 || height < 30)) 505 painter.draw_rect(tile_rect, palette().base_text()); 506 507 if (m_tiles[0][i].is_today && !m_tiles[0][i].is_outside_selected_month) { 508 painter.draw_text(text_rect, display_date, font().bold_variant(), text_alignment, palette().base_text()); 509 } else if (m_tiles[0][i].is_outside_selected_month) { 510 painter.draw_text(text_rect, display_date, m_tiles[0][i].is_today ? font().bold_variant() : font(), text_alignment, Color::LightGray); 511 } else { 512 painter.draw_text(text_rect, display_date, font(), text_alignment, palette().base_text()); 513 } 514 i++; 515 } 516 } 517 } else { 518 for (int i = 0; i < 4; i++) { 519 static int x_month_offset; 520 x_month_offset += (i > 0 ? m_month_size[i - 1].width() + 1 : 0); 521 auto month_rect = Gfx::IntRect( 522 x_month_offset, 523 y_offset, 524 m_month_size[i].width(), 525 19); 526 painter.fill_rect(month_rect, palette().hover_highlight()); 527 painter.draw_text(month_rect, long_month_names[i], medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 528 if (i > 0 && i < 4) { 529 painter.draw_line({ x_month_offset - 1, y_offset - 1 }, { x_month_offset - 1, y_offset + 18 }, palette().threed_shadow2(), 1); 530 painter.draw_line({ x_month_offset, y_offset - 1 }, { x_month_offset, y_offset + 18 }, palette().threed_highlight(), 1); 531 } 532 if (i == 3) 533 x_month_offset = 0; 534 } 535 y_offset += 19; 536 painter.draw_line({ 0, y_offset }, { frame_inner_rect().width(), y_offset }, palette().threed_shadow2(), 1); 537 y_offset += 1; 538 539 int x_translation = 0; 540 int y_translation = y_offset; 541 for (int l = 0; l < 12; l++) { 542 if ((l > 0 && l < 4) || (l > 4 && l < 8) || (l > 8)) { 543 x_translation += m_month_size[l - 1].width() + 1; 544 } else if (l % 4 == 0) { 545 x_translation = 0; 546 } 547 if (l < 4 || (l > 4 && l < 8) || l > 8) { 548 y_offset = y_translation; 549 } else if (l == 4 || l == 8) { 550 y_translation += m_month_size[l - 1].height(); 551 painter.draw_line({ 0, y_translation }, { frame_inner_rect().width(), y_translation }, palette().threed_shadow1(), 1); 552 y_translation += 1; 553 painter.draw_line({ 0, y_translation }, { frame_inner_rect().width(), y_translation }, palette().threed_highlight(), 1); 554 y_translation += 1; 555 y_offset = y_translation; 556 for (int i = l; i < (l == 4 ? 8 : 12); i++) { 557 static int x_month_offset; 558 x_month_offset += (i > (l == 4 ? 4 : 8) ? m_month_size[i - 1].width() + 1 : 0); 559 auto month_rect = Gfx::IntRect( 560 x_month_offset, 561 y_offset, 562 m_month_size[i].width(), 563 19); 564 painter.fill_rect(month_rect, palette().hover_highlight()); 565 painter.draw_text(month_rect, long_month_names[i], medium_font->bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 566 if (i > (l == 4 ? 4 : 8) && i < (l == 4 ? 8 : 12)) { 567 painter.draw_line({ x_month_offset - 1, y_offset - 1 }, { x_month_offset - 1, y_offset + 18 }, palette().threed_shadow2(), 1); 568 painter.draw_line({ x_month_offset, y_offset - 1 }, { x_month_offset, y_offset + 18 }, palette().threed_highlight(), 1); 569 } 570 if (i == 7 || i == 11) 571 x_month_offset = 0; 572 } 573 y_translation += 19; 574 painter.draw_line({ 0, y_translation }, { frame_inner_rect().width(), y_translation }, palette().threed_shadow2(), 1); 575 y_translation += 1; 576 y_offset = y_translation; 577 } 578 579 int i = 0; 580 for (int j = 0; j < 6; j++) { 581 x_offset = 0; 582 if (j > 0) 583 y_offset += m_tiles[l][(j - 1) * 7].height + (j < 6 ? 1 : 0); 584 if (j == 0 && l != 3 && l != 7 && l != 11) { 585 painter.draw_line( 586 { m_month_size[l].width() + x_translation, y_offset }, 587 { m_month_size[l].width() + x_translation, y_offset + m_month_size[l].height() }, 588 palette().threed_shadow2(), 589 1); 590 } 591 for (int k = 0; k < 7; k++) { 592 if (k > 0) 593 x_offset += m_tiles[l][k - 1].width + 1; 594 auto tile_rect = Gfx::IntRect( 595 x_offset + x_translation, 596 y_offset, 597 m_tiles[l][i].width, 598 m_tiles[l][i].height); 599 m_tiles[l][i].rect = tile_rect.translated(frame_thickness(), frame_thickness()); 600 601 if (m_tiles[l][i].is_hovered || m_tiles[l][i].is_selected) 602 painter.fill_rect(tile_rect, palette().hover_highlight()); 603 else 604 painter.fill_rect(tile_rect, palette().base()); 605 606 if (width > 50 && height > 50) { 607 set_font(medium_font); 608 } else { 609 set_font(small_font); 610 } 611 612 auto display_date = DeprecatedString::number(m_tiles[l][i].day); 613 if (m_tiles[l][i].is_selected) 614 painter.draw_rect(tile_rect, palette().base_text()); 615 616 if (m_tiles[l][i].is_today && !m_tiles[l][i].is_outside_selected_month) { 617 painter.draw_text(tile_rect, display_date, font().bold_variant(), Gfx::TextAlignment::Center, palette().base_text()); 618 } else if (!m_tiles[l][i].is_outside_selected_month) { 619 painter.draw_text(tile_rect, display_date, font(), Gfx::TextAlignment::Center, palette().base_text()); 620 } 621 i++; 622 } 623 } 624 } 625 } 626} 627 628void Calendar::leave_event(Core::Event&) 629{ 630 int months; 631 mode() == Month ? months = 1 : months = 12; 632 for (int i = 0; i < months; i++) { 633 if (mode() == Year && m_show_month_tiles) { 634 m_months[i].is_hovered = false; 635 continue; 636 } else { 637 for (int j = 0; j < 42; j++) { 638 m_tiles[i][j].is_hovered = false; 639 } 640 } 641 } 642 update(); 643} 644 645void Calendar::mousemove_event(GUI::MouseEvent& event) 646{ 647 static int last_index_i; 648 static int last_index_j; 649 650 if (mode() == Year && m_show_month_tiles) { 651 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)) { 652 return; 653 } else { 654 m_months[last_index_i].is_hovered = false; 655 m_months[last_index_i].is_being_pressed = false; 656 update(m_months[last_index_i].rect); 657 } 658 } else { 659 if (m_tiles[last_index_i][last_index_j].rect.contains(event.position()) && m_tiles[last_index_i][last_index_j].is_hovered) { 660 return; 661 } else { 662 m_tiles[last_index_i][last_index_j].is_hovered = false; 663 update(m_tiles[last_index_i][last_index_j].rect); 664 } 665 } 666 667 int months; 668 mode() == Month ? months = 1 : months = 12; 669 for (int i = 0; i < months; i++) { 670 if (mode() == Year && m_show_month_tiles) { 671 if (m_months[i].rect.contains(event.position())) { 672 if (m_currently_pressed_index == -1 || m_currently_pressed_index == i) 673 m_months[i].is_hovered = true; 674 if (m_currently_pressed_index == i) 675 m_months[i].is_being_pressed = true; 676 update(m_months[last_index_i].rect); 677 if (m_months[i].is_being_pressed == true) 678 m_currently_pressed_index = i; 679 last_index_i = i; 680 update(m_months[i].rect); 681 break; 682 } 683 } else { 684 for (int j = 0; j < 42; j++) { 685 if (mode() == Year && m_tiles[i][j].is_outside_selected_month) 686 continue; 687 if (m_tiles[i][j].rect.contains(event.position())) { 688 m_tiles[i][j].is_hovered = true; 689 update(m_tiles[last_index_i][last_index_j].rect); 690 last_index_i = i; 691 last_index_j = j; 692 update(m_tiles[i][j].rect); 693 break; 694 } 695 } 696 } 697 } 698} 699 700void Calendar::mouseup_event(GUI::MouseEvent& event) 701{ 702 int months; 703 mode() == Month ? months = 1 : months = 12; 704 for (int i = 0; i < months; i++) { 705 if (mode() == Year && m_show_month_tiles) { 706 if (m_months[i].rect.contains(event.position()) && m_months[i].is_being_pressed) { 707 set_view_date(view_year(), (unsigned)i + 1); 708 toggle_mode(); 709 if (on_month_click) 710 on_month_click(); 711 } 712 } else { 713 for (int j = 0; j < 42; j++) { 714 if (mode() == Year && m_tiles[i][j].is_outside_selected_month) 715 continue; 716 if (m_tiles[i][j].rect.contains(event.position())) { 717 m_previous_selected_date = m_selected_date; 718 m_selected_date = Core::DateTime::create(m_tiles[i][j].year, m_tiles[i][j].month, m_tiles[i][j].day); 719 update_tiles(m_selected_date.year(), m_selected_date.month()); 720 if (on_tile_click) 721 on_tile_click(); 722 } 723 } 724 } 725 if (months == 12) { 726 m_months[i].is_being_pressed = false; 727 m_months[i].is_hovered = false; 728 } 729 } 730 m_currently_pressed_index = -1; 731 update(); 732} 733 734void Calendar::mousedown_event(GUI::MouseEvent& event) 735{ 736 if (mode() == Year && m_show_month_tiles) { 737 for (int i = 0; i < 12; i++) { 738 if (m_months[i].rect.contains(event.position())) { 739 m_months[i].is_being_pressed = true; 740 m_currently_pressed_index = i; 741 update(m_months[i].rect); 742 break; 743 } 744 } 745 } 746} 747 748void Calendar::doubleclick_event(GUI::MouseEvent& event) 749{ 750 int months; 751 mode() == Month ? months = 1 : months = 12; 752 for (int i = 0; i < months; i++) { 753 for (int j = 0; j < 42; j++) { 754 if (m_tiles[i][j].day != m_previous_selected_date.day()) 755 continue; 756 if (mode() == Year && m_tiles[i][j].is_outside_selected_month) 757 continue; 758 if (m_tiles[i][j].rect.contains(event.position())) { 759 if (on_tile_doubleclick) 760 on_tile_doubleclick(); 761 } 762 } 763 } 764} 765 766size_t Calendar::day_of_week_index(DeprecatedString const& day_name) 767{ 768 auto const& day_names = AK::long_day_names; 769 return AK::find_index(day_names.begin(), day_names.end(), day_name); 770} 771 772void Calendar::config_string_did_change(DeprecatedString const& domain, DeprecatedString const& group, DeprecatedString const& key, DeprecatedString const& value) 773{ 774 if (domain == "Calendar" && group == "View" && key == "FirstDayOfWeek") { 775 m_first_day_of_week = static_cast<DayOfWeek>(day_of_week_index(value)); 776 update_tiles(m_view_year, m_view_month); 777 } else if (domain == "Calendar" && group == "View" && key == "FirstDayOfWeekend") { 778 m_first_day_of_weekend = static_cast<DayOfWeek>(day_of_week_index(value)); 779 update(); 780 } 781} 782 783void Calendar::config_i32_did_change(DeprecatedString const& domain, DeprecatedString const& group, DeprecatedString const& key, i32 value) 784{ 785 if (domain == "Calendar" && group == "View" && key == "WeekendLength") { 786 m_weekend_length = value; 787 update(); 788 } 789} 790 791bool Calendar::is_day_in_weekend(DayOfWeek day) 792{ 793 auto day_index = to_underlying(day); 794 auto weekend_start_index = to_underlying(m_first_day_of_weekend); 795 auto weekend_end_index = weekend_start_index + m_weekend_length; 796 797 if (day_index < weekend_start_index) 798 day_index += 7; 799 800 return day_index < weekend_end_index; 801} 802 803}