[Game][Counters] Split counters into AbstractCounter (graphics) and CounterState (logic) (#6917)

* [Counters] Split counters into graphics and logic states

Took 22 minutes

* Don't have widget hold pointer to state -> Copy what we need and subscribe to changes.

Took 12 minutes

Took 5 seconds

* Sync value too.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-05-21 20:16:28 +02:00 committed by GitHub
parent 0549892092
commit bddf9bd818
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 273 additions and 199 deletions

View file

@ -9,6 +9,7 @@
#include <QAction>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QKeyEvent>
#include <QMenu>
#include <QString>
@ -16,24 +17,24 @@
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
#include <libcockatrice/utility/expression.h>
AbstractCounter::AbstractCounter(PlayerLogic *_player,
int _id,
const QString &_name,
AbstractCounter::AbstractCounter(CounterState *state,
PlayerLogic *_player,
bool _shownInCounterArea,
int _value,
bool _useNameForShortcut,
QGraphicsItem *parent)
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value),
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false),
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea)
: QGraphicsItem(parent), player(_player), id(state->getId()), name(state->getName()), value(state->getValue()),
color(state->getColor()), radius(state->getRadius()), useNameForShortcut(_useNameForShortcut),
shownInCounterArea(_shownInCounterArea)
{
setAcceptHoverEvents(true);
shortcutActive = false;
connect(state, &CounterState::valueChanged, this, [this](int, int newValue) {
value = newValue;
update();
});
if (player->getPlayerInfo()->getLocalOrJudge()) {
QString displayName = TranslateCounterName::getDisplayName(_name);
menu = new TearOffMenu(displayName);
menu = new TearOffMenu(TranslateCounterName::getDisplayName(state->getName()));
aSet = new QAction(this);
connect(aSet, &QAction::triggered, this, &AbstractCounter::setCounter);
menu->addAction(aSet);
@ -41,17 +42,18 @@ AbstractCounter::AbstractCounter(PlayerLogic *_player,
for (int i = 10; i >= -10; --i) {
if (i == 0) {
menu->addSeparator();
} else {
QAction *aIncrement = new QAction(QString(i < 0 ? "%1" : "+%1").arg(i), this);
if (i == -1) {
aDec = aIncrement;
} else if (i == 1) {
aInc = aIncrement;
}
aIncrement->setData(i);
connect(aIncrement, &QAction::triggered, this, &AbstractCounter::incrementCounter);
menu->addAction(aIncrement);
continue;
}
auto *a = new QAction(QString(i < 0 ? "%1" : "+%1").arg(i), this);
if (i == -1) {
aDec = a;
}
if (i == 1) {
aInc = a;
}
a->setData(i);
connect(a, &QAction::triggered, this, &AbstractCounter::incrementCounter);
menu->addAction(a);
}
} else {
menu = nullptr;
@ -79,31 +81,26 @@ void AbstractCounter::delCounter()
void AbstractCounter::retranslateUi()
{
if (menu) {
if (aSet) {
aSet->setText(tr("&Set counter..."));
}
}
void AbstractCounter::setShortcutsActive()
{
if (!menu) {
if (!menu || !player->getPlayerInfo()->getLocal()) {
return;
}
if (!player->getPlayerInfo()->getLocal()) {
return;
}
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
ShortcutsSettings &sc = SettingsCache::instance().shortcuts();
shortcutActive = true;
if (name == "life") {
shortcutActive = true;
aSet->setShortcuts(shortcuts.getShortcut("Player/aSet"));
aDec->setShortcuts(shortcuts.getShortcut("Player/aDec"));
aInc->setShortcuts(shortcuts.getShortcut("Player/aInc"));
aSet->setShortcuts(sc.getShortcut("Player/aSet"));
aDec->setShortcuts(sc.getShortcut("Player/aDec"));
aInc->setShortcuts(sc.getShortcut("Player/aInc"));
} else if (useNameForShortcut) {
shortcutActive = true;
aSet->setShortcuts(shortcuts.getShortcut("Player/aSetCounter_" + name));
aDec->setShortcuts(shortcuts.getShortcut("Player/aDecCounter_" + name));
aInc->setShortcuts(shortcuts.getShortcut("Player/aIncCounter_" + name));
aSet->setShortcuts(sc.getShortcut("Player/aSetCounter_" + name));
aDec->setShortcuts(sc.getShortcut("Player/aDecCounter_" + name));
aInc->setShortcuts(sc.getShortcut("Player/aIncCounter_" + name));
}
}
@ -128,45 +125,32 @@ void AbstractCounter::refreshShortcuts()
}
}
void AbstractCounter::setValue(int _value)
{
value = _value;
update();
}
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (isUnderMouse() && player->getPlayerInfo()->getLocalOrJudge()) {
if (event->button() == Qt::MiddleButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
if (menu) {
menu->exec(event->screenPos());
}
event->accept();
} else if (event->button() == Qt::LeftButton) {
Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(1);
player->getPlayerActions()->sendGameCommand(cmd);
event->accept();
} else if (event->button() == Qt::RightButton) {
Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(-1);
player->getPlayerActions()->sendGameCommand(cmd);
event->accept();
if (!isUnderMouse() || !player->getPlayerInfo()->getLocalOrJudge()) {
event->ignore();
return;
}
if (event->button() == Qt::MiddleButton || QApplication::keyboardModifiers() & Qt::ShiftModifier) {
if (menu) {
menu->exec(event->screenPos());
}
} else {
event->ignore();
Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
player->getPlayerActions()->sendGameCommand(cmd);
}
event->accept();
}
void AbstractCounter::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
void AbstractCounter::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
hovered = true;
update();
}
void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
{
hovered = false;
update();
@ -174,35 +158,36 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
void AbstractCounter::incrementCounter()
{
const int delta = static_cast<QAction *>(sender())->data().toInt();
Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(delta);
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
player->getPlayerActions()->sendGameCommand(cmd);
}
void AbstractCounter::setCounter()
{
QWidget *parent = nullptr;
if (auto *view = scene() ? scene()->views().value(0) : nullptr) {
parent = view->window();
}
dialogSemaphore = true;
AbstractCounterDialog dialog(name, QString::number(value), player->getGame()->getTab());
const int ok = dialog.exec();
AbstractCounterDialog dlg(name, QString::number(value), parent);
const int ok = dlg.exec();
dialogSemaphore = false;
if (deleteAfterDialog) {
deleteLater();
return;
}
dialogSemaphore = false;
if (!ok) {
return;
}
Expression exp(value);
int newValue = static_cast<int>(exp.parse(dialog.textValue()));
Command_SetCounter cmd;
cmd.set_counter_id(id);
cmd.set_value(newValue);
cmd.set_value(static_cast<int>(exp.parse(dlg.textValue())));
player->getPlayerActions()->sendGameCommand(cmd);
}

View file

@ -9,6 +9,7 @@
#include "../../interface/widgets/menus/tearoff_menu.h"
#include "../player/menu/abstract_player_component.h"
#include "counter_state.h"
#include <QGraphicsItem>
#include <QInputDialog>
@ -29,18 +30,22 @@ protected:
int id;
QString name;
int value;
bool useNameForShortcut, hovered;
QColor color;
int radius;
bool hovered = false;
bool useNameForShortcut;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
private:
QAction *aSet, *aDec, *aInc;
TearOffMenu *menu;
bool dialogSemaphore, deleteAfterDialog;
QAction *aSet = nullptr, *aDec = nullptr, *aInc = nullptr;
TearOffMenu *menu = nullptr;
bool dialogSemaphore = false;
bool deleteAfterDialog = false;
bool shownInCounterArea;
bool shortcutActive;
bool shortcutActive = false;
private slots:
void refreshShortcuts();
@ -48,17 +53,14 @@ private slots:
void setCounter();
public:
AbstractCounter(PlayerLogic *_player,
int _id,
const QString &_name,
bool _shownInCounterArea,
int _value,
bool _useNameForShortcut = false,
AbstractCounter(CounterState *state,
PlayerLogic *player,
bool shownInCounterArea,
bool useNameForShortcut = false,
QGraphicsItem *parent = nullptr);
~AbstractCounter() override;
void retranslateUi() override;
void setValue(int _value);
void setShortcutsActive() override;
void setShortcutsInactive() override;
void delCounter();
@ -67,7 +69,6 @@ public:
{
return menu;
}
int getId() const
{
return id;
@ -76,14 +77,22 @@ public:
{
return name;
}
bool getShownInCounterArea() const
QColor getColor() const
{
return shownInCounterArea;
return color;
}
int getRadius() const
{
return radius;
}
int getValue() const
{
return value;
}
bool getShownInCounterArea() const
{
return shownInCounterArea;
}
};
class AbstractCounterDialog : public QInputDialog

View file

@ -5,15 +5,8 @@
#include <QPainter>
GeneralCounter::GeneralCounter(PlayerLogic *_player,
int _id,
const QString &_name,
const QColor &_color,
int _radius,
int _value,
bool useNameForShortcut,
QGraphicsItem *parent)
: AbstractCounter(_player, _id, _name, true, _value, useNameForShortcut, parent), color(_color), radius(_radius)
GeneralCounter::GeneralCounter(CounterState *state, PlayerLogic *player, bool useNameForShortcut, QGraphicsItem *parent)
: AbstractCounter(state, player, true, useNameForShortcut, parent)
{
setCacheMode(DeviceCoordinateCache);
}

View file

@ -12,17 +12,10 @@
class GeneralCounter : public AbstractCounter
{
Q_OBJECT
private:
QColor color;
int radius;
public:
GeneralCounter(PlayerLogic *_player,
int _id,
const QString &_name,
const QColor &_color,
int _radius,
int _value,
GeneralCounter(CounterState *state,
PlayerLogic *player,
bool useNameForShortcut = false,
QGraphicsItem *parent = nullptr);
QRectF boundingRect() const override;

View file

@ -0,0 +1,24 @@
#include "counter_state.h"
#include <libcockatrice/utility/color.h>
CounterState::CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent)
: QObject(parent), id(id), name(name), color(color), radius(radius), value(value)
{
}
CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject *parent)
{
return new CounterState(counter.id(), QString::fromStdString(counter.name()),
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
}
void CounterState::setValue(int newValue)
{
if (newValue == value) {
return;
}
int old = value;
value = newValue;
emit valueChanged(old, newValue);
}

View file

@ -0,0 +1,51 @@
#ifndef COCKATRICE_COUNTER_STATE_H
#define COCKATRICE_COUNTER_STATE_H
#include <QColor>
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_counter.pb.h>
class CounterState : public QObject
{
Q_OBJECT
public:
CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent = nullptr);
static CounterState *fromProto(const ServerInfo_Counter &counter, QObject *parent = nullptr);
int getId() const
{
return id;
}
QString getName() const
{
return name;
}
QColor getColor() const
{
return color;
}
int getRadius() const
{
return radius;
}
int getValue() const
{
return value;
}
void setValue(int newValue);
signals:
void valueChanged(int oldValue, int newValue);
private:
int id;
QString name;
QColor color;
int radius;
int value;
};
#endif // COCKATRICE_COUNTER_STATE_H