From 2a1fb77faf2bab7b742b4e66f05cfd34eaff279b Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 6 Jan 2023 08:25:38 -0500 Subject: [PATCH] LibCards: Support "previewing" cards that may be covered by other cards For example, in Solitaire, the vertical normal stacks cover the suit of all but the topmost card in the stack. To see the suit of covered cards the user currently has to move the cards on top of them out of the way. This adds an API for games to set a card at a location to be previewed, which will draw that card on top of all other cards without moving it. --- Userland/Libraries/LibCards/Card.h | 3 ++ Userland/Libraries/LibCards/CardGame.cpp | 18 +++++++++++ Userland/Libraries/LibCards/CardGame.h | 5 +++ Userland/Libraries/LibCards/CardStack.cpp | 37 +++++++++++++++++++++++ Userland/Libraries/LibCards/CardStack.h | 4 +++ 5 files changed, 67 insertions(+) diff --git a/Userland/Libraries/LibCards/Card.h b/Userland/Libraries/LibCards/Card.h index 72101ff0af..e1d05acad2 100644 --- a/Userland/Libraries/LibCards/Card.h +++ b/Userland/Libraries/LibCards/Card.h @@ -98,12 +98,14 @@ public: bool is_moving() const { return m_moving; } bool is_upside_down() const { return m_upside_down; } bool is_inverted() const { return m_inverted; } + bool is_previewed() const { return m_previewed; } Gfx::Color color() const { return (m_suit == Suit::Diamonds || m_suit == Suit::Hearts) ? Color::Red : Color::Black; } void set_position(const Gfx::IntPoint p) { m_rect.set_location(p); } void set_moving(bool moving) { m_moving = moving; } void set_upside_down(bool upside_down) { m_upside_down = upside_down; } void set_inverted(bool inverted) { m_inverted = inverted; } + void set_previewed(bool previewed) { m_previewed = previewed; } void save_old_position(); @@ -122,6 +124,7 @@ private: bool m_moving { false }; bool m_upside_down { false }; bool m_inverted { false }; + bool m_previewed { false }; }; enum class Shuffle { diff --git a/Userland/Libraries/LibCards/CardGame.cpp b/Userland/Libraries/LibCards/CardGame.cpp index 79410bc880..5596331cc4 100644 --- a/Userland/Libraries/LibCards/CardGame.cpp +++ b/Userland/Libraries/LibCards/CardGame.cpp @@ -144,4 +144,22 @@ void CardGame::set_background_color(Gfx::Color color) CardPainter::the().set_background_color(color); } +void CardGame::preview_card(CardStack& stack, Gfx::IntPoint click_location) +{ + if (!stack.preview_card(click_location)) + return; + + m_previewed_card_stack = stack; + update(stack.bounding_box()); +} + +void CardGame::clear_card_preview() +{ + VERIFY(m_previewed_card_stack); + + update(m_previewed_card_stack->bounding_box()); + m_previewed_card_stack->clear_card_preview(); + m_previewed_card_stack = nullptr; +} + } diff --git a/Userland/Libraries/LibCards/CardGame.h b/Userland/Libraries/LibCards/CardGame.h index d739dc84a2..02fe41ecd5 100644 --- a/Userland/Libraries/LibCards/CardGame.h +++ b/Userland/Libraries/LibCards/CardGame.h @@ -41,6 +41,10 @@ public: void drop_cards_on_stack(CardStack&, CardStack::MovementRule); void clear_moving_cards(); + bool is_previewing_card() const { return !m_previewed_card_stack.is_null(); } + void preview_card(CardStack&, Gfx::IntPoint click_location); + void clear_card_preview(); + void dump_layout() const; protected: @@ -53,6 +57,7 @@ private: NonnullRefPtrVector m_moving_cards; RefPtr m_moving_cards_source_stack; + RefPtr m_previewed_card_stack; }; } diff --git a/Userland/Libraries/LibCards/CardStack.cpp b/Userland/Libraries/LibCards/CardStack.cpp index 9e7c297225..8c4a9ff0d4 100644 --- a/Userland/Libraries/LibCards/CardStack.cpp +++ b/Userland/Libraries/LibCards/CardStack.cpp @@ -93,12 +93,23 @@ void CardStack::paint(GUI::Painter& painter, Gfx::Color background_color) return; } + RefPtr previewed_card; + for (size_t i = 0; i < m_stack.size(); ++i) { if (auto& card = m_stack[i]; !card.is_moving()) { + if (card.is_previewed()) { + VERIFY(!previewed_card); + previewed_card = card; + continue; + } + auto highlighted = m_highlighted && (i == m_stack.size() - 1); card.clear_and_paint(painter, Gfx::Color::Transparent, highlighted); } } + + if (previewed_card) + previewed_card->clear_and_paint(painter, Gfx::Color::Transparent, false); } void CardStack::rebound_cards() @@ -239,6 +250,32 @@ bool CardStack::is_allowed_to_push(Card const& card, size_t stack_size, Movement return true; } +bool CardStack::preview_card(Gfx::IntPoint click_location) +{ + RefPtr last_intersect; + + for (auto& card : m_stack) { + if (!card.rect().contains(click_location)) + continue; + if (card.is_upside_down()) + continue; + + last_intersect = card; + } + + if (!last_intersect) + return false; + + last_intersect->set_previewed(true); + return true; +} + +void CardStack::clear_card_preview() +{ + for (auto& card : m_stack) + card.set_previewed(false); +} + bool CardStack::make_top_card_visible() { if (is_empty()) diff --git a/Userland/Libraries/LibCards/CardStack.h b/Userland/Libraries/LibCards/CardStack.h index bebbb10a39..d27e03c9cd 100644 --- a/Userland/Libraries/LibCards/CardStack.h +++ b/Userland/Libraries/LibCards/CardStack.h @@ -50,6 +50,10 @@ public: bool is_allowed_to_push(Card const&, size_t stack_size = 1, MovementRule movement_rule = MovementRule::Alternating) const; void add_all_grabbed_cards(Gfx::IntPoint click_location, NonnullRefPtrVector& grabbed, MovementRule movement_rule = MovementRule::Alternating); + + bool preview_card(Gfx::IntPoint click_location); + void clear_card_preview(); + void paint(GUI::Painter&, Gfx::Color background_color); void clear();