mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Add user-defined custom properties on elements
ElementInfoWidget's fixed ~40 predefined ELMT_* keys had no way for a user to add a genuinely new element-info key, even though DiagramContext already stores/round-trips arbitrary keys generically via toXml()/fromXml(). Adds an "Ajouter une propriété personnalisée" button that appends a CustomElementInfoPartWidget row (both key and value user-editable, unlike the fixed ElementInfoPartWidget rows bound to one predefined key). The typed key is validated live against the existing DiagramContext::isKeyAcceptable() and flagged with a red border when it doesn't match, instead of silently dropping it. Any key already present on the element that isn't one of the predefined/special keys is re-displayed as a custom row on next selection. Implements the scope proposed in discussion #611.
This commit is contained in:
@@ -683,6 +683,8 @@ set(QET_SRC_FILES
|
|||||||
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.h
|
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.h
|
||||||
${QET_DIR}/sources/ui/dynamicelementtextmodel.cpp
|
${QET_DIR}/sources/ui/dynamicelementtextmodel.cpp
|
||||||
${QET_DIR}/sources/ui/dynamicelementtextmodel.h
|
${QET_DIR}/sources/ui/dynamicelementtextmodel.h
|
||||||
|
${QET_DIR}/sources/ui/customelementinfopartwidget.cpp
|
||||||
|
${QET_DIR}/sources/ui/customelementinfopartwidget.h
|
||||||
${QET_DIR}/sources/ui/elementinfopartwidget.cpp
|
${QET_DIR}/sources/ui/elementinfopartwidget.cpp
|
||||||
${QET_DIR}/sources/ui/elementinfopartwidget.h
|
${QET_DIR}/sources/ui/elementinfopartwidget.h
|
||||||
${QET_DIR}/sources/ui/elementinfowidget.cpp
|
${QET_DIR}/sources/ui/elementinfowidget.cpp
|
||||||
|
|||||||
@@ -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 "elementinfowidget.h"
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
#include <QPushButton>
|
||||||
#include "../diagram.h"
|
#include "../diagram.h"
|
||||||
#include "../qetapp.h"
|
#include "../qetapp.h"
|
||||||
#include "../qetgraphicsitem/element.h"
|
#include "../qetgraphicsitem/element.h"
|
||||||
#include "../qetinformation.h"
|
#include "../qetinformation.h"
|
||||||
#include "../ui_elementinfowidget.h"
|
#include "../ui_elementinfowidget.h"
|
||||||
#include "../undocommand/changeelementinformationcommand.h"
|
#include "../undocommand/changeelementinformationcommand.h"
|
||||||
|
#include "customelementinfopartwidget.h"
|
||||||
#include "elementinfopartwidget.h"
|
#include "elementinfopartwidget.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,6 +49,7 @@ ElementInfoWidget::ElementInfoWidget(Element *elmt, QWidget *parent) :
|
|||||||
ElementInfoWidget::~ElementInfoWidget()
|
ElementInfoWidget::~ElementInfoWidget()
|
||||||
{
|
{
|
||||||
qDeleteAll(m_eipw_list);
|
qDeleteAll(m_eipw_list);
|
||||||
|
qDeleteAll(m_custom_eipw_list);
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +210,11 @@ void ElementInfoWidget::buildInterface()
|
|||||||
ui->scroll_vlayout->addWidget(eipw);
|
ui->scroll_vlayout->addWidget(eipw);
|
||||||
m_eipw_list << 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();
|
ui->scroll_vlayout->addStretch();
|
||||||
|
|
||||||
// Existing potential isolating checkbox
|
// Existing potential isolating checkbox
|
||||||
@@ -235,6 +243,67 @@ void ElementInfoWidget::buildInterface()
|
|||||||
m_potential_isolating_cb->setVisible(false);
|
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
|
@brief ElementInfoWidget::infoPartWidgetForKey
|
||||||
@param key
|
@param key
|
||||||
@@ -271,6 +340,21 @@ void ElementInfoWidget::updateUi()
|
|||||||
for (ElementInfoPartWidget *eipw : m_eipw_list) {
|
for (ElementInfoPartWidget *eipw : m_eipw_list) {
|
||||||
eipw -> setText (element_info[eipw->key()].toString());
|
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
|
// Load the lock status for auto numbering
|
||||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||||
QString lock_value = element_info.value(QStringLiteral("auto_num_locked")).toString();
|
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
|
// Save the auto numbering lock status
|
||||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
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"));
|
info_.addValue(QStringLiteral("auto_num_locked"), ui->m_auto_num_locked_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||||
|
|||||||
@@ -26,8 +26,10 @@
|
|||||||
class Element;
|
class Element;
|
||||||
class QUndoCommand;
|
class QUndoCommand;
|
||||||
class ElementInfoPartWidget;
|
class ElementInfoPartWidget;
|
||||||
|
class CustomElementInfoPartWidget;
|
||||||
class ChangeElementInformationCommand;
|
class ChangeElementInformationCommand;
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ElementInfoWidget;
|
class ElementInfoWidget;
|
||||||
@@ -63,15 +65,20 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
|||||||
private:
|
private:
|
||||||
void buildInterface();
|
void buildInterface();
|
||||||
ElementInfoPartWidget *infoPartWidgetForKey(const QString &key) const;
|
ElementInfoPartWidget *infoPartWidgetForKey(const QString &key) const;
|
||||||
|
QStringList predefinedKeys() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void firstActivated();
|
void firstActivated();
|
||||||
void elementInfoChange();
|
void elementInfoChange();
|
||||||
|
void addCustomProperty(const QString &key = QString(), const QString &value = QString());
|
||||||
|
void removeCustomProperty(CustomElementInfoPartWidget *widget);
|
||||||
|
|
||||||
//ATTRIBUTES
|
//ATTRIBUTES
|
||||||
private:
|
private:
|
||||||
Ui::ElementInfoWidget *ui;
|
Ui::ElementInfoWidget *ui;
|
||||||
QList <ElementInfoPartWidget *> m_eipw_list;
|
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_potential_isolating_cb = nullptr;
|
||||||
QCheckBox *m_exclude_from_bom_cb = nullptr;
|
QCheckBox *m_exclude_from_bom_cb = nullptr;
|
||||||
bool m_first_activation;
|
bool m_first_activation;
|
||||||
|
|||||||
Reference in New Issue
Block a user