mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-09-04 09:13:27 +00:00
Minor: Additional functions (#25)
* several functions. fixes #1 and #2 * multi selection functionality
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include "ccm/services/SetService.hpp"
|
||||
#include "ccm/ui/ImageViewerDialog.hpp"
|
||||
#include "ccm/ui/Theme.hpp"
|
||||
#include "ccm/util/CardLookupDetect.hpp"
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/button.h>
|
||||
@@ -136,6 +137,26 @@ protected:
|
||||
// controls cannot outlive the lookup identity.
|
||||
virtual void onCardLookupContextChanged() {}
|
||||
|
||||
// Bidirectional Set # Auto detect: track which of Name / Set # the user
|
||||
// last typed so a second detect uses that field as the lookup key.
|
||||
void markNameLookupEdited() { lastLookupEditField_ = CardLookupEditField::Name; }
|
||||
|
||||
// Subclasses bind Set # `wxEVT_TEXT` to this (or call it from their handler).
|
||||
// Also clears print-variant caches via `onCardLookupContextChanged`.
|
||||
void markSetNoLookupEdited() {
|
||||
lastLookupEditField_ = CardLookupEditField::SetNo;
|
||||
onCardLookupContextChanged();
|
||||
}
|
||||
|
||||
[[nodiscard]] CardLookupEditField lastLookupEditField() const noexcept {
|
||||
return lastLookupEditField_;
|
||||
}
|
||||
|
||||
// `nameEmpty` / `setNoEmpty` must already be trimmed/normalized by the caller.
|
||||
[[nodiscard]] bool shouldDetectBySetNo(bool nameEmpty, bool setNoEmpty) const noexcept {
|
||||
return preferDetectBySetNo(nameEmpty, setNoEmpty, lastLookupEditField_);
|
||||
}
|
||||
|
||||
// Extra validation after name/set checks and writeFromControls(). Return
|
||||
// false to block OK (subclass should show its own themed dialog).
|
||||
[[nodiscard]] virtual bool validateExtraFields() { return true; }
|
||||
@@ -210,6 +231,7 @@ private:
|
||||
|
||||
nameCtrl_ = new wxTextCtrl(this, wxID_ANY, wxString::FromUTF8(card_.name.c_str()));
|
||||
nameCtrl_->Bind(wxEVT_TEXT, [this](wxCommandEvent& ev) {
|
||||
markNameLookupEdited();
|
||||
onCardLookupContextChanged();
|
||||
ev.Skip();
|
||||
});
|
||||
@@ -626,6 +648,7 @@ private:
|
||||
const std::vector<Set>* preloadedSets_{nullptr};
|
||||
|
||||
wxTextCtrl* nameCtrl_{nullptr};
|
||||
CardLookupEditField lastLookupEditField_{CardLookupEditField::None};
|
||||
wxComboBox* setCombo_{nullptr};
|
||||
wxSpinCtrl* amountCtrl_{nullptr};
|
||||
wxChoice* languageChoice_{nullptr};
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
#include "ccm/ui/Theme.hpp"
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/cursor.h>
|
||||
#include <wx/dataobj.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/listctrl.h>
|
||||
@@ -59,6 +61,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -74,6 +77,10 @@ wxDECLARE_EVENT(EVT_CARD_SELECTED, wxCommandEvent);
|
||||
// `IGameView` implementations bind this to open Edit for `selected()`.
|
||||
wxDECLARE_EVENT(EVT_CARD_ACTIVATED, wxCommandEvent);
|
||||
|
||||
// Raised when the list wants a short status-bar note (e.g. clipboard copy).
|
||||
// `event.GetString()` is the message; MainFrame shows it in the bottom strip.
|
||||
wxDECLARE_EVENT(EVT_UI_STATUS, wxCommandEvent);
|
||||
|
||||
template <typename TCard, typename TSortColumn>
|
||||
class BaseCardListPanel : public wxPanel {
|
||||
public:
|
||||
@@ -81,14 +88,18 @@ public:
|
||||
using sort_column_type = TSortColumn;
|
||||
|
||||
// Replace the displayed rows. When preferSelectId is set, selects that
|
||||
// card if present (used after Add). Otherwise preserves the previously
|
||||
// selected card by id when still present; the first-row CallAfter path in
|
||||
// rebuildRows() runs only when there was no prior selection (startup).
|
||||
// card exclusively if present (used after Add). Otherwise preserves the
|
||||
// previously selected card ids when still present; the first-row CallAfter
|
||||
// path in rebuildRows() runs only when there was no prior selection
|
||||
// (startup).
|
||||
void setCards(std::vector<TCard> cards,
|
||||
std::optional<std::uint32_t> preferSelectId = std::nullopt) {
|
||||
std::optional<std::uint32_t> keepId = preferSelectId;
|
||||
if (!keepId) {
|
||||
if (auto sel = selected()) keepId = sel->id;
|
||||
std::optional<std::vector<std::uint32_t>> keepIds;
|
||||
if (preferSelectId) {
|
||||
keepIds = std::vector<std::uint32_t>{*preferSelectId};
|
||||
} else {
|
||||
auto ids = selectedIds();
|
||||
if (!ids.empty()) keepIds = std::move(ids);
|
||||
}
|
||||
cards_ = std::move(cards);
|
||||
// Drop sort state when the underlying data is replaced - the indicator
|
||||
@@ -96,7 +107,7 @@ public:
|
||||
// wxListCtrl keeps the indicator across DeleteAllItems().
|
||||
nextDirByCol_.clear();
|
||||
list_->RemoveSortIndicator();
|
||||
rebuildRows(keepId);
|
||||
rebuildRows(keepIds);
|
||||
if (!autoSizedOnce_ && !cards_.empty()) {
|
||||
autoSizeAllColumns();
|
||||
autoSizedOnce_ = true;
|
||||
@@ -104,16 +115,17 @@ public:
|
||||
}
|
||||
|
||||
// Update the filter string and rebuild the visible rows in place. The
|
||||
// panel preserves the previously-selected card across the rebuild when
|
||||
// it still matches the new filter; otherwise the first remaining row is
|
||||
// panel preserves previously-selected cards across the rebuild when they
|
||||
// still match the new filter; otherwise the first remaining row is
|
||||
// selected, or none if the filter excluded everything. A single
|
||||
// EVT_CARD_SELECTED is emitted afterwards so the parent re-syncs.
|
||||
void setFilter(std::string_view filter) {
|
||||
if (filter_ == filter) return;
|
||||
filter_.assign(filter);
|
||||
std::optional<std::uint32_t> keepId;
|
||||
if (auto sel = selected()) keepId = sel->id;
|
||||
rebuildRows(keepId);
|
||||
auto ids = selectedIds();
|
||||
std::optional<std::vector<std::uint32_t>> keepIds;
|
||||
if (!ids.empty()) keepIds = std::move(ids);
|
||||
rebuildRows(keepIds);
|
||||
}
|
||||
|
||||
void applyTheme(const ThemePalette& palette) {
|
||||
@@ -123,22 +135,52 @@ public:
|
||||
SetForegroundColour(palette.text);
|
||||
rebuildIconBitmaps(palette.inputText, wxColour(255, 255, 255));
|
||||
refreshHeaderTheme(palette);
|
||||
std::optional<std::uint32_t> keepId;
|
||||
if (auto sel = selected()) keepId = sel->id;
|
||||
rebuildRows(keepId);
|
||||
auto ids = selectedIds();
|
||||
std::optional<std::vector<std::uint32_t>> keepIds;
|
||||
if (!ids.empty()) keepIds = std::move(ids);
|
||||
rebuildRows(keepIds);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<TCard>& cards() const noexcept { return cards_; }
|
||||
[[nodiscard]] const std::string& filter() const noexcept { return filter_; }
|
||||
// First selected card (detail panel / single-edit primary).
|
||||
[[nodiscard]] std::optional<TCard> selected() const {
|
||||
const long sel = list_->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (const TCard* c = cardForRow(sel)) return *c;
|
||||
return std::nullopt;
|
||||
}
|
||||
[[nodiscard]] std::size_t selectedCount() const {
|
||||
if (list_ == nullptr) return 0;
|
||||
std::size_t n = 0;
|
||||
long row = -1;
|
||||
while ((row = list_->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
[[nodiscard]] std::vector<TCard> selectedCards() const {
|
||||
std::vector<TCard> out;
|
||||
if (list_ == nullptr) return out;
|
||||
long row = -1;
|
||||
while ((row = list_->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0) {
|
||||
if (const TCard* c = cardForRow(row)) out.push_back(*c);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
[[nodiscard]] std::vector<std::uint32_t> selectedIds() const {
|
||||
std::vector<std::uint32_t> out;
|
||||
if (list_ == nullptr) return out;
|
||||
long row = -1;
|
||||
while ((row = list_->GetNextItem(row, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0) {
|
||||
if (const TCard* c = cardForRow(row)) out.push_back(c->id);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Ensure the selected row is actively focused so Windows uses the active
|
||||
// highlight color (blue in light mode), keeping selected-row icons legible.
|
||||
// Ensure the first selected row is actively focused so Windows uses the
|
||||
// active highlight color (blue in light mode), keeping selected-row icons
|
||||
// legible. Does not clear a multi-selection.
|
||||
void activateSelection() {
|
||||
if (list_ == nullptr || list_->GetItemCount() <= 0) return;
|
||||
long row = list_->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
@@ -151,8 +193,9 @@ public:
|
||||
}
|
||||
|
||||
// Move the selection by `delta` rows (+1 / -1). Used when Up/Down are
|
||||
// pressed while focus is on the filter box. Clamps to the visible range;
|
||||
// leaves list HWND focus alone so the caret can stay in the filter.
|
||||
// pressed while focus is on the filter box. Collapses any multi-selection
|
||||
// to a single row. Clamps to the visible range; leaves list HWND focus
|
||||
// alone so the caret can stay in the filter.
|
||||
void nudgeSelection(int delta) {
|
||||
if (list_ == nullptr || list_->GetItemCount() <= 0 || delta == 0) return;
|
||||
long row = list_->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
@@ -161,12 +204,12 @@ public:
|
||||
long next = row + delta;
|
||||
if (next < 0) next = 0;
|
||||
if (next >= count) next = count - 1;
|
||||
if (next == row) {
|
||||
list_->EnsureVisible(next);
|
||||
return;
|
||||
}
|
||||
suppressListFocus_ = true;
|
||||
list_->SetItemState(row, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
// Clear every selected row so filter nudge is always single-select.
|
||||
long sel = -1;
|
||||
while ((sel = list_->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) >= 0) {
|
||||
list_->SetItemState(sel, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
}
|
||||
list_->SetItemState(next,
|
||||
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
|
||||
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
@@ -215,8 +258,10 @@ protected:
|
||||
// Subclass calls this once from its constructor body (after virtual hooks
|
||||
// are reachable) to wire up columns + the header row + custom-draw hooks.
|
||||
void buildLayout() {
|
||||
// Multi-select: native Ctrl (toggle) and Shift (range) without
|
||||
// wxLC_SINGLE_SEL. Set-completion tables keep single-select separately.
|
||||
list_ = new IconListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
||||
wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
|
||||
wxLC_REPORT | wxLC_NO_HEADER);
|
||||
|
||||
textCols_ = declareTextColumns();
|
||||
iconCols_ = declareIconColumns();
|
||||
@@ -274,6 +319,7 @@ protected:
|
||||
list_->Bind(wxEVT_LIST_ITEM_SELECTED, &BaseCardListPanel::onSelectionChanged, this);
|
||||
list_->Bind(wxEVT_LIST_ITEM_DESELECTED, &BaseCardListPanel::onSelectionChanged, this);
|
||||
list_->Bind(wxEVT_LIST_ITEM_ACTIVATED, &BaseCardListPanel::onListItemActivated, this);
|
||||
list_->Bind(wxEVT_KEY_DOWN, &BaseCardListPanel::onListKeyDown, this);
|
||||
}
|
||||
|
||||
// Forwarded helpers ------------------------------------------------------
|
||||
@@ -523,11 +569,12 @@ private:
|
||||
const bool ascending = (it == nextDirByCol_.end()) ? true : it->second;
|
||||
nextDirByCol_[*sortCol] = !ascending;
|
||||
|
||||
std::optional<std::uint32_t> keepId;
|
||||
if (auto sel = selected()) keepId = sel->id;
|
||||
auto ids = selectedIds();
|
||||
std::optional<std::vector<std::uint32_t>> keepIds;
|
||||
if (!ids.empty()) keepIds = std::move(ids);
|
||||
|
||||
sortBy(*sortCol, ascending);
|
||||
rebuildRows(keepId);
|
||||
rebuildRows(keepIds);
|
||||
if (list_ != nullptr) list_->SetFocus();
|
||||
}
|
||||
|
||||
@@ -590,7 +637,9 @@ private:
|
||||
|
||||
// ----- row rendering -----------------------------------------------------
|
||||
|
||||
void rebuildRows(std::optional<std::uint32_t> keepId = std::nullopt) {
|
||||
// nullopt keepIds → no prior selection (startup / empty): defer first-row
|
||||
// select. Otherwise restore every id that is still visible after filter.
|
||||
void rebuildRows(std::optional<std::vector<std::uint32_t>> keepIds = std::nullopt) {
|
||||
// Suppress wxListCtrl's natural DESELECTED (from DeleteAllItems) and
|
||||
// SELECTED (from the SetItemState below) events while we churn through
|
||||
// the rebuild. See `ui_wx/AGENTS.md` for the rate-limit rationale.
|
||||
@@ -605,10 +654,16 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<std::uint32_t> keepSet;
|
||||
if (keepIds) {
|
||||
keepSet.insert(keepIds->begin(), keepIds->end());
|
||||
}
|
||||
|
||||
long row = 0;
|
||||
long rowToSelect = -1;
|
||||
long firstRestored = -1;
|
||||
const int firstText = firstTextColIdx();
|
||||
const int noteCol = noteColIdx();
|
||||
std::vector<long> rowsToSelect;
|
||||
for (std::size_t srcIdx : filteredIndices_) {
|
||||
const auto& c = cards_[srcIdx];
|
||||
// Insert via the hidden column-0 spacer. We never set sub-item
|
||||
@@ -632,16 +687,22 @@ private:
|
||||
const std::string note = renderTextCell(c, textCols_.size() - 1);
|
||||
list_->SetItem(idx, noteCol, wxString::FromUTF8(note.c_str()));
|
||||
|
||||
if (keepId && c.id == *keepId) rowToSelect = idx;
|
||||
if (!keepSet.empty() && keepSet.count(c.id) != 0) {
|
||||
rowsToSelect.push_back(idx);
|
||||
if (firstRestored < 0) firstRestored = idx;
|
||||
}
|
||||
++row;
|
||||
}
|
||||
bool deferredInitialSelect = false;
|
||||
if (!filteredIndices_.empty() && rowToSelect >= 0) {
|
||||
list_->SetItemState(rowToSelect,
|
||||
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
|
||||
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
list_->EnsureVisible(rowToSelect);
|
||||
} else if (!filteredIndices_.empty() && !keepId.has_value()) {
|
||||
if (!filteredIndices_.empty() && !rowsToSelect.empty()) {
|
||||
for (long r : rowsToSelect) {
|
||||
const long flags = (r == firstRestored)
|
||||
? (wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED)
|
||||
: wxLIST_STATE_SELECTED;
|
||||
list_->SetItemState(r, flags, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
}
|
||||
list_->EnsureVisible(firstRestored);
|
||||
} else if (!filteredIndices_.empty() && !keepIds.has_value()) {
|
||||
// Defer the initial selection to the next event turn so first
|
||||
// paint stays responsive.
|
||||
deferredInitialSelect = true;
|
||||
@@ -705,6 +766,72 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void onListKeyDown(wxKeyEvent& event) {
|
||||
const int key = event.GetKeyCode();
|
||||
const bool copyChord =
|
||||
(event.ControlDown() || event.CmdDown()) && (key == 'C' || key == 'c');
|
||||
if (!copyChord) {
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
copySelectedRowsToClipboard();
|
||||
}
|
||||
|
||||
void copySelectedRowsToClipboard() {
|
||||
const auto cards = selectedCards();
|
||||
if (cards.empty() || textCols_.empty()) return;
|
||||
|
||||
auto formatRow = [&](const TCard& card) {
|
||||
std::string line;
|
||||
auto appendCell = [&](std::string_view cell) {
|
||||
if (!line.empty()) line.push_back('\t');
|
||||
line.append(cell);
|
||||
};
|
||||
// Leading text columns (everything except trailing Note).
|
||||
for (std::size_t i = 0; i + 1 < textCols_.size(); ++i) {
|
||||
appendCell(renderTextCell(card, i));
|
||||
}
|
||||
// Icon/flag columns — no list text; export as true/false.
|
||||
for (std::size_t i = 0; i < iconCols_.size(); ++i) {
|
||||
appendCell(isIconColumnSet(card, i) ? "true" : "false");
|
||||
}
|
||||
// Trailing Note.
|
||||
appendCell(renderTextCell(card, textCols_.size() - 1));
|
||||
return line;
|
||||
};
|
||||
|
||||
std::string payload = formatRow(cards.front());
|
||||
for (std::size_t i = 1; i < cards.size(); ++i) {
|
||||
payload.push_back('\n');
|
||||
payload.append(formatRow(cards[i]));
|
||||
}
|
||||
|
||||
wxClipboardLocker lock;
|
||||
if (!lock) return;
|
||||
if (!wxTheClipboard->SetData(
|
||||
new wxTextDataObject(wxString::FromUTF8(payload.c_str())))) {
|
||||
return;
|
||||
}
|
||||
if (cards.size() == 1) {
|
||||
emitUiStatus("Saved entry to clipboard");
|
||||
} else {
|
||||
emitUiStatus(wxString::Format("Saved %zu entries to clipboard", cards.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void emitUiStatus(const wxString& message) {
|
||||
wxCommandEvent ev(EVT_UI_STATUS, GetId());
|
||||
ev.SetEventObject(this);
|
||||
ev.SetString(message);
|
||||
// Same parent-hop as BaseSelectedCardPanel::emitPreviewStatus so the
|
||||
// command event can propagate up to MainFrame's status strip.
|
||||
if (auto* parent = GetParent()) {
|
||||
parent->GetEventHandler()->ProcessEvent(ev);
|
||||
} else {
|
||||
ProcessWindowEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- members ----------------------------------------------------------
|
||||
|
||||
static constexpr int kFlagIconSize = 14;
|
||||
|
||||
@@ -49,9 +49,14 @@ private:
|
||||
std::string setName,
|
||||
bool fillSetNoOnSuccess,
|
||||
bool showFailureDialog);
|
||||
void requestBySetNoAsync(unsigned capturedEpoch,
|
||||
std::string setName,
|
||||
std::string setNo,
|
||||
bool showFailureDialog);
|
||||
void applyDetectedVariants(unsigned capturedEpoch,
|
||||
Result<std::vector<AutoDetectedPrint>> detected,
|
||||
bool fillSetNoOnSuccess,
|
||||
bool fillNameOnSuccess,
|
||||
bool showFailureDialog);
|
||||
void rebuildVariantRingFromCache();
|
||||
void syncRingPositionToControls();
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void syncEditToolbarVisibility();
|
||||
void ensureSetsLoaded();
|
||||
const std::vector<Set>& setsForDialog();
|
||||
void ensureSingleCardsMounted(wxWindow* splitterParent);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class wxBitmapButton;
|
||||
class wxPanel;
|
||||
class wxWindow;
|
||||
|
||||
@@ -67,6 +68,10 @@ public:
|
||||
virtual void onEditCard(wxWindow* parentWindow) = 0;
|
||||
virtual void onDeleteCard(wxWindow* parentWindow) = 0;
|
||||
|
||||
// Magic uses MainFrame's shared Edit button; hostsOwnLayout games ignore
|
||||
// this and manage their own toolbar. Default no-op.
|
||||
virtual void attachSharedToolbarEdit(wxBitmapButton* edit) { (void)edit; }
|
||||
|
||||
// Sets menu action ("Update Magic" / "Update Pokemon"). Returns the
|
||||
// user-visible status string for the parent's status bar.
|
||||
virtual std::string onUpdateSets(wxWindow* parentWindow) = 0;
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
void onAddCard(wxWindow* parentWindow) override;
|
||||
void onEditCard(wxWindow* parentWindow) override;
|
||||
void onDeleteCard(wxWindow* parentWindow) override;
|
||||
void attachSharedToolbarEdit(wxBitmapButton* edit) override;
|
||||
std::string onUpdateSets(wxWindow* parentWindow) override;
|
||||
void setFilter(std::string_view filter) override;
|
||||
void nudgeSelection(int delta) override;
|
||||
@@ -51,6 +52,7 @@ public:
|
||||
private:
|
||||
void ensureSetsLoaded();
|
||||
const std::vector<Set>& setsForDialog();
|
||||
void syncEditToolbarVisibility();
|
||||
|
||||
ConfigService& config_;
|
||||
CollectionService<MagicCard>& collection_;
|
||||
@@ -61,6 +63,7 @@ private:
|
||||
|
||||
MagicCardListPanel* listPanel_{nullptr};
|
||||
MagicSelectedCardPanel* selectedPanel_{nullptr};
|
||||
wxBitmapButton* sharedEditButton_{nullptr};
|
||||
std::vector<Set> setsCache_;
|
||||
bool attemptedInitialSetLoad_{false};
|
||||
};
|
||||
|
||||
@@ -62,9 +62,14 @@ private:
|
||||
std::string setId,
|
||||
bool fillSetNoOnSuccess,
|
||||
bool showFailureDialog);
|
||||
void requestBySetNoAsync(unsigned capturedEpoch,
|
||||
std::string setId,
|
||||
std::string setNo,
|
||||
bool showFailureDialog);
|
||||
void applyDetectedVariants(unsigned capturedEpoch,
|
||||
Result<std::vector<AutoDetectedPrint>> detected,
|
||||
bool fillSetNoOnSuccess,
|
||||
bool fillNameOnSuccess,
|
||||
bool showFailureDialog);
|
||||
void rebuildVariantRingFromCache();
|
||||
void syncRingPositionToControls();
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
[[nodiscard]] std::string updateSetsMenuLabel() const override { return "Update Pokemon"; }
|
||||
|
||||
private:
|
||||
void syncEditToolbarVisibility();
|
||||
void ensureSetsLoaded();
|
||||
const std::vector<Set>& setsForDialog(PokemonRegion region);
|
||||
void ensureSingleCardsMounted(wxWindow* splitterParent);
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
#include <wx/colour.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class wxBitmapButton;
|
||||
class wxDialog;
|
||||
class wxWindow;
|
||||
class wxString;
|
||||
@@ -30,4 +35,11 @@ void themeModalDialog(wxDialog* dlg, Theme theme);
|
||||
int showThemedMessageDialog(wxWindow* parent, const wxString& message, const wxString& caption, long style);
|
||||
int showThemedConfirmDialog(wxWindow* parent, const wxString& message, const wxString& caption);
|
||||
|
||||
// Show/hide the shared or per-game Edit toolbar button and reflow its sizer
|
||||
// so Add/Delete close the gap when Edit is hidden for multi-select.
|
||||
void setToolbarEditVisible(wxBitmapButton* edit, bool visible);
|
||||
|
||||
// Confirm copy for Delete: one card by name, or "Delete N selected entries?".
|
||||
wxString deleteCardsConfirmMessage(std::size_t count, std::string_view singleCardName);
|
||||
|
||||
} // namespace ccm::ui
|
||||
|
||||
@@ -54,7 +54,8 @@ private:
|
||||
void prefetchVariantsForCurrentCardSilent(unsigned capturedEpoch);
|
||||
void requestByNameAsync(unsigned capturedEpoch, std::string name, std::string setId,
|
||||
bool showFailureDialog);
|
||||
void requestByNoAsync(unsigned capturedEpoch, std::string setNo, bool showFailureDialog);
|
||||
void requestByNoAsync(unsigned capturedEpoch, std::string setId, std::string setNo,
|
||||
bool showFailureDialog);
|
||||
void applyDetectedList(unsigned capturedEpoch,
|
||||
Result<std::vector<AutoDetectedPrint>> detected,
|
||||
bool showFailureDialog, bool applyFirst);
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void syncEditToolbarVisibility();
|
||||
void ensureSetsLoaded();
|
||||
// Fetches sets + checklist catalog from Yugipedia and persists both.
|
||||
// Returns false on failure (error dialogs already shown).
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
#include <wx/button.h>
|
||||
#include <wx/stattext.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhCardEditDialog final : public BaseCardEditDialog<YuGiOhCard> {
|
||||
@@ -19,6 +24,7 @@ public:
|
||||
EditMode mode,
|
||||
YuGiOhCard initial,
|
||||
const std::vector<Set>* preloadedSets = nullptr);
|
||||
~YuGiOhCardEditDialog() override;
|
||||
|
||||
protected:
|
||||
void buildFlagsRow(wxBoxSizer* flagsBox) override;
|
||||
@@ -31,6 +37,10 @@ protected:
|
||||
void onSetSelectionApplied() override;
|
||||
|
||||
private:
|
||||
struct VariantFetchState {
|
||||
std::atomic<bool> alive{true};
|
||||
};
|
||||
|
||||
void onAutoDetectSetNo(wxCommandEvent&);
|
||||
void onAutoDetectRarity(wxCommandEvent&);
|
||||
void onNextSetNo(wxCommandEvent&);
|
||||
@@ -42,6 +52,10 @@ private:
|
||||
void onSetCodeAutoDetect(wxCommandEvent&);
|
||||
void syncSetModeHint();
|
||||
void autoDetectFromApi(bool fillSetNo, bool fillRarity);
|
||||
void requestBySetNoAsync(unsigned capturedEpoch, std::string setId, std::string setName,
|
||||
std::string setNo);
|
||||
void applyReverseDetectedList(unsigned capturedEpoch,
|
||||
Result<std::vector<AutoDetectedPrint>> detected);
|
||||
void refreshSetNoFullPreview();
|
||||
void clearCachedPrintVariants();
|
||||
bool fetchAndCachePrintVariants();
|
||||
@@ -59,6 +73,7 @@ private:
|
||||
EditMode dialogMode_;
|
||||
unsigned variantFetchEpoch_{0};
|
||||
CardPreviewService& cardPreview_;
|
||||
std::shared_ptr<VariantFetchState> variantFetchState_;
|
||||
wxTextCtrl* setNoCtrl_{nullptr};
|
||||
wxStaticText* setNoFullPreview_{nullptr};
|
||||
wxChoice* rarityChoice_{nullptr};
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
[[nodiscard]] std::string updateSetsMenuLabel() const override { return "Update Yu-Gi-Oh!"; }
|
||||
|
||||
private:
|
||||
void syncEditToolbarVisibility();
|
||||
void ensureSetsLoaded();
|
||||
const std::vector<Set>& setsForDialog();
|
||||
void ensureSingleCardsMounted(wxWindow* splitterParent);
|
||||
|
||||
Reference in New Issue
Block a user