mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 09:30:24 +00:00
Chess: Added ability to put markings on the board
With this patch you can use right-click to mark a square on the board. You can add modifier keys to the click to change to alternate color (with CTRL) or secondary color (with Shift). If you right-click and drag from one square to another you will create an arrow. The markings go away as soon as you left-click on the board or the board state changes. Note: The arrows sometimes look weird, and horizontal ones get cut off. They also don't account for alpha. This is not a bug in Chess code, rather, likely in the fill_path() function that's used to draw the arrows. If anyone might know what's up with that I urge you to take a look. :)
This commit is contained in:
parent
f631e73519
commit
e119d7d6b9
Notes:
sideshowbarker
2024-07-19 17:31:08 +09:00
Author: https://github.com/AnicJov Commit: https://github.com/SerenityOS/serenity/commit/e119d7d6b90 Pull-request: https://github.com/SerenityOS/serenity/pull/4380
2 changed files with 111 additions and 1 deletions
|
@ -32,6 +32,7 @@
|
|||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <unistd.h>
|
||||
|
||||
ChessWidget::ChessWidget(const StringView& set)
|
||||
|
@ -88,6 +89,13 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
|
|||
painter.draw_text(shrunken_rect, coord.substring_view(1, 1), Gfx::Font::default_bold_font(), Gfx::TextAlignment::TopLeft, text_color);
|
||||
}
|
||||
|
||||
for (auto& m : m_board_markings) {
|
||||
if (m.type() == BoardMarking::Type::Square && m.from == sq) {
|
||||
Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_alternate_color : m_marking_primary_color);
|
||||
painter.fill_rect(tile_rect, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(m_dragging_piece && sq == m_moving_square)) {
|
||||
auto bmp = m_pieces.get(active_board.get_piece(sq));
|
||||
if (bmp.has_value()) {
|
||||
|
@ -98,6 +106,53 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
|
|||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
auto draw_arrow = [&painter](Gfx::FloatPoint A, Gfx::FloatPoint B, float w1, float w2, float h, Gfx::Color color) {
|
||||
float dx = B.x() - A.x();
|
||||
float dy = A.y() - B.y();
|
||||
float phi = atan2f(dy, dx);
|
||||
float hdx = h * cos(phi);
|
||||
float hdy = h * sin(phi);
|
||||
|
||||
Gfx::FloatPoint A1(A.x() - (w1 / 2) * cos(M_PI_2 - phi), A.y() - (w1 / 2) * sin(M_PI_2 - phi));
|
||||
Gfx::FloatPoint B3(A.x() + (w1 / 2) * cos(M_PI_2 - phi), A.y() + (w1 / 2) * sin(M_PI_2 - phi));
|
||||
Gfx::FloatPoint A2(A1.x() + (dx - hdx), A1.y() - (dy - hdy));
|
||||
Gfx::FloatPoint B2(B3.x() + (dx - hdx), B3.y() - (dy - hdy));
|
||||
Gfx::FloatPoint A3(A2.x() - w2 * cos(M_PI_2 - phi), A2.y() - w2 * sin(M_PI_2 - phi));
|
||||
Gfx::FloatPoint B1(B2.x() + w2 * cos(M_PI_2 - phi), B2.y() + w2 * sin(M_PI_2 - phi));
|
||||
|
||||
auto path = Gfx::Path();
|
||||
path.move_to(A);
|
||||
path.line_to(A1);
|
||||
path.line_to(A2);
|
||||
path.line_to(A3);
|
||||
path.line_to(B);
|
||||
path.line_to(B1);
|
||||
path.line_to(B2);
|
||||
path.line_to(B3);
|
||||
path.line_to(A);
|
||||
path.close();
|
||||
|
||||
painter.fill_path(path, color, Gfx::Painter::WindingRule::EvenOdd);
|
||||
};
|
||||
|
||||
for (auto& m : m_board_markings) {
|
||||
if (m.type() == BoardMarking::Type::Arrow) {
|
||||
Gfx::FloatPoint arrow_start;
|
||||
Gfx::FloatPoint arrow_end;
|
||||
|
||||
if (side() == Chess::Colour::White) {
|
||||
arrow_start = { m.from.file * tile_width + tile_width / 2.0f, (7 - m.from.rank) * tile_height + tile_height / 2.0f };
|
||||
arrow_end = { m.to.file * tile_width + tile_width / 2.0f, (7 - m.to.rank) * tile_height + tile_height / 2.0f };
|
||||
} else {
|
||||
arrow_start = { (7 - m.from.file) * tile_width + tile_width / 2.0f, m.from.rank * tile_height + tile_height / 2.0f };
|
||||
arrow_end = { (7 - m.to.file) * tile_width + tile_width / 2.0f, m.to.rank * tile_height + tile_height / 2.0f };
|
||||
}
|
||||
|
||||
Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_primary_color : m_marking_alternate_color);
|
||||
draw_arrow(arrow_start, arrow_end, tile_width / 8.0f, tile_width / 10.0f, tile_height / 2.5f, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dragging_piece) {
|
||||
auto bmp = m_pieces.get(active_board.get_piece(m_moving_square));
|
||||
if (bmp.has_value()) {
|
||||
|
@ -110,19 +165,43 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
|
|||
void ChessWidget::mousedown_event(GUI::MouseEvent& event)
|
||||
{
|
||||
GUI::Widget::mousedown_event(event);
|
||||
|
||||
if (event.button() == GUI::MouseButton::Right) {
|
||||
m_current_marking.from = mouse_to_square(event);
|
||||
return;
|
||||
}
|
||||
m_board_markings.clear();
|
||||
|
||||
auto square = mouse_to_square(event);
|
||||
auto piece = board().get_piece(square);
|
||||
if (drag_enabled() && piece.colour == board().turn() && !m_playback) {
|
||||
m_dragging_piece = true;
|
||||
m_drag_point = event.position();
|
||||
m_moving_square = square;
|
||||
update();
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ChessWidget::mouseup_event(GUI::MouseEvent& event)
|
||||
{
|
||||
GUI::Widget::mouseup_event(event);
|
||||
|
||||
if (event.button() == GUI::MouseButton::Right) {
|
||||
m_current_marking.secondary_color = event.shift();
|
||||
m_current_marking.alternate_color = event.ctrl();
|
||||
m_current_marking.to = mouse_to_square(event);
|
||||
auto match_index = m_board_markings.find_first_index(m_current_marking);
|
||||
if (match_index.has_value()) {
|
||||
m_board_markings.remove(match_index.value());
|
||||
update();
|
||||
return;
|
||||
}
|
||||
m_board_markings.append(m_current_marking);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_dragging_piece)
|
||||
return;
|
||||
|
||||
|
@ -286,6 +365,7 @@ RefPtr<Gfx::Bitmap> ChessWidget::get_piece_graphic(const Chess::Piece& piece) co
|
|||
|
||||
void ChessWidget::reset()
|
||||
{
|
||||
m_board_markings.clear();
|
||||
m_playback = false;
|
||||
m_playback_move_number = 0;
|
||||
m_board_playback = Chess::Board();
|
||||
|
@ -325,6 +405,7 @@ void ChessWidget::maybe_input_engine_move()
|
|||
ASSERT(board().apply_move(move));
|
||||
m_playback_move_number = m_board.moves().size();
|
||||
m_playback = false;
|
||||
m_board_markings.clear();
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
@ -335,6 +416,7 @@ void ChessWidget::playback_move(PlaybackDirection direction)
|
|||
return;
|
||||
|
||||
m_playback = true;
|
||||
m_board_markings.clear();
|
||||
|
||||
switch (direction) {
|
||||
case PlaybackDirection::Backward:
|
||||
|
@ -469,6 +551,7 @@ bool ChessWidget::import_pgn(const StringView& import_path)
|
|||
}
|
||||
}
|
||||
|
||||
m_board_markings.clear();
|
||||
m_board_playback = m_board;
|
||||
m_playback_move_number = m_board_playback.moves().size();
|
||||
m_playback = true;
|
||||
|
|
|
@ -100,13 +100,40 @@ public:
|
|||
void set_coordinates(bool coordinates) { m_coordinates = coordinates; }
|
||||
bool coordinates() const { return m_coordinates; }
|
||||
|
||||
struct BoardMarking {
|
||||
Chess::Square from { 50, 50 };
|
||||
Chess::Square to { 50, 50 };
|
||||
bool alternate_color { false };
|
||||
bool secondary_color { false };
|
||||
enum class Type {
|
||||
Square,
|
||||
Arrow,
|
||||
None
|
||||
};
|
||||
Type type() const
|
||||
{
|
||||
if (from.in_bounds() && to.in_bounds() && from != to)
|
||||
return Type::Arrow;
|
||||
else if ((from.in_bounds() && !to.in_bounds()) || (from.in_bounds() && to.in_bounds() && from == to))
|
||||
return Type::Square;
|
||||
|
||||
return Type::None;
|
||||
}
|
||||
bool operator==(const BoardMarking& other) const { return from == other.from && to == other.to; }
|
||||
};
|
||||
|
||||
private:
|
||||
Chess::Board m_board;
|
||||
Chess::Board m_board_playback;
|
||||
bool m_playback { false };
|
||||
size_t m_playback_move_number { 0 };
|
||||
BoardMarking m_current_marking;
|
||||
Vector<BoardMarking> m_board_markings;
|
||||
BoardTheme m_board_theme { "Beige", Color::from_rgb(0xb58863), Color::from_rgb(0xf0d9b5) };
|
||||
Color m_move_highlight_color { Color::from_rgba(0x66ccee00) };
|
||||
Color m_marking_primary_color { Color::from_rgba(0x66ff0000) };
|
||||
Color m_marking_alternate_color { Color::from_rgba(0x66ffaa00) };
|
||||
Color m_marking_secondary_color { Color::from_rgba(0x6655dd55) };
|
||||
Chess::Colour m_side { Chess::Colour::White };
|
||||
HashMap<Chess::Piece, RefPtr<Gfx::Bitmap>> m_pieces;
|
||||
String m_piece_set;
|
||||
|
|
Loading…
Reference in a new issue