Gui: Move ComboLink from PartDesign to Gui.

This commit is contained in:
paddle
2025-07-09 09:29:11 +02:00
parent 1eb05317ee
commit 66e186f396
8 changed files with 401 additions and 158 deletions

View File

@@ -1166,6 +1166,7 @@ SOURCE_GROUP("View3D\\Inventor" FILES ${Inventor_SRCS})
# The widget sources
SET(Widget_CPP_SRCS
ComboLinks.cpp
FileDialog.cpp
MainWindow.cpp
MainWindowPy.cpp
@@ -1189,6 +1190,7 @@ SET(Widget_CPP_SRCS
FontScaledSVG.cpp
)
SET(Widget_HPP_SRCS
ComboLinks.h
FileDialog.h
MainWindow.h
MainWindowPy.h

223
src/Gui/ComboLinks.cpp Normal file
View File

@@ -0,0 +1,223 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2025 AstoCAD <hello@astocad.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "PreCompiled.h"
#include "ComboLinks.h"
#ifndef _PreComp_
#include <QComboBox>
#include <QVariant>
#include <QString>
#endif
#include <App/Document.h>
#include <App/DocumentObject.h>
namespace Gui {
ComboLinks::ComboLinks(QComboBox& combo)
: _combo(&combo)
{
if (_combo) {
_combo->clear();
}
}
ComboLinks::~ComboLinks()
{
clear(); // Deletes owned pointers in linksInList
_combo = nullptr; // Don't delete, not owned
}
void ComboLinks::setCombo(QComboBox& combo)
{
clear(); // Clear old state if any
_combo = &combo;
if (_combo) {
_combo->clear();
}
}
int ComboLinks::addLink(const App::PropertyLinkSub& lnk, const QString& itemText, int userData)
{
if (!_combo) {
return -1;
}
int newIndex = _combo->count();
int finalUserData = (userData == -1) ? newIndex : userData;
// Store link internally (create a copy)
auto* newLink = new App::PropertyLinkSub();
newLink->Paste(lnk);
linksInList.push_back(newLink);
// Add to combo box
_combo->addItem(itemText, QVariant(finalUserData));
// Track document context from the first valid object link
if (!doc && newLink->getValue()) {
doc = newLink->getValue()->getDocument();
}
return newIndex;
}
int ComboLinks::addLink(App::DocumentObject* linkObj, const std::string& linkSubname, const QString& itemText, int userData)
{
App::PropertyLinkSub lnk;
std::vector<std::string> sub = { linkSubname };
// Handle empty subname correctly
if (linkSubname.empty()) {
sub.clear();
}
lnk.setValue(linkObj, sub);
return addLink(lnk, itemText, userData);
}
int ComboLinks::addLinkBefore(const App::PropertyLinkSub& lnk, const QString& itemText, int targetUserData, int userData)
{
if (!_combo) {
return -1;
}
int insertPos = -1;
for (int i = 0; i < _combo->count(); ++i) {
if (_combo->itemData(i).toInt() == targetUserData) {
insertPos = i;
break;
}
}
// Store link internally (create a copy)
auto* newLink = new App::PropertyLinkSub();
newLink->Paste(lnk);
int finalUserData = (userData == -1) ? ((insertPos == -1) ? count() : insertPos) : userData;
if (insertPos != -1) {
linksInList.insert(linksInList.begin() + insertPos, newLink);
_combo->insertItem(insertPos, itemText, QVariant(finalUserData));
}
else {
// Target not found, append to end
insertPos = _combo->count();
linksInList.push_back(newLink);
_combo->addItem(itemText, QVariant(finalUserData));
}
// Update user data for subsequent items if default (-1) was used and inserting
if (userData == -1 && insertPos != -1 && insertPos < count() - 1) {
for (int i = insertPos + 1; i < count(); ++i) {
if (_combo->itemData(i).toInt() == i - 1) { // Check if it was using default index
_combo->setItemData(i, QVariant(i));
}
}
}
// Track document context
if (!doc && newLink->getValue()) {
doc = newLink->getValue()->getDocument();
}
return insertPos;
}
void ComboLinks::clear()
{
if (_combo) {
// Block signals while clearing to prevent issues if connected elsewhere
bool wasBlocked = _combo->signalsBlocked();
_combo->blockSignals(true);
_combo->clear();
_combo->blockSignals(wasBlocked);
}
for (App::PropertyLinkSub* linkPtr : linksInList) {
delete linkPtr; // Delete the objects pointed to
}
linksInList.clear(); // Clear the vector itself
doc = nullptr; // Reset document context
}
App::PropertyLinkSub& ComboLinks::getLink(int index) const
{
if (index < 0 || static_cast<size_t>(index) >= linksInList.size()) {
throw Base::IndexError("ComboLinks::getLink: Index out of range");
}
App::PropertyLinkSub* linkPtr = linksInList[static_cast<size_t>(index)];
// Perform validity check only if we have a document context and a linked object
if (doc && linkPtr->getValue() && !(doc->isIn(linkPtr->getValue()))) {
throw Base::ValueError("Linked object is not in the document; it may have been deleted");
}
return *linkPtr;
}
App::PropertyLinkSub& ComboLinks::getCurrentLink() const
{
assert(_combo);
return getLink(_combo->currentIndex());
}
int ComboLinks::getUserData(int index) const
{
if (!_combo || index < 0 || index >= _combo->count()) {
return -1; // Indicate invalid index or no combo
}
return _combo->itemData(index).toInt();
}
int ComboLinks::getCurrentUserData() const
{
if (!_combo) {
return -1;
}
return _combo->currentData().toInt();
}
int ComboLinks::setCurrentLink(const App::PropertyLinkSub& lnk)
{
if (!_combo) {
return -1;
}
for (size_t i = 0; i < linksInList.size(); ++i) {
const App::PropertyLinkSub& it = *(linksInList[i]);
// Compare object pointer and sub-values vector
if (lnk.getValue() == it.getValue() && lnk.getSubValues() == it.getSubValues()) {
bool wasBlocked = _combo->signalsBlocked();
_combo->blockSignals(true);
_combo->setCurrentIndex(static_cast<int>(i));
_combo->blockSignals(wasBlocked);
return static_cast<int>(i);
}
}
return -1; // Not found
}
int ComboLinks::count() const
{
return _combo ? _combo->count() : 0;
}
} // namespace Gui

172
src/Gui/ComboLinks.h Normal file
View File

@@ -0,0 +1,172 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2025 AstoCAD <hello@astocad.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef GUI_COMBOLINKS_H
#define GUI_COMBOLINKS_H
#include <vector>
#include <string>
#include <App/PropertyLinks.h> // For PropertyLinkSub
#include <Base/Exception.h> // For exceptions
// Forward declarations
class QComboBox;
class QString;
namespace App {
class Document;
class DocumentObject;
}
namespace Gui {
/**
* @brief The ComboLinks class is a helper class that binds to a QComboBox and
* provides an interface to add App::PropertyLinkSub items, retrieve links,
* and select items by link value.
*/
class GuiExport ComboLinks
{
public:
/**
* @brief Constructor. Binds to an existing QComboBox.
* @param combo The combo box to manage. It will be cleared upon binding.
* Do not add/remove items directly to/from the combo after binding.
*/
explicit ComboLinks(QComboBox& combo);
/**
* @brief Default constructor. Use setCombo() later.
*/
ComboLinks() = default;
/**
* @brief Destructor. Clears internal resources.
*/
~ComboLinks();
// Disable copy/move semantics for simplicity with pointer management
ComboLinks(const ComboLinks&) = delete;
ComboLinks& operator=(const ComboLinks&) = delete;
ComboLinks(ComboLinks&&) = delete;
ComboLinks& operator=(ComboLinks&&) = delete;
/**
* @brief Binds the helper to a QComboBox. Clears the combo box.
* @param combo The combo box to manage.
*/
void setCombo(QComboBox& combo);
/**
* @brief Adds an item to the combo box associated with a PropertyLinkSub.
* @param lnk The link data. Can point to nullptr (e.g., for "Select reference...").
* @param itemText The text to display in the combo box.
* @param userData Optional integer data associated with the item (default: item index).
* @return The index of the added item.
*/
int addLink(const App::PropertyLinkSub& lnk, const QString& itemText, int userData = -1);
/**
* @brief Adds an item to the combo box associated with specific object and subname.
* @param linkObj The document object (can be nullptr).
* @param linkSubname The sub-element name (e.g., "Edge1", "Face2", "X_Axis").
* @param itemText The text to display in the combo box.
* @param userData Optional integer data associated with the item (default: item index).
* @return The index of the added item.
*/
int addLink(App::DocumentObject* linkObj, const std::string& linkSubname, const QString& itemText, int userData = -1);
/**
* @brief Adds an item *before* an item identified by its user data.
* Useful for inserting custom items before a standard item like "Select reference...".
* If targetUserData is not found, appends to the end.
*
* @param lnk The link data.
* @param itemText The text to display.
* @param targetUserData The user data of the item to insert before.
* @param userData Optional integer data for the new item (default: item index).
* @return The index of the inserted item.
*/
int addLinkBefore(const App::PropertyLinkSub& lnk, const QString& itemText, int targetUserData, int userData = -1);
/**
* @brief Clears all items from the combo box and the internal list.
*/
void clear();
/**
* @brief Gets the PropertyLinkSub associated with the item at the given index.
* @param index The index of the item.
* @return A reference to the stored PropertyLinkSub.
* @throws Base::IndexError if index is out of range.
* @throws Base::ValueError if the linked object exists but is not in the tracked document.
*/
App::PropertyLinkSub& getLink(int index) const;
/**
* @brief Gets the PropertyLinkSub associated with the currently selected item.
* @return A reference to the stored PropertyLinkSub.
* @throws Base::IndexError if no item is selected or combo is invalid.
* @throws Base::ValueError if linked object validity check fails.
*/
App::PropertyLinkSub& getCurrentLink() const;
/**
* @brief Gets the user data associated with the item at the given index.
* @param index The index of the item.
* @return The user data. Returns -1 if index is invalid.
*/
int getUserData(int index) const;
/**
* @brief Gets the user data associated with the currently selected item.
* @return The user data. Returns -1 if no item is selected or combo is invalid.
*/
int getCurrentUserData() const;
/**
* @brief Selects the item whose PropertyLinkSub matches the provided link.
* Blocks combo box signals during the operation.
* @param lnk The link to match.
* @return The index of the selected item, or -1 if no match is found.
*/
int setCurrentLink(const App::PropertyLinkSub& lnk);
/**
* @brief Gets the number of items in the combo box.
* @return Item count.
*/
int count() const;
private:
QComboBox* _combo = nullptr;
App::Document* doc = nullptr; // Document context for validation
std::vector<App::PropertyLinkSub*> linksInList; // Owned pointers
};
} // namespace Gui
#endif // GUI_COMBOLINKS_H

View File

@@ -92,7 +92,7 @@ private:
std::unique_ptr<Ui_TaskLinearPatternParameters> ui;
QTimer* updateViewTimer = nullptr;
ComboLinks dirLinks;
Gui::ComboLinks dirLinks;
};

View File

@@ -73,7 +73,7 @@ private:
void getMirrorPlane(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
private:
ComboLinks planeLinks;
Gui::ComboLinks planeLinks;
std::unique_ptr<Ui_TaskMirroredParameters> ui;
};

View File

@@ -93,7 +93,7 @@ private:
std::unique_ptr<Ui_TaskPolarPatternParameters> ui;
QTimer* updateViewTimer = nullptr;
ComboLinks axesLinks;
Gui::ComboLinks axesLinks;
};

