summaryrefslogtreecommitdiff
path: root/src/createinstancedialogpages.h
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-11-06 16:13:06 -0500
committerisanae <14251494+isanae@users.noreply.github.com>2020-11-07 20:16:26 -0500
commitbaa5c3abee4960046e4fe1e2240dd1e73253c43c (patch)
tree332dcfbc6c85978e507ed0c908d97195e4b2981d /src/createinstancedialogpages.h
parentb962d8081624ffdd7ab5cecbb6b2701b000b6b73 (diff)
documentation
use an okay flag in PathsPage to avoid calling checkPaths() in ready() and make stuff mutable moved a few things around
Diffstat (limited to 'src/createinstancedialogpages.h')
-rw-r--r--src/createinstancedialogpages.h368
1 files changed, 352 insertions, 16 deletions
diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h
index 099071a2..e2eaf0fb 100644
--- a/src/createinstancedialogpages.h
+++ b/src/createinstancedialogpages.h
@@ -15,14 +15,26 @@ class NexusConnectionUI;
namespace cid
{
+// returns "%base_dir%/dir"
+//
QString makeDefaultPath(const std::wstring& dir);
+// remembers the original text of the given label and, if it contains a %1,
+// sets it in setText()
+//
class PlaceholderLabel
{
public:
PlaceholderLabel(QLabel* label);
+
+ // if the original label text contained a %1, replaces it by the arg and
+ // sets that as the new label text
+ //
void setText(const QString& arg);
+
+ // whether the label is visible
+ //
void setVisible(bool b);
private:
@@ -31,25 +43,67 @@ private:
};
+// one page in the wizard
+//
+// each page can implement one or more selected*() below; those are called
+// by CreateInstanceDialog to gather data from all pages
+//
class Page
{
public:
Page(CreateInstanceDialog& dlg);
+ // whether this page has been filled and is valid; used by the dialog to
+ // determine if it can move to the next page
+ //
virtual bool ready() const;
+
+ // called every time a page is shown in the screen
+ //
virtual void activated();
+ // overrides whether this page should be skipped; this is used by
+ // CreateInstanceDialog::setSinglePage() to disable all other pages
+ //
void setSkip(bool b);
+
+ // whether this page should be skipped
+ //
bool skip() const;
+ // asks the dialog to update its navigation buttons, typically used when a
+ // page changes its ready state without moving to a different page
+ //
void updateNavigation();
+
+ // asks the dialog to move to the next page; some pages will automatically
+ // advance once the user has made the proper selection
+ //
void next();
+
+ // returns the instance type
+ //
virtual CreateInstanceDialog::Types selectedInstanceType() const;
+
+ // returns the game plugin
+ //
virtual MOBase::IPluginGame* selectedGame() const;
+
+ // returns the game directory
+ //
virtual QString selectedGameLocation() const;
+
+ // returns the game variant
+ //
virtual QString selectedGameVariant(MOBase::IPluginGame* game) const;
+
+ // returns the instance name
+ //
virtual QString selectedInstanceName() const;
+
+ // returns the various paths
+ //
virtual CreateInstanceDialog::Paths selectedPaths() const;
protected:
@@ -58,10 +112,15 @@ protected:
const PluginContainer& m_pc;
bool m_skip;
+
+ // implemented by derived classes, overridden by setSkip(true)
+ //
virtual bool doSkip() const;
};
+// introduction page, can be disabled by a global setting
+//
class IntroPage : public Page
{
public:
@@ -72,15 +131,27 @@ protected:
};
+// instance type page
+//
class TypePage : public Page
{
public:
TypePage(CreateInstanceDialog& dlg);
+ // whether a type has been been selected
+ //
bool ready() const override;
+
+ // returns the selected type
+ //
CreateInstanceDialog::Types selectedInstanceType() const override;
+ // selects a global instance
+ //
void global();
+
+ // selects a portable instance
+ //
void portable();
private:
@@ -88,160 +159,425 @@ private:
};
+// game plugin page, displays a list of command buttons for each game, along
+// with a "browse" button for custom directories and filtering stuff
+//
+// the game list initially only shows plugins that report isInstalled(), and the
+// user has two ways of specifying paths for games that were not found:
+//
+// 1) by clicking the "Browse..." button and selecting an arbitrary directory
+//
+// all plugins are checked until one returns true for looksValid(); if none
+// of them do, this is an error
+//
+// 2) by checking the "Show all supported games" checkbox and clicking one
+// of the games on the list
+//
+// if the selected plugin doesn't recognize the directory, the user is
+// warned, but is allowed to continue; there's also some logic to try to
+// find another plugin that can manage this directory and suggest it
+// instead
+//
class GamePage : public Page
{
public:
GamePage(CreateInstanceDialog& dlg);
+ // whether a game has been selected
+ //
bool ready() const override;
+
+ // returns the selected game
+ //
MOBase::IPluginGame* selectedGame() const override;
+
+ // returns the selected game directory
QString selectedGameLocation() const override;
+
+ // selects the given game and toggles its associated button; the game
+ // directory can be overridden
+ //
+ // pops up a directory selection dialog if `dir` is empty and the plugin
+ // hasn't detected the game
+ //
void select(MOBase::IPluginGame* game, const QString& dir={});
+
+ // pops up a directory selection dialog and looks for a plugin to manage
+ // it
+ //
void selectCustom();
+ // pops up a warning dialog that the game at the given path is not supported
+ // by any plugin, includes a list of all game plugins in the details section
+ // of the dialog
+ //
void warnUnrecognized(const QString& path);
private:
+ // a single game, with its button and custom directory, if any
+ //
struct Game
{
+ // game plugin
MOBase::IPluginGame* game = nullptr;
+
+ // button on the ui
QCommandLinkButton* button = nullptr;
+
+ // game directory; set in ctor if the plugin has detected the game, or
+ // set later when the user selects a directory
QString dir;
+
+ // whether a directory has been set for this game, either auto detected
+ // or by the user
bool installed = false;
+
Game(MOBase::IPluginGame* g);
Game(const Game&) = delete;
Game& operator=(const Game&) = delete;
};
+ // list of all game plugins, even if they're not installed; those are filtered
+ // from the ui if the checkbox isn't checked
std::vector<std::unique_ptr<Game>> m_games;
+
+ // current selection
Game* m_selection;
+
+ // filter
MOBase::FilterWidget m_filter;
+
+ // returns a list of all the game plugins sorted with natsort
+ //
std::vector<MOBase::IPluginGame*> sortedGamePlugins() const;
- Game* findGame(MOBase::IPluginGame* game);
+
+ // creates the m_games list
+ //
void createGames();
+
+ // finds the game struct associated with the given game
+ //
+ Game* findGame(MOBase::IPluginGame* game);
+
+
+ // creates the ui for the given game button
+ //
+ void createGameButton(Game* g);
+
+ // adds the given button to the ui
+ //
+ void addButton(QAbstractButton* b);
+
+ // updates the given button on the ui, sets the text, icon, etc.
+ //
void updateButton(Game* g);
+
+ // called when a button has been clicked; selects the game or asks the user
+ // for directory, depending
+ //
void selectButton(Game* g);
+
+ // removes all buttons from the ui
+ //
void clearButtons();
- void addButton(QAbstractButton* b);
+
+ // creates the "Browse" button
+ //
QCommandLinkButton* createCustomButton();
- void createGameButton(Game* g);
+
+
+ // clears the button list and adds all the buttons to it, depending on
+ // filtering and stuff
+ //
void fillList();
- void onFilter();
+
+ // checks whether the given path looks valid to the given game plugin
+ //
+ // if the plugin doesn't like the path, allows the user to override and
+ // accept, but also attempts to find another plugin that wants it and
+ // propose that as an alternative, if there's one
+ //
+ // returns:
+ // - if the user selects the alternative plugin, returns that plugin
+ // instead;
+ // - if the path is bad but the user overrides, returns the given plugin
+ // - if the user cancels or if no plugins can manage the directory, returns
+ // null
+ //
Game* checkInstallation(const QString& path, Game* g);
- MOBase::IPluginGame* findAnotherGame(const QString& path);
+
+ // tells the user that the path cannot be handled by any game plugin, returns
+ // true if the user decides to accept anyway
+ //
bool confirmUnknown(const QString& path, MOBase::IPluginGame* game);
+
+ // tells the user that the path can be handled by a different plugin than the
+ // selected one and allows them to either
+ // 1) use the alternative, guessedGame is returned;
+ // 2) use the selection anyway, selectedGame is returned; or
+ // 3) cancel, null is returned
+ //
MOBase::IPluginGame* confirmOtherGame(
const QString& path,
MOBase::IPluginGame* selectedGame, MOBase::IPluginGame* guessedGame);
};
+// game variants page; displays a list of command buttons for game variants, as
+// reported by the game plugin
+//
+// this page is always skipped if the game plugin reports no variants
+//
class VariantsPage : public Page
{
public:
VariantsPage(CreateInstanceDialog& dlg);
+ // whether a variant has been selected or the game plugin reports no variants
+ //
bool ready() const override;
+
+ // uses the game selected in the previous page to fill the list, this must be
+ // called every time because the user may go back in forth in the wizard
+ //
void activated() override;
+
+ // returns the selected variant, if any
+ //
QString selectedGameVariant(MOBase::IPluginGame* game) const override;
+ // selects the given variant
+ //
void select(const QString& variant);
protected:
+ // returns true if the game has no variants
+ //
bool doSkip() const override;
private:
+ // game that was selected the last time this page was active
MOBase::IPluginGame* m_previousGame;
+
+ // buttons
std::vector<QCommandLinkButton*> m_buttons;
+
+ // selected variant
QString m_selection;
+
+ // fills the list with buttons
void fillList();
};
+// instance name page; displays a textbox where the user can enter a name and
+// does basic checks to make sure the name is valid and not a duplicate
+//
+// skipped for portable instances
+//
class NamePage : public Page
{
public:
NamePage(CreateInstanceDialog& dlg);
+ // whether a valid name has been entered
+ //
bool ready() const override;
+
+ // uses the selected game to generate an instance name
+ //
+ // as long as the user hasn't modified the textbox, this will regenerate a new
+ // instance name every time the selected game changes
+ //
void activated() override;
+
+ // returns the instance name
+ //
QString selectedInstanceName() const override;
protected:
+ // returns true for portable instances
+ //
bool doSkip() const override;
private:
- mutable PlaceholderLabel m_label, m_exists, m_invalid;
+ // game label, replaces %1 with the game name
+ PlaceholderLabel m_label;
+
+ // "instance already exists" label, replaces %1 with instance name
+ PlaceholderLabel m_exists;
+
+ // "instance name invalid" label, replaces %1 with instance name
+ PlaceholderLabel m_invalid;
+
+ // whether the user has modified the text, prevents auto generation when the
+ // selected game changes
bool m_modified;
+
+ // whether the instance name is valid
bool m_okay;
+
+ // called when the user modifies the textbox, remember that it has changed and
+ // calls verify()
+ //
void onChanged();
- void updateWarnings();
+
+ // check if the entered name is valid, sets m_okay and calls checkName()
+ //
+ void verify();
+
+ // updates the ui depending on whether the given instance name is valid in
+ // the given directory; returns false if the name is invalid
+ //
bool checkName(QString parentDir, QString name);
};
+// instance paths page; shows a single textbox for the base directory, or a
+// series of textboxes for all the configurable paths if the advanced checkbox
+// is checked
+//
class PathsPage : public Page
{
public:
PathsPage(CreateInstanceDialog& dlg);
+ // whether all paths make sense
+ //
bool ready() const override;
+
+ // resets all the paths if the instance type or instance name have changed,
+ // the current values are kept as long as these don't change; also updates the
+ // game name in the ui
+ //
void activated() override;
+ // returns the selected paths
+ //
CreateInstanceDialog::Paths selectedPaths() const override;
private:
+ // instance name the last time this page was active
QString m_lastInstanceName;
+
+ // instance type the last time this page was active
CreateInstanceDialog::Types m_lastType;
+
+ // help label, replaces %1 by the game name
PlaceholderLabel m_label;
- mutable PlaceholderLabel m_simpleExists, m_simpleInvalid;
- mutable PlaceholderLabel m_advancedExists, m_advancedInvalid;
+ // path exists/is invalid labels for the simple page, replaces %1 with the
+ // path
+ PlaceholderLabel m_simpleExists, m_simpleInvalid;
+
+ // path exists/is invalid labels for the advanced page, replaces %1 with the
+ // path
+ PlaceholderLabel m_advancedExists, m_advancedInvalid;
+
+ // whether the paths are valid
+ bool m_okay;
+
+
+ // called when the user changes any textbox, checks the path and updates nav
+ //
void onChanged();
- bool checkPaths() const;
- bool checkAdvancedPath(const QString& path) const;
+
+ // checks the simple or advanced paths, sets m_okay
+ //
+ void checkPaths();
+
+ // checks a simple path, forwards to checkPath() with the simple labels
+ //
+ bool checkSimplePath(const QString& path);
+
+ // checks an advanced path, forwards to checkPath() with the advanced labels
+ //
+ bool checkAdvancedPath(const QString& path);
+
+ // returns false if the path is invalid or already exists, sets the given
+ // labels accordingly
+ //
+ bool checkPath(
+ QString path,
+ PlaceholderLabel& existsLabel, PlaceholderLabel& invalidLabel);
+
+ // replaces %base_dir% in the given path by whatever's in the base path
+ // textbox
+ //
QString resolve(const QString& path) const;
+
+ // called when the advanced checkbox is toggled, switches the active page
+ // and checks the paths
+ //
void onAdvanced();
+
+ // called whenever the page becomes active
+ //
+ // this normally doesn't change the textboxes unless they're empty, but if the
+ // instance name or type have changed, `force` is true, which forces all paths
+ // to reset
+ //
void setPaths(const QString& name, bool force);
+
+ // sets the given textbox to the path if it's empty or if `force` is true
+ //
void setIfEmpty(QLineEdit* e, const QString& path, bool force);
- bool checkPath(
- QString path,
- PlaceholderLabel& existsLabel, PlaceholderLabel& invalidLabel) const;
};
+// nexus connection page; this reuses the ui found in the settings dialog and
+// is skipped if there's already an api key in the credentials manager
+//
class NexusPage : public Page
{
public:
NexusPage(CreateInstanceDialog& dlg);
~NexusPage();
+ // always returns true, this is an optional page
+ //
bool ready() const override;
- void activated() override;
protected:
+ // returns true if the api key was already detected
+ //
bool doSkip() const override;
private:
+ // connection ui
std::unique_ptr<NexusConnectionUI> m_connectionUI;
+
+ // set to true only if the api key was detected when opening the dialog, or
+ // going back and forth would skip the page after the process is completed,
+ // which would be unexpected
bool m_skip;
};
+// shows a text log of all the creation parameters
+//
class ConfirmationPage : public Page
{
public:
ConfirmationPage(CreateInstanceDialog& dlg);
+ // recreates the log with the latest settings
+ //
void activated() override;
- QString toLocalizedString(CreateInstanceDialog::Types t) const;
+ // returns the text for the log
+ //
QString makeReview() const;
+
+private:
+ // returns a log line with the given caption and path, something like
+ // " - caption: path"
+ //
QString dirLine(const QString& caption, const QString& path) const;
};