Remove declarative form creation from WindowScriptingInterface

This commit is contained in:
Bradley Austin Davis 2016-02-02 17:09:58 -08:00
parent 63df34541d
commit c1d45b0c78
4 changed files with 8 additions and 911 deletions

View file

@ -23,7 +23,6 @@ Script.include([
"libraries/ToolTip.js",
"libraries/entityPropertyDialogBox.js",
"libraries/entityCameraTool.js",
"libraries/gridTool.js",
"libraries/entityList.js",
@ -32,7 +31,6 @@ Script.include([
var selectionDisplay = SelectionDisplay;
var selectionManager = SelectionManager;
var entityPropertyDialogBox = EntityPropertyDialogBox;
var lightOverlayManager = new LightOverlayManager();

View file

@ -1,457 +0,0 @@
//
// entityPropertyDialogBox.js
// examples
//
// Created by Brad hefta-Gaub on 10/1/14.
// Copyright 2014 High Fidelity, Inc.
//
// This script implements a class useful for building tools for editing entities.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DEGREES_TO_RADIANS = Math.PI / 180.0;
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
EntityPropertyDialogBox = (function () {
var that = {};
var propertiesForEditedEntity;
var editEntityFormArray;
var decimals = 3;
var dimensionX;
var dimensionY;
var dimensionZ;
var rescalePercentage;
var editModelID = -1;
var previousAnimationIsPlaying;
var previousAnimationCurrentFrame;
that.cleanup = function () {
};
that.openDialog = function (entityID) {
print(" Edit Properties.... about to edit properties...");
editModelID = entityID;
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
var properties = propertiesForEditedEntity;
var array = new Array();
var index = 0;
array.push({ label: "Entity Type:" + properties.type, type: "header" });
index++;
array.push({ label: "ID:", value: properties.id });
index++;
array.push({ label: "Locked:", type: "checkbox", value: properties.locked });
index++;
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Shape Type:", value: properties.shapeType });
index++;
array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL });
index++;
array.push({ label: "Animation URL:", value: properties.animation.url });
index++;
array.push({ label: "Animation is playing:", type: "checkbox", value: properties.animation.running });
previousAnimationIsPlaying = properties.animation.running;
index++;
array.push({ label: "Animation FPS:", value: properties.animation.fps });
index++;
array.push({ label: "Animation Frame:", value: properties.animation.currentFrame });
previousAnimationCurrentFrame = properties.animation.currentFrame;
index++;
array.push({ label: "Textures:", value: properties.textures });
index++;
array.push({ label: "Original Textures:\n" + properties.originalTextures, type: "header" });
index++;
}
if (properties.type == "Text") {
array.push({ label: "Text:", value: properties.text });
index++;
array.push({ label: "Line Height:", value: properties.lineHeight });
index++;
array.push({ label: "Text Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.textColor.red });
index++;
array.push({ label: "Green:", value: properties.textColor.green });
index++;
array.push({ label: "Blue:", value: properties.textColor.blue });
index++;
array.push({ label: "Background Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.backgroundColor.red });
index++;
array.push({ label: "Green:", value: properties.backgroundColor.green });
index++;
array.push({ label: "Blue:", value: properties.backgroundColor.blue });
index++;
}
if (properties.type == "PolyVox") {
array.push({ label: "Voxel Space Size:", type: "header" });
index++;
array.push({ label: "X:", value: properties.voxelVolumeSize.x.toFixed(decimals) });
index++;
array.push({ label: "Y:", value: properties.voxelVolumeSize.y.toFixed(decimals) });
index++;
array.push({ label: "Z:", value: properties.voxelVolumeSize.z.toFixed(decimals) });
index++;
array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle });
index++;
array.push({ label: "X-axis Texture URL:", value: properties.xTextureURL });
index++;
array.push({ label: "Y-axis Texture URL:", value: properties.yTextureURL });
index++;
array.push({ label: "Z-axis Texture URL:", value: properties.zTextureURL });
index++;
}
array.push({ label: "Position:", type: "header" });
index++;
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
index++;
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
index++;
array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) });
index++;
array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) });
index++;
array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) });
index++;
array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) });
index++;
array.push({ label: "Rotation:", type: "header" });
index++;
var angles = Quat.safeEulerAngles(properties.rotation);
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
index++;
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
index++;
array.push({ label: "Roll:", value: angles.z.toFixed(decimals) });
index++;
array.push({ label: "Dimensions:", type: "header" });
index++;
array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) });
dimensionX = index;
index++;
array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) });
dimensionY = index;
index++;
array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) });
dimensionZ = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" });
index++;
array.push({ label: "Rescale Percentage:", value: 100 });
rescalePercentage = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Rescale", name: "rescaleDimensions" });
index++;
array.push({ label: "Velocity:", type: "header" });
index++;
array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) });
index++;
array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) });
index++;
array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) });
index++;
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
index++;
// NOTE: angular velocity is in radians/sec but we display degrees/sec for users
array.push({ label: "Angular Pitch:", value: (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(decimals) });
index++;
array.push({ label: "Angular Yaw:", value: (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(decimals) });
index++;
array.push({ label: "Angular Roll:", value: (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(decimals) });
index++;
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
index++;
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
index++;
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
index++;
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
index++;
array.push({ label: "Acceleration X:", value: properties.acceleration.x.toFixed(decimals) });
index++;
array.push({ label: "Acceleration Y:", value: properties.acceleration.y.toFixed(decimals) });
index++;
array.push({ label: "Acceleration Z:", value: properties.acceleration.z.toFixed(decimals) });
index++;
array.push({ label: "Collisions:", type: "header" });
index++;
array.push({ label: "Density:", value: properties.density.toFixed(decimals) });
index++;
array.push({ label: "Collisionless:", type: "checkbox", value: properties.collisionless });
index++;
array.push({ label: "Dynamic:", type: "checkbox", value: properties.dynamic });
index++;
array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL });
index++;
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
index++;
array.push({ label: "Visible:", type: "checkbox", value: properties.visible });
index++;
array.push({ label: "Script:", value: properties.script });
index++;
if (properties.type == "Box" || properties.type == "Sphere") {
array.push({ label: "Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.color.red });
index++;
array.push({ label: "Green:", value: properties.color.green });
index++;
array.push({ label: "Blue:", value: properties.color.blue });
index++;
}
if (properties.type == "Light") {
array.push({ label: "Light Properties:", type: "header" });
index++;
array.push({ label: "Is Spot Light:", value: properties.isSpotlight });
index++;
array.push({ label: "Diffuse Red:", value: properties.diffuseColor.red });
index++;
array.push({ label: "Diffuse Green:", value: properties.diffuseColor.green });
index++;
array.push({ label: "Diffuse Blue:", value: properties.diffuseColor.blue });
index++;
array.push({ label: "Ambient Red:", value: properties.ambientColor.red });
index++;
array.push({ label: "Ambient Green:", value: properties.ambientColor.green });
index++;
array.push({ label: "Ambient Blue:", value: properties.ambientColor.blue });
index++;
array.push({ label: "Specular Red:", value: properties.specularColor.red });
index++;
array.push({ label: "Specular Green:", value: properties.specularColor.green });
index++;
array.push({ label: "Specular Blue:", value: properties.specularColor.blue });
index++;
array.push({ label: "Constant Attenuation:", value: properties.constantAttenuation });
index++;
array.push({ label: "Linear Attenuation:", value: properties.linearAttenuation });
index++;
array.push({ label: "Quadratic Attenuation:", value: properties.quadraticAttenuation });
index++;
array.push({ label: "Exponent:", value: properties.exponent });
index++;
array.push({ label: "Cutoff (in degrees):", value: properties.cutoff });
index++;
}
array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" });
index++;
array.push({ button: "Cancel" });
index++;
editEntityFormArray = array;
Window.nonBlockingForm("Edit Properties", array);
};
Window.inlineButtonClicked.connect(function (name) {
if (name == "previewCamera") {
Camera.mode = "entity";
Camera.cameraEntity = propertiesForEditedEntity.id;
}
if (name == "resetDimensions") {
Window.reloadNonBlockingForm([
{ value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX },
{ value: propertiesForEditedEntity.naturalDimensions.y.toFixed(decimals), oldIndex: dimensionY },
{ value: propertiesForEditedEntity.naturalDimensions.z.toFixed(decimals), oldIndex: dimensionZ }
]);
}
if (name == "rescaleDimensions") {
var peekValues = editEntityFormArray;
Window.peekNonBlockingFormResult(peekValues);
var peekX = peekValues[dimensionX].value;
var peekY = peekValues[dimensionY].value;
var peekZ = peekValues[dimensionZ].value;
var peekRescale = peekValues[rescalePercentage].value;
var rescaledX = peekX * peekRescale / 100.0;
var rescaledY = peekY * peekRescale / 100.0;
var rescaledZ = peekZ * peekRescale / 100.0;
Window.reloadNonBlockingForm([
{ value: rescaledX.toFixed(decimals), oldIndex: dimensionX },
{ value: rescaledY.toFixed(decimals), oldIndex: dimensionY },
{ value: rescaledZ.toFixed(decimals), oldIndex: dimensionZ },
{ value: 100, oldIndex: rescalePercentage }
]);
}
});
Window.nonBlockingFormClosed.connect(function() {
array = editEntityFormArray;
if (Window.getNonBlockingFormResult(array)) {
var properties = propertiesForEditedEntity;
var index = 0;
index++; // skip type header
index++; // skip id item
properties.locked = array[index++].value;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.shapeType = array[index++].value;
properties.compoundShapeURL = array[index++].value;
properties.animation.url = array[index++].value;
var newAnimationIsPlaying = array[index++].value;
if (previousAnimationIsPlaying != newAnimationIsPlaying) {
properties.animation.running = newAnimationIsPlaying;
} else {
delete properties.animation.running;
}
properties.animation.fps = array[index++].value;
var newAnimationCurrentFrame = array[index++].value;
if (previousAnimationCurrentFrame != newAnimationCurrentFrame) {
properties.animation.currentFrame = newAnimationCurrentFrame;
} else {
delete properties.animation.currentFrame;
}
properties.textures = array[index++].value;
index++; // skip textureNames label
}
if (properties.type == "Text") {
properties.text = array[index++].value;
properties.lineHeight = array[index++].value;
index++; // skip header
properties.textColor.red = array[index++].value;
properties.textColor.green = array[index++].value;
properties.textColor.blue = array[index++].value;
index++; // skip header
properties.backgroundColor.red = array[index++].value;
properties.backgroundColor.green = array[index++].value;
properties.backgroundColor.blue = array[index++].value;
}
if (properties.type == "PolyVox") {
properties.shapeType = array[index++].value;
index++; // skip header
properties.voxelVolumeSize.x = array[index++].value;
properties.voxelVolumeSize.y = array[index++].value;
properties.voxelVolumeSize.z = array[index++].value;
properties.voxelSurfaceStyle = array[index++].value;
properties.xTextureURL = array[index++].value;
properties.yTextureURL = array[index++].value;
properties.zTextureURL = array[index++].value;
}
index++; // skip header
properties.position.x = array[index++].value;
properties.position.y = array[index++].value;
properties.position.z = array[index++].value;
properties.registrationPoint.x = array[index++].value;
properties.registrationPoint.y = array[index++].value;
properties.registrationPoint.z = array[index++].value;
index++; // skip header
var angles = Quat.safeEulerAngles(properties.rotation);
angles.x = array[index++].value;
angles.y = array[index++].value;
angles.z = array[index++].value;
properties.rotation = Quat.fromVec3Degrees(angles);
index++; // skip header
properties.dimensions.x = array[index++].value;
properties.dimensions.y = array[index++].value;
properties.dimensions.z = array[index++].value;
index++; // skip reset button
index++; // skip rescale percentage
index++; // skip rescale button
index++; // skip header
properties.velocity.x = array[index++].value;
properties.velocity.y = array[index++].value;
properties.velocity.z = array[index++].value;
properties.damping = array[index++].value;
// NOTE: angular velocity is in radians/sec but we display degrees/sec for users
properties.angularVelocity.x = array[index++].value * DEGREES_TO_RADIANS;
properties.angularVelocity.y = array[index++].value * DEGREES_TO_RADIANS;
properties.angularVelocity.z = array[index++].value * DEGREES_TO_RADIANS;
properties.angularDamping = array[index++].value;
properties.gravity.x = array[index++].value;
properties.gravity.y = array[index++].value;
properties.gravity.z = array[index++].value;
properties.acceleration.x = array[index++].value;
properties.acceleration.y = array[index++].value;
properties.acceleration.z = array[index++].value;
index++; // skip header
properties.density = array[index++].value;
properties.collisionless = array[index++].value;
properties.dynamic = array[index++].value;
properties.lifetime = array[index++].value;
properties.visible = array[index++].value;
properties.script = array[index++].value;
if (properties.type == "Box" || properties.type == "Sphere") {
index++; // skip header
properties.color.red = array[index++].value;
properties.color.green = array[index++].value;
properties.color.blue = array[index++].value;
}
if (properties.type == "Light") {
index++; // skip header
properties.isSpotlight = array[index++].value;
properties.diffuseColor.red = array[index++].value;
properties.diffuseColor.green = array[index++].value;
properties.diffuseColor.blue = array[index++].value;
properties.ambientColor.red = array[index++].value;
properties.ambientColor.green = array[index++].value;
properties.ambientColor.blue = array[index++].value;
properties.specularColor.red = array[index++].value;
properties.specularColor.green = array[index++].value;
properties.specularColor.blue = array[index++].value;
properties.constantAttenuation = array[index++].value;
properties.linearAttenuation = array[index++].value;
properties.quadraticAttenuation = array[index++].value;
properties.exponent = array[index++].value;
properties.cutoff = array[index++].value;
}
Entities.editEntity(editModelID, properties);
if (typeof(selectionDisplay) != "undefined") {
selectionDisplay.select(editModelID, false);
}
}
modelSelected = false;
});
return that;
}());

