/* * IdealIRC - Internet Relay Chat client * Copyright (C) 2021 Tom-Andre Barstad. * This software is licensed under the Software Attribution License. * See LICENSE for more information. */ #include "ScriptDialog.h" #include "Script/Dialog.h" #include "Script/ScriptException.h" #include "Script/ValueExtract.h" #include "Script/Builtin/ListUtils.h" // Needed for LIST_END_MAGIC #include "IdealIRC.h" #include <QDialog> #include <QLabel> #include <QPushButton> #include <QLineEdit> #include <QPlainTextEdit> #include <QSpinBox> #include <QListWidget> #include <QComboBox> #include <QTableWidget> #include <QHeaderView> #include <QTreeWidget> #include <QGroupBox> #include <QRadioButton> #include <QCheckBox> #include <QScrollBar> #include <unordered_map> #include <string> #include <stdexcept> extern IdealIRC* IIRC_MainWindow_Ptr; struct ScriptWidget { explicit ScriptWidget(Script& script_) : script(script_) {} std::unordered_map<std::string, ValueHolder> customAttrs; QWidget* widget{ nullptr }; // Pointer owned by the QDialog const DialogWidget* tmpl{ nullptr }; Script& script; }; struct ScriptDialogPriv { explicit ScriptDialogPriv(Script& script_) : dialog(new QDialog(nullptr)) , script(script_) { dialog->setAttribute(Qt::WA_DeleteOnClose, false); } std::unordered_map<std::string, ValueHolder> customAttrs; std::unordered_map<std::string, ScriptWidget> widgets; std::unique_ptr<QDialog> dialog; const Dialog* tmpl{ nullptr }; Script& script; }; namespace { template<DialogWidget::Type type, typename T> void buildWidget(T* widget, const DialogWidget& tmpl) { ValueHolder valX = tmpl.defaultAttr("x"); ValueHolder valY = tmpl.defaultAttr("y"); ValueHolder valWidth = tmpl.defaultAttr("width"); ValueHolder valHeight = tmpl.defaultAttr("height"); ValueHolder valFont = tmpl.defaultAttr("font"); ValueHolder valFontsize = tmpl.defaultAttr("fontsize"); ValueHolder valEnabled = tmpl.defaultAttr("enabled"); ValueHolder valVisible = tmpl.defaultAttr("visible"); ValueHolder valText = tmpl.defaultAttr("text"); ValueHolder valTooltip = tmpl.defaultAttr("tooltip"); auto rect = widget->geometry(); if (valX.getType() != ValueHolder::Type::Void) rect.setX(ValueExtract(valX).toInt()); if (valY.getType() != ValueHolder::Type::Void) rect.setY(ValueExtract(valY).toInt()); if (valWidth.getType() != ValueHolder::Type::Void) rect.setWidth(ValueExtract(valWidth).toInt()); if (valHeight.getType() != ValueHolder::Type::Void) rect.setHeight(ValueExtract(valHeight).toInt()); widget->setGeometry(rect); auto font = widget->font(); if (valFont.getType() != ValueHolder::Type::Void) font.setFamily(ValueExtract(valFont).toString().c_str()); if (valFontsize.getType() != ValueHolder::Type::Void) font.setPixelSize(ValueExtract(valFontsize).toInt()); widget->setFont(font); if (valEnabled.getType() != ValueHolder::Type::Void) widget->setEnabled(ValueExtract(valEnabled).toBool()); if (valVisible.getType() != ValueHolder::Type::Void) widget->setVisible(ValueExtract(valVisible).toBool()); if (valTooltip.getType() != ValueHolder::Type::Void) widget->setToolTip(ValueExtract(valHeight).toString().c_str()); using WType = DialogWidget::Type; if constexpr (type == WType::Spinbox) { ValueHolder value = tmpl.defaultAttr("value"); ValueHolder min = tmpl.defaultAttr("min"); ValueHolder max = tmpl.defaultAttr("max"); ValueHolder step = tmpl.defaultAttr("step"); if (min.getType() == ValueHolder::Type::Void) throw ScriptException("Attribute 'min' is required for Spinbox"); if (max.getType() == ValueHolder::Type::Void) throw ScriptException("Attribute 'max' is required for Spinbox"); if (min.getType() != ValueHolder::Type::Integer) throw ScriptException("Attribute 'min' for Spinbox must be a whole number"); if (max.getType() != ValueHolder::Type::Integer) throw ScriptException("Attribute 'max' for Spinbox must be a whole number"); if (value.getType() == ValueHolder::Type::Void) value = min; if (step.getType() == ValueHolder::Type::Void) step = 1; if (value.getType() != ValueHolder::Type::Integer) throw ScriptException("Attribute 'value' for Spinbox must be a whole number"); if (step.getType() != ValueHolder::Type::Integer) throw ScriptException("Attribute 'step' for Spinbox must be a whole number"); widget->setMinimum(ValueExtract(min).toInt()); widget->setMaximum(ValueExtract(max).toInt()); widget->setValue(ValueExtract(value).toInt()); widget->setSingleStep(ValueExtract(step).toInt()); } else if constexpr (type == WType::Textbox) { ValueHolder readOnly = tmpl.defaultAttr("readonly"); if (readOnly.getType() != ValueHolder::Type::Void) widget->setEnabled(ValueExtract(readOnly).toBool()); widget->setPlainText(ValueExtract(valText).toString().c_str()); } else if constexpr (type == WType::Groupbox) { ValueHolder readOnly = tmpl.defaultAttr("readonly"); if (readOnly.getType() != ValueHolder::Type::Void) widget->setEnabled(ValueExtract(readOnly).toBool()); widget->setTitle(ValueExtract(valText).toString().c_str()); } else if constexpr (type == WType::Button) { ValueHolder checkable = tmpl.defaultAttr("checkable"); ValueHolder checked = tmpl.defaultAttr("checked"); if (checkable.getType() != ValueHolder::Type::Void) widget->setCheckable(ValueExtract(checkable).toBool()); if (checked.getType() != ValueHolder::Type::Void) widget->setChecked(ValueExtract(checked).toBool()); widget->setText(ValueExtract(valText).toString().c_str()); } else if constexpr (type == WType::Checkbox) { ValueHolder checked = tmpl.defaultAttr("checked"); if (checked.getType() != ValueHolder::Type::Void) widget->setChecked(ValueExtract(checked).toBool()); widget->setText(ValueExtract(valText).toString().c_str()); } else if constexpr (type == WType::Radio) { ValueHolder checked = tmpl.defaultAttr("checked"); if (checked.getType() != ValueHolder::Type::Void) widget->setChecked(ValueExtract(checked).toBool()); widget->setText(ValueExtract(valText).toString().c_str()); } else if constexpr (type == WType::Table) { } else if constexpr (type != WType::Vscroll && type != WType::Hscroll && type != WType::Listbox && type != WType::Combobox && type != WType::Tree) { widget->setText(ValueExtract(valText).toString().c_str()); } } void widgetSetText(ScriptWidget& widget, ValueHolder& valh) { std::string text = ValueExtract(valh).toString(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Label) { auto* cw = dynamic_cast<QLabel*>(widget.widget); cw->setText(text.c_str()); } else if (widget.tmpl->type() == WType::Groupbox) { auto* cw = dynamic_cast<QGroupBox*>(widget.widget); cw->setTitle(text.c_str()); } else if (widget.tmpl->type() == WType::Textline) { auto* cw = dynamic_cast<QLineEdit*>(widget.widget); cw->setText(text.c_str()); } else if (widget.tmpl->type() == WType::Textbox) { auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget); cw->setPlainText(text.c_str()); } else if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); cw->setText(text.c_str()); } else if (widget.tmpl->type() == WType::Radio) { auto* cw = dynamic_cast<QRadioButton*>(widget.widget); cw->setText(text.c_str()); } else if (widget.tmpl->type() == WType::Checkbox) { auto* cw = dynamic_cast<QCheckBox*>(widget.widget); cw->setText(text.c_str()); } else { widget.customAttrs.insert_or_assign("text", valh); } } void widgetSetCheckable(ScriptWidget& widget, ValueHolder& valh) { bool value = ValueExtract(valh).toBool(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); cw->setCheckable(value); } else { widget.customAttrs.insert_or_assign("checkable", valh); } } void widgetSetChecked(ScriptWidget& widget, ValueHolder& valh) { bool value = ValueExtract(valh).toBool(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); cw->setChecked(value); } else if (widget.tmpl->type() == WType::Radio) { auto* cw = dynamic_cast<QRadioButton*>(widget.widget); cw->setChecked(value); } else if (widget.tmpl->type() == WType::Checkbox) { auto* cw = dynamic_cast<QCheckBox*>(widget.widget); cw->setChecked(value); } else { widget.customAttrs.insert_or_assign("checked", valh); } } void widgetSetReadOnly(ScriptWidget& widget, ValueHolder& valh) { bool value = ValueExtract(valh).toBool(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Textline) { auto* cw = dynamic_cast<QLineEdit*>(widget.widget); cw->setReadOnly(value); } else if (widget.tmpl->type() == WType::Textbox) { auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget); cw->setReadOnly(value); } else { widget.customAttrs.insert_or_assign("readonly", valh); } } void widgetSetValue(ScriptWidget& widget, ValueHolder& valh) { int value = ValueExtract(valh).toInt(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); cw->setValue(value); } else { widget.customAttrs.insert_or_assign("value", valh); } } void widgetSetMin(ScriptWidget& widget, ValueHolder& valh) { int value = ValueExtract(valh).toInt(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); cw->setMinimum(value); } else { widget.customAttrs.insert_or_assign("min", valh); } } void widgetSetMax(ScriptWidget& widget, ValueHolder& valh) { int value = ValueExtract(valh).toInt(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); cw->setMaximum(value); } else { widget.customAttrs.insert_or_assign("max", valh); } } void widgetSetStep(ScriptWidget& widget, ValueHolder& valh) { int value = ValueExtract(valh).toInt(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); cw->setSingleStep(value); } else { widget.customAttrs.insert_or_assign("step", valh); } } void widgetSetHeader(ScriptWidget& widget, ValueHolder& valh) { ValueArray& list = ValueExtract(valh).toMap(); using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Table) { auto* cw = dynamic_cast<QTableWidget*>(widget.widget); QStringList header; for (int i = 0; ; ++i) { std::string si = std::to_string(i); try { ValueHolder& val = *list.at(si).get(); switch (val.getType()) { case ValueHolder::Type::Integer: header.push_back(QString::number(ValueExtract(val).toInt())); break; case ValueHolder::Type::Real: header.push_back(QString::number(ValueExtract(val).toReal())); break; case ValueHolder::Type::Bool: header.push_back(ValueExtract(val).toBool() ? QStringLiteral("True") : QStringLiteral("False")); break; case ValueHolder::Type::String: header.push_back(ValueExtract(val).toString().c_str()); break; default: header.push_back(QStringLiteral("")); } } catch (const std::out_of_range&) { break; } } cw->setColumnCount(header.size()); cw->setHorizontalHeaderLabels(header); cw->resizeColumnsToContents(); } else { widget.customAttrs.insert_or_assign("step", valh); } } ValueHolder widgetGetText(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Label) { auto* cw = dynamic_cast<QLabel*>(widget.widget); return cw->text().toStdString(); } else if (widget.tmpl->type() == WType::Groupbox) { auto* cw = dynamic_cast<QGroupBox*>(widget.widget); return cw->title().toStdString(); } else if (widget.tmpl->type() == WType::Textline) { auto* cw = dynamic_cast<QLineEdit*>(widget.widget); return cw->text().toStdString(); } else if (widget.tmpl->type() == WType::Textbox) { auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget); return cw->toPlainText().toStdString(); } else if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); return cw->text().toStdString(); } else if (widget.tmpl->type() == WType::Radio) { auto* cw = dynamic_cast<QRadioButton*>(widget.widget); return cw->text().toStdString(); } else if (widget.tmpl->type() == WType::Checkbox) { auto* cw = dynamic_cast<QCheckBox*>(widget.widget); return cw->text().toStdString(); } else { ValueHolder val; try { val = widget.customAttrs.at("text"); } catch (...) {} return val; } } ValueHolder widgetGetCheckable(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); return cw->isCheckable(); } else { ValueHolder val; try { val = widget.customAttrs.at("checkable"); } catch (...) {} return val; } } ValueHolder widgetGetChecked(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Button) { auto* cw = dynamic_cast<QPushButton*>(widget.widget); return cw->isChecked(); } else if (widget.tmpl->type() == WType::Radio) { auto* cw = dynamic_cast<QRadioButton*>(widget.widget); return cw->isChecked(); } else if (widget.tmpl->type() == WType::Checkbox) { auto* cw = dynamic_cast<QCheckBox*>(widget.widget); return cw->isChecked(); } else { ValueHolder val; try { val = widget.customAttrs.at("checked"); } catch (...) {} return val; } } ValueHolder widgetGetReadOnly(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Textline) { auto* cw = dynamic_cast<QLineEdit*>(widget.widget); return cw->isReadOnly(); } else if (widget.tmpl->type() == WType::Textbox) { auto* cw = dynamic_cast<QPlainTextEdit*>(widget.widget); return cw->isReadOnly(); } else { ValueHolder val; try { val = widget.customAttrs.at("readonly"); } catch (...) {} return val; } } ValueHolder widgetGetValue(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); return cw->value(); } else { ValueHolder val; try { val = widget.customAttrs.at("value"); } catch (...) {} return val; } } ValueHolder widgetGetMin(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); return cw->minimum(); } else { ValueHolder val; try { val = widget.customAttrs.at("min"); } catch (...) {} return val; } } ValueHolder widgetGetMax(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); return cw->maximum(); } else { ValueHolder val; try { val = widget.customAttrs.at("max"); } catch (...) {} return val; } } ValueHolder widgetGetStep(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Spinbox) { auto* cw = dynamic_cast<QSpinBox*>(widget.widget); return cw->singleStep(); } else { ValueHolder val; try { val = widget.customAttrs.at("step"); } catch (...) {} return val; } } ValueHolder widgetGetHeader(ScriptWidget& widget) { using WType = DialogWidget::Type; if (widget.tmpl->type() == WType::Table) { auto* cw = dynamic_cast<QTableWidget*>(widget.widget); ValueArray header; for (int i = 0; ; ++i) { auto* item = cw->horizontalHeaderItem(i); if (!item) break; header.emplace(std::to_string(i), new ValueHolder(item->text().toStdString())); } return ValueHolder(std::move(header)); } else { ValueHolder val; try { val = widget.customAttrs.at("header"); } catch (...) {} return val; } } } // anonymous namespace ScriptDialog::ScriptDialog(const Dialog& dialog, Script& script, int handle) : mp(new ScriptDialogPriv(script)) { ValueHolder valX = dialog.defaultAttr("x"); ValueHolder valY = dialog.defaultAttr("y"); ValueHolder valWidth = dialog.defaultAttr("width"); ValueHolder valHeight = dialog.defaultAttr("height"); ValueHolder valTitle = dialog.defaultAttr("title"); ValueHolder valFont = dialog.defaultAttr("font"); ValueHolder valFontsize = dialog.defaultAttr("fontsize"); ValueHolder valIcon = dialog.defaultAttr("icon"); mp->tmpl = &dialog; ValueHolder voidValue; auto IIRC_rect = IIRC_MainWindow_Ptr->geometry(); if (valWidth != voidValue) setAttr("width", valWidth); if (valHeight != voidValue) setAttr("height", valHeight); if (valX != voidValue) setAttr("x", valX); else { int width = ValueExtract(valWidth).toInt(); ValueHolder x = (2 * IIRC_rect.x() + IIRC_rect.width() - width) / 2; setAttr("x", x); } if (valY != voidValue) setAttr("y", valY); else { int height = ValueExtract(valHeight).toInt(); ValueHolder y = (2 * IIRC_rect.y() + IIRC_rect.height() - height) / 2; setAttr("y", y); } if (valTitle != voidValue) setAttr("title", valTitle); else { ValueHolder defaultTitle = std::string("Unnamed dialog"); setAttr("title", defaultTitle); } if (valFont != voidValue) setAttr("font", valFont); if (valFontsize != voidValue) setAttr("fontsize", valFontsize); if (valIcon != voidValue) setAttr("icon", valIcon); for (auto wref : dialog.allWidgets()) { auto& widget = wref.get(); mp->widgets.emplace(widget.name(), ScriptWidget(script)); auto& qw = mp->widgets.at(widget.name()); qw.tmpl = &widget; using WType = DialogWidget::Type; switch (widget.type()) { case WType::Label: { auto* w = new QLabel(mp->dialog.get()); buildWidget<WType::Label, QLabel>(w, widget); qw.widget = w; break; } case WType::Button: { auto* w = new QPushButton(mp->dialog.get()); buildWidget<WType::Button, QPushButton>(w, widget); qw.widget = w; const DialogWidget* widgetTemplate = qw.tmpl; Script* scr = &mp->script; connect(w, &QPushButton::clicked, [scr, widgetTemplate, handle]{ auto hook = widgetTemplate->hook("clicked"); if (!hook) return; SymbolScope sym; sym.set("handle", handle); if (!scr->runScopeWithSymbols(*hook, sym)) ScriptManager::printError(scr->lastError()); }); break; } case WType::Textline: { auto* w = new QLineEdit(mp->dialog.get()); buildWidget<WType::Textline, QLineEdit>(w, widget); qw.widget = w; break; } case WType::Textbox: { auto* w = new QPlainTextEdit(mp->dialog.get()); buildWidget<WType::Textbox, QPlainTextEdit>(w, widget); qw.widget = w; break; } case WType::Spinbox: { auto* w = new QSpinBox(mp->dialog.get()); buildWidget<WType::Spinbox, QSpinBox>(w, widget); qw.widget = w; break; } case WType::Listbox: { auto* w = new QListWidget(mp->dialog.get()); buildWidget<WType::Listbox, QListWidget>(w, widget); qw.widget = w; break; } case WType::Combobox: { auto* w = new QComboBox(mp->dialog.get()); buildWidget<WType::Combobox, QComboBox>(w, widget); qw.widget = w; break; } case WType::Table: { auto* w = new QTableWidget(mp->dialog.get()); buildWidget<WType::Table, QTableWidget>(w, widget); qw.widget = w; w->setSelectionMode(QAbstractItemView::SingleSelection); w->setSelectionBehavior(QAbstractItemView::SelectRows); break; } case WType::Tree: { auto* w = new QTreeWidget(mp->dialog.get()); buildWidget<WType::Tree, QTreeWidget>(w, widget); qw.widget = w; break; } case WType::Groupbox: { auto* w = new QGroupBox(mp->dialog.get()); buildWidget<WType::Groupbox, QGroupBox>(w, widget); qw.widget = w; break; } // TODO case DialogWidget::Type::Tab: // break; case WType::Radio: { auto* w = new QRadioButton(mp->dialog.get()); buildWidget<WType::Radio, QRadioButton>(w, widget); qw.widget = w; break; } case WType::Checkbox: { auto* w = new QCheckBox(mp->dialog.get()); buildWidget<WType::Checkbox, QCheckBox>(w, widget); qw.widget = w; break; } case WType::Vscroll: { auto* w = new QScrollBar(mp->dialog.get()); buildWidget<WType::Vscroll, QScrollBar>(w, widget); qw.widget = w; break; } case WType::Hscroll: { auto* w = new QScrollBar(mp->dialog.get()); buildWidget<WType::Hscroll, QScrollBar>(w, widget); qw.widget = w; break; } // TODO case DialogWidget::Type::Canvas: // break; } } show(); } ScriptDialog::ScriptDialog(ScriptDialog&& other) : mp(std::move(other.mp)) {} ScriptDialog::~ScriptDialog() = default; void ScriptDialog::setAttr(const std::string& key, ValueHolder& value) { if (key == "x") { auto rect = mp->dialog->geometry(); rect.setX(ValueExtract(value).toInt()); mp->dialog->setGeometry(rect); } else if (key == "y") { auto rect = mp->dialog->geometry(); rect.setY(ValueExtract(value).toInt()); mp->dialog->setGeometry(rect); } else if (key == "width") { auto rect = mp->dialog->geometry(); int width = ValueExtract(value).toInt(); rect.setWidth(width); mp->dialog->setGeometry(rect); mp->dialog->setMinimumWidth(width); mp->dialog->setMaximumWidth(width); } else if (key == "height") { auto rect = mp->dialog->geometry(); int height = ValueExtract(value).toInt(); rect.setHeight(height); mp->dialog->setGeometry(rect); mp->dialog->setMinimumHeight(height); mp->dialog->setMaximumHeight(height); } else if (key == "title") { mp->dialog->setWindowTitle(ValueExtract(value).toString().c_str()); } else if (key == "font") { QFont font = mp->dialog->font(); font.setFamily(ValueExtract(value).toString().c_str()); mp->dialog->setFont(font); } else if (key == "fontsize") { QFont font = mp->dialog->font(); font.setPixelSize(ValueExtract(value).toInt()); mp->dialog->setFont(font); } else if (key == "icon") { mp->dialog->setWindowIcon(QIcon(ValueExtract(value).toString().c_str())); } else { mp->customAttrs.insert_or_assign(key, value); } } ValueHolder ScriptDialog::attr(const std::string& key) const { if (key == "x") { auto rect = mp->dialog->geometry(); return rect.x(); } else if (key == "y") { auto rect = mp->dialog->geometry(); return rect.y(); } else if (key == "width") { auto rect = mp->dialog->geometry(); return rect.width(); } else if (key == "height") { auto rect = mp->dialog->geometry(); return rect.height(); } else if (key == "title") { return mp->dialog->windowTitle().toStdString(); } else if (key == "font") { QFont font = mp->dialog->font(); return font.family().toStdString(); } else if (key == "fontsize") { QFont font = mp->dialog->font(); return font.pixelSize(); } else if (key == "icon") { return mp->dialog->windowIcon().name().toStdString(); } else { auto iter = mp->customAttrs.find(key); if (iter == mp->customAttrs.end()) return ValueHolder(); return iter->second; } } void ScriptDialog::setWidgetAttr(const std::string& widgetName, const std::string& key, ValueHolder& value) { try { ScriptWidget& widget = mp->widgets.at(widgetName); if (key == "x") { auto rect = widget.widget->geometry(); rect.setX(ValueExtract(value).toInt()); widget.widget->setGeometry(rect); } else if (key == "y") { auto rect = widget.widget->geometry(); rect.setY(ValueExtract(value).toInt()); widget.widget->setGeometry(rect); } else if (key == "width") { auto rect = widget.widget->geometry(); rect.setWidth(ValueExtract(value).toInt()); widget.widget->setGeometry(rect); } else if (key == "height") { auto rect = widget.widget->geometry(); rect.setHeight(ValueExtract(value).toInt()); widget.widget->setGeometry(rect); } else if (key == "font") { QFont font = widget.widget->font(); font.setFamily(ValueExtract(value).toString().c_str()); widget.widget->setFont(font); } else if (key == "fontsize") { QFont font = widget.widget->font(); font.setPixelSize(ValueExtract(value).toInt()); widget.widget->setFont(font); } else if (key == "enabled") { widget.widget->setEnabled(ValueExtract(value).toBool()); } else if (key == "visible") { widget.widget->setVisible(ValueExtract(value).toBool()); } else if (key == "text") { widgetSetText(widget, value); } else if (key == "checkable") { widgetSetCheckable(widget, value); } else if (key == "checked") { widgetSetChecked(widget, value); } else if (key == "readonly") { widgetSetReadOnly(widget, value); } else if (key == "tooltip") { widget.widget->setToolTip(ValueExtract(value).toString().c_str()); } else if (key == "value") { widgetSetValue(widget, value); } else if (key == "min") { widgetSetMin(widget, value); } else if (key == "max") { widgetSetMax(widget, value); } else if (key == "step") { widgetSetStep(widget, value); } else if (key == "header") { widgetSetHeader(widget, value); } else { widget.customAttrs.insert_or_assign(key, value); } } catch (std::out_of_range&) { throw ScriptException("Widget not found: " + widgetName); } } ValueHolder ScriptDialog::widgetAttr(const std::string& widgetName, const std::string& key) const { try { auto& widget = mp->widgets.at(widgetName); if (key == "x") { auto rect = widget.widget->geometry(); return rect.x(); } else if (key == "y") { auto rect = widget.widget->geometry(); return rect.y(); } else if (key == "width") { auto rect = widget.widget->geometry(); return rect.width(); } else if (key == "height") { auto rect = widget.widget->geometry(); return rect.height(); } else if (key == "font") { QFont font = widget.widget->font(); return font.family().toStdString(); } else if (key == "fontsize") { QFont font = widget.widget->font(); return font.pixelSize(); } else if (key == "enabled") { return widget.widget->isEnabled(); } else if (key == "visible") { return widget.widget->isVisible(); } else if (key == "text") { return widgetGetText(widget); } else if (key == "checkable") { return widgetGetCheckable(widget); } else if (key == "checked") { return widgetGetChecked(widget); } else if (key == "readonly") { return widgetGetReadOnly(widget); } else if (key == "tooltip") { return widget.widget->toolTip().toStdString(); } else if (key == "value") { return widgetGetValue(widget); } else if (key == "min") { return widgetGetMin(widget); } else if (key == "max") { return widgetGetMax(widget); } else if (key == "step") { return widgetGetStep(widget); } else if (key == "header") { return widgetGetHeader(widget); } else { auto iter = widget.customAttrs.find(key); if (iter == widget.customAttrs.end()) return ValueHolder(); return iter->second; } } catch (std::out_of_range&) { throw ScriptException("Widget not found: " + widgetName); } } int ScriptDialog::tableWidgetCount(const std::string& widgetName) { auto& baseWidget = mp->widgets.at(widgetName); auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget); if (!widget) throw ScriptException("Not a table widget"); return widget->rowCount(); } void ScriptDialog::tableWidgetInsert(const std::string& widgetName, const ValueArray& cols) { auto& baseWidget = mp->widgets.at(widgetName); auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget); if (!widget) throw ScriptException("Not a table widget"); const int rowCount = widget->rowCount() + 1; widget->setRowCount(rowCount); try { const auto& colCountHolder = cols.at(LIST_END_MAGIC); int colCount = ValueExtract(*(colCountHolder.get())).toInt(); if (colCount > widget->columnCount()) colCount = widget->columnCount(); for (int i = 0; i < colCount; ++i) { auto& valHolder = *(cols.at(std::to_string(i)).get()); QString value; switch (valHolder.getType()) { case ValueHolder::Type::Void: value = "[Void]"; break; case ValueHolder::Type::Integer: { int n = ValueExtract(valHolder).toInt(); value = QString::number(n); break; } case ValueHolder::Type::Real: { double n = ValueExtract(valHolder).toReal(); value = QString::number(n); break; } case ValueHolder::Type::Bool: { bool n = ValueExtract(valHolder).toBool(); value = QString::number(n); break; } case ValueHolder::Type::String: value = QString::fromStdString( ValueExtract(valHolder).toString() ); break; case ValueHolder::Type::Map: value = "[Map]"; break; } auto* item = new QTableWidgetItem(value); widget->setItem(rowCount - 1, i, item); } widget->resizeColumnsToContents(); } catch (const std::out_of_range&) { throw ScriptException("Not a list"); } } void ScriptDialog::tableWidgetRemove(const std::string& widgetName, int index) { auto& baseWidget = mp->widgets.at(widgetName); auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget); if (!widget) throw ScriptException("Not a table widget"); if (index < 0 || index >= widget->rowCount()) return; widget->removeRow(index); } int ScriptDialog::tableWidgetSelected(const std::string& widgetName) { auto& baseWidget = mp->widgets.at(widgetName); auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget); if (!widget) throw ScriptException("Not a table widget"); auto* selection = widget->selectionModel(); auto idx = selection->currentIndex(); return idx.isValid() ? idx.row() : -1; } ValueArray ScriptDialog::tableWidgetRow(const std::string& widgetName, int index) { auto& baseWidget = mp->widgets.at(widgetName); auto* widget = dynamic_cast<QTableWidget*>(baseWidget.widget); if (!widget) throw ScriptException("Not a table widget"); ValueArray ret; if (index < 0 || index >= widget->rowCount()) { ret.emplace(LIST_END_MAGIC, std::make_unique<ValueHolder>(0)); return ret; } const int colCount = widget->columnCount(); int i = 0; for (; i < colCount; ++i) { auto* item = widget->item(index, i); const auto text = item->text().toStdString(); ret.emplace(std::to_string(i), std::make_unique<ValueHolder>(text)); } ret.emplace(LIST_END_MAGIC, std::make_unique<ValueHolder>(i)); return ret; } const std::string& ScriptDialog::name() const { return mp->tmpl->name(); } void ScriptDialog::close() { mp->dialog->close(); mp->dialog->deleteLater(); mp->dialog.release(); } void ScriptDialog::hide() { mp->dialog->hide(); } void ScriptDialog::show() { mp->dialog->show(); }