View File

@@ -638,84 +638,3 @@ bool TaskDlgTransformedParameters::reject()
#include "moc_TaskTransformedParameters.cpp"
ComboLinks::ComboLinks(QComboBox& combo)
: _combo(&combo)
{
_combo->clear();
}
int ComboLinks::addLink(const App::PropertyLinkSub& lnk, QString const& itemText)
{
if (!_combo) {
return 0;
}
_combo->addItem(itemText);
this->linksInList.push_back(new App::PropertyLinkSub());
App::PropertyLinkSub& newitem = *(linksInList[linksInList.size() - 1]);
newitem.Paste(lnk);
if (newitem.getValue() && !this->doc) {
this->doc = newitem.getValue()->getDocument();
}
return linksInList.size() - 1;
}
int ComboLinks::addLink(App::DocumentObject* linkObj,
std::string const& linkSubname,
QString const& itemText)
{
if (!_combo) {
return 0;
}
_combo->addItem(itemText);
this->linksInList.push_back(new App::PropertyLinkSub());
App::PropertyLinkSub& newitem = *(linksInList[linksInList.size() - 1]);
newitem.setValue(linkObj, std::vector<std::string>(1, linkSubname));
if (newitem.getValue() && !this->doc) {
this->doc = newitem.getValue()->getDocument();
}
return linksInList.size() - 1;
}
void ComboLinks::clear()
{
for (size_t i = 0; i < this->linksInList.size(); i++) {
delete linksInList[i];
}
if (this->_combo) {
_combo->clear();
}
}
App::PropertyLinkSub& ComboLinks::getLink(int index) const
{
if (index < 0 || index > static_cast<int>(linksInList.size()) - 1) {
throw Base::IndexError("ComboLinks::getLink:Index out of range");
}
if (linksInList[index]->getValue() && doc && !(doc->isIn(linksInList[index]->getValue()))) {
throw Base::ValueError("Linked object is not in the document; it may have been deleted");
}
return *(linksInList[index]);
}
App::PropertyLinkSub& ComboLinks::getCurrentLink() const
{
assert(_combo);
return getLink(_combo->currentIndex());
}
int ComboLinks::setCurrentLink(const App::PropertyLinkSub& lnk)
{
for (size_t i = 0; i < linksInList.size(); i++) {
App::PropertyLinkSub& it = *(linksInList[i]);
if (lnk.getValue() == it.getValue() && lnk.getSubValues() == it.getSubValues()) {
bool wasBlocked = _combo->signalsBlocked();
_combo->blockSignals(true);
_combo->setCurrentIndex(i);
_combo->blockSignals(wasBlocked);
return i;
}
}
return -1;
}