View file

@ -27,11 +27,7 @@
#include "WindowScriptingInterface.h"
WindowScriptingInterface::WindowScriptingInterface() :
_editDialog(NULL),
_nonBlockingFormActive(false),
_formResult(QDialog::Rejected)
{
WindowScriptingInterface::WindowScriptingInterface() {
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
@ -98,14 +94,6 @@ QScriptValue WindowScriptingInterface::confirm(const QString& message) {
return retVal;
}
QScriptValue WindowScriptingInterface::form(const QString& title, QScriptValue form) {
QScriptValue retVal;
QMetaObject::invokeMethod(this, "showForm", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, retVal),
Q_ARG(const QString&, title), Q_ARG(QScriptValue, form));
return retVal;
}
QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {
QScriptValue retVal;
QMetaObject::invokeMethod(this, "showPrompt", Qt::BlockingQueuedConnection,
@ -139,32 +127,6 @@ QScriptValue WindowScriptingInterface::s3Browse(const QString& nameFilter) {
return retVal;
}
void WindowScriptingInterface::nonBlockingForm(const QString& title, QScriptValue form) {
QMetaObject::invokeMethod(this, "showNonBlockingForm", Qt::BlockingQueuedConnection,
Q_ARG(const QString&, title), Q_ARG(QScriptValue, form));
}
void WindowScriptingInterface::reloadNonBlockingForm(QScriptValue newValues) {
QMetaObject::invokeMethod(this, "doReloadNonBlockingForm", Qt::BlockingQueuedConnection,
Q_ARG(QScriptValue, newValues));
}
QScriptValue WindowScriptingInterface::getNonBlockingFormResult(QScriptValue form) {
QScriptValue retVal;
QMetaObject::invokeMethod(this, "doGetNonBlockingFormResult", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, retVal),
Q_ARG(QScriptValue, form));
return retVal;
}
QScriptValue WindowScriptingInterface::peekNonBlockingFormResult(QScriptValue form) {
QScriptValue retVal;
QMetaObject::invokeMethod(this, "doPeekNonBlockingFormResult", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, retVal),
Q_ARG(QScriptValue, form));
return retVal;
}
/// Display an alert box
/// \param const QString& message message to display
@ -217,402 +179,23 @@ void WindowScriptingInterface::inlineButtonClicked() {
emit inlineButtonClicked(name);
}
QString WindowScriptingInterface::jsRegExp2QtRegExp(QString string) {
QString WindowScriptingInterface::jsRegExp2QtRegExp(const QString& string) {
// Converts string representation of RegExp from JavaScript format to Qt format.
return string.mid(1, string.length() - 2) // No enclosing slashes.
.replace("\\/", "/"); // No escaping of forward slash.
}
void WindowScriptingInterface::showNonBlockingForm(const QString& title, QScriptValue form) {
if (!form.isArray() || (form.isArray() && form.property("length").toInt32() <= 0)) {
return;
}
// what should we do if someone calls us while we still think we have a dialog showing???
if (_nonBlockingFormActive) {
qDebug() << "Show Non-Blocking Form called when form already active.";
return;
}
_form = form;
_editDialog = createForm(title, _form);
_nonBlockingFormActive = true;
connect(_editDialog, SIGNAL(accepted()), this, SLOT(nonBlockingFormAccepted()));
connect(_editDialog, SIGNAL(rejected()), this, SLOT(nonBlockingFormRejected()));
_editDialog->setModal(true);
_editDialog->show();
}
void WindowScriptingInterface::doReloadNonBlockingForm(QScriptValue newValues) {
if (!newValues.isArray() || (newValues.isArray() && newValues.property("length").toInt32() <= 0)) {
return;
}
// what should we do if someone calls us while we still think we have a dialog showing???
if (!_editDialog) {
qDebug() << "Reload Non-Blocking Form called when no form is active.";
return;
}
for (int i = 0; i < newValues.property("length").toInt32(); ++i) {
QScriptValue item = newValues.property(i);
if (item.property("oldIndex").isValid()) {
int oldIndex = item.property("oldIndex").toInt32();
QScriptValue oldItem = _form.property(oldIndex);
if (oldItem.isValid()) {
QLineEdit* originalEdit = _edits[oldItem.property("editIndex").toInt32()];
originalEdit->setText(item.property("value").toString());
}
}
}
}
bool WindowScriptingInterface::nonBlockingFormActive() {
return _nonBlockingFormActive;
}
QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue array) {
QScriptValue retVal;
int e = -1;
int d = -1;
int c = -1;
int h = -1;
for (int i = 0; i < _form.property("length").toInt32(); ++i) {
QScriptValue item = _form.property(i);
QScriptValue value = item.property("value");
if (item.property("button").toString() != "") {
// Nothing to do
} else if (item.property("type").toString() == "inlineButton") {
// Nothing to do
} else if (item.property("type").toString() == "header") {
// Nothing to do
} else if (item.property("directory").toString() != "") {
d += 1;
value = _directories.at(d)->property("path").toString();
item.setProperty("directory", value);
_form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
} else if (item.property("type").toString() == "checkbox") {
h++;
value = _checks.at(h)->checkState() == Qt::Checked;
item.setProperty("value", value);
_form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
if (value.isNumber()) {
value = _edits.at(e)->text().toDouble(&ok);
} else if (value.isString()) {
value = _edits.at(e)->text();
} else if (value.isBool()) {
if (_edits.at(e)->text() == "true") {
value = true;
} else if (_edits.at(e)->text() == "false") {
value = false;
} else {
ok = false;
}
}
if (ok) {
item.setProperty("value", value);
_form.setProperty(i, item);
}
}
}
array = _form;
return (_formResult == QDialog::Accepted);
}
QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue array) {
QScriptValue retVal;
if (_formResult == QDialog::Accepted) {
int e = -1;
int d = -1;
int c = -1;
int h = -1;
for (int i = 0; i < _form.property("length").toInt32(); ++i) {
QScriptValue item = _form.property(i);
QScriptValue value = item.property("value");
if (item.property("button").toString() != "") {
// Nothing to do
} else if (item.property("type").toString() == "inlineButton") {
// Nothing to do
} else if (item.property("type").toString() == "header") {
// Nothing to do
} else if (item.property("directory").toString() != "") {
d += 1;
value = _directories.at(d)->property("path").toString();
item.setProperty("directory", value);
_form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
} else if (item.property("type").toString() == "checkbox") {
h++;
value = _checks.at(h)->checkState() == Qt::Checked;
item.setProperty("value", value);
_form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
if (value.isNumber()) {
value = _edits.at(e)->text().toDouble(&ok);
} else if (value.isString()) {
value = _edits.at(e)->text();
} else if (value.isBool()) {
if (_edits.at(e)->text() == "true") {
value = true;
} else if (_edits.at(e)->text() == "false") {
value = false;
} else {
ok = false;
}
}
if (ok) {
item.setProperty("value", value);
_form.setProperty(i, item);
}
}
}
}
delete _editDialog;
_editDialog = NULL;
_form = QScriptValue();
_edits.clear();
_directories.clear();
_combos.clear();
_checks.clear();
array = _form;
return (_formResult == QDialog::Accepted);
}
/// Display a form layout with an edit box
/// \param const QString& title title to display
/// \param const QScriptValue form to display as an array of objects:
/// - label, value
/// - label, directory, title, display regexp, validate regexp, error message
/// - button ("Cancel")
/// \return QScriptValue `true` if 'OK' was clicked, `false` otherwise
QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptValue form) {
if (form.isArray() && form.property("length").toInt32() <= 0) {
return false;
}
QDialog* editDialog = createForm(title, form);
int result = editDialog->exec();
if (result == QDialog::Accepted) {
int e = -1;
int d = -1;
int c = -1;
int h = -1;
for (int i = 0; i < form.property("length").toInt32(); ++i) {
QScriptValue item = form.property(i);
QScriptValue value = item.property("value");
if (item.property("button").toString() != "") {
// Nothing to do
} else if (item.property("type").toString() == "inlineButton") {
// Nothing to do
} else if (item.property("type").toString() == "header") {
// Nothing to do
} else if (item.property("directory").toString() != "") {
d += 1;
value = _directories.at(d)->property("path").toString();
item.setProperty("directory", value);
form.setProperty(i, item);
} else if (item.property("options").isArray()) {
c += 1;
item.setProperty("value",
_combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ?
item.property("options").property(_combos.at(c)->currentIndex()) :
form.engine()->undefinedValue()
);
form.setProperty(i, item);
} else if (item.property("type").toString() == "checkbox") {
h++;
value = _checks.at(h)->checkState() == Qt::Checked;
item.setProperty("value", value);
form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
if (value.isNumber()) {
value = _edits.at(e)->text().toDouble(&ok);
} else if (value.isString()) {
value = _edits.at(e)->text();
} else if (value.isBool()) {
if (_edits.at(e)->text() == "true") {
value = true;
} else if (_edits.at(e)->text() == "false") {
value = false;
} else {
ok = false;
}
}
if (ok) {
item.setProperty("value", value);
form.setProperty(i, item);
}
}
}
}
delete editDialog;
_combos.clear();
_checks.clear();
_edits.clear();
_directories.clear();
return (result == QDialog::Accepted);
}
QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue form) {
QDialog* editDialog = new QDialog(qApp->getWindow());
editDialog->setWindowTitle(title);
bool cancelButton = false;
QVBoxLayout* layout = new QVBoxLayout();
editDialog->setLayout(layout);
QScrollArea* area = new QScrollArea();
layout->addWidget(area);
area->setWidgetResizable(true);
QWidget* container = new QWidget();
QFormLayout* formLayout = new QFormLayout();
container->setLayout(formLayout);
container->sizePolicy().setHorizontalStretch(1);
formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop);
formLayout->setLabelAlignment(Qt::AlignLeft);
area->setWidget(container);
for (int i = 0; i < form.property("length").toInt32(); ++i) {
QScriptValue item = form.property(i);
if (item.property("button").toString() != "") {
cancelButton = cancelButton || item.property("button").toString().toLower() == "cancel";
} else if (item.property("directory").toString() != "") {
QString path = item.property("directory").toString();
QString title = item.property("title").toString();
if (title == "") {
title = "Choose Directory";
}
QString displayAsString = item.property("displayAs").toString();
QRegExp displayAs = QRegExp(displayAsString != "" ? jsRegExp2QtRegExp(displayAsString) : "^(.*)$");
QString validateAsString = item.property("validateAs").toString();
QRegExp validateAs = QRegExp(validateAsString != "" ? jsRegExp2QtRegExp(validateAsString) : ".*");
QString errorMessage = item.property("errorMessage").toString();
if (errorMessage == "") {
errorMessage = "Invalid directory";
}
QPushButton* directory = new QPushButton(displayAs.cap(1));
directory->setProperty("title", title);
directory->setProperty("path", path);
directory->setProperty("displayAs", displayAs);
directory->setProperty("validateAs", validateAs);
directory->setProperty("errorMessage", errorMessage);
displayAs.indexIn(path);
directory->setText(displayAs.cap(1) != "" ? displayAs.cap(1) : ".");
directory->setMinimumWidth(200);
_directories.push_back(directory);
formLayout->addRow(new QLabel(item.property("label").toString()), directory);
connect(directory, SIGNAL(clicked(bool)), SLOT(chooseDirectory()));
} else if (item.property("type").toString() == "inlineButton") {
QString buttonLabel = item.property("buttonLabel").toString();
QPushButton* inlineButton = new QPushButton(buttonLabel);
inlineButton->setMinimumWidth(200);
inlineButton->setProperty("name", item.property("name").toString());
formLayout->addRow(new QLabel(item.property("label").toString()), inlineButton);
connect(inlineButton, SIGNAL(clicked(bool)), SLOT(inlineButtonClicked()));
} else if (item.property("type").toString() == "header") {
formLayout->addRow(new QLabel(item.property("label").toString()));
} else if (item.property("options").isArray()) {
QComboBox* combo = new QComboBox();
combo->setMinimumWidth(200);
qint32 options_count = item.property("options").property("length").toInt32();
for (qint32 i = 0; i < options_count; i++) {
combo->addItem(item.property("options").property(i).toString());
}
_combos.push_back(combo);
formLayout->addRow(new QLabel(item.property("label").toString()), combo);
} else if (item.property("type").toString() == "checkbox") {
QCheckBox* check = new QCheckBox();
check->setTristate(false);
check->setCheckState(item.property("value").toString() == "true" ? Qt::Checked : Qt::Unchecked);
_checks.push_back(check);
formLayout->addRow(new QLabel(item.property("label").toString()), check);
} else {
QLineEdit* edit = new QLineEdit(item.property("value").toString());
edit->setMinimumWidth(200);
int editIndex = _edits.size();
_edits.push_back(edit);
item.setProperty("editIndex", editIndex);
formLayout->addRow(new QLabel(item.property("label").toString()), edit);
}
}
QDialogButtonBox* buttons = new QDialogButtonBox(
QDialogButtonBox::Ok
| (cancelButton ? QDialogButtonBox::Cancel : QDialogButtonBox::NoButton)
);
connect(buttons, SIGNAL(accepted()), editDialog, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), editDialog, SLOT(reject()));
layout->addWidget(buttons);
return editDialog;
}
/// Display a prompt with a text box
/// \param const QString& message message to display
/// \param const QString& defaultText default text in the text box
/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise.
QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) {
QInputDialog promptDialog(qApp->getWindow());
promptDialog.setWindowTitle("");
promptDialog.setLabelText(message);
promptDialog.setTextValue(defaultText);
promptDialog.setFixedSize(600, 200);
if (promptDialog.exec() == QDialog::Accepted) {
return QScriptValue(promptDialog.textValue());
bool ok = false;
QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok);
if (!ok) {
return QScriptValue::NullValue;
}
return QScriptValue::NullValue;
return QScriptValue(result);
}
/// Display a file dialog. If `directory` is an invalid file or directory the browser will start at the current

