mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Merge pull request #642 from ispyisail/feature-custom-element-properties
Add user-defined custom properties on elements (discussion #611)
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "customelementinfopartwidget.h"
|
||||
|
||||
#include "../diagramcontext.h"
|
||||
#include "../qeticons.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QToolButton>
|
||||
|
||||
/**
|
||||
@brief CustomElementInfoPartWidget::CustomElementInfoPartWidget
|
||||
Constructor
|
||||
@param key initial key name (empty for a freshly added row)
|
||||
@param value initial value
|
||||
@param parent parent widget
|
||||
*/
|
||||
CustomElementInfoPartWidget::CustomElementInfoPartWidget(
|
||||
const QString &key,
|
||||
const QString &value,
|
||||
QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_key_edit(new QLineEdit(key, this)),
|
||||
m_value_edit(new QLineEdit(value, this)),
|
||||
m_remove_button(new QToolButton(this))
|
||||
{
|
||||
m_key_edit->setPlaceholderText(tr("nom_de_la_propriete"));
|
||||
m_key_edit->setToolTip(tr("Lettres minuscules, chiffres, tiret et underscore uniquement"));
|
||||
m_value_edit->setClearButtonEnabled(true);
|
||||
|
||||
m_remove_button->setIcon(QET::Icons::Remove);
|
||||
m_remove_button->setToolTip(tr("Supprimer cette propriété"));
|
||||
m_remove_button->setAutoRaise(true);
|
||||
|
||||
auto *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 2, 0, 2);
|
||||
layout->setVerticalSpacing(2);
|
||||
layout->setHorizontalSpacing(0);
|
||||
layout->addWidget(m_key_edit, 0, 0);
|
||||
layout->addWidget(m_value_edit, 1, 0);
|
||||
layout->addWidget(m_remove_button, 0, 1, 2, 1);
|
||||
|
||||
connect(m_key_edit, &QLineEdit::textChanged, this, &CustomElementInfoPartWidget::validateKey);
|
||||
connect(m_key_edit, &QLineEdit::textChanged, this, &CustomElementInfoPartWidget::changed);
|
||||
connect(m_value_edit, &QLineEdit::textChanged, this, &CustomElementInfoPartWidget::changed);
|
||||
connect(m_remove_button, &QToolButton::clicked, this, [this]() {
|
||||
emit removeRequested(this);
|
||||
});
|
||||
|
||||
setFocusProxy(m_key_edit);
|
||||
validateKey();
|
||||
}
|
||||
|
||||
CustomElementInfoPartWidget::~CustomElementInfoPartWidget()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@return the key name currently typed in this row
|
||||
*/
|
||||
QString CustomElementInfoPartWidget::key() const
|
||||
{
|
||||
return m_key_edit->text().trimmed();
|
||||
}
|
||||
|
||||
/**
|
||||
@return the value currently typed in this row
|
||||
*/
|
||||
QString CustomElementInfoPartWidget::value() const
|
||||
{
|
||||
return m_value_edit->text();
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if the typed key is non-empty and matches
|
||||
DiagramContext::isKeyAcceptable()
|
||||
*/
|
||||
bool CustomElementInfoPartWidget::hasValidKey() const
|
||||
{
|
||||
const QString k = key();
|
||||
return !k.isEmpty() && DiagramContext::isKeyAcceptable(k);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CustomElementInfoPartWidget::validateKey
|
||||
Flag the key field when it doesn't match the accepted format,
|
||||
instead of silently dropping it later.
|
||||
*/
|
||||
void CustomElementInfoPartWidget::validateKey()
|
||||
{
|
||||
const QString k = key();
|
||||
if (k.isEmpty() || DiagramContext::isKeyAcceptable(k)) {
|
||||
m_key_edit->setStyleSheet(QString());
|
||||
} else {
|
||||
m_key_edit->setStyleSheet(QStringLiteral("border: 1px solid red;"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CUSTOMELEMENTINFOPARTWIDGET_H
|
||||
#define CUSTOMELEMENTINFOPARTWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLineEdit;
|
||||
class QToolButton;
|
||||
|
||||
/**
|
||||
@brief The CustomElementInfoPartWidget class
|
||||
A single row letting the user define their own element information
|
||||
key/value pair, unlike ElementInfoPartWidget which is bound to one
|
||||
predefined key. The key is validated against
|
||||
DiagramContext::isKeyAcceptable() as the user types.
|
||||
*/
|
||||
class CustomElementInfoPartWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomElementInfoPartWidget(
|
||||
const QString &key = QString(),
|
||||
const QString &value = QString(),
|
||||
QWidget *parent = nullptr);
|
||||
~CustomElementInfoPartWidget() override;
|
||||
|
||||
QString key() const;
|
||||
QString value() const;
|
||||
bool hasValidKey() const;
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
void removeRequested(CustomElementInfoPartWidget *self);
|
||||
|
||||
private slots:
|
||||
void validateKey();
|
||||
|
||||
private:
|
||||
QLineEdit *m_key_edit;
|
||||
QLineEdit *m_value_edit;
|
||||
QToolButton *m_remove_button;
|
||||
};
|
||||
|
||||
#endif // CUSTOMELEMENTINFOPARTWIDGET_H
|
||||
@@ -17,12 +17,14 @@
|
||||
*/
|
||||
#include "elementinfowidget.h"
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include "../diagram.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../ui_elementinfowidget.h"
|
||||
#include "../undocommand/changeelementinformationcommand.h"
|
||||
#include "customelementinfopartwidget.h"
|
||||
#include "elementinfopartwidget.h"
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,7 @@ ElementInfoWidget::ElementInfoWidget(Element *elmt, QWidget *parent) :
|
||||
ElementInfoWidget::~ElementInfoWidget()
|
||||
{
|
||||
qDeleteAll(m_eipw_list);
|
||||
qDeleteAll(m_custom_eipw_list);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@@ -207,6 +210,11 @@ void ElementInfoWidget::buildInterface()
|
||||
ui->scroll_vlayout->addWidget(eipw);
|
||||
m_eipw_list << eipw;
|
||||
}
|
||||
|
||||
m_add_custom_property_btn = new QPushButton(tr("Ajouter une propriété personnalisée"), this);
|
||||
connect(m_add_custom_property_btn, &QPushButton::clicked, this, [this]() { addCustomProperty(); });
|
||||
ui->scroll_vlayout->addWidget(m_add_custom_property_btn);
|
||||
|
||||
ui->scroll_vlayout->addStretch();
|
||||
|
||||
// Existing potential isolating checkbox
|
||||
@@ -235,6 +243,67 @@ void ElementInfoWidget::buildInterface()
|
||||
m_potential_isolating_cb->setVisible(false);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@brief ElementInfoWidget::predefinedKeys
|
||||
@return every key this widget already exposes a dedicated row for,
|
||||
whether through ElementInfoPartWidget (the ~40 ELMT_* keys) or one
|
||||
of the standalone checkboxes. Anything present in the element's
|
||||
informations but absent from this list is a user-defined custom
|
||||
property.
|
||||
*/
|
||||
QStringList ElementInfoWidget::predefinedKeys() const
|
||||
{
|
||||
QStringList keys = (m_element.data()->elementData().m_type == ElementData::Terminal)
|
||||
? QETInformation::terminalElementInfoKeys()
|
||||
: QETInformation::elementInfoKeys();
|
||||
|
||||
keys << QStringLiteral("auto_num_locked")
|
||||
<< QStringLiteral("potential_isolating")
|
||||
<< QStringLiteral("exclude_from_bom");
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::addCustomProperty
|
||||
Append a new user-defined key/value row to the widget.
|
||||
@param key initial key, left empty for a freshly added row
|
||||
@param value initial value
|
||||
*/
|
||||
void ElementInfoWidget::addCustomProperty(const QString &key, const QString &value)
|
||||
{
|
||||
auto *widget = new CustomElementInfoPartWidget(key, value, this);
|
||||
|
||||
const int insert_index = ui->scroll_vlayout->indexOf(m_add_custom_property_btn);
|
||||
ui->scroll_vlayout->insertWidget(insert_index >= 0 ? insert_index : ui->scroll_vlayout->count(), widget);
|
||||
m_custom_eipw_list << widget;
|
||||
|
||||
connect(widget, &CustomElementInfoPartWidget::removeRequested, this, &ElementInfoWidget::removeCustomProperty);
|
||||
connect(widget, &CustomElementInfoPartWidget::changed, this, [this]() {
|
||||
if (m_live_edit) apply();
|
||||
});
|
||||
|
||||
if (key.isEmpty()) {
|
||||
widget->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::removeCustomProperty
|
||||
Remove a user-defined key/value row.
|
||||
@param widget the row to remove
|
||||
*/
|
||||
void ElementInfoWidget::removeCustomProperty(CustomElementInfoPartWidget *widget)
|
||||
{
|
||||
if (!m_custom_eipw_list.removeOne(widget))
|
||||
return;
|
||||
|
||||
ui->scroll_vlayout->removeWidget(widget);
|
||||
widget->deleteLater();
|
||||
|
||||
if (m_live_edit) apply();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::infoPartWidgetForKey
|
||||
@param key
|
||||
@@ -271,6 +340,21 @@ void ElementInfoWidget::updateUi()
|
||||
for (ElementInfoPartWidget *eipw : m_eipw_list) {
|
||||
eipw -> setText (element_info[eipw->key()].toString());
|
||||
}
|
||||
|
||||
// Rebuild the custom-property rows to match whatever
|
||||
// user-defined keys this element currently carries.
|
||||
while (!m_custom_eipw_list.isEmpty()) {
|
||||
CustomElementInfoPartWidget *w = m_custom_eipw_list.takeLast();
|
||||
ui->scroll_vlayout->removeWidget(w);
|
||||
delete w;
|
||||
}
|
||||
const auto known_keys = predefinedKeys();
|
||||
for (const QString &key : element_info.keys()) {
|
||||
if (!known_keys.contains(key)) {
|
||||
addCustomProperty(key, element_info[key].toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Load the lock status for auto numbering
|
||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||
QString lock_value = element_info.value(QStringLiteral("auto_num_locked")).toString();
|
||||
@@ -314,6 +398,17 @@ DiagramContext ElementInfoWidget::currentInfo() const
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &custom : std::as_const(m_custom_eipw_list))
|
||||
{
|
||||
if (custom->hasValidKey() && !custom->value().isEmpty())
|
||||
{
|
||||
QString txt{custom->value()};
|
||||
txt.remove(QStringLiteral("\r"));
|
||||
txt.remove(QStringLiteral("\n"));
|
||||
info_.addValue(custom->key(), txt);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the auto numbering lock status
|
||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||
info_.addValue(QStringLiteral("auto_num_locked"), ui->m_auto_num_locked_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
class Element;
|
||||
class QUndoCommand;
|
||||
class ElementInfoPartWidget;
|
||||
class CustomElementInfoPartWidget;
|
||||
class ChangeElementInformationCommand;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
|
||||
namespace Ui {
|
||||
class ElementInfoWidget;
|
||||
@@ -63,15 +65,20 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
||||
private:
|
||||
void buildInterface();
|
||||
ElementInfoPartWidget *infoPartWidgetForKey(const QString &key) const;
|
||||
QStringList predefinedKeys() const;
|
||||
|
||||
private slots:
|
||||
void firstActivated();
|
||||
void elementInfoChange();
|
||||
void addCustomProperty(const QString &key = QString(), const QString &value = QString());
|
||||
void removeCustomProperty(CustomElementInfoPartWidget *widget);
|
||||
|
||||
//ATTRIBUTES
|
||||
private:
|
||||
Ui::ElementInfoWidget *ui;
|
||||
QList <ElementInfoPartWidget *> m_eipw_list;
|
||||
QList <CustomElementInfoPartWidget *> m_custom_eipw_list;
|
||||
QPushButton *m_add_custom_property_btn = nullptr;
|
||||
QCheckBox *m_potential_isolating_cb = nullptr;
|
||||
QCheckBox *m_exclude_from_bom_cb = nullptr;
|
||||
bool m_first_activation;
|
||||
|
||||
Reference in New Issue
Block a user