/* * Copyright (c) 2020, the SerenityOS developers. * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include "BoardView.h" #include #include #include #include BoardView::BoardView(Game::Board const* board) : m_board(board) { } void BoardView::set_board(Game::Board const* board) { if (has_timer()) stop_timer(); slide_animation_frame = 0; pop_in_animation_frame = 0; start_timer(frame_duration_ms); if (m_board == board) return; if (!board) { m_board = nullptr; return; } bool must_resize = !m_board || m_board->tiles().size() != board->tiles().size(); m_board = board; if (must_resize) resize(); update(); } void BoardView::pick_font() { DeprecatedString best_font_name; int best_font_size = -1; auto& font_database = Gfx::FontDatabase::the(); font_database.for_each_font([&](Gfx::Font const& font) { if (font.family() != "Liza" || font.weight() != 700) return; auto size = font.glyph_height(); if (size * 2 <= m_cell_size && size > best_font_size) { best_font_name = font.qualified_name(); best_font_size = size; } }); auto font = font_database.get_by_name(best_font_name); set_font(font); m_min_cell_size = best_font_size; } size_t BoardView::rows() const { if (!m_board) return 0; return m_board->tiles().size(); } size_t BoardView::columns() const { if (!m_board) return 0; if (m_board->tiles().is_empty()) return 0; return m_board->tiles()[0].size(); } void BoardView::resize_event(GUI::ResizeEvent&) { resize(); } void BoardView::resize() { constexpr float padding_ratio = 7; m_padding = min( width() / (columns() * (padding_ratio + 1) + 1), height() / (rows() * (padding_ratio + 1) + 1)); m_cell_size = m_padding * padding_ratio; pick_font(); } void BoardView::keydown_event(GUI::KeyEvent& event) { if (!on_move) { event.ignore(); return; } switch (event.key()) { case KeyCode::Key_A: case KeyCode::Key_Left: on_move(Game::Direction::Left); return; case KeyCode::Key_D: case KeyCode::Key_Right: on_move(Game::Direction::Right); return; case KeyCode::Key_W: case KeyCode::Key_Up: on_move(Game::Direction::Up); return; case KeyCode::Key_S: case KeyCode::Key_Down: on_move(Game::Direction::Down); return; default: break; } event.ignore(); } Gfx::Color BoardView::background_color_for_cell(u32 value) { switch (value) { case 0: return Color::from_rgb(0xcdc1b4); case 2: return Color::from_rgb(0xeee4da); case 4: return Color::from_rgb(0xede0c8); case 8: return Color::from_rgb(0xf2b179); case 16: return Color::from_rgb(0xf59563); case 32: return Color::from_rgb(0xf67c5f); case 64: return Color::from_rgb(0xf65e3b); case 128: return Color::from_rgb(0xedcf72); case 256: return Color::from_rgb(0xedcc61); case 512: return Color::from_rgb(0xedc850); case 1024: return Color::from_rgb(0xedc53f); case 2048: return Color::from_rgb(0xedc22e); default: VERIFY(value > 2048); return Color::from_rgb(0x3c3a32); } } Gfx::Color BoardView::text_color_for_cell(u32 value) { if (value <= 4) return Color::from_rgb(0x776e65); return Color::from_rgb(0xf9f6f2); } void BoardView::timer_event(Core::TimerEvent&) { if (slide_animation_frame < animation_duration) { slide_animation_frame++; update(); } else if (pop_in_animation_frame < animation_duration) { pop_in_animation_frame++; update(); if (pop_in_animation_frame == animation_duration) stop_timer(); } } void BoardView::paint_event(GUI::PaintEvent& event) { Frame::paint_event(event); Color background_color = Color::from_rgb(0xbbada0); GUI::Painter painter(*this); painter.add_clip_rect(event.rect()); painter.add_clip_rect(frame_inner_rect()); painter.translate(frame_thickness(), frame_thickness()); if (!m_board) { painter.fill_rect(rect(), background_color); return; } auto& tiles = m_board->tiles(); Gfx::IntRect field_rect { 0, 0, static_cast(m_padding + (m_cell_size + m_padding) * columns()), static_cast(m_padding + (m_cell_size + m_padding) * rows()) }; field_rect.center_within(rect()); painter.fill_rect(field_rect, background_color); auto tile_center = [&](size_t row, size_t column) { return Gfx::IntPoint { field_rect.x() + m_padding + (m_cell_size + m_padding) * column + m_cell_size / 2, field_rect.y() + m_padding + (m_cell_size + m_padding) * row + m_cell_size / 2, }; }; if (slide_animation_frame < animation_duration) { // background for (size_t column = 0; column < columns(); ++column) { for (size_t row = 0; row < rows(); ++row) { auto center = tile_center(row, column); auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; auto rect = Gfx::IntRect::centered_on(center, tile_size); painter.fill_rect(rect, background_color_for_cell(0)); } } for (auto& sliding_tile : m_board->sliding_tiles()) { auto center_from = tile_center(sliding_tile.row_from, sliding_tile.column_from); auto center_to = tile_center(sliding_tile.row_to, sliding_tile.column_to); auto offset = Gfx::FloatPoint(center_to - center_from); auto center = center_from + Gfx::IntPoint(offset * (slide_animation_frame / (float)animation_duration)); auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; auto rect = Gfx::IntRect::centered_on(center, tile_size); painter.fill_rect(rect, background_color_for_cell(sliding_tile.value_from)); painter.draw_text(rect, DeprecatedString::number(sliding_tile.value_from), font(), Gfx::TextAlignment::Center, text_color_for_cell(sliding_tile.value_from)); } } else { for (size_t column = 0; column < columns(); ++column) { for (size_t row = 0; row < rows(); ++row) { auto center = tile_center(row, column); auto tile_size = Gfx::IntSize { m_cell_size, m_cell_size }; if (pop_in_animation_frame < animation_duration && Game::Board::Position { row, column } == m_board->last_added_position()) { float pop_in_size = m_min_cell_size + (m_cell_size - m_min_cell_size) * (pop_in_animation_frame / (float)animation_duration); tile_size = Gfx::IntSize { pop_in_size, pop_in_size }; } auto rect = Gfx::IntRect::centered_on(center, tile_size); auto entry = tiles[row][column]; painter.fill_rect(rect, background_color_for_cell(entry)); if (entry > 0) painter.draw_text(rect, DeprecatedString::number(entry), font(), Gfx::TextAlignment::Center, text_color_for_cell(entry)); } } } }