mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-22 09:35:08 -07:00
[DeckShare] Create temporary share links for local and server decks (#7243)
* [DeckShare] Create temporary share links for local and server decks
* [DeckShare] Address review findings and harden the share flows
Gate every share entry point on login, de-duplicate the share-link and
color-identity logic behind DeckShareUtils and an injected querier, and
replace the silent tray/status-bar notices with always-visible dialogs.
- abstract_tab_deck_editor: explain that sharing requires a connection
instead of silently doing nothing when logged out
- tab_deck_storage: disable the share action on disconnect, reject
folder/deck mixes and the root folder with clear warnings, re-enable
Create on every entry/response so a dropped connection cannot leave
the button disabled
- tab_deck_storage_visual: same login gate for the context-menu entry,
visible success/error dialogs, and a symmetric in-flight guard
- getDeckColorIdentity now takes a CardDatabaseQuerier, dropping the
CardDatabaseManager singleton access and enabling unit tests
* [DeckShare] Fix share-link expiry build on the minimum-supported Qt
QTimeZone::UTC (the Initialization enum) only exists since Qt 6.7, so
Debian 12 and Ubuntu 24.04 (Qt 6.4) fail to compile the share-link expiry
handling in the share dialog and the two deck-storage tabs. Mirror the
existing games_model guard and fall back to Qt::UTC on older Qt.
* [DeckShare] Use the stable server client for the visual deck storage tab
* [DeckShare] Extract the share-creation response handling into DeckShareUtils
* [DeckShare] Drop includes left unused by the share-response extraction
* [DeckShare] Format share expiry with the locale-aware short format
* [DeckShare] Build share links with QUrl and QUrlQuery for percent-encoding
* [DeckShare] Replace the duplicate computeColorIdentity with the shared getDeckColorIdentity
* [DeckShare] Recover the share controls when the server never answers
* [DeckShare] Provide the full share hint in each plural form
* [DeckShare] Join the selected-count label with a non-translatable separator
* [DeckShare] Retranslate the share button tooltip with the storage widget
* [DeckShare] Forward retranslateUi to the visual deck storage widget
* [DeckShare] Let the share bar owners supply the hint text
* [DeckShare] End the share-related headers and sources with a trailing newline
* [DeckShare] Keep the settings include in the project include block
* [DeckShare] Include the network settings header used by the share timeout
* [DeckShare] Resolve the share theme icon through themePixmap
QPixmap("theme:icons/share") has no file extension, so ThemeManager::assetPath()
is bypassed and the pixmap is always null. Use themePixmap(QStringLiteral("icons/share"))
like every other toolbar action, so the .svg (and dark/light variants) resolves.
* [DeckShare] Keep the share selection consistent with the visible decks
Filtered-out previews are hidden but kept alive, so selectedFilePaths() counted
them in the share and the selection highlight. Only decks the user can see are
now shared, and a deck that stops matching the filters is deselectd as the deck
pass runs, keeping the %n count and the highlight in sync with the screen.
* [DeckShare] Abandon an in-flight tree share on cancel
Leaving share mode never stopped the timeout timer, and a late response still
ran shareFromTreeFinished, copying the link and announcing success for a share
the user backed out of. Stopping the timer and tracking the outstanding request
by sequence number means a stale reply (or a timed-out one) after cancel is
ignored, and cancelling + re-entering share mode can no longer confuse the two
requests.
* [DeckShare] Abandon an in-flight tile share on cancel
exitShareMode() left shareTimeoutTimer running and did not abandon the pending
Command_DeckShareCreate, so a timer pop or a late success still reported the
share after the user cancelled. Stop the timer and ignore stale responses via a
sequence number, mirroring the tree tab.
* [DeckShare] Wire the status-changed handler after shareBar exists
handleConnectionChanged() dereferences shareBar->isVisible(), but the connection
was set up before shareBar was constructed and shareBar had no in-class
initializer. On any status change delivered before construction the slot read an
indeterminate pointer. Seed the connection (and the initial share availability)
after shareBar exists and give shareBar a = nullptr initializer.
* [DeckShare] Explain why a blank deck cannot be shared
A blank deck exited the share flow silently. The menu only disables the entry
via setSaveStatus(), a different predicate, so the path is reachable (e.g. add a
card and remove it again). Mirror the not-logged-in branch with a short
information dialog.
* [DeckShare] Restore the banner-text doc comment
Re-add the doc block above refreshBannerCardText() that was removed as part of
the share-selection work; it documents the coupling to refreshBannerCardToolTip.
* [DeckShare] Resolve the stable server client in the deck editor gate
actShareDeck went through tabSupervisor->getClient(), which hands back a
LocalClient while an offline game is running. LocalClient never sets its status,
so a logged-in user could not share from the deck editor during a local game,
and got a misleading "You must be connected" message. Expose the supervisor's
stable remote client and use it for the gate and the dialog, matching the other
share tabs.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
a289d61765
commit
ba2900dcb9
29 changed files with 1173 additions and 52 deletions
|
|
@ -11,6 +11,7 @@
|
|||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFrame>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
|
|
@ -27,7 +28,7 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
|
|||
VisualDeckStorageWidget *_visualDeckStorageWidget,
|
||||
VisualDeckStorageModel *_model,
|
||||
const QString &_filePath)
|
||||
: QWidget(_parent), visualDeckStorageWidget(_visualDeckStorageWidget), model(_model), filePath(_filePath)
|
||||
: QWidget(_parent), filePath(_filePath), visualDeckStorageWidget(_visualDeckStorageWidget), model(_model)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
|
@ -36,6 +37,8 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
|
|||
new DeckPreviewCardPictureWidget(this, false, visualDeckStorageWidget->deckPreviewSelectionAnimationEnabled);
|
||||
pictureWidget->setFontSize(24);
|
||||
connect(pictureWidget, &DeckPreviewCardPictureWidget::imageClicked, this, &DeckPreviewWidget::imageClickedEvent);
|
||||
connect(pictureWidget, &DeckPreviewCardPictureWidget::imageSingleClicked, this,
|
||||
&DeckPreviewWidget::imageSingleClicked);
|
||||
connect(pictureWidget, &DeckPreviewCardPictureWidget::imageDoubleClicked, this,
|
||||
&DeckPreviewWidget::imageDoubleClickedEvent);
|
||||
bannerCardDisplayWidget = pictureWidget;
|
||||
|
|
@ -99,6 +102,15 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
|
|||
// to keep the resize handler from searching the widget tree on every layout pass.
|
||||
fixedWidthChildren = {bannerCardDisplayWidget, colorIdentityWidget, deckTagsDisplayWidget, bannerCardLabel,
|
||||
bannerCardComboBox};
|
||||
|
||||
// Child of the banner widget so the frame tracks the banner's selection animation
|
||||
// (which animates the banner's position) instead of staying at a stale static offset.
|
||||
selectionFrame = new QFrame(bannerCardDisplayWidget);
|
||||
selectionFrame->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
selectionFrame->setStyleSheet(QStringLiteral(
|
||||
"QFrame { border: 2px solid palette(highlight); border-radius: 4px; background: transparent; }"));
|
||||
selectionFrame->setVisible(false);
|
||||
bannerCardDisplayWidget->installEventFilter(this);
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::retranslateUi()
|
||||
|
|
@ -122,6 +134,63 @@ void DeckPreviewWidget::resizeEvent(QResizeEvent *event)
|
|||
for (QWidget *widget : fixedWidthChildren) {
|
||||
widget->setMaximumWidth(width);
|
||||
}
|
||||
updateSelectionFrameGeometry();
|
||||
}
|
||||
|
||||
bool DeckPreviewWidget::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == bannerCardDisplayWidget && (event->type() == QEvent::Resize || event->type() == QEvent::Move)) {
|
||||
updateSelectionFrameGeometry();
|
||||
}
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::setShareSelectable(bool selectable)
|
||||
{
|
||||
shareSelectable = selectable;
|
||||
if (!selectable) {
|
||||
setShareSelected(false);
|
||||
}
|
||||
updateSelectionStyle();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::setShareSelected(bool selected)
|
||||
{
|
||||
if (shareSelected == selected) {
|
||||
return;
|
||||
}
|
||||
shareSelected = selected;
|
||||
updateSelectionStyle();
|
||||
emit shareSelectionToggled(selected);
|
||||
}
|
||||
|
||||
bool DeckPreviewWidget::isShareSelected() const
|
||||
{
|
||||
return shareSelected;
|
||||
}
|
||||
|
||||
bool DeckPreviewWidget::isShareSelectable() const
|
||||
{
|
||||
return shareSelectable;
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateSelectionFrameGeometry()
|
||||
{
|
||||
if (selectionFrame == nullptr || bannerCardDisplayWidget == nullptr) {
|
||||
return;
|
||||
}
|
||||
// Frame is a child of the banner, so it is positioned in banner coordinates and
|
||||
// tracks the banner's selection animation automatically. A small inset keeps the
|
||||
// highlight visible around the card art without occluding it.
|
||||
selectionFrame->setGeometry(bannerCardDisplayWidget->rect().adjusted(1, 1, -1, -1));
|
||||
selectionFrame->raise();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateSelectionStyle()
|
||||
{
|
||||
if (selectionFrame != nullptr) {
|
||||
selectionFrame->setVisible(shareSelectable && isShareSelected());
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::enterEvent(QEnterEvent *event)
|
||||
|
|
@ -337,10 +406,20 @@ void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPic
|
|||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::imageSingleClicked()
|
||||
{
|
||||
if (isShareSelectable()) {
|
||||
setShareSelected(!isShareSelected());
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
Q_UNUSED(instance);
|
||||
if (isShareSelectable()) {
|
||||
return; // in share mode a double click would just toggle a single selection
|
||||
}
|
||||
emit deckLoadRequested(filePath);
|
||||
}
|
||||
|
||||
|
|
@ -365,6 +444,9 @@ QMenu *DeckPreviewWidget::createRightClickMenu()
|
|||
}
|
||||
});
|
||||
|
||||
connect(menu->addAction(tr("Share deck...")), &QAction::triggered, this,
|
||||
[this] { emit shareDeckRequested(filePath); });
|
||||
|
||||
connect(menu->addAction(tr("Edit Tags")), &QAction::triggered, deckTagsDisplayWidget,
|
||||
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QWidget>
|
||||
|
||||
class QEnterEvent;
|
||||
class QFrame;
|
||||
class QLabel;
|
||||
class QMenu;
|
||||
class QMouseEvent;
|
||||
|
|
@ -41,9 +42,19 @@ public:
|
|||
*/
|
||||
DeckPreviewCardPictureWidget *bannerCardDisplayWidget;
|
||||
|
||||
/** @brief The path of the deck file backing this preview. */
|
||||
QString filePath;
|
||||
|
||||
void setShareSelectable(bool selectable);
|
||||
void setShareSelected(bool selected);
|
||||
[[nodiscard]] bool isShareSelected() const;
|
||||
[[nodiscard]] bool isShareSelectable() const;
|
||||
|
||||
signals:
|
||||
void deckLoadRequested(const QString &filePath);
|
||||
void openDeckEditor(const LoadedDeck &deck);
|
||||
void shareDeckRequested(const QString &filePath);
|
||||
void shareSelectionToggled(bool selected);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
|
|
@ -66,6 +77,7 @@ public slots:
|
|||
protected:
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] int row() const;
|
||||
|
|
@ -76,6 +88,7 @@ private:
|
|||
QMenu *createRightClickMenu();
|
||||
void addSetBannerCardMenu(QMenu *menu);
|
||||
void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
|
||||
void imageSingleClicked();
|
||||
void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
|
||||
|
||||
void actRenameDeck();
|
||||
|
|
@ -84,7 +97,6 @@ private:
|
|||
|
||||
VisualDeckStorageWidget *visualDeckStorageWidget;
|
||||
VisualDeckStorageModel *model;
|
||||
QString filePath;
|
||||
QVBoxLayout *layout;
|
||||
ColorIdentityWidget *colorIdentityWidget;
|
||||
DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget;
|
||||
|
|
@ -92,6 +104,12 @@ private:
|
|||
QComboBox *bannerCardComboBox;
|
||||
QList<QWidget *> fixedWidthChildren; ///< Children clamped to the picture width on resize.
|
||||
int lastKnownBannerWidth = -1; ///< The picture width last applied to the children.
|
||||
QFrame *selectionFrame = nullptr;
|
||||
bool shareSelectable = false;
|
||||
bool shareSelected = false;
|
||||
|
||||
void updateSelectionStyle();
|
||||
void updateSelectionFrameGeometry();
|
||||
};
|
||||
|
||||
class NoScrollFilter : public QObject
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ void VisualDeckStorageFolderDisplayWidget::continueDeckPass()
|
|||
|
||||
const bool matches = index.data(VisualDeckStorageRoles::FilterMatchRole).toBool();
|
||||
deckPreviewWidget->setVisible(matches);
|
||||
if (!matches) {
|
||||
// A deck that no longer matches the filters is dropped from the selection so its
|
||||
// highlight cannot linger on an invisible preview or be counted in the share.
|
||||
deckPreviewWidget->setShareSelected(false);
|
||||
}
|
||||
if (matches) {
|
||||
++visibleDeckCount;
|
||||
}
|
||||
|
|
@ -209,6 +214,11 @@ DeckPreviewWidget *VisualDeckStorageFolderDisplayWidget::createDeckPreviewWidget
|
|||
&VisualDeckStorageWidget::deckLoadRequested);
|
||||
connect(deckPreviewWidget, &DeckPreviewWidget::openDeckEditor, visualDeckStorageWidget,
|
||||
&VisualDeckStorageWidget::openDeckEditor);
|
||||
connect(deckPreviewWidget, &DeckPreviewWidget::shareDeckRequested, visualDeckStorageWidget,
|
||||
&VisualDeckStorageWidget::shareDeckRequested);
|
||||
connect(deckPreviewWidget, &DeckPreviewWidget::shareSelectionToggled, visualDeckStorageWidget,
|
||||
&VisualDeckStorageWidget::shareSelectionChanged);
|
||||
deckPreviewWidget->setShareSelectable(visualDeckStorageWidget->isShareSelectable());
|
||||
connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::cardSizeChanged,
|
||||
deckPreviewWidget->bannerCardDisplayWidget, &CardInfoPictureWidget::setScaleFactor);
|
||||
deckPreviewWidget->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->settings()->getCardSize());
|
||||
|
|
@ -216,6 +226,18 @@ DeckPreviewWidget *VisualDeckStorageFolderDisplayWidget::createDeckPreviewWidget
|
|||
return deckPreviewWidget;
|
||||
}
|
||||
|
||||
void VisualDeckStorageFolderDisplayWidget::setShareSelectable(bool selectable)
|
||||
{
|
||||
const auto previews = flowWidget->findChildren<DeckPreviewWidget *>();
|
||||
for (DeckPreviewWidget *preview : previews) {
|
||||
preview->setShareSelectable(selectable);
|
||||
}
|
||||
const auto subFolders = findChildren<VisualDeckStorageFolderDisplayWidget *>();
|
||||
for (VisualDeckStorageFolderDisplayWidget *subFolder : subFolders) {
|
||||
subFolder->setShareSelectable(selectable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates, removes and keeps in sync the subfolder widgets of this folder.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public slots:
|
|||
*/
|
||||
void scheduleReconcile();
|
||||
void updateShowFolders(bool enabled);
|
||||
void setShareSelectable(bool selectable);
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "visual_deck_storage_model.h"
|
||||
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
#include "../cards/additional_info/deck_color_identity.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
|
|
@ -119,8 +120,6 @@ DeckScanResult scanDeckDirectory(const QString &deckPath)
|
|||
}
|
||||
} // namespace
|
||||
|
||||
static QString computeColorIdentity(const LoadedDeck &deck);
|
||||
|
||||
VisualDeckStorageModel::VisualDeckStorageModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
|
@ -306,7 +305,7 @@ void VisualDeckStorageModel::beginLoad(int row)
|
|||
}
|
||||
// Color identity walks every card through the database, so compute it here to
|
||||
// keep the completion handler on the UI thread cheap.
|
||||
const QString colorIdentity = computeColorIdentity(*deck);
|
||||
const QString colorIdentity = getDeckColorIdentity(deck->deckList, CardDatabaseManager::query());
|
||||
return DeckLoadResult{std::move(*deck), QFileInfo(filePath).lastModified(), colorIdentity};
|
||||
}));
|
||||
}
|
||||
|
|
@ -367,40 +366,6 @@ void VisualDeckStorageModel::drainPendingLoads()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the color identity of a deck in WUBRG order.
|
||||
*/
|
||||
static QString computeColorIdentity(const LoadedDeck &deck)
|
||||
{
|
||||
QStringList cardList = deck.deckList.getCardList({DECK_ZONE_MAIN, DECK_ZONE_SIDE});
|
||||
if (cardList.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QSet<QChar> colorSet; // A set to collect unique color symbols (e.g., W, U, B, R, G)
|
||||
|
||||
for (const QString &cardName : cardList) {
|
||||
CardInfoPtr currentCard = CardDatabaseManager::query()->getCardInfo(cardName);
|
||||
if (currentCard) {
|
||||
const QString colors = currentCard->getColors(); // Something like "WUB"
|
||||
for (const QChar &color : colors) {
|
||||
colorSet.insert(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the color identity is in WUBRG order
|
||||
QString colorIdentity;
|
||||
const QString wubrgOrder = "WUBRG";
|
||||
for (const QChar &color : wubrgOrder) {
|
||||
if (colorSet.contains(color)) {
|
||||
colorIdentity.append(color);
|
||||
}
|
||||
}
|
||||
|
||||
return colorIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recomputes all derived metadata of a row from its loaded deck.
|
||||
*/
|
||||
|
|
@ -414,7 +379,7 @@ void VisualDeckStorageModel::recomputeDeckMetadata(DeckPreviewData &data, bool r
|
|||
data.lastLoaded = QDateTime::fromString(deckList.getLastLoadedTimestamp());
|
||||
data.bannerCard = deckList.getBannerCard();
|
||||
if (recomputeColorIdentity) {
|
||||
data.colorIdentity = computeColorIdentity(data.deck);
|
||||
data.colorIdentity = getDeckColorIdentity(data.deck.deckList, CardDatabaseManager::query());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
|||
refreshButton->setFixedSize(32, 32);
|
||||
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
|
||||
|
||||
shareButton = new QToolButton(this);
|
||||
shareButton->setIcon(themePixmap(QStringLiteral("icons/share")));
|
||||
shareButton->setFixedSize(32, 32);
|
||||
shareButton->setVisible(false);
|
||||
connect(shareButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::shareRequested);
|
||||
|
||||
quickSettingsWidget = new VisualDeckStorageQuickSettingsWidget(this);
|
||||
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showFoldersChanged, this,
|
||||
&VisualDeckStorageWidget::updateShowFolders);
|
||||
|
|
@ -58,6 +64,7 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
|||
searchAndSortLayout->addWidget(sortWidget);
|
||||
searchAndSortLayout->addWidget(searchWidget);
|
||||
searchAndSortLayout->addWidget(refreshButton);
|
||||
searchAndSortLayout->addWidget(shareButton);
|
||||
searchAndSortLayout->addWidget(quickSettingsWidget);
|
||||
|
||||
// tag filter box
|
||||
|
|
@ -158,11 +165,71 @@ void VisualDeckStorageWidget::retranslateUi()
|
|||
databaseLoadIndicator->setText(tr("Loading database ..."));
|
||||
|
||||
refreshButton->setToolTip(tr("Refresh loaded files"));
|
||||
shareButton->setToolTip(tr("Select decks to share"));
|
||||
quickSettingsWidget->setToolTip(tr("Visual Deck Storage Settings"));
|
||||
|
||||
sortWidget->retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::setShareSelectable(bool selectable)
|
||||
{
|
||||
if (shareSelectable == selectable) {
|
||||
return;
|
||||
}
|
||||
shareSelectable = selectable;
|
||||
if (folderWidget != nullptr) {
|
||||
folderWidget->setShareSelectable(selectable);
|
||||
}
|
||||
emit shareSelectionChanged();
|
||||
}
|
||||
|
||||
bool VisualDeckStorageWidget::isShareSelectable() const
|
||||
{
|
||||
return shareSelectable;
|
||||
}
|
||||
|
||||
QStringList VisualDeckStorageWidget::selectedFilePaths() const
|
||||
{
|
||||
QStringList selectedPaths;
|
||||
if (folderWidget != nullptr) {
|
||||
const auto previews = folderWidget->findChildren<DeckPreviewWidget *>();
|
||||
for (DeckPreviewWidget *preview : previews) {
|
||||
// Filtered-out previews stay alive hidden in their sorted place, so only decks the
|
||||
// user can actually see are part of the share.
|
||||
if (preview->isVisible() && preview->isShareSelected()) {
|
||||
selectedPaths.append(preview->filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedPaths;
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::clearShareSelection()
|
||||
{
|
||||
if (folderWidget != nullptr) {
|
||||
const auto previews = folderWidget->findChildren<DeckPreviewWidget *>();
|
||||
for (DeckPreviewWidget *preview : previews) {
|
||||
preview->setShareSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::setShareAvailable(bool available)
|
||||
{
|
||||
shareButton->setVisible(available);
|
||||
shareButton->setEnabled(available);
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::setShareSelectedFiles(const QStringList &paths)
|
||||
{
|
||||
if (folderWidget != nullptr) {
|
||||
const auto previews = folderWidget->findChildren<DeckPreviewWidget *>();
|
||||
for (DeckPreviewWidget *preview : previews) {
|
||||
preview->setShareSelected(paths.contains(preview->filePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a const pointer to the quick settings so that the values can be accessed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ public:
|
|||
explicit VisualDeckStorageWidget(QWidget *parent);
|
||||
void refreshIfPossible();
|
||||
void retranslateUi();
|
||||
void setShareSelectable(bool selectable);
|
||||
[[nodiscard]] bool isShareSelectable() const;
|
||||
[[nodiscard]] QStringList selectedFilePaths() const;
|
||||
void setShareSelectedFiles(const QStringList &paths);
|
||||
void clearShareSelection();
|
||||
void setShareAvailable(bool available);
|
||||
|
||||
VisualDeckStorageTagFilterWidget *tagFilterWidget;
|
||||
bool deckPreviewSelectionAnimationEnabled;
|
||||
|
|
@ -63,6 +69,9 @@ public slots:
|
|||
signals:
|
||||
void deckLoadRequested(const QString &filePath);
|
||||
void openDeckEditor(const LoadedDeck &deck);
|
||||
void shareDeckRequested(const QString &filePath);
|
||||
void shareSelectionChanged();
|
||||
void shareRequested();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
|
@ -81,12 +90,14 @@ private:
|
|||
VisualDeckStorageSearchWidget *searchWidget;
|
||||
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
|
||||
QToolButton *refreshButton;
|
||||
QToolButton *shareButton;
|
||||
VisualDeckStorageQuickSettingsWidget *quickSettingsWidget;
|
||||
QScrollArea *scrollArea;
|
||||
VisualDeckStorageFolderDisplayWidget *folderWidget = nullptr;
|
||||
VisualDeckStorageModel *storageModel = nullptr;
|
||||
VisualDeckStorageSortFilterProxyModel *storageProxyModel = nullptr;
|
||||
QTimer *refreshTimer = nullptr; ///< Coalesces the re-apply/refresh burst following a batch of deck loads.
|
||||
bool shareSelectable = false;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_WIDGET_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue