BoardView.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "BoardView.h"
  8. #include <LibGUI/Painter.h>
  9. #include <LibGfx/Font/Font.h>
  10. #include <LibGfx/Font/FontDatabase.h>
  11. #include <LibGfx/Palette.h>
  12. BoardView::BoardView(Game::Board const* board)
  13. : m_board(board)
  14. {
  15. }
  16. void BoardView::set_board(Game::Board const* board)
  17. {
  18. if (has_timer())
  19. stop_timer();
  20. slide_animation_frame = 0;
  21. pop_in_animation_frame = 0;
  22. start_timer(frame_duration_ms);
  23. if (m_board == board)
  24. return;
  25. if (!board) {
  26. m_board = nullptr;
  27. return;
  28. }
  29. bool must_resize = !m_board || m_board->tiles().size() != board->tiles().size();
  30. m_board = board;
  31. if (must_resize)
  32. resize();
  33. update();
  34. }
  35. void BoardView::pick_font()
  36. {
  37. String best_font_name;
  38. int best_font_size = -1;
  39. auto& font_database = Gfx::FontDatabase::the();
  40. font_database.for_each_font([&](Gfx::Font const& font) {
  41. if (font.family() != "Liza" || font.weight() != 700)
  42. return;
  43. auto size = font.glyph_height();
  44. if (size * 2 <= m_cell_size && size > best_font_size) {
  45. best_font_name = font.qualified_name();
  46. best_font_size = size;
  47. }
  48. });
  49. auto font = font_database.get_by_name(best_font_name);
  50. set_font(font);
  51. m_min_cell_size = best_font_size;
  52. }
  53. size_t BoardView::rows() const
  54. {
  55. if (!m_board)
  56. return 0;
  57. return m_board->tiles().size();
  58. }
  59. size_t BoardView::columns() const
  60. {
  61. if (!m_board)
  62. return 0;
  63. if (m_board->tiles().is_empty())
  64. return 0;
  65. return m_board->tiles()[0].size();
  66. }
  67. void BoardView::resize_event(GUI::ResizeEvent&)
  68. {
  69. resize();
  70. }
  71. void BoardView::resize()
  72. {
  73. constexpr float padding_ratio = 7;
  74. m_padding = min(
  75. width() / (columns() * (padding_ratio + 1) + 1),
  76. height() / (rows() * (padding_ratio + 1) + 1));
  77. m_cell_size = m_padding * padding_ratio;
  78. pick_font();
  79. }
  80. void BoardView::keydown_event(GUI::KeyEvent& event)
  81. {
  82. if (!on_move)
  83. return;
  84. switch (event.key()) {
  85. case KeyCode::Key_A:
  86. case KeyCode::Key_Left:
  87. on_move(Game::Direction::Left);
  88. break;
  89. case KeyCode::Key_D:
  90. case KeyCode::Key_Right:
  91. on_move(Game::Direction::Right);
  92. break;
  93. case KeyCode::Key_W:
  94. case KeyCode::Key_Up:
  95. on_move(Game::Direction::Up);
  96. break;
  97. case KeyCode::Key_S:
  98. case KeyCode::Key_Down:
  99. on_move(Game::Direction::Down);
  100. break;
  101. default:
  102. return;
  103. }
  104. }
  105. Gfx::Color BoardView::background_color_for_cell(u32 value)
  106. {
  107. switch (value) {
  108. case 0:
  109. return Color::from_rgb(0xcdc1b4);
  110. case 2:
  111. return Color::from_rgb(0xeee4da);
  112. case 4:
  113. return Color::from_rgb(0xede0c8);
  114. case 8:
  115. return Color::from_rgb(0xf2b179);
  116. case 16:
  117. return Color::from_rgb(0xf59563);
  118. case 32:
  119. return Color::from_rgb(0xf67c5f);
  120. case 64:
  121. return Color::from_rgb(0xf65e3b);
  122. case 128:
  123. return Color::from_rgb(0xedcf72);
  124. case 256:
  125. return Color::from_rgb(0xedcc61);
  126. case 512:
  127. return Color::from_rgb(0xedc850);
  128. case 1024:
  129. return Color::from_rgb(0xedc53f);
  130. case 2048:
  131. return Color::from_rgb(0xedc22e);
  132. default:
  133. VERIFY(value > 2048);
  134. return Color::from_rgb(0x3c3a32);
  135. }
  136. }
  137. Gfx::Color BoardView::text_color_for_cell(u32 value)
  138. {
  139. if (value <= 4)
  140. return Color::from_rgb(0x776e65);
  141. return Color::from_rgb(0xf9f6f2);
  142. }
  143. void BoardView::timer_event(Core::TimerEvent&)
  144. {
  145. if (slide_animation_frame < animation_duration) {
  146. slide_animation_frame++;
  147. update();
  148. } else if (pop_in_animation_frame < animation_duration) {
  149. pop_in_animation_frame++;
  150. update();
  151. if (pop_in_animation_frame == animation_duration)
  152. stop_timer();
  153. }
  154. }
  155. void BoardView::paint_event(GUI::PaintEvent& event)
  156. {
  157. Frame::paint_event(event);
  158. Color background_color = Color::from_rgb(0xbbada0);
  159. GUI::Painter painter(*this);
  160. painter.add_clip_rect(event.rect());
  161. painter.add_clip_rect(frame_inner_rect());
  162. painter.translate(frame_thickness(), frame_thickness());
  163. if (!m_board) {
  164. painter.fill_rect(rect(), background_color);
  165. return;
  166. }
  167. auto& tiles = m_board->tiles();
  168. Gfx::IntRect field_rect {
  169. 0,
  170. 0,
  171. static_cast<int>(m_padding + (m_cell_size + m_padding) * columns()),
  172. static_cast<int>(m_padding + (m_cell_size + m_padding) * rows())
  173. };
  174. field_rect.center_within(rect());
  175. painter.fill_rect(field_rect, background_color);
  176. auto tile_center = [&](size_t row, size_t column) {
  177. return Gfx::IntPoint {
  178. field_rect.x() + m_padding + (m_cell_size + m_padding) * column + m_cell_size / 2,
  179. field_rect.y() + m_padding + (m_cell_size + m_padding) * row + m_cell_size / 2,
  180. };
  181. };
  182. if (slide_animation_frame < animation_duration) {
  183. // background
  184. for (size_t column = 0; column < columns(); ++column) {
  185. for (size_t row = 0; row < rows(); ++row) {
  186. auto center = tile_center(row, column);
  187. auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size };
  188. auto rect = Gfx::IntRect::centered_on(center, tile_size);
  189. painter.fill_rect(rect, background_color_for_cell(0));
  190. }
  191. }
  192. for (auto& sliding_tile : m_board->sliding_tiles()) {
  193. auto center_from = tile_center(sliding_tile.row_from, sliding_tile.column_from);
  194. auto center_to = tile_center(sliding_tile.row_to, sliding_tile.column_to);
  195. auto offset = Gfx::FloatPoint(center_to - center_from);
  196. auto center = center_from + Gfx::IntPoint(offset * (slide_animation_frame / (float)animation_duration));
  197. auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size };
  198. auto rect = Gfx::IntRect::centered_on(center, tile_size);
  199. painter.fill_rect(rect, background_color_for_cell(sliding_tile.value_from));
  200. painter.draw_text(rect, String::number(sliding_tile.value_from), font(), Gfx::TextAlignment::Center, text_color_for_cell(sliding_tile.value_from));
  201. }
  202. } else {
  203. for (size_t column = 0; column < columns(); ++column) {
  204. for (size_t row = 0; row < rows(); ++row) {
  205. auto center = tile_center(row, column);
  206. auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size };
  207. if (pop_in_animation_frame < animation_duration && Game::Board::Position { row, column } == m_board->last_added_position()) {
  208. float pop_in_size = m_min_cell_size + (m_cell_size - m_min_cell_size) * (pop_in_animation_frame / (float)animation_duration);
  209. tile_size = Gfx::IntSize { pop_in_size, pop_in_size };
  210. }
  211. auto rect = Gfx::IntRect::centered_on(center, tile_size);
  212. auto entry = tiles[row][column];
  213. painter.fill_rect(rect, background_color_for_cell(entry));
  214. if (entry > 0)
  215. painter.draw_text(rect, String::number(entry), font(), Gfx::TextAlignment::Center, text_color_for_cell(entry));
  216. }
  217. }
  218. }
  219. }