mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-25 11:56:11 -07:00
Compare commits
2 commits
ef3929356b
...
48776cfeba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48776cfeba | ||
|
|
ce2c31424c |
10 changed files with 144 additions and 22 deletions
26
cockatrice/src/game_graphics/animated_item.h
Normal file
26
cockatrice/src/game_graphics/animated_item.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef ANIMATED_ITEM_H
|
||||
#define ANIMATED_ITEM_H
|
||||
|
||||
/**
|
||||
* @file animated_item.h
|
||||
* @ingroup GameGraphics
|
||||
* @brief Interface for scene items driven by GameScene's shared animation timer.
|
||||
*
|
||||
* Items that want per-tick animation while a single QBasicTimer runs (instead of
|
||||
* owning their own QTimer) implement this interface and register with the scene
|
||||
* via GameScene::registerAnimationItem.
|
||||
*/
|
||||
|
||||
class IAnimatedItem
|
||||
{
|
||||
public:
|
||||
virtual ~IAnimatedItem() = default;
|
||||
|
||||
/**
|
||||
* @brief Advances the item's animation by one timer tick.
|
||||
* @return true while the animation is still running, false once it has finished.
|
||||
*/
|
||||
virtual bool animationEvent() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -305,6 +305,11 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
|
|||
}
|
||||
}
|
||||
|
||||
bool AbstractCardItem::animationEvent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AbstractCardItem::setFaceDown(bool _facedown)
|
||||
{
|
||||
facedown = _facedown;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef ABSTRACTCARDITEM_H
|
||||
#define ABSTRACTCARDITEM_H
|
||||
|
||||
#include "../animated_item.h"
|
||||
#include "../card_dimensions.h"
|
||||
#include "arrow_target.h"
|
||||
#include "graphics_item_type.h"
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
|
||||
class PlayerLogic;
|
||||
|
||||
class AbstractCardItem : public ArrowTarget
|
||||
class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
|
|
@ -126,6 +127,9 @@ public:
|
|||
emit deleteCardInfoPopup(cardRef.name);
|
||||
}
|
||||
|
||||
/** @brief Default: no per-tick animation. Subclasses override to animate. */
|
||||
bool animationEvent() override;
|
||||
|
||||
protected:
|
||||
void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle);
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ public:
|
|||
void resetState(bool keepAnnotations = false);
|
||||
void processCardInfo(const ServerInfo_Card &_info);
|
||||
|
||||
bool animationEvent();
|
||||
bool animationEvent() override;
|
||||
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown);
|
||||
void deleteDragItem();
|
||||
void drawArrow(const QColor &arrowColor);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsView>
|
||||
#include <QSet>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
|
@ -45,7 +44,14 @@ GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent)
|
|||
|
||||
GameScene::~GameScene()
|
||||
{
|
||||
// Sever all incoming connections (animated item destroy-tracking) before the
|
||||
// members below are destroyed: the base QGraphicsScene destructor destroys the
|
||||
// remaining items, and their destroyed() signals must not reach slots that
|
||||
// reference members that no longer exist.
|
||||
disconnect(this);
|
||||
|
||||
delete animationTimer;
|
||||
animationTimer = nullptr;
|
||||
|
||||
// Delete all ArrowItems before QGraphicsScene's base destructor runs.
|
||||
// QGraphicsScene::~QGraphicsScene() destroys items in arbitrary order.
|
||||
|
|
@ -736,30 +742,45 @@ bool GameScene::event(QEvent *event)
|
|||
|
||||
void GameScene::timerEvent(QTimerEvent * /*event*/)
|
||||
{
|
||||
QMutableSetIterator<CardItem *> i(cardsToAnimate);
|
||||
QMutableHashIterator<QObject *, IAnimatedItem *> i(animatedItems);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (!i.value()->animationEvent()) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
if (cardsToAnimate.isEmpty()) {
|
||||
if (animatedItems.isEmpty()) {
|
||||
animationTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::registerAnimationItem(AbstractCardItem *card)
|
||||
void GameScene::registerAnimationItem(IAnimatedItem *item)
|
||||
{
|
||||
cardsToAnimate.insert(static_cast<CardItem *>(card));
|
||||
if (!animationTimer->isActive()) {
|
||||
auto *object = dynamic_cast<QObject *>(item);
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
if (!animatedItems.contains(object)) {
|
||||
connect(object, &QObject::destroyed, this, &GameScene::removeAnimatedItem);
|
||||
}
|
||||
animatedItems.insert(object, item);
|
||||
if (animationTimer && !animationTimer->isActive()) {
|
||||
animationTimer->start(10, this);
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::unregisterAnimationItem(AbstractCardItem *card)
|
||||
void GameScene::unregisterAnimationItem(IAnimatedItem *item)
|
||||
{
|
||||
cardsToAnimate.remove(static_cast<CardItem *>(card));
|
||||
if (cardsToAnimate.isEmpty()) {
|
||||
animatedItems.remove(dynamic_cast<QObject *>(item));
|
||||
if (animationTimer && animatedItems.isEmpty()) {
|
||||
animationTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::removeAnimatedItem(QObject *item)
|
||||
{
|
||||
animatedItems.remove(item);
|
||||
if (animationTimer && animatedItems.isEmpty()) {
|
||||
animationTimer->stop();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
#include "../game/arrow_registry.h"
|
||||
#include "../game/board/arrow_data.h"
|
||||
#include "../game/zones/card_zone_logic.h"
|
||||
#include "animated_item.h"
|
||||
#include "board/arrow_item.h"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(GameSceneLog, "game_scene");
|
||||
inline Q_LOGGING_CATEGORY(GameScenePlayerAdditionRemovalLog, "game_scene.player_addition_removal");
|
||||
|
|
@ -24,6 +25,7 @@ class CardItem;
|
|||
class ServerInfo_Card;
|
||||
class PhasesToolbar;
|
||||
class QBasicTimer;
|
||||
class QObject;
|
||||
|
||||
/**
|
||||
* @class GameScene
|
||||
|
|
@ -50,8 +52,8 @@ private:
|
|||
QList<ZoneViewWidget *> zoneViews; ///< Active zone view widgets
|
||||
QSize viewSize; ///< Current view size
|
||||
QPointer<CardItem> hoveredCard; ///< Currently hovered card
|
||||
QBasicTimer *animationTimer; ///< Timer for card animations
|
||||
QSet<CardItem *> cardsToAnimate; ///< Cards currently animating
|
||||
QBasicTimer *animationTimer; ///< Timer for scene animations
|
||||
QHash<QObject *, IAnimatedItem *> animatedItems; ///< Items currently animating
|
||||
int playerRotation; ///< Rotation offset for player layout
|
||||
|
||||
/**
|
||||
|
|
@ -182,15 +184,24 @@ public:
|
|||
/** @brief Updates hovered card highlighting. */
|
||||
void updateHoveredCard(CardItem *newCard);
|
||||
|
||||
/** @brief Registers a card for animation updates. */
|
||||
void registerAnimationItem(AbstractCardItem *card);
|
||||
/**
|
||||
* @brief Registers an item for animation updates with the shared scene timer.
|
||||
*
|
||||
* The item must inherit QObject; it is unregistered automatically when it is
|
||||
* destroyed, so it may be deleted mid-animation without a dangling pointer.
|
||||
*/
|
||||
void registerAnimationItem(IAnimatedItem *item);
|
||||
|
||||
/** @brief Unregisters a card from animation updates. */
|
||||
void unregisterAnimationItem(AbstractCardItem *card);
|
||||
/** @brief Unregisters an item from animation updates. */
|
||||
void unregisterAnimationItem(IAnimatedItem *item);
|
||||
void startRubberBand(const QPointF &selectionOrigin);
|
||||
void resizeRubberBand(const QPointF &cursorPoint, int selectedCount);
|
||||
void stopRubberBand();
|
||||
|
||||
private slots:
|
||||
/** @brief Removes a destroyed item from the animation set. */
|
||||
void removeAnimatedItem(QObject *item);
|
||||
|
||||
public slots:
|
||||
void onCardSelectionChanged(AbstractCardItem *card, bool selected);
|
||||
void onCardRightClicked(AbstractCardItem *card, QPoint screenPos);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QTimer>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/server/remote/user_level.h>
|
||||
#include <libcockatrice/settings/chat_settings.h>
|
||||
|
|
@ -51,6 +52,9 @@ ChatView::ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _sho
|
|||
setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||
setOpenLinks(false);
|
||||
connect(this, &ChatView::anchorClicked, this, &ChatView::openLink);
|
||||
|
||||
connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &ChatView::onScrollBarRangeChanged);
|
||||
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &ChatView::onScrollBarValueChanged);
|
||||
}
|
||||
|
||||
void ChatView::adjustColorsToPalette()
|
||||
|
|
@ -151,7 +155,7 @@ void ChatView::appendHtml(const QString &html)
|
|||
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
|
||||
prepareBlock().insertHtml(html);
|
||||
if (atBottom) {
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +173,7 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold,
|
|||
|
||||
prepareBlock().insertHtml(htmlText);
|
||||
if (atBottom) {
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -338,11 +342,36 @@ void ChatView::appendMessage(QString message,
|
|||
}
|
||||
}
|
||||
|
||||
if (atBottom) {
|
||||
// ChatHistory messages are only ever sent once per room, right after joining, before the user can
|
||||
// interact with the view. Always scroll to the bottom so the whole history is visible on join.
|
||||
if (atBottom || messageType.testFlag(Event_RoomSay::ChatHistory)) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatView::scrollToBottom()
|
||||
{
|
||||
// The document layout, and therefore the scrollbar range, may be updated asynchronously (e.g. while
|
||||
// the chat history is loaded into a view that has not been laid out yet). Setting the value once is
|
||||
// not enough: keep stickToBottom set so any later range change scrolls to the new maximum as well.
|
||||
stickToBottom = true;
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void ChatView::onScrollBarRangeChanged()
|
||||
{
|
||||
if (stickToBottom) {
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
}
|
||||
|
||||
void ChatView::onScrollBarValueChanged(int value)
|
||||
{
|
||||
if (value < verticalScrollBar()->maximum()) {
|
||||
stickToBottom = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatView::checkTag(QTextCursor &cursor, QString &message)
|
||||
{
|
||||
if (message.startsWith("[card]")) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ private:
|
|||
QStringList highlightedWords;
|
||||
bool evenNumber;
|
||||
bool showTimestamps;
|
||||
bool stickToBottom = false;
|
||||
HoveredItemType hoveredItemType;
|
||||
QString hoveredContent;
|
||||
QAction *messageClicked;
|
||||
|
|
@ -67,6 +68,7 @@ private:
|
|||
|
||||
[[nodiscard]] QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
|
||||
QTextCursor prepareBlock(bool same = false);
|
||||
void scrollToBottom();
|
||||
void appendCardTag(QTextCursor &cursor, const QString &cardName);
|
||||
void appendUrlTag(QTextCursor &cursor, QString url);
|
||||
static QColor getCustomMentionColor();
|
||||
|
|
@ -88,6 +90,8 @@ private slots:
|
|||
void actMessageClicked();
|
||||
void adjustColorsToPalette();
|
||||
void refreshBlockColors();
|
||||
void onScrollBarRangeChanged();
|
||||
void onScrollBarValueChanged(int value);
|
||||
|
||||
public:
|
||||
ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr);
|
||||
|
|
|
|||
|
|
@ -116,8 +116,13 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
|
|||
connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().cardsDisplay(),
|
||||
&CardsDisplaySettings::setTapAnimation);
|
||||
|
||||
connect(&enableAllAnimationsButton, &QPushButton::clicked, this, &UserInterfaceSettingsPage::enableAllAnimations);
|
||||
connect(&disableAllAnimationsButton, &QPushButton::clicked, this, &UserInterfaceSettingsPage::disableAllAnimations);
|
||||
|
||||
auto *animationGrid = new QGridLayout;
|
||||
animationGrid->addWidget(&tapAnimationCheckBox, 0, 0);
|
||||
animationGrid->addWidget(&enableAllAnimationsButton, 0, 0);
|
||||
animationGrid->addWidget(&disableAllAnimationsButton, 0, 1);
|
||||
animationGrid->addWidget(&tapAnimationCheckBox, 1, 0);
|
||||
|
||||
animationGroupBox = new QGroupBox;
|
||||
animationGroupBox->setLayout(animationGrid);
|
||||
|
|
@ -268,6 +273,16 @@ void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i)
|
|||
}
|
||||
}
|
||||
|
||||
void UserInterfaceSettingsPage::enableAllAnimations()
|
||||
{
|
||||
tapAnimationCheckBox.setChecked(true);
|
||||
}
|
||||
|
||||
void UserInterfaceSettingsPage::disableAllAnimations()
|
||||
{
|
||||
tapAnimationCheckBox.setChecked(false);
|
||||
}
|
||||
|
||||
void UserInterfaceSettingsPage::updateCommanderSpellbookUiState()
|
||||
{
|
||||
const int mode = SettingsCache::instance().deckEditor().getCommanderSpellbookIntegrationEnabled();
|
||||
|
|
@ -310,6 +325,8 @@ void UserInterfaceSettingsPage::retranslateUi()
|
|||
specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating"));
|
||||
buddyConnectNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar when users in your buddy list connect"));
|
||||
animationGroupBox->setTitle(tr("Animation settings"));
|
||||
enableAllAnimationsButton.setText(tr("&Enable all animations"));
|
||||
disableAllAnimationsButton.setText(tr("&Disable all animations"));
|
||||
tapAnimationCheckBox.setText(tr("&Tap/untap animation"));
|
||||
deckEditorGroupBox->setTitle(tr("Deck editor/storage settings"));
|
||||
openDeckInNewTabCheckBox.setText(tr("Open deck in new tab by default"));
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
|
|
@ -17,6 +18,8 @@ class UserInterfaceSettingsPage : public AbstractSettingsPage
|
|||
Q_OBJECT
|
||||
private slots:
|
||||
void setNotificationEnabled(QT_STATE_CHANGED_T);
|
||||
void enableAllAnimations();
|
||||
void disableAllAnimations();
|
||||
void updateCommanderSpellbookUiState();
|
||||
|
||||
private:
|
||||
|
|
@ -34,6 +37,8 @@ private:
|
|||
QCheckBox showTotalSelectionCountCheckBox;
|
||||
QCheckBox useTearOffMenusCheckBox;
|
||||
QCheckBox keepGameChatFocusCheckBox;
|
||||
QPushButton enableAllAnimationsButton;
|
||||
QPushButton disableAllAnimationsButton;
|
||||
QCheckBox tapAnimationCheckBox;
|
||||
QCheckBox openDeckInNewTabCheckBox;
|
||||
QLabel visualDeckStoragePromptForConversionLabel;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue