Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/controllers/addonmetadataparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ AddOnFilterDescriptor AddOnMetadataParser::parse(const QString &service,
out.minimum = QString::fromUtf8(parameter.get("minimum"));
out.maximum = QString::fromUtf8(parameter.get("maximum"));
out.description = QString::fromUtf8(parameter.get("description"));
out.normalizedCoordinates = parseYesNoBool(parameter.get("normalized_coordinates"));
out.normalizedDefault = parseYesNoBool(parameter.get("normalized_default"));
out.name = QString::fromUtf8(parameter.get("identifier"));

const QString parameterType = out.type.trimmed().toLower();
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/addonmetadataparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct AddOnParameterDescriptor
QString minimum;
QString maximum;
QString description;
bool normalizedCoordinates = false;
bool normalizedDefault = false;
};

struct AddOnFilterDescriptor
Expand Down
101 changes: 99 additions & 2 deletions src/controllers/addonqmlgenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
QStringList quotedMaximums;
QStringList quotedDescriptions;
QStringList quotedValueLists;
QStringList quotedNormalizedCoordinates;
QStringList quotedNormalizedDefault;
QStringList quotedKeyframeProperties;
QStringList quotedKeyframeMapEntries;
QStringList setControlsLines;
Expand Down Expand Up @@ -161,6 +163,14 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
quotedMaximums << QStringLiteral("%1: %2").arg(quotedJsString(parameter.name),
quotedJsString(parameter.maximum));
}
if (parameter.normalizedCoordinates) {
quotedNormalizedCoordinates
<< QStringLiteral("%1: 'yes'").arg(quotedJsString(parameter.name));
}
if (parameter.normalizedDefault) {
quotedNormalizedDefault
<< QStringLiteral("%1: 'yes'").arg(quotedJsString(parameter.name));
}
if (!parameter.description.isEmpty()) {
quotedDescriptions << QStringLiteral("%1: %2").arg(quotedJsString(parameter.name),
quotedJsString(
Expand Down Expand Up @@ -216,6 +226,14 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
} else if (parameterType == QStringLiteral("boolean")) {
setControlsLines << QStringLiteral(" %1.checked = root.booleanValue(%2);")
.arg(editorId, nameLiteral);
} else if (parameterType == QStringLiteral("rect")
&& (parameterWidget == QStringLiteral("point")
|| parameterWidget == QStringLiteral("size"))) {
setControlsLines
<< QStringLiteral(
" { var _r = filter.getRect(%2); %1.valueX = isNaN(_r.x) ? 0 : "
"_r.x; %1.valueY = isNaN(_r.y) ? 0 : _r.y; }")
.arg(editorId, nameLiteral);
} else if ((parameterType == QStringLiteral("integer")
|| parameterType == QStringLiteral("float"))
&& !useTextForNumericEditor) {
Expand Down Expand Up @@ -274,6 +292,12 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
<< "}\n\n"
" property var propertyValues: {"
<< quotedValueLists.join(QStringLiteral(", "))
<< "}\n\n"
" property var propertyNormalizedCoordinates: {"
<< quotedNormalizedCoordinates.join(QStringLiteral(", "))
<< "}\n\n"
" property var propertyNormalizedDefault: {"
<< quotedNormalizedDefault.join(QStringLiteral(", "))
<< "}\n\n"
" keyframableParameters: ["
<< quotedKeyframeProperties.join(QStringLiteral(", "))
Expand Down Expand Up @@ -434,10 +458,24 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
" var d = root.propertyDefaults[p];\n"
" var type = propertyType(p);\n"
" var widget = propertyWidget(p);\n"
" if (d !== undefined && d !== null && d !== '')\n"
" filter.set(p, isBooleanType(type) ? (booleanValue(p) ? '1' : '0') "
" if (d !== undefined && d !== null && d !== '') {\n"
" if (type === 'rect' && root.propertyNormalizedDefault\n"
" && root.propertyNormalizedDefault[p] === 'yes') {\n"
" var _dp = String(d).trim().split(/\\s+/);\n"
" var _dx = _dp.length > 0 ? parseFloat(_dp[0]) : 0;\n"
" var _dy = _dp.length > 1 ? parseFloat(_dp[1]) : 0;\n"
Comment on lines +464 to +466

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure you are aware of this, but JavaScript let and const are preferred in new/changed QML/JS code:
https://github.com/mltframework/shotcut/blob/master/.github/instructions/qml.instructions.md

" if (isNaN(_dx)) _dx = 0;\n"
" if (isNaN(_dy)) _dy = 0;\n"
" _dx *= profile.width;\n"
" _dy *= profile.height;\n"
" filter.set(p, _dx + ' ' + _dy + ' 0 0 0');\n"
" } else {\n"
" filter.set(p, isBooleanType(type) ? (booleanValue(p) ? '1' : "
"'0') "
": ((isNumericType(type) && widget === 'text') ? String(d) : (isNumericType(type) ? "
"Number(d) : d)));\n"
" }\n"
" }\n"
" }\n"
" filter.savePreset(propertyNames);\n"
" }\n"
Expand Down Expand Up @@ -740,6 +778,48 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
" }\n"
" }\n"
" }\n";
} else if (parameterType == QStringLiteral("rect")
&& (parameterWidget == QStringLiteral("point")
|| parameterWidget == QStringLiteral("size"))) {
const QString labelFirst = (parameterWidget == QStringLiteral("size"))
? QStringLiteral("W")
: QStringLiteral("X");
const QString labelSecond = (parameterWidget == QStringLiteral("size"))
? QStringLiteral("H")
: QStringLiteral("Y");
stream
<< " Shotcut.Number2D {\n"
" id: "
<< editorId
<< "\n"
" Layout.columnSpan: "
<< (parameter.hideLabel ? "2" : "1")
<< "\n"
" Layout.fillWidth: true\n"
" Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter\n"
" readonly property string propertyName: "
<< nameLiteral
<< "\n"
" readonly property string typeName: "
"root.propertyType(propertyName)\n"
" decimals: root.isIntegerType(typeName) ? 0 : 3\n"
" from: (root.propertyMinimums && "
"root.propertyMinimums[propertyName] !== undefined && "
"root.propertyMinimums[propertyName] !== '') ? "
"Number(root.propertyMinimums[propertyName]) : -99999\n"
" to: (root.propertyMaximums && root.propertyMaximums[propertyName] "
"!== undefined && root.propertyMaximums[propertyName] !== '') ? "
"Number(root.propertyMaximums[propertyName]) : 99999\n"
" labelFirst: "
<< quotedJsString(labelFirst)
<< "\n"
" labelSecond: "
<< quotedJsString(labelSecond)
<< "\n"
" onValuesModified: {\n"
" filter.set(propertyName, valueX + ' ' + valueY + ' 0 0 1');\n"
" }\n"
" }\n";
} else if ((parameterType == QStringLiteral("integer")
|| parameterType == QStringLiteral("float"))
&& !useTextForNumericEditor) {
Expand Down Expand Up @@ -914,6 +994,23 @@ bool AddOnQmlGenerator::generate(const AddOnFilterDescriptor &descriptor,
" filter.set(propertyName, defaultValue);\n"
" root.setControls();\n";
}
} else if (parameterType == QStringLiteral("rect")
&& (parameterWidget == QStringLiteral("point")
|| parameterWidget == QStringLiteral("size"))) {
stream << " var _ds = root.propertyDefaults ? "
"(root.propertyDefaults[propertyName] || '') : '';\n"
" var _dp = String(_ds).trim().split(/\\s+/);\n"
" var _dx = _dp.length > 0 ? parseFloat(_dp[0]) : 0;\n"
" var _dy = _dp.length > 1 ? parseFloat(_dp[1]) : 0;\n"
" if (isNaN(_dx)) _dx = 0;\n"
" if (isNaN(_dy)) _dy = 0;\n"
" if (root.propertyNormalizedDefault &&\n"
"root.propertyNormalizedDefault[propertyName] === 'yes') {\n"
" _dx *= profile.width;\n"
" _dy *= profile.height;\n"
" }\n"
" filter.set(propertyName, _dx + ' ' + _dy + ' 0 0 0');\n"
" root.setControls();\n";
} else {
stream
<< " var defaultValue = root.defaultTextValue(propertyName);\n"
Expand Down
91 changes: 91 additions & 0 deletions src/qml/modules/Shotcut/Controls/Number2D.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2026 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Shotcut.Controls as Shotcut

// A pair of spinboxes for two-dimensional parameters (point or size).
// The caller sets valueX / valueY and connects to valuesModified to write back.
// Set decimals: 0 for integer parameters, > 0 for floating-point parameters.
RowLayout {
id: root

property real valueX: 0
property real valueY: 0
property int decimals: 0
property real from: -99999
property real to: 99999
property real stepSize: 1
property string labelFirst: 'X'
property string labelSecond: 'Y'

signal valuesModified

spacing: 4

onValueXChanged: {
if (Math.abs(xBox.value - valueX) > 1e-9)
xBox.value = valueX;
}
onValueYChanged: {
if (Math.abs(yBox.value - valueY) > 1e-9)
yBox.value = valueY;
}

Label {
text: root.labelFirst
textFormat: Text.PlainText
verticalAlignment: Text.AlignVCenter
}

Shotcut.DoubleSpinBox {
id: xBox

Layout.fillWidth: true
value: root.valueX
decimals: root.decimals
from: root.from
to: root.to
stepSize: root.stepSize
onValueModified: {
root.valueX = value;
root.valuesModified();
}
}

Label {
text: root.labelSecond
textFormat: Text.PlainText
verticalAlignment: Text.AlignVCenter
}

Shotcut.DoubleSpinBox {
id: yBox

Layout.fillWidth: true
value: root.valueY
decimals: root.decimals
from: root.from
to: root.to
stepSize: root.stepSize
onValueModified: {
root.valueY = value;
root.valuesModified();
}
}
}
1 change: 1 addition & 0 deletions src/qml/modules/Shotcut/Controls/qmldir
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ MotionTrackerDialog 1.0 MotionTrackerDialog.qml
HorizontalScrollBar 1.0 HorizontalScrollBar.qml
VerticalScrollBar 1.0 VerticalScrollBar.qml
ChannelMask 1.0 ChannelMask.qml
Number2D 1.0 Number2D.qml