diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mainwindow.cpp | 2 | ||||
| -rw-r--r-- | src/modinfodialog.cpp | 163 | ||||
| -rw-r--r-- | src/modinfodialog.h | 134 | ||||
| -rw-r--r-- | src/modinfodialog.ui | 6 | ||||
| -rw-r--r-- | src/modinfodialogtab.h | 61 |
5 files changed, 268 insertions, 98 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a596c542..7681b482 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3229,7 +3229,7 @@ void MainWindow::displayModInformation( //Open the tab first if we want to use the standard indexes of the tabs. if (tabID != ModInfoTabIDs::None) { - dialog.setTab(tabID); + dialog.selectTab(tabID); } dialog.restoreState(m_OrganizerCore.settings()); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c8ffa35b..adbd22df 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -170,8 +170,11 @@ ModInfoDialog::ModInfoDialog( [&, id=tabInfo.tab->tabID()]{ switchToTab(id); }); } - connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); + connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabSelectionChanged(); }); connect(ui->tabWidget->tabBar(), &QTabBar::tabMoved, [&]{ onTabMoved(); }); + connect(ui->close, &QPushButton::clicked, [&]{ onCloseButton(); }); + connect(ui->previousMod, &QPushButton::clicked, [&]{ onPreviousMod(); }); + connect(ui->nextMod, &QPushButton::clicked, [&]{ onNextMod(); }); } ModInfoDialog::~ModInfoDialog() = default; @@ -242,7 +245,7 @@ void ModInfoDialog::setMod(const QString& name) setMod(mod); } -void ModInfoDialog::setTab(ModInfoTabIDs id) +void ModInfoDialog::selectTab(ModInfoTabIDs id) { if (!isVisible()) { m_initialTab = id; @@ -323,6 +326,10 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) return; } + // something changed; save the current order (if necessary) because some tabs + // will be removed and others added + saveTabOrder(Settings::instance()); + // remember selection const int selIndex = ui->tabWidget->currentIndex(); auto sel = ModInfoTabIDs::None; @@ -337,6 +344,68 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) reAddTabs(visibility, sel); } +void ModInfoDialog::reAddTabs( + const std::vector<bool>& visibility, ModInfoTabIDs sel) +{ + Q_ASSERT(visibility.size() == m_tabs.size()); + + // ordered tab names from settings + const auto orderedNames = getOrderedTabNames(); + + bool canSort = true; + + // gathering visible tabs + std::vector<TabInfo*> visibleTabs; + for (std::size_t i=0; i<m_tabs.size(); ++i) { + if (visibility[i]) { + visibleTabs.push_back(&m_tabs[i]); + + if (canSort) { + const auto objectName = m_tabs[i].widget->objectName(); + auto itor = std::find(orderedNames.begin(), orderedNames.end(), objectName); + if (itor == orderedNames.end()) { + qCritical() << "can't sort tabs, '" << objectName << "' not found"; + canSort = false; + } + } + } + } + + // sorting tabs + if (canSort) { + std::sort(visibleTabs.begin(), visibleTabs.end(), [&](auto&& a, auto&& b){ + auto aItor = std::find(orderedNames.begin(), orderedNames.end(), a->widget->objectName()); + auto bItor = std::find(orderedNames.begin(), orderedNames.end(), b->widget->objectName()); + + // this was checked above + Q_ASSERT(aItor != orderedNames.end() && bItor != orderedNames.end()); + + return (aItor < bItor); + }); + } + + + // removing all tabs + ui->tabWidget->clear(); + + // reset real positions + for (auto& tabInfo : m_tabs) { + tabInfo.realPos = -1; + } + + // add visible tabs + for (std::size_t i=0; i<visibleTabs.size(); ++i) { + auto& tabInfo = *visibleTabs[i]; + + tabInfo.realPos = static_cast<int>(i); + ui->tabWidget->addTab(tabInfo.widget, tabInfo.icon, tabInfo.caption); + + if (tabInfo.tab->tabID() == sel) { + ui->tabWidget->setCurrentIndex(static_cast<int>(i)); + } + } +} + void ModInfoDialog::updateTabs(bool becauseOriginChanged) { auto* origin = getOrigin(); @@ -440,10 +509,7 @@ MOShared::FilesOrigin* ModInfoDialog::getOrigin() void ModInfoDialog::saveState(Settings& s) const { - const auto tabState = saveTabState(); - if (!tabState.isEmpty()) { - s.directInterface().setValue("mod_info_tab_order", tabState); - } + saveTabOrder(s); // remove 2.2.0 settings s.directInterface().remove("mod_info_tabs"); @@ -466,21 +532,24 @@ void ModInfoDialog::restoreState(const Settings& s) } } -QString ModInfoDialog::saveTabState() const +void ModInfoDialog::saveTabOrder(Settings& s) const { if (static_cast<int>(m_tabs.size()) != ui->tabWidget->count()) { // only save tab state when all tabs are visible - return {}; + return; } - QString result; - QTextStream stream(&result); + QString names; for (int i=0; i<ui->tabWidget->count(); ++i) { - stream << ui->tabWidget->widget(i)->objectName() << " "; + if (!names.isEmpty()) { + names += " "; + } + + names += ui->tabWidget->widget(i)->objectName(); } - return result.trimmed(); + s.directInterface().setValue("mod_info_tab_order", names); } std::vector<QString> ModInfoDialog::getOrderedTabNames() const @@ -516,68 +585,6 @@ std::vector<QString> ModInfoDialog::getOrderedTabNames() const return v; } -void ModInfoDialog::reAddTabs( - const std::vector<bool>& visibility, ModInfoTabIDs sel) -{ - Q_ASSERT(visibility.size() == m_tabs.size()); - - // ordered tab names from settings - const auto orderedNames = getOrderedTabNames(); - - bool canSort = true; - - // gathering visible tabs - std::vector<TabInfo*> visibleTabs; - for (std::size_t i=0; i<m_tabs.size(); ++i) { - if (visibility[i]) { - visibleTabs.push_back(&m_tabs[i]); - - if (canSort) { - const auto objectName = m_tabs[i].widget->objectName(); - auto itor = std::find(orderedNames.begin(), orderedNames.end(), objectName); - if (itor == orderedNames.end()) { - qCritical() << "can't sort tabs, '" << objectName << "' not found"; - canSort = false; - } - } - } - } - - // sorting tabs - if (canSort) { - std::sort(visibleTabs.begin(), visibleTabs.end(), [&](auto&& a, auto&& b){ - auto aItor = std::find(orderedNames.begin(), orderedNames.end(), a->widget->objectName()); - auto bItor = std::find(orderedNames.begin(), orderedNames.end(), b->widget->objectName()); - - // this was checked above - Q_ASSERT(aItor != orderedNames.end() && bItor != orderedNames.end()); - - return (aItor < bItor); - }); - } - - - // removing all tabs - ui->tabWidget->clear(); - - // reset real positions - for (auto& tabInfo : m_tabs) { - tabInfo.realPos = -1; - } - - // add visible tabs - for (std::size_t i=0; i<visibleTabs.size(); ++i) { - auto& tabInfo = *visibleTabs[i]; - - tabInfo.realPos = static_cast<int>(i); - ui->tabWidget->addTab(tabInfo.widget, tabInfo.icon, tabInfo.caption); - - if (tabInfo.tab->tabID() == sel) { - ui->tabWidget->setCurrentIndex(static_cast<int>(i)); - } - } -} - void ModInfoDialog::onOriginModified(int originID) { emit originModified(originID); @@ -600,7 +607,7 @@ void ModInfoDialog::closeEvent(QCloseEvent* e) } } -void ModInfoDialog::on_closeButton_clicked() +void ModInfoDialog::onCloseButton() { if (tryClose()) { close(); @@ -618,7 +625,7 @@ bool ModInfoDialog::tryClose() return true; } -void ModInfoDialog::onTabChanged() +void ModInfoDialog::onTabSelectionChanged() { if (m_arrangingTabs) { // this can be fired while re-arranging tabs, which happens before mods @@ -660,7 +667,7 @@ void ModInfoDialog::onTabMoved() } } -void ModInfoDialog::on_nextButton_clicked() +void ModInfoDialog::onNextMod() { auto mod = m_mainWindow->nextModInList(); if (!mod || mod == m_mod) { @@ -671,7 +678,7 @@ void ModInfoDialog::on_nextButton_clicked() update(); } -void ModInfoDialog::on_prevButton_clicked() +void ModInfoDialog::onPreviousMod() { auto mod = m_mainWindow->previousModInList(); if (!mod || mod == m_mod) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index effb5d98..035eb6d6 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -43,6 +43,9 @@ class ModInfoDialog : public MOBase::TutorableDialog {
Q_OBJECT;
+ // creates a tab, it's a friend because it uses a bunch of member variables
+ // to create ModInfoDialogTabContext
+ //
template <class T>
friend std::unique_ptr<ModInfoDialogTab> createTab(
ModInfoDialog& d, ModInfoTabIDs index);
@@ -54,37 +57,67 @@ public: ~ModInfoDialog();
- void setMod(ModInfo::Ptr mod);
- void setMod(const QString& name);
-
- void setTab(ModInfoTabIDs id);
+ // switches to the tab with the given id
+ //
+ void selectTab(ModInfoTabIDs id);
+ // updates all tabs, selects the initial tab and opens the dialog
+ //
int exec() override;
+ // saves the dialog state and calls saveState() on all tabs
+ //
void saveState(Settings& s) const;
+
+ // restores the dialog state and calls restoreState() on all tabs
+ //
void restoreState(const Settings& s);
signals:
+ // emitted when a tab changes the origin
+ //
void originModified(int originID);
protected:
+ // forwards to tryClose()
+ //
void closeEvent(QCloseEvent* e);
-private slots:
- void on_closeButton_clicked();
- void on_nextButton_clicked();
- void on_prevButton_clicked();
-
private:
+ // represents a single tab
+ //
struct TabInfo
{
+ // tab implementation
std::unique_ptr<ModInfoDialogTab> tab;
+
+ // actual position in the tab bar, updated every time a tab is moved
int realPos;
+
+ // widget used by the QTabWidget for this tab
+ //
+ // because QTabWidget doesn't support simply hiding tabs, they have to be
+ // completely removed from the widget when they don't support the current
+ // mod
+ //
+ // therefore, `widget, `caption` and `icon` are remembered so tabs can be
+ // removed and re-added when navigating between mods
+ //
+ // `widget` is also used figure out which tab is where when they're
+ // re-ordered
QWidget* widget;
+
+ // caption for this tab, see `widget`
QString caption;
+
+ // icon for this tab, see `widget`
QIcon icon;
+
TabInfo(std::unique_ptr<ModInfoDialogTab> tab);
+
+ // returns whether this tab is part of the tab widget
+ //
bool isVisible() const;
};
@@ -94,28 +127,99 @@ private: OrganizerCore* m_core;
PluginContainer* m_plugin;
std::vector<TabInfo> m_tabs;
+
+ // initial tab requested by the main window when the dialog is opened; whether
+ // the request can be honoured depends on what tabs are present
ModInfoTabIDs m_initialTab;
+
+ // set to true when tabs are being removed and re-added while navigating
+ // between mods; since the current index changes while this is happening,
+ // onTabSelectionChanged() will be called repeatedly
+ //
+ // however, it will check this flag and ignore the event so first activations
+ // are not fired incorrectly
bool m_arrangingTabs;
+
+ // creates all the tabs
+ //
std::vector<TabInfo> createTabs();
+
+
+ // sets the currently selected mod; resets first activation, but doesn't
+ // update anything
+ //
+ void setMod(ModInfo::Ptr mod);
+
+ // sets the currently selected mod, if found; forwards to setMod() above
+ //
+ void setMod(const QString& name);
+
+
+ // returns the currently selected tab, taking re-ordering in to account;
+ // shouldn't be null, but could be
+ //
TabInfo* currentTab();
- void restoreTabState(const QString& state);
- QString saveTabState() const;
+
+ // returns a list of tab object names in their visual order, used by
+ // saveState()
+ //
+ void saveTabOrder(Settings& s) const;
+
+
+ // fully updates the dialog; sets the title, the tab visibility and updates
+ // all the tabs; used when the current mod changes
+ //
+ // see setTabsVisibility() for firstTime
+ //
void update(bool firstTime=false);
+
+ // builds the list of visible tabs; if the list is different from what's
+ // currently displayed, or firstTime is true, forwards to reAddTabs()
+ void setTabsVisibility(bool firstTime);
+
+ // clears the tab widgets and re-adds the tabs having the visible flag in
+ // the given vector, following the
+ void reAddTabs(const std::vector<bool>& visibility, ModInfoTabIDs sel);
+
void onDeleteShortcut();
MOShared::FilesOrigin* getOrigin();
- void setTabsVisibility(bool firstTime);
void updateTabs(bool becauseOriginChanged=false);
void feedFiles(bool becauseOriginChanged);
void setTabsColors();
void switchToTab(ModInfoTabIDs id);
- void reAddTabs(const std::vector<bool>& visibility, ModInfoTabIDs sel);
std::vector<QString> getOrderedTabNames() const;
bool tryClose();
- void onOriginModified(int originID);
- void onTabChanged();
+
+ // called when the user clicks the close button; closing the dialog by other
+ // means ends up in closeEvent(); forwards to tryClose()
+ //
+ void onCloseButton();
+
+ // called when the user clicks the previous button; asks the main window for
+ // the previous mod in the list and loads it
+ //
+ void onPreviousMod();
+
+ // called when the user clicks the next button; asks the main window for the
+ // next mod in the list and loads it
+ //
+ void onNextMod();
+
+ // called when the selects a tab; handles first activation
+ //
+ void onTabSelectionChanged();
+
+ // called when the user re-orders tabs; sets the correct TabInfo::realPos for
+ // all tabs
+ //
void onTabMoved();
+
+ // called when a tab has modified the origin; emits originModified() and
+ // updates all the tabs that use origin files
+ //
+ void onOriginModified(int originID);
};
#endif // MODINFODIALOG_H
diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index e54b94b3..5b559992 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -1260,7 +1260,7 @@ p, li { white-space: pre-wrap; } <item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
- <widget class="QPushButton" name="prevButton">
+ <widget class="QPushButton" name="previousMod">
<property name="text">
<string>Previous</string>
</property>
@@ -1270,7 +1270,7 @@ p, li { white-space: pre-wrap; } </widget>
</item>
<item>
- <widget class="QPushButton" name="nextButton">
+ <widget class="QPushButton" name="nextMod">
<property name="text">
<string>Next</string>
</property>
@@ -1293,7 +1293,7 @@ p, li { white-space: pre-wrap; } </spacer>
</item>
<item>
- <widget class="QPushButton" name="closeButton">
+ <widget class="QPushButton" name="close">
<property name="text">
<string>Close</string>
</property>
diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 283d9e73..c43fa076 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -203,13 +203,29 @@ public: 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: @@ -219,15 +235,27 @@ protected: 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 // @@ -237,6 +265,12 @@ protected: 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) { @@ -246,16 +280,33 @@ protected: } 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(); @@ -263,6 +314,8 @@ private: }; +// the Notes tab +// class NotesTab : public ModInfoDialogTab { public: @@ -270,7 +323,13 @@ public: 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: |
