From af792a9bdcabc561389a3f5040fd70e95a86b949 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Fri, 26 Jul 2019 10:50:32 +0930 Subject: [PATCH] Add javascript variable inputs: Float, Int, Check, Color, Select --- dust3d.pro | 3 + src/document.cpp | 46 +++- src/document.h | 2 +- src/intnumberwidget.cpp | 74 +++++ src/intnumberwidget.h | 34 +++ src/scriptrunner.cpp | 260 +++++++++++++++++- src/scriptrunner.h | 9 +- src/scriptvariableswidget.cpp | 165 ++++++++++- src/scriptwidget.cpp | 1 + .../quickjs-2019-07-09-dust3d/quickjs.c | 6 +- .../quickjs-2019-07-09-dust3d/quickjs.h | 4 + thirdparty/simpleuv/simpleuv/chartpacker.cpp | 2 +- 12 files changed, 569 insertions(+), 37 deletions(-) create mode 100644 src/intnumberwidget.cpp create mode 100644 src/intnumberwidget.h diff --git a/dust3d.pro b/dust3d.pro index 0a12ffa3..cd7a912e 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -404,6 +404,9 @@ HEADERS += src/updateschecker.h SOURCES += src/updatescheckwidget.cpp HEADERS += src/updatescheckwidget.h +SOURCES += src/intnumberwidget.cpp +HEADERS += src/intnumberwidget.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/document.cpp b/src/document.cpp index d4935f56..0b28cfbe 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -3346,16 +3346,20 @@ void Document::updateScript(const QString &script) void Document::updateVariable(const QString &name, const std::map &value) { + bool needRunScript = false; auto variable = m_cachedVariables.find(name); if (variable == m_cachedVariables.end()) { - qDebug() << "Update a nonexist variable:" << name << "value:" << value; + m_cachedVariables[name] = value; + needRunScript = true; + } else if (variable->second != value) { + variable->second = value; + } else { return; } - if (variable->second == value) - return; - variable->second = value; m_mergedVariables[name] = value; emit mergedVaraiblesChanged(); + if (needRunScript) + runScript(); } void Document::updateVariableValue(const QString &name, const QString &value) @@ -3368,22 +3372,36 @@ void Document::updateVariableValue(const QString &name, const QString &value) auto &variableValue = variable->second["value"]; if (variableValue == value) return; - qDebug() << "Update variable:" << name << "from:" << variableValue << "to:" << value; variableValue = value; m_mergedVariables[name] = variable->second; runScript(); } -void Document::updateDefaultVariables(const std::map> &defaultVariables) +bool Document::updateDefaultVariables(const std::map> &defaultVariables) { bool updated = false; for (const auto &it: defaultVariables) { - if (m_mergedVariables.find(it.first) != m_mergedVariables.end()) - continue; + auto findMergedVariable = m_mergedVariables.find(it.first); + if (findMergedVariable != m_mergedVariables.end()) { + bool hasChangedAttribute = false; + for (const auto &attribute: it.second) { + if (attribute.first == "value") + continue; + const auto &findMatch = findMergedVariable->second.find(attribute.first); + if (findMatch != findMergedVariable->second.end()) { + if (findMatch->second == attribute.second) + continue; + } + hasChangedAttribute = true; + } + if (!hasChangedAttribute) + continue; + } updated = true; auto findCached = m_cachedVariables.find(it.first); if (findCached != m_cachedVariables.end()) { - m_mergedVariables[it.first] = findCached->second; + m_mergedVariables[it.first] = it.second; + m_mergedVariables[it.first]["value"] = valueOfKeyInMapOrEmpty(findCached->second, "value"); } else { m_mergedVariables[it.first] = it.second; m_cachedVariables[it.first] = it.second; @@ -3402,6 +3420,7 @@ void Document::updateDefaultVariables(const std::mapmoveToThread(thread); m_scriptRunner->setScript(new QString(m_script)); - m_scriptRunner->setVariables(new std::map>(m_mergedVariables)); + m_scriptRunner->setVariables(new std::map>( + m_mergedVariables.empty() ? m_cachedVariables : m_mergedVariables + )); connect(thread, &QThread::started, m_scriptRunner, &ScriptRunner::process); connect(m_scriptRunner, &ScriptRunner::finished, this, &Document::scriptResultReady); connect(m_scriptRunner, &ScriptRunner::finished, thread, &QThread::quit); @@ -3435,6 +3456,7 @@ void Document::scriptResultReady() std::map> *defaultVariables = m_scriptRunner->takeDefaultVariables(); bool errorChanged = false; bool consoleLogChanged = false; + bool mergedVariablesChanged = false; const QString &scriptError = m_scriptRunner->scriptError(); if (m_scriptError != scriptError) { @@ -3455,7 +3477,7 @@ void Document::scriptResultReady() } if (nullptr != defaultVariables) { - updateDefaultVariables(*defaultVariables); + mergedVariablesChanged = updateDefaultVariables(*defaultVariables); delete defaultVariables; } @@ -3470,7 +3492,7 @@ void Document::scriptResultReady() qDebug() << "Script run done"; - if (m_isScriptResultObsolete) { + if (m_isScriptResultObsolete || mergedVariablesChanged) { runScript(); } } diff --git a/src/document.h b/src/document.h index 40c94a8f..e21eed17 100644 --- a/src/document.h +++ b/src/document.h @@ -658,7 +658,6 @@ public slots: void applyPreferenceFlatShadingChange(); void initScript(const QString &script); void updateScript(const QString &script); - void updateDefaultVariables(const std::map> &defaultVariables); void runScript(); void scriptResultReady(); void updateVariable(const QString &name, const std::map &value); @@ -680,6 +679,7 @@ private: void removeRigResults(); void updateLinkedPart(QUuid oldPartId, QUuid newPartId); //void addToolToMesh(MeshLoader *mesh); + bool updateDefaultVariables(const std::map> &defaultVariables); private: // need initialize bool m_isResultMeshObsolete; MeshGenerator *m_meshGenerator; diff --git a/src/intnumberwidget.cpp b/src/intnumberwidget.cpp new file mode 100644 index 00000000..39c107c6 --- /dev/null +++ b/src/intnumberwidget.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include "theme.h" +#include "intnumberwidget.h" + +IntNumberWidget::IntNumberWidget(QWidget *parent, bool singleLine) : + QWidget(parent) +{ + m_slider = new QSlider(Qt::Horizontal, this); + m_slider->setRange(0, 100); + m_slider->setFixedWidth(120); + + m_label = new QLabel(this); + m_label->setAlignment(Qt::AlignLeft); + + connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) { + updateValueLabel(value); + emit valueChanged(value); + }); + + QBoxLayout *layout = nullptr; + if (singleLine) { + layout = new QHBoxLayout(this); + layout->setMargin(2); + layout->addWidget(m_slider); + layout->addWidget(m_label); + } else { + layout = new QVBoxLayout(this); + layout->setMargin(2); + layout->addWidget(m_label); + layout->addWidget(m_slider); + } +} + +void IntNumberWidget::updateValueLabel(int value) +{ + QString valueString = QString::number(value); + if (m_itemName.isEmpty()) + m_label->setText(valueString); + else + m_label->setText(m_itemName + ": " + valueString); +} + +void IntNumberWidget::setItemName(const QString &name) +{ + m_itemName = name; + updateValueLabel(value()); +} + +void IntNumberWidget::setRange(int min, int max) +{ + m_slider->setRange(min, max); +} + +void IntNumberWidget::increaseValue() +{ + m_slider->triggerAction(QSlider::SliderPageStepAdd); +} + +void IntNumberWidget::descreaseValue() +{ + m_slider->triggerAction(QSlider::SliderPageStepSub); +} + +int IntNumberWidget::value() const +{ + return m_slider->value(); +} + +void IntNumberWidget::setValue(int value) +{ + m_slider->setValue(value); +} diff --git a/src/intnumberwidget.h b/src/intnumberwidget.h new file mode 100644 index 00000000..e91d6ff6 --- /dev/null +++ b/src/intnumberwidget.h @@ -0,0 +1,34 @@ +#ifndef DUST3D_INT_NUMBER_WIDGET_H +#define DUST3D_INT_NUMBER_WIDGET_H +#include + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QSlider) + +class IntNumberWidget : public QWidget +{ + Q_OBJECT +public: + explicit IntNumberWidget(QWidget *parent = nullptr, bool singleLine=true); + void setRange(int min, int max); + int value() const; + void setItemName(const QString &name); + +public slots: + void increaseValue(); + void descreaseValue(); + void setValue(int value); + +signals: + void valueChanged(int value); + +private: + void updateValueLabel(int value); + +private: + QLabel *m_label = nullptr; + QSlider *m_slider = nullptr; + QString m_itemName; +}; + +#endif diff --git a/src/scriptrunner.cpp b/src/scriptrunner.cpp index 7e3c5693..ae778a64 100644 --- a/src/scriptrunner.cpp +++ b/src/scriptrunner.cpp @@ -2,6 +2,7 @@ #include #include #include "scriptrunner.h" +#include "util.h" JSClassID ScriptRunner::js_partClassId = 0; JSClassID ScriptRunner::js_componentClassId = 0; @@ -206,16 +207,82 @@ static JSValue js_createNode(JSContext *context, JSValueConst thisValue, return node; } -static JSValue js_createVariable(JSContext *context, JSValueConst thisValue, +static JSValue js_createFloatInput(JSContext *context, JSValueConst thisValue, + int argc, JSValueConst *argv) +{ + ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context); + if (argc < 4) { + runner->consoleLog() += "Incomplete parameters, expect: name, defaultValue, minValue, maxValue\r\n"; + return JS_EXCEPTION; + } + + double mergedValue = 0.0; + + const char *name = nullptr; + double defaultValue = 0.0; + double minValue = 0.0; + double maxValue = 0.0; + + name = JS_ToCString(context, argv[0]); + if (!name) + goto fail; + JS_ToFloat64(context, &defaultValue, argv[1]); + JS_ToFloat64(context, &minValue, argv[2]); + JS_ToFloat64(context, &maxValue, argv[3]); + + mergedValue = runner->createFloatInput(name, defaultValue, minValue, maxValue); + JS_FreeCString(context, name); + + return JS_NewFloat64(context, mergedValue); + +fail: + JS_FreeCString(context, name); + return JS_EXCEPTION; +} + +static JSValue js_createIntInput(JSContext *context, JSValueConst thisValue, + int argc, JSValueConst *argv) +{ + ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context); + if (argc < 4) { + runner->consoleLog() += "Incomplete parameters, expect: name, defaultValue, minValue, maxValue\r\n"; + return JS_EXCEPTION; + } + + int64_t mergedValue = 0.0; + + const char *name = nullptr; + int64_t defaultValue = 0; + int64_t minValue = 0; + int64_t maxValue = 0; + + name = JS_ToCString(context, argv[0]); + if (!name) + goto fail; + JS_ToInt64(context, &defaultValue, argv[1]); + JS_ToInt64(context, &minValue, argv[2]); + JS_ToInt64(context, &maxValue, argv[3]); + + mergedValue = runner->createIntInput(name, defaultValue, minValue, maxValue); + JS_FreeCString(context, name); + + return JS_NewInt64(context, mergedValue); + +fail: + JS_FreeCString(context, name); + return JS_EXCEPTION; +} + +static JSValue js_createColorInput(JSContext *context, JSValueConst thisValue, int argc, JSValueConst *argv) { ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context); if (argc < 2) { - runner->consoleLog() += "Incomplete parameters, expect: name, value\r\n"; + runner->consoleLog() += "Incomplete parameters, expect: name, defaultValue\r\n"; return JS_EXCEPTION; } - QString mergedValue; + QColor mergeValue; const char *name = nullptr; const char *defaultValue = nullptr; @@ -227,11 +294,12 @@ static JSValue js_createVariable(JSContext *context, JSValueConst thisValue, if (!defaultValue) goto fail; - mergedValue = runner->createVariable(name, defaultValue); + mergeValue = runner->createColorInput(name, defaultValue); + JS_FreeCString(context, name); JS_FreeCString(context, defaultValue); - return JS_NewFloat64(context, mergedValue.toDouble()); + return JS_NewString(context, mergeValue.name().toUtf8().constData()); fail: JS_FreeCString(context, name); @@ -239,6 +307,98 @@ fail: return JS_EXCEPTION; } +static JSValue js_createCheckInput(JSContext *context, JSValueConst thisValue, + int argc, JSValueConst *argv) +{ + ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context); + if (argc < 2) { + runner->consoleLog() += "Incomplete parameters, expect: name, defaultValue\r\n"; + return JS_EXCEPTION; + } + + bool mergedValue = false; + + const char *name = nullptr; + const char *value = nullptr; + bool defaultValue = false; + + name = JS_ToCString(context, argv[0]); + if (!name) + goto fail; + value = JS_ToCString(context, argv[1]); + if (!value) + goto fail; + + defaultValue = isTrueValueString(value); + mergedValue = runner->createCheckInput(name, defaultValue); + JS_FreeCString(context, name); + JS_FreeCString(context, value); + + return JS_NewInt64(context, mergedValue); + +fail: + JS_FreeCString(context, name); + JS_FreeCString(context, value); + return JS_EXCEPTION; +} + +static JSValue js_createSelectInput(JSContext *context, JSValueConst thisValue, + int argc, JSValueConst *argv) +{ + ScriptRunner *runner = (ScriptRunner *)JS_GetContextOpaque(context); + if (argc < 3) { + runner->consoleLog() += "Incomplete parameters, expect: name, defaultValue, options\r\n"; + return JS_EXCEPTION; + } + + const char *name = nullptr; + int32_t defaultValue = 0; + int32_t mergedValue = 0; + JSValue *arrayItems = nullptr; + uint32_t arrayLength = 0; + QStringList options; + + name = JS_ToCString(context, argv[0]); + if (!name) + goto fail; + + JS_ToInt32(context, &defaultValue, argv[1]); + + if (true != JS_IsArray(context, argv[2])) { + runner->consoleLog() += "Expect array as the third parameter\r\n"; + goto fail; + } + + if (!js_get_fast_array(context, argv[2], &arrayItems, &arrayLength)) { + runner->consoleLog() += "Read third parameter as array failed\r\n"; + goto fail; + } + + for (uint32_t i = 0; i < arrayLength; ++i) { + const char *optionValue = JS_ToCString(context, arrayItems[i]); + if (nullptr != optionValue) { + options.append(optionValue); + JS_FreeCString(context, optionValue); + } else { + options.append(""); + } + } + + mergedValue = runner->createSelectInput(name, defaultValue, options); + if (mergedValue < 0) + mergedValue = 0; + else if (mergedValue >= options.size()) + mergedValue = options.size() - 1; + + JS_FreeCString(context, name); + + return JS_NewInt32(context, mergedValue); + +fail: + JS_FreeCString(context, name); + return JS_EXCEPTION; +} + ScriptRunner::ScriptRunner() { } @@ -375,7 +535,7 @@ void ScriptRunner::run() JS_SetContextOpaque(context, this); if (nullptr != m_script && - !m_script->isEmpty()) { + !m_script->trimmed().isEmpty()) { auto buffer = m_script->toUtf8(); JSValue globalObject = JS_GetGlobalObject(context); @@ -391,8 +551,20 @@ void ScriptRunner::run() document, "createNode", JS_NewCFunction(context, js_createNode, "createNode", 1)); JS_SetPropertyStr(context, - document, "createVariable", - JS_NewCFunction(context, js_createVariable, "createVariable", 2)); + document, "createFloatInput", + JS_NewCFunction(context, js_createFloatInput, "createFloatInput", 4)); + JS_SetPropertyStr(context, + document, "createIntInput", + JS_NewCFunction(context, js_createIntInput, "createIntInput", 4)); + JS_SetPropertyStr(context, + document, "createColorInput", + JS_NewCFunction(context, js_createColorInput, "createColorInput", 2)); + JS_SetPropertyStr(context, + document, "createCheckInput", + JS_NewCFunction(context, js_createCheckInput, "createCheckInput", 2)); + JS_SetPropertyStr(context, + document, "createSelectInput", + JS_NewCFunction(context, js_createSelectInput, "createSelectInput", 3)); JS_SetPropertyStr(context, document, "connect", JS_NewCFunction(context, js_connect, "connect", 2)); @@ -552,13 +724,13 @@ void ScriptRunner::generateSnapshot() } } -QString ScriptRunner::createVariable(const QString &name, const QString &defaultValue) +QString ScriptRunner::createInput(const QString &name, const std::map &attributes) { if (nullptr != m_defaultVariables) { if (m_defaultVariables->find(name) != m_defaultVariables->end()) { m_scriptError += "Repeated variable name found: \"" + name + "\"\r\n"; } - (*m_defaultVariables)[name]["value"] = defaultValue; + (*m_defaultVariables)[name] = attributes; } if (nullptr != m_variables) { auto findVariable = m_variables->find(name); @@ -568,7 +740,73 @@ QString ScriptRunner::createVariable(const QString &name, const QString &default return findValue->second; } } - return defaultValue; + auto findValue = attributes.find("value"); + if (findValue != attributes.end()) + return findValue->second; + return QString(); +} + +float ScriptRunner::createFloatInput(const QString &name, float defaultValue, float minValue, float maxValue) +{ + std::map attributes; + attributes["input"] = "Float"; + attributes["value"] = QString::number(defaultValue); + attributes["defaultValue"] = attributes["value"]; + attributes["minValue"] = QString::number(minValue); + attributes["maxValue"] = QString::number(maxValue); + auto inputValue = createInput(name, attributes); + return inputValue.toFloat(); +} + +int ScriptRunner::createIntInput(const QString &name, int defaultValue, int minValue, int maxValue) +{ + std::map attributes; + attributes["input"] = "Int"; + attributes["value"] = QString::number(defaultValue); + attributes["defaultValue"] = attributes["value"]; + attributes["minValue"] = QString::number(minValue); + attributes["maxValue"] = QString::number(maxValue); + auto inputValue = createInput(name, attributes); + return inputValue.toInt(); +} + +QColor ScriptRunner::createColorInput(const QString &name, const QColor &defaultValue) +{ + std::map attributes; + attributes["input"] = "Color"; + attributes["value"] = defaultValue.name(); + attributes["defaultValue"] = attributes["value"]; + auto inputValue = createInput(name, attributes); + return QColor(inputValue); +} + +bool ScriptRunner::createCheckInput(const QString &name, bool checked) +{ + std::map attributes; + attributes["input"] = "Check"; + attributes["value"] = checked ? "true" : "false"; + attributes["defaultValue"] = attributes["value"]; + auto inputValue = createInput(name, attributes); + return isTrueValueString(inputValue); +} + +int ScriptRunner::createSelectInput(const QString &name, int defaultSelectedIndex, const QStringList &options) +{ + std::map attributes; + attributes["input"] = "Select"; + attributes["value"] = QString::number(defaultSelectedIndex); + attributes["defaultValue"] = attributes["value"]; + attributes["length"] = QString::number(options.size()); + for (int i = 0; i < options.size(); ++i) { + attributes["option" + QString::number(i)] = options[i]; + } + auto inputValue = createInput(name, attributes); + int selectedIndex = inputValue.toInt(); + if (selectedIndex >= options.size()) { + m_scriptError += QString("Selected index of select input \"%1\" is been reset to 0 because of out of range\r\n").arg(name); + selectedIndex = 0; + } + return selectedIndex; } void ScriptRunner::process() diff --git a/src/scriptrunner.h b/src/scriptrunner.h index e5917067..e1794bb5 100644 --- a/src/scriptrunner.h +++ b/src/scriptrunner.h @@ -2,6 +2,8 @@ #define DUST3D_SCRIPT_RUNNER_H #include #include +#include +#include #include "snapshot.h" extern "C" { #include "quickjs.h" @@ -63,7 +65,6 @@ public: std::map> *takeDefaultVariables(); const QString &scriptError(); static void mergeVaraibles(std::map> *target, const std::map> &source); - QString createVariable(const QString &name, const QString &defaultValue); DocumentPart *createPart(DocumentComponent *component); DocumentComponent *createComponent(DocumentComponent *parentComponent); DocumentNode *createNode(DocumentPart *part); @@ -71,6 +72,12 @@ public: QString attribute(DocumentElement *element, const QString &name); void connect(DocumentNode *firstNode, DocumentNode *secondNode); QString &consoleLog(); + QString createInput(const QString &name, const std::map &attributes); + float createFloatInput(const QString &name, float defaultValue, float minValue, float maxValue); + int createIntInput(const QString &name, int defaultValue, int minValue, int maxValue); + QColor createColorInput(const QString &name, const QColor &defaultValue); + bool createCheckInput(const QString &name, bool checked); + int createSelectInput(const QString &name, int defaultSelectedIndex, const QStringList &options); signals: void finished(); public slots: diff --git a/src/scriptvariableswidget.cpp b/src/scriptvariableswidget.cpp index f2eabf12..19b66e5e 100644 --- a/src/scriptvariableswidget.cpp +++ b/src/scriptvariableswidget.cpp @@ -1,9 +1,15 @@ #include #include #include +#include +#include +#include +#include #include "scriptvariableswidget.h" #include "util.h" #include "theme.h" +#include "floatnumberwidget.h" +#include "intnumberwidget.h" ScriptVariablesWidget::ScriptVariablesWidget(const Document *document, QWidget *parent) : @@ -23,14 +29,157 @@ void ScriptVariablesWidget::reload() QFormLayout *formLayout = new QFormLayout; for (const auto &variable: m_document->variables()) { auto name = variable.first; - auto value = valueOfKeyInMapOrEmpty(variable.second, "value").toFloat(); - qDebug() << "Script variable, name:" << name << "value:" << value; - QDoubleSpinBox *inputBox = new QDoubleSpinBox; - inputBox->setValue(value); - connect(inputBox, static_cast(&QDoubleSpinBox::valueChanged), this, [=](double toValue) { - emit updateVariableValue(name, QString::number(toValue)); - }); - formLayout->addRow(name, inputBox); + auto input = valueOfKeyInMapOrEmpty(variable.second, "input"); + if ("Float" == input) { + auto minValue = valueOfKeyInMapOrEmpty(variable.second, "minValue").toFloat(); + auto maxValue = valueOfKeyInMapOrEmpty(variable.second, "maxValue").toFloat(); + auto value = valueOfKeyInMapOrEmpty(variable.second, "value").toFloat(); + auto defaultValue = valueOfKeyInMapOrEmpty(variable.second, "defaultValue").toFloat(); + FloatNumberWidget *floatWidget = new FloatNumberWidget; + floatWidget->setItemName(name); + floatWidget->setRange(minValue, maxValue); + floatWidget->setValue(value); + + connect(floatWidget, &FloatNumberWidget::valueChanged, [=](float toValue) { + emit updateVariableValue(name, QString::number(toValue)); + }); + + QPushButton *floatEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(floatEraser); + + connect(floatEraser, &QPushButton::clicked, [=]() { + floatWidget->setValue(defaultValue); + }); + + QHBoxLayout *floatLayout = new QHBoxLayout; + floatLayout->addWidget(floatEraser); + floatLayout->addWidget(floatWidget); + + formLayout->addRow(floatLayout); + } else if ("Int" == input) { + auto minValue = valueOfKeyInMapOrEmpty(variable.second, "minValue").toInt(); + auto maxValue = valueOfKeyInMapOrEmpty(variable.second, "maxValue").toInt(); + auto value = valueOfKeyInMapOrEmpty(variable.second, "value").toInt(); + auto defaultValue = valueOfKeyInMapOrEmpty(variable.second, "defaultValue").toInt(); + IntNumberWidget *intWidget = new IntNumberWidget; + intWidget->setItemName(name); + intWidget->setRange(minValue, maxValue); + intWidget->setValue(value); + + connect(intWidget, &IntNumberWidget::valueChanged, [=](int toValue) { + emit updateVariableValue(name, QString::number(toValue)); + }); + + QPushButton *intEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(intEraser); + + connect(intEraser, &QPushButton::clicked, [=]() { + intWidget->setValue(defaultValue); + }); + + QHBoxLayout *intLayout = new QHBoxLayout; + intLayout->addWidget(intEraser); + intLayout->addWidget(intWidget); + + formLayout->addRow(intLayout); + } else if ("Check" == input) { + auto value = isTrueValueString(valueOfKeyInMapOrEmpty(variable.second, "value")); + auto defaultValue = isTrueValueString(valueOfKeyInMapOrEmpty(variable.second, "defaultValue")); + + QCheckBox *checkBox = new QCheckBox; + checkBox->setText(name); + checkBox->setChecked(value); + + connect(checkBox, &QCheckBox::stateChanged, [=](int) { + emit updateVariableValue(name, checkBox->isChecked() ? "true" : "false"); + }); + + QPushButton *checkEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(checkEraser); + + connect(checkEraser, &QPushButton::clicked, [=]() { + checkBox->setChecked(defaultValue); + }); + + QHBoxLayout *checkLayout = new QHBoxLayout; + checkLayout->addWidget(checkEraser); + checkLayout->addWidget(checkBox); + + formLayout->addRow(checkLayout); + } else if ("Select" == input) { + auto value = valueOfKeyInMapOrEmpty(variable.second, "value").toInt(); + auto defaultValue = valueOfKeyInMapOrEmpty(variable.second, "defaultValue").toInt(); + auto length = valueOfKeyInMapOrEmpty(variable.second, "length").toInt(); + + QComboBox *selectBox = new QComboBox; + selectBox->setEditable(false); + selectBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + for (auto i = 0; i < length; ++i) { + selectBox->addItem(valueOfKeyInMapOrEmpty(variable.second, "option" + QString::number(i))); + } + selectBox->setCurrentIndex(value); + + connect(selectBox, static_cast(&QComboBox::currentIndexChanged), [=](int index) { + emit updateVariableValue(name, QString::number(index)); + }); + + QPushButton *selectEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(selectEraser); + + connect(selectEraser, &QPushButton::clicked, [=]() { + selectBox->setCurrentIndex(defaultValue); + }); + + QLabel *selectLabel = new QLabel; + selectLabel->setText(name); + + QHBoxLayout *selectLayout = new QHBoxLayout; + selectLayout->addWidget(selectEraser); + selectLayout->addWidget(selectBox); + selectLayout->addWidget(selectLabel); + + formLayout->addRow(selectLayout); + } else if ("Color" == input) { + auto value = valueOfKeyInMapOrEmpty(variable.second, "value"); + auto defaultValue = valueOfKeyInMapOrEmpty(variable.second, "defaultValue"); + + QPushButton *colorEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(colorEraser); + + QPushButton *pickButton = new QPushButton(); + Theme::initAwesomeToolButtonWithoutFont(pickButton); + + auto updatePickButtonColor = [=](const QColor &color) { + QPalette palette = pickButton->palette(); + palette.setColor(QPalette::Window, color); + palette.setColor(QPalette::Button, color); + pickButton->setPalette(palette); + }; + updatePickButtonColor(QColor(value)); + + connect(colorEraser, &QPushButton::clicked, [=]() { + emit updateVariableValue(name, defaultValue); + }); + + connect(pickButton, &QPushButton::clicked, [=]() { + QColor color = QColorDialog::getColor(value, this); + if (color.isValid()) { + updatePickButtonColor(color.name()); + emit updateVariableValue(name, color.name()); + } + }); + + QLabel *selectLabel = new QLabel; + selectLabel->setText(name); + + QHBoxLayout *colorLayout = new QHBoxLayout; + colorLayout->addWidget(colorEraser); + colorLayout->addWidget(pickButton); + colorLayout->addWidget(selectLabel); + colorLayout->addStretch(); + + formLayout->addRow(colorLayout); + } } widget->setLayout(formLayout); diff --git a/src/scriptwidget.cpp b/src/scriptwidget.cpp index 0b7bdf5c..adbeddde 100644 --- a/src/scriptwidget.cpp +++ b/src/scriptwidget.cpp @@ -42,6 +42,7 @@ ScriptWidget::ScriptWidget(const Document *document, QWidget *parent) : splitter->addWidget(scriptEditWidget); splitter->addWidget(m_consoleEdit); splitter->addWidget(scriptVariablesWidget); + splitter->setStretchFactor(2, 1); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(splitter); diff --git a/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.c b/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.c index e7fd065f..e4935b6c 100644 --- a/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.c +++ b/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.c @@ -13632,7 +13632,7 @@ static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, return enum_obj; } -static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) +JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) { JSValue method, ret, sync_iter; @@ -13707,7 +13707,7 @@ static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, return JS_EXCEPTION; } -static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, +JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst *argv, BOOL *pdone) { @@ -13898,7 +13898,7 @@ static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); /* Access an Array's internal JSValue array if available */ -static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, +int js_get_fast_array(JSContext *ctx, JSValueConst obj, JSValue **arrpp, uint32_t *countp) { /* Try and handle fast arrays explicitly */ diff --git a/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.h b/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.h index 1761dc71..fc44a09c 100644 --- a/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.h +++ b/thirdparty/quickjs/quickjs-2019-07-09-dust3d/quickjs.h @@ -894,4 +894,8 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, #undef js_unlikely #undef js_force_inline +// https://www.freelists.org/post/quickjs-devel/how-to-iterator-over-a-JSValue-object,1 +int js_get_fast_array(JSContext *ctx, JSValueConst obj, + JSValue **arrpp, uint32_t *countp); + #endif /* QUICKJS_H */ diff --git a/thirdparty/simpleuv/simpleuv/chartpacker.cpp b/thirdparty/simpleuv/simpleuv/chartpacker.cpp index b8fdfa78..8b9c3fa0 100644 --- a/thirdparty/simpleuv/simpleuv/chartpacker.cpp +++ b/thirdparty/simpleuv/simpleuv/chartpacker.cpp @@ -32,7 +32,7 @@ bool ChartPacker::tryPack(float textureSize) std::vector rects; int width = textureSize * m_floatToIntFactor; int height = width; - if (m_tryNum > 3) { + if (m_tryNum > 50) { qDebug() << "Try the " << m_tryNum << "nth times pack with factor:" << m_textureSizeFactor << " size:" << width << "x" << height; } for (const auto &chartSize: m_chartSizes) {