From 788ea1d3c072bde0cb3f2fe9575f628d0a1221e4 Mon Sep 17 00:00:00 2001 From: Paddle Date: Fri, 24 Mar 2023 11:45:50 +0100 Subject: [PATCH] Preferences: Workbench : Enable drag and drop. Replaces the QTable by a QListWidget. --- src/Gui/CMakeLists.txt | 2 + src/Gui/DlgSettingsLazyLoaded.ui | 32 +++---- src/Gui/DlgSettingsLazyLoadedImp.cpp | 123 ++++++++++++--------------- src/Gui/DlgSettingsLazyLoadedImp.h | 6 +- src/Gui/QListWidgetDragBugFix.cpp | 58 +++++++++++++ src/Gui/QListWidgetDragBugFix.h | 42 +++++++++ 6 files changed, 171 insertions(+), 92 deletions(-) create mode 100644 src/Gui/QListWidgetDragBugFix.cpp create mode 100644 src/Gui/QListWidgetDragBugFix.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index d38d4c8644..f01bd764b8 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -528,6 +528,7 @@ SET(Dialog_Customize_CPP_SRCS DlgToolbarsImp.cpp DlgWorkbenchesImp.cpp QListWidgetCustom.cpp + QListWidgetDragBugFix.cpp ) SET(Dialog_Customize_HPP_SRCS DlgActionsImp.h @@ -539,6 +540,7 @@ SET(Dialog_Customize_HPP_SRCS DlgToolbarsImp.h DlgWorkbenchesImp.h QListWidgetCustom.h + QListWidgetDragBugFix.h ) SET(Dialog_Customize_SRCS ${Dialog_Customize_CPP_SRCS} diff --git a/src/Gui/DlgSettingsLazyLoaded.ui b/src/Gui/DlgSettingsLazyLoaded.ui index 3447f17d34..8c47ce85cb 100644 --- a/src/Gui/DlgSettingsLazyLoaded.ui +++ b/src/Gui/DlgSettingsLazyLoaded.ui @@ -29,34 +29,28 @@ - <html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p>Enabling or disabling workbenches requires a restart of the application.</p><p>The following workbenches are available in your installation:</p></body></html> + <html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p> +You can enable/disable and reorder workbenches (requires restart).</p><p> +Your installed workbenches(you can add more through the addon manager):</p></body></html> true - - - - false - - - false - - - false - - - - - - - + + - + + + + QListWidgetDragBugFix + QListWidget +
QListWidgetDragBugFix.h
+
+
diff --git a/src/Gui/DlgSettingsLazyLoadedImp.cpp b/src/Gui/DlgSettingsLazyLoadedImp.cpp index 2566bf5b46..9999e9c0ac 100644 --- a/src/Gui/DlgSettingsLazyLoadedImp.cpp +++ b/src/Gui/DlgSettingsLazyLoadedImp.cpp @@ -30,17 +30,14 @@ #include "DlgSettingsLazyLoadedImp.h" #include "ui_DlgSettingsLazyLoaded.h" #include "Application.h" -#include "BitmapFactory.h" #include "Workbench.h" #include "WorkbenchManager.h" using namespace Gui::Dialog; -const uint DlgSettingsLazyLoadedImp::WorkbenchNameRole = Qt::UserRole; - -const int DlgSettingsLazyLoadedImp::columnCount = 1; - +const QString DlgSettingsLazyLoadedImp::iconLabelStr = QString::fromLatin1("iconLabel"); +const QString DlgSettingsLazyLoadedImp::nameLabelStr = QString::fromLatin1("nameLabel"); const QString DlgSettingsLazyLoadedImp::loadLabelStr = QString::fromLatin1("loadLabel"); const QString DlgSettingsLazyLoadedImp::loadButtonStr = QString::fromLatin1("loadButton"); const QString DlgSettingsLazyLoadedImp::enableCheckboxStr = QString::fromLatin1("enableCheckbox"); @@ -70,8 +67,8 @@ void DlgSettingsLazyLoadedImp::saveSettings() { std::ostringstream enabledStr, disabledStr, autoloadStr; - for (int i = 0; i < ui->workbenchTable->rowCount(); i++) { - QWidget* widget = ui->workbenchTable->cellWidget(i, 0); + for (int i = 0; i < ui->wbList->count(); i++) { + QWidget* widget = ui->wbList->itemWidget(ui->wbList->item(i)); if (!widget) continue; QCheckBox* enableBox = widget->findChild(enableCheckboxStr); @@ -102,8 +99,11 @@ void DlgSettingsLazyLoadedImp::saveSettings() if (enabledStr.str().empty()) //make sure that we have at least one enabled workbench. enabledStr << "NoneWorkbench"; - else + else { + if (!disabledStr.str().empty()) + disabledStr << ","; disabledStr << "NoneWorkbench"; //Note, NoneWorkbench is not in the table so it's not added before. + } ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbenches"); hGrp->SetASCII("Enabled", enabledStr.str().c_str()); @@ -144,8 +144,8 @@ void DlgSettingsLazyLoadedImp::onLoadClicked(const QString &wbName) Application::Instance->activateWorkbench(originalActiveWB->name().c_str()); // replace load button with loaded indicator - for (int i = 0; i < ui->workbenchTable->rowCount(); i++) { - QWidget* widget = ui->workbenchTable->cellWidget(i, 0); + for (int i = 0; i < ui->wbList->count(); i++) { + QWidget* widget = ui->wbList->itemWidget(ui->wbList->item(i)); if (widget && widget->objectName() == wbName) { QWidget* loadLabel = widget->findChild(loadLabelStr); QWidget* loadButton = widget->findChild(loadButtonStr); @@ -156,42 +156,29 @@ void DlgSettingsLazyLoadedImp::onLoadClicked(const QString &wbName) } } -void DlgSettingsLazyLoadedImp::onUpDownClicked(const QString& wbName, bool up) +void DlgSettingsLazyLoadedImp::onWbActivated(const QString &wbName, bool checked) { - int rowIndex = 0; - //find the index of the row that is moving. - for (int i = 0; i < ui->workbenchTable->rowCount(); i++) { - QWidget* widget = ui->workbenchTable->cellWidget(i, 0); - if(widget->objectName() == wbName) { + // activate/deactivate the widgets + for (int i = 0; i < ui->wbList->count(); i++) { + QWidget* widget = ui->wbList->itemWidget(ui->wbList->item(i)); + if (widget && widget->objectName() == wbName) { + QWidget* iconLabel = widget->findChild(iconLabelStr); + QWidget* nameLabel = widget->findChild(nameLabelStr); + QWidget* loadLabel = widget->findChild(loadLabelStr); + QWidget* loadButton = widget->findChild(loadButtonStr); + QCheckBox* autoloadCheckbox = widget->findChild(autoloadCheckboxStr); + if (!iconLabel || !nameLabel || !loadLabel || !loadButton || !autoloadCheckbox) + return; + iconLabel->setEnabled(checked); + nameLabel->setEnabled(checked); + loadLabel->setEnabled(checked); + loadButton->setEnabled(checked); + autoloadCheckbox->setEnabled(checked); + if (!checked) //disabling wb disable auto-load. + autoloadCheckbox->setChecked(false); break; } - rowIndex++; } - - //Check if it can move - if (((rowIndex == 0) && up) || ((rowIndex == ui->workbenchTable->rowCount() - 1) && !up)) - return; - - //Move in the _enabledCheckBoxes vector. - //std::iter_swap(_enabledCheckBoxes.begin() + rowIndex, _enabledCheckBoxes.begin() + rowIndex + (up ? -1 : 1)); - - //Move the rows in the table - auto widget = ui->workbenchTable->cellWidget(rowIndex, 0); - auto widget2 = ui->workbenchTable->cellWidget(rowIndex + (up ? -1 : 1), 0); - - auto newWidget = new QWidget(this); - newWidget->setObjectName(widget->objectName()); - newWidget->setLayout(widget->layout()); - - auto newWidget2 = new QWidget(this); - newWidget2->setObjectName(widget2->objectName()); - newWidget2->setLayout(widget2->layout()); - - ui->workbenchTable->removeCellWidget(rowIndex, 0); - ui->workbenchTable->removeCellWidget(rowIndex + (up ? -1 : 1), 0); - - ui->workbenchTable->setCellWidget(rowIndex + (up ? -1 : 1), 0, newWidget); - ui->workbenchTable->setCellWidget(rowIndex , 0, newWidget2); } /** @@ -203,15 +190,12 @@ void DlgSettingsLazyLoadedImp::buildWorkbenchList() QStringList enabledWbs = getEnabledWorkbenches(); QStringList disabledWbs = getDisabledWorkbenches(); - ui->workbenchTable->setSelectionMode(QAbstractItemView::NoSelection); - ui->workbenchTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); - ui->workbenchTable->setRowCount(0); - ui->workbenchTable->setColumnCount(columnCount); - ui->workbenchTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents); - QStringList columnHeaders; - columnHeaders << tr("Enable"); - ui->workbenchTable->setHorizontalHeaderLabels(columnHeaders); - + ui->wbList->setDragDropMode(QAbstractItemView::InternalMove); + ui->wbList->setSelectionMode(QAbstractItemView::SingleSelection); + ui->wbList->viewport()->setAcceptDrops(true); + ui->wbList->setDropIndicatorShown(true); + ui->wbList->setDragEnabled(true); + ui->wbList->setDefaultDropAction(Qt::MoveAction); //First we add the enabled wbs in their saved order. for (const auto& wbName : enabledWbs) { @@ -219,7 +203,7 @@ void DlgSettingsLazyLoadedImp::buildWorkbenchList() addWorkbench(wbName, true); } else { - qDebug() << "Ignoring unknown" << wbName << "workbench found in user preferences."; + Base::Console().Warning("Ignoring unknown %s workbench found in user preferences.", wbName.toStdString().c_str()); } } //Second we add workbench in alphabetical order that are either Disabled, or !enabled && !disabled, ie newly added wb. @@ -228,7 +212,7 @@ void DlgSettingsLazyLoadedImp::buildWorkbenchList() addWorkbench(wbName, false); } else if (!enabledWbs.contains(wbName)) { - qDebug() << "Adding unknown " << wbName << "workbench."; + Base::Console().Warning("Adding unknown %s workbench.", wbName.toStdString().c_str()); addWorkbench(wbName, false); } } @@ -239,10 +223,11 @@ void DlgSettingsLazyLoadedImp::addWorkbench(const QString& wbName, bool enabled) if (wbName.toStdString() == "NoneWorkbench") return; // Do not list the default empty Workbench - int rowNumber = ui->workbenchTable->rowCount(); - ui->workbenchTable->insertRow(rowNumber); QWidget* widget = createWorkbenchWidget(wbName, enabled); - ui->workbenchTable->setCellWidget(rowNumber, 0, widget); + auto wItem = new QListWidgetItem(); + wItem->setSizeHint(widget->sizeHint()); + ui->wbList->addItem(wItem); + ui->wbList->setItemWidget(wItem, widget); } QWidget* DlgSettingsLazyLoadedImp::createWorkbenchWidget(const QString& wbName, bool enabled) @@ -260,23 +245,32 @@ QWidget* DlgSettingsLazyLoadedImp::createWorkbenchWidget(const QString& wbName, enableCheckBox->setEnabled(false); enableCheckBox->setToolTip(tr("This is the current startup module, and must be enabled. See Preferences/General/Autoload to change.")); } + connect(enableCheckBox, &QCheckBox::toggled, this, [this, wbName](bool checked) { onWbActivated(wbName, checked); }); // 2: Workbench Icon auto wbIcon = Application::Instance->workbenchIcon(wbName); auto iconLabel = new QLabel(wbDisplayName); + iconLabel->setObjectName(iconLabelStr); iconLabel->setPixmap(wbIcon.scaled(QSize(20, 20), Qt::AspectRatioMode::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); iconLabel->setToolTip(wbTooltip); iconLabel->setContentsMargins(5, 0, 0, 5); // Left, top, right, bottom + iconLabel->setEnabled(enableCheckBox->isChecked()); // 3: Workbench Display Name auto textLabel = new QLabel(wbDisplayName); - textLabel->setToolTip(wbTooltip); + textLabel->setObjectName(nameLabelStr); + textLabel->setToolTip(wbTooltip); + QFont font = textLabel->font(); + font.setBold(true); + textLabel->setFont(font); + textLabel->setEnabled(enableCheckBox->isChecked()); // 4: Autoloaded checkBox. auto autoloadCheckBox = new QCheckBox(this); autoloadCheckBox->setObjectName(autoloadCheckboxStr); autoloadCheckBox->setText(tr("Auto-load")); autoloadCheckBox->setToolTip(tr("If checked, %1 will be loaded automatically when FreeCAD starts up").arg(wbDisplayName)); + autoloadCheckBox->setEnabled(enableCheckBox->isChecked()); if (wbName.toStdString() == _startupModule) { // Figure out whether to check and/or disable this checkBox: autoloadCheckBox->setChecked(true); @@ -292,8 +286,10 @@ QWidget* DlgSettingsLazyLoadedImp::createWorkbenchWidget(const QString& wbName, auto loadLabel = new QLabel(tr("Loaded")); loadLabel->setAlignment(Qt::AlignCenter); loadLabel->setObjectName(loadLabelStr); + loadLabel->setEnabled(enableCheckBox->isChecked()); auto loadButton = new QPushButton(tr("Load")); loadButton->setObjectName(loadButtonStr); + loadButton->setEnabled(enableCheckBox->isChecked()); connect(loadButton, &QPushButton::clicked, this, [this, wbName]() { onLoadClicked(wbName); }); if (WorkbenchManager::instance()->getWorkbench(wbName.toStdString())) { loadButton->setVisible(false); @@ -302,17 +298,6 @@ QWidget* DlgSettingsLazyLoadedImp::createWorkbenchWidget(const QString& wbName, loadLabel->setVisible(false); } - // 6: up down buttons - auto downButton = new QToolButton(this); - auto upButton = new QToolButton(this); - downButton->setToolTip(tr("Move %1 down").arg(wbDisplayName)); - upButton->setToolTip(tr("Move %1 up").arg(wbDisplayName)); - downButton->setIcon(Gui::BitmapFactory().iconFromTheme("button_down")); - upButton->setIcon(Gui::BitmapFactory().iconFromTheme("button_up")); - connect(upButton, &QToolButton::clicked, this, [this, wbName]() { onUpDownClicked(wbName, true); }); - connect(downButton, &QToolButton::clicked, this, [this, wbName]() { onUpDownClicked(wbName, false); }); - - auto mainWidget = new QWidget(this); mainWidget->setObjectName(wbName); auto layout = new QHBoxLayout(mainWidget); @@ -323,8 +308,6 @@ QWidget* DlgSettingsLazyLoadedImp::createWorkbenchWidget(const QString& wbName, layout->addWidget(loadButton); layout->addWidget(loadLabel); layout->addWidget(autoloadCheckBox); - layout->addWidget(downButton); - layout->addWidget(upButton); layout->setAlignment(Qt::AlignLeft); layout->setContentsMargins(10, 0, 0, 0); diff --git a/src/Gui/DlgSettingsLazyLoadedImp.h b/src/Gui/DlgSettingsLazyLoadedImp.h index e1f76c6a21..b2a6b93caa 100644 --- a/src/Gui/DlgSettingsLazyLoadedImp.h +++ b/src/Gui/DlgSettingsLazyLoadedImp.h @@ -55,7 +55,7 @@ public: protected Q_SLOTS: void onLoadClicked(const QString& wbName); - void onUpDownClicked(const QString& wbName, bool up); + void onWbActivated(const QString& wbName, bool checked); protected: void buildWorkbenchList(); @@ -66,8 +66,8 @@ private: QWidget* createWorkbenchWidget(const QString& it, bool enabled); std::unique_ptr ui; - static const uint WorkbenchNameRole; - static const int columnCount; + static const QString iconLabelStr; + static const QString nameLabelStr; static const QString loadLabelStr; static const QString loadButtonStr; static const QString enableCheckboxStr; diff --git a/src/Gui/QListWidgetDragBugFix.cpp b/src/Gui/QListWidgetDragBugFix.cpp new file mode 100644 index 0000000000..71f864f4e4 --- /dev/null +++ b/src/Gui/QListWidgetDragBugFix.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2023 Boyer Pierre-louis * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include "QListWidgetDragBugFix.h" + + +QListWidgetDragBugFix::QListWidgetDragBugFix(QWidget * parent) + : QListWidget(parent) +{ +} + +QListWidgetDragBugFix::~QListWidgetDragBugFix() +{ +} + +/* Qt has a recent bug (2023, https://bugreports.qt.io/browse/QTBUG-100128) +* where the items disappears in certain conditions when drag and dropping. +* Here we prevent the situation where this happens. +* 1 - If the item is dropped on the item below such that the item doesn't move (ie superior half of the below item) +* 2 - The item is the last one and user drop it on the empty space below. +* In both those cases the item widget was lost. + */ +void QListWidgetDragBugFix::dragMoveEvent(QDragMoveEvent *e) +{ + if ((row(itemAt(e->pos())) == currentRow() + 1) + || (currentRow() == count() - 1 && row(itemAt(e->pos())) == -1)) { + e->ignore(); + } + else { + QListWidget::dragMoveEvent(e); + } +} + +#include "moc_QListWidgetDragBugFix.cpp" diff --git a/src/Gui/QListWidgetDragBugFix.h b/src/Gui/QListWidgetDragBugFix.h new file mode 100644 index 0000000000..6b19195566 --- /dev/null +++ b/src/Gui/QListWidgetDragBugFix.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (c) 2023 Boyer Pierre-louis * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef QLISTWIDGETDRAGBUGFIX_HPP +#define QLISTWIDGETDRAGBUGFIX_HPP + +#include +#include + + +class QListWidgetDragBugFix : public QListWidget +{ + Q_OBJECT + +public: + QListWidgetDragBugFix(QWidget *parent); + ~QListWidgetDragBugFix() override; + +protected: + void dragMoveEvent(QDragMoveEvent *e) override; +}; + +#endif