View file

@ -43,59 +43,32 @@ public slots:
void raiseMainWindow();
QScriptValue alert(const QString& message = "");
QScriptValue confirm(const QString& message = "");
QScriptValue form(const QString& title, QScriptValue array);
QScriptValue prompt(const QString& message = "", const QString& defaultText = "");
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue s3Browse(const QString& nameFilter = "");
void nonBlockingForm(const QString& title, QScriptValue array);
void reloadNonBlockingForm(QScriptValue array);
QScriptValue getNonBlockingFormResult(QScriptValue array);
QScriptValue peekNonBlockingFormResult(QScriptValue array);
signals:
void domainChanged(const QString& domainHostname);
void inlineButtonClicked(const QString& name);
void nonBlockingFormClosed();
void svoImportRequested(const QString& url);
void domainConnectionRefused(const QString& reason);
private slots:
QScriptValue showAlert(const QString& message);
QScriptValue showConfirm(const QString& message);
QScriptValue showForm(const QString& title, QScriptValue form);
QScriptValue showPrompt(const QString& message, const QString& defaultText);
QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter,
QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen);
QScriptValue showS3Browse(const QString& nameFilter);
void showNonBlockingForm(const QString& title, QScriptValue array);
void doReloadNonBlockingForm(QScriptValue array);
bool nonBlockingFormActive();
QScriptValue doGetNonBlockingFormResult(QScriptValue array);
QScriptValue doPeekNonBlockingFormResult(QScriptValue array);
void chooseDirectory();
void inlineButtonClicked();
void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); }
void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); }
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
private:
QString jsRegExp2QtRegExp(QString string);
QDialog* createForm(const QString& title, QScriptValue form);
QDialog* _editDialog;
QScriptValue _form;
bool _nonBlockingFormActive;
int _formResult;
QVector<QComboBox*> _combos;
QVector<QCheckBox*> _checks;
QVector<QLineEdit*> _edits;
QVector<QPushButton*> _directories;
QString jsRegExp2QtRegExp(const QString& string);
};
#endif // hifi_WindowScriptingInterface_h