Merge pull request #12747 from Zvork/fade

Update to Transitions debugging / editing script
This commit is contained in:
John Conklin II 2018-04-04 14:31:59 -07:00 committed by GitHub
commit 2e37b4a312
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 358 additions and 120 deletions

View file

@ -110,7 +110,6 @@ bool SelectionScriptingInterface::enableListHighlight(const QString& listName, c
} }
if (!(*highlightStyle).isBoundToList()) { if (!(*highlightStyle).isBoundToList()) {
setupHandler(listName);
(*highlightStyle).setBoundToList(true); (*highlightStyle).setBoundToList(true);
} }
@ -172,6 +171,18 @@ render::HighlightStyle SelectionScriptingInterface::getHighlightStyle(const QStr
} }
} }
bool SelectionScriptingInterface::enableListToScene(const QString& listName) {
setupHandler(listName);
return true;
}
bool SelectionScriptingInterface::disableListToScene(const QString& listName) {
removeHandler(listName);
return true;
}
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) { template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
{ {
QWriteLocker lock(&_selectionListsLock); QWriteLocker lock(&_selectionListsLock);
@ -303,6 +314,15 @@ void SelectionScriptingInterface::setupHandler(const QString& selectionName) {
(*handler)->initialize(selectionName); (*handler)->initialize(selectionName);
} }
void SelectionScriptingInterface::removeHandler(const QString& selectionName) {
QWriteLocker lock(&_selectionHandlersLock);
auto handler = _handlerMap.find(selectionName);
if (handler != _handlerMap.end()) {
delete handler.value();
_handlerMap.erase(handler);
}
}
void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) { void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) {
{ {
QWriteLocker lock(&_selectionHandlersLock); QWriteLocker lock(&_selectionHandlersLock);

View file

@ -167,6 +167,7 @@ public:
* @returns {bool} true if the selection was successfully enabled for highlight. * @returns {bool} true if the selection was successfully enabled for highlight.
*/ */
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle); Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
/**jsdoc /**jsdoc
* Disable highlighting for the named selection. * Disable highlighting for the named selection.
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false. * If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
@ -176,6 +177,26 @@ public:
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise. * @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
*/ */
Q_INVOKABLE bool disableListHighlight(const QString& listName); Q_INVOKABLE bool disableListHighlight(const QString& listName);
/**jsdoc
* Enable scene selection for the named selection.
* If the Selection doesn't exist, it will be created.
* All objects in the list will be sent to a scene selection.
*
* @function Selection.enableListToScene
* @param listName {string} name of the selection
* @returns {bool} true if the selection was successfully enabled on the scene.
*/
Q_INVOKABLE bool enableListToScene(const QString& listName);
/**jsdoc
* Disable scene selection for the named selection.
* If the Selection doesn't exist or wasn't enabled on the scene then nothing happens simply returning false.
*
* @function Selection.disableListToScene
* @param listName {string} name of the selection
* @returns {bool} true if the selection was successfully disabled on the scene, false otherwise.
*/
Q_INVOKABLE bool disableListToScene(const QString& listName);
/**jsdoc /**jsdoc
* Query the highlight style values for the named selection. * Query the highlight style values for the named selection.
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object. * If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
@ -223,7 +244,7 @@ private:
template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove); template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove);
void setupHandler(const QString& selectionName); void setupHandler(const QString& selectionName);
void removeHandler(const QString& selectionName);
}; };

View file

@ -39,9 +39,11 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
auto scene = renderContext->_scene; auto scene = renderContext->_scene;
if (_isEditEnabled) { if (_isEditEnabled) {
float minIsectDistance = std::numeric_limits<float>::max(); static const std::string selectionName("TransitionEdit");
auto& itemBounds = inputs.get0(); auto scene = renderContext->_scene;
auto editedItem = findNearestItem(renderContext, itemBounds, minIsectDistance); if (!scene->isSelectionEmpty(selectionName)) {
auto selection = scene->getSelection(selectionName);
auto editedItem = selection.getItems().front();
render::Transaction transaction; render::Transaction transaction;
bool hasTransaction{ false }; bool hasTransaction{ false };
@ -64,7 +66,7 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
auto transitionType = categoryToTransition[inputs.get1()]; auto transitionType = categoryToTransition[inputs.get1()];
transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) { transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) {
if (transition == nullptr || transition->isFinished || transition->eventType!=transitionType) { if (transition == nullptr || transition->isFinished || transition->eventType != transitionType) {
// Relaunch transition // Relaunch transition
render::Transaction transaction; render::Transaction transaction;
transaction.addTransitionToItem(id, transitionType); transaction.addTransitionToItem(id, transitionType);
@ -77,6 +79,13 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
if (hasTransaction) { if (hasTransaction) {
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
} }
} else if (render::Item::isValidID(_editedItem)) {
// Remove transition from previously edited item as we've disabled fade edition
render::Transaction transaction;
transaction.removeTransitionFromItem(_editedItem);
scene->enqueueTransaction(transaction);
_editedItem = render::Item::INVALID_ITEM_ID;
}
} }
else if (render::Item::isValidID(_editedItem)) { else if (render::Item::isValidID(_editedItem)) {
// Remove transition from previously edited item as we've disabled fade edition // Remove transition from previously edited item as we've disabled fade edition
@ -87,28 +96,6 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
} }
} }
render::ItemID FadeEditJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const {
const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition();
const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection();
BoxFace face;
glm::vec3 normal;
float isectDistance;
render::ItemID nearestItem = render::Item::INVALID_ITEM_ID;
const float minDistance = 1.f;
const float maxDistance = 50.f;
for (const auto& itemBound : inputs) {
if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) {
auto& item = renderContext->_scene->getItem(itemBound.id);
if (item.getKey().isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance<maxDistance) {
nearestItem = itemBound.id;
minIsectDistance = isectDistance;
}
}
}
return nearestItem;
}
FadeConfig::FadeConfig() FadeConfig::FadeConfig()
{ {
events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f }; events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f };
@ -353,11 +340,9 @@ QString FadeConfig::eventNames[FADE_CATEGORY_COUNT] = {
"avatar_change", "avatar_change",
}; };
void FadeConfig::save() const { void FadeConfig::save(const QString& configFilePath) const {
// Save will only work if the HIFI_USE_SOURCE_TREE_RESOURCES environment variable is set
assert(editedCategory < FADE_CATEGORY_COUNT); assert(editedCategory < FADE_CATEGORY_COUNT);
QJsonObject lProperties; QJsonObject lProperties;
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
QFile file(configFilePath); QFile file(configFilePath);
if (!file.open(QFile::WriteOnly | QFile::Text)) { if (!file.open(QFile::WriteOnly | QFile::Text)) {
qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened"; qWarning() << "Fade event configuration file " << configFilePath << " cannot be opened";
@ -382,8 +367,7 @@ void FadeConfig::save() const {
} }
} }
void FadeConfig::load() { void FadeConfig::load(const QString& configFilePath) {
const QString configFilePath = PathUtils::resourcesPath() + "config/" + eventNames[editedCategory] + ".json";
QFile file(configFilePath); QFile file(configFilePath);
if (!file.exists()) { if (!file.exists()) {
qWarning() << "Fade event configuration file " << configFilePath << " does not exist"; qWarning() << "Fade event configuration file " << configFilePath << " does not exist";
@ -594,7 +578,7 @@ void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Ou
if (update(*jobConfig, scene, transaction, state, deltaTime)) { if (update(*jobConfig, scene, transaction, state, deltaTime)) {
hasTransaction = true; hasTransaction = true;
} }
if (isFirstItem && jobConfig->manualFade && (state.threshold != jobConfig->threshold)) { if (isFirstItem && (state.threshold != jobConfig->threshold)) {
jobConfig->setProperty("threshold", state.threshold); jobConfig->setProperty("threshold", state.threshold);
isFirstItem = false; isFirstItem = false;
} }

View file

@ -160,8 +160,8 @@ public:
float manualThreshold{ 0.f }; float manualThreshold{ 0.f };
bool manualFade{ false }; bool manualFade{ false };
Q_INVOKABLE void save() const; Q_INVOKABLE void save(const QString& filePath) const;
Q_INVOKABLE void load(); Q_INVOKABLE void load(const QString& filePath);
static QString eventNames[FADE_CATEGORY_COUNT]; static QString eventNames[FADE_CATEGORY_COUNT];
@ -190,7 +190,6 @@ private:
bool _isEditEnabled{ false }; bool _isEditEnabled{ false };
render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID }; render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID };
render::ItemID findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const;
}; };
class FadeJob { class FadeJob {

View file

@ -1,5 +1,3 @@
"use strict";
// //
// debugTransition.js // debugTransition.js
// developer/utilities/render // developer/utilities/render
@ -12,11 +10,16 @@
// //
(function() { (function() {
"use strict";
var TABLET_BUTTON_NAME = "Transition"; var TABLET_BUTTON_NAME = "Transition";
var QMLAPP_URL = Script.resolvePath("./transition.qml"); var QMLAPP_URL = Script.resolvePath("./transition.qml");
var ICON_URL = Script.resolvePath("../../../system/assets/images/transition-i.svg"); var ICON_URL = Script.resolvePath("../../../system/assets/images/transition-i.svg");
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/transition-a.svg"); var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/transition-a.svg");
Script.include([
Script.resolvePath("../../../system/libraries/stringHelpers.js"),
]);
var onScreen = false; var onScreen = false;
@ -37,6 +40,71 @@
var hasEventBridge = false; var hasEventBridge = false;
function enableSphereVisualization() {
if (gradientSphere==undefined) {
gradientSphere = Overlays.addOverlay("sphere", {
position: MyAvatar.position,
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0),
dimensions: { x: 1.0, y: 1.0, z: 1.0 },
color: { red: 100, green: 150, blue: 255},
alpha: 0.2,
solid: false
});
}
if (noiseSphere==undefined) {
noiseSphere = Overlays.addOverlay("sphere", {
position: MyAvatar.position,
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0),
dimensions: { x: 1.0, y: 1.0, z: 1.0 },
color: { red: 255, green: 150, blue: 100},
alpha: 0.2,
solid: false
});
}
}
function disableSphereVisualization() {
Overlays.deleteOverlay(noiseSphere);
Overlays.deleteOverlay(gradientSphere);
noiseSphere = undefined
gradientSphere = undefined
}
// Create a Laser pointer used to pick and add objects to selections
var END_DIMENSIONS = { x: 0.05, y: 0.05, z: 0.05 };
var COLOR1 = {red: 255, green: 0, blue: 0};
var COLOR2 = {red: 0, green: 255, blue: 0};
var end1 = {
type: "sphere",
dimensions: END_DIMENSIONS,
color: COLOR1,
ignoreRayIntersection: true
}
var end2 = {
type: "sphere",
dimensions: END_DIMENSIONS,
color: COLOR2,
ignoreRayIntersection: true
}
var laser
function enablePointer() {
laser = Pointers.createPointer(PickType.Ray, {
joint: "Mouse",
filter: Picks.PICK_ENTITIES,
renderStates: [{name: "one", end: end1}],
defaultRenderStates: [{name: "one", end: end2, distance: 2.0}],
enabled: true
});
Pointers.setRenderState(laser, "one");
Pointers.enablePointer(laser)
}
function disablePointer() {
Pointers.disablePointer(laser)
Pointers.removePointer(laser);
}
function wireEventBridge(on) { function wireEventBridge(on) {
if (!tablet) { if (!tablet) {
print("Warning in wireEventBridge(): 'tablet' undefined!"); print("Warning in wireEventBridge(): 'tablet' undefined!");
@ -46,11 +114,15 @@
if (!hasEventBridge) { if (!hasEventBridge) {
tablet.fromQml.connect(fromQml); tablet.fromQml.connect(fromQml);
hasEventBridge = true; hasEventBridge = true;
enablePointer();
Render.getConfig("RenderMainView.FadeEdit")["editFade"] = true
} }
} else { } else {
if (hasEventBridge) { if (hasEventBridge) {
tablet.fromQml.disconnect(fromQml); tablet.fromQml.disconnect(fromQml);
hasEventBridge = false; hasEventBridge = false;
disablePointer();
Render.getConfig("RenderMainView.FadeEdit")["editFade"] = false
} }
} }
} }
@ -66,7 +138,82 @@
wireEventBridge(onScreen); wireEventBridge(onScreen);
} }
var isEditEnabled = false
var noiseSphere
var gradientSphere
var selectedEntity
var editedCategory
var FADE_MIN_SCALE = 0.001
var FADE_MAX_SCALE = 10000.0
function parameterToValuePow(parameter, minValue, maxOverMinValue) {
return minValue * Math.pow(maxOverMinValue, parameter);
//return parameter
}
function update(dt) {
var gradientProperties = Entities.getEntityProperties(selectedEntity, ["position", "dimensions"]);
if (gradientProperties!=undefined) {
var pos = gradientProperties.position
if (pos!=undefined) {
var config = Render.getConfig("RenderMainView.Fade")
//print("Center at "+pos.x+" "+pos.y+" "+pos.z)
var dim = {x:config.baseSizeX, y:config.baseSizeY, z:config.baseSizeZ}
dim.x = parameterToValuePow(dim.x, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
dim.y = parameterToValuePow(dim.y, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
dim.z = parameterToValuePow(dim.z, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
if (editedCategory==4 || editedCategory==5) {
dim.y = gradientProperties.dimensions.y
pos.y = pos.y - gradientProperties.dimensions.y/2
}
Overlays.editOverlay(gradientSphere, { position: pos, dimensions: dim, alpha: config.baseLevel })
dim.x = parameterToValuePow(config.noiseSizeX, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
dim.y = parameterToValuePow(config.noiseSizeY, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
dim.z = parameterToValuePow(config.noiseSizeZ, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE)
Overlays.editOverlay(noiseSphere, { position: pos, dimensions: dim, alpha: config.noiseLevel })
}
}
}
Script.update.connect(update);
function loadConfiguration(fileUrl) {
var config = Render.getConfig("RenderMainView.Fade")
config.load(fileUrl)
}
function saveConfiguration(fileUrl) {
var config = Render.getConfig("RenderMainView.Fade")
config.save(fileUrl)
}
function fromQml(message) { function fromQml(message) {
tokens = message.split('*')
//print("Received '"+message+"' from transition.qml")
command = tokens[0].toLowerCase()
if (command=="category") {
editedCategory = parseInt(tokens[1])
} else if (command=="save") {
var filePath = tokens[1]
print("Raw token = "+filePath)
if (filePath.startsWith("file:///")) {
filePath = filePath.substr(8)
print("Saving configuration to "+filePath)
saveConfiguration(filePath)
} else {
print("Configurations can only be saved to local files")
}
} else if (command=="load") {
var filePath = tokens[1]
if (filePath.startsWith("file:///")) {
filePath = filePath.substr(8)
print("Loading configuration from "+filePath)
loadConfiguration(filePath)
} else {
print("Configurations can only be loaded from local files")
}
}
} }
button.clicked.connect(onClicked); button.clicked.connect(onClicked);
@ -81,4 +228,28 @@
tablet.removeButton(button); tablet.removeButton(button);
}); });
var currentSelectionName = ""
var SelectionList = "TransitionEdit"
Selection.enableListToScene(SelectionList)
Selection.clearSelectedItemsList(SelectionList)
Entities.clickDownOnEntity.connect(function (id, event) {
if (selectedEntity) {
Selection.removeFromSelectedItemsList(SelectionList, "entity", selectedEntity)
}
selectedEntity = id
Selection.addToSelectedItemsList(SelectionList, "entity", selectedEntity)
update()
})
function cleanup() {
disablePointer();
Selection.removeListFromMap(SelectionList)
Selection.disableListToScene(SelectionList);
Overlays.deleteOverlay(noiseSphere);
Overlays.deleteOverlay(gradientSphere);
}
Script.scriptEnding.connect(cleanup);
}()); }());

View file

@ -11,6 +11,7 @@
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.0
import "qrc:///qml/styles-uit" import "qrc:///qml/styles-uit"
import "qrc:///qml/controls-uit" as HifiControls import "qrc:///qml/controls-uit" as HifiControls
@ -22,11 +23,31 @@ Rectangle {
id: root id: root
anchors.margins: hifi.dimensions.contentMargin.x anchors.margins: hifi.dimensions.contentMargin.x
signal sendToScript(var message);
color: hifi.colors.baseGray; color: hifi.colors.baseGray;
property var config: Render.getConfig("RenderMainView.Fade"); property var config: Render.getConfig("RenderMainView.Fade");
property var configEdit: Render.getConfig("RenderMainView.FadeEdit"); property var configEdit: Render.getConfig("RenderMainView.FadeEdit");
FileDialog {
id: fileDialog
title: "Please choose a file"
folder: shortcuts.documents
nameFilters: [ "JSON files (*.json)", "All files (*)" ]
onAccepted: {
root.sendToScript(title.split(" ")[0]+"*"+fileUrl.toString())
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
// by setting the loader source to Null and then recreate it 500ms later
paramWidgetLoader.sourceComponent = undefined;
postpone.interval = 500
postpone.start()
}
onRejected: {
}
Component.onCompleted: visible = false
}
ColumnLayout { ColumnLayout {
spacing: 3 spacing: 3
anchors.left: parent.left anchors.left: parent.left
@ -37,23 +58,14 @@ Rectangle {
} }
RowLayout { RowLayout {
spacing: 20 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
id: root_col id: root_col
HifiControls.CheckBox {
anchors.verticalCenter: parent.verticalCenter
boxSize: 20
text: "Edit"
checked: root.configEdit["editFade"]
onCheckedChanged: {
root.configEdit["editFade"] = checked;
Render.getConfig("RenderMainView.DrawFadedOpaqueBounds").enabled = checked;
}
}
HifiControls.ComboBox { HifiControls.ComboBox {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Layout.fillWidth: true anchors.left : parent.left
width: 300
id: categoryBox id: categoryBox
model: ["Elements enter/leave domain", "Bubble isect. - Owner POV", "Bubble isect. - Trespasser POV", "Another user leaves/arrives", "Changing an avatar"] model: ["Elements enter/leave domain", "Bubble isect. - Owner POV", "Bubble isect. - Trespasser POV", "Another user leaves/arrives", "Changing an avatar"]
Timer { Timer {
@ -61,17 +73,48 @@ Rectangle {
interval: 100; running: false; repeat: false interval: 100; running: false; repeat: false
onTriggered: { onTriggered: {
paramWidgetLoader.sourceComponent = paramWidgets paramWidgetLoader.sourceComponent = paramWidgets
var isTimeBased = categoryBox.currentIndex==0 || categoryBox.currentIndex==3
paramWidgetLoader.item.isTimeBased = isTimeBased
} }
} }
onCurrentIndexChanged: { onCurrentIndexChanged: {
var descriptions = [
"Time based threshold, gradients centered on object",
"Fixed threshold, gradients centered on owner avatar",
"Position based threshold (increases when trespasser moves closer to avatar), gradients centered on trespasser avatar",
"Time based threshold, gradients centered on bottom of object",
"UNSUPPORTED"
]
description.text = descriptions[currentIndex]
root.config["editedCategory"] = currentIndex; root.config["editedCategory"] = currentIndex;
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
// by setting the loader source to Null and then recreate it 100ms later // by setting the loader source to Null and then recreate it 100ms later
paramWidgetLoader.sourceComponent = undefined; paramWidgetLoader.sourceComponent = undefined;
postpone.interval = 100 postpone.interval = 100
postpone.start() postpone.start()
root.sendToScript("category*"+currentIndex)
} }
} }
HifiControls.Button {
action: saveAction
Layout.fillWidth: true
anchors.top: parent.top
anchors.bottom: parent.bottom
}
HifiControls.Button {
action: loadAction
Layout.fillWidth: true
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}
HifiControls.Label {
id: description
text: "..."
Layout.fillWidth: true
wrapMode: Text.WordWrap
} }
RowLayout { RowLayout {
@ -104,19 +147,18 @@ Rectangle {
id: saveAction id: saveAction
text: "Save" text: "Save"
onTriggered: { onTriggered: {
root.config.save() fileDialog.title = "Save configuration..."
fileDialog.selectExisting = false
fileDialog.open()
} }
} }
Action { Action {
id: loadAction id: loadAction
text: "Load" text: "Load"
onTriggered: { onTriggered: {
root.config.load() fileDialog.title = "Load configuration..."
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component fileDialog.selectExisting = true
// by setting the loader source to Null and then recreate it 500ms later fileDialog.open()
paramWidgetLoader.sourceComponent = undefined;
postpone.interval = 500
postpone.start()
} }
} }
@ -128,13 +170,8 @@ Rectangle {
ColumnLayout { ColumnLayout {
spacing: 3 spacing: 3
width: root_col.width width: root_col.width
property bool isTimeBased
HifiControls.CheckBox {
text: "Invert"
boxSize: 20
checked: root.config["isInverted"]
onCheckedChanged: { root.config["isInverted"] = checked }
}
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
@ -196,9 +233,22 @@ Rectangle {
} }
} }
RowLayout {
ConfigSlider { spacing: 20
height: 36 height: 36
HifiControls.CheckBox {
text: "Invert gradient"
anchors.verticalCenter: parent.verticalCenter
boxSize: 20
checked: root.config["isInverted"]
onCheckedChanged: { root.config["isInverted"] = checked }
}
ConfigSlider {
anchors.left: undefined
anchors.verticalCenter: parent.verticalCenter
height: 36
width: 300
label: "Edge Width" label: "Edge Width"
integral: false integral: false
config: root.config config: root.config
@ -206,6 +256,9 @@ Rectangle {
max: 1.0 max: 1.0
min: 0.0 min: 0.0
} }
}
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
@ -278,6 +331,8 @@ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
ConfigSlider { ConfigSlider {
enabled: isTimeBased
opacity: isTimeBased ? 1.0 : 0.0
anchors.left: undefined anchors.left: undefined
anchors.right: undefined anchors.right: undefined
Layout.fillWidth: true Layout.fillWidth: true
@ -290,6 +345,8 @@ Rectangle {
min: 0.1 min: 0.1
} }
HifiControls.ComboBox { HifiControls.ComboBox {
enabled: isTimeBased
opacity: isTimeBased ? 1.0 : 0.0
Layout.fillWidth: true Layout.fillWidth: true
model: ["Linear", "Ease In", "Ease Out", "Ease In / Out"] model: ["Linear", "Ease In", "Ease Out", "Ease In / Out"]
currentIndex: root.config["timing"] currentIndex: root.config["timing"]
@ -365,19 +422,5 @@ Rectangle {
sourceComponent: paramWidgets sourceComponent: paramWidgets
} }
Row {
anchors.left: parent.left
anchors.right: parent.right
Button {
action: saveAction
}
Button {
action: loadAction
}
}
} }
} }