diff --git a/libs/piqt_widgets/pivaluetree_edit.cpp b/libs/piqt_widgets/pivaluetree_edit.cpp new file mode 100644 index 0000000..d722921 --- /dev/null +++ b/libs/piqt_widgets/pivaluetree_edit.cpp @@ -0,0 +1,253 @@ +#include "pivaluetree_edit.h" +#include "ui_pivaluetree_edit_array.h" +#include "pivariant_edit.h" +#include "piqt.h" +#include +#include +#include + + +PIValueTreeEdit::PIValueTreeEdit(QWidget * parent): QWidget(parent) { + ui = new Ui::PIValueTreeEditArray(); + grid = new GridWidgets(); + auto * lay = new QBoxLayout(QBoxLayout::TopToBottom); + lay->setContentsMargins(0, 0, 0, 0); + setLayout(lay); +} + + +PIValueTreeEdit::~PIValueTreeEdit() { + delete ui; + delete grid; +} + + +void PIValueTreeEdit::setValue(const PIValueTree & v) { + source = v; + build(); +} + + +PIValueTree PIValueTreeEdit::value() const { + applyValues(); + return source; +} + + +void PIValueTreeEdit::setGroupingEnabled(bool yes) { + is_grouping = yes; + build(); +} + + +void PIValueTreeEdit::rollback() { + build(); +} + + +void PIValueTreeEdit::clear() { + source = PIValueTree(); + removeAll(); +} + + +void PIValueTreeEdit::removeAll() { + array_edits.clear(); + value_edits.clear(); + tree_edits.clear(); + if (ui_array) { + ui->layoutArray->takeAt(0); + delete ui_array; + ui_array = nullptr; + } + QLayoutItem * child = nullptr; + while ((child = layout()->takeAt(0)) != nullptr) { + delete child; + } + grid->clear(); +} + + +void PIValueTreeEdit::build() { + removeAll(); + //piCout << source.attributes().value(PIValueTree::attributeArrayType) << array_type; + if (source.isArray()) { + ui_array = new QWidget(); + ui->setupUi(ui_array); + ui->spinCount->setRange(source.attribute(PIValueTree::attributeArrayMinCount, 0).toInt(), + source.attribute(PIValueTree::attributeArrayMaxCount, 65536).toInt()); + ui->spinCount->setValue(source.children().size_s()); + ui->widgetEdit->setVisible(source.attribute(PIValueTree::attributeArrayResize, false).toBool()); + ui->layoutArray->addWidget(grid); + uint array_type = PIVariant::typeIDFromName(source.attribute(PIValueTree::attributeArrayType).toString()); + int index = 0; + for (const auto & i: source.children()) { + auto * ve = new PIVariantEdit(); + ve->setAttributes(source.attributes()); + ve->setValue(i.value(), array_type); + grid->add(QString::number(++index), ve, PI2QString(i.comment())); + array_edits << ve; + } + connect(ui->spinCount, QOverload::of(&QSpinBox::valueChanged), this, [this,array_type](int value){ + value = piMaxi(value, 0); + for (int i = grid->rowCount() - 1; i >= value; --i) + grid->removeRow(i); + array_edits.resize(grid->rowCount()); + for (int i = grid->rowCount(); i < value; ++i) { + auto * ve = new PIVariantEdit(); + ve->setAttributes(source.attributes()); + ve->setValue(PIVariant::fromType(array_type), array_type); + grid->add(QString::number(i + 1), ve, ""); + array_edits << ve; + } + }); + layout()->addWidget(ui_array); + } else { + layout()->addWidget(grid); + for (const auto & i: source.children()) { + if (i.attribute(PIValueTree::attributeHidden, false).toBool()) continue; + if (i.attribute(PIValueTree::attributeIsLabel, false).toBool()) { + if (i.name().isEmpty()) continue; + auto * l = new QLabel(PI2QString(i.name())); + l->setAlignment(Qt::AlignCenter); + grid->add(l); + continue; + } + if (i.hasChildren() || i.isArray()) { + auto * ve = new PIValueTreeEdit(); + ve->setValue(i); + if (is_grouping) { + auto * gb = new QGroupBox(); + gb->setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); + gb->layout()->addWidget(ve); + gb->setTitle(PI2QString(i.name())); + gb->setToolTip(PI2QString(i.comment())); + gb->setCheckable(true); + gb->setChecked(true); + gb->setAlignment(Qt::AlignCenter); + connect(gb, &QGroupBox::toggled, ve, &QWidget::setVisible); + grid->add(gb); + } else { + grid->add(PI2QString(i.name()), ve, PI2QString(i.comment())); + } + tree_edits[i.name()] = ve; + } else { + auto * ve = new PIVariantEdit(); + ve->setAttributes(i.attributes()); + ve->setValue(i.value()); + grid->add(PI2QString(i.name()), ve, PI2QString(i.comment())); + value_edits[i.name()] = ve; + } + } + } +} + + +void PIValueTreeEdit::applyValues() const { + if (source.isArray()) { + if (array_edits.isNotEmpty()) + source.mergeAttributes(array_edits[0]->defaultAttributes()); + source.clearChildren(); + for (int i = 0; i < array_edits.size_s(); ++i) + source.addChild({PIString::fromNumber(i), array_edits[i]->value()}); + } else { + auto vit = value_edits.makeIterator(); + while (vit.next()) { + auto & c(source.child(vit.key())); + c.mergeAttributes(vit.value()->defaultAttributes()); + c.setValue(vit.value()->value()); + } + auto tit = tree_edits.makeIterator(); + while (tit.next()) { + auto & c(source.child(tit.key())); + if (!c.isNull()) c = tit.value()->value(); + } + } +} + + + + +void PIValueTreeEdit::GridWidgets::add(QString label, QWidget * w, const QString & comment) { + if (!label.isEmpty()) + label += ':'; + auto * l = new QLabel(label); + auto * c = new QLabel(comment); + l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + c->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + lay->addWidget(l, row_count, 0, Qt::AlignVCenter | Qt::AlignRight); + lay->addWidget(w, row_count, 1); + lay->addWidget(c, row_count, 2, Qt::AlignVCenter | Qt::AlignLeft); + widgets << l << w << c; + ++row_count; +} + + +void PIValueTreeEdit::GridWidgets::add(QWidget * w) { + lay->addWidget(w, row_count, 0, 1, -1); + widgets << w; + ++row_count; +} + + +void PIValueTreeEdit::GridWidgets::clear() { + piDeleteAllAndClear(widgets); + if (lay) + delete lay; + lay = new QGridLayout(); + lay->setContentsMargins(0, 0, 0, 0); + setLayout(lay); + row_count = 0; +} + + +void PIValueTreeEdit::GridWidgets::removeRow(int index) { + if (!lay) return; + if (row_count <= index) return; + for (int c = 0; c < lay->columnCount(); ++c) { + auto * li = lay->itemAtPosition(index, c); + if (li) { + if (li->widget()) { + widgets.removeOne(li->widget()); + delete li->widget(); + } + delete li; + } + } + --row_count; + simplify(); +} + + +void PIValueTreeEdit::GridWidgets::simplify() { + if (!lay) return; + QVector> wg; + QMap wa; + for (int r = 0; r < lay->rowCount(); ++r) { + QMap row; + for (int c = 0; c < lay->columnCount(); ++c) { + auto * li = lay->itemAtPosition(r, c); + if (!li) continue; + if (li->widget()) { + row[c] = li->widget(); + wa[li->widget()] = li->alignment(); + } + } + if (!row.isEmpty()) + wg << row; + } + delete lay; + lay = new QGridLayout(); + lay->setContentsMargins(0, 0, 0, 0); + int rindex = 0; + for (const auto & row: wg) { + QMapIterator it(row); + while (it.hasNext()) { + it.next(); + lay->addWidget(it.value(), rindex, it.key(), wa.value(it.value())); + } + ++rindex; + } + setLayout(lay); +} diff --git a/libs/piqt_widgets/pivaluetree_edit.h b/libs/piqt_widgets/pivaluetree_edit.h new file mode 100644 index 0000000..5325c47 --- /dev/null +++ b/libs/piqt_widgets/pivaluetree_edit.h @@ -0,0 +1,82 @@ +/* + PIQt Utils - Qt utilites for PIP + + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + This program 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 3 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see . +*/ + +#ifndef pivaluetree_edit_H +#define pivaluetree_edit_H + +#include +#include +#include "pivaluetree.h" +#include "qad_piqt_utils_export.h" + +class QGridLayout; +class PIVariantEdit; +namespace Ui { + class PIValueTreeEditArray; +} + +class QAD_PIQT_UTILS_EXPORT PIValueTreeEdit: public QWidget { +public: + PIValueTreeEdit(QWidget * parent = nullptr); + ~PIValueTreeEdit(); + + enum TreeStyle { + Label + }; + + void setValue(const PIValueTree & v); + PIValueTree value() const; + + void setGroupingEnabled(bool yes); + + void rollback(); + void clear(); + +private: + void removeAll(); + void build(); + void applyValues() const; + + class GridWidgets: public QWidget { + public: + int rowCount() const {return row_count;} + void removeRow(int index); + void add(QString label, QWidget * w, const QString & comment); + void add(QWidget * w); + void clear(); + private: + void simplify(); + int row_count = 0; + QGridLayout * lay = nullptr; + QWidgetList widgets; + }; + + QWidget * ui_array = nullptr; + PIVector array_edits; + PIMap value_edits; + PIMap tree_edits; + Ui::PIValueTreeEditArray * ui; + GridWidgets * grid = nullptr; + mutable PIValueTree source; + bool is_grouping = true; + +}; + + +#endif diff --git a/libs/piqt_widgets/pivaluetree_edit_array.ui b/libs/piqt_widgets/pivaluetree_edit_array.ui new file mode 100644 index 0000000..b559aa1 --- /dev/null +++ b/libs/piqt_widgets/pivaluetree_edit_array.ui @@ -0,0 +1,102 @@ + + + PIValueTreeEditArray + + + + 0 + 0 + 387 + 345 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + + + Count: + + + + + + + 65536 + + + + + + + Qt::Horizontal + + + + 233 + 0 + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + recreateConnection() + + diff --git a/libs/piqt_widgets/pivariant_edit.cpp b/libs/piqt_widgets/pivariant_edit.cpp new file mode 100644 index 0000000..f48839c --- /dev/null +++ b/libs/piqt_widgets/pivariant_edit.cpp @@ -0,0 +1,90 @@ +#include "pivariant_edit.h" +#include "piqt.h" +#include + + +PIVariantEditorBase * PIVariantEditorBase::createEditor(uint type_id) { + auto f = factories().value(type_id, nullptr); + if (!f) return nullptr; + auto ret = f(); + ret->retranslate(); + return ret; +} + + +void PIVariantEditorBase::createBoxLayout(QBoxLayout::Direction d) { + auto * l = new QBoxLayout(d); + l->setContentsMargins(0, 0, 0, 0); + setLayout(l); +} + + +void PIVariantEditorBase::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) + retranslate(); + QWidget::changeEvent(e); +} + + +PIMap & PIVariantEditorBase::factories() { + static PIMap ret; + return ret; +} + + + + +PIVariantEdit::PIVariantEdit(QWidget * parent): QWidget(parent) { + label = new QLabel(); + label->setAlignment(Qt::AlignCenter); + auto * l = new QBoxLayout(QBoxLayout::LeftToRight); + l->setContentsMargins(0, 0, 0, 0); + setLayout(l); + setValue(PIVariant()); +} + + +PIVariantEdit::~PIVariantEdit() { + delete label; +} + + +void PIVariantEdit::setValue(const PIVariant & v, uint type_id) { + if (type_id == 0) type_id = v.typeID(); + if (current_type_id != type_id || !editor) { + if (editor) delete editor; + current_type_id = type_id; + editor = PIVariantEditorBase::createEditor(current_type_id); + if (editor) { + editor->applyAttributes(_attributes); + layout()->removeWidget(label); + layout()->addWidget(editor); + label->hide(); + } else { + label->setText(!v.isValid() ? tr("Invalid type") : tr("No editor for %1").arg(PI2QString(PIVariant::typeNameFromID(type_id)))); + layout()->addWidget(label); + label->show(); + } + } + if (!editor) return; + editor->setValue(v); +} + + +PIVariant PIVariantEdit::value() const { + if (!editor) return PIVariant(); + return editor->value(); +} + + +void PIVariantEdit::setAttributes(const PIVariantMap & a) { + _attributes = a; + if (!editor) return; + editor->applyAttributes(_attributes); +} + + +PIVariantMap PIVariantEdit::defaultAttributes() const { + if (!editor) return PIVariantMap(); + return editor->defaultAttributes(); +} diff --git a/libs/piqt_widgets/pivariant_edit.h b/libs/piqt_widgets/pivariant_edit.h new file mode 100644 index 0000000..b6fdef8 --- /dev/null +++ b/libs/piqt_widgets/pivariant_edit.h @@ -0,0 +1,91 @@ +/* + PIQt Utils - Qt utilites for PIP + + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + This program 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 3 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see . +*/ + +#ifndef pivariant_edit_H +#define pivariant_edit_H + +#include +#include +#include +#include "pivariant.h" +#include "qad_piqt_utils_export.h" + +#define REGISTER_PIVARIANTEDITOR(type_name, class_name) \ + STATIC_INITIALIZER_BEGIN \ + PIVariantEditorBase::registerEditor(PIVariant::typeIDFromName(PIStringAscii(#type_name))); \ + STATIC_INITIALIZER_END \ + +class PIVariantEdit; + + +class QAD_PIQT_UTILS_EXPORT PIVariantEditorBase: public QWidget { + friend class PIVariantEdit; +public: + virtual ~PIVariantEditorBase() {} + + virtual void setValue(const PIVariant & v) = 0; + virtual PIVariant value() const = 0; + + virtual PIVariantMap defaultAttributes() const {return PIVariantMap();} + + template + static void registerEditor(uint type_id) { + if (factories().contains(type_id)) { + piCout << "[PIVariantEditorBase::registerEditor] Editor with typeID" << type_id << "already registered, ignore"; + return; + } + factories()[type_id] = []()->PIVariantEditorBase*{return new T();}; + } + static PIVariantEditorBase * createEditor(uint type_id); + +protected: + void createBoxLayout(QBoxLayout::Direction d = QBoxLayout::LeftToRight); + + virtual void applyAttributes(const PIVariantMap & a) {} + virtual void retranslate() {} + +private: + void changeEvent(QEvent * e) override; + + static PIMap & factories(); + +}; + + +class QAD_PIQT_UTILS_EXPORT PIVariantEdit: public QWidget { +public: + PIVariantEdit(QWidget * parent = nullptr); + ~PIVariantEdit(); + + void setValue(const PIVariant & v, uint type_id = 0); + PIVariant value() const; + + void setAttributes(const PIVariantMap & a); + PIVariantMap defaultAttributes() const; + +private: + PIVariantEditorBase * editor = nullptr; + PIVariantMap _attributes; + QLabel * label; + uint current_type_id = -1; + +}; + + +#endif diff --git a/libs/piqt_widgets/pivariant_edit_widgets.cpp b/libs/piqt_widgets/pivariant_edit_widgets.cpp new file mode 100644 index 0000000..bdaa30d --- /dev/null +++ b/libs/piqt_widgets/pivariant_edit_widgets.cpp @@ -0,0 +1,49 @@ +#include "pivariant_edit_widgets.h" +#include "pivaluetree.h" +#include +#include + +REGISTER_PIVARIANTEDITOR(bool, PIVariantEditorBool); +REGISTER_PIVARIANTEDITOR( short, PIVariantEditorInt); +REGISTER_PIVARIANTEDITOR(ushort, PIVariantEditorInt); +REGISTER_PIVARIANTEDITOR( int, PIVariantEditorInt); +REGISTER_PIVARIANTEDITOR(uint, PIVariantEditorInt); +REGISTER_PIVARIANTEDITOR(float , PIVariantEditorDouble); +REGISTER_PIVARIANTEDITOR(double, PIVariantEditorDouble); + + + + +PIVariantMap PIVariantEditorInt::defaultAttributes() const { + return { + {PIValueTree::attributeMinimum, widget->minimum()}, + {PIValueTree::attributeMaximum, widget->maximum()}, + {PIValueTree::attributeSingleStep, widget->singleStep()}, + }; +} + + +void PIVariantEditorInt::applyAttributes(const PIVariantMap & a) { + widget->setRange(a.value(PIValueTree::attributeMinimum, widget->minimum()).toInt(), + a.value(PIValueTree::attributeMaximum, widget->maximum()).toInt()); + widget->setSingleStep(a.value(PIValueTree::attributeSingleStep, widget->singleStep()).toInt()); +} + + + + +PIVariantMap PIVariantEditorDouble::defaultAttributes() const { + return { + {PIValueTree::attributeMinimum, widget->minimum()}, + {PIValueTree::attributeMaximum, widget->maximum()}, + {PIValueTree::attributeSingleStep, widget->singleStep()}, + }; +} + + +void PIVariantEditorDouble::applyAttributes(const PIVariantMap & a) { + widget->setRange(a.value(PIValueTree::attributeMinimum, widget->minimum()).toDouble(), + a.value(PIValueTree::attributeMaximum, widget->maximum()).toDouble()); + widget->setSingleStep(a.value(PIValueTree::attributeSingleStep, widget->singleStep()).toDouble()); +} + diff --git a/libs/piqt_widgets/pivariant_edit_widgets.h b/libs/piqt_widgets/pivariant_edit_widgets.h new file mode 100644 index 0000000..9a88e2c --- /dev/null +++ b/libs/piqt_widgets/pivariant_edit_widgets.h @@ -0,0 +1,87 @@ +/* + PIQt Utils - Qt utilites for PIP + + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + This program 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 3 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see . +*/ + +#ifndef pivariant_edit_widgets_H +#define pivariant_edit_widgets_H + +#include "pivariant_edit.h" +#include +#include +#include + + +class QAD_PIQT_UTILS_EXPORT PIVariantEditorBool: public PIVariantEditorBase { +public: + PIVariantEditorBool() { + createBoxLayout(); + widget = new QCheckBox(); + layout()->addWidget(widget); + } + + void setValue(const PIVariant & v) override {widget->setChecked(v.toBool());} + PIVariant value() const override {return widget->isChecked();} + +private: + QCheckBox * widget; + +}; + + +class QAD_PIQT_UTILS_EXPORT PIVariantEditorInt: public PIVariantEditorBase { +public: + PIVariantEditorInt() { + createBoxLayout(); + widget = new QSpinBox(); + layout()->addWidget(widget); + } + + void setValue(const PIVariant & v) override {widget->setValue(v.toInt());} + PIVariant value() const override {return widget->value();} + PIVariantMap defaultAttributes() const override; + +private: + void applyAttributes(const PIVariantMap & a) override; + + QSpinBox * widget; + +}; + + +class QAD_PIQT_UTILS_EXPORT PIVariantEditorDouble: public PIVariantEditorBase { +public: + PIVariantEditorDouble() { + createBoxLayout(); + widget = new QDoubleSpinBox(); + widget->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); + layout()->addWidget(widget); + } + + void setValue(const PIVariant & v) override {widget->setValue(v.toDouble());} + PIVariant value() const override {return widget->value();} + PIVariantMap defaultAttributes() const override; + +private: + void applyAttributes(const PIVariantMap & a) override; + + QDoubleSpinBox * widget; + +}; + + +#endif