mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 09:05:10 -07:00
Compare commits
25 commits
5d65adfc9c
...
6150eb43e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6150eb43e8 | ||
|
|
6d7dadf657 | ||
|
|
5d025ca0bd | ||
|
|
a85203e457 | ||
|
|
e9bf1e6e46 | ||
|
|
202a5ac958 | ||
|
|
d5d99e4dfb | ||
|
|
7d867b9745 | ||
|
|
69e8f80fa1 | ||
|
|
df3defa890 | ||
|
|
030e4f5871 | ||
|
|
6e5c58069b | ||
|
|
ebeae48652 | ||
|
|
36f998e466 | ||
|
|
760fe88fa3 | ||
|
|
fff506dbbe | ||
|
|
8e0bdafb14 | ||
|
|
9b0d62c152 | ||
|
|
6cdeb0c428 | ||
|
|
745e94f332 | ||
|
|
b01e107908 | ||
|
|
8d30ac54f0 | ||
|
|
fb482f037e | ||
|
|
0f0e46a177 | ||
|
|
2fe59d6326 |
149 changed files with 2962 additions and 423 deletions
4
.github/workflows/desktop-build.yml
vendored
4
.github/workflows/desktop-build.yml
vendored
|
|
@ -152,7 +152,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CACHE: ${{ github.workspace }}/.cache/${{ matrix.distro }}${{ matrix.version }} # directory for caching docker image and ccache
|
CACHE: ${{ github.workspace }}/.cache/${{ matrix.distro }}${{ matrix.version }} # directory for caching docker image and ccache
|
||||||
CCACHE_EVICTION_AGE: 7d
|
CCACHE_EVICTION_AGE: 7d
|
||||||
CCACHE_SIZE: 550M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
CCACHE_SIZE: 600M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||||
CMAKE_GENERATOR: 'Ninja'
|
CMAKE_GENERATOR: 'Ninja'
|
||||||
NAME: ${{ matrix.distro }}${{ matrix.version }}
|
NAME: ${{ matrix.distro }}${{ matrix.version }}
|
||||||
|
|
||||||
|
|
@ -342,7 +342,7 @@ jobs:
|
||||||
timeout-minutes: 100
|
timeout-minutes: 100
|
||||||
env:
|
env:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.cache/
|
CCACHE_DIR: ${{ github.workspace }}/.cache/
|
||||||
CCACHE_SIZE: 550M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
CCACHE_SIZE: 600M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -149,15 +149,15 @@ You can then
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
The following flags (with their non-default values) can be passed to `cmake`:
|
The following flags (with their non-default values) can be passed to `cmake`:
|
||||||
| Flag | Description |
|
| Flag | Description |
|
||||||
| --- | --- |
|
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `-DWITH_SERVER=1` | Build <kbd>Servatrice</kbd> server |
|
| `-DWITH_SERVER=1` | Build <kbd>Servatrice</kbd> server |
|
||||||
| `-DWITH_CLIENT=0` | Don't build <kbd>Cockatrice</kbd> client |
|
| `-DWITH_CLIENT=0` | Don't build <kbd>Cockatrice</kbd> client |
|
||||||
| `-DWITH_ORACLE=0` | Don't build <kbd>Oracle</kbd> card database tool |
|
| `-DWITH_ORACLE=0` | Don't build <kbd>Oracle</kbd> card database tool |
|
||||||
| `-DCMAKE_BUILD_TYPE=Debug` | Compile in debug mode<br> Enables extra logging output, debug symbols, and much more verbose compiler warnings |
|
| `-DCMAKE_BUILD_TYPE=Debug` | Compile in debug mode<br> Enables extra logging output, debug symbols, and much more verbose compiler warnings |
|
||||||
| `-DWARNING_AS_ERROR=0` | Don't treat compilation warnings as errors in debug mode |
|
| `-DWARNING_AS_ERROR=0` | Don't treat compilation warnings as errors in debug mode |
|
||||||
| `-DUPDATE_TRANSLATIONS=1` | Configure `make` to update the translation .ts files for new strings in the source code<br> **Note:** `make clean` will remove the .ts files |
|
| `-DUPDATE_TRANSLATIONS=1` | Configure `make` to update the translation .ts files for new strings in the source code<br> **Note:** `make clean` will remove the .ts files |
|
||||||
| `-DTEST=1` | Enable regression tests<br> **Note:** `make test` to run tests, *googletest* will be downloaded if not available |
|
| `-DTEST=1` | Enable regression tests<br> **Note:** `make test` to run tests, *googletest* will be downloaded if not available |
|
||||||
|
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ set(QT_COMPONENTS_COCKATRICE
|
||||||
QuickWidgets
|
QuickWidgets
|
||||||
)
|
)
|
||||||
|
|
||||||
set(QT_COMPONENTS_ORACLE Concurrent Network Svg Widgets)
|
set(QT_COMPONENTS_ORACLE Concurrent Network Svg Widgets Xml)
|
||||||
|
|
||||||
set(QT_COMPONENTS_SERVATRICE Network Sql WebSockets)
|
set(QT_COMPONENTS_SERVATRICE Network Sql WebSockets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/cards/additional_info/mana_cost_widget.cpp
|
src/interface/widgets/cards/additional_info/mana_cost_widget.cpp
|
||||||
src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp
|
src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp
|
||||||
src/interface/widgets/cards/art_crop_attribution.cpp
|
src/interface/widgets/cards/art_crop_attribution.cpp
|
||||||
|
src/interface/widgets/cards/card_art_utils.cpp
|
||||||
src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
|
src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
|
||||||
src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
|
src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
|
||||||
src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
|
src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
|
||||||
|
|
@ -381,6 +382,7 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/tabs/tab_card_art_rules.cpp
|
src/interface/widgets/tabs/tab_card_art_rules.cpp
|
||||||
src/interface/widgets/tabs/tab_deck_editor.cpp
|
src/interface/widgets/tabs/tab_deck_editor.cpp
|
||||||
src/interface/widgets/tabs/tab_deck_storage.cpp
|
src/interface/widgets/tabs/tab_deck_storage.cpp
|
||||||
|
src/interface/widgets/tabs/tab_developer.cpp
|
||||||
src/interface/widgets/tabs/tab_game.cpp
|
src/interface/widgets/tabs/tab_game.cpp
|
||||||
src/interface/widgets/tabs/tab_home.cpp
|
src/interface/widgets/tabs/tab_home.cpp
|
||||||
src/interface/widgets/tabs/tab_logs.cpp
|
src/interface/widgets/tabs/tab_logs.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "remote_connection_controller.h"
|
#include "remote_connection_controller.h"
|
||||||
|
|
||||||
|
#include "../../../interface/pixel_map_generator.h"
|
||||||
#include "../../settings/cache_settings.h"
|
#include "../../settings/cache_settings.h"
|
||||||
#include "../interface/widgets/dialogs/dlg_connect.h"
|
#include "../interface/widgets/dialogs/dlg_connect.h"
|
||||||
#include "../interface/widgets/dialogs/dlg_forgot_password_challenge.h"
|
#include "../interface/widgets/dialogs/dlg_forgot_password_challenge.h"
|
||||||
|
|
@ -180,7 +181,7 @@ void ConnectionController::onServerShutdownEvent(const Event_ServerShutdown &eve
|
||||||
"games will be lost.\nReason for shutdown: %1",
|
"games will be lost.\nReason for shutdown: %1",
|
||||||
"", event.minutes())
|
"", event.minutes())
|
||||||
.arg(QString::fromStdString(event.reason())));
|
.arg(QString::fromStdString(event.reason())));
|
||||||
serverShutdownMessageBox.setIconPixmap(QPixmap("theme:cockatrice").scaled(64, 64));
|
serverShutdownMessageBox.setIconPixmap(themePixmap(QStringLiteral("cockatrice")).scaled(64, 64));
|
||||||
serverShutdownMessageBox.setText(tr("Scheduled server shutdown"));
|
serverShutdownMessageBox.setText(tr("Scheduled server shutdown"));
|
||||||
serverShutdownMessageBox.setWindowModality(Qt::ApplicationModal);
|
serverShutdownMessageBox.setWindowModality(Qt::ApplicationModal);
|
||||||
serverShutdownMessageBox.setVisible(true);
|
serverShutdownMessageBox.setVisible(true);
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ QStringMap &SoundEngine::getAvailableThemes()
|
||||||
QDir dir;
|
QDir dir;
|
||||||
availableThemes.clear();
|
availableThemes.clear();
|
||||||
|
|
||||||
// load themes from user profile dir
|
// Load themes from user profile dir
|
||||||
|
|
||||||
dir.setPath(SettingsCache::instance().getDataPath() + "/sounds");
|
dir.setPath(SettingsCache::instance().getDataPath() + "/sounds");
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ QStringMap &SoundEngine::getAvailableThemes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load themes from cockatrice system dir
|
// Load themes from Cockatrice system dir
|
||||||
dir.setPath(qApp->applicationDirPath() +
|
dir.setPath(qApp->applicationDirPath() +
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
"/../Resources/sounds"
|
"/../Resources/sounds"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "filter_builder.h"
|
#include "filter_builder.h"
|
||||||
|
|
||||||
|
#include "../interface/pixel_map_generator.h"
|
||||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
@ -21,7 +22,7 @@ FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
|
||||||
typeCombo->addItem(CardFilter::typeName(static_cast<CardFilter::Type>(i)), QVariant(i));
|
typeCombo->addItem(CardFilter::typeName(static_cast<CardFilter::Type>(i)), QVariant(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton *ok = new QPushButton(QPixmap("theme:icons/increment"), QString());
|
QPushButton *ok = new QPushButton(themePixmap(QStringLiteral("icons/increment")), QString());
|
||||||
ok->setObjectName("ok");
|
ok->setObjectName("ok");
|
||||||
ok->setMaximumSize(20, 20);
|
ok->setMaximumSize(20, 20);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject
|
||||||
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
|
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CounterState::setValue(int newValue)
|
void CounterState::setValue(int newValue, bool skipDamageAnimation)
|
||||||
{
|
{
|
||||||
if (newValue == value) {
|
if (newValue == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int old = value;
|
int old = value;
|
||||||
value = newValue;
|
value = newValue;
|
||||||
emit valueChanged(old, newValue);
|
emit valueChanged(old, newValue, skipDamageAnimation);
|
||||||
}
|
}
|
||||||
|
|
@ -35,10 +35,23 @@ public:
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValue(int newValue);
|
/**
|
||||||
|
* @brief Set the counter value.
|
||||||
|
* @param newValue The new value.
|
||||||
|
* @param skipDamageAnimation When true, valueChanged is emitted with skipDamageAnimation=true, letting views
|
||||||
|
* suppress damage-related feedback (e.g. battlefield shimmer, life counter flash) for values set during replay
|
||||||
|
* rewinds.
|
||||||
|
*/
|
||||||
|
void setValue(int newValue, bool skipDamageAnimation = false);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(int oldValue, int newValue);
|
/**
|
||||||
|
* @brief Emitted whenever the value changes.
|
||||||
|
* @param oldValue The previous value.
|
||||||
|
* @param newValue The new value.
|
||||||
|
* @param skipDamageAnimation True when the change should not trigger damage/life-change feedback in views.
|
||||||
|
*/
|
||||||
|
void valueChanged(int oldValue, int newValue, bool skipDamageAnimation);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int id;
|
int id;
|
||||||
|
|
|
||||||
|
|
@ -430,12 +430,13 @@ void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/,
|
||||||
QString playerName = QString::fromStdString(playerInfo.user_info().name());
|
QString playerName = QString::fromStdString(playerInfo.user_info().name());
|
||||||
emit addPlayerToAutoCompleteList(playerName);
|
emit addPlayerToAutoCompleteList(playerName);
|
||||||
|
|
||||||
if (game->getPlayerManager()->getPlayers().contains(playerId)) {
|
PlayerManager *playerManager = game->getPlayerManager();
|
||||||
|
if (playerManager->getPlayers().contains(playerId) || playerManager->getSpectators().contains(playerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerInfo.spectator()) {
|
if (playerInfo.spectator()) {
|
||||||
game->getPlayerManager()->addSpectator(playerId, playerInfo);
|
playerManager->addSpectator(playerId, playerInfo);
|
||||||
emit logJoinSpectator(playerName);
|
emit logJoinSpectator(playerName);
|
||||||
emit spectatorJoined(playerInfo);
|
emit spectatorJoined(playerInfo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
enum EventProcessingOption
|
enum EventProcessingOption
|
||||||
{
|
{
|
||||||
SKIP_REVEAL_WINDOW = 0x0001,
|
SKIP_REVEAL_WINDOW = 0x0001,
|
||||||
SKIP_TAP_ANIMATION = 0x0002
|
SKIP_TAP_ANIMATION = 0x0002,
|
||||||
|
SKIP_DAMAGE_ANIMATION = 0x0004
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrap it in a QFlags typedef
|
// Wrap it in a QFlags typedef
|
||||||
|
|
|
||||||
|
|
@ -262,14 +262,15 @@ void PlayerEventHandler::eventCreateCounter(const Event_CreateCounter &event)
|
||||||
player->addCounter(event.counter_info());
|
player->addCounter(event.counter_info());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event)
|
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event, EventProcessingOptions options)
|
||||||
{
|
{
|
||||||
CounterState *ctr = player->getCounters().value(event.counter_id(), nullptr);
|
CounterState *ctr = player->getCounters().value(event.counter_id(), nullptr);
|
||||||
if (!ctr) {
|
if (!ctr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldValue = ctr->getValue();
|
int oldValue = ctr->getValue();
|
||||||
ctr->setValue(event.value());
|
const bool skipDamageAnimation = options.testFlag(SKIP_DAMAGE_ANIMATION);
|
||||||
|
ctr->setValue(event.value(), skipDamageAnimation);
|
||||||
emit logSetCounter(player, ctr->getName(), event.value(), oldValue);
|
emit logSetCounter(player, ctr->getName(), event.value(), oldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -625,7 +626,7 @@ void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type,
|
||||||
eventCreateCounter(event.GetExtension(Event_CreateCounter::ext));
|
eventCreateCounter(event.GetExtension(Event_CreateCounter::ext));
|
||||||
break;
|
break;
|
||||||
case GameEvent::SET_COUNTER:
|
case GameEvent::SET_COUNTER:
|
||||||
eventSetCounter(event.GetExtension(Event_SetCounter::ext));
|
eventSetCounter(event.GetExtension(Event_SetCounter::ext), options);
|
||||||
break;
|
break;
|
||||||
case GameEvent::DEL_COUNTER:
|
case GameEvent::DEL_COUNTER:
|
||||||
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
|
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ public:
|
||||||
void eventCreateCounter(const Event_CreateCounter &event);
|
void eventCreateCounter(const Event_CreateCounter &event);
|
||||||
|
|
||||||
/// Set a player-level counter value.
|
/// Set a player-level counter value.
|
||||||
void eventSetCounter(const Event_SetCounter &event);
|
void eventSetCounter(const Event_SetCounter &event, EventProcessingOptions options);
|
||||||
|
|
||||||
/// Delete a player-level counter.
|
/// Delete a player-level counter.
|
||||||
void eventDelCounter(const Event_DelCounter &event);
|
void eventDelCounter(const Event_DelCounter &event);
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,15 @@ void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
||||||
auto *card = new CardItem(this);
|
auto *card = new CardItem(this);
|
||||||
card->processCardInfo(cardInfo);
|
card->processCardInfo(cardInfo);
|
||||||
zone->addCard(card, false, cardInfo.x(), cardInfo.y());
|
// Zones without coordinates (hand, piles, stack) preserve the order
|
||||||
|
// they arrive in on the server in the positions of their cards list.
|
||||||
|
// The x coordinate of such cards is always 0, so inserting at it
|
||||||
|
// would reverse the list on reconnect. Append instead.
|
||||||
|
if (zoneInfo.with_coords()) {
|
||||||
|
zone->addCard(card, false, cardInfo.x(), cardInfo.y());
|
||||||
|
} else {
|
||||||
|
zone->addCard(card, false, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (zoneInfo.has_always_reveal_top_card()) {
|
if (zoneInfo.has_always_reveal_top_card()) {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,14 @@ PlayerLogic *PlayerManager::getPlayer(int playerId) const
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerManager::clearSpectators()
|
||||||
|
{
|
||||||
|
const QList<int> spectatorIds = spectators.keys();
|
||||||
|
for (int spectatorId : spectatorIds) {
|
||||||
|
removeSpectator(spectatorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerManager::onPlayerConceded(int playerId, bool conceded)
|
void PlayerManager::onPlayerConceded(int playerId, bool conceded)
|
||||||
{
|
{
|
||||||
// Everything else cares about this
|
// Everything else cares about this
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ public:
|
||||||
emit spectatorRemoved(spectatorId, spectatorInfo);
|
emit spectatorRemoved(spectatorId, spectatorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Remove all spectators, emitting the removal signal for each. */
|
||||||
|
void clearSpectators();
|
||||||
|
|
||||||
[[nodiscard]] AbstractGame *getGame() const
|
[[nodiscard]] AbstractGame *getGame() const
|
||||||
{
|
{
|
||||||
return game;
|
return game;
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ AbstractCounter::AbstractCounter(CounterState *state,
|
||||||
{
|
{
|
||||||
setAcceptHoverEvents(true);
|
setAcceptHoverEvents(true);
|
||||||
|
|
||||||
connect(state, &CounterState::valueChanged, this, [this](int oldValue, int newValue) {
|
connect(state, &CounterState::valueChanged, this, [this](int oldValue, int newValue, bool skipDamageAnimation) {
|
||||||
value = newValue;
|
value = newValue;
|
||||||
onValueChanged(oldValue, newValue);
|
onValueChanged(oldValue, newValue, skipDamageAnimation);
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -230,7 +230,7 @@ void AbstractCounterDialog::changeValue(int diff)
|
||||||
setTextValue(QString::number(curValue));
|
setTextValue(QString::number(curValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractCounter::onValueChanged(int /*oldValue*/, int /*newValue*/)
|
void AbstractCounter::onValueChanged(int /*oldValue*/, int /*newValue*/, bool /*skipDamageAnimation*/)
|
||||||
{
|
{
|
||||||
// Default: no feedback. Subclasses such as PlayerCounter override this to
|
// Default: no feedback. Subclasses such as PlayerCounter override this to
|
||||||
// flash the counter on meaningful changes (life gain/loss).
|
// flash the counter on meaningful changes (life gain/loss).
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,9 @@ protected:
|
||||||
* @brief Hook for subclasses that need per-value-change feedback (e.g. life-total flash).
|
* @brief Hook for subclasses that need per-value-change feedback (e.g. life-total flash).
|
||||||
*
|
*
|
||||||
* Called whenever the counter's value changes, before the item repaints.
|
* Called whenever the counter's value changes, before the item repaints.
|
||||||
|
* @param skipDamageAnimation True when damage-related feedback should be suppressed (replay rewinds).
|
||||||
*/
|
*/
|
||||||
virtual void onValueChanged(int oldValue, int newValue);
|
virtual void onValueChanged(int oldValue, int newValue, bool skipDamageAnimation);
|
||||||
|
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ void CardItem::drawAttachArrow()
|
||||||
|
|
||||||
for (const auto &item : scene()->selectedItems()) {
|
for (const auto &item : scene()->selectedItems()) {
|
||||||
CardItem *card = qgraphicsitem_cast<CardItem *>(item);
|
CardItem *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
if (card == nullptr) {
|
if (card == nullptr || card == this) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (card->getZone() != state->getZone()) {
|
if (card->getZone() != state->getZone()) {
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,12 @@ void GameScene::removePlayer(PlayerLogic *player)
|
||||||
|
|
||||||
clearArrowsForPlayer(player->getPlayerInfo()->getId());
|
clearArrowsForPlayer(player->getPlayerInfo()->getId());
|
||||||
|
|
||||||
for (ZoneViewWidget *zone : zoneViews) {
|
// Closing a view removes it from zoneViews synchronously, so iterate over a
|
||||||
|
// copy: otherwise a player with several open views (e.g. library and hand)
|
||||||
|
// only has the first one closed here and the remaining views are left
|
||||||
|
// pointing at a player that is about to be deleted.
|
||||||
|
const QList<ZoneViewWidget *> zoneViewCopy = zoneViews;
|
||||||
|
for (ZoneViewWidget *zone : zoneViewCopy) {
|
||||||
if (zone->getPlayer() == player) {
|
if (zone->getPlayer() == player) {
|
||||||
zone->close();
|
zone->close();
|
||||||
}
|
}
|
||||||
|
|
@ -664,7 +669,10 @@ CardItem *GameScene::findTopmostCardInZone(const QList<QGraphicsItem *> &items,
|
||||||
*/
|
*/
|
||||||
void GameScene::toggleZoneView(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed)
|
void GameScene::toggleZoneView(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed)
|
||||||
{
|
{
|
||||||
for (auto &view : zoneViews) {
|
// Closing a view removes it from zoneViews synchronously, so iterate over a
|
||||||
|
// copy to make sure every already-open matching view is closed.
|
||||||
|
const QList<ZoneViewWidget *> zoneViewCopy = zoneViews;
|
||||||
|
for (auto *view : zoneViewCopy) {
|
||||||
ZoneViewZone *temp = view->getZone();
|
ZoneViewZone *temp = view->getZone();
|
||||||
if (temp->getLogic()->getName() == zoneName && temp->getLogic()->getPlayer() == player &&
|
if (temp->getLogic()->getName() == zoneName && temp->getLogic()->getPlayer() == player &&
|
||||||
qobject_cast<ZoneViewZoneLogic *>(temp->getLogic())->getNumberCards() == numberCards) {
|
qobject_cast<ZoneViewZoneLogic *>(temp->getLogic())->getNumberCards() == numberCards) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "hand_counter.h"
|
#include "hand_counter.h"
|
||||||
|
|
||||||
|
#include "../interface/pixel_map_generator.h"
|
||||||
#include "zones/card_zone.h"
|
#include "zones/card_zone.h"
|
||||||
|
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
|
@ -32,7 +33,8 @@ void HandCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
|
||||||
QSize translatedSize = painter->combinedTransform().mapRect(boundingRect()).size().toSize();
|
QSize translatedSize = painter->combinedTransform().mapRect(boundingRect()).size().toSize();
|
||||||
QPixmap cachedPixmap;
|
QPixmap cachedPixmap;
|
||||||
if (!QPixmapCache::find("handCounter" + QString::number(translatedSize.width()), &cachedPixmap)) {
|
if (!QPixmapCache::find("handCounter" + QString::number(translatedSize.width()), &cachedPixmap)) {
|
||||||
cachedPixmap = QPixmap("theme:hand").scaled(translatedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
cachedPixmap =
|
||||||
|
themePixmap(QStringLiteral("hand")).scaled(translatedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
QPixmapCache::insert("handCounter" + QString::number(translatedSize.width()), cachedPixmap);
|
QPixmapCache::insert("handCounter" + QString::number(translatedSize.width()), cachedPixmap);
|
||||||
}
|
}
|
||||||
resetPainterTransform(painter);
|
resetPainterTransform(painter);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../game/player/player_actions.h"
|
#include "../../game/player/player_actions.h"
|
||||||
#include "../../interface/card_picture_loader/card_picture_loader.h"
|
#include "../../interface/card_picture_loader/card_picture_loader.h"
|
||||||
#include "../../interface/widgets/cards/art_crop_attribution.h"
|
#include "../../interface/widgets/cards/art_crop_attribution.h"
|
||||||
|
#include "../../interface/widgets/cards/card_art_utils.h"
|
||||||
#include "../../interface/widgets/playmat/playmat_utils.h"
|
#include "../../interface/widgets/playmat/playmat_utils.h"
|
||||||
#include "../../interface/widgets/tabs/tab_game.h"
|
#include "../../interface/widgets/tabs/tab_game.h"
|
||||||
#include "../board/abstract_card_item.h"
|
#include "../board/abstract_card_item.h"
|
||||||
|
|
@ -251,8 +252,8 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state)
|
||||||
AbstractCounter *widget;
|
AbstractCounter *widget;
|
||||||
if (state->getName() == "life") {
|
if (state->getName() == "life") {
|
||||||
widget = playerTarget->addCounter(state);
|
widget = playerTarget->addCounter(state);
|
||||||
connect(state, &CounterState::valueChanged, this, [this](int oldValue, int newValue) {
|
connect(state, &CounterState::valueChanged, this, [this](int oldValue, int newValue, bool skipDamageAnimation) {
|
||||||
if (newValue < oldValue) {
|
if (newValue < oldValue && !skipDamageAnimation) {
|
||||||
tableZoneGraphicsItem->triggerDamageShimmer();
|
tableZoneGraphicsItem->triggerDamageShimmer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -442,7 +443,7 @@ void PlayerGraphicsItem::updatePlaymat()
|
||||||
hasPlaymat = true;
|
hasPlaymat = true;
|
||||||
emit playmatChanged(true);
|
emit playmatChanged(true);
|
||||||
}
|
}
|
||||||
playmatPixmap = fullRes;
|
playmatPixmap = CardArtUtils::rotateSidewaysLayoutArt(fullRes, card);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,13 @@ PlayerListWidget::PlayerListWidget(TabSupervisor *_tabSupervisor,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QTreeWidget(parent), tabSupervisor(_tabSupervisor), client(_client), game(_game), gameStarted(false)
|
: QTreeWidget(parent), tabSupervisor(_tabSupervisor), client(_client), game(_game), gameStarted(false)
|
||||||
{
|
{
|
||||||
readyIcon = QPixmap("theme:icons/ready_start");
|
readyIcon = themePixmap(QStringLiteral("icons/ready_start"));
|
||||||
notReadyIcon = QPixmap("theme:icons/not_ready_start");
|
notReadyIcon = themePixmap(QStringLiteral("icons/not_ready_start"));
|
||||||
concededIcon = QPixmap("theme:icons/conceded");
|
concededIcon = themePixmap(QStringLiteral("icons/conceded"));
|
||||||
playerIcon = loadColorAdjustedPixmap("theme:icons/player");
|
playerIcon = loadColorAdjustedPixmap("theme:icons/player");
|
||||||
judgeIcon = loadColorAdjustedPixmap("theme:icons/scales");
|
judgeIcon = loadColorAdjustedPixmap("theme:icons/scales");
|
||||||
spectatorIcon = loadColorAdjustedPixmap("theme:icons/spectator");
|
spectatorIcon = loadColorAdjustedPixmap("theme:icons/spectator");
|
||||||
lockIcon = QPixmap("theme:icons/lock");
|
lockIcon = themePixmap(QStringLiteral("icons/lock"));
|
||||||
|
|
||||||
if (tabSupervisor) {
|
if (tabSupervisor) {
|
||||||
itemDelegate = new PlayerListItemDelegate(this);
|
itemDelegate = new PlayerListItemDelegate(this);
|
||||||
|
|
@ -92,6 +92,11 @@ void PlayerListWidget::retranslateUi()
|
||||||
|
|
||||||
void PlayerListWidget::addPlayer(const ServerInfo_PlayerProperties &player)
|
void PlayerListWidget::addPlayer(const ServerInfo_PlayerProperties &player)
|
||||||
{
|
{
|
||||||
|
if (players.contains(player.player_id())) {
|
||||||
|
updatePlayerProperties(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeWidgetItem *newPlayer = new PlayerListTWI;
|
QTreeWidgetItem *newPlayer = new PlayerListTWI;
|
||||||
players.insert(player.player_id(), newPlayer);
|
players.insert(player.player_id(), newPlayer);
|
||||||
updatePlayerProperties(player);
|
updatePlayerProperties(player);
|
||||||
|
|
@ -176,6 +181,17 @@ void PlayerListWidget::removePlayer(int playerId)
|
||||||
delete takeTopLevelItem(indexOfTopLevelItem(player));
|
delete takeTopLevelItem(indexOfTopLevelItem(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerListWidget::clearSpectators()
|
||||||
|
{
|
||||||
|
const QList<int> playerIds = players.keys();
|
||||||
|
for (int playerId : playerIds) {
|
||||||
|
QTreeWidgetItem *player = players.value(playerId, 0);
|
||||||
|
if (player && !player->data(1, Qt::UserRole).toBool()) {
|
||||||
|
removePlayer(playerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerListWidget::setActivePlayer(int playerId)
|
void PlayerListWidget::setActivePlayer(int playerId)
|
||||||
{
|
{
|
||||||
QMapIterator<int, QTreeWidgetItem *> i(players);
|
QMapIterator<int, QTreeWidgetItem *> i(players);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ public slots:
|
||||||
void addPlayer(const ServerInfo_PlayerProperties &player);
|
void addPlayer(const ServerInfo_PlayerProperties &player);
|
||||||
void removePlayer(int playerId);
|
void removePlayer(int playerId);
|
||||||
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
|
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
|
||||||
|
void clearSpectators();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ void PlayerCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerCounter::onValueChanged(int oldValue, int newValue)
|
void PlayerCounter::onValueChanged(int oldValue, int newValue, bool skipDamageAnimation)
|
||||||
{
|
{
|
||||||
flashDelta = newValue - oldValue;
|
flashDelta = newValue - oldValue;
|
||||||
if (flashDelta == 0) {
|
if (flashDelta == 0) {
|
||||||
|
|
@ -81,6 +81,11 @@ void PlayerCounter::onValueChanged(int oldValue, int newValue)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipDamageAnimation) {
|
||||||
|
flashAlpha = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
flashAlpha = 1.0;
|
flashAlpha = 1.0;
|
||||||
flashClock.start();
|
flashClock.start();
|
||||||
if (scene()) {
|
if (scene()) {
|
||||||
|
|
@ -132,8 +137,18 @@ void PlayerTarget::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*o
|
||||||
QRectF translatedRect = painter->combinedTransform().mapRect(avatarBoundingRect);
|
QRectF translatedRect = painter->combinedTransform().mapRect(avatarBoundingRect);
|
||||||
QSize translatedSize = translatedRect.size().toSize();
|
QSize translatedSize = translatedRect.size().toSize();
|
||||||
QPixmap cachedPixmap;
|
QPixmap cachedPixmap;
|
||||||
|
// The key must cover everything the generated pawn depends on: the rendered
|
||||||
|
// size, the user level, and the pixmap being drawn. fullPixmap.cacheKey() is
|
||||||
|
// 0 for every null pixmap, so the default-pawn branch additionally needs the
|
||||||
|
// pawn's privlevel (lowercased, matching UserLevelPixmapGenerator) and colors
|
||||||
|
// in the key — otherwise two players without a custom avatar (and the same
|
||||||
|
// user level) would share one cached pawn.
|
||||||
const QString cacheKey = "avatar" + QString::number(translatedSize.width()) + "_" +
|
const QString cacheKey = "avatar" + QString::number(translatedSize.width()) + "_" +
|
||||||
QString::number(info->user_level()) + "_" + QString::number(fullPixmap.cacheKey());
|
QString::number(translatedSize.height()) + "_" + QString::number(info->user_level()) +
|
||||||
|
"_" + QString::number(fullPixmap.cacheKey()) + "_" +
|
||||||
|
QString::fromStdString(info->privlevel()).toLower() + "_" +
|
||||||
|
QString::fromStdString(info->pawn_colors().left_side()) + "_" +
|
||||||
|
QString::fromStdString(info->pawn_colors().right_side());
|
||||||
if (!QPixmapCache::find(cacheKey, &cachedPixmap)) {
|
if (!QPixmapCache::find(cacheKey, &cachedPixmap)) {
|
||||||
cachedPixmap = QPixmap(translatedSize.width(), translatedSize.height());
|
cachedPixmap = QPixmap(translatedSize.width(), translatedSize.height());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class PlayerCounter : public AbstractCounter, public IAnimatedItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
void onValueChanged(int oldValue, int newValue) override;
|
void onValueChanged(int oldValue, int newValue, bool skipDamageAnimation) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr qreal flashDurationMs = 450.0;
|
static constexpr qreal flashDurationMs = 450.0;
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
|
||||||
searchEdit.setPlaceholderText(tr("Search by card name (or search expressions)"));
|
searchEdit.setPlaceholderText(tr("Search by card name (or search expressions)"));
|
||||||
searchEdit.setClearButtonEnabled(true);
|
searchEdit.setClearButtonEnabled(true);
|
||||||
searchEdit.addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchEdit.addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||||
auto help = searchEdit.addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
auto help = searchEdit.addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition);
|
||||||
|
|
||||||
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); });
|
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); });
|
||||||
|
|
||||||
|
|
@ -549,7 +549,7 @@ void ZoneViewWidget::initStyleOption(QStyleOption *option) const
|
||||||
{
|
{
|
||||||
QStyleOptionTitleBar *titleBar = qstyleoption_cast<QStyleOptionTitleBar *>(option);
|
QStyleOptionTitleBar *titleBar = qstyleoption_cast<QStyleOptionTitleBar *>(option);
|
||||||
if (titleBar) {
|
if (titleBar) {
|
||||||
titleBar->icon = QPixmap("theme:cockatrice");
|
titleBar->icon = themePixmap(QStringLiteral("cockatrice"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "card_picture_loader.h"
|
#include "card_picture_loader.h"
|
||||||
|
|
||||||
#include "../../client/settings/cache_settings.h"
|
#include "../../client/settings/cache_settings.h"
|
||||||
|
#include "../pixel_map_generator.h"
|
||||||
#include "card_picture_loader_cache_method.h"
|
#include "card_picture_loader_cache_method.h"
|
||||||
#include "card_picture_loader_local_schemes.h"
|
#include "card_picture_loader_local_schemes.h"
|
||||||
|
|
||||||
|
|
@ -62,7 +63,7 @@ void CardPictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size)
|
||||||
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
qCDebug(CardPictureLoaderLog) << "PictureLoader: cache miss for" << backCacheKey;
|
qCDebug(CardPictureLoaderLog) << "PictureLoader: cache miss for" << backCacheKey;
|
||||||
QPixmap tmpPixmap("theme:cardback");
|
QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback"));
|
||||||
|
|
||||||
if (tmpPixmap.isNull()) {
|
if (tmpPixmap.isNull()) {
|
||||||
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback'! Using fallback pixmap.";
|
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback'! Using fallback pixmap.";
|
||||||
|
|
@ -83,7 +84,7 @@ void CardPictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSiz
|
||||||
"_trice_card_back_inprogress_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
"_trice_card_back_inprogress_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
|
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
|
||||||
QPixmap tmpPixmap("theme:cardback");
|
QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback"));
|
||||||
|
|
||||||
if (tmpPixmap.isNull()) {
|
if (tmpPixmap.isNull()) {
|
||||||
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for in-progress state! Using fallback.";
|
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for in-progress state! Using fallback.";
|
||||||
|
|
@ -105,7 +106,7 @@ void CardPictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize si
|
||||||
"_trice_card_back_failed_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
"_trice_card_back_failed_" + QString::number(size.width()) + "x" + QString::number(size.height());
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
|
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
|
||||||
QPixmap tmpPixmap("theme:cardback");
|
QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback"));
|
||||||
|
|
||||||
if (tmpPixmap.isNull()) {
|
if (tmpPixmap.isNull()) {
|
||||||
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for failed state! Using fallback.";
|
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for failed state! Using fallback.";
|
||||||
|
|
@ -138,7 +139,8 @@ void CardPictureLoader::getPixmap(QPixmap &pixmap, const ExactCard &card, QSize
|
||||||
QPixmap bigPixmap;
|
QPixmap bigPixmap;
|
||||||
if (QPixmapCache::find(key, &bigPixmap)) {
|
if (QPixmapCache::find(key, &bigPixmap)) {
|
||||||
if (bigPixmap.isNull()) {
|
if (bigPixmap.isNull()) {
|
||||||
getCardBackLoadingFailedPixmap(pixmap, size);
|
// Leave the pixmap null so callers fall back to a solid color
|
||||||
|
// instead of showing the card back.
|
||||||
QDateTime failedAtTime = getInstance().failedAt.value(key);
|
QDateTime failedAtTime = getInstance().failedAt.value(key);
|
||||||
if (!failedAtTime.isValid() ||
|
if (!failedAtTime.isValid() ||
|
||||||
failedAtTime.addSecs(RETRY_FAILED_CARDS_SECS) < QDateTime::currentDateTime()) {
|
failedAtTime.addSecs(RETRY_FAILED_CARDS_SECS) < QDateTime::currentDateTime()) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ enum Format
|
||||||
PlainText,
|
PlainText,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is cockatrice's native deck file format, and supports deck metadata such as banner cards and tags.
|
* This is Cockatrice's native deck file format, and supports deck metadata such as banner cards and tags.
|
||||||
* Stored as .cod files.
|
* Stored as .cod files.
|
||||||
*/
|
*/
|
||||||
Cockatrice
|
Cockatrice
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo
|
||||||
result = deckList.loadFromFile_Native(&file);
|
result = deckList.loadFromFile_Native(&file);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
qCInfo(DeckLoaderLog) << "Failed to load " << fileName
|
qCInfo(DeckLoaderLog) << "Failed to load " << fileName
|
||||||
<< "as cockatrice format; retrying as plain format";
|
<< "as Cockatrice format; retrying as plain format";
|
||||||
file.seek(0);
|
file.seek(0);
|
||||||
result = deckList.loadFromFile_Plain(&file, CardNameNormalizer());
|
result = deckList.loadFromFile_Plain(&file, CardNameNormalizer());
|
||||||
fmt = DeckFileFormat::PlainText;
|
fmt = DeckFileFormat::PlainText;
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ public:
|
||||||
static void printDeckList(QPrinter *printer, const DeckList &deckList);
|
static void printDeckList(QPrinter *printer, const DeckList &deckList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given deck's file to the cockatrice file format.
|
* Converts the given deck's file to the Cockatrice file format.
|
||||||
* Uses the lastLoadInfo in the LoadedDeck to determine the current name of the file and where to save to.
|
* Uses the lastLoadInfo in the LoadedDeck to determine the current name of the file and where to save to.
|
||||||
* @param deck The deck to convert. Should have valid lastLoadInfo. Will update the lastLoadInfo.
|
* @param deck The deck to convert. Should have valid lastLoadInfo. Will update the lastLoadInfo.
|
||||||
* @return Whether the conversion succeeded.
|
* @return Whether the conversion succeeded.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "pixel_map_generator.h"
|
#include "pixel_map_generator.h"
|
||||||
|
|
||||||
|
#include "theme_manager.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
@ -14,6 +16,7 @@
|
||||||
#define DEFAULT_COLOR_MODERATOR_LEFT "#ffffff";
|
#define DEFAULT_COLOR_MODERATOR_LEFT "#ffffff";
|
||||||
#define DEFAULT_COLOR_MODERATOR_RIGHT "#000000";
|
#define DEFAULT_COLOR_MODERATOR_RIGHT "#000000";
|
||||||
#define DEFAULT_COLOR_ADMIN "#ff2701";
|
#define DEFAULT_COLOR_ADMIN "#ff2701";
|
||||||
|
#define DEFAULT_COLOR_DEVELOPER "#B8B8B8"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamps an svg render size so that rendering does not exceed a multiple of the requested size.
|
* Clamps an svg render size so that rendering does not exceed a multiple of the requested size.
|
||||||
|
|
@ -82,7 +85,13 @@ static QPixmap loadSvg(const QString &svgPath, const QSize &size, bool expandOnl
|
||||||
/**
|
/**
|
||||||
* Try to load path image from non-SVG formats, otherwise fall back to SVG.
|
* Try to load path image from non-SVG formats, otherwise fall back to SVG.
|
||||||
* This is to allow custom themes to support non-SVG format type overrides, since SVG requires custom loading.
|
* This is to allow custom themes to support non-SVG format type overrides, since SVG requires custom loading.
|
||||||
* @param path The path to the file, with no file extension. File formats will be automatically detected.
|
*
|
||||||
|
* The path may already carry the resolved file extension (e.g. via
|
||||||
|
* ThemeManager::assetPath); such paths are loaded directly. Otherwise a
|
||||||
|
* format-agnostic lookup probes png, jpg and finally svg.
|
||||||
|
*
|
||||||
|
* @param path The path to the file, with no file extension unless the caller
|
||||||
|
* already resolved it. File formats will be automatically detected.
|
||||||
* @param size The desired size of the pixmap.
|
* @param size The desired size of the pixmap.
|
||||||
* @param expandOnly If true, then keep the size of the initial pixmap to at least the size (Only relevant if SVG).
|
* @param expandOnly If true, then keep the size of the initial pixmap to at least the size (Only relevant if SVG).
|
||||||
*
|
*
|
||||||
|
|
@ -90,6 +99,19 @@ static QPixmap loadSvg(const QString &svgPath, const QSize &size, bool expandOnl
|
||||||
*/
|
*/
|
||||||
static QPixmap tryLoadImage(const QString &path, const QSize &size, bool expandOnly = false)
|
static QPixmap tryLoadImage(const QString &path, const QSize &size, bool expandOnly = false)
|
||||||
{
|
{
|
||||||
|
if (path.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)) {
|
||||||
|
return loadSvg(path, size, expandOnly);
|
||||||
|
}
|
||||||
|
if (path.endsWith(QLatin1String(".png"), Qt::CaseInsensitive) ||
|
||||||
|
path.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) ||
|
||||||
|
path.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive)) {
|
||||||
|
QPixmap pix(path);
|
||||||
|
if (!pix.isNull()) {
|
||||||
|
return pix.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
const auto formats = {"png", "jpg"};
|
const auto formats = {"png", "jpg"};
|
||||||
|
|
||||||
QPixmap returnPixmap;
|
QPixmap returnPixmap;
|
||||||
|
|
@ -111,7 +133,8 @@ QPixmap PhasePixmapGenerator::generatePixmap(int height, QString name)
|
||||||
return pmCache.value(key);
|
return pmCache.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap pixmap = tryLoadImage("theme:phases/" + name, QSize(height, height));
|
QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("phases/") + name),
|
||||||
|
QSize(height, height));
|
||||||
|
|
||||||
pmCache.insert(key, pixmap);
|
pmCache.insert(key, pixmap);
|
||||||
return pixmap;
|
return pixmap;
|
||||||
|
|
@ -359,6 +382,8 @@ QIcon UserLevelPixmapGenerator::generateIconDefault(int height,
|
||||||
|
|
||||||
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
colorLeft = DEFAULT_COLOR_ADMIN;
|
colorLeft = DEFAULT_COLOR_ADMIN;
|
||||||
|
} else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
colorLeft = DEFAULT_COLOR_DEVELOPER;
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
colorLeft = DEFAULT_COLOR_MODERATOR_LEFT;
|
colorLeft = DEFAULT_COLOR_MODERATOR_LEFT;
|
||||||
colorRight = DEFAULT_COLOR_MODERATOR_RIGHT;
|
colorRight = DEFAULT_COLOR_MODERATOR_RIGHT;
|
||||||
|
|
@ -396,7 +421,8 @@ QPixmap LockPixmapGenerator::generatePixmap(int height)
|
||||||
return pmCache.value(key);
|
return pmCache.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap pixmap = tryLoadImage("theme:icons/lock", QSize(height, height), true);
|
QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("icons/lock")),
|
||||||
|
QSize(height, height), true);
|
||||||
pmCache.insert(key, pixmap);
|
pmCache.insert(key, pixmap);
|
||||||
return pixmap;
|
return pixmap;
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +437,8 @@ QPixmap DropdownIconPixmapGenerator::generatePixmap(int height, bool expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString name = expanded ? "dropdown_expanded" : "dropdown_collapsed";
|
QString name = expanded ? "dropdown_expanded" : "dropdown_collapsed";
|
||||||
QPixmap pixmap = tryLoadImage("theme:icons/" + name, QSize(height, height), true);
|
QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("icons/") + name),
|
||||||
|
QSize(height, height), true);
|
||||||
|
|
||||||
pmCache.insert(key, pixmap);
|
pmCache.insert(key, pixmap);
|
||||||
return pixmap;
|
return pixmap;
|
||||||
|
|
@ -472,6 +499,13 @@ QHash<QString, QPixmap> ManaSymbolPixmapGenerator::scaledCache;
|
||||||
|
|
||||||
QPixmap loadColorAdjustedPixmap(const QString &name)
|
QPixmap loadColorAdjustedPixmap(const QString &name)
|
||||||
{
|
{
|
||||||
|
// Prefer an authored scheme-qualified variant when one exists for this asset.
|
||||||
|
const QString variant = themeManager->schemeVariantPath(QStringView(name).mid(QStringLiteral("theme:").size()));
|
||||||
|
if (!variant.isEmpty()) {
|
||||||
|
return QPixmap(QStringLiteral("theme:") + variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy fallback: runtime-invert for dark mode when no authored variant.
|
||||||
if (qApp->palette().windowText().color().lightness() > 200) {
|
if (qApp->palette().windowText().color().lightness() > 200) {
|
||||||
QImage img(name);
|
QImage img(name);
|
||||||
img.invertPixels();
|
img.invertPixels();
|
||||||
|
|
@ -482,3 +516,21 @@ QPixmap loadColorAdjustedPixmap(const QString &name)
|
||||||
return QPixmap(name);
|
return QPixmap(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPixmap themePixmap(QStringView prefix)
|
||||||
|
{
|
||||||
|
const QString resolved = themeManager->assetPath(prefix);
|
||||||
|
return QPixmap(QStringLiteral("theme:") + resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPixmapGeneratorCaches()
|
||||||
|
{
|
||||||
|
PhasePixmapGenerator::clear();
|
||||||
|
CounterPixmapGenerator::clear();
|
||||||
|
PingPixmapGenerator::clear();
|
||||||
|
CountryPixmapGenerator::clear();
|
||||||
|
UserLevelPixmapGenerator::clear();
|
||||||
|
LockPixmapGenerator::clear();
|
||||||
|
DropdownIconPixmapGenerator::clear();
|
||||||
|
ManaSymbolPixmapGenerator::clear();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,4 +156,15 @@ public:
|
||||||
|
|
||||||
QPixmap loadColorAdjustedPixmap(const QString &name);
|
QPixmap loadColorAdjustedPixmap(const QString &name);
|
||||||
|
|
||||||
|
// Loads a "theme:" asset (with no file extension in prefix), preferring the
|
||||||
|
// scheme-qualified variant (prefix-dark / prefix-light, resolved via
|
||||||
|
// ThemeManager::assetPath) and falling back to the plain asset. Callers load
|
||||||
|
// the returned path directly. Use for scheme-sensitive pixmaps like
|
||||||
|
// backgrounds, the card back, and the app logo.
|
||||||
|
QPixmap themePixmap(QStringView prefix);
|
||||||
|
|
||||||
|
// Clears every PixmapGenerator's static cache so scheme variants are
|
||||||
|
// re-resolved when the active theme or color scheme changes.
|
||||||
|
void clearPixmapGeneratorCaches();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
#include "theme_manager.h"
|
#include "theme_manager.h"
|
||||||
|
|
||||||
#include "../../client/settings/cache_settings.h"
|
#include "../../client/settings/cache_settings.h"
|
||||||
|
#include "pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
|
|
@ -140,6 +142,48 @@ bool ThemeManager::isDarkMode(const QString &themeDirPath) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ThemeManager::schemeVariantPath(QStringView prefix) const
|
||||||
|
{
|
||||||
|
static const QStringList formats = {QStringLiteral(".png"), QStringLiteral(".jpg"), QStringLiteral(".jpeg"),
|
||||||
|
QStringLiteral(".svg")};
|
||||||
|
const QString scheme = isDarkMode(currentThemePath) ? QStringLiteral("dark") : QStringLiteral("light");
|
||||||
|
const QString variantStem = prefix.toString() + QLatin1Char('-') + scheme;
|
||||||
|
|
||||||
|
for (const QString &format : formats) {
|
||||||
|
if (QFileInfo::exists(QStringLiteral("theme:") + variantStem + format)) {
|
||||||
|
return variantStem + format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ThemeManager::assetPath(QStringView prefix) const
|
||||||
|
{
|
||||||
|
// Probe order mirrors tryLoadImage: a theme may override the default SVG
|
||||||
|
// with a raster of the same stem, so raster wins over SVG within a stem.
|
||||||
|
static const QStringList formats = {QStringLiteral(".png"), QStringLiteral(".jpg"), QStringLiteral(".jpeg"),
|
||||||
|
QStringLiteral(".svg")};
|
||||||
|
|
||||||
|
auto findExisting = [](const QString &stem) {
|
||||||
|
for (const QString &format : formats) {
|
||||||
|
if (QFileInfo::exists(QStringLiteral("theme:") + stem + format)) {
|
||||||
|
return stem + format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prefer the scheme-qualified variant when it exists, else the plain
|
||||||
|
// asset as the super fallback. Both return the resolved path including
|
||||||
|
// its file extension so callers can load it directly.
|
||||||
|
const QString variant = schemeVariantPath(prefix);
|
||||||
|
if (!variant.isEmpty()) {
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
const QString resolvedPlain = findExisting(prefix.toString());
|
||||||
|
return resolvedPlain.isEmpty() ? prefix.toString() : resolvedPlain;
|
||||||
|
}
|
||||||
|
|
||||||
bool ThemeManager::isBuiltInTheme()
|
bool ThemeManager::isBuiltInTheme()
|
||||||
{
|
{
|
||||||
const auto themeName = SettingsCache::instance().getThemeName();
|
const auto themeName = SettingsCache::instance().getThemeName();
|
||||||
|
|
@ -180,7 +224,7 @@ QStringMap &ThemeManager::getAvailableThemes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load themes from cockatrice system dir
|
// Load themes from Cockatrice system dir
|
||||||
dir.setPath(systemThemesBasePath());
|
dir.setPath(systemThemesBasePath());
|
||||||
|
|
||||||
for (QString themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
for (QString themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||||
|
|
@ -195,7 +239,7 @@ QStringMap &ThemeManager::getAvailableThemes()
|
||||||
QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor)
|
QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor)
|
||||||
{
|
{
|
||||||
QBrush brush;
|
QBrush brush;
|
||||||
QPixmap tmp = QPixmap("theme:zones/" + fileName);
|
QPixmap tmp = QPixmap("theme:" + assetPath(QStringLiteral("zones/") + fileName));
|
||||||
if (tmp.isNull()) {
|
if (tmp.isNull()) {
|
||||||
brush.setColor(fallbackColor);
|
brush.setColor(fallbackColor);
|
||||||
brush.setStyle(Qt::SolidPattern);
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
|
@ -209,7 +253,7 @@ QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor)
|
||||||
QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush)
|
QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush)
|
||||||
{
|
{
|
||||||
QBrush brush;
|
QBrush brush;
|
||||||
QPixmap tmp = QPixmap("theme:zones/" + fileName);
|
QPixmap tmp = QPixmap("theme:" + assetPath(QStringLiteral("zones/") + fileName));
|
||||||
|
|
||||||
if (tmp.isNull()) {
|
if (tmp.isNull()) {
|
||||||
brush = fallbackBrush;
|
brush = fallbackBrush;
|
||||||
|
|
@ -393,9 +437,19 @@ void ThemeManager::themeChangedSlot()
|
||||||
currentThemePath = dirPath;
|
currentThemePath = dirPath;
|
||||||
QDir dir(dirPath);
|
QDir dir(dirPath);
|
||||||
|
|
||||||
// CSS
|
// CSS — prefer the scheme-qualified stylesheet (style-dark.css /
|
||||||
if (!dirPath.isEmpty() && dir.exists(STYLE_CSS_NAME)) {
|
// style-light.css) when present, else the plain style.css as fallback.
|
||||||
qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME));
|
if (!dirPath.isEmpty()) {
|
||||||
|
const QString scheme = isDarkMode(dirPath) ? QStringLiteral("dark") : QStringLiteral("light");
|
||||||
|
const QString schemeCss = QFileInfo(QStringLiteral(STYLE_CSS_NAME)).completeBaseName() + QLatin1Char('-') +
|
||||||
|
scheme + QStringLiteral(".css");
|
||||||
|
if (dir.exists(schemeCss)) {
|
||||||
|
qApp->setStyleSheet("file:///" + dir.absoluteFilePath(schemeCss));
|
||||||
|
} else if (dir.exists(STYLE_CSS_NAME)) {
|
||||||
|
qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME));
|
||||||
|
} else {
|
||||||
|
qApp->setStyleSheet("");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qApp->setStyleSheet("");
|
qApp->setStyleSheet("");
|
||||||
}
|
}
|
||||||
|
|
@ -446,6 +500,7 @@ void ThemeManager::themeChangedSlot()
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmapCache::clear();
|
QPixmapCache::clear();
|
||||||
|
clearPixmapGeneratorCaches();
|
||||||
|
|
||||||
emit themeChanged();
|
emit themeChanged();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,20 @@ public:
|
||||||
// Load/save per-scheme palette colors
|
// Load/save per-scheme palette colors
|
||||||
static PaletteConfig loadPaletteConfig(const QString &themeDirPath, const QString &colorScheme);
|
static PaletteConfig loadPaletteConfig(const QString &themeDirPath, const QString &colorScheme);
|
||||||
static bool savePaletteConfig(const QString &themeDirPath, const QString &colorScheme, const PaletteConfig &cfg);
|
static bool savePaletteConfig(const QString &themeDirPath, const QString &colorScheme, const PaletteConfig &cfg);
|
||||||
|
// Resolve prefix to a scheme-qualified "theme:" path. Existence is probed
|
||||||
|
// internally across the formats themes may ship (.png/.jpg/.svg), so
|
||||||
|
// callers load the returned path directly. Prefers "<prefix>-<dark|light>"
|
||||||
|
// when a file exists at that stem, otherwise the plain "<prefix>" as the
|
||||||
|
// super fallback. The resolved scheme covers explicit light/dark as well
|
||||||
|
// as OS-resolved "system". Returns the path with its file extension when a
|
||||||
|
// match is found; unqualified assets keep working unchanged.
|
||||||
|
QString assetPath(QStringView prefix) const;
|
||||||
|
// Like assetPath, but resolves only the scheme-qualified variant
|
||||||
|
// ("<prefix>-<dark|light>.<ext>") and returns an empty string when no
|
||||||
|
// variant exists — it never falls back to the plain "<prefix>" asset.
|
||||||
|
// Callers that must distinguish "no authored variant" (e.g. to keep a
|
||||||
|
// legacy runtime fallback alive) should use this instead of assetPath.
|
||||||
|
QString schemeVariantPath(QStringView prefix) const;
|
||||||
// Load the theme's shipped default palette, falling back to the system
|
// Load the theme's shipped default palette, falling back to the system
|
||||||
// theme directory when it is absent from the resolved (user) directory.
|
// theme directory when it is absent from the resolved (user) directory.
|
||||||
static PaletteConfig
|
static PaletteConfig
|
||||||
|
|
|
||||||
18
cockatrice/src/interface/widgets/cards/card_art_utils.cpp
Normal file
18
cockatrice/src/interface/widgets/cards/card_art_utils.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "card_art_utils.h"
|
||||||
|
|
||||||
|
#include <QTransform>
|
||||||
|
#include <libcockatrice/card/printing/exact_card.h>
|
||||||
|
|
||||||
|
namespace CardArtUtils
|
||||||
|
{
|
||||||
|
QPixmap rotateSidewaysLayoutArt(const QPixmap &art, const ExactCard &card)
|
||||||
|
{
|
||||||
|
if (!card.getInfo().getUiAttributes().landscapeOrientation) {
|
||||||
|
return art;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTransform transform;
|
||||||
|
transform.rotate(90);
|
||||||
|
return art.transformed(transform, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
} // namespace CardArtUtils
|
||||||
25
cockatrice/src/interface/widgets/cards/card_art_utils.h
Normal file
25
cockatrice/src/interface/widgets/cards/card_art_utils.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef CARD_ART_UTILS_H
|
||||||
|
#define CARD_ART_UTILS_H
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
class ExactCard;
|
||||||
|
|
||||||
|
namespace CardArtUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Rotates a card's art upright when its layout shows sideways.
|
||||||
|
*
|
||||||
|
* Sideways-layout cards (planes, sieges/battles, split cards) store their
|
||||||
|
* landscape artwork rotated 90° inside a portrait frame. Art-crop displays,
|
||||||
|
* playmat art, and the card-info picture must show such art upright before
|
||||||
|
* sampling or painting. Portrait cards are returned unchanged.
|
||||||
|
*
|
||||||
|
* @param art The card pixmap to orient.
|
||||||
|
* @param card The card describing the art orientation.
|
||||||
|
* @return @p art rotated 90° clockwise for sideways-layout cards, else @p art.
|
||||||
|
*/
|
||||||
|
QPixmap rotateSidewaysLayoutArt(const QPixmap &art, const ExactCard &card);
|
||||||
|
} // namespace CardArtUtils
|
||||||
|
|
||||||
|
#endif // CARD_ART_UTILS_H
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "../../../interface/card_picture_loader/card_picture_loader.h"
|
#include "../../../interface/card_picture_loader/card_picture_loader.h"
|
||||||
#include "../../../interface/widgets/tabs/tab_supervisor.h"
|
#include "../../../interface/widgets/tabs/tab_supervisor.h"
|
||||||
#include "../../window_main.h"
|
#include "../../window_main.h"
|
||||||
|
#include "card_art_utils.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
@ -193,12 +194,7 @@ void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
|
||||||
|
|
||||||
QPixmap transformedPixmap = resizedPixmap; // Default pixmap
|
QPixmap transformedPixmap = resizedPixmap; // Default pixmap
|
||||||
if (SettingsCache::instance().cardsDisplay().getAutoRotateSidewaysLayoutCards()) {
|
if (SettingsCache::instance().cardsDisplay().getAutoRotateSidewaysLayoutCards()) {
|
||||||
if (exactCard.getInfo().getUiAttributes().landscapeOrientation) {
|
transformedPixmap = CardArtUtils::rotateSidewaysLayoutArt(resizedPixmap, exactCard);
|
||||||
// Rotate pixmap 90 degrees to the left
|
|
||||||
QTransform transform;
|
|
||||||
transform.rotate(90);
|
|
||||||
transformedPixmap = resizedPixmap.transformed(transform, Qt::SmoothTransformation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle DPI scaling
|
// Handle DPI scaling
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "abstract_analytics_panel_widget.h"
|
#include "abstract_analytics_panel_widget.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "deck_list_statistics_analyzer.h"
|
#include "deck_list_statistics_analyzer.h"
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
@ -20,7 +21,7 @@ AbstractAnalyticsPanelWidget::AbstractAnalyticsPanelWidget(QWidget *parent, Deck
|
||||||
|
|
||||||
// config button
|
// config button
|
||||||
configureButton = new QPushButton(this);
|
configureButton = new QPushButton(this);
|
||||||
configureButton->setIcon(QPixmap("theme:icons/cogwheel"));
|
configureButton->setIcon(themePixmap(QStringLiteral("icons/cogwheel")));
|
||||||
configureButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
configureButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
connect(configureButton, &QPushButton::clicked, this, &AbstractAnalyticsPanelWidget::applyConfigFromDialog);
|
connect(configureButton, &QPushButton::clicked, this, &AbstractAnalyticsPanelWidget::applyConfigFromDialog);
|
||||||
bannerAndSettingsLayout->addWidget(configureButton, 0);
|
bannerAndSettingsLayout->addWidget(configureButton, 0);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
|
||||||
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
||||||
searchEdit->setClearButtonEnabled(true);
|
searchEdit->setClearButtonEnabled(true);
|
||||||
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||||
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
auto help = searchEdit->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition);
|
||||||
|
|
||||||
setFocusProxy(searchEdit);
|
setFocusProxy(searchEdit);
|
||||||
setFocusPolicy(Qt::ClickFocus);
|
setFocusPolicy(Qt::ClickFocus);
|
||||||
|
|
@ -59,13 +59,13 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
|
||||||
&DeckEditorDatabaseDisplayWidget::onRelatedCardClicked);
|
&DeckEditorDatabaseDisplayWidget::onRelatedCardClicked);
|
||||||
|
|
||||||
aAddCard = new QAction(QString(), this);
|
aAddCard = new QAction(QString(), this);
|
||||||
aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green"));
|
aAddCard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_green")));
|
||||||
connect(aAddCard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
|
connect(aAddCard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
|
||||||
auto *tbAddCard = new QToolButton(this);
|
auto *tbAddCard = new QToolButton(this);
|
||||||
tbAddCard->setDefaultAction(aAddCard);
|
tbAddCard->setDefaultAction(aAddCard);
|
||||||
|
|
||||||
aAddCardToSideboard = new QAction(QString(), this);
|
aAddCardToSideboard = new QAction(QString(), this);
|
||||||
aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue"));
|
aAddCardToSideboard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_blue")));
|
||||||
connect(aAddCardToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
|
connect(aAddCardToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
|
||||||
auto *tbAddCardToSideboard = new QToolButton(this);
|
auto *tbAddCardToSideboard = new QToolButton(this);
|
||||||
tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard);
|
tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../playmat/playmat_settings_dialog.h"
|
#include "../playmat/playmat_settings_dialog.h"
|
||||||
#include "../settings_page/user_interface_settings_page.h"
|
#include "../settings_page/user_interface_settings_page.h"
|
||||||
#include "../tabs/api/commander_spellbook/commander_bracket_widget.h"
|
#include "../tabs/api/commander_spellbook/commander_bracket_widget.h"
|
||||||
|
|
@ -192,25 +193,25 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
&DeckEditorDeckDockWidget::applyActiveGroupCriteria);
|
&DeckEditorDeckDockWidget::applyActiveGroupCriteria);
|
||||||
|
|
||||||
aIncrement = new QAction(QString(), this);
|
aIncrement = new QAction(QString(), this);
|
||||||
aIncrement->setIcon(QPixmap("theme:icons/increment"));
|
aIncrement->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
||||||
connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection);
|
connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection);
|
||||||
auto *tbIncrement = new QToolButton(this);
|
auto *tbIncrement = new QToolButton(this);
|
||||||
tbIncrement->setDefaultAction(aIncrement);
|
tbIncrement->setDefaultAction(aIncrement);
|
||||||
|
|
||||||
aDecrement = new QAction(QString(), this);
|
aDecrement = new QAction(QString(), this);
|
||||||
aDecrement->setIcon(QPixmap("theme:icons/decrement"));
|
aDecrement->setIcon(themePixmap(QStringLiteral("icons/decrement")));
|
||||||
connect(aDecrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actDecrementSelection);
|
connect(aDecrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actDecrementSelection);
|
||||||
auto *tbDecrement = new QToolButton(this);
|
auto *tbDecrement = new QToolButton(this);
|
||||||
tbDecrement->setDefaultAction(aDecrement);
|
tbDecrement->setDefaultAction(aDecrement);
|
||||||
|
|
||||||
aRemoveCard = new QAction(QString(), this);
|
aRemoveCard = new QAction(QString(), this);
|
||||||
aRemoveCard->setIcon(QPixmap("theme:icons/remove_row"));
|
aRemoveCard->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
connect(aRemoveCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actRemoveCard);
|
connect(aRemoveCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actRemoveCard);
|
||||||
auto *tbRemoveCard = new QToolButton(this);
|
auto *tbRemoveCard = new QToolButton(this);
|
||||||
tbRemoveCard->setDefaultAction(aRemoveCard);
|
tbRemoveCard->setDefaultAction(aRemoveCard);
|
||||||
|
|
||||||
aSwapCard = new QAction(QString(), this);
|
aSwapCard = new QAction(QString(), this);
|
||||||
aSwapCard->setIcon(QPixmap("theme:icons/swap"));
|
aSwapCard->setIcon(themePixmap(QStringLiteral("icons/swap")));
|
||||||
connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection);
|
connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection);
|
||||||
auto *tbSwapCard = new QToolButton(this);
|
auto *tbSwapCard = new QToolButton(this);
|
||||||
tbSwapCard->setDefaultAction(aSwapCard);
|
tbSwapCard->setDefaultAction(aSwapCard);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
#include "../../../filters/filter_builder.h"
|
#include "../../../filters/filter_builder.h"
|
||||||
#include "../../../filters/filter_tree_model.h"
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
@ -42,11 +43,11 @@ void DeckEditorFilterDockWidget::createFiltersDock()
|
||||||
connect(filterBuilder, &FilterBuilder::add, filterModel, &FilterTreeModel::addFilter);
|
connect(filterBuilder, &FilterBuilder::add, filterModel, &FilterTreeModel::addFilter);
|
||||||
|
|
||||||
aClearFilterOne = new QAction(QString(), this);
|
aClearFilterOne = new QAction(QString(), this);
|
||||||
aClearFilterOne->setIcon(QPixmap("theme:icons/decrement"));
|
aClearFilterOne->setIcon(themePixmap(QStringLiteral("icons/decrement")));
|
||||||
connect(aClearFilterOne, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterOne);
|
connect(aClearFilterOne, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterOne);
|
||||||
|
|
||||||
aClearFilterAll = new QAction(QString(), this);
|
aClearFilterAll = new QAction(QString(), this);
|
||||||
aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch"));
|
aClearFilterAll->setIcon(themePixmap(QStringLiteral("icons/clearsearch")));
|
||||||
connect(aClearFilterAll, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterAll);
|
connect(aClearFilterAll, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterAll);
|
||||||
|
|
||||||
auto *filterDelOne = new QToolButton();
|
auto *filterDelOne = new QToolButton();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "deck_list_history_manager_widget.h"
|
#include "deck_list_history_manager_widget.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "deck_state_manager.h"
|
#include "deck_state_manager.h"
|
||||||
|
|
||||||
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager,
|
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager,
|
||||||
|
|
@ -10,7 +11,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de
|
||||||
layout = new QHBoxLayout(this);
|
layout = new QHBoxLayout(this);
|
||||||
|
|
||||||
aUndo = new QAction(QString(), this);
|
aUndo = new QAction(QString(), this);
|
||||||
aUndo->setIcon(QPixmap("theme:icons/arrow_undo"));
|
aUndo->setIcon(themePixmap(QStringLiteral("icons/arrow_undo")));
|
||||||
aUndo->setShortcut(QKeySequence::Undo);
|
aUndo->setShortcut(QKeySequence::Undo);
|
||||||
aUndo->setShortcutContext(Qt::ApplicationShortcut);
|
aUndo->setShortcutContext(Qt::ApplicationShortcut);
|
||||||
connect(aUndo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doUndo);
|
connect(aUndo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doUndo);
|
||||||
|
|
@ -19,7 +20,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de
|
||||||
undoButton->setDefaultAction(aUndo);
|
undoButton->setDefaultAction(aUndo);
|
||||||
|
|
||||||
aRedo = new QAction(QString(), this);
|
aRedo = new QAction(QString(), this);
|
||||||
aRedo->setIcon(QPixmap("theme:icons/arrow_redo"));
|
aRedo->setIcon(themePixmap(QStringLiteral("icons/arrow_redo")));
|
||||||
aRedo->setShortcut(QKeySequence::Redo);
|
aRedo->setShortcut(QKeySequence::Redo);
|
||||||
aRedo->setShortcutContext(Qt::ApplicationShortcut);
|
aRedo->setShortcutContext(Qt::ApplicationShortcut);
|
||||||
connect(aRedo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doRedo);
|
connect(aRedo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doRedo);
|
||||||
|
|
@ -31,7 +32,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de
|
||||||
layout->addWidget(redoButton);
|
layout->addWidget(redoButton);
|
||||||
|
|
||||||
historyButton = new SettingsButtonWidget(this);
|
historyButton = new SettingsButtonWidget(this);
|
||||||
historyButton->setButtonIcon(QPixmap("theme:icons/arrow_history"));
|
historyButton->setButtonIcon(themePixmap(QStringLiteral("icons/arrow_history")));
|
||||||
|
|
||||||
historyLabel = new QLabel(this);
|
historyLabel = new QLabel(this);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "dlg_connect.h"
|
#include "dlg_connect.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
@ -21,7 +22,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
|
||||||
previousHosts = new QComboBox(this);
|
previousHosts = new QComboBox(this);
|
||||||
|
|
||||||
btnDeleteServer = new QPushButton(this);
|
btnDeleteServer = new QPushButton(this);
|
||||||
btnDeleteServer->setIcon(QPixmap("theme:icons/remove_row"));
|
btnDeleteServer->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
|
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
|
||||||
btnDeleteServer->setFixedWidth(30);
|
btnDeleteServer->setFixedWidth(30);
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
|
||||||
|
|
||||||
hps = new HandlePublicServers(this);
|
hps = new HandlePublicServers(this);
|
||||||
btnRefreshServers = new QPushButton(this);
|
btnRefreshServers = new QPushButton(this);
|
||||||
btnRefreshServers->setIcon(QPixmap("theme:icons/sync"));
|
btnRefreshServers->setIcon(themePixmap(QStringLiteral("icons/sync")));
|
||||||
btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers"));
|
btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers"));
|
||||||
btnRefreshServers->setFixedWidth(30);
|
btnRefreshServers->setFixedWidth(30);
|
||||||
|
|
||||||
|
|
@ -99,7 +100,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
|
||||||
updateDisplayInfo(previousHosts->currentText());
|
updateDisplayInfo(previousHosts->currentText());
|
||||||
|
|
||||||
btnForgotPassword = new QPushButton(this);
|
btnForgotPassword = new QPushButton(this);
|
||||||
btnForgotPassword->setIcon(QPixmap("theme:icons/forgot_password"));
|
btnForgotPassword->setIcon(themePixmap(QStringLiteral("icons/forgot_password")));
|
||||||
btnForgotPassword->setToolTip(tr("Reset Password"));
|
btnForgotPassword->setToolTip(tr("Reset Password"));
|
||||||
btnForgotPassword->setFixedWidth(30);
|
btnForgotPassword->setFixedWidth(30);
|
||||||
connect(btnForgotPassword, &QPushButton::released, this, &DlgConnect::actForgotPassword);
|
connect(btnForgotPassword, &QPushButton::released, this, &DlgConnect::actForgotPassword);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "dlg_edit_tokens.h"
|
#include "dlg_edit_tokens.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/utility/get_text_with_max.h"
|
#include "../interface/widgets/utility/get_text_with_max.h"
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
@ -90,10 +91,10 @@ DlgEditTokens::DlgEditTokens(QWidget *parent) : QDialog(parent), currentCard(nul
|
||||||
&DlgEditTokens::tokenSelectionChanged);
|
&DlgEditTokens::tokenSelectionChanged);
|
||||||
|
|
||||||
QAction *aAddToken = new QAction(tr("Add token"), this);
|
QAction *aAddToken = new QAction(tr("Add token"), this);
|
||||||
aAddToken->setIcon(QPixmap("theme:icons/increment"));
|
aAddToken->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
||||||
connect(aAddToken, &QAction::triggered, this, &DlgEditTokens::actAddToken);
|
connect(aAddToken, &QAction::triggered, this, &DlgEditTokens::actAddToken);
|
||||||
QAction *aRemoveToken = new QAction(tr("Remove token"), this);
|
QAction *aRemoveToken = new QAction(tr("Remove token"), this);
|
||||||
aRemoveToken->setIcon(QPixmap("theme:icons/decrement"));
|
aRemoveToken->setIcon(themePixmap(QStringLiteral("icons/decrement")));
|
||||||
connect(aRemoveToken, &QAction::triggered, this, &DlgEditTokens::actRemoveToken);
|
connect(aRemoveToken, &QAction::triggered, this, &DlgEditTokens::actRemoveToken);
|
||||||
|
|
||||||
auto *databaseToolBar = new QToolBar;
|
auto *databaseToolBar = new QToolBar;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "dlg_manage_sets.h"
|
#include "dlg_manage_sets.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/card_picture_loader/card_picture_loader.h"
|
#include "../interface/card_picture_loader/card_picture_loader.h"
|
||||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||||
|
|
||||||
|
|
@ -35,28 +36,28 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent)
|
||||||
setsEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
setsEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
aTop = new QAction(QString(), this);
|
aTop = new QAction(QString(), this);
|
||||||
aTop->setIcon(QPixmap("theme:icons/arrow_top_green"));
|
aTop->setIcon(themePixmap(QStringLiteral("icons/arrow_top_green")));
|
||||||
aTop->setToolTip(tr("Move selected set to the top"));
|
aTop->setToolTip(tr("Move selected set to the top"));
|
||||||
aTop->setEnabled(false);
|
aTop->setEnabled(false);
|
||||||
connect(aTop, &QAction::triggered, this, &WndSets::actTop);
|
connect(aTop, &QAction::triggered, this, &WndSets::actTop);
|
||||||
setsEditToolBar->addAction(aTop);
|
setsEditToolBar->addAction(aTop);
|
||||||
|
|
||||||
aUp = new QAction(QString(), this);
|
aUp = new QAction(QString(), this);
|
||||||
aUp->setIcon(QPixmap("theme:icons/arrow_up_green"));
|
aUp->setIcon(themePixmap(QStringLiteral("icons/arrow_up_green")));
|
||||||
aUp->setToolTip(tr("Move selected set up"));
|
aUp->setToolTip(tr("Move selected set up"));
|
||||||
aUp->setEnabled(false);
|
aUp->setEnabled(false);
|
||||||
connect(aUp, &QAction::triggered, this, &WndSets::actUp);
|
connect(aUp, &QAction::triggered, this, &WndSets::actUp);
|
||||||
setsEditToolBar->addAction(aUp);
|
setsEditToolBar->addAction(aUp);
|
||||||
|
|
||||||
aDown = new QAction(QString(), this);
|
aDown = new QAction(QString(), this);
|
||||||
aDown->setIcon(QPixmap("theme:icons/arrow_down_green"));
|
aDown->setIcon(themePixmap(QStringLiteral("icons/arrow_down_green")));
|
||||||
aDown->setToolTip(tr("Move selected set down"));
|
aDown->setToolTip(tr("Move selected set down"));
|
||||||
aDown->setEnabled(false);
|
aDown->setEnabled(false);
|
||||||
connect(aDown, &QAction::triggered, this, &WndSets::actDown);
|
connect(aDown, &QAction::triggered, this, &WndSets::actDown);
|
||||||
setsEditToolBar->addAction(aDown);
|
setsEditToolBar->addAction(aDown);
|
||||||
|
|
||||||
aBottom = new QAction(QString(), this);
|
aBottom = new QAction(QString(), this);
|
||||||
aBottom->setIcon(QPixmap("theme:icons/arrow_bottom_green"));
|
aBottom->setIcon(themePixmap(QStringLiteral("icons/arrow_bottom_green")));
|
||||||
aBottom->setToolTip(tr("Move selected set to the bottom"));
|
aBottom->setToolTip(tr("Move selected set to the bottom"));
|
||||||
aBottom->setEnabled(false);
|
aBottom->setEnabled(false);
|
||||||
connect(aBottom, &QAction::triggered, this, &WndSets::actBottom);
|
connect(aBottom, &QAction::triggered, this, &WndSets::actBottom);
|
||||||
|
|
@ -66,7 +67,7 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent)
|
||||||
searchField = new LineEditUnfocusable;
|
searchField = new LineEditUnfocusable;
|
||||||
searchField->setObjectName("searchEdit");
|
searchField->setObjectName("searchEdit");
|
||||||
searchField->setPlaceholderText(tr("Search by set name, code, type, or release date"));
|
searchField->setPlaceholderText(tr("Search by set name, code, type, or release date"));
|
||||||
searchField->addAction(QPixmap("theme:icons/search"), LineEditUnfocusable::LeadingPosition);
|
searchField->addAction(themePixmap(QStringLiteral("icons/search")), LineEditUnfocusable::LeadingPosition);
|
||||||
searchField->setClearButtonEnabled(true);
|
searchField->setClearButtonEnabled(true);
|
||||||
setFocusProxy(searchField);
|
setFocusProxy(searchField);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "dlg_register.h"
|
#include "dlg_register.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../server/handle_public_servers.h"
|
#include "../server/handle_public_servers.h"
|
||||||
#include "../server/user/user_info_connection.h"
|
#include "../server/user/user_info_connection.h"
|
||||||
|
|
||||||
|
|
@ -24,7 +25,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
|
||||||
previousHosts = new QComboBox(this);
|
previousHosts = new QComboBox(this);
|
||||||
|
|
||||||
btnDeleteServer = new QPushButton(this);
|
btnDeleteServer = new QPushButton(this);
|
||||||
btnDeleteServer->setIcon(QPixmap("theme:icons/remove_row"));
|
btnDeleteServer->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
|
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
|
||||||
btnDeleteServer->setFixedWidth(30);
|
btnDeleteServer->setFixedWidth(30);
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
|
||||||
|
|
||||||
hps = new HandlePublicServers(this);
|
hps = new HandlePublicServers(this);
|
||||||
btnRefreshServers = new QPushButton(this);
|
btnRefreshServers = new QPushButton(this);
|
||||||
btnRefreshServers->setIcon(QPixmap("theme:icons/sync"));
|
btnRefreshServers->setIcon(themePixmap(QStringLiteral("icons/sync")));
|
||||||
btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers"));
|
btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers"));
|
||||||
btnRefreshServers->setFixedWidth(30);
|
btnRefreshServers->setFixedWidth(30);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "dlg_settings.h"
|
#include "dlg_settings.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../main.h"
|
#include "../main.h"
|
||||||
#include "../settings_page/appearance_settings_page.h"
|
#include "../settings_page/appearance_settings_page.h"
|
||||||
#include "../settings_page/deck_editor_settings_page.h"
|
#include "../settings_page/deck_editor_settings_page.h"
|
||||||
|
|
@ -96,7 +97,7 @@ void DlgSettings::setupUi()
|
||||||
// Search bar
|
// Search bar
|
||||||
searchEdit = new QLineEdit;
|
searchEdit = new QLineEdit;
|
||||||
searchEdit->setClearButtonEnabled(true);
|
searchEdit->setClearButtonEnabled(true);
|
||||||
searchEdit->addAction(QPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchEdit->addAction(themePixmap(QStringLiteral("icons/search")), QLineEdit::LeadingPosition);
|
||||||
searchEdit->installEventFilter(this);
|
searchEdit->installEventFilter(this);
|
||||||
connect(searchEdit, &QLineEdit::textChanged, this, &DlgSettings::onSearchTextChanged);
|
connect(searchEdit, &QLineEdit::textChanged, this, &DlgSettings::onSearchTextChanged);
|
||||||
|
|
||||||
|
|
@ -132,7 +133,7 @@ void DlgSettings::setupUi()
|
||||||
pagesWidget->addWidget(makeScrollable(userInterfacePage));
|
pagesWidget->addWidget(makeScrollable(userInterfacePage));
|
||||||
pagesWidget->addWidget(makeScrollable(deckEditorPage));
|
pagesWidget->addWidget(makeScrollable(deckEditorPage));
|
||||||
pagesWidget->addWidget(makeScrollable(storagePage));
|
pagesWidget->addWidget(makeScrollable(storagePage));
|
||||||
pagesWidget->addWidget(messagesPage);
|
pagesWidget->addWidget(makeScrollable(messagesPage));
|
||||||
pagesWidget->addWidget(soundPage);
|
pagesWidget->addWidget(soundPage);
|
||||||
pagesWidget->addWidget(shortcutsPage);
|
pagesWidget->addWidget(shortcutsPage);
|
||||||
|
|
||||||
|
|
@ -477,13 +478,13 @@ void DlgSettings::closeEvent(QCloseEvent *event)
|
||||||
case Invalid:
|
case Invalid:
|
||||||
loadErrorMessage = tr("Your card database is invalid.\n\n"
|
loadErrorMessage = tr("Your card database is invalid.\n\n"
|
||||||
"Cockatrice may not function correctly with an invalid database\n\n"
|
"Cockatrice may not function correctly with an invalid database\n\n"
|
||||||
"You may need to rerun oracle to update your card database.\n\n"
|
"You may need to rerun Oracle to update your card database.\n\n"
|
||||||
"Would you like to change your database location setting?");
|
"Would you like to change your database location setting?");
|
||||||
break;
|
break;
|
||||||
case VersionTooOld:
|
case VersionTooOld:
|
||||||
loadErrorMessage = tr("Your card database version is too old.\n\n"
|
loadErrorMessage = tr("Your card database version is too old.\n\n"
|
||||||
"This can cause problems loading card information or images\n\n"
|
"This can cause problems loading card information or images\n\n"
|
||||||
"Usually this can be fixed by rerunning oracle to to update your card database.\n\n"
|
"Usually this can be fixed by rerunning Oracle to to update your card database.\n\n"
|
||||||
"Would you like to change your database location setting?");
|
"Would you like to change your database location setting?");
|
||||||
break;
|
break;
|
||||||
case NotLoaded:
|
case NotLoaded:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../interface/widgets/tabs/tab_supervisor.h"
|
#include "../../../interface/widgets/tabs/tab_supervisor.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../../theme_manager.h"
|
#include "../../theme_manager.h"
|
||||||
#include "../../window_main.h"
|
#include "../../window_main.h"
|
||||||
#include "../cards/art_crop_attribution.h"
|
#include "../cards/art_crop_attribution.h"
|
||||||
|
|
@ -20,7 +21,8 @@
|
||||||
#include <libcockatrice/settings/paths_settings.h>
|
#include <libcockatrice/settings/paths_settings.h>
|
||||||
|
|
||||||
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
||||||
: QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice")
|
: QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home"))),
|
||||||
|
overlay(themePixmap(QStringLiteral("cockatrice")))
|
||||||
{
|
{
|
||||||
layout = new QGridLayout(this);
|
layout = new QGridLayout(this);
|
||||||
|
|
||||||
|
|
@ -56,6 +58,9 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
||||||
&HomeWidget::initializeBackgroundFromSource);
|
&HomeWidget::initializeBackgroundFromSource);
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
|
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
|
||||||
&HomeWidget::updateButtonsToBackgroundColor);
|
&HomeWidget::updateButtonsToBackgroundColor);
|
||||||
|
// Scheme flips (light/dark/system with an OS switch) fire on themeManager,
|
||||||
|
// not on SettingsCache::themeChanged, so re-resolve the variant background.
|
||||||
|
connect(themeManager, &ThemeManager::themeChanged, this, &HomeWidget::initializeBackgroundFromSource);
|
||||||
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabButtonColorChanged, this,
|
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabButtonColorChanged, this,
|
||||||
&HomeWidget::updateButtonsToBackgroundColor);
|
&HomeWidget::updateButtonsToBackgroundColor);
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +79,7 @@ void HomeWidget::initializeBackgroundFromSource()
|
||||||
switch (backgroundSourceType) {
|
switch (backgroundSourceType) {
|
||||||
case BackgroundSources::Theme:
|
case BackgroundSources::Theme:
|
||||||
cardChangeTimer->stop();
|
cardChangeTimer->stop();
|
||||||
background = QPixmap("theme:backgrounds/home");
|
background = themePixmap(QStringLiteral("backgrounds/home"));
|
||||||
backgroundSourceDeck = DeckList();
|
backgroundSourceDeck = DeckList();
|
||||||
backgroundSourceCard->setCard(ExactCard());
|
backgroundSourceCard->setCard(ExactCard());
|
||||||
updateButtonsToBackgroundColor();
|
updateButtonsToBackgroundColor();
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,13 @@ void FirstRunWizard::onCardDatabaseUpdateFinished(bool success)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FirstRunWizard::onCardDatabaseUpdateProgress(const QString &stage, qint64 done, qint64 total)
|
||||||
|
{
|
||||||
|
if (cardDatabasePage) {
|
||||||
|
cardDatabasePage->onUpdateProgress(stage, done, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FirstRunWizard::finish()
|
void FirstRunWizard::finish()
|
||||||
{
|
{
|
||||||
accept();
|
accept();
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ public slots:
|
||||||
/** @brief Forwarded from MainWindow once the background card database update process exits. */
|
/** @brief Forwarded from MainWindow once the background card database update process exits. */
|
||||||
void onCardDatabaseUpdateFinished(bool success);
|
void onCardDatabaseUpdateFinished(bool success);
|
||||||
|
|
||||||
|
/** @brief Forwarded from MainWindow while the background card database update process runs. */
|
||||||
|
void onCardDatabaseUpdateProgress(const QString &stage, qint64 done, qint64 total);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void changeEvent(QEvent *event) override;
|
void changeEvent(QEvent *event) override;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <climits>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
#include <libcockatrice/settings/updates_settings.h>
|
#include <libcockatrice/settings/updates_settings.h>
|
||||||
|
|
||||||
|
|
@ -179,6 +180,25 @@ void CardDatabaseSetupPage::onUpdateFinished(bool success)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CardDatabaseSetupPage::onUpdateProgress(const QString &stage, qint64 done, qint64 total)
|
||||||
|
{
|
||||||
|
if (state != State::Running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressBar->setRange(0, total > 0 ? static_cast<int>(qMin<qint64>(total, INT_MAX)) : 0);
|
||||||
|
progressBar->setValue(static_cast<int>(qMin<qint64>(done, INT_MAX)));
|
||||||
|
if (total > 0) {
|
||||||
|
const int percent = static_cast<int>((100.0 * done) / total);
|
||||||
|
if (stage == QLatin1String("download")) {
|
||||||
|
statusLabel->setText(tr("Downloading the card database (%1%)…").arg(percent));
|
||||||
|
} else if (stage == QLatin1String("scan")) {
|
||||||
|
statusLabel->setText(tr("Parsing the card database (%1%)…").arg(percent));
|
||||||
|
} else if (stage == QLatin1String("import")) {
|
||||||
|
statusLabel->setText(tr("Importing cards (%1%)…").arg(percent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString CardDatabaseSetupPage::nextButtonText() const
|
QString CardDatabaseSetupPage::nextButtonText() const
|
||||||
{
|
{
|
||||||
return state == State::NotStarted ? tr("Download") : QString();
|
return state == State::NotStarted ? tr("Download") : QString();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ public:
|
||||||
void retranslateUi() override;
|
void retranslateUi() override;
|
||||||
|
|
||||||
void onUpdateFinished(bool success);
|
void onUpdateFinished(bool success);
|
||||||
|
void onUpdateProgress(const QString &stage, qint64 done, qint64 total);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateRequested();
|
void updateRequested();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../card_picture_loader/card_picture_loader.h"
|
#include "../../card_picture_loader/card_picture_loader.h"
|
||||||
#include "../cards/art_crop_attribution.h"
|
#include "../cards/art_crop_attribution.h"
|
||||||
|
#include "../cards/card_art_utils.h"
|
||||||
#include "../utility/completer_utils.h"
|
#include "../utility/completer_utils.h"
|
||||||
#include "card_database_display_model.h"
|
#include "card_database_display_model.h"
|
||||||
#include "card_database_model.h"
|
#include "card_database_model.h"
|
||||||
|
|
@ -276,7 +277,7 @@ void PlaymatSettingsDialog::reloadPreview()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPixmap = fullRes;
|
currentPixmap = CardArtUtils::rotateSidewaysLayoutArt(fullRes, card);
|
||||||
preview->setPixmap(currentPixmap);
|
preview->setPixmap(currentPixmap);
|
||||||
preview->setParams(currentParams);
|
preview->setParams(currentParams);
|
||||||
preview->setAttribution(buildArtAttribution(card));
|
preview->setAttribution(buildArtAttribution(card));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "settings_button_widget.h"
|
#include "settings_button_widget.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
@ -8,7 +10,7 @@
|
||||||
SettingsButtonWidget::SettingsButtonWidget(QWidget *parent)
|
SettingsButtonWidget::SettingsButtonWidget(QWidget *parent)
|
||||||
: QWidget(parent), button(new QToolButton(this)), popup(new SettingsPopupWidget(nullptr))
|
: QWidget(parent), button(new QToolButton(this)), popup(new SettingsPopupWidget(nullptr))
|
||||||
{
|
{
|
||||||
button->setIcon(QPixmap("theme:icons/cogwheel"));
|
button->setIcon(themePixmap(QStringLiteral("icons/cogwheel")));
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setFixedSize(32, 32);
|
button->setFixedSize(32, 32);
|
||||||
connect(button, &QToolButton::clicked, this, &SettingsButtonWidget::togglePopup);
|
connect(button, &QToolButton::clicked, this, &SettingsButtonWidget::togglePopup);
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,10 @@ void ReplayManager::processNewEvents(PlaybackMode playbackMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// backwards skip => always skip tap animation
|
// backwards skip => always skip tap animation
|
||||||
|
// backwards skip => always skip damage animation (battlefield shimmer / life counter flash)
|
||||||
if (playbackMode == BACKWARD_SKIP) {
|
if (playbackMode == BACKWARD_SKIP) {
|
||||||
options |= SKIP_TAP_ANIMATION;
|
options |= SKIP_TAP_ANIMATION;
|
||||||
|
options |= SKIP_DAMAGE_ANIMATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit eventReplayed(replay->event_list(currentEvent), options);
|
emit eventReplayed(replay->event_list(currentEvent), options);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/tabs/tab_game.h"
|
#include "../interface/widgets/tabs/tab_game.h"
|
||||||
#include "replay_manager.h"
|
#include "replay_manager.h"
|
||||||
#include "replay_quick_settings_widget.h"
|
#include "replay_quick_settings_widget.h"
|
||||||
|
|
@ -50,15 +51,15 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay)
|
||||||
replayPlayButton = new QToolButton;
|
replayPlayButton = new QToolButton;
|
||||||
replayPlayButton->setIconSize(QSize(32, 32));
|
replayPlayButton->setIconSize(QSize(32, 32));
|
||||||
QIcon playButtonIcon = QIcon();
|
QIcon playButtonIcon = QIcon();
|
||||||
playButtonIcon.addPixmap(QPixmap("theme:replay/start"), QIcon::Normal, QIcon::Off);
|
playButtonIcon.addPixmap(themePixmap(QStringLiteral("replay/start")), QIcon::Normal, QIcon::Off);
|
||||||
playButtonIcon.addPixmap(QPixmap("theme:replay/pause"), QIcon::Normal, QIcon::On);
|
playButtonIcon.addPixmap(themePixmap(QStringLiteral("replay/pause")), QIcon::Normal, QIcon::On);
|
||||||
replayPlayButton->setIcon(playButtonIcon);
|
replayPlayButton->setIcon(playButtonIcon);
|
||||||
replayPlayButton->setCheckable(true);
|
replayPlayButton->setCheckable(true);
|
||||||
connect(replayPlayButton, &QToolButton::toggled, this, &ReplayWidget::replayPlayButtonToggled);
|
connect(replayPlayButton, &QToolButton::toggled, this, &ReplayWidget::replayPlayButtonToggled);
|
||||||
|
|
||||||
replayFastForwardButton = new QToolButton;
|
replayFastForwardButton = new QToolButton;
|
||||||
replayFastForwardButton->setIconSize(QSize(32, 32));
|
replayFastForwardButton->setIconSize(QSize(32, 32));
|
||||||
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
|
replayFastForwardButton->setIcon(themePixmap(QStringLiteral("replay/fastforward")));
|
||||||
replayFastForwardButton->setCheckable(true);
|
replayFastForwardButton->setCheckable(true);
|
||||||
connect(replayFastForwardButton, &QToolButton::toggled, this, &ReplayWidget::updateTimeScaleFactor);
|
connect(replayFastForwardButton, &QToolButton::toggled, this, &ReplayWidget::updateTimeScaleFactor);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "game_selector.h"
|
#include "game_selector.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/dialogs/dlg_create_game.h"
|
#include "../interface/widgets/dialogs/dlg_create_game.h"
|
||||||
#include "../interface/widgets/dialogs/dlg_filter_games.h"
|
#include "../interface/widgets/dialogs/dlg_filter_games.h"
|
||||||
#include "../interface/widgets/tabs/tab_account.h"
|
#include "../interface/widgets/tabs/tab_account.h"
|
||||||
|
|
@ -95,10 +96,10 @@ GameSelector::GameSelector(AbstractClient *_client,
|
||||||
}
|
}
|
||||||
|
|
||||||
filterButton = new QPushButton;
|
filterButton = new QPushButton;
|
||||||
filterButton->setIcon(QPixmap("theme:icons/search"));
|
filterButton->setIcon(themePixmap(QStringLiteral("icons/search")));
|
||||||
connect(filterButton, &QPushButton::clicked, this, &GameSelector::actSetFilter);
|
connect(filterButton, &QPushButton::clicked, this, &GameSelector::actSetFilter);
|
||||||
clearFilterButton = new QPushButton;
|
clearFilterButton = new QPushButton;
|
||||||
clearFilterButton->setIcon(QPixmap("theme:icons/clearsearch"));
|
clearFilterButton->setIcon(themePixmap(QStringLiteral("icons/clearsearch")));
|
||||||
bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults();
|
bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults();
|
||||||
clearFilterButton->setEnabled(!filtersSetToDefault);
|
clearFilterButton->setEnabled(!filtersSetToDefault);
|
||||||
connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter);
|
connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "remote_replay_list_tree_widget.h"
|
#include "remote_replay_list_tree_widget.h"
|
||||||
|
|
||||||
|
#include "../../../pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QFileIconProvider>
|
#include <QFileIconProvider>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
@ -37,7 +39,7 @@ RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client,
|
||||||
QFileIconProvider fip;
|
QFileIconProvider fip;
|
||||||
dirIcon = fip.icon(QFileIconProvider::Folder);
|
dirIcon = fip.icon(QFileIconProvider::Folder);
|
||||||
fileIcon = fip.icon(QFileIconProvider::File);
|
fileIcon = fip.icon(QFileIconProvider::File);
|
||||||
lockIcon = QPixmap("theme:icons/lock");
|
lockIcon = themePixmap(QStringLiteral("icons/lock"));
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel()
|
RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "user_card_art_provider.h"
|
#include "user_card_art_provider.h"
|
||||||
|
|
||||||
#include "../../../card_picture_loader/card_picture_loader.h"
|
#include "../../../card_picture_loader/card_picture_loader.h"
|
||||||
|
#include "../../cards/card_art_utils.h"
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
|
|
@ -52,16 +53,25 @@ void UserCardArtProvider::requestCardArt(const QString &userName, const QString
|
||||||
processQueue();
|
processQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap UserCardArtProvider::cropCardArt(const QPixmap &fullRes)
|
QPixmap UserCardArtProvider::cropCardArt(const QPixmap &fullRes, const ExactCard &card)
|
||||||
{
|
{
|
||||||
const QSize sz = fullRes.size();
|
QPixmap source = fullRes;
|
||||||
|
|
||||||
|
// Sideways-layout cards (plane, siege/battle, split) store their landscape
|
||||||
|
// artwork rotated 90° inside a portrait frame. Rotate it upright first so
|
||||||
|
// the crop below lands on the horizontal art, mirroring the way
|
||||||
|
// CardInfoPictureWidget displays these cards.
|
||||||
|
const bool landscape = card.getInfo().getUiAttributes().landscapeOrientation;
|
||||||
|
source = CardArtUtils::rotateSidewaysLayoutArt(source, card);
|
||||||
|
|
||||||
|
const QSize sz = source.size();
|
||||||
const int marginX = sz.width() * 0.07;
|
const int marginX = sz.width() * 0.07;
|
||||||
const int topMargin = sz.height() * 0.11;
|
const int topMargin = landscape ? sz.height() * 0.05 : sz.height() * 0.11;
|
||||||
const int bottomMargin = sz.height() * 0.45;
|
const int bottomMargin = landscape ? sz.height() * 0.42 : sz.height() * 0.45;
|
||||||
|
|
||||||
const QRect foilRect(marginX, topMargin, sz.width() - 2 * marginX, sz.height() - topMargin - bottomMargin);
|
const QRect artRect(marginX, topMargin, sz.width() - 2 * marginX, sz.height() - topMargin - bottomMargin);
|
||||||
|
|
||||||
return fullRes.copy(foilRect.intersected(fullRes.rect()));
|
return source.copy(artRect.intersected(source.rect()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserCardArtProvider::insertIntoCache(const QString &key, const QPixmap &pixmap)
|
void UserCardArtProvider::insertIntoCache(const QString &key, const QPixmap &pixmap)
|
||||||
|
|
@ -111,7 +121,7 @@ void UserCardArtProvider::processQueue()
|
||||||
|
|
||||||
// Synchronous hit (already loaded/on disk)
|
// Synchronous hit (already loaded/on disk)
|
||||||
if (!fullRes.isNull()) {
|
if (!fullRes.isNull()) {
|
||||||
insertIntoCache(key, cropCardArt(fullRes));
|
insertIntoCache(key, cropCardArt(fullRes, card));
|
||||||
pending.remove(key);
|
pending.remove(key);
|
||||||
|
|
||||||
emit cardArtUpdated(userName);
|
emit cardArtUpdated(userName);
|
||||||
|
|
@ -135,7 +145,7 @@ void UserCardArtProvider::processQueue()
|
||||||
CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040));
|
CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040));
|
||||||
|
|
||||||
if (!fullRes.isNull()) {
|
if (!fullRes.isNull()) {
|
||||||
self->insertIntoCache(key, self->cropCardArt(fullRes));
|
self->insertIntoCache(key, self->cropCardArt(fullRes, card));
|
||||||
}
|
}
|
||||||
|
|
||||||
self->pending.remove(key);
|
self->pending.remove(key);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <libcockatrice/card/printing/exact_card.h>
|
||||||
|
|
||||||
class UserCardArtProvider : public QObject
|
class UserCardArtProvider : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +17,7 @@ public:
|
||||||
|
|
||||||
void requestCardArt(const QString &userName, const QString &cardName, const QString &providerId);
|
void requestCardArt(const QString &userName, const QString &cardName, const QString &providerId);
|
||||||
const QMap<QString, QPixmap> &cache() const;
|
const QMap<QString, QPixmap> &cache() const;
|
||||||
static QPixmap cropCardArt(const QPixmap &fullRes);
|
static QPixmap cropCardArt(const QPixmap &fullRes, const ExactCard &card);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void cardArtUpdated(const QString &userName);
|
void cardArtUpdated(const QString &userName);
|
||||||
|
|
|
||||||
|
|
@ -560,7 +560,7 @@ void UserCardArtSettingsDialog::reloadPreview()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPixmap = UserCardArtProvider::cropCardArt(fullRes);
|
currentPixmap = UserCardArtProvider::cropCardArt(fullRes, card);
|
||||||
preview->setPixmap(currentPixmap);
|
preview->setPixmap(currentPixmap);
|
||||||
preview->setParams(currentParams);
|
preview->setParams(currentParams);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent,
|
||||||
aDemoteFromMod = new QAction(QString(), this);
|
aDemoteFromMod = new QAction(QString(), this);
|
||||||
aPromoteToJudge = new QAction(QString(), this);
|
aPromoteToJudge = new QAction(QString(), this);
|
||||||
aDemoteFromJudge = new QAction(QString(), this);
|
aDemoteFromJudge = new QAction(QString(), this);
|
||||||
|
aPromoteToDeveloper = new QAction(QString(), this);
|
||||||
|
aDemoteFromDeveloper = new QAction(QString(), this);
|
||||||
aGetAdminNotes = new QAction(QString(), this);
|
aGetAdminNotes = new QAction(QString(), this);
|
||||||
aInvestigateUser = new QAction(QString(), this);
|
aInvestigateUser = new QAction(QString(), this);
|
||||||
|
|
||||||
|
|
@ -76,6 +78,8 @@ void UserContextMenu::retranslateUi()
|
||||||
aDemoteFromMod->setText(tr("Dem&ote user from moderator"));
|
aDemoteFromMod->setText(tr("Dem&ote user from moderator"));
|
||||||
aPromoteToJudge->setText(tr("Promote user to &judge"));
|
aPromoteToJudge->setText(tr("Promote user to &judge"));
|
||||||
aDemoteFromJudge->setText(tr("Demote user from judge"));
|
aDemoteFromJudge->setText(tr("Demote user from judge"));
|
||||||
|
aPromoteToDeveloper->setText(tr("Promote user to &developer"));
|
||||||
|
aDemoteFromDeveloper->setText(tr("Demote user from de&veloper"));
|
||||||
aGetAdminNotes->setText(tr("View admin notes"));
|
aGetAdminNotes->setText(tr("View admin notes"));
|
||||||
aInvestigateUser->setText(tr("Investigate user"));
|
aInvestigateUser->setText(tr("Investigate user"));
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +272,7 @@ void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const
|
||||||
const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext);
|
const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext);
|
||||||
|
|
||||||
if (resp.response_code() == Response::RespOk) {
|
if (resp.response_code() == Response::RespOk) {
|
||||||
if (cmd.should_be_mod() || cmd.should_be_judge()) {
|
if (cmd.should_be_mod() || cmd.should_be_judge() || cmd.should_be_developer()) {
|
||||||
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Success"),
|
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Success"),
|
||||||
tr("Successfully promoted user."));
|
tr("Successfully promoted user."));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -276,7 +280,7 @@ void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (cmd.should_be_mod() || cmd.should_be_judge()) {
|
if (cmd.should_be_mod() || cmd.should_be_judge() || cmd.should_be_developer()) {
|
||||||
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to promote user."));
|
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to promote user."));
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to demote user."));
|
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to demote user."));
|
||||||
|
|
@ -437,9 +441,18 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
|
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
|
||||||
menu->addAction(aPromoteToJudge);
|
menu->addAction(aPromoteToJudge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userLevel.testFlag(ServerInfo_User::IsDeveloper) &&
|
||||||
|
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
|
||||||
|
menu->addAction(aDemoteFromDeveloper);
|
||||||
|
|
||||||
|
} else if (userLevel.testFlag(ServerInfo_User::IsRegistered) &&
|
||||||
|
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
|
||||||
|
menu->addAction(aPromoteToDeveloper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
aDetails->setEnabled(true);
|
aDetails->setEnabled(true);
|
||||||
aChat->setEnabled(anotherUser && online);
|
aChat->setEnabled(anotherUser && online && !userListProxy->isUserIgnored(userName));
|
||||||
aShowGames->setEnabled(online);
|
aShowGames->setEnabled(online);
|
||||||
aReport->setEnabled(anotherUser);
|
aReport->setEnabled(anotherUser);
|
||||||
aAddToBuddyList->setEnabled(anotherUser);
|
aAddToBuddyList->setEnabled(anotherUser);
|
||||||
|
|
@ -455,6 +468,10 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
aInvestigateUser->setEnabled(anotherUser);
|
aInvestigateUser->setEnabled(anotherUser);
|
||||||
aPromoteToMod->setEnabled(anotherUser);
|
aPromoteToMod->setEnabled(anotherUser);
|
||||||
aDemoteFromMod->setEnabled(anotherUser);
|
aDemoteFromMod->setEnabled(anotherUser);
|
||||||
|
aPromoteToJudge->setEnabled(anotherUser);
|
||||||
|
aDemoteFromJudge->setEnabled(anotherUser);
|
||||||
|
aPromoteToDeveloper->setEnabled(anotherUser);
|
||||||
|
aDemoteFromDeveloper->setEnabled(anotherUser);
|
||||||
|
|
||||||
QAction *actionClicked = menu->exec(pos);
|
QAction *actionClicked = menu->exec(pos);
|
||||||
if (actionClicked == nullptr) {
|
if (actionClicked == nullptr) {
|
||||||
|
|
@ -489,6 +506,8 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
execAdjustMod(userName, actionClicked == aPromoteToMod);
|
execAdjustMod(userName, actionClicked == aPromoteToMod);
|
||||||
} else if (actionClicked == aPromoteToJudge || actionClicked == aDemoteFromJudge) {
|
} else if (actionClicked == aPromoteToJudge || actionClicked == aDemoteFromJudge) {
|
||||||
execAdjustJudge(userName, actionClicked == aPromoteToJudge);
|
execAdjustJudge(userName, actionClicked == aPromoteToJudge);
|
||||||
|
} else if (actionClicked == aPromoteToDeveloper || actionClicked == aDemoteFromDeveloper) {
|
||||||
|
execAdjustDeveloper(userName, actionClicked == aPromoteToDeveloper);
|
||||||
} else if (actionClicked == aBanHistory) {
|
} else if (actionClicked == aBanHistory) {
|
||||||
execBanHistory(userName);
|
execBanHistory(userName);
|
||||||
} else if (actionClicked == aWarnUser) {
|
} else if (actionClicked == aWarnUser) {
|
||||||
|
|
@ -606,7 +625,15 @@ void UserContextMenu::execAddToIgnore(const QString &userName)
|
||||||
Command_AddToList cmd;
|
Command_AddToList cmd;
|
||||||
cmd.set_list("ignore");
|
cmd.set_list("ignore");
|
||||||
cmd.set_user_name(userName.toStdString());
|
cmd.set_user_name(userName.toStdString());
|
||||||
client->sendCommand(client->prepareSessionCommand(cmd));
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this,
|
||||||
|
[this, userName](const Response &response, const CommandContainer &, const QVariant &) {
|
||||||
|
if (response.response_code() == Response::RespOk) {
|
||||||
|
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Ignore list"),
|
||||||
|
tr("%1 has been added to your ignore list.").arg(userName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client->sendCommand(pend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserContextMenu::execRemoveFromIgnore(const QString &userName)
|
void UserContextMenu::execRemoveFromIgnore(const QString &userName)
|
||||||
|
|
@ -698,4 +725,14 @@ void UserContextMenu::execAdjustJudge(const QString &userName, bool shouldBeJudg
|
||||||
PendingCommand *pend = client->prepareAdminCommand(cmd);
|
PendingCommand *pend = client->prepareAdminCommand(cmd);
|
||||||
connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse);
|
connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse);
|
||||||
client->sendCommand(pend);
|
client->sendCommand(pend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserContextMenu::execAdjustDeveloper(const QString &userName, bool shouldBeDeveloper)
|
||||||
|
{
|
||||||
|
Command_AdjustMod cmd;
|
||||||
|
cmd.set_user_name(userName.toStdString());
|
||||||
|
cmd.set_should_be_developer(shouldBeDeveloper);
|
||||||
|
PendingCommand *pend = client->prepareAdminCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ private:
|
||||||
QAction *aBan, *aBanHistory;
|
QAction *aBan, *aBanHistory;
|
||||||
QAction *aPromoteToMod, *aDemoteFromMod;
|
QAction *aPromoteToMod, *aDemoteFromMod;
|
||||||
QAction *aPromoteToJudge, *aDemoteFromJudge;
|
QAction *aPromoteToJudge, *aDemoteFromJudge;
|
||||||
|
QAction *aPromoteToDeveloper, *aDemoteFromDeveloper;
|
||||||
QAction *aWarnUser, *aWarnHistory;
|
QAction *aWarnUser, *aWarnHistory;
|
||||||
QAction *aGetAdminNotes;
|
QAction *aGetAdminNotes;
|
||||||
std::function<QList<GameInviteOption>()> gameInviteLinkProvider;
|
std::function<QList<GameInviteOption>()> gameInviteLinkProvider;
|
||||||
|
|
@ -123,6 +124,7 @@ public:
|
||||||
void execInvestigateUser(const QString &userName);
|
void execInvestigateUser(const QString &userName);
|
||||||
void execAdjustMod(const QString &userName, bool shouldBeMod);
|
void execAdjustMod(const QString &userName, bool shouldBeMod);
|
||||||
void execAdjustJudge(const QString &userName, bool shouldBeJudge);
|
void execAdjustJudge(const QString &userName, bool shouldBeJudge);
|
||||||
|
void execAdjustDeveloper(const QString &userName, bool shouldBeDeveloper);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void execInvite(const QString &userName, const GameInviteOption &option);
|
void execInvite(const QString &userName, const GameInviteOption &option);
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ void UserInfoBox::updateInfo(const ServerInfo_User &user)
|
||||||
QString userLevelText;
|
QString userLevelText;
|
||||||
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
userLevelText = tr("Administrator");
|
userLevelText = tr("Administrator");
|
||||||
|
} else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
userLevelText = tr("Developer");
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
userLevelText = tr("Moderator");
|
userLevelText = tr("Moderator");
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsRegistered)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsRegistered)) {
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,9 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *)
|
||||||
if (level.testFlag(ServerInfo_User::IsAdmin)) {
|
if (level.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
return QColor(245, 158, 11);
|
return QColor(245, 158, 11);
|
||||||
}
|
}
|
||||||
|
if (level.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
return QColor(185, 28, 28);
|
||||||
|
}
|
||||||
if (level.testFlag(ServerInfo_User::IsModerator)) {
|
if (level.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
return QColor(59, 130, 246);
|
return QColor(59, 130, 246);
|
||||||
}
|
}
|
||||||
|
|
@ -300,6 +303,8 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *)
|
||||||
} badge;
|
} badge;
|
||||||
if (level.testFlag(ServerInfo_User::IsAdmin)) {
|
if (level.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
badge = {"ADMIN", QColor(245, 158, 11)};
|
badge = {"ADMIN", QColor(245, 158, 11)};
|
||||||
|
} else if (level.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
badge = {"DEV", QColor(185, 28, 28)};
|
||||||
} else if (level.testFlag(ServerInfo_User::IsModerator)) {
|
} else if (level.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
badge = {"MOD", QColor(59, 130, 246)};
|
badge = {"MOD", QColor(59, 130, 246)};
|
||||||
} else if (level.testFlag(ServerInfo_User::IsJudge)) {
|
} else if (level.testFlag(ServerInfo_User::IsJudge)) {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ QColor UserListPainter::getAccentColor(const UserLevelFlags &userLevel, bool onl
|
||||||
|
|
||||||
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
accentColor = QColor(245, 158, 11);
|
accentColor = QColor(245, 158, 11);
|
||||||
|
} else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
accentColor = QColor(185, 28, 28);
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
accentColor = QColor(59, 130, 246);
|
accentColor = QColor(59, 130, 246);
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsJudge)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsJudge)) {
|
||||||
|
|
@ -299,6 +301,8 @@ QList<UserListPainter::Badge> UserListPainter::buildBadges(const UserLevelFlags
|
||||||
|
|
||||||
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
if (userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
badges << Badge{"ADMIN", QColor(245, 158, 11)};
|
badges << Badge{"ADMIN", QColor(245, 158, 11)};
|
||||||
|
} else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) {
|
||||||
|
badges << Badge{"DEV", QColor(185, 28, 28)};
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsModerator)) {
|
||||||
badges << Badge{"MOD", QColor(59, 130, 246)};
|
badges << Badge{"MOD", QColor(59, 130, 246)};
|
||||||
} else if (userLevel.testFlag(ServerInfo_User::IsJudge)) {
|
} else if (userLevel.testFlag(ServerInfo_User::IsJudge)) {
|
||||||
|
|
@ -385,9 +389,9 @@ void UserListPainter::paint(QPainter *painter,
|
||||||
const QString userName = QString::fromStdString(userInfo.name());
|
const QString userName = QString::fromStdString(userInfo.name());
|
||||||
const QString privLevel = QString::fromStdString(userInfo.privlevel());
|
const QString privLevel = QString::fromStdString(userInfo.privlevel());
|
||||||
const QColor accentColor = getAccentColor(userLevel, online);
|
const QColor accentColor = getAccentColor(userLevel, online);
|
||||||
const bool hasRole = userLevel.testFlag(ServerInfo_User::IsAdmin) ||
|
const bool hasRole =
|
||||||
userLevel.testFlag(ServerInfo_User::IsModerator) ||
|
userLevel.testFlag(ServerInfo_User::IsAdmin) || userLevel.testFlag(ServerInfo_User::IsDeveloper) ||
|
||||||
userLevel.testFlag(ServerInfo_User::IsJudge);
|
userLevel.testFlag(ServerInfo_User::IsModerator) || userLevel.testFlag(ServerInfo_User::IsJudge);
|
||||||
const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2);
|
const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2);
|
||||||
const int cardRight = getCardRight(option, rect);
|
const int cardRight = getCardRight(option, rect);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,9 +234,11 @@ bool UserListTWI::operator<(const QTreeWidgetItem &other) const
|
||||||
const auto &lhsUserLevelFlags = UserLevelFlags(data(0, Qt::UserRole).toInt());
|
const auto &lhsUserLevelFlags = UserLevelFlags(data(0, Qt::UserRole).toInt());
|
||||||
const auto &rhsUserLevelFlags = UserLevelFlags(other.data(0, Qt::UserRole).toInt());
|
const auto &rhsUserLevelFlags = UserLevelFlags(other.data(0, Qt::UserRole).toInt());
|
||||||
|
|
||||||
// Admins & Mods need no additional comparison checks, just to see if they're an admin or a moderator
|
// Admins, Developers & Mods need no additional comparison checks, just to see if they're an admin, a developer
|
||||||
|
// or a moderator
|
||||||
static const QList<ServerInfo_User_UserLevelFlag> userLevelWithNoOtherPrefOrder = {
|
static const QList<ServerInfo_User_UserLevelFlag> userLevelWithNoOtherPrefOrder = {
|
||||||
ServerInfo_User_UserLevelFlag_IsAdmin, ServerInfo_User_UserLevelFlag_IsModerator};
|
ServerInfo_User_UserLevelFlag_IsAdmin, ServerInfo_User_UserLevelFlag_IsDeveloper,
|
||||||
|
ServerInfo_User_UserLevelFlag_IsModerator};
|
||||||
for (const auto &userLevelEntry : userLevelWithNoOtherPrefOrder) {
|
for (const auto &userLevelEntry : userLevelWithNoOtherPrefOrder) {
|
||||||
if (lhsUserLevelFlags.testFlag(userLevelEntry) &&
|
if (lhsUserLevelFlags.testFlag(userLevelEntry) &&
|
||||||
lhsUserLevelFlags.testFlag(userLevelEntry) == rhsUserLevelFlags.testFlag(userLevelEntry)) {
|
lhsUserLevelFlags.testFlag(userLevelEntry) == rhsUserLevelFlags.testFlag(userLevelEntry)) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "deck_editor_settings_page.h"
|
#include "deck_editor_settings_page.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "update/card_spoiler/spoiler_background_updater.h"
|
#include "update/card_spoiler/spoiler_background_updater.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
@ -53,15 +54,15 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
|
||||||
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
|
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
|
||||||
|
|
||||||
aAdd = new QAction(this);
|
aAdd = new QAction(this);
|
||||||
aAdd->setIcon(QPixmap("theme:icons/increment"));
|
aAdd->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
||||||
connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL);
|
connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL);
|
||||||
|
|
||||||
aEdit = new QAction(this);
|
aEdit = new QAction(this);
|
||||||
aEdit->setIcon(QPixmap("theme:icons/pencil"));
|
aEdit->setIcon(themePixmap(QStringLiteral("icons/pencil")));
|
||||||
connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL);
|
connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL);
|
||||||
|
|
||||||
aRemove = new QAction(this);
|
aRemove = new QAction(this);
|
||||||
aRemove->setIcon(QPixmap("theme:icons/decrement"));
|
aRemove->setIcon(themePixmap(QStringLiteral("icons/decrement")));
|
||||||
connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL);
|
connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL);
|
||||||
|
|
||||||
auto *urlToolBar = new QToolBar;
|
auto *urlToolBar = new QToolBar;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "messages_settings_page.h"
|
#include "messages_settings_page.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/utility/get_text_with_max.h"
|
#include "../interface/widgets/utility/get_text_with_max.h"
|
||||||
|
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
|
@ -59,6 +60,10 @@ MessagesSettingsPage::MessagesSettingsPage()
|
||||||
connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().chat(),
|
connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().chat(),
|
||||||
&ChatSettings::setRoomHistory);
|
&ChatSettings::setRoomHistory);
|
||||||
|
|
||||||
|
ignoreAllPrivateMessagesCheckBox.setChecked(SettingsCache::instance().chat().getIgnoreAllPrivateMessages());
|
||||||
|
connect(&ignoreAllPrivateMessagesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().chat(),
|
||||||
|
&ChatSettings::setIgnoreAllPrivateMessages);
|
||||||
|
|
||||||
customAlertString = new QLineEdit();
|
customAlertString = new QLineEdit();
|
||||||
customAlertString->setText(SettingsCache::instance().chat().getHighlightWords());
|
customAlertString->setText(SettingsCache::instance().chat().getHighlightWords());
|
||||||
connect(customAlertString, &QLineEdit::textChanged, &SettingsCache::instance().chat(),
|
connect(customAlertString, &QLineEdit::textChanged, &SettingsCache::instance().chat(),
|
||||||
|
|
@ -76,6 +81,7 @@ MessagesSettingsPage::MessagesSettingsPage()
|
||||||
chatGrid->addWidget(&messagePopups, 5, 0);
|
chatGrid->addWidget(&messagePopups, 5, 0);
|
||||||
chatGrid->addWidget(&mentionPopups, 6, 0);
|
chatGrid->addWidget(&mentionPopups, 6, 0);
|
||||||
chatGrid->addWidget(&roomHistory, 7, 0);
|
chatGrid->addWidget(&roomHistory, 7, 0);
|
||||||
|
chatGrid->addWidget(&ignoreAllPrivateMessagesCheckBox, 8, 0);
|
||||||
chatGroupBox = new QGroupBox;
|
chatGroupBox = new QGroupBox;
|
||||||
chatGroupBox->setLayout(chatGrid);
|
chatGroupBox->setLayout(chatGrid);
|
||||||
|
|
||||||
|
|
@ -102,15 +108,15 @@ MessagesSettingsPage::MessagesSettingsPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
aAdd = new QAction(this);
|
aAdd = new QAction(this);
|
||||||
aAdd->setIcon(QPixmap("theme:icons/increment"));
|
aAdd->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
||||||
connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd);
|
connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd);
|
||||||
|
|
||||||
aEdit = new QAction(this);
|
aEdit = new QAction(this);
|
||||||
aEdit->setIcon(QPixmap("theme:icons/pencil"));
|
aEdit->setIcon(themePixmap(QStringLiteral("icons/pencil")));
|
||||||
connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit);
|
connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit);
|
||||||
|
|
||||||
aRemove = new QAction(this);
|
aRemove = new QAction(this);
|
||||||
aRemove->setIcon(QPixmap("theme:icons/decrement"));
|
aRemove->setIcon(themePixmap(QStringLiteral("icons/decrement")));
|
||||||
connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove);
|
connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove);
|
||||||
|
|
||||||
auto *messageToolBar = new QToolBar;
|
auto *messageToolBar = new QToolBar;
|
||||||
|
|
@ -246,6 +252,7 @@ void MessagesSettingsPage::retranslateUi()
|
||||||
messagePopups.setText(tr("Enable desktop notifications for private messages"));
|
messagePopups.setText(tr("Enable desktop notifications for private messages"));
|
||||||
mentionPopups.setText(tr("Enable desktop notification for mentions"));
|
mentionPopups.setText(tr("Enable desktop notification for mentions"));
|
||||||
roomHistory.setText(tr("Enable room message history on join"));
|
roomHistory.setText(tr("Enable room message history on join"));
|
||||||
|
ignoreAllPrivateMessagesCheckBox.setText(tr("Ignore all private messages"));
|
||||||
hexLabel.setText(tr("(Color is hexadecimal)"));
|
hexLabel.setText(tr("(Color is hexadecimal)"));
|
||||||
hexHighlightLabel.setText(tr("(Color is hexadecimal)"));
|
hexHighlightLabel.setText(tr("(Color is hexadecimal)"));
|
||||||
customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only"));
|
customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only"));
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ private:
|
||||||
QCheckBox messagePopups;
|
QCheckBox messagePopups;
|
||||||
QCheckBox mentionPopups;
|
QCheckBox mentionPopups;
|
||||||
QCheckBox roomHistory;
|
QCheckBox roomHistory;
|
||||||
|
QCheckBox ignoreAllPrivateMessagesCheckBox;
|
||||||
QGroupBox *chatGroupBox;
|
QGroupBox *chatGroupBox;
|
||||||
QGroupBox *highlightGroupBox;
|
QGroupBox *highlightGroupBox;
|
||||||
QGroupBox *messageGroupBox;
|
QGroupBox *messageGroupBox;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../client/settings/shortcut_treeview.h"
|
#include "../../../client/settings/shortcut_treeview.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/utility/custom_line_edit.h"
|
#include "../interface/widgets/utility/custom_line_edit.h"
|
||||||
#include "../interface/widgets/utility/sequence_edit.h"
|
#include "../interface/widgets/utility/sequence_edit.h"
|
||||||
|
|
||||||
|
|
@ -47,8 +48,8 @@ ShortcutSettingsPage::ShortcutSettingsPage()
|
||||||
btnResetAll = new QPushButton(this);
|
btnResetAll = new QPushButton(this);
|
||||||
btnClearAll = new QPushButton(this);
|
btnClearAll = new QPushButton(this);
|
||||||
|
|
||||||
btnResetAll->setIcon(QPixmap("theme:icons/update"));
|
btnResetAll->setIcon(themePixmap(QStringLiteral("icons/update")));
|
||||||
btnClearAll->setIcon(QPixmap("theme:icons/clearsearch"));
|
btnClearAll->setIcon(themePixmap(QStringLiteral("icons/clearsearch")));
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
auto *_editLayout = new QGridLayout;
|
auto *_editLayout = new QGridLayout;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "tab_archidekt.h"
|
#include "tab_archidekt.h"
|
||||||
|
|
||||||
#include "../../../../../client/settings/cache_settings.h"
|
#include "../../../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../../../pixel_map_generator.h"
|
||||||
#include "../../../cards/additional_info/mana_symbol_widget.h"
|
#include "../../../cards/additional_info/mana_symbol_widget.h"
|
||||||
#include "../../../utility/completer_utils.h"
|
#include "../../../utility/completer_utils.h"
|
||||||
#include "../../tab_supervisor.h"
|
#include "../../tab_supervisor.h"
|
||||||
|
|
@ -213,7 +214,7 @@ void TabArchidekt::setupFilterWidgets()
|
||||||
// Format filter (collapsible)
|
// Format filter (collapsible)
|
||||||
formatButton = new SettingsButtonWidget(secondaryToolbar);
|
formatButton = new SettingsButtonWidget(secondaryToolbar);
|
||||||
formatButton->setButtonText(tr("Formats"));
|
formatButton->setButtonText(tr("Formats"));
|
||||||
formatButton->setButtonIcon(QPixmap("theme:icons/scale_balanced"));
|
formatButton->setButtonIcon(themePixmap(QStringLiteral("icons/scale_balanced")));
|
||||||
|
|
||||||
QWidget *formatContainer = new QWidget(secondaryToolbar);
|
QWidget *formatContainer = new QWidget(secondaryToolbar);
|
||||||
QGridLayout *formatLayout = new QGridLayout(formatContainer);
|
QGridLayout *formatLayout = new QGridLayout(formatContainer);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "commander_bracket_widget.h"
|
#include "commander_bracket_widget.h"
|
||||||
|
|
||||||
#include "../../../../../client/settings/cache_settings.h"
|
#include "../../../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../../../pixel_map_generator.h"
|
||||||
#include "commander_bracket_service.h"
|
#include "commander_bracket_service.h"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
@ -30,7 +31,7 @@ CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent
|
||||||
bracketInfoButton->setEnabled(false);
|
bracketInfoButton->setEnabled(false);
|
||||||
|
|
||||||
bracketRefreshButton = new QToolButton(this);
|
bracketRefreshButton = new QToolButton(this);
|
||||||
bracketRefreshButton->setIcon(QPixmap("theme:icons/reload"));
|
bracketRefreshButton->setIcon(themePixmap(QStringLiteral("icons/reload")));
|
||||||
bracketRefreshButton->setAutoRaise(true);
|
bracketRefreshButton->setAutoRaise(true);
|
||||||
|
|
||||||
connect(bracketRefreshButton, &QToolButton::clicked, this, &CommanderBracketWidget::requestBracketEstimate);
|
connect(bracketRefreshButton, &QToolButton::clicked, this, &CommanderBracketWidget::requestBracketEstimate);
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,11 @@ void TabAccount::retranslateUi()
|
||||||
buddyList->retranslateUi();
|
buddyList->retranslateUi();
|
||||||
ignoreList->retranslateUi();
|
ignoreList->retranslateUi();
|
||||||
userInfoBox->retranslateUi();
|
userInfoBox->retranslateUi();
|
||||||
|
|
||||||
|
buddyList->setToolTip(tr("Buddies are marked with a star in chat, a sound plays when they join or leave the "
|
||||||
|
"server, and they can be invited to buddy-only games."));
|
||||||
|
ignoreList->setToolTip(tr("Ignored users' chat messages are hidden from you, and they cannot send you private "
|
||||||
|
"messages or join your games."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabAccount::processListUsersResponse(const Response &response)
|
void TabAccount::processListUsersResponse(const Response &response)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../deck_loader/deck_loader.h"
|
#include "../../deck_loader/deck_loader.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
|
#include "../interface/widgets/server/remote/remote_decklist_tree_widget.h"
|
||||||
#include "../interface/widgets/utility/get_text_with_max.h"
|
#include "../interface/widgets/utility/get_text_with_max.h"
|
||||||
|
|
||||||
|
|
@ -105,19 +106,19 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
|
||||||
|
|
||||||
// Left side actions
|
// Left side actions
|
||||||
aOpenLocalDeck = new QAction(this);
|
aOpenLocalDeck = new QAction(this);
|
||||||
aOpenLocalDeck->setIcon(QPixmap("theme:icons/pencil"));
|
aOpenLocalDeck->setIcon(themePixmap(QStringLiteral("icons/pencil")));
|
||||||
connect(aOpenLocalDeck, &QAction::triggered, this, &TabDeckStorage::actOpenLocalDeck);
|
connect(aOpenLocalDeck, &QAction::triggered, this, &TabDeckStorage::actOpenLocalDeck);
|
||||||
aRenameLocal = new QAction(this);
|
aRenameLocal = new QAction(this);
|
||||||
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
|
aRenameLocal->setIcon(themePixmap(QStringLiteral("icons/rename")));
|
||||||
connect(aRenameLocal, &QAction::triggered, this, &TabDeckStorage::actRenameLocal);
|
connect(aRenameLocal, &QAction::triggered, this, &TabDeckStorage::actRenameLocal);
|
||||||
aUpload = new QAction(this);
|
aUpload = new QAction(this);
|
||||||
aUpload->setIcon(QPixmap("theme:icons/arrow_right_green"));
|
aUpload->setIcon(themePixmap(QStringLiteral("icons/arrow_right_green")));
|
||||||
connect(aUpload, &QAction::triggered, this, &TabDeckStorage::actUpload);
|
connect(aUpload, &QAction::triggered, this, &TabDeckStorage::actUpload);
|
||||||
aNewLocalFolder = new QAction(this);
|
aNewLocalFolder = new QAction(this);
|
||||||
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||||
connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder);
|
connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder);
|
||||||
aDeleteLocalDeck = new QAction(this);
|
aDeleteLocalDeck = new QAction(this);
|
||||||
aDeleteLocalDeck->setIcon(QPixmap("theme:icons/remove_row"));
|
aDeleteLocalDeck->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck);
|
connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck);
|
||||||
|
|
||||||
aOpenDecksFolder = new QAction(this);
|
aOpenDecksFolder = new QAction(this);
|
||||||
|
|
@ -126,16 +127,16 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
|
||||||
|
|
||||||
// Right side actions
|
// Right side actions
|
||||||
aOpenRemoteDeck = new QAction(this);
|
aOpenRemoteDeck = new QAction(this);
|
||||||
aOpenRemoteDeck->setIcon(QPixmap("theme:icons/pencil"));
|
aOpenRemoteDeck->setIcon(themePixmap(QStringLiteral("icons/pencil")));
|
||||||
connect(aOpenRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actOpenRemoteDeck);
|
connect(aOpenRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actOpenRemoteDeck);
|
||||||
aDownload = new QAction(this);
|
aDownload = new QAction(this);
|
||||||
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
|
aDownload->setIcon(themePixmap(QStringLiteral("icons/arrow_left_green")));
|
||||||
connect(aDownload, &QAction::triggered, this, &TabDeckStorage::actDownload);
|
connect(aDownload, &QAction::triggered, this, &TabDeckStorage::actDownload);
|
||||||
aNewFolder = new QAction(this);
|
aNewFolder = new QAction(this);
|
||||||
aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||||
connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder);
|
connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder);
|
||||||
aDeleteRemoteDeck = new QAction(this);
|
aDeleteRemoteDeck = new QAction(this);
|
||||||
aDeleteRemoteDeck->setIcon(QPixmap("theme:icons/remove_row"));
|
aDeleteRemoteDeck->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck);
|
connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck);
|
||||||
|
|
||||||
// Add actions to toolbars
|
// Add actions to toolbars
|
||||||
|
|
|
||||||
261
cockatrice/src/interface/widgets/tabs/tab_developer.cpp
Normal file
261
cockatrice/src/interface/widgets/tabs/tab_developer.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
/**
|
||||||
|
* @file tab_developer.cpp
|
||||||
|
* @ingroup ServerTabs
|
||||||
|
*/
|
||||||
|
//! \todo Document this file.
|
||||||
|
|
||||||
|
#include "tab_developer.h"
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_get_server_stats.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_get_server_stats.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pending_command.h>
|
||||||
|
|
||||||
|
static constexpr int DEFAULT_AUTO_REFRESH_INTERVAL_SECS = 30;
|
||||||
|
|
||||||
|
TabDeveloper::TabDeveloper(TabSupervisor *_tabSupervisor, AbstractClient *_client)
|
||||||
|
: Tab(_tabSupervisor), client(_client)
|
||||||
|
{
|
||||||
|
statsTable = new QTableWidget(0, 2);
|
||||||
|
statsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
|
statsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
statsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
statsTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
statsTable->verticalHeader()->setVisible(false);
|
||||||
|
statsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive);
|
||||||
|
statsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive);
|
||||||
|
statsTable->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
|
||||||
|
commandTable = new QTableWidget(0, 4);
|
||||||
|
commandTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
commandTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
commandTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
commandTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
commandTable->verticalHeader()->setVisible(false);
|
||||||
|
commandTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive);
|
||||||
|
commandTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive);
|
||||||
|
commandTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive);
|
||||||
|
commandTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Interactive);
|
||||||
|
|
||||||
|
statusLabel = new QLabel;
|
||||||
|
|
||||||
|
autoRefreshCheckBox = new QCheckBox;
|
||||||
|
autoRefreshCheckBox->setChecked(false);
|
||||||
|
|
||||||
|
refreshIntervalSpinBox = new QSpinBox;
|
||||||
|
refreshIntervalSpinBox->setRange(5, 3600);
|
||||||
|
refreshIntervalSpinBox->setValue(DEFAULT_AUTO_REFRESH_INTERVAL_SECS);
|
||||||
|
refreshIntervalSpinBox->setEnabled(false);
|
||||||
|
|
||||||
|
autoRefreshTimer = new QTimer(this);
|
||||||
|
connect(autoRefreshTimer, &QTimer::timeout, this, &TabDeveloper::refreshClicked);
|
||||||
|
connect(autoRefreshCheckBox, &QCheckBox::toggled, this, &TabDeveloper::autoRefreshToggled);
|
||||||
|
connect(refreshIntervalSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||||
|
&TabDeveloper::refreshIntervalChanged);
|
||||||
|
|
||||||
|
refreshButton = new QPushButton;
|
||||||
|
refreshButton->setAutoDefault(true);
|
||||||
|
connect(refreshButton, &QPushButton::clicked, this, &TabDeveloper::refreshClicked);
|
||||||
|
|
||||||
|
auto *buttonLayout = new QHBoxLayout;
|
||||||
|
buttonLayout->addWidget(statusLabel, 1, Qt::AlignLeft);
|
||||||
|
buttonLayout->addWidget(autoRefreshCheckBox, 0, Qt::AlignRight);
|
||||||
|
buttonLayout->addWidget(refreshIntervalSpinBox, 0, Qt::AlignRight);
|
||||||
|
buttonLayout->addWidget(refreshButton, 0, Qt::AlignRight);
|
||||||
|
|
||||||
|
auto *tableLayout = new QHBoxLayout;
|
||||||
|
tableLayout->addWidget(statsTable, 1);
|
||||||
|
tableLayout->addWidget(commandTable, 2);
|
||||||
|
|
||||||
|
auto *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(tableLayout, 1);
|
||||||
|
mainLayout->addLayout(buttonLayout);
|
||||||
|
|
||||||
|
auto *central = new QWidget;
|
||||||
|
central->setLayout(mainLayout);
|
||||||
|
setCentralWidget(central);
|
||||||
|
|
||||||
|
retranslateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::retranslateUi()
|
||||||
|
{
|
||||||
|
autoRefreshCheckBox->setText(tr("Auto-refresh"));
|
||||||
|
autoRefreshCheckBox->setToolTip(tr("Automatically request fresh server statistics at a fixed interval."));
|
||||||
|
refreshIntervalSpinBox->setSuffix(tr(" s"));
|
||||||
|
refreshIntervalSpinBox->setToolTip(tr("Seconds between automatic refreshes."));
|
||||||
|
refreshButton->setText(tr("Refresh server stats"));
|
||||||
|
statsTable->setHorizontalHeaderLabels(QString(tr("Statistic;Value")).split(";"));
|
||||||
|
commandTable->setHorizontalHeaderLabels(QString(tr("Command;Count;Total ms;Avg ms")).split(";"));
|
||||||
|
if (statsTable->rowCount() == 0) {
|
||||||
|
statusLabel->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabDeveloper::formatBytes(quint64 bytes)
|
||||||
|
{
|
||||||
|
const quint64 kib = 1024;
|
||||||
|
const quint64 mib = 1024 * kib;
|
||||||
|
const quint64 gib = 1024 * mib;
|
||||||
|
if (bytes >= gib) {
|
||||||
|
return tr("%1 GiB").arg(QString::number(bytes / static_cast<double>(gib), 'f', 2));
|
||||||
|
}
|
||||||
|
if (bytes >= mib) {
|
||||||
|
return tr("%1 MiB").arg(QString::number(bytes / static_cast<double>(mib), 'f', 2));
|
||||||
|
}
|
||||||
|
if (bytes >= kib) {
|
||||||
|
return tr("%1 KiB").arg(QString::number(bytes / static_cast<double>(kib), 'f', 2));
|
||||||
|
}
|
||||||
|
return tr("%1 bytes").arg(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabDeveloper::formatDurationMs(qint64 ms)
|
||||||
|
{
|
||||||
|
if (ms >= 1000) {
|
||||||
|
return tr("%1 s").arg(QString::number(ms / 1000.0, 'f', 2));
|
||||||
|
}
|
||||||
|
return tr("%1 ms").arg(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::appendStatRow(const QString &name, const QString &value)
|
||||||
|
{
|
||||||
|
const int row = statsTable->rowCount();
|
||||||
|
statsTable->insertRow(row);
|
||||||
|
statsTable->setItem(row, 0, new QTableWidgetItem(name));
|
||||||
|
statsTable->setItem(row, 1, new QTableWidgetItem(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::appendSeparatorRow(const QString §ionTitle)
|
||||||
|
{
|
||||||
|
const int row = statsTable->rowCount();
|
||||||
|
statsTable->insertRow(row);
|
||||||
|
auto *labelItem = new QTableWidgetItem(sectionTitle);
|
||||||
|
auto font = labelItem->font();
|
||||||
|
font.setBold(true);
|
||||||
|
labelItem->setFont(font);
|
||||||
|
labelItem->setFlags(labelItem->flags() & ~Qt::ItemIsSelectable);
|
||||||
|
statsTable->setItem(row, 0, labelItem);
|
||||||
|
statsTable->setItem(row, 1, new QTableWidgetItem(QString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::refreshClicked()
|
||||||
|
{
|
||||||
|
if (requestPending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestPending = true;
|
||||||
|
Command_GetServerStats cmd;
|
||||||
|
PendingCommand *pend = client->prepareDeveloperCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabDeveloper::serverStatsResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::autoRefreshToggled(bool checked)
|
||||||
|
{
|
||||||
|
refreshIntervalSpinBox->setEnabled(checked);
|
||||||
|
if (checked) {
|
||||||
|
refreshIntervalChanged();
|
||||||
|
refreshClicked();
|
||||||
|
} else {
|
||||||
|
autoRefreshTimer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::refreshIntervalChanged()
|
||||||
|
{
|
||||||
|
if (autoRefreshCheckBox->isChecked()) {
|
||||||
|
autoRefreshTimer->start(refreshIntervalSpinBox->value() * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabDeveloper::serverStatsResponse(const Response &resp)
|
||||||
|
{
|
||||||
|
requestPending = false;
|
||||||
|
if (resp.response_code() != Response::RespOk) {
|
||||||
|
statusLabel->setText(tr("No server statistics available yet."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_GetServerStats &response = resp.GetExtension(Response_GetServerStats::ext);
|
||||||
|
|
||||||
|
statsTable->setRowCount(0);
|
||||||
|
|
||||||
|
// Overview section
|
||||||
|
appendStatRow(tr("Registered users online"), QString::number(response.users_count()));
|
||||||
|
appendStatRow(tr("Moderators online"), QString::number(response.mods_count()));
|
||||||
|
appendStatRow(tr("Games running"), QString::number(response.games_count()));
|
||||||
|
appendStatRow(tr("Traffic sent (last tick)"), formatBytes(response.tx_bytes()));
|
||||||
|
appendStatRow(tr("Traffic received (last tick)"), formatBytes(response.rx_bytes()));
|
||||||
|
|
||||||
|
const qint64 uptime = static_cast<qint64>(response.uptime_secs());
|
||||||
|
const int days = static_cast<int>(uptime / 86400);
|
||||||
|
const int hours = static_cast<int>((uptime % 86400) / 3600);
|
||||||
|
const int minutes = static_cast<int>((uptime % 3600) / 60);
|
||||||
|
appendStatRow(tr("Server uptime"), days > 0 ? tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes)
|
||||||
|
: tr("%1h %2m").arg(hours).arg(minutes));
|
||||||
|
|
||||||
|
const QDateTime snapshotTime = QDateTime::fromSecsSinceEpoch(static_cast<qint64>(response.timest()));
|
||||||
|
appendStatRow(tr("Snapshot taken"), snapshotTime.toLocalTime().toString("yyyy-MM-dd HH:mm"));
|
||||||
|
|
||||||
|
// Live metrics section
|
||||||
|
appendSeparatorRow(tr("Live Metrics"));
|
||||||
|
appendStatRow(tr("Cards in live games"), QString::number(response.cards_in_games()));
|
||||||
|
appendStatRow(tr("Total commands processed"), QString::number(response.total_commands()));
|
||||||
|
|
||||||
|
if (response.total_commands() > 0) {
|
||||||
|
const double avgMs = static_cast<double>(response.total_command_time_ms()) / response.total_commands();
|
||||||
|
appendStatRow(tr("Avg command time"), QString::number(avgMs, 'f', 2) + " ms");
|
||||||
|
}
|
||||||
|
appendStatRow(tr("Active command types"), QString::number(response.active_command_types()));
|
||||||
|
|
||||||
|
appendStatRow(tr("Event loop stalls"), QString::number(response.eventloop_stalls_total()));
|
||||||
|
appendStatRow(tr("Last stall overshoot"), formatDurationMs(response.eventloop_last_stall_ms()));
|
||||||
|
appendStatRow(tr("Worst stall overshoot"), formatDurationMs(response.eventloop_max_stall_ms()));
|
||||||
|
|
||||||
|
if (response.game_start_count() > 0) {
|
||||||
|
appendStatRow(tr("Game starts"), QString::number(response.game_start_count()));
|
||||||
|
const double avgStartMs = static_cast<double>(response.game_start_total_ms()) / response.game_start_count();
|
||||||
|
appendStatRow(tr("Avg game start time"), QString::number(avgStartMs, 'f', 1) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-command breakdown table
|
||||||
|
QList<CommandStats> sortedStats(response.command_stats().begin(), response.command_stats().end());
|
||||||
|
std::sort(sortedStats.begin(), sortedStats.end(),
|
||||||
|
[](const auto &a, const auto &b) { return a.total_ms() > b.total_ms(); });
|
||||||
|
|
||||||
|
commandTable->setRowCount(0);
|
||||||
|
for (const auto &cs : sortedStats) {
|
||||||
|
const int row = commandTable->rowCount();
|
||||||
|
commandTable->insertRow(row);
|
||||||
|
commandTable->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(cs.command_name())));
|
||||||
|
|
||||||
|
auto *countItem = new QTableWidgetItem(QString::number(cs.count()));
|
||||||
|
countItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
commandTable->setItem(row, 1, countItem);
|
||||||
|
|
||||||
|
auto *totalItem = new QTableWidgetItem(QString::number(cs.total_ms()));
|
||||||
|
totalItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
commandTable->setItem(row, 2, totalItem);
|
||||||
|
|
||||||
|
const double avg = cs.count() > 0 ? static_cast<double>(cs.total_ms()) / cs.count() : 0.0;
|
||||||
|
auto *avgItem = new QTableWidgetItem(QString::number(avg, 'f', 2));
|
||||||
|
avgItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
commandTable->setItem(row, 3, avgItem);
|
||||||
|
}
|
||||||
|
commandTable->resizeColumnsToContents();
|
||||||
|
statsTable->resizeColumnsToContents();
|
||||||
|
commandTable->resizeColumnsToContents();
|
||||||
|
|
||||||
|
statusLabel->setText(tr("Updated %1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm")));
|
||||||
|
}
|
||||||
55
cockatrice/src/interface/widgets/tabs/tab_developer.h
Normal file
55
cockatrice/src/interface/widgets/tabs/tab_developer.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* @file tab_developer.h
|
||||||
|
* @ingroup ServerTabs
|
||||||
|
*/
|
||||||
|
//! \todo Document this file.
|
||||||
|
|
||||||
|
#ifndef TAB_DEVELOPER_H
|
||||||
|
#define TAB_DEVELOPER_H
|
||||||
|
|
||||||
|
#include "tab.h"
|
||||||
|
|
||||||
|
class AbstractClient;
|
||||||
|
class QCheckBox;
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
|
class QSpinBox;
|
||||||
|
class QTableWidget;
|
||||||
|
class QTimer;
|
||||||
|
class Response;
|
||||||
|
|
||||||
|
class TabDeveloper : public Tab
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
AbstractClient *client;
|
||||||
|
QTableWidget *statsTable;
|
||||||
|
QTableWidget *commandTable;
|
||||||
|
QPushButton *refreshButton;
|
||||||
|
QLabel *statusLabel;
|
||||||
|
QCheckBox *autoRefreshCheckBox;
|
||||||
|
QSpinBox *refreshIntervalSpinBox;
|
||||||
|
QTimer *autoRefreshTimer;
|
||||||
|
bool requestPending = false;
|
||||||
|
|
||||||
|
void appendStatRow(const QString &name, const QString &value);
|
||||||
|
void appendSeparatorRow(const QString §ionTitle);
|
||||||
|
static QString formatBytes(quint64 bytes);
|
||||||
|
static QString formatDurationMs(qint64 ms);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void refreshClicked();
|
||||||
|
void serverStatsResponse(const Response &resp);
|
||||||
|
void autoRefreshToggled(bool checked);
|
||||||
|
void refreshIntervalChanged();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TabDeveloper(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
||||||
|
void retranslateUi() override;
|
||||||
|
[[nodiscard]] QString getTabText() const override
|
||||||
|
{
|
||||||
|
return tr("Developer");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -266,6 +266,10 @@ void TabGame::resetChatAndPhase()
|
||||||
|
|
||||||
// reset phase markers
|
// reset phase markers
|
||||||
game->getGameState()->setCurrentPhase(-1);
|
game->getGameState()->setCurrentPhase(-1);
|
||||||
|
|
||||||
|
// reset spectator state so the replay can rebuild it from the start
|
||||||
|
game->getPlayerManager()->clearSpectators();
|
||||||
|
playerListWidget->clearSpectators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabGame::emitUserEvent()
|
void TabGame::emitUserEvent()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
#include <libcockatrice/protocol/pending_command.h>
|
#include <libcockatrice/protocol/pending_command.h>
|
||||||
#include <libcockatrice/utility/string_limits.h>
|
#include <libcockatrice/utility/string_limits.h>
|
||||||
|
|
||||||
TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
|
TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _canUseDeveloperCommands)
|
||||||
|
: Tab(_tabSupervisor), client(_client), canUseDeveloperCommands(_canUseDeveloperCommands)
|
||||||
{
|
{
|
||||||
roomTable = new QTableWidget();
|
roomTable = new QTableWidget();
|
||||||
roomTable->setColumnCount(6);
|
roomTable->setColumnCount(6);
|
||||||
|
|
@ -80,7 +81,9 @@ void TabLog::getClicked()
|
||||||
if (!mainRoom->isChecked() && !gameRoom->isChecked() && !privateChat->isChecked()) {
|
if (!mainRoom->isChecked() && !gameRoom->isChecked() && !privateChat->isChecked()) {
|
||||||
mainRoom->setChecked(true);
|
mainRoom->setChecked(true);
|
||||||
gameRoom->setChecked(true);
|
gameRoom->setChecked(true);
|
||||||
privateChat->setChecked(true);
|
if (!canUseDeveloperCommands) {
|
||||||
|
privateChat->setChecked(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maximumResults->value() == 0) {
|
if (maximumResults->value() == 0) {
|
||||||
|
|
@ -117,7 +120,15 @@ void TabLog::getClicked()
|
||||||
};
|
};
|
||||||
cmd.set_date_range(dateRange);
|
cmd.set_date_range(dateRange);
|
||||||
cmd.set_maximum_results(maximumResults->value());
|
cmd.set_maximum_results(maximumResults->value());
|
||||||
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
|
||||||
|
PendingCommand *pend;
|
||||||
|
if (canUseDeveloperCommands) {
|
||||||
|
// Developers query logs through the developer command family.
|
||||||
|
pend = client->prepareDeveloperCommand(cmd);
|
||||||
|
} else {
|
||||||
|
pend = client->prepareModeratorCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
connect(pend, &PendingCommand::finished, this, &TabLog::viewLogHistory_processResponse);
|
connect(pend, &PendingCommand::finished, this, &TabLog::viewLogHistory_processResponse);
|
||||||
client->sendCommand(pend);
|
client->sendCommand(pend);
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +182,14 @@ void TabLog::createDock()
|
||||||
mainRoom = new QCheckBox(tr("Main Room"));
|
mainRoom = new QCheckBox(tr("Main Room"));
|
||||||
gameRoom = new QCheckBox(tr("Game Room"));
|
gameRoom = new QCheckBox(tr("Game Room"));
|
||||||
privateChat = new QCheckBox(tr("Private Chat"));
|
privateChat = new QCheckBox(tr("Private Chat"));
|
||||||
|
if (canUseDeveloperCommands) {
|
||||||
|
// Developers cannot query private conversations.
|
||||||
|
privateChat->setVisible(false);
|
||||||
|
// The developer family ignores the IP filter server-side, so showing
|
||||||
|
// the field would silently unfilter the result by it. Hide it.
|
||||||
|
labelFindIPAddress->setVisible(false);
|
||||||
|
findIPAddress->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
pastDays = new QRadioButton(tr("Past X Days: "));
|
pastDays = new QRadioButton(tr("Past X Days: "));
|
||||||
today = new QRadioButton(tr("Today"));
|
today = new QRadioButton(tr("Today"));
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class TabLog : public Tab
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
AbstractClient *client;
|
AbstractClient *client;
|
||||||
|
bool canUseDeveloperCommands;
|
||||||
QLabel *labelFindUserName, *labelFindIPAddress, *labelFindGameName, *labelFindGameID, *labelMessage, *labelMaximum,
|
QLabel *labelFindUserName, *labelFindIPAddress, *labelFindGameName, *labelFindGameID, *labelMessage, *labelMaximum,
|
||||||
*labelDescription;
|
*labelDescription;
|
||||||
LineEditUnfocusable *findUsername, *findIPAddress, *findGameName, *findGameID, *findMessage;
|
LineEditUnfocusable *findUsername, *findIPAddress, *findGameName, *findGameID, *findMessage;
|
||||||
|
|
@ -58,7 +59,7 @@ private slots:
|
||||||
void restartLayout();
|
void restartLayout();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client);
|
TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _canUseDeveloperCommands = false);
|
||||||
~TabLog() override;
|
~TabLog() override;
|
||||||
void retranslateUi() override;
|
void retranslateUi() override;
|
||||||
[[nodiscard]] QString getTabText() const override
|
[[nodiscard]] QString getTabText() const override
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,12 @@ void TabMessage::closeEvent(QCloseEvent *event)
|
||||||
|
|
||||||
void TabMessage::sendPrivateMessage(const QString &text)
|
void TabMessage::sendPrivateMessage(const QString &text)
|
||||||
{
|
{
|
||||||
|
if (tabSupervisor->getUserListManager()->isUserIgnored(getUserName())) {
|
||||||
|
chatView->appendMessage(tr("You have ignored %1; your messages are not delivered.")
|
||||||
|
.arg(QString::fromStdString(otherUserInfo->name())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Command_Message cmd;
|
Command_Message cmd;
|
||||||
cmd.set_user_name(otherUserInfo->name());
|
cmd.set_user_name(otherUserInfo->name());
|
||||||
cmd.set_message(text.toStdString());
|
cmd.set_message(text.toStdString());
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,9 @@ void TabModeration::moderatorLoginsResponse(const Response &response)
|
||||||
if (login.user_level() & ServerInfo_User::IsAdmin) {
|
if (login.user_level() & ServerInfo_User::IsAdmin) {
|
||||||
levels << tr("Admin");
|
levels << tr("Admin");
|
||||||
}
|
}
|
||||||
|
if (login.user_level() & ServerInfo_User::IsDeveloper) {
|
||||||
|
levels << tr("Developer");
|
||||||
|
}
|
||||||
if (login.user_level() & ServerInfo_User::IsModerator) {
|
if (login.user_level() & ServerInfo_User::IsModerator) {
|
||||||
levels << tr("Moderator");
|
levels << tr("Moderator");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "tab_replays.h"
|
#include "tab_replays.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/server/remote/remote_replay_list_tree_widget.h"
|
#include "../interface/widgets/server/remote/remote_replay_list_tree_widget.h"
|
||||||
#include "tab_game.h"
|
#include "tab_game.h"
|
||||||
|
|
||||||
|
|
@ -102,17 +103,17 @@ QGroupBox *TabReplays::createLeftLayout()
|
||||||
|
|
||||||
// Left side actions
|
// Left side actions
|
||||||
aOpenLocalReplay = new QAction(this);
|
aOpenLocalReplay = new QAction(this);
|
||||||
aOpenLocalReplay->setIcon(QPixmap("theme:icons/view"));
|
aOpenLocalReplay->setIcon(themePixmap(QStringLiteral("icons/view")));
|
||||||
connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay);
|
connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay);
|
||||||
connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay);
|
connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay);
|
||||||
aRenameLocal = new QAction(this);
|
aRenameLocal = new QAction(this);
|
||||||
aRenameLocal->setIcon(QPixmap("theme:icons/rename"));
|
aRenameLocal->setIcon(themePixmap(QStringLiteral("icons/rename")));
|
||||||
connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal);
|
connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal);
|
||||||
aNewLocalFolder = new QAction(this);
|
aNewLocalFolder = new QAction(this);
|
||||||
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
|
||||||
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
|
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
|
||||||
aDeleteLocalReplay = new QAction(this);
|
aDeleteLocalReplay = new QAction(this);
|
||||||
aDeleteLocalReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
aDeleteLocalReplay->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
|
connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
|
||||||
|
|
||||||
aOpenReplaysFolder = new QAction(this);
|
aOpenReplaysFolder = new QAction(this);
|
||||||
|
|
@ -164,24 +165,24 @@ QGroupBox *TabReplays::createRightLayout()
|
||||||
|
|
||||||
// Right side actions
|
// Right side actions
|
||||||
aOpenRemoteReplay = new QAction(this);
|
aOpenRemoteReplay = new QAction(this);
|
||||||
aOpenRemoteReplay->setIcon(QPixmap("theme:icons/view"));
|
aOpenRemoteReplay->setIcon(themePixmap(QStringLiteral("icons/view")));
|
||||||
connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay);
|
connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay);
|
||||||
connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay);
|
connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay);
|
||||||
aDownload = new QAction(this);
|
aDownload = new QAction(this);
|
||||||
aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
|
aDownload->setIcon(themePixmap(QStringLiteral("icons/arrow_left_green")));
|
||||||
connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload);
|
connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload);
|
||||||
aKeep = new QAction(this);
|
aKeep = new QAction(this);
|
||||||
aKeep->setIcon(QPixmap("theme:icons/lock"));
|
aKeep->setIcon(themePixmap(QStringLiteral("icons/lock")));
|
||||||
connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
|
connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
|
||||||
aDeleteRemoteReplay = new QAction(this);
|
aDeleteRemoteReplay = new QAction(this);
|
||||||
aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
aDeleteRemoteReplay->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
|
||||||
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
|
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
|
||||||
aGetReplayCode = new QAction(this);
|
aGetReplayCode = new QAction(this);
|
||||||
aGetReplayCode->setIcon(QPixmap("theme:icons/share"));
|
aGetReplayCode->setIcon(themePixmap(QStringLiteral("icons/share")));
|
||||||
connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode);
|
connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode);
|
||||||
|
|
||||||
aSubmitReplayCode = new QAction(this);
|
aSubmitReplayCode = new QAction(this);
|
||||||
aSubmitReplayCode->setIcon(QPixmap("theme:icons/search"));
|
aSubmitReplayCode->setIcon(themePixmap(QStringLiteral("icons/search")));
|
||||||
connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode);
|
connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode);
|
||||||
|
|
||||||
// Add actions to toolbars
|
// Add actions to toolbars
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../interface/widgets/dialogs/dlg_settings.h"
|
#include "../interface/widgets/dialogs/dlg_settings.h"
|
||||||
#include "../interface/widgets/server/chat_view/chat_view.h"
|
#include "../interface/widgets/server/chat_view/chat_view.h"
|
||||||
#include "../interface/widgets/server/game_link.h"
|
#include "../interface/widgets/server/game_link.h"
|
||||||
|
|
@ -98,7 +99,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
|
||||||
connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings);
|
connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings);
|
||||||
|
|
||||||
auto *chatSettingsButton = new QToolButton;
|
auto *chatSettingsButton = new QToolButton;
|
||||||
chatSettingsButton->setIcon(QPixmap("theme:icons/settings"));
|
chatSettingsButton->setIcon(themePixmap(QStringLiteral("icons/settings")));
|
||||||
chatSettingsButton->setMenu(chatSettingsMenu);
|
chatSettingsButton->setMenu(chatSettingsMenu);
|
||||||
chatSettingsButton->setPopupMode(QToolButton::InstantPopup);
|
chatSettingsButton->setPopupMode(QToolButton::InstantPopup);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "tab_card_art_rules.h"
|
#include "tab_card_art_rules.h"
|
||||||
#include "tab_deck_editor.h"
|
#include "tab_deck_editor.h"
|
||||||
#include "tab_deck_storage.h"
|
#include "tab_deck_storage.h"
|
||||||
|
#include "tab_developer.h"
|
||||||
#include "tab_game.h"
|
#include "tab_game.h"
|
||||||
#include "tab_home.h"
|
#include "tab_home.h"
|
||||||
#include "tab_logs.h"
|
#include "tab_logs.h"
|
||||||
|
|
@ -119,7 +120,7 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
||||||
: QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabHome(nullptr),
|
: QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabHome(nullptr),
|
||||||
tabVisualDeckStorage(nullptr), tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr),
|
tabVisualDeckStorage(nullptr), tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr),
|
||||||
tabReplays(nullptr), tabAdmin(nullptr), tabCardArtRules(nullptr), tabLog(nullptr), tabReport(nullptr),
|
tabReplays(nullptr), tabAdmin(nullptr), tabCardArtRules(nullptr), tabLog(nullptr), tabReport(nullptr),
|
||||||
tabModeration(nullptr), isLocalGame(false)
|
tabModeration(nullptr), tabDeveloper(nullptr), isLocalGame(false)
|
||||||
{
|
{
|
||||||
setElideMode(Qt::ElideRight);
|
setElideMode(Qt::ElideRight);
|
||||||
setMovable(true);
|
setMovable(true);
|
||||||
|
|
@ -205,6 +206,10 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
||||||
aTabModeration->setCheckable(true);
|
aTabModeration->setCheckable(true);
|
||||||
connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration);
|
connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration);
|
||||||
|
|
||||||
|
aTabDeveloper = new QAction(this);
|
||||||
|
aTabDeveloper->setCheckable(true);
|
||||||
|
connect(aTabDeveloper, &QAction::triggered, this, &TabSupervisor::actTabDeveloper);
|
||||||
|
|
||||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||||
&TabSupervisor::refreshShortcuts);
|
&TabSupervisor::refreshShortcuts);
|
||||||
refreshShortcuts();
|
refreshShortcuts();
|
||||||
|
|
@ -247,6 +252,7 @@ void TabSupervisor::retranslateUi()
|
||||||
aTabReport->setText(tr("Report Queue"));
|
aTabReport->setText(tr("Report Queue"));
|
||||||
aTabModeration->setText(tr("Moderation"));
|
aTabModeration->setText(tr("Moderation"));
|
||||||
aTabCardArtRules->setText(tr("Card Art Rules"));
|
aTabCardArtRules->setText(tr("Card Art Rules"));
|
||||||
|
aTabDeveloper->setText(tr("Developer"));
|
||||||
|
|
||||||
// tabs
|
// tabs
|
||||||
QList<Tab *> tabs;
|
QList<Tab *> tabs;
|
||||||
|
|
@ -259,6 +265,7 @@ void TabSupervisor::retranslateUi()
|
||||||
tabs.append(tabReport);
|
tabs.append(tabReport);
|
||||||
tabs.append(tabModeration);
|
tabs.append(tabModeration);
|
||||||
tabs.append(tabCardArtRules);
|
tabs.append(tabCardArtRules);
|
||||||
|
tabs.append(tabDeveloper);
|
||||||
QMapIterator<int, TabRoom *> roomIterator(roomTabs);
|
QMapIterator<int, TabRoom *> roomIterator(roomTabs);
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
tabs.append(roomIterator.next().value());
|
tabs.append(roomIterator.next().value());
|
||||||
|
|
@ -528,6 +535,19 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userInfo->user_level() & ServerInfo_User::IsDeveloper) {
|
||||||
|
tabsMenu->addSeparator();
|
||||||
|
tabsMenu->addAction(aTabDeveloper);
|
||||||
|
// Developers without moderation rights get log access through their
|
||||||
|
// own role. Moderators already have the Logs entry from above.
|
||||||
|
if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) {
|
||||||
|
tabsMenu->addAction(aTabLog);
|
||||||
|
if (SettingsCache::instance().tabs().getTabLogOpen()) {
|
||||||
|
openTabLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,6 +560,7 @@ void TabSupervisor::startLocal(const QList<AbstractClient *> &_clients)
|
||||||
tabLog = nullptr;
|
tabLog = nullptr;
|
||||||
tabReport = nullptr;
|
tabReport = nullptr;
|
||||||
tabModeration = nullptr;
|
tabModeration = nullptr;
|
||||||
|
tabDeveloper = nullptr;
|
||||||
isLocalGame = true;
|
isLocalGame = true;
|
||||||
userInfo = new ServerInfo_User;
|
userInfo = new ServerInfo_User;
|
||||||
localClients = _clients;
|
localClients = _clients;
|
||||||
|
|
@ -590,6 +611,9 @@ void TabSupervisor::stop()
|
||||||
if (tabCardArtRules) {
|
if (tabCardArtRules) {
|
||||||
tabCardArtRules->close();
|
tabCardArtRules->close();
|
||||||
}
|
}
|
||||||
|
if (tabDeveloper) {
|
||||||
|
tabDeveloper->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Tab *> tabsToDelete;
|
QList<Tab *> tabsToDelete;
|
||||||
|
|
@ -819,7 +843,13 @@ void TabSupervisor::actTabLog(bool checked)
|
||||||
|
|
||||||
void TabSupervisor::openTabLog()
|
void TabSupervisor::openTabLog()
|
||||||
{
|
{
|
||||||
tabLog = new TabLog(this, client);
|
// Developers query logs through the developer command family, so tell the
|
||||||
|
// tab which family to use. The moderator family is strictly stronger, so a
|
||||||
|
// moderator who also holds the developer bit keeps the moderator path — the
|
||||||
|
// developer bit only selects the (narrowed) developer family on its own.
|
||||||
|
const bool useDeveloperCommands = (userInfo->user_level() & ServerInfo_User::IsDeveloper) &&
|
||||||
|
!(userInfo->user_level() & ServerInfo_User::IsModerator);
|
||||||
|
tabLog = new TabLog(this, client, useDeveloperCommands);
|
||||||
myAddTab(tabLog, aTabLog);
|
myAddTab(tabLog, aTabLog);
|
||||||
connect(tabLog, &QObject::destroyed, this, [this] {
|
connect(tabLog, &QObject::destroyed, this, [this] {
|
||||||
tabLog = nullptr;
|
tabLog = nullptr;
|
||||||
|
|
@ -881,6 +911,27 @@ void TabSupervisor::openTabModeration(const QString &userName)
|
||||||
aTabModeration->setChecked(true);
|
aTabModeration->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TabSupervisor::actTabDeveloper(bool checked)
|
||||||
|
{
|
||||||
|
if (checked && !tabDeveloper) {
|
||||||
|
openTabDeveloper();
|
||||||
|
setCurrentWidget(tabDeveloper);
|
||||||
|
} else if (!checked && tabDeveloper) {
|
||||||
|
tabDeveloper->closeRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabSupervisor::openTabDeveloper()
|
||||||
|
{
|
||||||
|
tabDeveloper = new TabDeveloper(this, client);
|
||||||
|
myAddTab(tabDeveloper, aTabDeveloper);
|
||||||
|
connect(tabDeveloper, &QObject::destroyed, this, [this] {
|
||||||
|
tabDeveloper = nullptr;
|
||||||
|
aTabDeveloper->setChecked(false);
|
||||||
|
});
|
||||||
|
aTabDeveloper->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
void TabSupervisor::updatePingTime(int value, int max)
|
void TabSupervisor::updatePingTime(int value, int max)
|
||||||
{
|
{
|
||||||
if (!tabServer) {
|
if (!tabServer) {
|
||||||
|
|
@ -1063,6 +1114,13 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus
|
||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (focus && userListManager->isUserIgnored(receiverName)) {
|
||||||
|
QMessageBox::information(
|
||||||
|
this, tr("Ignored user"),
|
||||||
|
tr("You have ignored %1. Remove them from your ignore list to open a private chat.").arg(receiverName));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
tab = new TabMessage(this, client, *userInfo, otherUser, userOnline);
|
tab = new TabMessage(this, client, *userInfo, otherUser, userOnline);
|
||||||
connect(tab, &TabMessage::talkClosing, this, &TabSupervisor::talkLeft);
|
connect(tab, &TabMessage::talkClosing, this, &TabSupervisor::talkLeft);
|
||||||
connect(tab, &TabMessage::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
|
connect(tab, &TabMessage::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
|
||||||
|
|
@ -1242,7 +1300,7 @@ void TabSupervisor::tabUserEvent(bool globalEvent)
|
||||||
auto *tab = static_cast<Tab *>(sender());
|
auto *tab = static_cast<Tab *>(sender());
|
||||||
if (tab != currentWidget()) {
|
if (tab != currentWidget()) {
|
||||||
tab->setContentsChanged(true);
|
tab->setContentsChanged(true);
|
||||||
setTabIcon(indexOf(tab), QPixmap("theme:icons/tab_changed"));
|
setTabIcon(indexOf(tab), themePixmap(QStringLiteral("icons/tab_changed")));
|
||||||
}
|
}
|
||||||
if (globalEvent && SettingsCache::instance().userInterface().getNotificationsEnabled()) {
|
if (globalEvent && SettingsCache::instance().userInterface().getNotificationsEnabled()) {
|
||||||
QApplication::alert(this);
|
QApplication::alert(this);
|
||||||
|
|
@ -1277,7 +1335,21 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont)
|
||||||
|
|
||||||
void TabSupervisor::processUserMessageEvent(const Event_UserMessage &event)
|
void TabSupervisor::processUserMessageEvent(const Event_UserMessage &event)
|
||||||
{
|
{
|
||||||
|
// "Ignore all private messages" silences every PM, including messages to
|
||||||
|
// already-open tabs — unlike the unregistered/non-buddy filters below,
|
||||||
|
// which only apply when creating a new tab. Messages from moderators/admins
|
||||||
|
// are exempt to ensure warnings still reach users.
|
||||||
QString senderName = QString::fromStdString(event.sender_name());
|
QString senderName = QString::fromStdString(event.sender_name());
|
||||||
|
if (SettingsCache::instance().chat().getIgnoreAllPrivateMessages()) {
|
||||||
|
const ServerInfo_User *onlineUserInfo = userListManager->getOnlineUser(senderName);
|
||||||
|
if (!onlineUserInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const UserLevelFlags userLevel(onlineUserInfo->user_level());
|
||||||
|
if (!userLevel.testFlag(ServerInfo_User::IsModerator) && !userLevel.testFlag(ServerInfo_User::IsAdmin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
TabMessage *tab = messageTabs.value(senderName);
|
TabMessage *tab = messageTabs.value(senderName);
|
||||||
if (!tab) {
|
if (!tab) {
|
||||||
tab = messageTabs.value(QString::fromStdString(event.receiver_name()));
|
tab = messageTabs.value(QString::fromStdString(event.receiver_name()));
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class TabReport;
|
||||||
class TabModeration;
|
class TabModeration;
|
||||||
class TabAccount;
|
class TabAccount;
|
||||||
class TabDeckEditor;
|
class TabDeckEditor;
|
||||||
|
class TabDeveloper;
|
||||||
class TabLog;
|
class TabLog;
|
||||||
class RoomEvent;
|
class RoomEvent;
|
||||||
class GameEventContainer;
|
class GameEventContainer;
|
||||||
|
|
@ -108,6 +109,7 @@ private:
|
||||||
TabLog *tabLog;
|
TabLog *tabLog;
|
||||||
TabReport *tabReport;
|
TabReport *tabReport;
|
||||||
TabModeration *tabModeration;
|
TabModeration *tabModeration;
|
||||||
|
TabDeveloper *tabDeveloper;
|
||||||
QMap<int, TabRoom *> roomTabs;
|
QMap<int, TabRoom *> roomTabs;
|
||||||
QMap<int, TabGame *> gameTabs;
|
QMap<int, TabGame *> gameTabs;
|
||||||
QList<TabGame *> replayTabs;
|
QList<TabGame *> replayTabs;
|
||||||
|
|
@ -117,7 +119,7 @@ private:
|
||||||
|
|
||||||
QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage,
|
QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage,
|
||||||
*aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin,
|
*aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin,
|
||||||
*aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration;
|
*aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration, *aTabDeveloper;
|
||||||
|
|
||||||
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
||||||
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
||||||
|
|
@ -207,6 +209,7 @@ private slots:
|
||||||
void actTabLog(bool checked);
|
void actTabLog(bool checked);
|
||||||
void actTabReport(bool checked);
|
void actTabReport(bool checked);
|
||||||
void actTabModeration(bool checked);
|
void actTabModeration(bool checked);
|
||||||
|
void actTabDeveloper(bool checked);
|
||||||
|
|
||||||
void openTabVisualDeckStorage();
|
void openTabVisualDeckStorage();
|
||||||
void openTabHome();
|
void openTabHome();
|
||||||
|
|
@ -218,6 +221,7 @@ private slots:
|
||||||
void openTabCardArtRules();
|
void openTabCardArtRules();
|
||||||
void openTabLog();
|
void openTabLog();
|
||||||
void openTabReport();
|
void openTabReport();
|
||||||
|
void openTabDeveloper();
|
||||||
|
|
||||||
void updateCurrent(int index);
|
void updateCurrent(int index);
|
||||||
void updatePingTime(int value, int max);
|
void updatePingTime(int value, int max);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
@ -14,8 +15,8 @@ SequenceEdit::SequenceEdit(const QString &_shortcutName, QWidget *parent) : QWid
|
||||||
defaultButton = new QPushButton("", this);
|
defaultButton = new QPushButton("", this);
|
||||||
|
|
||||||
lineEdit->setMinimumWidth(70);
|
lineEdit->setMinimumWidth(70);
|
||||||
clearButton->setIcon(QPixmap("theme:icons/clearsearch"));
|
clearButton->setIcon(themePixmap(QStringLiteral("icons/clearsearch")));
|
||||||
defaultButton->setIcon(QPixmap("theme:icons/update"));
|
defaultButton->setIcon(themePixmap(QStringLiteral("icons/update")));
|
||||||
|
|
||||||
auto *layout = new QHBoxLayout(this);
|
auto *layout = new QHBoxLayout(this);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "visual_database_display_filter_toolbar_widget.h"
|
#include "visual_database_display_filter_toolbar_widget.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../deck_editor/card_database_view.h"
|
#include "../deck_editor/card_database_view.h"
|
||||||
#include "visual_database_display_widget.h"
|
#include "visual_database_display_widget.h"
|
||||||
|
|
||||||
|
|
@ -60,22 +61,22 @@ VisualDatabaseDisplayFilterToolbarWidget::VisualDatabaseDisplayFilterToolbarWidg
|
||||||
});
|
});
|
||||||
|
|
||||||
quickFilterSaveLoadWidget = new SettingsButtonWidget(this);
|
quickFilterSaveLoadWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterSaveLoadWidget->setButtonIcon(QPixmap("theme:icons/floppy_disk"));
|
quickFilterSaveLoadWidget->setButtonIcon(themePixmap(QStringLiteral("icons/floppy_disk")));
|
||||||
|
|
||||||
quickFilterNameWidget = new SettingsButtonWidget(this);
|
quickFilterNameWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterNameWidget->setButtonIcon(QPixmap("theme:icons/pen_to_square"));
|
quickFilterNameWidget->setButtonIcon(themePixmap(QStringLiteral("icons/pen_to_square")));
|
||||||
|
|
||||||
quickFilterMainTypeWidget = new SettingsButtonWidget(this);
|
quickFilterMainTypeWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterMainTypeWidget->setButtonIcon(QPixmap("theme:icons/circle_half_stroke"));
|
quickFilterMainTypeWidget->setButtonIcon(themePixmap(QStringLiteral("icons/circle_half_stroke")));
|
||||||
|
|
||||||
quickFilterSubTypeWidget = new SettingsButtonWidget(this);
|
quickFilterSubTypeWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterSubTypeWidget->setButtonIcon(QPixmap("theme:icons/dragon"));
|
quickFilterSubTypeWidget->setButtonIcon(themePixmap(QStringLiteral("icons/dragon")));
|
||||||
|
|
||||||
quickFilterSetWidget = new SettingsButtonWidget(this);
|
quickFilterSetWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterSetWidget->setButtonIcon(QPixmap("theme:icons/scroll"));
|
quickFilterSetWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scroll")));
|
||||||
|
|
||||||
quickFilterFormatLegalityWidget = new SettingsButtonWidget(this);
|
quickFilterFormatLegalityWidget = new SettingsButtonWidget(this);
|
||||||
quickFilterFormatLegalityWidget->setButtonIcon(QPixmap("theme:icons/scale_balanced"));
|
quickFilterFormatLegalityWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scale_balanced")));
|
||||||
|
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
||||||
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
||||||
searchEdit->setClearButtonEnabled(true);
|
searchEdit->setClearButtonEnabled(true);
|
||||||
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||||
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
auto help = searchEdit->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition);
|
||||||
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(searchEdit); });
|
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(searchEdit); });
|
||||||
|
|
||||||
setFocusProxy(searchEdit);
|
setFocusProxy(searchEdit);
|
||||||
|
|
@ -121,7 +121,7 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
||||||
|
|
||||||
clearFilterWidget = new QToolButton();
|
clearFilterWidget = new QToolButton();
|
||||||
clearFilterWidget->setFixedSize(32, 32);
|
clearFilterWidget->setFixedSize(32, 32);
|
||||||
clearFilterWidget->setIcon(QPixmap("theme:icons/delete"));
|
clearFilterWidget->setIcon(themePixmap(QStringLiteral("icons/delete")));
|
||||||
connect(clearFilterWidget, &QToolButton::clicked, this, [this] {
|
connect(clearFilterWidget, &QToolButton::clicked, this, [this] {
|
||||||
filterModel->blockSignals(true);
|
filterModel->blockSignals(true);
|
||||||
filterModel->filterTree()->blockSignals(true);
|
filterModel->filterTree()->blockSignals(true);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "visual_deck_display_options_widget.h"
|
#include "visual_deck_display_options_widget.h"
|
||||||
|
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../tabs/visual_deck_editor/tab_deck_editor_visual.h"
|
#include "../tabs/visual_deck_editor/tab_deck_editor_visual.h"
|
||||||
|
|
||||||
#include <libcockatrice/utility/qt_utils.h>
|
#include <libcockatrice/utility/qt_utils.h>
|
||||||
|
|
@ -47,7 +48,7 @@ VisualDeckDisplayOptionsWidget::VisualDeckDisplayOptionsWidget(QWidget *parent)
|
||||||
sortByLabel = new QLabel(this);
|
sortByLabel = new QLabel(this);
|
||||||
|
|
||||||
sortCriteriaButton = new SettingsButtonWidget(this);
|
sortCriteriaButton = new SettingsButtonWidget(this);
|
||||||
sortCriteriaButton->setButtonIcon(QPixmap("theme:icons/sort_arrow_down"));
|
sortCriteriaButton->setButtonIcon(themePixmap(QStringLiteral("icons/sort_arrow_down")));
|
||||||
|
|
||||||
sortLabel = new QLabel(sortCriteriaButton);
|
sortLabel = new QLabel(sortCriteriaButton);
|
||||||
sortLabel->setWordWrap(true);
|
sortLabel->setWordWrap(true);
|
||||||
|
|
@ -92,7 +93,7 @@ void VisualDeckDisplayOptionsWidget::retranslateUi()
|
||||||
sortLabel->setText(tr("Click and drag to change the sort order within the groups"));
|
sortLabel->setText(tr("Click and drag to change the sort order within the groups"));
|
||||||
sortCriteriaButton->setToolTip(tr("Configure how cards are sorted within their groups"));
|
sortCriteriaButton->setToolTip(tr("Configure how cards are sorted within their groups"));
|
||||||
displayTypeButton->setButtonText(tr("Toggle Layout: Overlap"));
|
displayTypeButton->setButtonText(tr("Toggle Layout: Overlap"));
|
||||||
displayTypeButton->setButtonIcon(QPixmap("theme:icons/scales"));
|
displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scales")));
|
||||||
displayTypeButton->setToolTip(
|
displayTypeButton->setToolTip(
|
||||||
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
|
tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)"));
|
||||||
}
|
}
|
||||||
|
|
@ -117,11 +118,11 @@ void VisualDeckDisplayOptionsWidget::updateDisplayType()
|
||||||
switch (currentDisplayType) {
|
switch (currentDisplayType) {
|
||||||
case DisplayType::Flat:
|
case DisplayType::Flat:
|
||||||
displayTypeButton->setButtonText(tr("Toggle Layout: Flat"));
|
displayTypeButton->setButtonText(tr("Toggle Layout: Flat"));
|
||||||
displayTypeButton->setButtonIcon(QPixmap("theme:icons/scroll"));
|
displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scroll")));
|
||||||
break;
|
break;
|
||||||
case DisplayType::Overlap:
|
case DisplayType::Overlap:
|
||||||
displayTypeButton->setButtonText(tr("Toggle Layout: Overlap"));
|
displayTypeButton->setButtonText(tr("Toggle Layout: Overlap"));
|
||||||
displayTypeButton->setButtonIcon(QPixmap("theme:icons/scales"));
|
displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scales")));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
emit displayTypeChanged(currentDisplayType);
|
emit displayTypeChanged(currentDisplayType);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../../../main.h"
|
#include "../../../main.h"
|
||||||
#include "../../deck_loader/deck_loader.h"
|
#include "../../deck_loader/deck_loader.h"
|
||||||
#include "../../layouts/overlap_layout.h"
|
#include "../../layouts/overlap_layout.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
||||||
#include "../cards/deck_card_zone_display_widget.h"
|
#include "../cards/deck_card_zone_display_widget.h"
|
||||||
#include "../general/layout_containers/flow_widget.h"
|
#include "../general/layout_containers/flow_widget.h"
|
||||||
|
|
@ -131,7 +132,7 @@ void VisualDeckEditorWidget::initializeSearchBarAndCompleter()
|
||||||
|
|
||||||
// Search button functionality
|
// Search button functionality
|
||||||
searchPushButton = new CompactPushButton(searchContainer);
|
searchPushButton = new CompactPushButton(searchContainer);
|
||||||
searchPushButton->setButtonIcon(QPixmap("theme:icons/search"));
|
searchPushButton->setButtonIcon(themePixmap(QStringLiteral("icons/search")));
|
||||||
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() {
|
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() {
|
||||||
ExactCard card = CardDatabaseManager::query()->getCard({searchBar->text()});
|
ExactCard card = CardDatabaseManager::query()->getCard({searchBar->text()});
|
||||||
if (card) {
|
if (card) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(QWidget *parent) :
|
||||||
searchBar->setClearButtonEnabled(true);
|
searchBar->setClearButtonEnabled(true);
|
||||||
searchBar->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchBar->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||||
|
|
||||||
auto help = searchBar->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
auto help = searchBar->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition);
|
||||||
connect(help, &QAction::triggered, this, [this] { createDeckSearchSyntaxHelpWindow(searchBar); });
|
connect(help, &QAction::triggered, this, [this] { createDeckSearchSyntaxHelpWindow(searchBar); });
|
||||||
|
|
||||||
layout->addWidget(searchBar);
|
layout->addWidget(searchBar);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "visual_deck_storage_widget.h"
|
#include "visual_deck_storage_widget.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../pixel_map_generator.h"
|
||||||
#include "../quick_settings/settings_button_widget.h"
|
#include "../quick_settings/settings_button_widget.h"
|
||||||
#include "deck_preview/deck_preview_color_identity_filter_widget.h"
|
#include "deck_preview/deck_preview_color_identity_filter_widget.h"
|
||||||
#include "deck_preview/deck_preview_widget.h"
|
#include "deck_preview/deck_preview_widget.h"
|
||||||
|
|
@ -43,7 +44,7 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
||||||
searchWidget = new VisualDeckStorageSearchWidget(this);
|
searchWidget = new VisualDeckStorageSearchWidget(this);
|
||||||
|
|
||||||
refreshButton = new QToolButton(this);
|
refreshButton = new QToolButton(this);
|
||||||
refreshButton->setIcon(QPixmap("theme:icons/reload"));
|
refreshButton->setIcon(themePixmap(QStringLiteral("icons/reload")));
|
||||||
refreshButton->setFixedSize(32, 32);
|
refreshButton->setFixedSize(32, 32);
|
||||||
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
|
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
#include "intents/intent_open_server_room_by_name.h"
|
#include "intents/intent_open_server_room_by_name.h"
|
||||||
#include "intents/url_parser.h"
|
#include "intents/url_parser.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "pixel_map_generator.h"
|
||||||
#include "version_string.h"
|
#include "version_string.h"
|
||||||
#include "widgets/dialogs/dlg_connect.h"
|
#include "widgets/dialogs/dlg_connect.h"
|
||||||
#include "widgets/server/handle_public_servers.h"
|
#include "widgets/server/handle_public_servers.h"
|
||||||
|
|
@ -91,8 +92,8 @@
|
||||||
#include <libcockatrice/settings/updates_settings.h>
|
#include <libcockatrice/settings/updates_settings.h>
|
||||||
|
|
||||||
#define GITHUB_PAGES_URL "https://cockatrice.github.io"
|
#define GITHUB_PAGES_URL "https://cockatrice.github.io"
|
||||||
#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors?type=c"
|
#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors"
|
||||||
#define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#cockatrice"
|
#define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#"
|
||||||
#define GITHUB_TRANSIFEX_TRANSLATORS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translator-Hall-of-Fame"
|
#define GITHUB_TRANSIFEX_TRANSLATORS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translator-Hall-of-Fame"
|
||||||
#define GITHUB_TRANSLATOR_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ"
|
#define GITHUB_TRANSLATOR_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ"
|
||||||
#define GITHUB_ISSUES_URL "https://github.com/Cockatrice/Cockatrice/issues"
|
#define GITHUB_ISSUES_URL "https://github.com/Cockatrice/Cockatrice/issues"
|
||||||
|
|
@ -271,7 +272,8 @@ void MainWindow::actAbout()
|
||||||
GITHUB_TROUBLESHOOTING_URL + "'>" + tr("Troubleshooting") + "</a><br>" + "<a href='" + GITHUB_FAQ_URL +
|
GITHUB_TROUBLESHOOTING_URL + "'>" + tr("Troubleshooting") + "</a><br>" + "<a href='" + GITHUB_FAQ_URL +
|
||||||
"'>" + tr("F.A.Q.") + "</a><br>"),
|
"'>" + tr("F.A.Q.") + "</a><br>"),
|
||||||
QMessageBox::Ok, this);
|
QMessageBox::Ok, this);
|
||||||
mb.setIconPixmap(QPixmap("theme:cockatrice").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
mb.setIconPixmap(
|
||||||
|
themePixmap(QStringLiteral("cockatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
mb.setTextInteractionFlags(Qt::TextBrowserInteraction);
|
mb.setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||||
mb.exec();
|
mb.exec();
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +325,7 @@ void MainWindow::retranslateUi()
|
||||||
aRegister->setText(tr("&Register to server..."));
|
aRegister->setText(tr("&Register to server..."));
|
||||||
aForgotPassword->setText(tr("&Restore password..."));
|
aForgotPassword->setText(tr("&Restore password..."));
|
||||||
aSettings->setText(tr("&Settings..."));
|
aSettings->setText(tr("&Settings..."));
|
||||||
aSettings->setIcon(QPixmap("theme:icons/settings"));
|
aSettings->setIcon(themePixmap(QStringLiteral("icons/settings")));
|
||||||
aExit->setText(tr("&Exit"));
|
aExit->setText(tr("&Exit"));
|
||||||
|
|
||||||
#if defined(__APPLE__) /* For OSX */
|
#if defined(__APPLE__) /* For OSX */
|
||||||
|
|
@ -682,6 +684,7 @@ void MainWindow::runFirstRunWizard()
|
||||||
connect(wizard, &FirstRunWizard::cardDatabaseUpdateRequested, this, &MainWindow::actCheckCardUpdatesBackground);
|
connect(wizard, &FirstRunWizard::cardDatabaseUpdateRequested, this, &MainWindow::actCheckCardUpdatesBackground);
|
||||||
connect(wizard, &FirstRunWizard::manualCardDatabaseSetupRequested, this, &MainWindow::actCheckCardUpdates);
|
connect(wizard, &FirstRunWizard::manualCardDatabaseSetupRequested, this, &MainWindow::actCheckCardUpdates);
|
||||||
connect(this, &MainWindow::cardDatabaseUpdateFinished, wizard, &FirstRunWizard::onCardDatabaseUpdateFinished);
|
connect(this, &MainWindow::cardDatabaseUpdateFinished, wizard, &FirstRunWizard::onCardDatabaseUpdateFinished);
|
||||||
|
connect(this, &MainWindow::cardDatabaseUpdateProgress, wizard, &FirstRunWizard::onCardDatabaseUpdateProgress);
|
||||||
connect(wizard, &FirstRunWizard::registerRequested, connectionController, &ConnectionController::registerToServer);
|
connect(wizard, &FirstRunWizard::registerRequested, connectionController, &ConnectionController::registerToServer);
|
||||||
connect(wizard, &FirstRunWizard::connectRequested, connectionController, &ConnectionController::connectToServer);
|
connect(wizard, &FirstRunWizard::connectRequested, connectionController, &ConnectionController::connectToServer);
|
||||||
|
|
||||||
|
|
@ -816,7 +819,7 @@ void MainWindow::createTrayIcon()
|
||||||
|
|
||||||
trayIcon = new QSystemTrayIcon(this);
|
trayIcon = new QSystemTrayIcon(this);
|
||||||
trayIcon->setContextMenu(trayIconMenu);
|
trayIcon->setContextMenu(trayIconMenu);
|
||||||
trayIcon->setIcon(QPixmap("theme:cockatrice"));
|
trayIcon->setIcon(themePixmap(QStringLiteral("cockatrice")));
|
||||||
trayIcon->show();
|
trayIcon->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -843,6 +846,17 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
||||||
}
|
}
|
||||||
bClosingDown = true;
|
bClosingDown = true;
|
||||||
|
|
||||||
|
if (cardUpdateProcess && cardUpdateProcess->state() != QProcess::NotRunning) {
|
||||||
|
if (QMessageBox::question(this, tr("Are you sure?"),
|
||||||
|
tr("A card database update is still running. Quitting now will cancel it.\n"
|
||||||
|
"Are you sure you want to quit?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
|
||||||
|
event->ignore();
|
||||||
|
bClosingDown = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!tabSupervisor->close()) {
|
if (!tabSupervisor->close()) {
|
||||||
event->ignore();
|
event->ignore();
|
||||||
bClosingDown = false;
|
bClosingDown = false;
|
||||||
|
|
@ -1036,7 +1050,7 @@ void MainWindow::createCardUpdateProcess(bool background)
|
||||||
|
|
||||||
if (dir.exists(binaryName)) {
|
if (dir.exists(binaryName)) {
|
||||||
updaterCmd = dir.absoluteFilePath(binaryName);
|
updaterCmd = dir.absoluteFilePath(binaryName);
|
||||||
} else { // try and find the directory oracle is stored in the build directory
|
} else { // try and find the directory Oracle is stored in the build directory
|
||||||
QDir findLocalDir(dir);
|
QDir findLocalDir(dir);
|
||||||
findLocalDir.cdUp();
|
findLocalDir.cdUp();
|
||||||
findLocalDir.cd(getCardUpdaterBinaryName());
|
findLocalDir.cd(getCardUpdaterBinaryName());
|
||||||
|
|
@ -1057,11 +1071,45 @@ void MainWindow::createCardUpdateProcess(bool background)
|
||||||
if (!background) {
|
if (!background) {
|
||||||
cardUpdateProcess->start(updaterCmd, QStringList());
|
cardUpdateProcess->start(updaterCmd, QStringList());
|
||||||
} else {
|
} else {
|
||||||
|
cardUpdateOutputBuffer.clear();
|
||||||
|
connect(cardUpdateProcess, &QProcess::readyReadStandardOutput, this, &MainWindow::cardUpdateProgressOutput);
|
||||||
cardUpdateProcess->start(updaterCmd, QStringList("-b"));
|
cardUpdateProcess->start(updaterCmd, QStringList("-b"));
|
||||||
statusBar()->showMessage(tr("Card database update running."));
|
statusBar()->showMessage(tr("Card database update running."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::cardUpdateProgressOutput()
|
||||||
|
{
|
||||||
|
if (!cardUpdateProcess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cardUpdateOutputBuffer.append(cardUpdateProcess->readAllStandardOutput());
|
||||||
|
while (true) {
|
||||||
|
const int newline = cardUpdateOutputBuffer.indexOf('\n');
|
||||||
|
if (newline < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const QByteArray line = cardUpdateOutputBuffer.left(newline).trimmed();
|
||||||
|
cardUpdateOutputBuffer.remove(0, newline + 1);
|
||||||
|
// Protocol emitted by `oracle -b`: "PROGRESS <stage> <done> <total>"
|
||||||
|
if (!line.startsWith("PROGRESS ")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QList<QByteArray> parts = line.split(' ');
|
||||||
|
if (parts.size() != 4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool doneOk = false;
|
||||||
|
bool totalOk = false;
|
||||||
|
const qint64 done = parts.at(2).toLongLong(&doneOk);
|
||||||
|
const qint64 total = parts.at(3).toLongLong(&totalOk);
|
||||||
|
if (!doneOk || !totalOk || done < 0 || total < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
emit cardDatabaseUpdateProgress(QString::fromLatin1(parts.at(1)), done, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::exitCardDatabaseUpdate()
|
void MainWindow::exitCardDatabaseUpdate()
|
||||||
{
|
{
|
||||||
if (!cardUpdateProcess) {
|
if (!cardUpdateProcess) {
|
||||||
|
|
@ -1109,6 +1157,8 @@ void MainWindow::cardUpdateError(QProcess::ProcessError err)
|
||||||
|
|
||||||
void MainWindow::cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
void MainWindow::cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||||
{
|
{
|
||||||
|
cardUpdateProgressOutput(); // drain any progress lines not yet parsed
|
||||||
|
|
||||||
const bool success = (exitStatus == QProcess::NormalExit) && (exitCode == 0);
|
const bool success = (exitStatus == QProcess::NormalExit) && (exitCode == 0);
|
||||||
if (exitStatus == QProcess::NormalExit) {
|
if (exitStatus == QProcess::NormalExit) {
|
||||||
SettingsCache::instance().updates().setLastCardUpdateCheck(QDateTime::currentDateTime().date());
|
SettingsCache::instance().updates().setLastCardUpdateCheck(QDateTime::currentDateTime().date());
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,11 @@ signals:
|
||||||
/** @brief Emitted after the background card-database update subprocess exits. */
|
/** @brief Emitted after the background card-database update subprocess exits. */
|
||||||
void cardDatabaseUpdateFinished(bool success);
|
void cardDatabaseUpdateFinished(bool success);
|
||||||
|
|
||||||
|
/** @brief Emitted while the background card-database update subprocess runs.
|
||||||
|
* @p stage is one of "download", "scan" or "import"; @p done/@p total
|
||||||
|
* are byte counts for the first two stages and set indices for "import". */
|
||||||
|
void cardDatabaseUpdateProgress(const QString &stage, qint64 done, qint64 total);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void actCheckCardUpdates();
|
void actCheckCardUpdates();
|
||||||
void actCheckCardUpdatesBackground();
|
void actCheckCardUpdatesBackground();
|
||||||
|
|
@ -96,6 +101,7 @@ private slots:
|
||||||
|
|
||||||
void cardUpdateError(QProcess::ProcessError err);
|
void cardUpdateError(QProcess::ProcessError err);
|
||||||
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
void cardUpdateProgressOutput();
|
||||||
void refreshShortcuts();
|
void refreshShortcuts();
|
||||||
void cardDatabaseLoadingFailed();
|
void cardDatabaseLoadingFailed();
|
||||||
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
|
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
|
||||||
|
|
@ -159,6 +165,7 @@ private:
|
||||||
LatencyStatusWidget *latencyStatus = nullptr; ///< status bar widget with live round-trip stats and history graph
|
LatencyStatusWidget *latencyStatus = nullptr; ///< status bar widget with live round-trip stats and history graph
|
||||||
bool bHasActivated, askedForDbUpdater;
|
bool bHasActivated, askedForDbUpdater;
|
||||||
QProcess *cardUpdateProcess;
|
QProcess *cardUpdateProcess;
|
||||||
|
QByteArray cardUpdateOutputBuffer;
|
||||||
DlgViewLog *logviewDialog;
|
DlgViewLog *logviewDialog;
|
||||||
GameReplay *replay;
|
GameReplay *replay;
|
||||||
DlgTipOfTheDay *tip;
|
DlgTipOfTheDay *tip;
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ int main(int argc, char *argv[])
|
||||||
// These values are only used by the settings loader/saver
|
// These values are only used by the settings loader/saver
|
||||||
// Wrong or outdated values are kept to not break things
|
// Wrong or outdated values are kept to not break things
|
||||||
QCoreApplication::setOrganizationName("Cockatrice");
|
QCoreApplication::setOrganizationName("Cockatrice");
|
||||||
QCoreApplication::setOrganizationDomain("cockatrice.de");
|
QCoreApplication::setOrganizationDomain("cockatrice.github.io");
|
||||||
QCoreApplication::setApplicationName("Cockatrice");
|
QCoreApplication::setApplicationName("Cockatrice");
|
||||||
QCoreApplication::setApplicationVersion(VERSION_STRING);
|
QCoreApplication::setApplicationVersion(VERSION_STRING);
|
||||||
|
|
||||||
|
|
@ -250,7 +250,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
// Command-line parser
|
// Command-line parser
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription("Cockatrice");
|
parser.setApplicationDescription("Cockatrice Client");
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
|
|
@ -349,9 +349,9 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
qCInfo(MainLog) << "MainWindow constructor finished";
|
qCInfo(MainLog) << "MainWindow constructor finished";
|
||||||
|
|
||||||
ui.setWindowIcon(QPixmap("theme:cockatrice"));
|
ui.setWindowIcon(themePixmap(QStringLiteral("cockatrice")));
|
||||||
// set name of the app desktop file; used by wayland to load the window icon
|
// Set name of the app desktop file; used by wayland to load the window icon
|
||||||
QGuiApplication::setDesktopFileName("cockatrice");
|
QGuiApplication::setDesktopFileName("Cockatrice");
|
||||||
|
|
||||||
SettingsCache::instance().network().setClientID(generateClientID());
|
SettingsCache::instance().network().setClientID(generateClientID());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public:
|
||||||
[[nodiscard]] virtual bool getShowMessagePopup() const = 0;
|
[[nodiscard]] virtual bool getShowMessagePopup() const = 0;
|
||||||
[[nodiscard]] virtual bool getShowMentionPopup() const = 0;
|
[[nodiscard]] virtual bool getShowMentionPopup() const = 0;
|
||||||
[[nodiscard]] virtual bool getRoomHistory() const = 0;
|
[[nodiscard]] virtual bool getRoomHistory() const = 0;
|
||||||
|
[[nodiscard]] virtual bool getIgnoreAllPrivateMessages() const = 0;
|
||||||
[[nodiscard]] virtual QString getHighlightWords() const = 0;
|
[[nodiscard]] virtual QString getHighlightWords() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,3 +253,24 @@ PendingCommand *AbstractClient::prepareAdminCommand(const ::google::protobuf::Me
|
||||||
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
|
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
|
||||||
return new PendingCommand(cont);
|
return new PendingCommand(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PendingCommand *AbstractClient::prepareDeveloperCommand(const ::google::protobuf::Message &cmd)
|
||||||
|
{
|
||||||
|
CommandContainer cont;
|
||||||
|
DeveloperCommand *c = cont.add_developer_command();
|
||||||
|
// A developer command message may also be usable through other command
|
||||||
|
// families, so select the extension scoped to DeveloperCommand rather than
|
||||||
|
// guessing by name.
|
||||||
|
const ::google::protobuf::Descriptor *cmdDescriptor = cmd.GetDescriptor();
|
||||||
|
const ::google::protobuf::Descriptor *developerDescriptor = DeveloperCommand::descriptor();
|
||||||
|
const ::google::protobuf::FieldDescriptor *developerExtension = nullptr;
|
||||||
|
for (int i = 0; i < cmdDescriptor->extension_count(); ++i) {
|
||||||
|
if (cmdDescriptor->extension(i)->containing_type() == developerDescriptor) {
|
||||||
|
developerExtension = cmdDescriptor->extension(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_ASSERT(developerExtension != nullptr);
|
||||||
|
c->GetReflection()->MutableMessage(c, developerExtension)->CopyFrom(cmd);
|
||||||
|
return new PendingCommand(cont);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,7 @@ public:
|
||||||
static PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd, int roomId);
|
static PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd, int roomId);
|
||||||
static PendingCommand *prepareModeratorCommand(const ::google::protobuf::Message &cmd);
|
static PendingCommand *prepareModeratorCommand(const ::google::protobuf::Message &cmd);
|
||||||
static PendingCommand *prepareAdminCommand(const ::google::protobuf::Message &cmd);
|
static PendingCommand *prepareAdminCommand(const ::google::protobuf::Message &cmd);
|
||||||
|
static PendingCommand *prepareDeveloperCommand(const ::google::protobuf::Message &cmd);
|
||||||
|
|
||||||
QMap<QString, bool> clientFeatures;
|
QMap<QString, bool> clientFeatures;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue