Calendar.cpp 29 KB

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