mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-09 01:23:57 -07:00
feat(command-zone): add graphics implementation with integration tests
Implement CommandZone - the main Qt graphics class for rendering and interacting with command zones. This commit brings together all previous components: - Uses CommandZoneState for visibility management - Uses CommandZoneLogic for card data handling - Uses ZoneToggleButton for visibility controls - Uses CommanderTaxCounter for tax display - Uses z_values.h for proper visual layering Zone modifications for command zone support: - SelectZone: add command zone to zone selection - StackZone: support command zone in stack operations - TableZone: command zone positioning integration - ViewZoneWidget: command zone viewing support Integration tests verify: - Counter parenting behavior - Zone state coordination - Full graphics stack interaction
This commit is contained in:
parent
78db49a9b3
commit
6e83d64622
12 changed files with 1570 additions and 8 deletions
374
cockatrice/src/game/zones/command_zone.cpp
Normal file
374
cockatrice/src/game/zones/command_zone.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
#include "command_zone.h"
|
||||
|
||||
// @see CommandZoneState for the extracted visibility/height state machine
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../interface/theme_manager.h"
|
||||
#include "../board/abstract_counter.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../player/player.h"
|
||||
#include "../player/player_actions.h"
|
||||
#include "../z_values.h"
|
||||
#include "select_zone.h"
|
||||
#include "zone_toggle_button.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <libcockatrice/common/counter_ids.h>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
|
||||
CommandZone::CommandZone(CommandZoneLogic *_logic, int _zoneHeight, CommandZoneType _zoneType, QGraphicsItem *parent)
|
||||
: SelectZone(_logic, parent), m_state(_zoneHeight, _zoneType), toggleButton(nullptr), cardClipContainer(nullptr)
|
||||
{
|
||||
connect(themeManager, &ThemeManager::themeChanged, this, &CommandZone::updateBg);
|
||||
updateBg();
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
|
||||
|
||||
cardClipContainer = new QGraphicsRectItem(this);
|
||||
cardClipContainer->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
|
||||
cardClipContainer->setPen(Qt::NoPen);
|
||||
cardClipContainer->setBrush(Qt::NoBrush);
|
||||
cardClipContainer->setRect(boundingRect());
|
||||
cardClipContainer->setZValue(ZValues::CARD_BASE);
|
||||
|
||||
disconnect(getLogic(), &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded);
|
||||
connect(getLogic(), &CardZoneLogic::cardAdded, this, [this](CardItem *card) {
|
||||
if (cardClipContainer && card) {
|
||||
card->setParentItem(cardClipContainer);
|
||||
card->update();
|
||||
}
|
||||
});
|
||||
|
||||
if (!isPrimary()) {
|
||||
toggleButton = new ZoneToggleButton(getZoneType(), this);
|
||||
connect(toggleButton, &ZoneToggleButton::clicked, this, &CommandZone::toggleExpanded);
|
||||
repositionToggleButton();
|
||||
}
|
||||
}
|
||||
|
||||
CommandZone::~CommandZone()
|
||||
{
|
||||
disconnect(getLogic(), &CardZoneLogic::cardAdded, this, nullptr);
|
||||
}
|
||||
|
||||
void CommandZone::updateBg()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
QRectF CommandZone::boundingRect() const
|
||||
{
|
||||
return {0, 0, ZoneSizes::COMMAND_ZONE_WIDTH, currentHeight()};
|
||||
}
|
||||
|
||||
qreal CommandZone::currentHeight() const
|
||||
{
|
||||
return m_state.currentHeight();
|
||||
}
|
||||
|
||||
bool CommandZone::isPrimary() const
|
||||
{
|
||||
return m_state.isPrimary();
|
||||
}
|
||||
|
||||
bool CommandZone::isMinimized() const
|
||||
{
|
||||
return m_state.isMinimized();
|
||||
}
|
||||
|
||||
bool CommandZone::isExpanded() const
|
||||
{
|
||||
return m_state.isExpanded();
|
||||
}
|
||||
|
||||
bool CommandZone::isCollapsed() const
|
||||
{
|
||||
return m_state.isCollapsed();
|
||||
}
|
||||
|
||||
void CommandZone::toggleExpanded()
|
||||
{
|
||||
applyStateChange(m_state.tryToggleExpanded(shouldPreventCollapse()));
|
||||
}
|
||||
|
||||
void CommandZone::toggleMinimized()
|
||||
{
|
||||
applyStateChange(m_state.tryToggleMinimized(), true);
|
||||
}
|
||||
|
||||
void CommandZone::applyStateChange(const StateChangeResult &result, bool updateTogglePosition)
|
||||
{
|
||||
if (!result.geometryChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
prepareGeometryChange();
|
||||
if (cardClipContainer) {
|
||||
cardClipContainer->setRect(boundingRect());
|
||||
}
|
||||
if (toggleButton) {
|
||||
toggleButton->setExpanded(m_state.isExpanded());
|
||||
}
|
||||
reorganizeCards();
|
||||
if (updateTogglePosition) {
|
||||
repositionToggleButton();
|
||||
}
|
||||
update();
|
||||
|
||||
if (result.shouldEmitMinimized) {
|
||||
emit minimizedChanged(m_state.isMinimized());
|
||||
}
|
||||
if (result.shouldEmitExpanded) {
|
||||
emit expandedChanged(m_state.isExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandZone::paint(QPainter *painter,
|
||||
[[maybe_unused]] const QStyleOptionGraphicsItem *option,
|
||||
[[maybe_unused]] QWidget *widget)
|
||||
{
|
||||
if (isCollapsed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Command, getLogic()->getPlayer()->getZoneId());
|
||||
|
||||
QPointF scenePos = mapToScene(QPointF(0, 0));
|
||||
painter->setBrushOrigin(-scenePos);
|
||||
|
||||
painter->fillRect(boundingRect(), brush);
|
||||
}
|
||||
|
||||
void CommandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
||||
CardZoneLogic *startZone,
|
||||
const QPoint &dropPoint)
|
||||
{
|
||||
if (dragItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (startZone == nullptr || startZone->getPlayer() == nullptr) {
|
||||
qCWarning(CommandZoneLog) << "handleDropEvent: Invalid startZone or player";
|
||||
return;
|
||||
}
|
||||
|
||||
// Primary and Partner zones hold exactly one card; Companion and Background support multiple
|
||||
if (!supportsMultipleCards(getZoneType()) && !getLogic()->getCards().isEmpty()) {
|
||||
qCDebug(CommandZoneLog) << "handleDropEvent: Single-card zone already occupied, ignoring drop";
|
||||
return;
|
||||
}
|
||||
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
|
||||
// Calculate insertion index from drop position for multi-card zones
|
||||
int index = 0;
|
||||
const auto &cards = getLogic()->getCards();
|
||||
if (!cards.isEmpty()) {
|
||||
const auto cardCount = static_cast<int>(cards.size());
|
||||
const qreal cardHeight = cards.at(0)->boundingRect().height();
|
||||
|
||||
index = qRound(divideCardSpaceInZone(dropPoint.y(), cardCount, boundingRect().height(), cardHeight, true));
|
||||
index = qBound(0, index, cardCount);
|
||||
}
|
||||
|
||||
cmd.set_x(index);
|
||||
cmd.set_y(0);
|
||||
|
||||
// Add all dragged cards to the move command
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
if (item != nullptr) {
|
||||
cmd.mutable_cards_to_move()->add_card()->set_card_id(item->getId());
|
||||
}
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
}
|
||||
|
||||
void CommandZone::reorganizeCards()
|
||||
{
|
||||
const auto &cards = getLogic()->getCards();
|
||||
|
||||
if (cardClipContainer) {
|
||||
cardClipContainer->setRect(boundingRect());
|
||||
}
|
||||
|
||||
// Hide all cards when collapsed
|
||||
if (isCollapsed()) {
|
||||
for (CardItem *card : cards) {
|
||||
if (card != nullptr) {
|
||||
card->setVisible(false);
|
||||
}
|
||||
}
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cards.isEmpty()) {
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cardCount = static_cast<int>(cards.size());
|
||||
const qreal totalWidth = boundingRect().width();
|
||||
const qreal totalHeight = boundingRect().height();
|
||||
const qreal cardWidth = cards.at(0)->boundingRect().width();
|
||||
const qreal cardHeight = cards.at(0)->boundingRect().height();
|
||||
|
||||
// Zigzag horizontal positioning (StackZone pattern)
|
||||
// Single card stays centered for visual consistency with Primary/Partner zones
|
||||
const qreal xMargin = 5;
|
||||
const qreal x1 = xMargin;
|
||||
const qreal x2 = totalWidth - xMargin - cardWidth;
|
||||
|
||||
for (int i = 0; i < cardCount; i++) {
|
||||
CardItem *card = cards.at(i);
|
||||
if (card == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Horizontal: center single card, zigzag for multiple
|
||||
qreal x = (cardCount == 1) ? (totalWidth - cardWidth) / 2.0 : ((i % 2) ? x2 : x1);
|
||||
|
||||
// Vertical: use divideCardSpaceInZone for proper stacking with overlap compression.
|
||||
// divideCardSpaceInZone can return negative Y when totalHeight < cardHeight.
|
||||
// Clamp to 0 so card tops remain visible when clipped by cardClipContainer.
|
||||
const qreal y = qMax(0.0, divideCardSpaceInZone(i, cardCount, totalHeight, cardHeight));
|
||||
|
||||
card->setVisible(true);
|
||||
card->setPos(x, y);
|
||||
card->setRealZValue(ZValues::CARD_BASE + i);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void CommandZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
toggleMinimized();
|
||||
event->accept();
|
||||
} else {
|
||||
SelectZone::mouseDoubleClickEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandZone::repositionToggleButton()
|
||||
{
|
||||
if (toggleButton) {
|
||||
QRectF btnRect = toggleButton->boundingRect();
|
||||
qreal btnX = (boundingRect().width() - btnRect.width()) / 2.0;
|
||||
qreal btnY = 0;
|
||||
toggleButton->setPos(btnX, btnY);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandZone::setExpanded(bool _expanded)
|
||||
{
|
||||
applyStateChange(m_state.trySetExpanded(_expanded, shouldPreventCollapse()));
|
||||
}
|
||||
|
||||
bool CommandZone::shouldPreventCollapse() const
|
||||
{
|
||||
if (isPrimary())
|
||||
return false;
|
||||
|
||||
if (!getLogic()->getCards().isEmpty())
|
||||
return true;
|
||||
|
||||
if (getZoneType() == CommandZoneType::Partner) {
|
||||
Player *player = getLogic()->getPlayer();
|
||||
if (player) {
|
||||
AbstractCounter *partnerTax = player->getCounters().value(CounterIds::PartnerTax, nullptr);
|
||||
if (partnerTax && partnerTax->getValue() > 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check Qt children (Partner has toggle button as child, and Partner is nested under Command)
|
||||
for (QGraphicsItem *child : childItems()) {
|
||||
if (auto *childZone = dynamic_cast<CommandZone *>(child)) {
|
||||
if (childZone->shouldPreventCollapse())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check logical children (for sibling zones: Companion is logical child of Partner,
|
||||
// Background is logical child of Companion)
|
||||
for (CommandZone *childZone : logicalChildZones) {
|
||||
if (childZone != nullptr && childZone->shouldPreventCollapse())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandZone::tryAutoMinimize()
|
||||
{
|
||||
// Check Qt children first (Partner is nested under Command)
|
||||
for (QGraphicsItem *child : childItems()) {
|
||||
if (auto *childZone = dynamic_cast<CommandZone *>(child)) {
|
||||
if (childZone->tryAutoMinimize())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check logical children (sibling zones: Companion under Partner, Background under Companion)
|
||||
for (CommandZone *childZone : logicalChildZones) {
|
||||
if (childZone != nullptr && childZone->tryAutoMinimize())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isExpanded() && !isMinimized() && !isPrimary()) {
|
||||
toggleMinimized();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandZone *CommandZone::getParentCommandZone() const
|
||||
{
|
||||
QGraphicsItem *parent = parentItem();
|
||||
while (parent != nullptr) {
|
||||
if (auto *parentZone = dynamic_cast<CommandZone *>(parent)) {
|
||||
return parentZone;
|
||||
}
|
||||
parent = parent->parentItem();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CommandZone::tryMinimizeAbove()
|
||||
{
|
||||
// First check logical parent (for sibling zones: Companion, Background)
|
||||
CommandZone *parentZone = logicalParentZone;
|
||||
|
||||
// Fall back to Qt parent chain for Partner zone (still nested under Command)
|
||||
if (parentZone == nullptr) {
|
||||
parentZone = getParentCommandZone();
|
||||
}
|
||||
|
||||
if (parentZone != nullptr && parentZone->isExpanded() && !parentZone->isMinimized()) {
|
||||
parentZone->toggleMinimized();
|
||||
return true;
|
||||
}
|
||||
// If parent can't minimize, try grandparent (recursion bounded by zone depth)
|
||||
if (parentZone != nullptr) {
|
||||
return parentZone->tryMinimizeAbove();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandZone::setToggleButtonVisible(bool visible)
|
||||
{
|
||||
if (toggleButton) {
|
||||
toggleButton->setVisible(visible);
|
||||
}
|
||||
}
|
||||
190
cockatrice/src/game/zones/command_zone.h
Normal file
190
cockatrice/src/game/zones/command_zone.h
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* @file command_zone.h
|
||||
* @ingroup GameGraphicsZones
|
||||
* @brief Graphics layer for the command zone, used for Commander format.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_COMMAND_ZONE_H
|
||||
#define COCKATRICE_COMMAND_ZONE_H
|
||||
|
||||
#include "command_zone_state.h"
|
||||
#include "command_zone_types.h"
|
||||
#include "logic/command_zone_logic.h"
|
||||
#include "select_zone.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CommandZoneLog, "command_zone");
|
||||
|
||||
class ZoneToggleButton;
|
||||
|
||||
/**
|
||||
* @class CommandZone
|
||||
* @brief Graphics layer for command zones in Commander format games.
|
||||
*
|
||||
* Supports primary (always visible) and collapsible (partner, companion, background)
|
||||
* variants. Primary/Partner hold one card each; Companion/Background support multiple
|
||||
* cards for edge-case mechanics. Single cards display centered; multiple cards use
|
||||
* zigzag horizontal positioning with vertical stacking.
|
||||
*
|
||||
* @see CommandZoneState for visibility/height state machine
|
||||
* @see supportsMultipleCards() in command_zone_types.h
|
||||
*/
|
||||
class CommandZone : public SelectZone
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
CommandZoneState m_state; ///< Pure state machine for visibility/height
|
||||
ZoneToggleButton *toggleButton; ///< Only created for collapsible zones
|
||||
QGraphicsRectItem *cardClipContainer; ///< Clips cards to zone bounds when minimized
|
||||
|
||||
/**
|
||||
* @brief Logical parent for Qt-sibling zones (Companion, Background).
|
||||
*
|
||||
* INVARIANT: A zone has EITHER a Qt parent CommandZone (Partner) OR this
|
||||
* logical parent, never both. tryMinimizeAbove() relies on this exclusivity.
|
||||
*/
|
||||
CommandZone *logicalParentZone = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Logical child zones for sibling architecture.
|
||||
*
|
||||
* Tracks zones that are logically children but Qt siblings. Used by
|
||||
* shouldPreventCollapse() and tryAutoMinimize() to traverse the hierarchy.
|
||||
*/
|
||||
QList<CommandZone *> logicalChildZones;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a CommandZone graphics item.
|
||||
* @param _logic Logic layer managing card data
|
||||
* @param _zoneHeight Zone height in pixels
|
||||
* @param _zoneType Type of command zone (Primary, Partner, Companion, Background)
|
||||
* @param parent Parent graphics item
|
||||
*/
|
||||
CommandZone(CommandZoneLogic *_logic, int _zoneHeight, CommandZoneType _zoneType, QGraphicsItem *parent);
|
||||
~CommandZone() override;
|
||||
|
||||
/**
|
||||
* @brief Handles card drops, calculating insertion position from drop point.
|
||||
* @param dragItems Cards being dragged
|
||||
* @param startZone Source zone
|
||||
* @param dropPoint Drop position in local coordinates
|
||||
*/
|
||||
void
|
||||
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;
|
||||
|
||||
[[nodiscard]] QRectF boundingRect() const override;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
void reorganizeCards() override;
|
||||
|
||||
/**
|
||||
* @brief Sets expanded/collapsed state of a collapsible zone.
|
||||
*
|
||||
* When collapsed, background and cards are hidden but toggle button remains.
|
||||
* When expanded, zone renders normally with centered cards.
|
||||
* No-op for Primary zone.
|
||||
*
|
||||
* @param expanded True to show zone content, false to collapse
|
||||
*/
|
||||
void setExpanded(bool expanded);
|
||||
|
||||
void toggleMinimized();
|
||||
[[nodiscard]] bool isMinimized() const;
|
||||
[[nodiscard]] bool isExpanded() const;
|
||||
[[nodiscard]] bool isCollapsed() const;
|
||||
[[nodiscard]] bool isPrimary() const;
|
||||
[[nodiscard]] CommandZoneType getZoneType() const
|
||||
{
|
||||
return m_state.getZoneType();
|
||||
}
|
||||
[[nodiscard]] qreal currentHeight() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the logical parent zone for sibling architecture.
|
||||
* @param parent The logical parent CommandZone, or nullptr for nested zones
|
||||
*/
|
||||
void setLogicalParentZone(CommandZone *parent)
|
||||
{
|
||||
logicalParentZone = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the logical parent zone.
|
||||
* @return The logical parent, or nullptr if using Qt parent-child hierarchy
|
||||
*/
|
||||
[[nodiscard]] CommandZone *getLogicalParentZone() const
|
||||
{
|
||||
return logicalParentZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a logical child zone for sibling architecture.
|
||||
* @param child The zone that is logically a child but a Qt sibling
|
||||
*/
|
||||
void addLogicalChildZone(CommandZone *child)
|
||||
{
|
||||
logicalChildZones.append(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to auto-minimize this zone or a child zone (depth-first).
|
||||
*
|
||||
* Tries children first (depth-first, bottom-up) to minimize the
|
||||
* deepest non-minimized zone. Used as a fallback safety-net in
|
||||
* rearrangeZones() for non-expansion triggers (e.g., window resize).
|
||||
*
|
||||
* For user-initiated zone expansion, tryMinimizeAbove() is preferred
|
||||
* as it minimizes the zone above instead of below.
|
||||
*
|
||||
* @return True if a zone was minimized, false if no minimization possible
|
||||
* @see tryMinimizeAbove()
|
||||
*/
|
||||
bool tryAutoMinimize();
|
||||
|
||||
/**
|
||||
* @brief Returns the nearest CommandZone ancestor in the graphics item hierarchy.
|
||||
* @return Pointer to parent CommandZone, or nullptr if none exists.
|
||||
*/
|
||||
[[nodiscard]] CommandZone *getParentCommandZone() const;
|
||||
|
||||
/**
|
||||
* @brief Attempts to minimize the zone directly above this one.
|
||||
*
|
||||
* Walks up the parent chain to find the first ancestor CommandZone that
|
||||
* can be minimized (is expanded and not already minimized). The Primary
|
||||
* zone can be minimized (reduced to 25% height) but cannot be collapsed.
|
||||
*
|
||||
* Recursion is bounded by zone hierarchy depth (max 4 zones).
|
||||
*
|
||||
* @return True if an ancestor zone was minimized, false if none could be.
|
||||
* @see tryAutoMinimize()
|
||||
*/
|
||||
bool tryMinimizeAbove();
|
||||
|
||||
/**
|
||||
* @brief Sets toggle button visibility.
|
||||
* @param visible Whether toggle should be visible
|
||||
*/
|
||||
void setToggleButtonVisible(bool visible);
|
||||
|
||||
public slots:
|
||||
void toggleExpanded();
|
||||
|
||||
signals:
|
||||
void expandedChanged(bool isExpanded);
|
||||
void minimizedChanged(bool isMinimized);
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void updateBg();
|
||||
|
||||
private:
|
||||
void repositionToggleButton();
|
||||
void applyStateChange(const StateChangeResult &result, bool updateTogglePosition = false);
|
||||
[[nodiscard]] bool shouldPreventCollapse() const;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_COMMAND_ZONE_H
|
||||
|
|
@ -9,6 +9,15 @@
|
|||
|
||||
qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal cardHeight, bool reverse)
|
||||
{
|
||||
// Handle single card case - return centered position or index 0
|
||||
if (cardCount <= 1) {
|
||||
if (reverse) {
|
||||
return 0; // Single card maps to index 0
|
||||
} else {
|
||||
return (totalHeight - cardHeight) / 2.0; // Center the single card
|
||||
}
|
||||
}
|
||||
|
||||
qreal cardMinOverlap = cardHeight * SettingsCache::instance().getStackCardOverlapPercent() / 100;
|
||||
qreal desiredHeight = cardHeight * cardCount - cardMinOverlap * (cardCount - 1);
|
||||
qreal y;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @file select_zone.h
|
||||
* @ingroup GameGraphicsZones
|
||||
* @brief TODO: Document this.
|
||||
* @brief Base class for zones with directly clickable, laid-out cards.
|
||||
*/
|
||||
|
||||
#ifndef SELECTZONE_H
|
||||
|
|
@ -12,7 +12,16 @@
|
|||
#include <QSet>
|
||||
|
||||
/**
|
||||
* A CardZone where the cards are laid out, with each card directly interactable by clicking.
|
||||
* @class SelectZone
|
||||
* @brief Base class for zones where cards are laid out and directly clickable.
|
||||
*
|
||||
* SelectZone provides mouse interaction handling for zones that display cards
|
||||
* in a visible layout (as opposed to hidden zones like the deck). It supports
|
||||
* click-to-select and rectangle selection via click-and-drag.
|
||||
*
|
||||
* Subclasses include TableZone, HandZone, and CommandZone.
|
||||
*
|
||||
* @see CardZone
|
||||
*/
|
||||
class SelectZone : public CardZone
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ QRectF StackZone::boundingRect() const
|
|||
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId());
|
||||
|
||||
// Set brush origin to compensate for zone position, keeping texture fixed in scene coordinates
|
||||
QPointF scenePos = pos();
|
||||
painter->setBrushOrigin(-scenePos);
|
||||
|
||||
painter->fillRect(boundingRect(), brush);
|
||||
}
|
||||
|
||||
|
|
@ -106,3 +111,11 @@ void StackZone::reorganizeCards()
|
|||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void StackZone::setHeight(qreal newHeight)
|
||||
{
|
||||
prepareGeometryChange();
|
||||
zoneHeight = newHeight;
|
||||
reorganizeCards();
|
||||
update();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public:
|
|||
QRectF boundingRect() const override;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
void reorganizeCards() override;
|
||||
void setHeight(qreal newHeight);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "../board/card_item.h"
|
||||
#include "../player/player.h"
|
||||
#include "../player/player_actions.h"
|
||||
#include "../z_value_layer_manager.h"
|
||||
#include "logic/table_zone_logic.h"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
|
|
@ -167,7 +168,9 @@ void TableZone::reorganizeCards()
|
|||
actualY += 15;
|
||||
|
||||
getLogic()->getCards()[i]->setPos(actualX, actualY);
|
||||
getLogic()->getCards()[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100);
|
||||
qreal cardZValue = (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100;
|
||||
Q_ASSERT(ZValueLayerManager::isValidCardZValue(cardZValue));
|
||||
getLogic()->getCards()[i]->setRealZValue(cardZValue);
|
||||
|
||||
QListIterator<CardItem *> attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards());
|
||||
int j = 0;
|
||||
|
|
@ -177,7 +180,9 @@ void TableZone::reorganizeCards()
|
|||
qreal childX = actualX - j * STACKED_CARD_OFFSET_X;
|
||||
qreal childY = y + 5;
|
||||
attachedCard->setPos(childX, childY);
|
||||
attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100);
|
||||
qreal attachedZValue = (childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100;
|
||||
Q_ASSERT(ZValueLayerManager::isValidCardZValue(attachedZValue));
|
||||
attachedCard->setRealZValue(attachedZValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "../game_scene.h"
|
||||
#include "../player/player.h"
|
||||
#include "../player/player_actions.h"
|
||||
#include "../z_values.h"
|
||||
#include "view_zone.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
|
@ -47,7 +48,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
|
|||
{
|
||||
setAcceptHoverEvents(true);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setZValue(2000000006);
|
||||
setZValue(ZValues::ZONE_VIEW_WIDGET);
|
||||
setFlag(ItemIgnoresTransformations);
|
||||
|
||||
QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical);
|
||||
|
|
@ -71,7 +72,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
|
|||
|
||||
QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget;
|
||||
searchEditProxy->setWidget(&searchEdit);
|
||||
searchEditProxy->setZValue(2000000007);
|
||||
searchEditProxy->setZValue(ZValues::DRAG_ITEM);
|
||||
vbox->addItem(searchEditProxy);
|
||||
|
||||
// top row
|
||||
|
|
@ -80,13 +81,13 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
|
|||
// groupBy options
|
||||
QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget;
|
||||
groupBySelectorProxy->setWidget(&groupBySelector);
|
||||
groupBySelectorProxy->setZValue(2000000008);
|
||||
groupBySelectorProxy->setZValue(ZValues::TOP_UI);
|
||||
hTopRow->addItem(groupBySelectorProxy);
|
||||
|
||||
// sortBy options
|
||||
QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget;
|
||||
sortBySelectorProxy->setWidget(&sortBySelector);
|
||||
sortBySelectorProxy->setZValue(2000000007);
|
||||
sortBySelectorProxy->setZValue(ZValues::DRAG_ITEM);
|
||||
hTopRow->addItem(sortBySelectorProxy);
|
||||
|
||||
vbox->addItem(hTopRow);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue