Calendar.cpp 30 KB

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