Commit 773343f3 authored by con's avatar con

Delay widget creation of options pages till their category is shown

Also add the alternative way: IOptionsPageProvider, which states
a category, and is asked for a list of options pages for that
category when it is shown.

Task-number: QTCREATORBUG-3131
parent 5c993166
......@@ -315,6 +315,8 @@ void CMakeSettingsPage::saveSettings() const
void CMakeSettingsPage::apply()
{
if (!m_pathchooser) // page was never shown
return;
if (m_cmakeExecutable == m_pathchooser->path())
return;
m_cmakeExecutable = m_pathchooser->path();
......
......@@ -54,7 +54,7 @@ using namespace Core;
using namespace Core::Internal;
CommandMappings::CommandMappings(QObject *parent)
: IOptionsPage(parent)
: IOptionsPage(parent), m_page(0)
{
}
......@@ -140,7 +140,10 @@ void CommandMappings::setTargetHeader(const QString &s)
void CommandMappings::finish()
{
if (!m_page) // page was never shown
return;
delete m_page;
m_page = 0;
}
void CommandMappings::commandChanged(QTreeWidgetItem *current)
......
......@@ -34,14 +34,19 @@
\mainclass
\brief The IOptionsPage is an interface for providing options pages.
You need to subclass this interface and put an instance of your subclass
into the plugin manager object pool (e.g. ExtensionSystem::PluginManager::addObject).
Guidelines for implementing:
\list
\o id() is an id used for filtering when calling ICore:: showOptionsDialog()
\o displayName() is the (translated) name for display.
\o category() is the category used for filtering when calling ICore:: showOptionsDialog()
\o displayCategory() is the translated category
\o id() is a unique identifier for referencing this page
\o displayName() is the (translated) name for display
\o category() is the unique id for the category that the page should be displayed in
\o displayCategory() is the translated name of the category
\o createPage() is called to retrieve the widget to show in the preferences dialog
The widget will be destroyed by the widget hierarchy when the dialog closes
\o apply() is called to store the settings. It should detect if any changes have been
made and store those.
\o matches() is used for the options dialog search filter.
made and store those
\o finish() is called directly before the preferences dialog closes
\o matches() is used for the options dialog search filter
\endlist
*/
......@@ -60,6 +60,29 @@ public:
virtual void finish() = 0;
};
/*
Alternative way for providing option pages instead of adding IOptionsPage
objects into the plugin manager pool. Should only be used if creation of the
actual option pages is not possible or too expensive at Qt Creator startup.
(Like the designer integration, which needs to initialize designer plugins
before the options pages get available.)
*/
class CORE_EXPORT IOptionsPageProvider : public QObject
{
Q_OBJECT
public:
IOptionsPageProvider(QObject *parent = 0) : QObject(parent) {}
virtual ~IOptionsPageProvider() {}
virtual QString category() const = 0;
virtual QString displayCategory() const = 0;
virtual QIcon categoryIcon() const = 0;
virtual QList<IOptionsPage *> pages() const = 0;
};
} // namespace Core
#endif // IOPTIONSPAGE_H
......@@ -72,7 +72,8 @@ public:
QString id;
QString displayName;
QIcon icon;
QList<IOptionsPage*> pages;
QList<IOptionsPage *> pages;
QList<IOptionsPageProvider *> providers;
int index;
QTabWidget *tabWidget;
};
......@@ -86,7 +87,8 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
void setPages(const QList<IOptionsPage*> &pages);
void setPages(const QList<IOptionsPage*> &pages,
const QList<IOptionsPageProvider *> &providers);
const QList<Category*> &categories() const { return m_categories; }
private:
......@@ -130,7 +132,8 @@ QVariant CategoryModel::data(const QModelIndex &index, int role) const
return QVariant();
}
void CategoryModel::setPages(const QList<IOptionsPage*> &pages)
void CategoryModel::setPages(const QList<IOptionsPage*> &pages,
const QList<IOptionsPageProvider *> &providers)
{
// Clear any previous categories
qDeleteAll(m_categories);
......@@ -143,13 +146,32 @@ void CategoryModel::setPages(const QList<IOptionsPage*> &pages)
if (!category) {
category = new Category;
category->id = categoryId;
category->tabWidget = 0;
category->index = -1;
m_categories.append(category);
}
if (category->displayName.isEmpty())
category->displayName = page->displayCategory();
if (category->icon.isNull())
category->icon = page->categoryIcon();
category->pages.append(page);
category->pages.append(page);
}
foreach (IOptionsPageProvider *provider, providers) {
const QString &categoryId = provider->category();
Category *category = findCategoryById(categoryId);
if (!category) {
category = new Category;
category->id = categoryId;
category->tabWidget = 0;
category->index = -1;
m_categories.append(category);
} else {
category->pages.append(page);
}
if (category->displayName.isEmpty())
category->displayName = provider->displayCategory();
if (category->icon.isNull())
category->icon = provider->categoryIcon();
category->providers.append(provider);
}
reset();
......@@ -277,26 +299,8 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
setWindowTitle(tr("Options"));
#endif
m_model->setPages(m_pages);
// Create the tab widgets with the pages in each category
const QList<Category*> &categories = m_model->categories();
for (int i = 0; i < categories.size(); ++i) {
Category *category = categories.at(i);
QTabWidget *tabWidget = new QTabWidget;
for (int j = 0; j < category->pages.size(); ++j) {
IOptionsPage *page = category->pages.at(j);
QWidget *widget = page->createPage(0);
tabWidget->addTab(widget, page->displayName());
}
connect(tabWidget, SIGNAL(currentChanged(int)),
this, SLOT(currentTabChanged(int)));
category->tabWidget = tabWidget;
category->index = m_stackedLayout->addWidget(tabWidget);
}
m_model->setPages(m_pages,
ExtensionSystem::PluginManager::instance()->getObjects<IOptionsPageProvider>());
m_proxyModel->setSourceModel(m_model);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
......@@ -383,6 +387,7 @@ void SettingsDialog::createGui()
mainGridLayout->addWidget(buttonBox, 2, 0, 1, 2);
mainGridLayout->setColumnStretch(1, 4);
setLayout(mainGridLayout);
setMinimumSize(1070, 680);
}
SettingsDialog::~SettingsDialog()
......@@ -392,7 +397,7 @@ SettingsDialog::~SettingsDialog()
void SettingsDialog::showCategory(int index)
{
Category *category = m_model->categories().at(index);
ensureCategoryWidget(category);
// Update current category and page
m_currentCategory = category->id;
const int currentTabIndex = category->tabWidget->currentIndex();
......@@ -408,6 +413,29 @@ void SettingsDialog::showCategory(int index)
updateEnabledTabs(category, m_filterLineEdit->text());
}
void SettingsDialog::ensureCategoryWidget(Category *category)
{
if (category->tabWidget != 0)
return;
foreach (const IOptionsPageProvider *provider, category->providers) {
category->pages += provider->pages();
}
qStableSort(category->pages.begin(), category->pages.end(), optionsPageLessThan);
QTabWidget *tabWidget = new QTabWidget;
for (int j = 0; j < category->pages.size(); ++j) {
IOptionsPage *page = category->pages.at(j);
QWidget *widget = page->createPage(0);
tabWidget->addTab(widget, page->displayName());
}
connect(tabWidget, SIGNAL(currentChanged(int)),
this, SLOT(currentTabChanged(int)));
category->tabWidget = tabWidget;
category->index = m_stackedLayout->addWidget(tabWidget);
}
void SettingsDialog::updateEnabledTabs(Category *category, const QString &searchText)
{
for (int i = 0; i < category->pages.size(); ++i) {
......
......@@ -93,6 +93,7 @@ private:
void showCategory(int index);
void showPage(const QString &categoryId, const QString &pageId);
void updateEnabledTabs(Category *category, const QString &searchText);
void ensureCategoryWidget(Category *category);
const QList<Core::IOptionsPage*> m_pages;
......
......@@ -53,7 +53,7 @@ using namespace Core::Internal;
GeneralSettings::GeneralSettings():
m_dialog(0)
m_page(0), m_dialog(0)
{
}
......@@ -188,6 +188,8 @@ bool GeneralSettings::matches(const QString &s) const
void GeneralSettings::apply()
{
if (!m_page) // wasn't shown, can't be changed
return;
int currentIndex = m_page->languageBox->currentIndex();
setLanguage(m_page->languageBox->itemData(currentIndex, Qt::UserRole).toString());
// Apply the new base color if accepted
......@@ -205,7 +207,10 @@ void GeneralSettings::apply()
void GeneralSettings::finish()
{
if (!m_page) // page was never shown
return;
delete m_page;
m_page = 0;
}
void GeneralSettings::resetInterfaceColor()
......
......@@ -96,7 +96,7 @@ FileShareProtocolSettings FileShareProtocolSettingsWidget::settings() const
// ----------FileShareProtocolSettingsPage
FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(const QSharedPointer<FileShareProtocolSettings> &s,
QObject *parent) :
Core::IOptionsPage(parent), m_settings(s)
Core::IOptionsPage(parent), m_settings(s), m_widget(0)
{
}
......@@ -134,6 +134,8 @@ QWidget *FileShareProtocolSettingsPage::createPage(QWidget *parent)
void FileShareProtocolSettingsPage::apply()
{
if (!m_widget) // page was never shown
return;
const FileShareProtocolSettings newSettings = m_widget->settings();
if (newSettings != *m_settings) {
*m_settings = newSettings;
......
......@@ -76,7 +76,7 @@ Settings SettingsWidget::settings()
}
SettingsPage::SettingsPage(const QSharedPointer<Settings> &settings) :
m_settings(settings)
m_settings(settings), m_widget(0)
{
}
......@@ -121,7 +121,8 @@ QWidget *SettingsPage::createPage(QWidget *parent)
void SettingsPage::apply()
{
QTC_ASSERT(m_widget, return)
if (!m_widget) // page was never shown
return;
const Settings newSettings = m_widget->settings();
if (newSettings != *m_settings) {
*m_settings = newSettings;
......
......@@ -40,7 +40,7 @@
using namespace CppTools::Internal;
CompletionSettingsPage::CompletionSettingsPage()
: m_page(new Ui_CompletionSettingsPage)
: m_page(0)
{
}
......@@ -62,6 +62,7 @@ QString CompletionSettingsPage::displayName() const
QWidget *CompletionSettingsPage::createPage(QWidget *parent)
{
QWidget *w = new QWidget(parent);
m_page = new Ui_CompletionSettingsPage;
m_page->setupUi(w);
const TextEditor::CompletionSettings &settings =
......@@ -113,6 +114,8 @@ QWidget *CompletionSettingsPage::createPage(QWidget *parent)
void CompletionSettingsPage::apply()
{
if (!m_page) // page was never shown
return;
TextEditor::CompletionSettings settings;
settings.m_caseSensitivity = caseSensitivity();
settings.m_completionTrigger = completionTrigger();
......@@ -151,3 +154,11 @@ TextEditor::CompletionTrigger CompletionSettingsPage::completionTrigger() const
return TextEditor::AutomaticCompletion;
}
}
void CompletionSettingsPage::finish()
{
if (!m_page) // page was never shown
return;
delete m_page;
m_page = 0;
}
......@@ -55,7 +55,7 @@ public:
QWidget *createPage(QWidget *parent);
void apply();
void finish() { }
void finish();
virtual bool matches(const QString &) const;
private:
......
......@@ -139,6 +139,7 @@ void GdbOptionsPage::writeGdbBinarySettings() /* static */
}
GdbOptionsPage::GdbOptionsPage()
: m_ui(0)
{
}
......@@ -170,67 +171,68 @@ QIcon GdbOptionsPage::categoryIcon() const
QWidget *GdbOptionsPage::createPage(QWidget *parent)
{
QWidget *w = new QWidget(parent);
m_ui.setupUi(w);
m_ui.gdbChooserWidget->setGdbBinaries(gdbBinaryToolChainMap);
m_ui.scriptFileChooser->setExpectedKind(Utils::PathChooser::File);
m_ui.scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File"));
m_ui = new Ui::GdbOptionsPage;
m_ui->setupUi(w);
m_ui->gdbChooserWidget->setGdbBinaries(gdbBinaryToolChainMap);
m_ui->scriptFileChooser->setExpectedKind(Utils::PathChooser::File);
m_ui->scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File"));
m_group.clear();
m_group.insert(debuggerCore()->action(GdbScriptFile),
m_ui.scriptFileChooser);
m_ui->scriptFileChooser);
m_group.insert(debuggerCore()->action(GdbEnvironment),
m_ui.environmentEdit);
m_ui->environmentEdit);
m_group.insert(debuggerCore()->action(AdjustBreakpointLocations),
m_ui.checkBoxAdjustBreakpointLocations);
m_ui->checkBoxAdjustBreakpointLocations);
m_group.insert(debuggerCore()->action(GdbWatchdogTimeout),
m_ui.spinBoxGdbWatchdogTimeout);
m_ui->spinBoxGdbWatchdogTimeout);
m_group.insert(debuggerCore()->action(UseMessageBoxForSignals),
m_ui.checkBoxUseMessageBoxForSignals);
m_ui->checkBoxUseMessageBoxForSignals);
m_group.insert(debuggerCore()->action(SkipKnownFrames),
m_ui.checkBoxSkipKnownFrames);
m_ui->checkBoxSkipKnownFrames);
m_group.insert(debuggerCore()->action(EnableReverseDebugging),
m_ui.checkBoxEnableReverseDebugging);
m_ui->checkBoxEnableReverseDebugging);
m_group.insert(debuggerCore()->action(GdbWatchdogTimeout), 0);
#if 1
m_ui.groupBoxPluginDebugging->hide();
m_ui->groupBoxPluginDebugging->hide();
#else // The related code (handleAqcuiredInferior()) is disabled as well.
m_group.insert(debuggerCore()->action(AllPluginBreakpoints),
m_ui.radioButtonAllPluginBreakpoints);
m_ui->radioButtonAllPluginBreakpoints);
m_group.insert(debuggerCore()->action(SelectedPluginBreakpoints),
m_ui.radioButtonSelectedPluginBreakpoints);
m_ui->radioButtonSelectedPluginBreakpoints);
m_group.insert(debuggerCore()->action(NoPluginBreakpoints),
m_ui.radioButtonNoPluginBreakpoints);
m_ui->radioButtonNoPluginBreakpoints);
m_group.insert(debuggerCore()->action(SelectedPluginBreakpointsPattern),
m_ui.lineEditSelectedPluginBreakpointsPattern);
m_ui->lineEditSelectedPluginBreakpointsPattern);
#endif
m_ui.lineEditSelectedPluginBreakpointsPattern->
m_ui->lineEditSelectedPluginBreakpointsPattern->
setEnabled(debuggerCore()->action(SelectedPluginBreakpoints)->value().toBool());
connect(m_ui.radioButtonSelectedPluginBreakpoints, SIGNAL(toggled(bool)),
m_ui.lineEditSelectedPluginBreakpointsPattern, SLOT(setEnabled(bool)));
connect(m_ui->radioButtonSelectedPluginBreakpoints, SIGNAL(toggled(bool)),
m_ui->lineEditSelectedPluginBreakpointsPattern, SLOT(setEnabled(bool)));
// FIXME
m_ui.environmentEdit->hide();
m_ui.labelEnvironment->hide();
m_ui->environmentEdit->hide();
m_ui->labelEnvironment->hide();
if (m_searchKeywords.isEmpty()) {
QLatin1Char sep(' ');
QTextStream(&m_searchKeywords)
<< sep << m_ui.groupBoxLocations->title()
<< sep << m_ui.labelEnvironment->text()
<< sep << m_ui.labelGdbStartupScript->text()
<< sep << m_ui.labelGdbWatchdogTimeout->text()
<< sep << m_ui.checkBoxEnableReverseDebugging->text()
<< sep << m_ui.checkBoxSkipKnownFrames->text()
<< sep << m_ui.checkBoxUseMessageBoxForSignals->text()
<< sep << m_ui.checkBoxAdjustBreakpointLocations->text()
<< sep << m_ui.groupBoxPluginDebugging->title()
<< sep << m_ui.radioButtonAllPluginBreakpoints->text()
<< sep << m_ui.radioButtonSelectedPluginBreakpoints->text()
<< sep << m_ui.labelSelectedPluginBreakpoints->text()
<< sep << m_ui.radioButtonNoPluginBreakpoints->text()
<< sep << m_ui->groupBoxLocations->title()
<< sep << m_ui->labelEnvironment->text()
<< sep << m_ui->labelGdbStartupScript->text()
<< sep << m_ui->labelGdbWatchdogTimeout->text()
<< sep << m_ui->checkBoxEnableReverseDebugging->text()
<< sep << m_ui->checkBoxSkipKnownFrames->text()
<< sep << m_ui->checkBoxUseMessageBoxForSignals->text()
<< sep << m_ui->checkBoxAdjustBreakpointLocations->text()
<< sep << m_ui->groupBoxPluginDebugging->title()
<< sep << m_ui->radioButtonAllPluginBreakpoints->text()
<< sep << m_ui->radioButtonSelectedPluginBreakpoints->text()
<< sep << m_ui->labelSelectedPluginBreakpoints->text()
<< sep << m_ui->radioButtonNoPluginBreakpoints->text()
;
m_searchKeywords.remove(QLatin1Char('&'));
}
......@@ -239,17 +241,22 @@ QWidget *GdbOptionsPage::createPage(QWidget *parent)
void GdbOptionsPage::apply()
{
if (!m_ui) // page never shown
return;
m_group.apply(Core::ICore::instance()->settings());
if (m_ui.gdbChooserWidget->isDirty()) {
if (m_ui->gdbChooserWidget->isDirty()) {
gdbBinariesChanged = true;
gdbBinaryToolChainMap = m_ui.gdbChooserWidget->gdbBinaries();
m_ui.gdbChooserWidget->clearDirty();
gdbBinaryToolChainMap = m_ui->gdbChooserWidget->gdbBinaries();
m_ui->gdbChooserWidget->clearDirty();
}
}
void GdbOptionsPage::finish()
{
if (!m_ui) // page never shown
return;
delete m_ui;
m_ui = 0;
m_group.finish();
}
......
......@@ -64,7 +64,7 @@ public:
static void writeGdbBinarySettings();
private:
Ui::GdbOptionsPage m_ui;
Ui::GdbOptionsPage *m_ui;
Utils::SavedActionSet m_group;
QString m_searchKeywords;
};
......
......@@ -37,6 +37,7 @@
# include "cppsettingspage.h"
#endif
#include "settingspage.h"
#include "designerconstants.h"
#include <coreplugin/icore.h>
......@@ -84,6 +85,7 @@ bool FormEditorPlugin::initialize(const QStringList &arguments, QString *error)
initializeTemplates();
addAutoReleasedObject(new FormEditorFactory);
addAutoReleasedObject(new SettingsPageProvider);
// Ensure that loading designer translations is done before FormEditorW is instantiated
const QString locale = Core::ICore::instance()->userInterfaceLanguage();
......@@ -97,18 +99,9 @@ bool FormEditorPlugin::initialize(const QStringList &arguments, QString *error)
qApp->installTranslator(qtr);
}
error->clear();
// Delayed loading: Make sure settings pages are there if options
// dialog is requested.
connect(Core::ICore::instance(), SIGNAL(optionsDialogRequested()),
this, SLOT(ensurePluginInitialized()));
return true;
}
void FormEditorPlugin::ensurePluginInitialized()
{
FormEditorW::instance()->ensureInitStage(FormEditorW::RegisterPlugins);
}
void FormEditorPlugin::extensionsInitialized()
{
// 4) test and make sure everything works (undo, saving, editors, opening/closing multiple files, dirtiness etc)
......
......@@ -57,9 +57,6 @@ public:
bool initialize(const QStringList &arguments, QString *error_message = 0);
void extensionsInitialized();
public slots:
void ensurePluginInitialized();
private:
void initializeTemplates();
......
......@@ -186,7 +186,6 @@ FormEditorW::FormEditorW() :
foreach (QDesignerOptionsPageInterface *designerPage, m_formeditor->optionsPages()) {
SettingsPage *settingsPage = new SettingsPage(designerPage);
ExtensionSystem::PluginManager::instance()->addObject(settingsPage);
m_settingsPages.append(settingsPage);
}
......@@ -213,10 +212,8 @@ FormEditorW::~FormEditorW()
}
delete m_formeditor;
foreach (SettingsPage *settingsPage, m_settingsPages) {
ExtensionSystem::PluginManager::instance()->removeObject(settingsPage);
delete settingsPage;
}
qDeleteAll(m_settingsPages);
m_settingsPages.clear();
delete m_integration;
m_self = 0;
......@@ -389,6 +386,11 @@ void FormEditorW::initDesignerSubWindows()
m_designerSubWindows[ActionEditorSubWindow] = ae;
}
QList<Core::IOptionsPage *> FormEditorW::optionsPages() const
{
return m_settingsPages;
}
void FormEditorW::ensureInitStage(InitializationStage s)
{
if (Designer::Constants::Internal::debug)
......
......@@ -32,7 +32,8 @@
#include "designerconstants.h"
#include "coreplugin/icontext.h"
#include <coreplugin/icontext.h>
#include <coreplugin/dialogs/ioptionspage.h>
#include <QtCore/QMap>
#include <QtCore/QObject>
......@@ -85,12 +86,10 @@ class DesignerContext;
* The plugin uses this stage at first by calling ensureInitStage().
* Requesting an editor via instance() will fully initialize the class.
* This is based on the assumption that the Designer settings work with
* no plugins loaded. If that does not work, full initialization can be
* triggered by connection to the ICore::optionsDialogRequested() signal.
* no plugins loaded.
*
* The form editor shows a read-only XML editor in edit mode and Qt Designer
* in Design mode. It connects to void EditorManager::currentEditorChanged()
* and switches modes if a designer XML editor is activated. */
* in Design mode. */
class FormEditorW : public QObject
{
Q_OBJECT
......@@ -117,6 +116,7 @@ public:
inline QWidget * const*designerSubWindows() const { return m_designerSubWindows; }
EditorData activeEditor() const;
QList<Core::IOptionsPage *> optionsPages() const;
private slots:
void activateEditMode(int id);
......@@ -178,7 +178,7 @@ private:
QAction *m_lockAction;
QAction *m_resetLayoutAction;
QList<SettingsPage *> m_settingsPages;
QList<Core::IOptionsPage *> m_settingsPages;
QActionGroup *m_actionGroupEditMode;
QAction *m_actionPrint;
QAction *m_actionPreview;
......
......@@ -29,6 +29,7 @@
#include "settingspage.h"
#include "designerconstants.h"
#include "formeditorw.h"