diff --git a/src/Gui/DlgPreferences.ui b/src/Gui/DlgPreferences.ui index aa54ee4c1f..092907905d 100644 --- a/src/Gui/DlgPreferences.ui +++ b/src/Gui/DlgPreferences.ui @@ -6,8 +6,8 @@ 0 0 - 570 - 454 + 800 + 600 @@ -20,80 +20,233 @@ true - - 9 + + 0 + + + 0 + + + 0 + + + 0 6 - - + + - 6 - - 0 - + - + 0 0 - 120 + 180 0 - 128 + 240 16777215 + + false + + + QFrame { background-color: rgba(0, 0, 0, 25); } +QFrame::item { padding: 6px 8px }; + - QFrame::StyledPanel + QFrame::NoFrame - QFrame::Sunken + QFrame::Raised - - Qt::ScrollBarAlwaysOff - - - - 96 - 96 - - - - 12 - - - QListView::IconMode + + 0 + + + 0 + + + 0 + + + 12 + + + 0 + + + 0 + + + + + Qt::NoContextMenu + + + false + + + background-color: transparent; + + + QFrame::NoFrame + + + 0 + + + false + + + + 24 + 24 + + + + false + + + + + + + 8 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + + 1 + 0 + + + + Reset + + + + + + - + + + + 12 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + QLayout::SetDefaultConstraint + + + + + + 18 + + + + Header + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 1 + 1 + + + + + + + + + 0 + 0 + + + + false + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + false + + + + + - - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Reset - - - - listBox + groupsTreeView diff --git a/src/Gui/DlgPreferencesImp.cpp b/src/Gui/DlgPreferencesImp.cpp index 87c48f5879..baa741cbb2 100644 --- a/src/Gui/DlgPreferencesImp.cpp +++ b/src/Gui/DlgPreferencesImp.cpp @@ -56,7 +56,24 @@ using namespace Gui::Dialog; -const int DlgPreferencesImp::GroupNameRole = Qt::UserRole; +QWidget* PreferencesPageItem::getWidget() const { + return _widget; +} + +void PreferencesPageItem::setWidget(QWidget* widget) +{ + if (_widget) { + _widget->setProperty(PropertyName, QVariant::fromValue(nullptr)); + } + + _widget = widget; + _widget->setProperty(PropertyName, QVariant::fromValue(this)); +} + +Q_DECLARE_METATYPE(PreferencesPageItem*); + +const int DlgPreferencesImp::GroupNameRole = Qt::UserRole + 1; +const int DlgPreferencesImp::PageNameRole = Qt::UserRole + 2; /* TRANSLATOR Gui::Dialog::DlgPreferencesImp */ @@ -78,16 +95,28 @@ DlgPreferencesImp::DlgPreferencesImp(QWidget* parent, Qt::WindowFlags fl) { ui->setupUi(this); - QFontMetrics fm(font()); - int length = QtTools::horizontalAdvance(fm, longestGroupName()); - ui->listBox->setFixedWidth(Base::clamp(length + 20, 108, 120)); - ui->listBox->setGridSize(QSize(Base::clamp(length + 20, 108, 120), 75)); - // remove unused help button setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - setupConnections(); + connect(ui->buttonBox, + &QDialogButtonBox::clicked, + this, + &DlgPreferencesImp::onButtonBoxClicked); + connect(ui->buttonBox, + &QDialogButtonBox::helpRequested, + getMainWindow(), + &MainWindow::whatsThis); + connect(ui->groupsTreeView, + &QTreeView::clicked, + this, + &DlgPreferencesImp::onPageSelected); + connect(ui->buttonReset, + &QPushButton::clicked, + this, + &DlgPreferencesImp::showResetOptions); + ui->groupsTreeView->setModel(&_model); + setupPages(); // Maintain a static pointer to the current active dialog (if there is one) so that @@ -106,110 +135,127 @@ DlgPreferencesImp::~DlgPreferencesImp() } } -void DlgPreferencesImp::setupConnections() -{ - connect(ui->buttonBox, &QDialogButtonBox::clicked, - this, &DlgPreferencesImp::onButtonBoxClicked); - connect(ui->buttonBox, &QDialogButtonBox::helpRequested, - getMainWindow(), &MainWindow::whatsThis); - connect(ui->listBox, &QListWidget::currentItemChanged, - this, &DlgPreferencesImp::changeGroup); - if (auto reset = ui->buttonBox->button(QDialogButtonBox::Reset)) { - QString text = reset->text(); - text.append(QLatin1String("...")); - reset->setText(text); - } -} - void DlgPreferencesImp::setupPages() { // make sure that pages are ready to create GetWidgetFactorySupplier(); - for (const auto &group : _pages) { - QTabWidget* groupTab = createTabForGroup(group.first); - for (const auto &page : group.second) { - createPageInGroup(groupTab, page); + + for (const auto &[name, pages] : _pages) { + auto* group = createGroup(name); + + for (const auto &page : pages) { + createPageInGroup(group, page); } } - // show the first group - ui->listBox->setCurrentRow(0); + updatePageDependentLabels(); } -QString DlgPreferencesImp::longestGroupName() const +QPixmap DlgPreferencesImp::loadIconForGroup(const std::string &name) const { - std::string name; - for (const auto &group : _pages) { - if (group.first.size() > name.size()) - name = group.first; + std::string fileName = name; + + // normalize file name + for (auto& ch : fileName) { + ch = ch == ' ' ? '_' : tolower(ch); } - return QString::fromStdString(name); -} - -/** - * Create the necessary widgets for a new group named \a groupName. Returns a - * pointer to the group's QTabWidget: that widget's lifetime is managed by the - * tabWidgetStack, do not manually deallocate. - */ -QTabWidget* DlgPreferencesImp::createTabForGroup(const std::string &groupName) -{ - QString groupNameQString = QString::fromStdString(groupName); - - std::string fileName = groupName; - QString tooltip; - getGroupData(groupName, fileName, tooltip); - - auto tabWidget = new QTabWidget; - ui->tabWidgetStack->addWidget(tabWidget); - tabWidget->setProperty("GroupName", QVariant(groupNameQString)); - - auto item = new QListWidgetItem(ui->listBox); - item->setData(GroupNameRole, QVariant(groupNameQString)); - item->setText(QObject::tr(groupNameQString.toLatin1())); - item->setToolTip(tooltip); - - for (auto &ch : fileName) { - if (ch == ' ') - ch = '_'; - else - ch = tolower(ch); - } fileName = std::string("preferences-") + fileName; - QPixmap icon = Gui::BitmapFactory().pixmapFromSvg(fileName.c_str(), QSize(48, 48)); + QPixmap icon = Gui::BitmapFactory().pixmapFromSvg(fileName.c_str(), QSize(24, 24)); + if (icon.isNull()) { icon = Gui::BitmapFactory().pixmap(fileName.c_str()); if (icon.isNull()) { qWarning() << "No group icon found for " << fileName.c_str(); } - else if (icon.size() != QSize(48, 48)) { - icon = icon.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); - qWarning() << "Group icon for " << fileName.c_str() << " is not of size 48x48, so it was scaled"; + else if (icon.size() != QSize(24, 24)) { + icon = icon.scaled(24, 24, Qt::KeepAspectRatio, Qt::SmoothTransformation); } } - item->setIcon(icon); - item->setTextAlignment(Qt::AlignHCenter); - item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - return tabWidget; + return icon; } /** - * Create a new preference page called \a pageName on the group tab \a tabWidget. + * Create the necessary widgets for a new group named \a groupName. Returns a + * pointer to the group's SettingsPageItem: that widget's lifetime is managed by the + * QStandardItemModel, do not manually deallocate. */ -void DlgPreferencesImp::createPageInGroup(QTabWidget *tabWidget, const std::string &pageName) +PreferencesPageItem* DlgPreferencesImp::createGroup(const std::string &groupName) +{ + QString groupNameQString = QString::fromStdString(groupName); + + std::string iconName; + + QString tooltip; + getGroupData(groupName, iconName, tooltip); + + auto groupPages = new QStackedWidget; + groupPages->setProperty(GroupNameProperty, QVariant(groupNameQString)); + + ui->groupWidgetStack->addWidget(groupPages); + + auto item = new PreferencesPageItem; + + item->setData(QVariant(groupNameQString), GroupNameRole); + item->setText(QObject::tr(groupNameQString.toLatin1())); + item->setToolTip(tooltip); + item->setIcon(loadIconForGroup(iconName)); + item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item->setWidget(groupPages); + + _model.invisibleRootItem()->appendRow(item); + + return item; +} + + +PreferencePage* DlgPreferencesImp::createPreferencePage(const std::string& pageName, const std::string& groupName) +{ + PreferencePage* page = WidgetFactory().createPreferencePage(pageName.c_str()); + + if (!page) { + return nullptr; + } + + // settings layout already takes care for margins, we need to reset everything to 0 + page->setContentsMargins(0, 0, 0, 0); + page->layout()->setContentsMargins(0, 0, 0, 0); + page->setProperty(GroupNameProperty, QString::fromStdString(groupName)); + page->setProperty(PageNameProperty, QString::fromStdString(pageName)); + + return page; +} + +/** + * Create a new preference page called \a pageName in the group \a groupItem. + */ +void DlgPreferencesImp::createPageInGroup(PreferencesPageItem *groupItem, const std::string &pageName) { try { - PreferencePage* page = WidgetFactory().createPreferencePage(pageName.c_str()); - if (page) { - tabWidget->addTab(page, page->windowTitle()); - page->loadSettings(); - page->setProperty("GroupName", tabWidget->property("GroupName")); - page->setProperty("PageName", QVariant(QString::fromStdString(pageName))); - } - else { + PreferencePage* page = createPreferencePage(pageName, groupItem->data(GroupNameRole).toString().toStdString()); + + if (!page) { Base::Console().Warning("%s is not a preference page\n", pageName.c_str()); + + return; } + + auto pageItem = new PreferencesPageItem; + + pageItem->setText(page->windowTitle()); + pageItem->setEditable(false); + pageItem->setData(groupItem->data(GroupNameRole), GroupNameRole); + pageItem->setData(QString::fromStdString(pageName), PageNameRole); + pageItem->setWidget(page); + + groupItem->appendRow(pageItem); + + page->loadSettings(); + + auto pages = qobject_cast(groupItem->getWidget()); + pages->addWidget(page); } catch (const Base::Exception& e) { Base::Console().Error("Base exception thrown for '%s'\n", pageName.c_str()); @@ -220,11 +266,11 @@ void DlgPreferencesImp::createPageInGroup(QTabWidget *tabWidget, const std::stri } } -void DlgPreferencesImp::changeGroup(QListWidgetItem *current, QListWidgetItem *previous) +void DlgPreferencesImp::updatePageDependentLabels() { - if (!current) - current = previous; - ui->tabWidgetStack->setCurrentIndex(ui->listBox->row(current)); + auto currentPageItem = getCurrentPage(); + + ui->headerLabel->setText(currentPageItem->text()); } /** @@ -236,7 +282,7 @@ void DlgPreferencesImp::changeGroup(QListWidgetItem *current, QListWidgetItem *p */ void DlgPreferencesImp::addPage(const std::string& className, const std::string& group) { - std::list::iterator groupToAddTo = _pages.end(); + auto groupToAddTo = _pages.end(); for (auto it = _pages.begin(); it != _pages.end(); ++it) { if (it->first == group) { groupToAddTo = it; @@ -263,7 +309,7 @@ void DlgPreferencesImp::addPage(const std::string& className, const std::string& void DlgPreferencesImp::removePage(const std::string& className, const std::string& group) { - for (std::list::iterator it = _pages.begin(); it != _pages.end(); ++it) { + for (auto it = _pages.begin(); it != _pages.end(); ++it) { if (it->first == group) { if (className.empty()) { _pages.erase(it); @@ -274,8 +320,9 @@ void DlgPreferencesImp::removePage(const std::string& className, const std::stri for (auto jt = p.begin(); jt != p.end(); ++jt) { if (*jt == className) { p.erase(jt); - if (p.empty()) + if (p.empty()) { _pages.erase(it); + } return; } } @@ -302,16 +349,19 @@ void DlgPreferencesImp::setGroupData(const std::string& name, const std::string& void DlgPreferencesImp::getGroupData(const std::string& group, std::string& icon, QString& tip) { auto it = _groupMap.find(group); + if (it != _groupMap.end()) { icon = it->second.iconName; tip = it->second.tooltip; } - if (icon.empty()) + if (icon.empty()) { icon = group; + } - if (tip.isEmpty()) + if (tip.isEmpty()) { tip = QObject::tr(group.c_str()); + } } /** @@ -319,16 +369,20 @@ void DlgPreferencesImp::getGroupData(const std::string& group, std::string& icon */ void DlgPreferencesImp::activateGroupPage(const QString& group, int index) { - int ct = ui->listBox->count(); - for (int i=0; ilistBox->item(i); - if (item->data(GroupNameRole).toString() == group) { - ui->listBox->setCurrentItem(item); - auto tabWidget = dynamic_cast(ui->tabWidgetStack->widget(i)); - if (tabWidget) { - tabWidget->setCurrentIndex(index); - break; - } + for (int i = 0; i < ui->groupWidgetStack->count(); i++) { + auto* pageStackWidget = qobject_cast(ui->groupWidgetStack->widget(i)); + + if (!pageStackWidget) { + continue; + } + + if (pageStackWidget->property(GroupNameProperty).toString() == group) { + ui->groupWidgetStack->setCurrentWidget(pageStackWidget); + pageStackWidget->setCurrentIndex(index); + + updatePageDependentLabels(); + + return; } } } @@ -338,20 +392,20 @@ void DlgPreferencesImp::activateGroupPage(const QString& group, int index) */ void DlgPreferencesImp::activeGroupPage(QString& group, int& index) const { - int row = ui->listBox->currentRow(); - auto item = ui->listBox->item(row); - auto tabWidget = dynamic_cast(ui->tabWidgetStack->widget(row)); + auto groupWidget = qobject_cast(ui->groupWidgetStack->currentWidget()); - if (item && tabWidget) { - group = item->data(GroupNameRole).toString(); - index = tabWidget->currentIndex(); + if (groupWidget) { + group = groupWidget->property(GroupNameProperty).toString(); + index = groupWidget->currentIndex(); } } void DlgPreferencesImp::accept() { this->invalidParameter = false; + applyChanges(); + if (!this->invalidParameter) { QDialog::accept(); restartIfRequired(); @@ -364,48 +418,48 @@ void DlgPreferencesImp::reject() restartIfRequired(); } -void DlgPreferencesImp::onButtonBoxClicked(QAbstractButton* btn) +void DlgPreferencesImp::onButtonBoxClicked(QAbstractButton* btn) { if (ui->buttonBox->standardButton(btn) == QDialogButtonBox::Apply) { applyChanges(); } - else if (ui->buttonBox->standardButton(btn) == QDialogButtonBox::Reset) { - showResetOptions(); - } } void DlgPreferencesImp::showResetOptions() { - // clang-format off QMenu menu(this); - // Reset per tab - auto tabWidget = static_cast(ui->tabWidgetStack->currentWidget()); - int tabIndex = tabWidget->currentIndex(); - QString tabText = tabWidget->tabText(tabIndex); - QAction* tabAction = menu.addAction(tr("Reset tab '%1'...").arg(tabText), this, - &DlgPreferencesImp::onButtonResetTabClicked); - tabAction->setToolTip(tr("Resets the user settings for the tab '%1'").arg(tabText)); + auto currentPageItem = getCurrentPage(); + auto currentGroupItem = static_cast(currentPageItem->parent()); + + auto pageText = currentPageItem->text(); + auto groupText = currentGroupItem->text(); + + // Reset per page + QAction* pageAction = menu.addAction(tr("Reset page '%1'...").arg(pageText), + this, + [&] { restorePageDefaults(currentPageItem); }); + pageAction->setToolTip(tr("Resets the user settings for the page '%1'").arg(pageText)); // Reset per group - int groupIndex = ui->listBox->currentRow(); - QString group = ui->listBox->item(groupIndex)->text(); - QAction* grpAction = menu.addAction(tr("Reset group '%1'...").arg(group), this, - &DlgPreferencesImp::onButtonResetGroupClicked); - grpAction->setToolTip(tr("Resets the user settings for the group '%1'").arg(group)); + QAction* groupAction = menu.addAction(tr("Reset group '%1'...").arg(groupText), + this, + [&] { restorePageDefaults(static_cast(currentPageItem->parent())); }); + groupAction->setToolTip(tr("Resets the user settings for the group '%1'").arg(groupText)); // Reset all - QAction* allAction = menu.addAction(tr("Reset all..."), this, + QAction* allAction = menu.addAction(tr("Reset all..."), + this, &DlgPreferencesImp::restoreDefaults); allAction->setToolTip(tr("Resets the user settings entirely")); - connect(&menu, &QMenu::hovered, [&menu](QAction* hover){ + connect(&menu, &QMenu::hovered, [&menu](QAction* hover) { QPoint pos = menu.pos(); pos.rx() += menu.width() + 10; QToolTip::showText(pos, hover->toolTip()); }); + menu.exec(QCursor::pos()); - // clang-format on } void DlgPreferencesImp::restoreDefaults() @@ -432,89 +486,6 @@ void DlgPreferencesImp::restoreDefaults() reject(); } } - -void DlgPreferencesImp::onButtonResetTabClicked() -{ - auto tabWidget = static_cast(ui->tabWidgetStack->widget(ui->listBox->currentRow())); - - QMessageBox box(this); - box.setIcon(QMessageBox::Question); - box.setWindowTitle(tr("Reset Tab Settings")); - box.setText(tr("All the settings for the tab '%1' will be deleted.").arg(tabWidget->tabText(tabWidget->currentIndex()))); - box.setInformativeText(tr("Do you want to continue?")); - box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - box.setDefaultButton(QMessageBox::No); - - if (box.exec() == QMessageBox::Yes) { - int pageIndex = tabWidget->currentIndex(); - QString pageText = tabWidget->tabText(pageIndex); - PreferencePage* page = qobject_cast(tabWidget->widget(pageIndex)); - - restorePageDefaults(&page); - page->setProperty("GroupName", tabWidget->property("GroupName")); - - tabWidget->removeTab(pageIndex); - tabWidget->insertTab(pageIndex, page, pageText); - tabWidget->setCurrentIndex(pageIndex); - - applyChanges(); - } -} - -void DlgPreferencesImp::onButtonResetGroupClicked() -{ - QMessageBox box(this); - box.setIcon(QMessageBox::Question); - box.setWindowTitle(tr("Reset Group Settings")); - box.setText(tr("All the settings for the group '%1' will be deleted.").arg(ui->listBox->currentItem()->text())); - box.setInformativeText(tr("Do you want to continue?")); - box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - box.setDefaultButton(QMessageBox::No); - - if (box.exec() == QMessageBox::Yes) { - auto tabWidget = static_cast(ui->tabWidgetStack->widget(ui->listBox->currentRow())); - int pageIndex = tabWidget->currentIndex(); - - for (int i = 0; i < tabWidget->count(); i++) { - QString pageText = tabWidget->tabText(i); - PreferencePage* page = qobject_cast(tabWidget->widget(i)); - - restorePageDefaults(&page); - page->setProperty("GroupName", tabWidget->property("GroupName")); - - tabWidget->removeTab(i); - tabWidget->insertTab(i, page, pageText); - } - - tabWidget->setCurrentIndex(pageIndex); - - applyChanges(); - } -} - -void DlgPreferencesImp::restorePageDefaults(PreferencePage** page) -{ - QList prefs = (*page)->findChildren(); - - for (const auto & pref : prefs) { - if (!pref->property("prefPath").isNull() && !pref->property("prefEntry").isNull()) { - std::string path = pref->property("prefPath").toString().toStdString(); - std::string entry = pref->property("prefEntry").toString().toStdString(); - - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(std::string("User parameter:BaseApp/Preferences/" + path).c_str()); - - for (const auto & pn : hGrp->GetParameterNames(entry.c_str())){ - hGrp->RemoveAttribute(pn.first, pn.second.c_str()); - } - } - } - - std::string pageName = (*page)->property("PageName").toString().toStdString(); - (*page) = WidgetFactory().createPreferencePage(pageName.c_str()); - (*page)->loadSettings(); - (*page)->setProperty("PageName", QVariant(QString::fromStdString(pageName))); -} - /** * If the dialog is currently showing and the static variable _pages changed, this function * will rescan that list of pages and add any that are new to the current dialog. It will not @@ -526,33 +497,39 @@ void DlgPreferencesImp::reloadPages() // Make sure that pages are ready to create GetWidgetFactorySupplier(); - for (const auto &group : _pages) { - QString groupName = QString::fromStdString(group.first); + for (const auto &[ group, pages ] : _pages) { + QString groupName = QString::fromStdString(group); // First, does this group already exist? - QTabWidget* tabWidget = nullptr; - for (int tabNumber = 0; tabNumber < ui->tabWidgetStack->count(); ++tabNumber) { - auto thisTabWidget = qobject_cast(ui->tabWidgetStack->widget(tabNumber)); - if (thisTabWidget->property("GroupName").toString() == groupName) { - tabWidget = thisTabWidget; + PreferencesPageItem* groupItem = nullptr; + + auto root = _model.invisibleRootItem(); + for (int i = 0; i < root->rowCount(); i++) { + auto currentGroupItem = static_cast(root->child(i)); + auto currentGroupName = currentGroupItem->data(GroupNameRole).toString(); + + if (currentGroupName == groupName) { + groupItem = currentGroupItem; break; } } - // This is a new tab that wasn't there when we started this instance of the dialog: - if (!tabWidget) { - tabWidget = createTabForGroup(group.first); + // This is a new group that wasn't there when we started this instance of the dialog: + if (!groupItem) { + groupItem = createGroup(group); } // Move on to the pages in the group to see if we need to add any - for (const auto& page : group.second) { - + for (const auto& page : pages) { // Does this page already exist? QString pageName = QString::fromStdString(page); + bool pageExists = false; - for (int pageNumber = 0; pageNumber < tabWidget->count(); ++pageNumber) { - auto prefPage = qobject_cast(tabWidget->widget(pageNumber)); - if (prefPage && prefPage->property("PageName").toString() == pageName) { + + for (int i = 0; i < groupItem->rowCount(); i++) { + auto currentPageItem = static_cast(groupItem->child(i)); + + if (currentPageItem->data(PageNameRole).toString() == pageName) { pageExists = true; break; } @@ -560,7 +537,7 @@ void DlgPreferencesImp::reloadPages() // This is a new page that wasn't there when we started this instance of the dialog: if (!pageExists) { - createPageInGroup(tabWidget, page); + createPageInGroup(groupItem, page); } } } @@ -573,36 +550,44 @@ void DlgPreferencesImp::applyChanges() // call it to validate if user input is correct. If something fails (i.e., // not correct), shows a messageBox and set this->invalidParameter = true to // cancel further operation in other methods (like in accept()). - try { - for (int i=0; itabWidgetStack->count(); i++) { - auto tabWidget = static_cast(ui->tabWidgetStack->widget(i)); - for (int j=0; jcount(); j++) { - QWidget* page = tabWidget->widget(j); - int index = page->metaObject()->indexOfMethod("checkSettings()"); + + for (int i = 0; i < ui->groupWidgetStack->count(); i++) { + auto pagesStackWidget = qobject_cast(ui->groupWidgetStack->widget(i)); + + for (int j = 0; j < pagesStackWidget->count(); j++) { + QWidget* page = pagesStackWidget->widget(j); + + int index = page->metaObject()->indexOfMethod("checkSettings()"); + + if (index >= 0) { try { - if (index >= 0) { - page->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr); - } + page->qt_metacall(QMetaObject::InvokeMetaMethod, index, nullptr); } catch (const Base::Exception& e) { - ui->listBox->setCurrentRow(i); - tabWidget->setCurrentIndex(j); - QMessageBox::warning(this, tr("Wrong parameter"), QString::fromLatin1(e.what())); - throw; + ui->groupWidgetStack->setCurrentIndex(i); + pagesStackWidget->setCurrentIndex(j); + + QMessageBox::warning(this, + tr("Wrong parameter"), + QString::fromLatin1(e.what())); + + this->invalidParameter = true; + + // exit early due to found errors + return; } } } - } catch (const Base::Exception&) { - this->invalidParameter = true; - return; } // If everything is ok (i.e., no validation problem), call method // saveSettings() in every subpage (DlgSetting*) object. - for (int i=0; itabWidgetStack->count(); i++) { - auto tabWidget = static_cast(ui->tabWidgetStack->widget(i)); - for (int j=0; jcount(); j++) { - auto page = qobject_cast(tabWidget->widget(j)); + for (int i = 0; i < ui->groupWidgetStack->count(); i++) { + auto pageStackWidget = qobject_cast(ui->groupWidgetStack->widget(i)); + + for (int j = 0; j < pageStackWidget->count(); j++) { + auto page = qobject_cast(pageStackWidget->widget(j)); + if (page) { page->saveSettings(); restartRequired = restartRequired || page->isRestartRequired(); @@ -610,8 +595,10 @@ void DlgPreferencesImp::applyChanges() } } - bool saveParameter = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")-> - GetBool("SaveUserParameter", true); + bool saveParameter = App::GetApplication() + .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General") + ->GetBool("SaveUserParameter", true); + if (saveParameter) { ParameterManager* parmgr = App::GetApplication().GetParameterSet("User parameter"); parmgr->SaveDocument(App::Application::Config()["UserParameter"].c_str()); @@ -621,18 +608,19 @@ void DlgPreferencesImp::applyChanges() void DlgPreferencesImp::restartIfRequired() { if (restartRequired) { - QMessageBox* restartBox = new QMessageBox(); - restartBox->setIcon(QMessageBox::Warning); - restartBox->setWindowTitle(tr("Restart required")); - restartBox->setText(tr("You must restart FreeCAD for changes to take effect.")); - restartBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - restartBox->setDefaultButton(QMessageBox::Cancel); - auto okBtn = restartBox->button(QMessageBox::Ok); - auto cancelBtn = restartBox->button(QMessageBox::Cancel); + QMessageBox restartBox; + + restartBox.setIcon(QMessageBox::Warning); + restartBox.setWindowTitle(tr("Restart required")); + restartBox.setText(tr("You must restart FreeCAD for changes to take effect.")); + restartBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + restartBox.setDefaultButton(QMessageBox::Cancel); + auto okBtn = restartBox.button(QMessageBox::Ok); + auto cancelBtn = restartBox.button(QMessageBox::Cancel); okBtn->setText(tr("Restart now")); cancelBtn->setText(tr("Restart later")); - int exec = restartBox->exec(); + int exec = restartBox.exec(); if (exec == QMessageBox::Ok) { //restart FreeCAD after a delay to give time to this dialog to close @@ -653,71 +641,54 @@ void DlgPreferencesImp::showEvent(QShowEvent* ev) QDialog::showEvent(ev); } -void DlgPreferencesImp::resizeEvent(QResizeEvent* ev) +QModelIndex findRootIndex(const QModelIndex& index) { - if (canEmbedScrollArea) { - // embed the widget stack into a scroll area if the size is - // bigger than the available desktop - QRect rect = QApplication::primaryScreen()->availableGeometry(); - int maxHeight = rect.height() - 60; - int maxWidth = rect.width(); - if (height() > maxHeight || width() > maxWidth) { - canEmbedScrollArea = false; - ui->hboxLayout->removeWidget(ui->tabWidgetStack); - auto scrollArea = new QScrollArea(this); - scrollArea->setFrameShape(QFrame::NoFrame); - scrollArea->setWidgetResizable(true); - scrollArea->setWidget(ui->tabWidgetStack); - ui->hboxLayout->addWidget(scrollArea); + auto root = index; - // if possible the minimum width should so that it doesn't show - // a horizontal scroll bar. - auto bar = scrollArea->verticalScrollBar(); - if (bar) { - int newWidth = width() + bar->width(); - newWidth = std::min(newWidth, maxWidth); - int newHeight = std::min(height(), maxHeight); - QMetaObject::invokeMethod(this, "resizeWindow", - Qt::QueuedConnection, - Q_ARG(int, newWidth), - Q_ARG(int, newHeight)); - } - QPoint center = rect.center(); - move(center.x() - width() * 0.5, 10); - } + while (root.parent().isValid()) { + root = root.parent(); } - QDialog::resizeEvent(ev); + + return root; } -void DlgPreferencesImp::resizeWindow(int w, int h) +void DlgPreferencesImp::onPageSelected(const QModelIndex& index) { - resize(w, h); + auto root = findRootIndex(index); + + auto* groupItem = static_cast(_model.itemFromIndex(root)); + auto* pagesStackWidget = static_cast(groupItem->getWidget()); + + ui->groupWidgetStack->setCurrentWidget(groupItem->getWidget()); + + if (index != root) { + pagesStackWidget->setCurrentIndex(index.row()); + } + + updatePageDependentLabels(); } void DlgPreferencesImp::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); - // update the widgets' tabs - for (int i=0; itabWidgetStack->count(); i++) { - auto tabWidget = static_cast(ui->tabWidgetStack->widget(i)); - for (int j=0; jcount(); j++) { - QWidget* page = tabWidget->widget(j); - tabWidget->setTabText(j, page->windowTitle()); + + auto root = _model.invisibleRootItem(); + for (int i = 0; i < root->rowCount(); i++) { + auto groupItem = static_cast(root->child(i)); + auto groupName = groupItem->data(GroupNameRole).toString(); + + groupItem->setText(QObject::tr(groupName.toLatin1())); + + for (int j = 0; j < groupItem->rowCount(); j++) { + auto pageModelItem = static_cast(groupItem->child(j)); + auto pageModelWidget = static_cast(pageModelItem->getWidget()); + + pageModelItem->setText(pageModelWidget->windowTitle()); } } - // update the items' text - for (int i=0; ilistBox->count(); i++) { - QListWidgetItem *item = ui->listBox->item(i); - QByteArray group = item->data(GroupNameRole).toByteArray(); - item->setText(QObject::tr(group.constData())); - } - //resizes items list and buttons - QFontMetrics fm(font()); - int length = QtTools::horizontalAdvance(fm, longestGroupName()); - ui->listBox->setFixedWidth(Base::clamp(length + 20, 108, 120)); - ui->listBox->setGridSize(QSize(Base::clamp(length + 20, 108, 120), 75)); + updatePageDependentLabels(); } else { QWidget::changeEvent(e); } @@ -725,15 +696,74 @@ void DlgPreferencesImp::changeEvent(QEvent *e) void DlgPreferencesImp::reload() { - for (int i = 0; i < ui->tabWidgetStack->count(); i++) { - auto tabWidget = static_cast(ui->tabWidgetStack->widget(i)); - for (int j = 0; j < tabWidget->count(); j++) { - auto page = qobject_cast(tabWidget->widget(j)); - if (page) + for (int i = 0; i < ui->groupWidgetStack->count(); i++) { + auto pageStackWidget = static_cast(ui->groupWidgetStack->widget(i)); + + for (int j = 0; j < pageStackWidget->count(); j++) { + auto page = qobject_cast(pageStackWidget->widget(j)); + + if (page) { page->loadSettings(); + } } } + applyChanges(); } +void DlgPreferencesImp::restorePageDefaults(PreferencesPageItem* item) +{ + if (item->hasChildren()) { + // If page has children iterate over them and restore each + for (int i = 0; i < item->rowCount(); i++) { + auto child = static_cast(item->child(i)); + + restorePageDefaults(child); + } + } + else { + auto* page = qobject_cast(item->getWidget()); + auto prefs = page->findChildren(); + + page->resetSettingsToDefaults(); + + std::string pageName = page->property(PageNameProperty).toString().toStdString(); + std::string groupName = page->property(GroupNameProperty).toString().toStdString(); + + auto newPage = createPreferencePage(pageName, groupName); + + newPage->loadSettings(); + + auto groupPageStack = qobject_cast(page->parentWidget()); + auto replacedWidgetIndex = groupPageStack->indexOf(page); + auto currentWidgetIndex = groupPageStack->currentIndex(); + + groupPageStack->removeWidget(page); + groupPageStack->insertWidget(replacedWidgetIndex, newPage); + + item->setWidget(newPage); + + if (replacedWidgetIndex == currentWidgetIndex) { + groupPageStack->setCurrentIndex(currentWidgetIndex); + } + } +} + +PreferencesPageItem* DlgPreferencesImp::getCurrentPage() const +{ + auto groupPagesStack = qobject_cast(ui->groupWidgetStack->currentWidget()); + + if (!groupPagesStack) { + return nullptr; + } + + auto pageWidget = qobject_cast(groupPagesStack->currentWidget()); + + if (!pageWidget) { + return nullptr; + } + + return pageWidget->property(PreferencesPageItem::PropertyName).value(); +} + #include "moc_DlgPreferencesImp.cpp" diff --git a/src/Gui/DlgPreferencesImp.h b/src/Gui/DlgPreferencesImp.h index 18631e3869..013aa1ced7 100644 --- a/src/Gui/DlgPreferencesImp.h +++ b/src/Gui/DlgPreferencesImp.h @@ -27,6 +27,7 @@ #define GUI_DIALOG_DLGPREFERENCESIMP_H #include +#include #include #include @@ -34,11 +35,22 @@ class QAbstractButton; class QListWidgetItem; class QTabWidget; -namespace Gui { -namespace Dialog { +namespace Gui::Dialog { class PreferencePage; class Ui_DlgPreferences; +class PreferencesPageItem : public QStandardItem +{ +public: + QWidget* getWidget() const; + void setWidget(QWidget* widget); + + static constexpr char const* PropertyName = "SettingsPageItem"; + +private: + QWidget *_widget = nullptr; +}; + /** * This class implements a dialog containing several preference pages. * @@ -120,6 +132,8 @@ public: static void getGroupData(const std::string& group, std::string& icon, QString& tip); static void reloadSettings(); + static PreferencePage* createPreferencePage(const std::string& pageName, const std::string& groupName); + explicit DlgPreferencesImp(QWidget* parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags()); ~DlgPreferencesImp() override; @@ -130,53 +144,63 @@ public: void activeGroupPage(QString& group, int& index) const; protected: - void setupConnections(); void changeEvent(QEvent *e) override; void showEvent(QShowEvent*) override; - void resizeEvent(QResizeEvent*) override; - void onButtonResetTabClicked(); - void onButtonResetGroupClicked(); - protected Q_SLOTS: - void changeGroup(QListWidgetItem *current, QListWidgetItem *previous); void onButtonBoxClicked(QAbstractButton*); - void resizeWindow(int w, int h); + void onPageSelected(const QModelIndex &index); private: /** @name for internal use only */ //@{ void setupPages(); void reloadPages(); - QTabWidget* createTabForGroup(const std::string& groupName); - void createPageInGroup(QTabWidget* tabWidget, const std::string& pageName); + + PreferencesPageItem* getCurrentPage() const; + + PreferencesPageItem* createGroup(const std::string& groupName); + void createPageInGroup(PreferencesPageItem* item, const std::string& pageName); + void applyChanges(); void showResetOptions(); void restoreDefaults(); - void restorePageDefaults(PreferencePage**); - QString longestGroupName() const; + void restorePageDefaults(PreferencesPageItem* item); void restartIfRequired(); + + void updatePageDependentLabels(); + + QPixmap loadIconForGroup(const std::string& name) const; //@} private: using TGroupPages = std::pair>; + static std::list _pages; /**< Name of all registered preference pages */ + + QStandardItemModel _model; + struct Group { std::string iconName; QString tooltip; }; static std::map _groupMap; std::unique_ptr ui; + bool invalidParameter; bool canEmbedScrollArea; bool restartRequired; - static const int GroupNameRole; /**< A name for our Qt::UserRole, used when storing user data in a list item */ + /**< A name for our Qt::UserRole, used when storing user data in a list item */ + static const int GroupNameRole; + static const int PageNameRole; + + static constexpr char const* GroupNameProperty = "GroupName"; + static constexpr char const* PageNameProperty = "PageName"; static DlgPreferencesImp* _activeDialog; /**< Defaults to the nullptr, points to the current instance if there is one */ }; -} // namespace Dialog } // namespace Gui #endif // GUI_DIALOG_DLGPREFERENCESIMP_H diff --git a/src/Gui/PrefWidgets.cpp b/src/Gui/PrefWidgets.cpp index 314380b311..445392c041 100644 --- a/src/Gui/PrefWidgets.cpp +++ b/src/Gui/PrefWidgets.cpp @@ -815,7 +815,7 @@ void PrefFontBox::savePreferences() QFont currFont = currentFont(); QString currName = currFont.family(); - getWindowParameter()->SetASCII( entryName() , currName.toUtf8() ); + getWindowParameter()->SetASCII(entryName(), currName.toUtf8()); } #include "moc_PrefWidgets.cpp" diff --git a/src/Gui/PropertyPage.cpp b/src/Gui/PropertyPage.cpp index 26123473f2..2526ad8967 100644 --- a/src/Gui/PropertyPage.cpp +++ b/src/Gui/PropertyPage.cpp @@ -28,6 +28,7 @@ #endif #include +#include #include "PropertyPage.h" #include "PrefWidgets.h" @@ -209,6 +210,24 @@ void PreferenceUiForm::saveSettings() savePrefWidgets(); } +void PreferencePage::resetSettingsToDefaults() +{ + auto prefs = this->findChildren(); + + for (const auto& pref : prefs) { + if (!pref->property("prefPath").isNull() && !pref->property("prefEntry").isNull()) { + std::string path = pref->property("prefPath").toString().toStdString(); + std::string entry = pref->property("prefEntry").toString().toStdString(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + std::string("User parameter:BaseApp/Preferences/" + path).c_str()); + + for (const auto& pn : hGrp->GetParameterNames(entry.c_str())) { + hGrp->RemoveAttribute(pn.first, pn.second.c_str()); + } + } + } +} // ---------------------------------------------------------------- /** Construction */ diff --git a/src/Gui/PropertyPage.h b/src/Gui/PropertyPage.h index 5d17f3bf8f..9fbf03e0b5 100644 --- a/src/Gui/PropertyPage.h +++ b/src/Gui/PropertyPage.h @@ -79,6 +79,7 @@ public: public Q_SLOTS: virtual void loadSettings()=0; virtual void saveSettings()=0; + virtual void resetSettingsToDefaults(); protected: void changeEvent(QEvent* event) override = 0;