Part: Add PatternParametersWidget
This commit is contained in:
@@ -59,6 +59,7 @@ set(PartGui_UIC_SRCS
|
||||
DlgSettingsGeneral.ui
|
||||
DlgSettingsObjectColor.ui
|
||||
DlgProjectionOnSurface.ui
|
||||
PatternParametersWidget.ui
|
||||
SectionCutting.ui
|
||||
ShapeFromMesh.ui
|
||||
TaskFaceAppearances.ui
|
||||
@@ -145,6 +146,9 @@ SET(PartGui_SRCS
|
||||
DlgProjectionOnSurface.cpp
|
||||
DlgProjectionOnSurface.h
|
||||
DlgProjectionOnSurface.ui
|
||||
PatternParametersWidget.cpp
|
||||
PatternParametersWidget.h
|
||||
PatternParametersWidget.ui
|
||||
Resources/Part.qrc
|
||||
PreCompiled.cpp
|
||||
PreCompiled.h
|
||||
|
||||
478
src/Mod/Part/Gui/PatternParametersWidget.cpp
Normal file
478
src/Mod/Part/Gui/PatternParametersWidget.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
// 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"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QHBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QLabel>
|
||||
#endif
|
||||
|
||||
#include "ui_PatternParametersWidget.h"
|
||||
#include "PatternParametersWidget.h"
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <Gui/ComboLinks.h>
|
||||
#include <Gui/QuantitySpinBox.h>
|
||||
#include <Gui/SpinBox.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
using namespace PartGui;
|
||||
|
||||
PatternParametersWidget::PatternParametersWidget(PatternType type, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
type(type),
|
||||
ui(new Ui_PatternParametersWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setupUiElements();
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
PatternParametersWidget::~PatternParametersWidget()
|
||||
{
|
||||
// ui is managed by unique_ptr
|
||||
// Bound properties are managed externally
|
||||
// Dynamic spacing rows are deleted by clearDynamicSpacingRows or Qt's parent mechanism
|
||||
}
|
||||
|
||||
void PatternParametersWidget::setupUiElements()
|
||||
{
|
||||
// Configure UI elements if needed (e.g., icons for mode)
|
||||
QIcon icon1 = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_extent");
|
||||
QIcon icon2 = Gui::BitmapFactory().iconFromTheme("Part_LinearPattern_spacing");
|
||||
ui->comboMode->setItemIcon(0, icon1);
|
||||
ui->comboMode->setItemIcon(1, icon2);
|
||||
|
||||
if (type == PatternType::Polar) {
|
||||
setTitle(tr("Axis"));
|
||||
}
|
||||
|
||||
// Set combo box helper
|
||||
dirLinks.setCombo(*(ui->comboDirection));
|
||||
}
|
||||
|
||||
void PatternParametersWidget::connectSignals()
|
||||
{
|
||||
connect(ui->comboDirection, qOverload<int>(&QComboBox::activated),
|
||||
this, &PatternParametersWidget::onDirectionChanged);
|
||||
connect(ui->PushButtonReverse, &QToolButton::pressed,
|
||||
this, &PatternParametersWidget::onReversePressed);
|
||||
connect(ui->comboMode, qOverload<int>(&QComboBox::activated),
|
||||
this, &PatternParametersWidget::onModeChanged);
|
||||
|
||||
connect(ui->spinExtent, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
||||
this, &PatternParametersWidget::onLengthChanged);
|
||||
connect(ui->spinSpacing, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
|
||||
this, &PatternParametersWidget::onOffsetChanged);
|
||||
connect(ui->spinOccurrences, &Gui::UIntSpinBox::unsignedChanged,
|
||||
this, &PatternParametersWidget::onOccurrencesChanged);
|
||||
|
||||
// Dynamic spacing buttons
|
||||
connect(ui->addSpacingButton, &QToolButton::clicked,
|
||||
this, &PatternParametersWidget::onAddSpacingButtonClicked);
|
||||
|
||||
// Note: Connections for dynamic rows are done in addSpacingRow()
|
||||
}
|
||||
|
||||
void PatternParametersWidget::bindProperties(App::PropertyLinkSub& directionProp,
|
||||
App::PropertyBool& reversedProp,
|
||||
App::PropertyEnumeration& modeProp,
|
||||
App::PropertyQuantity& lengthProp,
|
||||
App::PropertyQuantity& offsetProp,
|
||||
App::PropertyFloatList& spacingPatternProp,
|
||||
App::PropertyIntegerConstraint& occurrencesProp,
|
||||
App::DocumentObject* feature)
|
||||
{
|
||||
// Store pointers to the properties
|
||||
m_directionProp = &directionProp;
|
||||
m_reversedProp = &reversedProp;
|
||||
m_modeProp = &modeProp;
|
||||
m_extentProp = &lengthProp;
|
||||
m_spacingProp = &offsetProp;
|
||||
m_spacingPatternProp = &spacingPatternProp;
|
||||
m_occurrencesProp = &occurrencesProp;
|
||||
m_feature = feature; // Store feature for context (units, etc.)
|
||||
|
||||
ui->spinExtent->bind(*m_extentProp);
|
||||
Base::Unit unit = type == PatternType::Linear ? Base::Unit::Length : Base::Unit::Angle;
|
||||
|
||||
ui->spinExtent->blockSignals(true);
|
||||
ui->spinExtent->setUnit(unit);
|
||||
ui->spinExtent->blockSignals(false);
|
||||
|
||||
ui->spinSpacing->bind(*m_spacingProp);
|
||||
ui->spinSpacing->blockSignals(true);
|
||||
ui->spinSpacing->setUnit(unit);
|
||||
ui->spinSpacing->blockSignals(false);
|
||||
|
||||
ui->spinOccurrences->bind(*m_occurrencesProp);
|
||||
ui->spinOccurrences->blockSignals(true);
|
||||
ui->spinOccurrences->setMaximum(m_occurrencesProp->getMaximum());
|
||||
ui->spinOccurrences->setMinimum(m_occurrencesProp->getMinimum());
|
||||
ui->spinOccurrences->blockSignals(false);
|
||||
|
||||
// Initial UI update from properties
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::addDirection(App::DocumentObject* linkObj, const std::string& linkSubname, const QString& itemText, int userData)
|
||||
{
|
||||
// Insert custom directions before "Select reference..."
|
||||
dirLinks.addLink(linkObj, linkSubname, itemText, userData);
|
||||
}
|
||||
|
||||
void PatternParametersWidget::updateUI()
|
||||
{
|
||||
if (blockUpdate || !m_feature) { // Need properties to be bound
|
||||
return;
|
||||
}
|
||||
blockUpdate = true;
|
||||
|
||||
// Update direction combo
|
||||
if (dirLinks.setCurrentLink(*m_directionProp) == -1) {
|
||||
// failed to set current, because the link isn't in the list yet
|
||||
if (m_directionProp->getValue()) {
|
||||
QString refStr = QString::fromLatin1(m_directionProp->getValue()->getNameInDocument()) + QStringLiteral(":") +
|
||||
QString::fromLatin1(m_directionProp->getSubValues().front().c_str());
|
||||
|
||||
dirLinks.addLink(*m_directionProp, refStr);
|
||||
dirLinks.setCurrentLink(*m_directionProp);
|
||||
}
|
||||
}
|
||||
|
||||
// Update other controls directly from properties
|
||||
ui->comboMode->setCurrentIndex(m_modeProp->getValue());
|
||||
ui->spinExtent->setValue(m_extentProp->getValue());
|
||||
ui->spinSpacing->setValue(m_spacingProp->getValue());
|
||||
ui->spinOccurrences->setValue(m_occurrencesProp->getValue());
|
||||
|
||||
rebuildDynamicSpacingUI();
|
||||
|
||||
adaptVisibilityToMode();
|
||||
|
||||
blockUpdate = false;
|
||||
}
|
||||
|
||||
|
||||
void PatternParametersWidget::adaptVisibilityToMode()
|
||||
{
|
||||
if (!m_modeProp) return;
|
||||
// Use the enum names defined in FeatureLinearPattern.h
|
||||
auto mode = static_cast<PartGui::PatternMode>(m_modeProp->getValue());
|
||||
|
||||
ui->extentWrapper->setVisible(mode == PartGui::PatternMode::Extent);
|
||||
ui->spacingWrapper->setVisible(mode == PartGui::PatternMode::Spacing);
|
||||
ui->spacingPatternWidget->setVisible(mode == PartGui::PatternMode::Spacing);
|
||||
ui->addSpacingButton->setVisible(mode == PartGui::PatternMode::Spacing);
|
||||
}
|
||||
|
||||
const App::PropertyLinkSub& PatternParametersWidget::getCurrentDirectionLink() const
|
||||
{
|
||||
return dirLinks.getCurrentLink();
|
||||
}
|
||||
|
||||
bool PatternParametersWidget::isSelectReferenceMode() const
|
||||
{
|
||||
return !dirLinks.getCurrentLink().getValue();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::setTitle(const QString& title)
|
||||
{
|
||||
ui->groupBox->setTitle(title);
|
||||
}
|
||||
void PatternParametersWidget::setCheckable(bool on)
|
||||
{
|
||||
ui->groupBox->setCheckable(on);
|
||||
}
|
||||
void PatternParametersWidget::setChecked(bool on)
|
||||
{
|
||||
ui->groupBox->setChecked(on);
|
||||
}
|
||||
|
||||
|
||||
// --- Slots ---
|
||||
|
||||
void PatternParametersWidget::onDirectionChanged(int /*index*/)
|
||||
{
|
||||
if (blockUpdate || !m_directionProp) return;
|
||||
|
||||
if (isSelectReferenceMode()) {
|
||||
// Emit signal for the task panel to handle reference selection
|
||||
requestReferenceSelection();
|
||||
}
|
||||
else {
|
||||
m_directionProp->Paste(dirLinks.getCurrentLink()); // Update the property
|
||||
parametersChanged(); // Notify change
|
||||
}
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onReversePressed()
|
||||
{
|
||||
if (blockUpdate || !m_reversedProp) return;
|
||||
|
||||
m_reversedProp->setValue(!m_reversedProp->getValue());
|
||||
parametersChanged();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onModeChanged(int index)
|
||||
{
|
||||
if (blockUpdate || !m_modeProp) return;
|
||||
m_modeProp->setValue(index); // Assuming enum values match index
|
||||
adaptVisibilityToMode(); // Update visibility based on new mode
|
||||
parametersChanged();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onLengthChanged(double value)
|
||||
{
|
||||
// Usually handled by bind(). If manual update needed:
|
||||
if (blockUpdate || !m_extentProp) return;
|
||||
m_extentProp->setValue(value);
|
||||
parametersChanged(); // Still emit signal even if bound
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onOffsetChanged(double value)
|
||||
{
|
||||
if (blockUpdate || !m_spacingProp || !m_spacingPatternProp) return;
|
||||
|
||||
m_spacingProp->setValue(value);
|
||||
|
||||
// Crucially, also update the *first* element of the SpacingPattern list
|
||||
std::vector<double> currentSpacings = m_spacingPatternProp->getValues();
|
||||
if (currentSpacings.empty()) {
|
||||
currentSpacings.push_back(ui->spinSpacing->value().getValue()); // Use UI value which includes units
|
||||
}
|
||||
else {
|
||||
currentSpacings[0] = ui->spinSpacing->value().getValue();
|
||||
}
|
||||
|
||||
m_spacingPatternProp->setValues(currentSpacings); // Update the property list
|
||||
parametersChanged(); // Emit signal
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onOccurrencesChanged(unsigned int value)
|
||||
{
|
||||
// Usually handled by bind(). If manual update needed:
|
||||
if (blockUpdate || !m_occurrencesProp) return;
|
||||
|
||||
m_occurrencesProp->setValue(value);
|
||||
parametersChanged(); // Still emit signal even if bound
|
||||
}
|
||||
|
||||
|
||||
// --- Dynamic Spacing Logic (Copied and adapted) ---
|
||||
|
||||
void PatternParametersWidget::clearDynamicSpacingRows()
|
||||
{
|
||||
for (Gui::QuantitySpinBox* spinBox : dynamicSpacingSpinBoxes) {
|
||||
spinBox->blockSignals(true); // Block signals during deletion
|
||||
}
|
||||
qDeleteAll(dynamicSpacingRows);
|
||||
dynamicSpacingRows.clear();
|
||||
dynamicSpacingSpinBoxes.clear();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::addSpacingRow(double value)
|
||||
{
|
||||
if (!m_spacingProp) return; // Need context for units
|
||||
|
||||
QWidget* rowWidget = new QWidget(ui->spacingPatternWidget);
|
||||
QHBoxLayout* rowLayout = new QHBoxLayout(rowWidget);
|
||||
rowLayout->setContentsMargins(0, 0, 0, 0);
|
||||
rowLayout->setSpacing(3);
|
||||
|
||||
int newIndex = dynamicSpacingRows.count();
|
||||
// Use qApp->translate for dynamic labels
|
||||
QLabel* label = new QLabel(qApp->translate("PartGui::PatternParameters", "Spacing %1").arg(newIndex + 2), rowWidget);
|
||||
|
||||
Gui::QuantitySpinBox* spinBox = new Gui::QuantitySpinBox(rowWidget);
|
||||
Base::Unit unit = type == PatternType::Linear ? Base::Unit::Length : Base::Unit::Angle;
|
||||
spinBox->setUnit(unit);
|
||||
spinBox->setKeyboardTracking(false);
|
||||
spinBox->setValue(value); // Set initial value
|
||||
|
||||
QToolButton* removeButton = new QToolButton(rowWidget);
|
||||
removeButton->setIcon(Gui::BitmapFactory().iconFromTheme("list-remove"));
|
||||
removeButton->setToolTip(qApp->translate("PartGui::PatternParameters", "Remove this spacing definition."));
|
||||
removeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||
|
||||
rowLayout->addWidget(label);
|
||||
rowLayout->addWidget(spinBox);
|
||||
rowLayout->addWidget(removeButton);
|
||||
// No need to explicitly call rowWidget->setLayout(rowLayout); QHBoxLayout constructor handles it
|
||||
|
||||
ui->spacingPatternLayout->addWidget(rowWidget);
|
||||
dynamicSpacingRows.append(rowWidget);
|
||||
dynamicSpacingSpinBoxes.append(spinBox);
|
||||
|
||||
// Connect signals for the new row
|
||||
connect(spinBox, qOverload<double>(&Gui::QuantitySpinBox::valueChanged), this, &PatternParametersWidget::onDynamicSpacingChanged);
|
||||
connect(removeButton, &QToolButton::clicked, this, [this, rowWidget]() {
|
||||
this->onRemoveSpacingButtonClicked(rowWidget);
|
||||
});
|
||||
|
||||
// rowWidget->show(); // Usually not needed when added to a visible layout
|
||||
}
|
||||
|
||||
void PatternParametersWidget::rebuildDynamicSpacingUI()
|
||||
{
|
||||
if (!m_spacingPatternProp) return;
|
||||
|
||||
std::vector<double> currentSpacings = m_spacingPatternProp->getValues();
|
||||
|
||||
clearDynamicSpacingRows(); // Clear existing dynamic UI first
|
||||
|
||||
// Start from index 1, as index 0 corresponds to ui->spinSpacing
|
||||
for (size_t i = 1; i < currentSpacings.size(); ++i) {
|
||||
// Values in PropertyFloatList are unitless. Assume they match the Offset unit.
|
||||
addSpacingRow(currentSpacings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onAddSpacingButtonClicked()
|
||||
{
|
||||
if (blockUpdate || !m_spacingProp) return;
|
||||
|
||||
// Add a new row to the UI with a default value (same as main offset)
|
||||
addSpacingRow(ui->spinSpacing->value().getValue());
|
||||
|
||||
// Update the underlying property list
|
||||
updateSpacingPatternProperty(); // This will emit parametersChanged
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onDynamicSpacingChanged()
|
||||
{
|
||||
if (blockUpdate) return;
|
||||
// Update the entire property list based on the current UI state.
|
||||
updateSpacingPatternProperty(); // This will emit parametersChanged
|
||||
}
|
||||
|
||||
void PatternParametersWidget::onRemoveSpacingButtonClicked(QWidget* rowWidget)
|
||||
{
|
||||
if (blockUpdate || !m_spacingPatternProp) return;
|
||||
|
||||
int indexToRemove = dynamicSpacingRows.indexOf(rowWidget);
|
||||
if (indexToRemove == -1) return;
|
||||
|
||||
// Remove from UI lists
|
||||
dynamicSpacingRows.removeAt(indexToRemove);
|
||||
dynamicSpacingSpinBoxes.removeAt(indexToRemove); // Just remove, widget deleted below
|
||||
|
||||
// Remove from layout and delete
|
||||
ui->spacingPatternLayout->removeWidget(rowWidget);
|
||||
delete rowWidget; // Deletes children too
|
||||
|
||||
// Update labels of subsequent rows
|
||||
for (int i = indexToRemove; i < dynamicSpacingRows.count(); ++i) {
|
||||
QWidget* row = dynamicSpacingRows.at(i);
|
||||
QLabel* label = row->findChild<QLabel*>();
|
||||
if (label) {
|
||||
label->setText(qApp->translate("PartGui::PatternParameters", "Spacing %1").arg(i + 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Update the underlying property list
|
||||
updateSpacingPatternProperty(); // This will emit parametersChanged
|
||||
}
|
||||
|
||||
void PatternParametersWidget::updateSpacingPatternProperty()
|
||||
{
|
||||
if (blockUpdate || !m_spacingPatternProp || !m_spacingProp) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<double> newSpacings;
|
||||
|
||||
// First element is always the main offset's value
|
||||
newSpacings.push_back(ui->spinSpacing->value().getValue());
|
||||
|
||||
// Add values from dynamic spin boxes
|
||||
for (Gui::QuantitySpinBox* spinBox : dynamicSpacingSpinBoxes) {
|
||||
newSpacings.push_back(spinBox->value().getValue());
|
||||
}
|
||||
|
||||
m_spacingPatternProp->setValues(newSpacings); // Set the property list
|
||||
parametersChanged(); // Emit signal after property is set
|
||||
}
|
||||
|
||||
// --- Getters ---
|
||||
|
||||
void PatternParametersWidget::getAxis(App::DocumentObject*& obj,
|
||||
std::vector<std::string>& sub) const
|
||||
{
|
||||
const App::PropertyLinkSub& lnk = dirLinks.getCurrentLink();
|
||||
obj = lnk.getValue();
|
||||
sub = lnk.getSubValues();
|
||||
}
|
||||
|
||||
bool PatternParametersWidget::getReverse() const
|
||||
{
|
||||
return m_reversedProp->getValue();
|
||||
}
|
||||
|
||||
int PatternParametersWidget::getMode() const
|
||||
{
|
||||
return ui->comboMode->currentIndex();
|
||||
}
|
||||
|
||||
double PatternParametersWidget::getExtent() const
|
||||
{
|
||||
return ui->spinExtent->value().getValue();
|
||||
}
|
||||
|
||||
double PatternParametersWidget::getSpacing() const
|
||||
{
|
||||
return ui->spinSpacing->value().getValue();
|
||||
}
|
||||
|
||||
unsigned PatternParametersWidget::getOccurrences() const
|
||||
{
|
||||
return ui->spinOccurrences->value();
|
||||
}
|
||||
|
||||
std::string PatternParametersWidget::getSpacingPatternsAsString() const
|
||||
{
|
||||
// Build the Python list string for SpacingPattern
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
const auto& spacingValues = m_spacingPatternProp->getValues();
|
||||
for (size_t i = 0; i < spacingValues.size(); ++i) {
|
||||
ss << (i > 0 ? ", " : "") << spacingValues[i];
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void PatternParametersWidget::applyQuantitySpinboxes() const
|
||||
{
|
||||
ui->spinExtent->apply();
|
||||
ui->spinSpacing->apply();
|
||||
ui->spinOccurrences->apply();
|
||||
}
|
||||
|
||||
//#include "moc_PatternParametersWidget.cpp"
|
||||
216
src/Mod/Part/Gui/PatternParametersWidget.h
Normal file
216
src/Mod/Part/Gui/PatternParametersWidget.h
Normal file
@@ -0,0 +1,216 @@
|
||||
// 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 PARTGUI_PATTERNPARAMETERSWIDGET_H
|
||||
#define PARTGUI_PATTERNPARAMETERSWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <App/PropertyStandard.h> // For Property types
|
||||
#include <App/PropertyLinks.h> // For PropertyLinkSub
|
||||
#include <Gui/ComboLinks.h>
|
||||
|
||||
#include <Mod/Part/PartGlobal.h>
|
||||
|
||||
class Ui_PatternParametersWidget;
|
||||
|
||||
namespace App {
|
||||
class PropertyLinkSub;
|
||||
class PropertyBool;
|
||||
class PropertyEnumeration;
|
||||
class PropertyQuantity;
|
||||
class PropertyFloatList;
|
||||
class PropertyIntegerConstraint;
|
||||
class DocumentObject;
|
||||
}
|
||||
namespace Gui {
|
||||
class QuantitySpinBox;
|
||||
}
|
||||
class QToolButton;
|
||||
|
||||
namespace PartGui {
|
||||
|
||||
enum class PatternType {
|
||||
Linear,
|
||||
Polar
|
||||
};
|
||||
|
||||
enum class PatternMode {
|
||||
Extent,
|
||||
Spacing
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A widget to configure the parameters for a single direction of a linear pattern.
|
||||
*
|
||||
* This widget encapsulates the UI and logic for Direction, Reverse, Mode,
|
||||
* Length/Spacing (including dynamic spacing), and Occurrences. It binds directly
|
||||
* to the corresponding properties of a Feature.
|
||||
*/
|
||||
class PartGuiExport PatternParametersWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PatternParametersWidget(PatternType type, QWidget* parent = nullptr);
|
||||
~PatternParametersWidget() override;
|
||||
|
||||
/**
|
||||
* @brief Binds the widget's UI elements to the properties of a DocumentObject.
|
||||
*
|
||||
* This must be called after creating the widget to link its controls
|
||||
* to the underlying feature's data.
|
||||
*
|
||||
* @param directionProp Reference to the Direction property (PropertyLinkSub).
|
||||
* @param reversedProp Reference to the Reversed property (PropertyBool).
|
||||
* @param modeProp Reference to the Mode property (PropertyEnumeration).
|
||||
* @param lengthProp Reference to the Length property (PropertyQuantity).
|
||||
* @param offsetProp Reference to the Offset property (PropertyQuantity).
|
||||
* @param spacingPatternProp Reference to the SpacingPattern property (PropertyFloatList).
|
||||
* @param occurrencesProp Reference to the Occurrences property (PropertyIntegerConstraint).
|
||||
* @param feature The feature object itself, needed for context (e.g., units).
|
||||
*/
|
||||
void bindProperties(App::PropertyLinkSub& directionProp,
|
||||
App::PropertyBool& reversedProp,
|
||||
App::PropertyEnumeration& modeProp,
|
||||
App::PropertyQuantity& lengthProp,
|
||||
App::PropertyQuantity& offsetProp,
|
||||
App::PropertyFloatList& spacingPatternProp,
|
||||
App::PropertyIntegerConstraint& occurrencesProp,
|
||||
App::DocumentObject* feature); // Pass feature for context
|
||||
|
||||
|
||||
/**
|
||||
* @brief Adds a custom direction option to the Direction combo box.
|
||||
*
|
||||
* Used by consuming tools (like PartDesign tasks) to add context-specific
|
||||
* directions (e.g., Sketch axes).
|
||||
*
|
||||
* @param link The PropertyLinkSub representing the custom direction.
|
||||
* @param text The user-visible text for the combo box item.
|
||||
*/
|
||||
void addDirection(App::DocumentObject* linkObj, const std::string& linkSubname, const QString& itemText, int userData = -1);
|
||||
|
||||
/**
|
||||
* @brief Updates the UI elements to reflect the current values of the bound properties.
|
||||
*/
|
||||
void updateUI();
|
||||
|
||||
/**
|
||||
* @brief Returns the currently selected direction link from the combo box.
|
||||
* Returns an empty link if "Select reference..." is chosen.
|
||||
*/
|
||||
const App::PropertyLinkSub& getCurrentDirectionLink() const;
|
||||
|
||||
/**
|
||||
* @brief Checks if the "Select reference..." item is currently selected.
|
||||
*/
|
||||
bool isSelectReferenceMode() const;
|
||||
|
||||
void getAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
|
||||
bool getReverse() const;
|
||||
int getMode() const;
|
||||
double getExtent() const;
|
||||
double getSpacing() const;
|
||||
unsigned getOccurrences() const;
|
||||
std::string getSpacingPatternsAsString() const;
|
||||
|
||||
void setTitle(const QString& title);
|
||||
void setCheckable(bool on);
|
||||
void setChecked(bool on);
|
||||
|
||||
void applyQuantitySpinboxes() const;
|
||||
|
||||
Gui::ComboLinks dirLinks;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief Emitted when the user selects the "Select reference..." option
|
||||
* in the direction combo box, indicating the need to enter selection mode.
|
||||
*/
|
||||
void requestReferenceSelection();
|
||||
|
||||
/**
|
||||
* @brief Emitted when any parameter value controlled by this widget changes
|
||||
* that requires a recompute of the feature.
|
||||
*/
|
||||
void parametersChanged();
|
||||
|
||||
|
||||
private Q_SLOTS:
|
||||
// Slots connected to UI elements
|
||||
void onDirectionChanged(int index);
|
||||
void onReversePressed();
|
||||
void onModeChanged(int index);
|
||||
// Note: Spinbox value changes are often handled by direct binding,
|
||||
// but we might need slots if extra logic (like updating SpacingPattern[0]) is needed.
|
||||
void onLengthChanged(double value);
|
||||
void onOffsetChanged(double value);
|
||||
void onOccurrencesChanged(unsigned int value);
|
||||
|
||||
// Slots for dynamic spacing
|
||||
void onAddSpacingButtonClicked();
|
||||
void onDynamicSpacingChanged(); // Simplified slot
|
||||
void onRemoveSpacingButtonClicked(QWidget* rowWidget);
|
||||
|
||||
private:
|
||||
// Initialization and setup
|
||||
void setupUiElements();
|
||||
void connectSignals();
|
||||
|
||||
// UI Update and state management
|
||||
void adaptVisibilityToMode();
|
||||
|
||||
// Dynamic spacing helpers
|
||||
void addSpacingRow(double value);
|
||||
void clearDynamicSpacingRows();
|
||||
void rebuildDynamicSpacingUI();
|
||||
void updateSpacingPatternProperty(); // Updates the property from the UI
|
||||
|
||||
std::unique_ptr<Ui_PatternParametersWidget> ui;
|
||||
|
||||
// Pointers to bound properties (raw pointers, lifetime managed externally)
|
||||
App::PropertyLinkSub* m_directionProp = nullptr;
|
||||
App::PropertyBool* m_reversedProp = nullptr;
|
||||
App::PropertyEnumeration* m_modeProp = nullptr;
|
||||
App::PropertyQuantity* m_extentProp = nullptr;
|
||||
App::PropertyQuantity* m_spacingProp = nullptr;
|
||||
App::PropertyFloatList* m_spacingPatternProp = nullptr;
|
||||
App::PropertyIntegerConstraint* m_occurrencesProp = nullptr;
|
||||
App::DocumentObject* m_feature = nullptr; // Store feature for context
|
||||
|
||||
bool blockUpdate = false; // Prevents signal loops
|
||||
|
||||
// Helpers for direction combo box
|
||||
static const int SELECT_REFERENCE_INDEX = -10; // Special index for "Select reference..."
|
||||
|
||||
// Store pointers to dynamically created widgets for removal and access
|
||||
QList<QWidget*> dynamicSpacingRows;
|
||||
QList<Gui::QuantitySpinBox*> dynamicSpacingSpinBoxes;
|
||||
|
||||
PatternType type;
|
||||
};
|
||||
|
||||
} // namespace PartGui
|
||||
|
||||
#endif // PARTGUI_PATTERNPARAMETERSWIDGET_H
|
||||
233
src/Mod/Part/Gui/PatternParametersWidget.ui
Normal file
233
src/Mod/Part/Gui/PatternParametersWidget.ui
Normal file
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PartGui::PatternParametersWidget</class>
|
||||
<widget class="QWidget" name="PartGui::PatternParametersWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>270</width>
|
||||
<height>200</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="generalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Direction</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QToolButton" name="PushButtonReverse">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reverse the direction of the pattern.</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Resources/resource.qrc">
|
||||
<normaloff>:/icons/button_sort.svg</normaloff>:/icons/button_sort.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboDirection"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Mode">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelMode">
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Extent</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spacing</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="extentWrapper" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Length">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelLength">
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::QuantitySpinBox" name="spinExtent" native="true">
|
||||
<property name="keyboardTracking" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="spacingWrapper" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Offset">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelOffset">
|
||||
<property name="text">
|
||||
<string>Spacing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::QuantitySpinBox" name="spinSpacing" native="true">
|
||||
<property name="keyboardTracking" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="addSpacingButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add spacing to create spacing patterns.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/icons/list-add.svg</normaloff>:/icons/list-add.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="spacingPatternWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="spacingPatternLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Occurrences">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelOccurrences">
|
||||
<property name="text">
|
||||
<string>Occurrences</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::UIntSpinBox" name="spinOccurrences"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::UIntSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>Gui/SpinBox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user