mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-09-01 02:18:48 +00:00
Minor: New Game Yu-Gi-Oh! Bandai (#22)
This commit is contained in:
@@ -27,6 +27,7 @@ struct AppContext {
|
||||
IGameModule& pokemonModule;
|
||||
IGameModule& yuGiOhModule;
|
||||
IGameModule& digiBattle99Module;
|
||||
IGameModule& yuGiOhBandaiModule;
|
||||
// Asia Pokemon sets/preview backend (not a separate Game menu entry).
|
||||
IGameModule& japanesePokemonModule;
|
||||
// Active per-game UI bundles. The order is the order shown in the
|
||||
|
||||
@@ -136,6 +136,10 @@ protected:
|
||||
// controls cannot outlive the lookup identity.
|
||||
virtual void onCardLookupContextChanged() {}
|
||||
|
||||
// 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; }
|
||||
|
||||
// Common helpers ----------------------------------------------------------
|
||||
|
||||
void appendRow(wxFlexGridSizer* grid, const wxString& label, wxWindow* ctrl) {
|
||||
@@ -148,6 +152,8 @@ protected:
|
||||
[[nodiscard]] const TCard& constCard() const noexcept { return card_; }
|
||||
void syncCardFromControls() { writeFromControls(); }
|
||||
[[nodiscard]] wxComboBox* setComboControl() const noexcept { return setCombo_; }
|
||||
[[nodiscard]] wxTextCtrl* nameControl() const noexcept { return nameCtrl_; }
|
||||
[[nodiscard]] wxChoice* languageChoiceControl() const noexcept { return languageChoice_; }
|
||||
|
||||
[[nodiscard]] const Set* selectedSetFromControls() const {
|
||||
const auto& available = availableSets();
|
||||
@@ -471,6 +477,7 @@ private:
|
||||
"Add card", wxOK | wxICON_INFORMATION);
|
||||
return;
|
||||
}
|
||||
if (!validateExtraFields()) return;
|
||||
if (mode_ == EditMode::Edit && !(card_ == openingSnapshot_)) {
|
||||
if (showThemedConfirmDialog(
|
||||
this,
|
||||
|
||||
@@ -307,6 +307,8 @@ private:
|
||||
case Game::DigiBattle99:
|
||||
// No stable public Digi-Battle back URL; UI uses bundled PNG.
|
||||
return {};
|
||||
case Game::YuGiOhBandai:
|
||||
return "https://ms.yugipedia.com//3/34/Back-BAN-JP-1999.png";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -51,10 +51,6 @@ private:
|
||||
|
||||
[[nodiscard]] IGameView* activeView();
|
||||
|
||||
#ifdef __WXMSW__
|
||||
WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) override;
|
||||
#endif
|
||||
|
||||
AppContext& ctx_;
|
||||
Game activeGame_{Game::Magic};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
class wxDialog;
|
||||
class wxWindow;
|
||||
class wxString;
|
||||
class wxTextCtrl;
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
@@ -23,6 +24,8 @@ struct ThemePalette {
|
||||
ThemePalette paletteForTheme(Theme theme);
|
||||
Theme inferThemeFromWindow(const wxWindow* window);
|
||||
void applyThemeToWindowTree(wxWindow* root, const ThemePalette& palette, Theme theme);
|
||||
// Force palette colors onto a text input (incl. MSW dark-mode typed-text fix).
|
||||
void applyPaletteToTextCtrl(wxTextCtrl* text, const ThemePalette& palette, Theme theme);
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include "ccm/domain/YuGiOhBandaiCard.hpp"
|
||||
#include "ccm/ports/ICardPreviewSource.hpp"
|
||||
#include "ccm/services/CardPreviewService.hpp"
|
||||
#include "ccm/ui/BaseCardEditDialog.hpp"
|
||||
#include <wx/button.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhBandaiCardEditDialog final : public BaseCardEditDialog<YuGiOhBandaiCard> {
|
||||
public:
|
||||
YuGiOhBandaiCardEditDialog(wxWindow* parent,
|
||||
ImageService& imageService,
|
||||
SetService& setService,
|
||||
CardPreviewService& cardPreview,
|
||||
EditMode mode,
|
||||
YuGiOhBandaiCard initial,
|
||||
const std::vector<Set>* preloadedSets = nullptr);
|
||||
~YuGiOhBandaiCardEditDialog() override;
|
||||
|
||||
protected:
|
||||
void buildFlagsRow(wxBoxSizer* flagsBox) override;
|
||||
void appendExtraRows(wxFlexGridSizer* grid) override;
|
||||
void readExtraFromCard() override;
|
||||
void writeExtraToCard() override;
|
||||
[[nodiscard]] std::span<const Language> languagesForChoice() const override;
|
||||
[[nodiscard]] std::string updateMenuName() const override {
|
||||
return "Update Yu-Gi-Oh! (Bandai)";
|
||||
}
|
||||
void onCardLookupContextChanged() override;
|
||||
[[nodiscard]] bool validateExtraFields() override;
|
||||
|
||||
private:
|
||||
struct VariantFetchState {
|
||||
std::atomic<bool> alive{true};
|
||||
};
|
||||
|
||||
void onAutoDetectBySetNo(wxCommandEvent&);
|
||||
void onNextSetNo(wxCommandEvent&);
|
||||
void onAutoDetectByName(wxCommandEvent&);
|
||||
void onRarityChoiceChanged(wxCommandEvent&);
|
||||
void onSetSelectionChanged(wxCommandEvent&);
|
||||
|
||||
void scheduleDeferredVariantPrefetch();
|
||||
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 applyDetectedList(unsigned capturedEpoch,
|
||||
Result<std::vector<AutoDetectedPrint>> detected,
|
||||
bool showFailureDialog, bool applyFirst);
|
||||
void clearCachedPrintVariants();
|
||||
void applyDetectedPrint(const AutoDetectedPrint& print);
|
||||
void applyRarityStringToChoice(const std::string& rarity);
|
||||
void maybeAutoCheckHoloForRarity(const std::string& rarity);
|
||||
void refreshVariantNextControls();
|
||||
[[nodiscard]] std::size_t findAvailableSetIndex(const std::string& setId) const;
|
||||
|
||||
EditMode dialogMode_;
|
||||
unsigned variantFetchEpoch_{0};
|
||||
CardPreviewService& cardPreview_;
|
||||
std::shared_ptr<VariantFetchState> variantFetchState_;
|
||||
|
||||
wxTextCtrl* setNoCtrl_{nullptr};
|
||||
wxButton* autoSetNoBtn_{nullptr};
|
||||
wxButton* nextSetNoBtn_{nullptr};
|
||||
wxChoice* rarityChoice_{nullptr};
|
||||
wxButton* autoRarityBtn_{nullptr};
|
||||
wxCheckBox* holoCheck_{nullptr};
|
||||
wxCheckBox* signedCheck_{nullptr};
|
||||
wxCheckBox* alteredCheck_{nullptr};
|
||||
|
||||
std::vector<AutoDetectedPrint> cachedVariants_;
|
||||
std::size_t variantRingPos_{0};
|
||||
};
|
||||
|
||||
} // namespace ccm::ui
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "ccm/domain/YuGiOhBandaiCard.hpp"
|
||||
#include "ccm/services/CardSorter.hpp"
|
||||
#include "ccm/ui/BaseCardListPanel.hpp"
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhBandaiCardListPanel final
|
||||
: public BaseCardListPanel<YuGiOhBandaiCard, YuGiOhBandaiSortColumn> {
|
||||
public:
|
||||
explicit YuGiOhBandaiCardListPanel(wxWindow* parent);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] std::vector<TextColumnSpec> declareTextColumns() const override;
|
||||
[[nodiscard]] std::vector<IconColumnSpec> declareIconColumns() const override;
|
||||
[[nodiscard]] std::string renderTextCell(const YuGiOhBandaiCard& card,
|
||||
std::size_t idx) const override;
|
||||
[[nodiscard]] bool isIconColumnSet(const YuGiOhBandaiCard& card,
|
||||
std::size_t idx) const override;
|
||||
void sortBy(YuGiOhBandaiSortColumn column, bool ascending) override;
|
||||
[[nodiscard]] bool matchesFilter(const YuGiOhBandaiCard& card,
|
||||
std::string_view filter) const override;
|
||||
};
|
||||
|
||||
} // namespace ccm::ui
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "ccm/domain/YuGiOhBandaiCard.hpp"
|
||||
#include "ccm/games/IGameModule.hpp"
|
||||
#include "ccm/services/CardPreviewService.hpp"
|
||||
#include "ccm/services/CollectionService.hpp"
|
||||
#include "ccm/services/ConfigService.hpp"
|
||||
#include "ccm/services/ImageService.hpp"
|
||||
#include "ccm/services/SetService.hpp"
|
||||
#include "ccm/services/YuGiOhBandaiSetCatalogService.hpp"
|
||||
#include "ccm/ui/IGameView.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class wxBitmapButton;
|
||||
class wxBoxSizer;
|
||||
class wxPanel;
|
||||
class wxSimplebook;
|
||||
class wxSplitterWindow;
|
||||
class wxStaticText;
|
||||
class wxTextCtrl;
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhBandaiCardListPanel;
|
||||
class YuGiOhBandaiSelectedCardPanel;
|
||||
class YuGiOhBandaiSetCompletionPanel;
|
||||
|
||||
class YuGiOhBandaiGameView final : public IGameView {
|
||||
public:
|
||||
YuGiOhBandaiGameView(ConfigService& config,
|
||||
CollectionService<YuGiOhBandaiCard>& collection,
|
||||
SetService& sets,
|
||||
ImageService& images,
|
||||
CardPreviewService& cardPreview,
|
||||
IGameModule& module,
|
||||
YuGiOhBandaiSetCatalogService& catalogStore);
|
||||
|
||||
[[nodiscard]] Game gameId() const noexcept override { return Game::YuGiOhBandai; }
|
||||
[[nodiscard]] std::string displayName() const override { return "Yu-Gi-Oh! (Bandai)"; }
|
||||
|
||||
wxPanel* listPanel(wxWindow* parent) override;
|
||||
wxPanel* selectedPanel(wxWindow* parent) override;
|
||||
wxPanel* contentPanel(wxWindow* parent) override;
|
||||
[[nodiscard]] wxPanel* contentPanelIfCreated() const noexcept override {
|
||||
return contentPanel_;
|
||||
}
|
||||
[[nodiscard]] bool hostsOwnLayout() const noexcept override { return true; }
|
||||
|
||||
void refreshCollection(std::optional<std::uint32_t> selectId = std::nullopt) override;
|
||||
void onAddCard(wxWindow* parentWindow) override;
|
||||
void onEditCard(wxWindow* parentWindow) override;
|
||||
void onDeleteCard(wxWindow* parentWindow) override;
|
||||
std::string onUpdateSets(wxWindow* parentWindow) override;
|
||||
void setFilter(std::string_view filter) override;
|
||||
void nudgeSelection(int delta) override;
|
||||
void applyTheme(const ThemePalette& palette) override;
|
||||
[[nodiscard]] std::string updateSetsMenuLabel() const override {
|
||||
return "Update Yu-Gi-Oh! (Bandai)";
|
||||
}
|
||||
|
||||
private:
|
||||
void ensureSetsLoaded();
|
||||
// Fetches sets + checklist catalog from Yugipedia and persists both.
|
||||
// Returns false on failure (error dialogs already shown).
|
||||
[[nodiscard]] bool downloadSetsAndCatalog(wxWindow* parentWindow,
|
||||
std::size_t* setCountOut = nullptr,
|
||||
std::size_t* packCountOut = nullptr);
|
||||
// Fetches sets + checklist catalog when set-catalog.json is missing.
|
||||
// Returns true if the catalog exists afterward. Shows error dialogs on failure.
|
||||
[[nodiscard]] bool ensureCatalogLoaded(wxWindow* parentWindow);
|
||||
void refreshSetCompletionFromStore();
|
||||
const std::vector<Set>& setsForDialog();
|
||||
void ensureSingleCardsMounted(wxWindow* splitterParent);
|
||||
void buildSingleCardsToolbar(wxWindow* parent, wxBoxSizer* pageSizer);
|
||||
void buildTabBar(wxWindow* parent, wxBoxSizer* rootSizer);
|
||||
void selectTab(int index);
|
||||
void refreshToolbarIcons(const ThemePalette& palette);
|
||||
void refreshTabBarTheme(const ThemePalette& palette);
|
||||
|
||||
ConfigService& config_;
|
||||
CollectionService<YuGiOhBandaiCard>& collection_;
|
||||
SetService& sets_;
|
||||
ImageService& images_;
|
||||
CardPreviewService& cardPreview_;
|
||||
IGameModule& module_;
|
||||
YuGiOhBandaiSetCatalogService& catalogStore_;
|
||||
|
||||
wxPanel* contentPanel_{nullptr};
|
||||
wxPanel* tabBar_{nullptr};
|
||||
wxSimplebook* book_{nullptr};
|
||||
wxSplitterWindow* singleSplitter_{nullptr};
|
||||
YuGiOhBandaiCardListPanel* listPanel_{nullptr};
|
||||
YuGiOhBandaiSelectedCardPanel* selectedPanel_{nullptr};
|
||||
YuGiOhBandaiSetCompletionPanel* setCompletionPanel_{nullptr};
|
||||
std::array<wxPanel*, 2> tabPanels_{{nullptr, nullptr}};
|
||||
std::array<wxStaticText*, 2> tabLabels_{{nullptr, nullptr}};
|
||||
int activeTab_{0};
|
||||
std::array<wxBitmapButton*, 3> toolbarButtons_{{nullptr, nullptr, nullptr}};
|
||||
wxTextCtrl* filterInput_{nullptr};
|
||||
std::vector<Set> setsCache_;
|
||||
bool attemptedInitialSetLoad_{false};
|
||||
};
|
||||
|
||||
} // namespace ccm::ui
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "ccm/domain/YuGiOhBandaiCard.hpp"
|
||||
#include "ccm/ui/BaseSelectedCardPanel.hpp"
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhBandaiSelectedCardPanel final : public BaseSelectedCardPanel<YuGiOhBandaiCard> {
|
||||
public:
|
||||
YuGiOhBandaiSelectedCardPanel(wxWindow* parent,
|
||||
ImageService& imageService,
|
||||
CardPreviewService& cardPreview);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] std::vector<DetailRowSpec> declareDetailRows() const override;
|
||||
[[nodiscard]] std::vector<FlagIconSpec> declareFlagIcons() const override;
|
||||
[[nodiscard]] std::string detailValueFor(const YuGiOhBandaiCard& card,
|
||||
DetailKey key) const override;
|
||||
[[nodiscard]] bool isFlagSet(const YuGiOhBandaiCard& card, DetailKey key) const override;
|
||||
[[nodiscard]] std::tuple<std::string, std::string, std::string>
|
||||
previewKey(const YuGiOhBandaiCard& card) const override;
|
||||
[[nodiscard]] Game gameId() const noexcept override { return Game::YuGiOhBandai; }
|
||||
};
|
||||
|
||||
} // namespace ccm::ui
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
// YuGiOhBandaiSetCompletionPanel: Set Completion tab — pack tiles with
|
||||
// progress bars for sets the user owns >=1 card of, plus an in-tab checklist
|
||||
// drill-down (unowned rows greyed). Catalog is offline (set-catalog.json).
|
||||
// Optional language filter restricts ownership to one language and labels
|
||||
// set titles as "{setName} ({language})".
|
||||
|
||||
#include "ccm/domain/Enums.hpp"
|
||||
#include "ccm/domain/YuGiOhBandaiCard.hpp"
|
||||
#include "ccm/domain/YuGiOhBandaiSetCatalog.hpp"
|
||||
#include "ccm/services/YuGiOhBandaiSetCatalogService.hpp"
|
||||
#include "ccm/ui/Theme.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxChoice;
|
||||
class wxListCtrl;
|
||||
class wxScrolledWindow;
|
||||
class wxSimplebook;
|
||||
class wxStaticText;
|
||||
|
||||
namespace ccm::ui {
|
||||
|
||||
class YuGiOhBandaiSetCompletionPanel : public wxPanel {
|
||||
public:
|
||||
YuGiOhBandaiSetCompletionPanel(wxWindow* parent, YuGiOhBandaiSetCatalogService& catalogStore);
|
||||
|
||||
void setCollection(std::vector<YuGiOhBandaiCard> cards);
|
||||
void reloadFromStore();
|
||||
void applyTheme(const ThemePalette& palette);
|
||||
|
||||
private:
|
||||
void showGridPage();
|
||||
void showChecklistPage(const std::string& setId, const std::string& setName);
|
||||
void rebuildGrid();
|
||||
void rebuildChecklist(const std::string& setId);
|
||||
void setEmptyMessage(const wxString& message);
|
||||
void clearGridTiles();
|
||||
void refreshLanguageChoice();
|
||||
void onLanguageChoice(wxCommandEvent& event);
|
||||
void rebuildCurrentView();
|
||||
[[nodiscard]] std::string displaySetName(const std::string& setName) const;
|
||||
|
||||
YuGiOhBandaiSetCatalogService& catalogStore_;
|
||||
YuGiOhBandaiSetCatalog catalog_;
|
||||
bool catalogLoaded_{false};
|
||||
std::vector<YuGiOhBandaiCard> collection_;
|
||||
ThemePalette palette_{};
|
||||
std::optional<Language> languageFilter_;
|
||||
|
||||
wxChoice* languageChoice_{nullptr};
|
||||
wxSimplebook* book_{nullptr};
|
||||
wxPanel* gridPage_{nullptr};
|
||||
wxScrolledWindow* scroll_{nullptr};
|
||||
wxBoxSizer* gridSizer_{nullptr};
|
||||
wxStaticText* emptyLabel_{nullptr};
|
||||
|
||||
wxPanel* detailPage_{nullptr};
|
||||
wxStaticText* detailTitle_{nullptr};
|
||||
wxListCtrl* checklist_{nullptr};
|
||||
std::string detailSetId_;
|
||||
std::string detailSetName_;
|
||||
};
|
||||
|
||||
} // namespace ccm::ui
|
||||
Reference in New Issue
Block a user