diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index 7b52b7bcc..5af116470 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -117,21 +117,22 @@ ${If} $InstDir == "" ; we need to set a default based on the install mode StrCpy $InstDir $0 ${EndIf} -Call SetModeDestinationFromInstdir -; --- Detect portable install when using /R --- +; --- Detect portable install when using /R (must come BEFORE SetModeDestinationFromInstdir) --- ${If} $ReinstallMode = 1 IfFileExists "$InstDir\portable.dat" 0 not_portable StrCpy $PortableMode 1 Goto portable_done - not_portable: StrCpy $PortableMode 0 - portable_done: ${EndIf} +; Now that $PortableMode reflects reality, commit InstDir into the correct slot +Call SetModeDestinationFromInstdir + ${If} $ReinstallMode = 1 +${AndIf} $PortableMode = 0 Call AutoUninstallIfNeeded ${EndIf} diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 73e5a98a1..cc34e1707 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -309,6 +309,7 @@ SettingsCache::SettingsCache() cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt(); closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool(); focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool(); + keepGameChatFocus = settings->value("interface/keepGameChatFocus", false).toBool(); showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool(); showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool(); @@ -457,6 +458,13 @@ void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T value) settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar); } +void SettingsCache::setKeepGameChatFocus(QT_STATE_CHANGED_T value) +{ + keepGameChatFocus = value; + settings->setValue("interface/keepGameChatFocus", keepGameChatFocus); + emit keepGameChatFocusChanged(keepGameChatFocus); +} + void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures) { knownMissingFeatures = _knownMissingFeatures; diff --git a/cockatrice/src/client/settings/cache_settings.h b/cockatrice/src/client/settings/cache_settings.h index 8ee372766..a166917c1 100644 --- a/cockatrice/src/client/settings/cache_settings.h +++ b/cockatrice/src/client/settings/cache_settings.h @@ -195,6 +195,7 @@ signals: void downloadSpoilerStatusChanged(); void useTearOffMenusChanged(bool state); void roundCardCornersChanged(bool roundCardCorners); + void keepGameChatFocusChanged(bool value); private: QSettings *settings; @@ -306,6 +307,7 @@ private: int cardViewExpandedRowsMax; bool closeEmptyCardView; bool focusCardViewSearchBar; + bool keepGameChatFocus; int pixmapCacheSize; int networkCacheSize; int redirectCacheTtl; @@ -935,6 +937,7 @@ public: void setCardViewExpandedRowsMax(int value); void setCloseEmptyCardView(QT_STATE_CHANGED_T value); void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value); + void setKeepGameChatFocus(QT_STATE_CHANGED_T value); QString getClientID() override { return clientID; @@ -967,6 +970,10 @@ public: { return focusCardViewSearchBar; } + [[nodiscard]] bool getKeepGameChatFocus() const + { + return keepGameChatFocus; + } [[nodiscard]] ShortcutsSettings &shortcuts() const { return *shortcutsSettings; diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index 45e2c4fca..95155b8d1 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -223,6 +223,10 @@ private: {"TabDeckEditor/aLoadDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck..."), parseSequenceString("Ctrl+O"), ShortcutGroup::Deck_Editor)}, + {"TabDeckEditor/aLoadDeckFromWebsite", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load deck from online service..."), + parseSequenceString("Ctrl+Shift+O"), + ShortcutGroup::Deck_Editor)}, {"TabDeckEditor/aLoadDeckFromClipboard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), parseSequenceString("Ctrl+Shift+V"), @@ -283,6 +287,10 @@ private: ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), parseSequenceString("Ctrl+Shift+V"), ShortcutGroup::Game_Lobby)}, + {"DeckViewContainer/loadFromWebsiteButton", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load from website..."), + parseSequenceString("Ctrl+Shift+O"), + ShortcutGroup::Game_Lobby)}, {"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"), parseSequenceString("Ctrl+Alt+U"), ShortcutGroup::Game_Lobby)}, diff --git a/cockatrice/src/game/deckview/deck_view.cpp b/cockatrice/src/game/deckview/deck_view.cpp index ced02c8db..a5d0fa3bc 100644 --- a/cockatrice/src/game/deckview/deck_view.cpp +++ b/cockatrice/src/game/deckview/deck_view.cpp @@ -360,6 +360,16 @@ void DeckViewScene::rebuildTree() return; } + QStringList requiredZones = {DECK_ZONE_MAIN, DECK_ZONE_SIDE}; + + for (const QString &zoneName : requiredZones) { + if (!cardContainers.contains(zoneName)) { + auto *container = new DeckViewCardContainer(zoneName); + cardContainers.insert(zoneName, container); + addItem(container); + } + } + for (auto *currentZone : deck->getZoneNodes()) { DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0); if (!container) { diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp index cbd6c2bad..21284c517 100644 --- a/cockatrice/src/game/deckview/deck_view_container.cpp +++ b/cockatrice/src/game/deckview/deck_view_container.cpp @@ -209,6 +209,7 @@ void DeckViewContainer::refreshShortcuts() loadLocalButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadLocalButton")); loadRemoteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadRemoteButton")); loadFromClipboardButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadFromClipboardButton")); + loadFromWebsiteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadFromWebsiteButton")); unloadDeckButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/unloadDeckButton")); readyStartButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/readyStartButton")); sideboardLockButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/sideboardLockButton")); diff --git a/cockatrice/src/game/game_view.cpp b/cockatrice/src/game/game_view.cpp index 4ba41cffb..41befd9a4 100644 --- a/cockatrice/src/game/game_view.cpp +++ b/cockatrice/src/game/game_view.cpp @@ -34,7 +34,6 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par { setBackgroundBrush(QBrush(QColor(0, 0, 0))); setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing); - setFocusPolicy(Qt::ClickFocus); setViewportUpdateMode(BoundingRectViewportUpdate); connect(scene, &GameScene::sceneRectChanged, this, &GameView::updateSceneRect); @@ -44,6 +43,9 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par connect(scene, &GameScene::sigStopRubberBand, this, &GameView::stopRubberBand); connect(scene, &QGraphicsScene::selectionChanged, this, [this]() { updateTotalSelectionCount(); }); + setFocusDisabled(SettingsCache::instance().getKeepGameChatFocus()); + connect(&SettingsCache::instance(), &SettingsCache::keepGameChatFocusChanged, this, &GameView::setFocusDisabled); + aCloseMostRecentZoneView = new QAction(this); connect(aCloseMostRecentZoneView, &QAction::triggered, scene, &GameScene::closeMostRecentZoneView); @@ -186,3 +188,12 @@ void GameView::updateTotalSelectionCount(const QSize &viewSize) totalCountLabel->hide(); } } + +/** + * Disabling focus on the game view will allow chat to maintain the autofocusing behavior of pre 2.10.3, + * at the cost of disabling the zone view search bar. + */ +void GameView::setFocusDisabled(bool disabled) +{ + setFocusPolicy(disabled ? Qt::NoFocus : Qt::ClickFocus); +} diff --git a/cockatrice/src/game/game_view.h b/cockatrice/src/game/game_view.h index 15abad9af..80e8e96b5 100644 --- a/cockatrice/src/game/game_view.h +++ b/cockatrice/src/game/game_view.h @@ -31,6 +31,7 @@ private slots: void stopRubberBand(); void refreshShortcuts(); void updateTotalSelectionCount(const QSize &viewSize = QSize()); + void setFocusDisabled(bool disabled); public slots: void updateSceneRect(const QRectF &rect); diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 1706c44dc..fc2d894af 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -874,7 +874,8 @@ void PlayerActions::actCreateToken() ExactCard correctedCard = CardDatabaseManager::query()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId}); if (correctedCard) { lastTokenInfo.name = correctedCard.getName(); - lastTokenTableRow = TableZone::tableRowToGridY(correctedCard.getInfo().getUiAttributes().tableRow); + int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow; + lastTokenTableRow = TableZone::tableRowToGridY(tableRow); if (lastTokenInfo.pt.isEmpty()) { lastTokenInfo.pt = correctedCard.getInfo().getPowTough(); } diff --git a/cockatrice/src/game_graphics/zones/view_zone_widget.cpp b/cockatrice/src/game_graphics/zones/view_zone_widget.cpp index 03c6d8925..ce3559ba3 100644 --- a/cockatrice/src/game_graphics/zones/view_zone_widget.cpp +++ b/cockatrice/src/game_graphics/zones/view_zone_widget.cpp @@ -75,6 +75,11 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player, searchEditProxy->setZValue(ZValues::DRAG_ITEM); vbox->addItem(searchEditProxy); + // hide search bar if chat autofocus setting is enabled, since typing into it will no longer work anyway + searchEditProxy->setVisible(!SettingsCache::instance().getKeepGameChatFocus()); + connect(&SettingsCache::instance(), &SettingsCache::keepGameChatFocusChanged, searchEditProxy, + [searchEditProxy](bool keepFocus) { searchEditProxy->setVisible(!keepFocus); }); + // top row QGraphicsLinearLayout *hTopRow = new QGraphicsLinearLayout(Qt::Horizontal); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp index f12550fa8..ee2149309 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp @@ -219,9 +219,25 @@ void DlgUpdate::downloadError(const QString &errorString) void DlgUpdate::downloadSuccessful(const QUrl &filepath) { setLabel(tr("Installing...")); + + QString installerPath = filepath.toLocalFile(); + + QString appDir = QDir::toNativeSeparators(QCoreApplication::applicationDirPath()); + QProcess process; + process.setProgram(installerPath); + + // NSIS needs the /D= argument to be an UNQUOTED string, even if it contains spaces. Qt likes to quote arguments if + // they contain spaces, so we use the windows exclusive QProcess::setNativeArguments in the only case where this is + // relevant, which preserves the argument unquoted. +#ifdef Q_OS_WIN + process.setNativeArguments(QString("/R /D=%1").arg(appDir)); +#else + // Linux/macOS: normal argument passing (not relevant since they update differently.) + process.setArguments({"/R", QString("/D=%1").arg(appDir)}); +#endif + // Try to open the installer. If it opens, quit Cockatrice - if (QProcess::startDetached( - QString("\"%1\" /R /D=\"%2\"").arg(filepath.toLocalFile(), QCoreApplication::applicationDirPath()))) { + if (process.startDetached()) { QMetaObject::invokeMethod(static_cast(parent()), "close", Qt::QueuedConnection); qCInfo(DlgUpdateLog) << "Opened downloaded update file successfully - closing Cockatrice"; close(); diff --git a/cockatrice/src/interface/widgets/menus/deck_editor_menu.cpp b/cockatrice/src/interface/widgets/menus/deck_editor_menu.cpp index 23d19abbb..d6df694df 100644 --- a/cockatrice/src/interface/widgets/menus/deck_editor_menu.cpp +++ b/cockatrice/src/interface/widgets/menus/deck_editor_menu.cpp @@ -193,6 +193,8 @@ void DeckEditorMenu::refreshShortcuts() aEditDeckInClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboardRaw")); aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck")); + aLoadDeckFromWebsite->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromWebsite")); + aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist")); aExportDeckDecklistXyz->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklistXyz")); aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck")); diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp index 195b1cc8d..faa96fa1f 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp @@ -476,10 +476,15 @@ void UserContextMenu::showContextMenu(const QPoint &pos, client->sendCommand(client->prepareSessionCommand(cmd)); } else if (actionClicked == aKick) { - Command_KickFromGame cmd; - cmd.set_player_id(playerId); + auto result = QMessageBox::question(static_cast(parent()), tr("Kick Player"), + tr("Are you sure you want to kick this player from the game?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (result == QMessageBox::Yes) { + Command_KickFromGame cmd; + cmd.set_player_id(playerId); - game->getGameEventHandler()->sendGameCommand(cmd); + game->getGameEventHandler()->sendGameCommand(cmd); + } } else if (actionClicked == aBan) { Command_GetUserInfo cmd; cmd.set_user_name(userName.toStdString()); diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp index dfa736a1a..6039e3758 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp @@ -72,6 +72,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); + keepGameChatFocusCheckBox.setChecked(SettingsCache::instance().getKeepGameChatFocus()); + connect(&keepGameChatFocusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setKeepGameChatFocus); + auto *generalGrid = new QGridLayout; generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); @@ -83,6 +87,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0); generalGrid->addWidget(&showTotalSelectionCountCheckBox, 8, 0); generalGrid->addWidget(&useTearOffMenusCheckBox, 9, 0); + generalGrid->addWidget(&keepGameChatFocusCheckBox, 10, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -207,6 +212,9 @@ void UserInterfaceSettingsPage::retranslateUi() showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection")); showTotalSelectionCountCheckBox.setText(tr("Show total selection counter")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); + keepGameChatFocusCheckBox.setText( + tr("Keep game chat focused when clicking in game (Note: disables card view search bar)")); + notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h index 6dd43ceae..e10ed2a06 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h @@ -30,6 +30,7 @@ private: QCheckBox showDragSelectionCountCheckBox; QCheckBox showTotalSelectionCountCheckBox; QCheckBox useTearOffMenusCheckBox; + QCheckBox keepGameChatFocusCheckBox; QCheckBox tapAnimationCheckBox; QCheckBox openDeckInNewTabCheckBox; QLabel visualDeckStoragePromptForConversionLabel; diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp index 951381aa4..edad46174 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database.cpp @@ -92,7 +92,7 @@ void CardDatabase::refreshCachedReverseRelatedCards() } } -void CardDatabase::addCard(CardInfoPtr card) +void CardDatabase::addCard(const CardInfoPtr &card) { if (card == nullptr) { qCWarning(CardDatabaseLog) << "CardDatabase::addCard(nullptr)"; @@ -118,7 +118,7 @@ void CardDatabase::addCard(CardInfoPtr card) emit cardAdded(card); } -void CardDatabase::removeCard(CardInfoPtr card) +void CardDatabase::removeCard(const CardInfoPtr &card) { if (card.isNull()) { qCWarning(CardDatabaseLog) << "CardDatabase::removeCard(nullptr)"; @@ -143,7 +143,7 @@ void CardDatabase::removeCard(CardInfoPtr card) emit cardRemoved(card); } -void CardDatabase::addSet(CardSetPtr set) +void CardDatabase::addSet(const CardSetPtr &set) { sets.insert(set->getShortName(), set); } @@ -215,7 +215,7 @@ void CardDatabase::notifyEnabledSetsChanged() emit cardDatabaseEnabledSetsChanged(); } -void CardDatabase::addFormat(FormatRulesPtr format) +void CardDatabase::addFormat(const FormatRulesPtr &format) { formats.insert(format->formatName.toLower(), format); -} \ No newline at end of file +} diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.h b/libcockatrice_card/libcockatrice/card/database/card_database.h index 521be8fbc..44838962d 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database.h +++ b/libcockatrice_card/libcockatrice/card/database/card_database.h @@ -88,7 +88,7 @@ public: * @brief Removes a card from the database. * @param card Pointer to the card to remove. */ - void removeCard(CardInfoPtr card); + void removeCard(const CardInfoPtr &card); /** @brief Clears all cards, sets, and internal state. */ void clear(); @@ -140,15 +140,15 @@ public slots: * @brief Adds a card to the database. * @param card CardInfoPtr to add. */ - void addCard(CardInfoPtr card); + void addCard(const CardInfoPtr &card); /** * @brief Adds a set to the database. * @param set Pointer to CardSet to add. */ - void addSet(CardSetPtr set); + void addSet(const CardSetPtr &set); - void addFormat(FormatRulesPtr format); + void addFormat(const FormatRulesPtr &format); /** @brief Loads card databases from configured paths. */ void loadCardDatabases();