diff options
| author | Loic Guegan <manzerbredes@mailbox.org> | 2022-02-28 13:44:27 +0100 |
|---|---|---|
| committer | Loic Guegan <manzerbredes@mailbox.org> | 2022-02-28 13:44:27 +0100 |
| commit | bf485fa577a76731f9eac97de3b0a647cd492e49 (patch) | |
| tree | 731d9ec91070406124044e75b4b27949e5d230c0 /src/game_tab/left_panel | |
| parent | 7178f18ab88bcc93bfbf2019adf53d2f60d8fa20 (diff) | |
Refactoring game tab
Diffstat (limited to 'src/game_tab/left_panel')
| -rw-r--r-- | src/game_tab/left_panel/GameTabLeftPanel.cpp | 96 | ||||
| -rw-r--r-- | src/game_tab/left_panel/GameTabLeftPanel.hpp | 30 | ||||
| -rw-r--r-- | src/game_tab/left_panel/board/BoardCanvas.cpp | 294 | ||||
| -rw-r--r-- | src/game_tab/left_panel/board/BoardCanvas.hpp | 68 | ||||
| -rw-r--r-- | src/game_tab/left_panel/board/Theme.cpp | 184 | ||||
| -rw-r--r-- | src/game_tab/left_panel/board/Theme.hpp | 37 |
6 files changed, 709 insertions, 0 deletions
diff --git a/src/game_tab/left_panel/GameTabLeftPanel.cpp b/src/game_tab/left_panel/GameTabLeftPanel.cpp new file mode 100644 index 0000000..79894d0 --- /dev/null +++ b/src/game_tab/left_panel/GameTabLeftPanel.cpp @@ -0,0 +1,96 @@ +#include "GameTabLeftPanel.hpp" +#include <wx/clipbrd.h> + +GameTabLeftPanel::GameTabLeftPanel(wxFrame *parent, Game *game) + : wxPanel(parent), game(game) { + + wxBoxSizer *board_panel_sizer = new wxBoxSizer(wxVERTICAL); + board_canvas = new BoardCanvas((wxFrame *)this); + board_panel_sizer->Add(board_canvas, 1, wxEXPAND); + + // Left Panel buttons + wxBoxSizer *board_panel_button_sizer = new wxBoxSizer(wxHORIZONTAL); + board_panel_button_sizer->Add( + new wxBitmapButton(this, SWAP_BTN, LoadPNG("swap")), 0); + board_panel_button_sizer->Add( + new wxBitmapButton(this, ZOOM_IN_BTN, LoadPNG("zoomin")), 0); + board_panel_button_sizer->Add( + new wxBitmapButton(this, ZOOM_OUT_BTN, LoadPNG("zoomout")), 0); + board_panel_button_sizer->Add(new wxButton(this, COPY_FEN_BTN, L"Copy FEN"), + 0, wxEXPAND); + board_panel_sizer->Add(board_panel_button_sizer, 0); + this->SetSizer(board_panel_sizer); + + Bind(PLAY_MOVE_EVENT, &GameTabLeftPanel::OnPlay, this, wxID_ANY); + Bind(PREVIOUS_MOVE_EVENT, &GameTabLeftPanel::OnPreviousMove, this, wxID_ANY); + Bind(NEXT_MOVE_EVENT, &GameTabLeftPanel::OnNextMove, this, wxID_ANY); + Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnSwap, this, SWAP_BTN); + Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomIn, this, ZOOM_IN_BTN); + Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnZoomOut, this, ZOOM_OUT_BTN); + Bind(wxEVT_BUTTON, &GameTabLeftPanel::OnCopyFEN, this, COPY_FEN_BTN); +} + +void GameTabLeftPanel::OnPreviousMove(wxCommandEvent &event) { + game->Previous(); + Notify(); + NotifyEditor(); +} + +void GameTabLeftPanel::OnZoomIn(wxCommandEvent &event) { + wxLogDebug("Clicked on zoom in"); + board_canvas->Zoom(10); +} + +void GameTabLeftPanel::OnZoomOut(wxCommandEvent &event) { + wxLogDebug("Clicked on zoom out"); + board_canvas->Zoom(-10); +} + +void GameTabLeftPanel::OnSwap(wxCommandEvent &event) { + wxLogDebug("Clicked on swap"); + board_canvas->Swap(); +} + +void GameTabLeftPanel::OnNextMove(wxCommandEvent &event) { + wxLogDebug("Game tab received NEXT_MOVE_EVENT"); + game->Next(); + Notify(); + NotifyEditor(); +} + +void GameTabLeftPanel::OnPlay(wxCommandEvent &event) { + wxLogDebug("Game tab received PLAY_MOVE_EVENT"); + if (game->Play(event.GetString().ToStdString())) { + NotifyEditor(); + } + Notify(); +} + +void GameTabLeftPanel::OnCopyFEN(wxCommandEvent &event) { + wxLogDebug("Clicked on copy fen"); + if (wxTheClipboard->Open()) { + wxTheClipboard->SetData(new wxTextDataObject(game->GetFen())); + wxTheClipboard->Close(); + } +} + +void GameTabLeftPanel::Notify() { + std::string fen = game->GetFen(); + std::map<char, std::uint8_t> captures; + HalfMove *m = game->GetCurrentMove(); + if (m != NULL) { + captures = m->GetLineCaptures(); + } + board_canvas->SetupBoard(chessarbiter::FENParser::Parse(fen).board, + game->IsBlackToPlay(), captures); +} + +void GameTabLeftPanel::NotifyEditor() { + wxCommandEvent previousEvent(GAME_CHANGE, GetId()); + previousEvent.SetEventObject(this); + ProcessEvent(previousEvent); +} + +void GameTabLeftPanel::ApplyPreferences() { + board_canvas->ApplyPreferences(); +} diff --git a/src/game_tab/left_panel/GameTabLeftPanel.hpp b/src/game_tab/left_panel/GameTabLeftPanel.hpp new file mode 100644 index 0000000..85b1e6e --- /dev/null +++ b/src/game_tab/left_panel/GameTabLeftPanel.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "../Game.hpp" +#include "board/BoardCanvas.hpp" +#include "ochess.hpp" + +// Foreign events +wxDECLARE_EVENT(GAME_CHANGE, wxCommandEvent); + +enum { COPY_FEN_BTN = wxID_HIGHEST + 1, ZOOM_IN_BTN, ZOOM_OUT_BTN, SWAP_BTN }; + +class GameTabLeftPanel : public wxPanel { + Game *game; + BoardCanvas *board_canvas; + void NotifyEditor(); + +public: + GameTabLeftPanel(wxFrame *parent, Game *game); + void Notify(); + void OnPlay(wxCommandEvent &event); + void OnGotoMove(wxCommandEvent &event); + void OnPreviousMove(wxCommandEvent &event); + void OnNextMove(wxCommandEvent &event); + void OnCopyFEN(wxCommandEvent &event); + void OnZoomIn(wxCommandEvent &event); + void OnZoomOut(wxCommandEvent &event); + void OnSwap(wxCommandEvent &event); + void OnRefreshBoard(wxCommandEvent &event); + void ApplyPreferences(); +};
\ No newline at end of file diff --git a/src/game_tab/left_panel/board/BoardCanvas.cpp b/src/game_tab/left_panel/board/BoardCanvas.cpp new file mode 100644 index 0000000..237a68a --- /dev/null +++ b/src/game_tab/left_panel/board/BoardCanvas.cpp @@ -0,0 +1,294 @@ +#include "BoardCanvas.hpp" + +wxDEFINE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent); + +BoardCanvas::BoardCanvas(wxFrame *parent) + : wxPanel(parent), black_side(false), is_black_turn(true), frozen(false), + lock_square_size(false), t(new Theme()), t_captures(new Theme()) { + board = "rnbqkbnrpppppppp PPPPPPPPRNBQKBNR"; + is_dragging = false; + valid_drag = false; + t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR); + SetClockTime(-1, -1, -1, false); + SetClockTime(-1, -1, -1, true); + ApplyPreferences(); +} + +BoardCanvas::BoardCanvas(wxFrame *parent, std::uint32_t square_width, + bool frozen) + : BoardCanvas(parent) { + t->ResizeSquaresAndPieces(square_width); + t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR); + this->frozen = true; + lock_square_size = true; +} + +void BoardCanvas::OnPaint(wxPaintEvent &event) { + wxPaintDC dc(this); + REFRESH_MOUSE_LOCATION(); + square_width = t->GetSquaresSizes(); + canvas_size = dc.GetSize(); + boardX = (canvas_size.x - (8 * square_width)) / 2; + boardY = (canvas_size.y - (8 * square_width)) / 2; + if (boardX > canvas_size.x) + boardX = 0; + if (boardY > canvas_size.y) + boardY = 0; + DrawBoard(dc); +} + +void BoardCanvas::ApplyPreferences() { + if (t != NULL) + delete t; + if (t_captures != NULL) + delete t_captures; + t = new Theme(); + t_captures = new Theme(); + + CONFIG_OPEN(config); + black_side = config->Read("board/black_by_default", false); + if (lock_square_size) { + t->ResizeSquaresAndPieces(square_width); + } else { + t->ResizeSquaresAndPieces(config->Read("board/square_size", 80)); + } + t->SetSquareRadius(config->Read("board/corner_radius", 10)); + t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR); + CONFIG_CLOSE(config); + + Refresh(); +} + +void BoardCanvas::SetupBoard(std::string board, bool is_black_turn, + std::map<char, std::uint8_t> captures) { + this->board = board; + this->is_black_turn = is_black_turn; + this->captures = captures; + Refresh(); +} + +void BoardCanvas::DrawBoard(wxPaintDC &dc) { + std::uint32_t piece_width = t->GetPiecesSizes(); + std::uint32_t centrer_offset = (square_width - piece_width) / 2; + + bool DrawDraggingPiece = false; + char dp = 'p'; + std::uint32_t dpx = 0, dpy = 0; + for (std::int8_t file = 7; file >= 0; file--) { + for (std::uint8_t rank = 0; rank <= 7; rank++) { + std::uint32_t x = boardX + (7 - file) * square_width; + std::uint32_t y = boardY + rank * square_width; + if ((file + rank) % 2 == 0) { + if (file == 0 && rank == 0) { + dc.DrawBitmap(*t->Get('1'), x, y, true); + } else if (file == 7 && rank == 7) { + dc.DrawBitmap(*t->Get('2'), x, y, true); + } else { + dc.DrawBitmap(*t->Get('s'), x, y, true); + } + } else { + if (file == 7 && rank == 0) { + dc.DrawBitmap(*t->Get('0'), x, y, true); + } else if (file == 0 && rank == 7) { + dc.DrawBitmap(*t->Get('3'), x, y, true); + } else { + dc.DrawBitmap(*t->Get('S'), x, y, true); + } + } + + std::uint8_t prank = rank; + std::uint8_t pfile = file; + if (black_side) { + prank = 7 - rank; + pfile = 7 - file; + } + std::uint32_t px = x + centrer_offset; + std::uint32_t py = y + centrer_offset; + char piece = board[(7 - pfile) + 8 * prank]; + if (is_dragging && (7 - pfile) == active_square.x && + (7 - prank) == active_square.y) { + dp = piece; + dpx = px - (lastClickX - mouseX); + dpy = py - (lastClickY - mouseY); + DrawDraggingPiece = true; + continue; + } + if (piece != ' ') { + dc.DrawBitmap(*t->Get(piece), px, py, false); + } + } + } + + // Draw badge + dc.SetPen(wxPen(*wxBLACK, 3)); + std::uint32_t badgeY = boardY; + std::uint32_t badgeWidth = square_width / 2; + if (is_black_turn) { + dc.SetBrush(*wxBLACK_BRUSH); + if (black_side) { + badgeY = boardY + (8 * square_width) - badgeWidth; + } + } else { + dc.SetBrush(*wxWHITE_BRUSH); + if (!black_side) { + badgeY = boardY + (8 * square_width) - badgeWidth; + } + } + wxRect badge(boardX + (8 * square_width) + badgeWidth / 2, badgeY, badgeWidth, + badgeWidth); + dc.DrawRectangle(badge); + + // Draw captures first for white then for black + std::uint32_t captures_size = t_captures->GetPiecesSizes(); + std::uint8_t padding = 10; + std::uint32_t offsetX = 0; + std::uint32_t offsetY = -(captures_size + padding); + if (black_side) { + offsetY = 8 * square_width + padding; + } + for (char p : {'P', 'N', 'B', 'R', 'Q'}) { + if (captures.find(p) != captures.end()) { + for (std::uint8_t i = 0; i < captures[p]; i++) { + dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY); + offsetX += captures_size / 2; + } + offsetX += captures_size / 2; + } + } + offsetX = 0; + if (black_side) { + offsetY = -(captures_size + padding); + } else { + offsetY = 8 * square_width + padding; + } + for (char p : {'p', 'n', 'b', 'r', 'q'}) { + if (captures.find(p) != captures.end()) { + for (std::uint8_t i = 0; i < captures[p]; i++) { + dc.DrawBitmap(*t_captures->Get(p), boardX + offsetX, boardY + offsetY); + offsetX += captures_size / 2; + } + offsetX += captures_size / 2; + } + } + + // Draw dragging piece + if (DrawDraggingPiece) { + dc.DrawBitmap(*t->Get(dp), dpx, dpy, false); + } + + // Draw numbers + for (char l = 'a'; l < 'a' + 8; l++) { + dc.DrawText(wxString(l), wxPoint(boardX + l - 'a' * square_width, + boardY + 8 * square_width + 10)); + } + + // Draw Clocks + if (std::get<0>(black_time) >= 0) { + wxFont font = dc.GetFont(); + ClockTime clock = black_side ? white_time : black_time; + wxString time = + wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock)); + if (std::get<0>(clock) > 0) { + time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock)); + } else if (std::get<1>(clock) > 0) { + time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock)); + } + dc.SetFont(font.Scale(1.5).MakeBold()); + wxCoord width, height; + dc.GetTextExtent(time, &width, &height); + dc.DrawText(time, + wxPoint(boardX + square_width * 8 - width, boardY - height)); + clock = black_side ? black_time : white_time; + time = wxString::Format("%ds", std::get<1>(clock), std::get<2>(clock)); + if (std::get<0>(clock) > 0) { + time = wxString::Format("%d:%d", std::get<0>(clock), std::get<1>(clock)); + } else if (std::get<1>(clock) > 0) { + time = wxString::Format("%d:%ds", std::get<1>(clock), std::get<2>(clock)); + } + dc.GetTextExtent(time, &width, &height); + dc.DrawText(time, wxPoint(boardX + square_width * 8 - width, + boardY + square_width * 8)); + } +} + +void BoardCanvas::MouseEvent(wxMouseEvent &event) { + if (!frozen) { + if (event.Dragging()) { + if (valid_drag) { + is_dragging = true; + Refresh(); + } + } else { + if (is_dragging) { + is_dragging = false; + valid_drag = false; + // Handle drop + REFRESH_MOUSE_LOCATION(); + INIT_CURRENT_SQUARE(); + + if (IsCurrentSquareValid) { + wxLogDebug("Drag end on square (%d,%d)", file, rank); + /// Play the move + wxCommandEvent event(PLAY_MOVE_EVENT, GetId()); + event.SetEventObject(this); + std::string move = ((char)('a' + active_square.x)) + + std::to_string(+active_square.y + 1) + + ((char)('a' + file)) + std::to_string(rank + 1); + event.SetString(move); + ProcessEvent(event); + } + } + if (event.LeftDown()) { + SetFocus(); + REFRESH_MOUSE_LOCATION(); + lastClickX = mouseX; + lastClickY = mouseY; + INIT_CURRENT_SQUARE(); + if (IsCurrentSquareValid) { + active_square.x = file; + active_square.y = rank; + if (board[(7 - rank) * 8 + file] != ' ') { + wxLogDebug("Drag start on square (%d,%d)", file, rank); + valid_drag = true; + } + } + } + } + } +} + +void BoardCanvas::Zoom(std::int32_t zoom) { + t->Zoom(zoom); + t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR); + Refresh(); +} + +void BoardCanvas::Swap() { + black_side = !black_side; + Refresh(); +} + +void BoardCanvas::OnKeyEvent(wxKeyEvent &event) { + if (event.GetKeyCode() == WXK_LEFT) { + wxCommandEvent previousEvent(PREVIOUS_MOVE_EVENT, GetId()); + previousEvent.SetEventObject(this); + ProcessEvent(previousEvent); + } else if (event.GetKeyCode() == WXK_RIGHT) { + wxCommandEvent nextEvent(NEXT_MOVE_EVENT, GetId()); + nextEvent.SetEventObject(this); + ProcessEvent(nextEvent); + } +} + +void BoardCanvas::SetClockTime(short hours, short min, short sec, + bool IsBlack) { + if (IsBlack) { + black_time = std::make_tuple(hours, min, sec); + } else { + white_time = std::make_tuple(hours, min, sec); + } +} + +wxBEGIN_EVENT_TABLE(BoardCanvas, wxPanel) EVT_PAINT(BoardCanvas::OnPaint) + EVT_MOUSE_EVENTS(BoardCanvas::MouseEvent) + EVT_CHAR_HOOK(BoardCanvas::OnKeyEvent) wxEND_EVENT_TABLE() diff --git a/src/game_tab/left_panel/board/BoardCanvas.hpp b/src/game_tab/left_panel/board/BoardCanvas.hpp new file mode 100644 index 0000000..34c4911 --- /dev/null +++ b/src/game_tab/left_panel/board/BoardCanvas.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "Theme.hpp" +#include "ochess.hpp" +#include <map> +#include <tuple> +#include <utility> +#include <vector> +#include <wx/artprov.h> + +// Local events +wxDECLARE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent); + +// Foreign events +wxDECLARE_EVENT(PREVIOUS_MOVE_EVENT, wxCommandEvent); +wxDECLARE_EVENT(NEXT_MOVE_EVENT, wxCommandEvent); + +#define REFRESH_MOUSE_LOCATION() \ + { \ + const wxPoint pt = wxGetMousePosition(); \ + mouseX = pt.x - this->GetScreenPosition().x; \ + mouseY = pt.y - this->GetScreenPosition().y; \ + } + +#define INIT_CURRENT_SQUARE() \ + std::uint32_t file = 7 - (mouseX - boardX) / square_width; \ + std::uint32_t rank = (mouseY - boardY) / square_width; \ + if (!black_side) { \ + file = 7 - file; \ + rank = 7 - rank; \ + } \ + bool IsCurrentSquareValid = file >= 0 && file <= 7 && rank >= 0 && rank <= 7; + +#define MOUSE_ON(x, y, width, height) \ + (mouseX >= (x) && mouseX <= ((x) + (width)) && mouseY >= (y) && \ + mouseY <= ((y) + (height))) + +#define CAPTURE_FACTOR 0.5 + +typedef std::tuple<short, short, short> ClockTime; + +class BoardCanvas : public wxPanel { + Theme *t, *t_captures; + std::string board; + bool black_side, is_dragging, valid_drag, is_black_turn; + std::uint32_t boardX, boardY, square_width, mouseX, mouseY, lastClickX, + lastClickY; + wxSize canvas_size; + wxPoint active_square; + std::map<char, std::uint8_t> captures; + ClockTime black_time, white_time; + bool frozen,lock_square_size; + +public: + BoardCanvas(wxFrame *parent); + BoardCanvas(wxFrame *parent,std::uint32_t square_width, bool frozen); + void ApplyPreferences(); + void DrawBoard(wxPaintDC &dc); + void OnPaint(wxPaintEvent &event); + void OnKeyEvent(wxKeyEvent &event); + void MouseEvent(wxMouseEvent &event); + void Zoom(std::int32_t zoom); + void Swap(); + void SetupBoard(std::string board, bool is_black_turn, + std::map<char, std::uint8_t> captures); + void SetClockTime(short hours, short min, short sec, bool IsBlack); + DECLARE_EVENT_TABLE() +}; diff --git a/src/game_tab/left_panel/board/Theme.cpp b/src/game_tab/left_panel/board/Theme.cpp new file mode 100644 index 0000000..6ecbafc --- /dev/null +++ b/src/game_tab/left_panel/board/Theme.cpp @@ -0,0 +1,184 @@ +#include "Theme.hpp" +#include <wx/filename.h> + +Theme::Theme() : square_radius(10) { + // Load config + CONFIG_OPEN(config); + std::string piece = + config->Read("board/theme/pieces/path", "default").ToStdString(); + wxFileName piece_file(piece); + std::string square = + config->Read("board/theme/squares/path", "default").ToStdString(); + wxFileName square_file(square); + CONFIG_CLOSE(config); + // Piece + if (piece == "default" || !piece_file.FileExists()) { + wxLogDebug("Loading piece skin from binres"); + LoadPiecesSkin(LoadPNG("cburnett").ConvertToImage()); + } else { + wxLogDebug("Loading piece skin: %s", piece); + LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG)); + } + // Square + if (square == "default" || !square_file.FileExists()) { + wxLogDebug("Loading square skin from binres"); + LoadSquaresSkin(LoadPNG("chesscom_8bits").ConvertToImage()); + } else { + wxLogDebug("Loading square skin: %s", square); + LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG)); + } +} + +Theme::Theme(std::string piece, std::string square) : square_radius(10) { + wxLogDebug("Loading piece skin: %s", piece); + LoadPiecesSkin(wxImage(piece, wxBITMAP_TYPE_PNG)); + wxLogDebug("Loading square skin: %s", square); + LoadSquaresSkin(wxImage(square, wxBITMAP_TYPE_PNG)); +} + +Theme::~Theme() { + for (std::pair<char, wxBitmap *> c : skin_scaled) { + delete c.second; + } +} + +std::uint8_t Theme::GetSquareRadius() { return (square_radius); } + +void Theme::LoadPiecesSkin(wxImage iskin) { + if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != 6 * ELT_DIM) { + throw "Invalid piece theme"; + } + + int offset = 0; + // Kings + skin['k'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['K'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + // Queen + offset = ELT_DIM; + skin['q'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['Q'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + // Rook + offset = ELT_DIM * 2; + skin['r'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['R'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + // Bishop + offset = ELT_DIM * 3; + skin['b'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['B'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + // Knight + offset = ELT_DIM * 4; + skin['n'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['N'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + // Pawn + offset = ELT_DIM * 5; + skin['p'] = iskin.GetSubImage(wxRect(0, offset, ELT_DIM, ELT_DIM)); + skin['P'] = iskin.GetSubImage(wxRect(ELT_DIM, offset, ELT_DIM, ELT_DIM)); + + // Update scaled version + ResizePieces(DEFAULT_SIZE * PIECE_SIZE_FACTOR); +} + +void Theme::LoadSquaresSkin(wxImage iskin) { + if (iskin.GetWidth() != 2 * ELT_DIM || iskin.GetHeight() != ELT_DIM) { + throw "Invalid piece theme"; + } + + // Square + skin['s'] = iskin.GetSubImage(wxRect(0, 0, ELT_DIM, ELT_DIM)); + skin['S'] = iskin.GetSubImage(wxRect(ELT_DIM, 0, ELT_DIM, ELT_DIM)); + + // Update scaled version + ResizeSquares(DEFAULT_SIZE); +} + +wxBitmap *Theme::Get(char c) { return (skin_scaled[c]); } + +void Theme::ResizePieces(std::uint32_t width) { + for (std::pair<char, wxImage> c : skin) { + if (c.first != 's' && c.first != 'S') { + if (skin_scaled.count(c.first)) + delete skin_scaled[c.first]; + skin_scaled[c.first] = + new wxBitmap(c.second.Scale(width, width, wxIMAGE_QUALITY_HIGH)); + } + } +} + +void Theme::ResizeSquaresAndPieces(std::uint32_t width) { + ResizeSquares(width); + ResizePieces(width * PIECE_SIZE_FACTOR); +} + +void Theme::ResizeSquares(std::uint32_t width) { + if (skin_scaled.count('s')) + delete skin_scaled['s']; + skin_scaled['s'] = + new wxBitmap(skin['s'].Scale(width, width, wxIMAGE_QUALITY_HIGH)); + if (skin_scaled.count('S')) + delete skin_scaled['S']; + skin_scaled['S'] = + new wxBitmap(skin['S'].Scale(width, width, wxIMAGE_QUALITY_HIGH)); + + skin_scaled['0'] = new wxBitmap(*skin_scaled['S']); + skin_scaled['1'] = new wxBitmap(*skin_scaled['s']); + skin_scaled['2'] = new wxBitmap(*skin_scaled['s']); + skin_scaled['3'] = new wxBitmap(*skin_scaled['S']); + + skin_scaled['0']->SetMask(RoundedMask(width, 0)); + skin_scaled['1']->SetMask(RoundedMask(width, 1)); + skin_scaled['2']->SetMask(RoundedMask(width, 2)); + skin_scaled['3']->SetMask(RoundedMask(width, 3)); +} + +void Theme::Zoom(int amount) { + double width = skin_scaled['s']->GetWidth() + amount; + ResizeSquares(std::max(width, 1.0)); + ResizePieces(std::max(width * PIECE_SIZE_FACTOR, 1.0)); +} + +void Theme::SetSquareRadius(std::uint8_t radius) { + square_radius = radius; + Zoom(0); // Refresh scale +} + +/** + * This will never fail since k entry always exists (cf constructor and + * ResizePieces) + */ +double Theme::GetPiecesSizes() { return (skin_scaled['k']->GetWidth()); } + +/** + * This will never fail since s entry always exists (cf constructor and + * ResizeSquares) + */ +double Theme::GetSquaresSizes() { return (skin_scaled['s']->GetWidth()); } + +wxMask *Theme::RoundedMask(std::uint32_t width, std::uint8_t corner) { + + wxBitmap b(width, width, 1); + wxMemoryDC dc; + dc.SelectObject(b); + dc.SetPen(*wxBLACK_PEN); + dc.SetBrush(*wxBLACK_BRUSH); + dc.DrawRectangle(0, 0, width, width); + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(*wxWHITE_PEN); + dc.DrawRoundedRectangle(0, 0, width, width, square_radius); + + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(*wxWHITE_PEN); + if (corner == 0) { + dc.DrawRectangle(0, width / 2, width, width); + dc.DrawRectangle(width / 2, 0, width, width); + } else if (corner == 1) { + dc.DrawRectangle(0, 0, width / 2, width); + dc.DrawRectangle(0, width / 2, width, width); + } else if (corner == 2) { + dc.DrawRectangle(0, 0, width, width / 2); + dc.DrawRectangle(width / 2, 0, width, width); + } else if (corner == 3) { + dc.DrawRectangle(0, 0, width / 2, width); + dc.DrawRectangle(0, 0, width, width / 2); + } + return (new wxMask(b)); +} diff --git a/src/game_tab/left_panel/board/Theme.hpp b/src/game_tab/left_panel/board/Theme.hpp new file mode 100644 index 0000000..88036d8 --- /dev/null +++ b/src/game_tab/left_panel/board/Theme.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "ochess.hpp" + +#include <string> +#include <unordered_map> + +#define ELT_DIM 200 +#define DEFAULT_SIZE 80 +#define PIECE_SIZE_FACTOR 0.8 // Should be between 0 and 1 +#define DEFAULT_PIECE_THEME "assets/pieces/cburnett.png" +#define DEFAULT_SQUARE_THEME "assets/boards/chesscom_8bits.png" + +class Theme { +private: + std::unordered_map<char, wxImage> skin; + std::unordered_map<char, wxBitmap *> skin_scaled; + std::uint8_t square_radius; + wxMask *RoundedMask(std::uint32_t width, std::uint8_t corner); + +public: + Theme(); + Theme(std::string piece, std::string square); + ~Theme(); + void LoadPiecesSkin(wxImage skin); + void LoadSquaresSkin(wxImage iskin); + void ResizePieces(std::uint32_t width); + void ResizeSquares(std::uint32_t width); + void ResizeSquaresAndPieces(std::uint32_t width); + void SetSquareRadius(std::uint8_t radius); + std::uint8_t GetSquareRadius(); + void Zoom(int amount); + double GetPiecesSizes(); + double GetSquaresSizes(); + + wxBitmap *Get(char c); +}; |