View File

@@ -24,8 +24,7 @@
#ifndef GUI_TASKVIEW_TaskTransformedParameters_H
#define GUI_TASKVIEW_TaskTransformedParameters_H
#include <QComboBox>
#include <Gui/ComboLinks.h>
#include <Gui/DocumentObserver.h>
#include <Gui/Selection/Selection.h>
#include <Gui/TaskView/TaskView.h>
@@ -55,78 +54,6 @@ namespace PartDesignGui
class TaskMultiTransformParameters;
/**
* @brief The ComboLinks class is a helper class that binds to a combo box and
* provides an interface to add links, retrieve links and select items by link
* value
*/
class ComboLinks
{
public:
/**
* @brief ComboLinks constructor.
* @param combo. It will be cleared as soon as it is bound. Don't add or
* remove items from the combo directly, otherwise internal tracking list
* will go out of sync, and crashes may result.
*/
explicit ComboLinks(QComboBox& combo);
ComboLinks() = default;
void setCombo(QComboBox& combo)
{
assert(!_combo);
this->_combo = &combo;
_combo->clear();
}
/**
* @brief addLink adds an item to the combo. Doesn't check for duplicates.
* @param lnk can be a link to NULL, which is usually used for special item "Select Reference"
* @param itemText
* @return
*/
int addLink(const App::PropertyLinkSub& lnk, QString const& itemText);
int addLink(App::DocumentObject* linkObj, std::string const& linkSubname, QString const& itemText);
void clear();
App::PropertyLinkSub& getLink(int index) const;
/**
* @brief getCurrentLink
* @return the link corresponding to the selected item. May be null link,
* which is usually used to indicate a "Select reference..." special item.
* Otherwise, the link is automatically tested for validity (oif an object
* doesn't exist in the document, an exception will be thrown.)
*/
App::PropertyLinkSub& getCurrentLink() const;
/**
* @brief setCurrentLink selects the item with the link that matches the
* argument. If there is no such link in the list, -1 is returned and
* selected item is not changed. Signals from combo are blocked in this
* function.
* @param lnk
* @return the index of an item that was selected, -1 if link is not in the list yet.
*/
int setCurrentLink(const App::PropertyLinkSub& lnk);
QComboBox& combo() const
{
assert(_combo);
return *_combo;
}
~ComboLinks()
{
_combo = nullptr;
clear();
}
private:
QComboBox* _combo = nullptr;
App::Document* doc = nullptr;
std::vector<App::PropertyLinkSub*> linksInList;
};
/**
The transformed subclasses will be used in two different modes:
1. As a stand-alone feature