summaryrefslogtreecommitdiff
path: root/src/modinfodialogtab.h
diff options
context:
space:
mode:
authorSilarn <jrim@rimpo.org>2019-07-22 01:00:42 -0500
committerSilarn <jrim@rimpo.org>2019-07-22 01:00:42 -0500
commitdcd6d624672019727d7effd17aac86f72bff438b (patch)
tree1e8d3856f657d898c5992631599cf272d785f973 /src/modinfodialogtab.h
parent179a73857125ee604f42b0d5c2d765183c86d2c7 (diff)
parente73c309f08eff98f0dbd2590f594a83b67431eac (diff)
Merge branch 'Develop'
Diffstat (limited to 'src/modinfodialogtab.h')
-rw-r--r--src/modinfodialogtab.h341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h
new file mode 100644
index 00000000..c43fa076
--- /dev/null
+++ b/src/modinfodialogtab.h
@@ -0,0 +1,341 @@
+#ifndef MODINFODIALOGTAB_H
+#define MODINFODIALOGTAB_H
+
+#include "modinfodialogfwd.h"
+#include <QObject>
+
+namespace MOShared { class FilesOrigin; }
+namespace Ui { class ModInfoDialog; }
+
+class Settings;
+class OrganizerCore;
+
+// helper struct to avoid passing too much stuff to tab constructors
+//
+struct ModInfoDialogTabContext
+{
+ OrganizerCore& core;
+ PluginContainer& plugin;
+ QWidget* parent;
+ Ui::ModInfoDialog* ui;
+ ModInfoTabIDs id;
+ ModInfoPtr mod;
+ MOShared::FilesOrigin* origin;
+
+ ModInfoDialogTabContext(
+ OrganizerCore& core,
+ PluginContainer& plugin,
+ QWidget* parent,
+ Ui::ModInfoDialog* ui,
+ ModInfoTabIDs id,
+ ModInfoPtr mod,
+ MOShared::FilesOrigin* origin) :
+ core(core), plugin(plugin), parent(parent), ui(ui), id(id),
+ mod(mod), origin(origin)
+ {
+ }
+};
+
+
+// base class for all tabs in the mod info dialog
+//
+// when the dialog is opened or when next/previous is clicked, the sequence is:
+// setMod(), clear(), feedFile() an update()
+//
+// when the dialog is closed, canClose() is called on all tabs
+//
+// when a tab is selected for the first time for the current mod,
+// firstActivation() is called; this is used by NexusTab to refresh stuff
+//
+// when the dialog is first shown, restoreState() is called on all tabs and
+// saveState() is called when the dialog is closed
+//
+// there isn't a good framework for keyboard shortcuts because only the delete
+// key is used for now, which calls deletedRequested() on all tabs until one
+// returns true
+//
+// each tab override canHandleSeparators() and canHandleUnmanaged() to return
+// true if they can handle separators or unmanaged mods; if these return false
+// (which they do by default), the tabs will be removed from the widget entirely
+//
+// when tabs modify the origin and call emitOriginModified() (such as the
+// conflicts tabs), all tabs that return true in usesOriginFiles() will go
+// through the full update sequence as above
+//
+// tabs can call emitModOpen() to request showing a different mod
+//
+// hasDataChanged() should be called when a tab goes from having data to being
+// empty or vice versa; this will update the tab text colour
+//
+class ModInfoDialogTab : public QObject
+{
+ Q_OBJECT;
+
+public:
+ ModInfoDialogTab(const ModInfoDialogTab&) = delete;
+ ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete;
+ ModInfoDialogTab(ModInfoDialogTab&&) = default;
+ ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default;
+ virtual ~ModInfoDialogTab() = default;
+
+ // called by ModInfoDialog every time this tab is selected; this will call
+ // firstActivation() the first time it's called, until resetFirstActivation()
+ // is called
+ //
+ void activated();
+
+ // called by ModInfoDialog when the selected mod has changed, to make sure
+ // activated() will call firstActivation() next time
+ //
+ void resetFirstActivation();
+
+
+ // called when the selected mod changed, `mod` can never be empty, but
+ // `origin` can (if the mod is not active, for example)
+ //
+ // derived classes can override this to connect to events on the mod for
+ // examples (see NexusTab), but must call the base class implementation
+ //
+ virtual void setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin);
+
+ // this tab should clear its user interface; clear() will always be called
+ // before feedFile() and update()
+ //
+ virtual void clear() = 0;
+
+ // the dialog will go through each file in the mod and call feedFile()
+ // with it on all tabs; if a tab handles the file, it should return true to
+ // prevent other tabs from displaying it
+ //
+ // this prevents individual tabs from having to go through the filesystem
+ // independently, which would kill performance, but it cannot be the only way
+ // to update tabs because some of them don't actually use files (like
+ // NotesTab) or they use the internal structures such as `FilesOrigin` (like
+ // ConflictsTab)
+ //
+ // once all the files have been fed to tabs, update() will be called; tabs
+ // that need to do additional work after being fed files, or tabs that are
+ // unable to use feedFile() at all can use update() to do work
+ //
+ virtual bool feedFile(const QString& rootPath, const QString& filename);
+
+ // called after all the files on the filesystem have been sent through
+ // feedFile()
+ //
+ // this can be used to do a final processing of the files handled by
+ // feedFile() (ImagesTab will set up the scrollbars, for example) or something
+ // completely different (CategoriesTab will fill up the lists)
+ //
+ virtual void update();
+
+ // called when a tab is visible for the first time for the current mod, can
+ // be used to do expensive work that's not worth doing until the tab is
+ // actually selected by the user
+ //
+ virtual void firstActivation();
+
+ // called when closing the dialog, can return false to stop the dialog from
+ // closing
+ //
+ // this is typically used by tabs that require manual saving, like text files;
+ // tabs that refuse to close should focus themselves before showing whatever
+ // confirmation they have
+ //
+ virtual bool canClose();
+
+
+ // called after the dialog is closed, tabs should save whatever UI state they
+ // want
+ //
+ virtual void saveState(Settings& s);
+
+ // called before the is shown, tabs should restore whatever UI state they
+ // saved in saveState()
+ //
+ virtual void restoreState(const Settings& s);
+
+
+ // called on the selected tab when the Delete key is pressed on the keyboard;
+ // tabs _must_ check which widget currently has focus to decide whether this
+ // should be handled or not; do not blindly delete stuff when this is called
+ //
+ // if the delete request was handled, this should return true
+ //
+ virtual bool deleteRequested();
+
+
+ // return true if this tab can handle a separator mod, defaults to false;
+ // when this returns false, the tab is removed from the widget entirely
+ //
+ // if a tab can show meaningful information about a separator (like
+ // categories or notes), it should return true
+ //
+ virtual bool canHandleSeparators() const;
+
+ // return true if this tab can handle unmanaged mods, defaults to false;
+ // when this returns false, the tab is removed from the widget entirely
+ //
+ virtual bool canHandleUnmanaged() const;
+
+ // return true if this tab uses the files from the mod's origin, defaults to
+ // false
+ //
+ // tabs that do not care about the files inside a mod should return false,
+ // such as the notes or categories tab
+ //
+ // mods that return true will be updated anytime a tab calls
+ // emitOriginModifed()
+ //
+ virtual bool usesOriginFiles() const;
+
+
+ // returns the currently selected mod
+ //
+ ModInfo& mod() const;
+
+ // returns the currently selected mod, can never be empty
+ //
+ ModInfoPtr modPtr() const;
+
+ // returns the origin of the selected mod; this can be null for mods that
+ // don't have an origin, like deactivated mods
+ //
+ MOShared::FilesOrigin* origin() const;
+
+
+ // return this tab's ID
+ //
+ ModInfoTabIDs tabID() const;
+
+ // returns whether this tab has data; derived classes should call setHasData()
+ //
+ bool hasData() const;
+
+signals:
+ // emitted when a tab modified the files in a mod
+ //
+ void originModified(int originID);
+
+ // emitted when a tab wants to open a mod by name
+ //
+ void modOpen(QString name);
+
+ // emitted when a tab used to have data and is now empty, or vice versa
+ //
+ void hasDataChanged();
+
+ // emitted when a tab wants focus
+ //
+ void wantsFocus();
+
+protected:
+ Ui::ModInfoDialog* ui;
+
+ ModInfoDialogTab(ModInfoDialogTabContext cx);
+
+ OrganizerCore& core();
+ PluginContainer& plugin();
+ QWidget* parentWidget();
+
+ // emits originModified
+ //
+ void emitOriginModified();
+
+ // emits modOpen
+ //
+ void emitModOpen(QString name);
+
+ // emits hasDataChanged
+ //
+ void setHasData(bool b);
+
+ // emits wantsFocus
+ //
+ void setFocus();
+
+
+ // saves the sate of the given widget in geometry/modinfodialog_[objectname]
+ //
+ // this needs to be a template because saveState() and restoreState() are
+ // not in QWidget, but they're in various widgets
+ //
+ template <class Widget>
+ void saveWidgetState(QSettings& s, Widget* w)
+ {
+ s.setValue(settingName(w), w->saveState());
+ }
+
+ // restores the sate of the given widget from
+ // geometry/modinfodialog_[objectname]
+ //
+ // this needs to be a template because saveState() and restoreState() are
+ // not in QWidget, but they're in various widgets
+ //
+ template <class Widget>
+ void restoreWidgetState(const QSettings& s, Widget* w)
+ {
+ if (s.contains(settingName(w))) {
+ w->restoreState(s.value(settingName(w)).toByteArray());
+ }
+ }
+
+private:
+ // core
+ OrganizerCore& m_core;
+
+ // plugin
+ PluginContainer& m_plugin;
+
+ // parent widget, used to display modal dialogs
+ QWidget* m_parent;
+
+ // current mod, never null
+ ModInfoPtr m_mod;
+
+ // current mod origin, may be null
+ MOShared::FilesOrigin* m_origin;
+
+ // tab ID
+ ModInfoTabIDs m_tabID;
+
+ // whether the tab has data
+ bool m_hasData;
+
+ // true if the tab has never been selected for the current mod
+ bool m_firstActivation;
+
+
+ // used by saveWidgetState() and restoreWidgetState()
+ //
+ QString settingName(QWidget* w)
+ {
+ return "geometry/modinfodialog_" + w->objectName();
+ }
+};
+
+
+// the Notes tab
+//
+class NotesTab : public ModInfoDialogTab
+{
+public:
+ NotesTab(ModInfoDialogTabContext cx);
+
+ void clear() override;
+ void update() override;
+
+ // returns true, separators can have notes
+ //
+ bool canHandleSeparators() const override;
+
+ // returns false, notes don't use files
+ //
+ bool usesOriginFiles() const override;
+
+private:
+ void onComments();
+ void onNotes();
+ void checkHasData();
+};
+
+#endif // MODINFODIALOGTAB_H