#include "pivariant_edit_widgets.h" #include "evalspinbox.h" #include "pivaluetree.h" #include "pivariant_edit_enum.h" #include "pivarianttypes.h" #include "scroll_spin_box.h" #include "spinslider.h" #include #include #include #include #include REGISTER_PIVARIANTEDITOR(bool, PIVariantEditors::Bool); REGISTER_PIVARIANTEDITOR(short, PIVariantEditors::Int); REGISTER_PIVARIANTEDITOR(ushort, PIVariantEditors::Int); REGISTER_PIVARIANTEDITOR(int, PIVariantEditors::Int); REGISTER_PIVARIANTEDITOR(uint, PIVariantEditors::Int); REGISTER_PIVARIANTEDITOR(float, PIVariantEditors::Double); REGISTER_PIVARIANTEDITOR(double, PIVariantEditors::Double); REGISTER_PIVARIANTEDITOR(PIString, PIVariantEditors::String); REGISTER_PIVARIANTEDITOR(PIStringList, PIVariantEditors::StringList); REGISTER_PIVARIANTEDITOR(PITime, PIVariantEditors::Time); REGISTER_PIVARIANTEDITOR(PIDate, PIVariantEditors::Date); REGISTER_PIVARIANTEDITOR(PIDateTime, PIVariantEditors::DateTime); REGISTER_PIVARIANTEDITOR(PIVariantTypes::Color, PIVariantEditors::Color); REGISTER_PIVARIANTEDITOR(PIVariantTypes::Enum, PIVariantEditors::Enum); REGISTER_PIVARIANTEDITOR(PINetworkAddress, PIVariantEditors::NetworkAddress); REGISTER_PIVARIANTEDITOR(PIVariantTypes::File, PIVariantEditors::File); REGISTER_PIVARIANTEDITOR(PIVariantTypes::Dir, PIVariantEditors::Dir); using Attribute = PIValueTree::Attribute; // PIVariantEditors::NumberBase PIVariantEditors::NumberBase::NumberBase() {} void PIVariantEditors::NumberBase::setValueNumeric(double v) { switch (type) { case tSpinBox: { auto * w = qobject_cast(widget); if (w) w->setValue(v); } break; case tSlider: { auto * w = qobject_cast(widget); if (w) w->setValue(v); } break; case tSpinSlider: { auto * w = qobject_cast(widget); if (w) w->setValue(v); } break; case tEvalSpinBox: { auto * w = qobject_cast(widget); if (w) { PIString vs = PIString::fromNumber(v), es = PIString::fromNumber(w->value()); if (vs != es) w->setValue(v); } } break; case tScrollSpinBox: { auto * w = qobject_cast(widget); if (w) w->setValue(v); } break; default: break; } } double PIVariantEditors::NumberBase::valueNumeric() const { switch (type) { case tSpinBox: { auto * w = qobject_cast(widget); if (w) return w->value(); } break; case tSlider: { auto * w = qobject_cast(widget); if (w) return w->value(); } break; case tSpinSlider: { auto * w = qobject_cast(widget); if (w) return w->value(); } break; case tEvalSpinBox: { auto * w = qobject_cast(widget); if (w) return w->value(); } break; case tScrollSpinBox: { auto * w = qobject_cast(widget); if (w) return w->value(); } break; default: break; } return 0.; } PIVariantMap PIVariantEditors::NumberBase::attributes() const { auto etype = createTypes(); etype.selectValue(type); PIVariantMap ret = { {Attribute::widgetType, etype }, {Attribute::prefix, prefix}, {Attribute::suffix, suffix}, }; switch (type) { case tSpinBox: { auto * w = qobject_cast(widget); if (w) { ret[Attribute::minimum] = w->minimum(); ret[Attribute::maximum] = w->maximum(); ret[Attribute::singleStep] = w->singleStep(); if (!is_int) ret[Attribute::decimals] = w->decimals(); } } break; case tSlider: { auto * w = qobject_cast(widget); if (w) { ret[Attribute::minimum] = w->minimum(); ret[Attribute::maximum] = w->maximum(); ret[Attribute::singleStep] = w->singleStep(); } } break; case tSpinSlider: { auto * w = qobject_cast(widget); if (w) { ret[Attribute::minimum] = w->minimum(); ret[Attribute::maximum] = w->maximum(); ret[Attribute::singleStep] = w->singleStep(); if (!is_int) ret[Attribute::decimals] = w->decimals(); } } break; case tEvalSpinBox: { auto * w = qobject_cast(widget); if (w) { ret[Attribute::expression] = Q2PIString(w->expression()); ret[Attribute::singleStep] = w->singleStep(); ret[Attribute::decimals] = w->precision(); } } break; case tScrollSpinBox: { auto * w = qobject_cast(widget); if (w) { ret[Attribute::minimum] = w->minimum(); ret[Attribute::maximum] = w->maximum(); } } break; default: break; } return ret; } PIVariantMap PIVariantEditors::NumberBase::defaultAttributes() { return { {Attribute::widgetType, createTypes() }, {Attribute::minimum, -std::numeric_limits::max()}, {Attribute::maximum, std::numeric_limits::max() }, {Attribute::singleStep, 1. }, {Attribute::decimals, 3 }, {Attribute::prefix, "" }, {Attribute::suffix, "" }, }; } PIVariantTypes::Enum PIVariantEditors::NumberBase::createTypes() { PIVariantTypes::Enum ret; ret << PIVariantTypes::Enumerator(tSpinBox, "spin box") << PIVariantTypes::Enumerator(tSlider, "slider") << PIVariantTypes::Enumerator(tSpinSlider, "spin-slider") << PIVariantTypes::Enumerator(tEvalSpinBox, "eval spin box") << PIVariantTypes::Enumerator(tScrollSpinBox, "scroll spin box"); ret.selectValue(tSpinBox); return ret; } void PIVariantEditors::NumberBase::retranslate() { switch (type) { case tSpinBox: { auto * w = qobject_cast(widget); if (!w) return; w->setPrefix(PIVariantEditorBase::vtTr(prefix)); w->setSuffix(PIVariantEditorBase::vtTr(suffix)); } break; case tSpinSlider: { auto * w = qobject_cast(widget); if (!w) return; w->setPrefix(PIVariantEditorBase::vtTr(prefix)); w->setSuffix(PIVariantEditorBase::vtTr(suffix)); } break; default: break; } } void PIVariantEditors::NumberBase::applyAttributes(const PIVariantMap & a) { bool ro = a.value(Attribute::readOnly, false).toBool(); Type new_type = static_cast(a.value(Attribute::widgetType).toEnum().selectedValue()); if (new_type == tInvalid) new_type = tSpinBox; if (type != new_type) { type = new_type; if (widget) delete widget; widget = nullptr; // clang-format off switch (type) { case tSpinBox : widget = new QDoubleSpinBox(); break; case tSlider : widget = new QSlider (Qt::Horizontal); break; case tSpinSlider : widget = new SpinSlider (); break; case tEvalSpinBox : widget = new EvalSpinBox (); break; case tScrollSpinBox: widget = new ScrollSpinBox (); break; default: break; } // clang-format on if (!widget) return; widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); layout()->addWidget(widget); } prefix = a.value(Attribute::prefix).toString(); suffix = a.value(Attribute::suffix).toString(); double min = a.value(Attribute::minimum, -65535).toDouble(); double max = a.value(Attribute::maximum, 65535).toDouble(); double step = a.value(Attribute::singleStep, 1.).toDouble(); int dec = is_int ? 0 : a.value(Attribute::decimals, 2).toInt(); switch (type) { case tSpinBox: { auto * w = qobject_cast(widget); if (!w) return; w->setReadOnly(ro); w->setPrefix(PIVariantEditorBase::vtTr(prefix)); w->setSuffix(PIVariantEditorBase::vtTr(suffix)); w->setRange(min, max); w->setSingleStep(step); w->setDecimals(dec); } break; case tSlider: { auto * w = qobject_cast(widget); if (!w) return; w->setEnabled(!ro); w->setTickInterval(piRoundd(piMaxd(1., (max - min) / 100.))); w->setRange(min, max); w->setSingleStep(step); } break; case tSpinSlider: { auto * w = qobject_cast(widget); if (!w) return; w->setReadOnly(ro); w->setPrefix(PIVariantEditorBase::vtTr(prefix)); w->setSuffix(PIVariantEditorBase::vtTr(suffix)); w->setMinimum(min); w->setMaximum(max); w->setSingleStep(step); w->setDecimals(dec); } break; case tEvalSpinBox: { auto * w = qobject_cast(widget); if (!w) return; w->setReadOnly(ro); w->setSingleStep(step); w->setPrecision(dec); w->setExpression(PI2QString(a.value(Attribute::expression).toString())); } break; case tScrollSpinBox: { auto * w = qobject_cast(widget); if (!w) return; w->setReadOnly(ro); w->setMinimum(min); w->setMaximum(max); } break; default: break; } } // PIVariantEditors::Bool void PIVariantEditors::Bool::applyAttributes(const PIVariantMap & a) { widget->setEnabled(!a.value(Attribute::readOnly, !widget->isEnabled()).toBool()); } // PIVariantEditors::Int // PIVariantEditors::Double // PIVariantEditors::String PIVariantMap PIVariantEditors::String::attributes() const { return {}; } PIVariantMap PIVariantEditors::String::defaultAttributes() { return {}; } void PIVariantEditors::String::applyAttributes(const PIVariantMap & a) { widget->setReadOnly(a.value(Attribute::readOnly, widget->isReadOnly()).toBool()); } // PIVariantEditors::StringList PIVariantEditors::StringList::StringList() { combo = new EComboBox(this); combo->setEditable(true); combo->setLineEdit(new CLineEdit); combo->setInsertPolicy(QComboBox::NoInsert); layout()->setContentsMargins(0, 0, 0, 0); layout()->addWidget(combo); auto newButton = [this](QString icon, QString tooltip) { auto * b = new QToolButton(this); b->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); b->setIcon(QIcon(icon)); b->setToolTip(tooltip); layout()->addWidget(b); return b; }; butt_apply = newButton(":/icons/list-edit-apply.png", tr("Apply")); butt_add = newButton(":/icons/list-add.png", tr("Add")); butt_del = newButton(":/icons/list-remove.png", tr("Remove")); butt_clear = newButton(":/icons/edit-clear.png", tr("Clear")); connect(combo->lineEdit(), SIGNAL(returnPressed()), butt_apply, SLOT(click())); connect(butt_apply, &QToolButton::clicked, [this]() { int ci = combo->currentIndex(); if (ci < 0) return; combo->setItemText(ci, combo->currentText()); }); connect(butt_add, &QToolButton::clicked, [this]() { combo->addItem(combo->currentText()); }); connect(butt_del, &QToolButton::clicked, [this]() { if (combo->currentIndex() < 0) return; combo->removeItem(combo->currentIndex()); }); connect(butt_clear, &QToolButton::clicked, [this]() { if (QMessageBox::question(nullptr, tr("Clear All"), tr("Clear All?"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) setValue(PIStringList()); }); } void PIVariantEditors::StringList::setValue(const PIVariant & v) { int pi = combo->currentIndex(); combo->clear(); combo->addItems(PI2QStringList(v.toStringList())); if (combo->count() > 0) { if (pi < combo->count() && pi >= 0) { combo->setCurrentIndex(pi); } else { combo->setCurrentIndex(0); } } } PIVariant PIVariantEditors::StringList::value() const { QStringList l; for (int i = 0; i < combo->count(); ++i) l << combo->itemText(i); return Q2PIStringList(l); } PIVariantMap PIVariantEditors::StringList::attributes() const { return { {Attribute::readOnly, !combo->isEditable()}, }; } void PIVariantEditors::StringList::applyAttributes(const PIVariantMap & a) { bool ro = a.value(Attribute::readOnly, !combo->isEditable()).toBool(); combo->setEditable(!ro); butt_apply->setEnabled(!ro); butt_add->setEnabled(!ro); butt_del->setEnabled(!ro); butt_clear->setEnabled(!ro); } void PIVariantEditors::StringList::retranslate() { butt_apply->setToolTip(tr("Apply")); butt_add->setToolTip(tr("Add")); butt_del->setToolTip(tr("Remove")); butt_clear->setToolTip(tr("Clear")); } // PIVariantEditors::Color PIVariantMap PIVariantEditors::Color::attributes() const { return { {"useAlpha", widget->useAlphaChannel()}, }; } PIVariantMap PIVariantEditors::Color::defaultAttributes() { return { {"useAlpha", true}, }; } void PIVariantEditors::Color::applyAttributes(const PIVariantMap & a) { widget->setUseAlphaChannel(a.value("useAlpha", widget->useAlphaChannel()).toBool()); widget->setEnabled(!a.value(Attribute::readOnly, !widget->isEnabled()).toBool()); } // PIVariantEditors::Time void PIVariantEditors::Time::applyAttributes(const PIVariantMap & a) { widget->setReadOnly(a.value(Attribute::readOnly, widget->isReadOnly()).toBool()); } // PIVariantEditors::Date void PIVariantEditors::Date::applyAttributes(const PIVariantMap & a) { widget->setReadOnly(a.value(Attribute::readOnly, widget->isReadOnly()).toBool()); } // PIVariantEditors::DateTime void PIVariantEditors::DateTime::applyAttributes(const PIVariantMap & a) { widget->setReadOnly(a.value(Attribute::readOnly, widget->isReadOnly()).toBool()); } // PIVariantEditors::Enum PIVariantEditors::Enum::Enum() { widget = new QComboBox(); layout()->addWidget(widget); edit_widget = new QWidget(); edit_widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); edit_widget->setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); QMargins m = edit_widget->layout()->contentsMargins(); edit_widget->layout()->setContentsMargins(m.left(), 0, 0, 0); auto * b = new QToolButton(); b->setIcon(QIcon(":/icons/document-edit.png")); connect(b, &QToolButton::clicked, this, [this]() { PIValueTreeEditEnum dlg; if (!dlg.showFor(src)) return; setValue(PIVariant(dlg.ret)); }); edit_widget->layout()->addWidget(b); layout()->setSpacing(0); layout()->addWidget(edit_widget); setFullEditMode(false); } void PIVariantEditors::Enum::setValue(const PIVariant & v) { src = v.toEnum(); int sv = src.selectedValue(); widget->clear(); for (const auto & e: src.enum_list) { widget->addItem(PI2QString(e.name), e.value); if (e.value == sv) widget->setCurrentIndex(widget->count() - 1); } } PIVariant PIVariantEditors::Enum::value() const { src.selectValue(widget->currentData().toInt()); return src; } void PIVariantEditors::Enum::applyAttributes(const PIVariantMap & a) { widget->setEnabled(!a.value(Attribute::readOnly, !widget->isEnabled()).toBool()); } void PIVariantEditors::Enum::setFullEditMode(bool on) { edit_widget->setVisible(on); } // PIVariantEditors::NetworkAddress PIVariantEditors::NetworkAddress::NetworkAddress() { widget = new QLineEdit(); widget->setInputMask("000.000.000.000:00000;_"); layout()->addWidget(widget); } void PIVariantEditors::NetworkAddress::setValue(const PIVariant & v) { if (has_port) widget->setText(PI2QString(v.toNetworkAddress().toString())); else widget->setText(PI2QString(v.toNetworkAddress().ipString())); } PIVariant PIVariantEditors::NetworkAddress::value() const { return PINetworkAddress(Q2PIString(widget->text())); } PIVariantMap PIVariantEditors::NetworkAddress::attributes() const { return { {"port", has_port} }; } PIVariantMap PIVariantEditors::NetworkAddress::defaultAttributes() { return { {"port", true} }; } void PIVariantEditors::NetworkAddress::applyAttributes(const PIVariantMap & a) { has_port = a.value("port", true).toBool(); if (has_port) widget->setInputMask("000.000.000.000:00000;_"); else widget->setInputMask("000.000.000.000;_"); widget->setReadOnly(a.value(Attribute::readOnly, widget->isReadOnly()).toBool()); } // PIVariantEditors::FileBase PIVariantEditors::FileBase::FileBase() { filter = "All files(*)"; widget = new CLineEdit(); layout()->addWidget(widget); sel_widget = new QWidget(); sel_widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); sel_widget->setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); QMargins m = sel_widget->layout()->contentsMargins(); sel_widget->layout()->setContentsMargins(m.left(), 0, 0, 0); auto * b = new QToolButton(); b->setIcon(QIcon(":/icons/document-open.png")); b->setToolTip(tr("Choose") + " ..."); connect(b, &QToolButton::clicked, this, [this]() { QString ret; if (is_dir) ret = QFileDialog::getExistingDirectory(this, tr("Select directory"), widget->text()); else { if (is_save) ret = QFileDialog::getSaveFileName(this, tr("Select file"), widget->text(), PIVariantEditorBase::vtTr(filter)); else ret = QFileDialog::getOpenFileName(this, tr("Select file"), widget->text(), PIVariantEditorBase::vtTr(filter)); } if (ret.isEmpty()) return; if (!is_abs) ret = QDir::current().relativeFilePath(ret); widget->setText(ret); }); sel_widget->layout()->addWidget(b); layout()->addWidget(sel_widget); edit_widget = new QWidget(); edit_widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); edit_widget->setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); m = edit_widget->layout()->contentsMargins(); edit_widget->layout()->setContentsMargins(m.left(), 0, 0, 0); b = new QToolButton(); b->setIcon(QIcon(":/icons/document-edit.png")); b->setPopupMode(QToolButton::InstantPopup); b->setMenu(&edit_menu); edit_widget->layout()->addWidget(b); layout()->setSpacing(0); layout()->addWidget(edit_widget); } PIVariantMap PIVariantEditors::FileBase::attributes() const { return { {Attribute::filter, filter }, {Attribute::absolutePath, is_abs }, {Attribute::onlyExisting, !is_save} }; } PIVariantMap PIVariantEditors::FileBase::defaultAttributes() { return { {Attribute::filter, "" }, {Attribute::absolutePath, false}, {Attribute::onlyExisting, true } }; } void PIVariantEditors::FileBase::applyAttributes(const PIVariantMap & a) { filter = a.value(Attribute::filter).toString(); is_abs = a.value(Attribute::absolutePath).toBool(); is_save = !a.value(Attribute::onlyExisting, true).toBool(); bool ro = a.value(Attribute::readOnly).toBool(); widget->setReadOnly(ro); sel_widget->setHidden(ro); if (act_abs) act_abs->setChecked(is_abs); if (act_save) act_save->setChecked(!is_save); } void PIVariantEditors::FileBase::setFullEditMode(bool on) { edit_widget->setVisible(on); if (act_save) act_save->setVisible(on); if (act_filter) act_filter->setVisible(on); } void PIVariantEditors::FileBase::createMenu() { act_abs = edit_menu.addAction(tr("Absolute path"), this, [this](bool on) { is_abs = on; }); act_abs->setCheckable(true); act_abs->setChecked(is_abs); if (is_dir) return; act_save = edit_menu.addAction(tr("Existing only"), this, [this](bool on) { is_save = !on; }); act_save->setCheckable(true); act_save->setChecked(!is_save); act_filter = edit_menu.addAction(tr("Set filter ..."), this, [this]() { bool ok = false; QString nf = QInputDialog::getText(nullptr, tr("Select filter"), tr("Input filter:"), QLineEdit::Normal, PI2QString(filter), &ok); if (!ok) return; filter = Q2PIString(nf); }); } // PIVariantEditors::File void PIVariantEditors::File::setValue(const PIVariant & v) { widget->setText(PI2QString(v.toFile().file)); } PIVariant PIVariantEditors::File::value() const { PIVariantTypes::File v; v.file = Q2PIString(widget->text()); return v; } // PIVariantEditors::Dir void PIVariantEditors::Dir::setValue(const PIVariant & v) { widget->setText(PI2QString(v.toDir().dir)); } PIVariant PIVariantEditors::Dir::value() const { PIVariantTypes::Dir v; v.dir = Q2PIString(widget->text()); return v; }