mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 15:59:49 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into red
This commit is contained in:
commit
57b6ad3c54
30 changed files with 369 additions and 1131 deletions
|
@ -23,7 +23,6 @@ Script.include([
|
||||||
|
|
||||||
"libraries/ToolTip.js",
|
"libraries/ToolTip.js",
|
||||||
|
|
||||||
"libraries/entityPropertyDialogBox.js",
|
|
||||||
"libraries/entityCameraTool.js",
|
"libraries/entityCameraTool.js",
|
||||||
"libraries/gridTool.js",
|
"libraries/gridTool.js",
|
||||||
"libraries/entityList.js",
|
"libraries/entityList.js",
|
||||||
|
@ -32,7 +31,6 @@ Script.include([
|
||||||
|
|
||||||
var selectionDisplay = SelectionDisplay;
|
var selectionDisplay = SelectionDisplay;
|
||||||
var selectionManager = SelectionManager;
|
var selectionManager = SelectionManager;
|
||||||
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
|
||||||
|
|
||||||
var lightOverlayManager = new LightOverlayManager();
|
var lightOverlayManager = new LightOverlayManager();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
||||||
}());
|
|
|
@ -10,7 +10,8 @@ Original.CheckBox {
|
||||||
}
|
}
|
||||||
label: Text {
|
label: Text {
|
||||||
text: control.text
|
text: control.text
|
||||||
|
renderType: Text.QtRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
7
interface/resources/qml/controls/TextField.qml
Normal file
7
interface/resources/qml/controls/TextField.qml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
|
}
|
|
@ -262,11 +262,10 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component { id: fileDialogBuilder; FileDialog { } }
|
Component { id: fileDialogBuilder; FileDialog { } }
|
||||||
function fileOpenDialog(properties) {
|
function fileDialog(properties) {
|
||||||
return fileDialogBuilder.createObject(desktop, properties);
|
return fileDialogBuilder.createObject(desktop, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MenuMouseHandler { id: menuPopperUpper }
|
MenuMouseHandler { id: menuPopperUpper }
|
||||||
function popupMenu(point) {
|
function popupMenu(point) {
|
||||||
menuPopperUpper.popup(desktop, rootMenu.items, point);
|
menuPopperUpper.popup(desktop, rootMenu.items, point);
|
||||||
|
|
|
@ -2,6 +2,8 @@ import QtQuick 2.0
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import Qt.labs.folderlistmodel 2.1
|
import Qt.labs.folderlistmodel 2.1
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
|
|
||||||
import ".."
|
import ".."
|
||||||
import "../windows"
|
import "../windows"
|
||||||
|
@ -34,18 +36,26 @@ ModalWindow {
|
||||||
// Set from OffscreenUi::getOpenFile()
|
// Set from OffscreenUi::getOpenFile()
|
||||||
property int options; // <-- FIXME unused
|
property int options; // <-- FIXME unused
|
||||||
|
|
||||||
|
|
||||||
property bool selectDirectory: false;
|
property bool selectDirectory: false;
|
||||||
property bool showHidden: false;
|
property bool showHidden: false;
|
||||||
// FIXME implement
|
// FIXME implement
|
||||||
property bool multiSelect: false;
|
property bool multiSelect: false;
|
||||||
// FIXME implement
|
|
||||||
property bool saveDialog: false;
|
property bool saveDialog: false;
|
||||||
property var helper: fileDialogHelper
|
property var helper: fileDialogHelper
|
||||||
property alias model: fileTableView.model
|
property alias model: fileTableView.model
|
||||||
|
property var drives: helper.drives()
|
||||||
|
|
||||||
signal selectedFile(var file);
|
signal selectedFile(var file);
|
||||||
signal canceled();
|
signal canceled();
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("Helper " + helper + " drives " + drives)
|
||||||
|
drivesSelector.onCurrentTextChanged.connect(function(){
|
||||||
|
root.dir = helper.pathToUrl(drivesSelector.currentText);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "white"
|
color: "white"
|
||||||
|
@ -77,11 +87,22 @@ ModalWindow {
|
||||||
size: 32
|
size: 32
|
||||||
onClicked: d.navigateHome();
|
onClicked: d.navigateHome();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VrControls.ComboBox {
|
||||||
|
id: drivesSelector
|
||||||
|
width: 48
|
||||||
|
height: homeButton.height
|
||||||
|
model: drives
|
||||||
|
visible: drives.length > 1
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: currentDirectory
|
id: currentDirectory
|
||||||
height: homeButton.height
|
height: homeButton.height
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 }
|
anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 }
|
||||||
property var lastValidFolder: helper.urlToPath(model.folder)
|
property var lastValidFolder: helper.urlToPath(model.folder)
|
||||||
onLastValidFolderChanged: text = lastValidFolder;
|
onLastValidFolderChanged: text = lastValidFolder;
|
||||||
|
@ -122,6 +143,8 @@ ModalWindow {
|
||||||
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
||||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||||
currentSelection.text = helper.urlToPath(currentSelectionUrl);
|
currentSelection.text = helper.urlToPath(currentSelectionUrl);
|
||||||
|
} else {
|
||||||
|
currentSelection.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,17 +196,19 @@ ModalWindow {
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
fileTableView.model.folder = file
|
fileTableView.model.folder = file
|
||||||
} else {
|
} else {
|
||||||
root.selectedFile(file);
|
okAction.trigger();
|
||||||
root.destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: currentSelection
|
id: currentSelection
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top }
|
anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top }
|
||||||
readOnly: true
|
readOnly: !root.saveDialog
|
||||||
activeFocusOnTab: false
|
activeFocusOnTab: !readOnly
|
||||||
|
onActiveFocusChanged: if (activeFocus) { selectAll(); }
|
||||||
|
onAccepted: okAction.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTypeSelection {
|
FileTypeSelection {
|
||||||
|
@ -203,25 +228,91 @@ ModalWindow {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
Button {
|
Button {
|
||||||
id: openButton
|
id: openButton
|
||||||
text: root.selectDirectory ? "Choose" : "Open"
|
action: okAction
|
||||||
enabled: currentSelection.text ? true : false
|
Keys.onReturnPressed: okAction.trigger()
|
||||||
onClicked: { selectedFile(d.currentSelectionUrl); root.visible = false; }
|
|
||||||
Keys.onReturnPressed: { selectedFile(d.currentSelectionUrl); root.visible = false; }
|
|
||||||
|
|
||||||
KeyNavigation.up: selectionType
|
KeyNavigation.up: selectionType
|
||||||
KeyNavigation.left: selectionType
|
KeyNavigation.left: selectionType
|
||||||
KeyNavigation.right: cancelButton
|
KeyNavigation.right: cancelButton
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
text: "Cancel"
|
action: cancelAction
|
||||||
KeyNavigation.up: selectionType
|
KeyNavigation.up: selectionType
|
||||||
KeyNavigation.left: openButton
|
KeyNavigation.left: openButton
|
||||||
KeyNavigation.right: fileTableView.contentItem
|
KeyNavigation.right: fileTableView.contentItem
|
||||||
Keys.onReturnPressed: { canceled(); root.enabled = false }
|
Keys.onReturnPressed: { canceled(); root.enabled = false }
|
||||||
onClicked: { canceled(); root.visible = false; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: okAction
|
||||||
|
text: root.saveDialog ? "Save" : (root.selectDirectory ? "Choose" : "Open")
|
||||||
|
enabled: currentSelection.text ? true : false
|
||||||
|
onTriggered: okActionTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: okActionTimer
|
||||||
|
interval: 50
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
if (!root.saveDialog) {
|
||||||
|
selectedFile(d.currentSelectionUrl);
|
||||||
|
root.destroy()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Handle the ambiguity between different cases
|
||||||
|
// * typed name (with or without extension)
|
||||||
|
// * full path vs relative vs filename only
|
||||||
|
var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter);
|
||||||
|
|
||||||
|
if (!selection) {
|
||||||
|
desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" })
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper.urlIsDir(selection)) {
|
||||||
|
root.dir = selection;
|
||||||
|
currentSelection.text = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the file is a valid target
|
||||||
|
if (!helper.urlIsWritable(selection)) {
|
||||||
|
desktop.messageBox({
|
||||||
|
icon: OriginalDialogs.StandardIcon.Warning,
|
||||||
|
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
|
||||||
|
text: "Unable to write to location " + selection
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper.urlExists(selection)) {
|
||||||
|
var messageBox = desktop.messageBox({
|
||||||
|
icon: OriginalDialogs.StandardIcon.Question,
|
||||||
|
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
|
||||||
|
text: "Do you wish to overwrite " + selection + "?",
|
||||||
|
});
|
||||||
|
var result = messageBox.exec();
|
||||||
|
if (OriginalDialogs.StandardButton.Yes !== result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Selecting " + selection)
|
||||||
|
selectedFile(selection);
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: cancelAction
|
||||||
|
text: "Cancel"
|
||||||
|
onTriggered: { canceled(); root.visible = false; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
|
|
|
@ -25,6 +25,10 @@ ModalWindow {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exec() {
|
||||||
|
return OffscreenUi.waitForMessageBoxResult(root);
|
||||||
|
}
|
||||||
|
|
||||||
property alias detailedText: detailedText.text
|
property alias detailedText: detailedText.text
|
||||||
property alias text: mainTextContainer.text
|
property alias text: mainTextContainer.text
|
||||||
property alias informativeText: informativeTextContainer.text
|
property alias informativeText: informativeTextContainer.text
|
||||||
|
|
|
@ -62,7 +62,7 @@ ModalWindow {
|
||||||
Item {
|
Item {
|
||||||
anchors { top: mainTextContainer.bottom; bottom: buttons.top; left: parent.left; right: parent.right; margins: d.spacing }
|
anchors { top: mainTextContainer.bottom; bottom: buttons.top; left: parent.left; right: parent.right; margins: d.spacing }
|
||||||
// FIXME make a text field type that can be bound to a history for autocompletion
|
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||||
TextField {
|
VrControls.TextField {
|
||||||
id: textResult
|
id: textResult
|
||||||
focus: items ? false : true
|
focus: items ? false : true
|
||||||
visible: items ? false : true
|
visible: items ? false : true
|
||||||
|
|
|
@ -219,7 +219,7 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
HifiControls.TextField {
|
||||||
id: filterEdit
|
id: filterEdit
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
@ -252,7 +252,7 @@ Window {
|
||||||
TableViewColumn { title: "Name"; role: "display"; }
|
TableViewColumn { title: "Name"; role: "display"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
HifiControls.TextField {
|
||||||
id: selectedScript
|
id: selectedScript
|
||||||
readOnly: true
|
readOnly: true
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|
|
@ -17,9 +17,13 @@ VrControls.ComboBox {
|
||||||
onCurrentTextChanged: {
|
onCurrentTextChanged: {
|
||||||
var globRegex = /\((.*)\)$/
|
var globRegex = /\((.*)\)$/
|
||||||
var globs = globRegex.exec(currentText);
|
var globs = globRegex.exec(currentText);
|
||||||
if (!globs[1]) {
|
if (!globs || !globs[1]) {
|
||||||
console.warn("Unable to parse filter " + currentText);
|
globRegex = /^(\*.*)$/
|
||||||
return;
|
globs = globRegex.exec(currentText);
|
||||||
|
if (!globs || !globs[1]) {
|
||||||
|
console.warn("Unable to parse filter " + currentText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
currentFilter = globs[1].split(" ");
|
currentFilter = globs[1].split(" ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
Preference {
|
Preference {
|
||||||
id: root
|
id: root
|
||||||
|
@ -50,6 +51,7 @@ Preference {
|
||||||
id: dataTextField
|
id: dataTextField
|
||||||
placeholderText: root.placeholderText
|
placeholderText: root.placeholderText
|
||||||
text: preference.value
|
text: preference.value
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors {
|
anchors {
|
||||||
top: labelText.bottom
|
top: labelText.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
import "../../dialogs"
|
import "../../dialogs"
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ Preference {
|
||||||
id: dataTextField
|
id: dataTextField
|
||||||
placeholderText: root.placeholderText
|
placeholderText: root.placeholderText
|
||||||
text: preference.value
|
text: preference.value
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors {
|
anchors {
|
||||||
top: labelText.bottom
|
top: labelText.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import "../../controls"
|
||||||
|
|
||||||
Preference {
|
Preference {
|
||||||
id: root
|
id: root
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
Preference {
|
Preference {
|
||||||
id: root
|
id: root
|
||||||
|
@ -24,6 +25,7 @@ Preference {
|
||||||
TextField {
|
TextField {
|
||||||
id: dataTextField
|
id: dataTextField
|
||||||
placeholderText: preference.placeholderText
|
placeholderText: preference.placeholderText
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors {
|
anchors {
|
||||||
top: labelText.bottom
|
top: labelText.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.XmlListModel 2.0
|
import QtQuick.XmlListModel 2.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
import "../../windows"
|
import "../../windows"
|
||||||
import "../../js/Utils.js" as Utils
|
import "../../js/Utils.js" as Utils
|
||||||
|
@ -27,6 +28,7 @@ ModalWindow {
|
||||||
TextField {
|
TextField {
|
||||||
id: filterEdit
|
id: filterEdit
|
||||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
placeholderText: "filter"
|
placeholderText: "filter"
|
||||||
onTextChanged: tableView.model.filter = text
|
onTextChanged: tableView.model.filter = text
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.XmlListModel 2.0
|
import QtQuick.XmlListModel 2.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
import "../../windows"
|
import "../../windows"
|
||||||
import "../../js/Utils.js" as Utils
|
import "../../js/Utils.js" as Utils
|
||||||
|
@ -26,6 +27,7 @@ ModalWindow {
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: filterEdit
|
id: filterEdit
|
||||||
|
style: TextFieldStyle { renderType: Text.QtRendering }
|
||||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||||
placeholderText: "filter"
|
placeholderText: "filter"
|
||||||
onTextChanged: tableView.model.filter = text
|
onTextChanged: tableView.model.filter = text
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
import "../../../windows"
|
import "../../../windows"
|
||||||
import "../../../controls" as VrControls
|
import "../../../controls" as VrControls
|
||||||
|
@ -31,7 +32,7 @@ Item {
|
||||||
height: modelChooserButton.height
|
height: modelChooserButton.height
|
||||||
anchors { left: parent.left; right: parent.right; }
|
anchors { left: parent.left; right: parent.right; }
|
||||||
Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter }
|
Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter }
|
||||||
TextField {
|
VrControls.TextField {
|
||||||
id: modelUrl;
|
id: modelUrl;
|
||||||
height: jointChooser.height;
|
height: jointChooser.height;
|
||||||
anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left }
|
anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left }
|
||||||
|
|
|
@ -268,17 +268,6 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
|
||||||
return _localLights;
|
return _localLights;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
|
|
||||||
QReadLocker locker(&_hashLock);
|
|
||||||
return _avatarHash.keys().toVector();
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
|
||||||
// Null/Default-constructed QUuids will return MyAvatar
|
|
||||||
return getAvatarBySessionID(avatarID).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||||
result.clear();
|
result.clear();
|
||||||
result.swap(_motionStatesToRemoveFromPhysics);
|
result.swap(_motionStatesToRemoveFromPhysics);
|
||||||
|
|
|
@ -39,7 +39,7 @@ public:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
|
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
|
||||||
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID);
|
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) override;
|
||||||
|
|
||||||
void updateMyAvatar(float deltaTime);
|
void updateMyAvatar(float deltaTime);
|
||||||
void updateOtherAvatars(float deltaTime);
|
void updateOtherAvatars(float deltaTime);
|
||||||
|
@ -56,9 +56,6 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||||
// Currently, your own avatar will be included as the null avatar id.
|
|
||||||
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
|
||||||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
|
||||||
|
|
||||||
|
|
||||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||||
|
|
|
@ -1311,7 +1311,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||||
_prevShouldDrawHead = shouldDrawHead;
|
_prevShouldDrawHead = shouldDrawHead;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f;
|
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.6f;
|
||||||
|
|
||||||
bool MyAvatar::cameraInsideHead() const {
|
bool MyAvatar::cameraInsideHead() const {
|
||||||
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||||
|
@ -1361,6 +1361,37 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
||||||
totalBodyYaw += _driveKeys[STEP_YAW];
|
totalBodyYaw += _driveKeys[STEP_YAW];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use head/HMD orientation to turn while flying
|
||||||
|
if (getCharacterController()->getState() == CharacterController::State::Hover) {
|
||||||
|
|
||||||
|
// This is the direction the user desires to fly in.
|
||||||
|
glm::vec3 desiredFacing = getHead()->getCameraOrientation() * Vectors::UNIT_Z;
|
||||||
|
desiredFacing.y = 0.0f;
|
||||||
|
|
||||||
|
// This is our reference frame, it is captured when the user begins to move.
|
||||||
|
glm::vec3 referenceFacing = transformVector(_sensorToWorldMatrix, _hoverReferenceCameraFacing);
|
||||||
|
referenceFacing.y = 0.0f;
|
||||||
|
referenceFacing = glm::normalize(referenceFacing);
|
||||||
|
glm::vec3 referenceRight(referenceFacing.z, 0.0f, -referenceFacing.x);
|
||||||
|
const float HOVER_FLY_ROTATION_PERIOD = 0.5f;
|
||||||
|
float tau = glm::clamp(deltaTime / HOVER_FLY_ROTATION_PERIOD, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// new facing is a linear interpolation between the desired and reference vectors.
|
||||||
|
glm::vec3 newFacing = glm::normalize((1.0f - tau) * referenceFacing + tau * desiredFacing);
|
||||||
|
|
||||||
|
// calcualte the signed delta yaw angle to apply so that we match our newFacing.
|
||||||
|
float sign = copysignf(1.0f, glm::dot(desiredFacing, referenceRight));
|
||||||
|
float deltaAngle = sign * acosf(glm::clamp(glm::dot(referenceFacing, newFacing), -1.0f, 1.0f));
|
||||||
|
|
||||||
|
// speedFactor is 0 when we are at rest adn 1.0 when we are at max flying speed.
|
||||||
|
const float MAX_FLYING_SPEED = 30.0f;
|
||||||
|
float speedFactor = glm::min(glm::length(getVelocity()) / MAX_FLYING_SPEED, 1.0f);
|
||||||
|
|
||||||
|
// apply our delta, but scale it by the speed factor, so we turn faster when we are flying faster.
|
||||||
|
totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// update body orientation by movement inputs
|
// update body orientation by movement inputs
|
||||||
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
||||||
|
|
||||||
|
@ -1537,6 +1568,16 @@ void MyAvatar::updatePosition(float deltaTime) {
|
||||||
// update _moving flag based on speed
|
// update _moving flag based on speed
|
||||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||||
|
|
||||||
|
|
||||||
|
// capture the head rotation, in sensor space, when the user first indicates they would like to move/fly.
|
||||||
|
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) {
|
||||||
|
_hoverReferenceCameraFacingIsCaptured = true;
|
||||||
|
// transform the camera facing vector into sensor space.
|
||||||
|
_hoverReferenceCameraFacing = transformVector(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
|
||||||
|
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) {
|
||||||
|
_hoverReferenceCameraFacingIsCaptured = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
||||||
|
|
|
@ -413,6 +413,9 @@ private:
|
||||||
|
|
||||||
AtRestDetector _hmdAtRestDetector;
|
AtRestDetector _hmdAtRestDetector;
|
||||||
bool _lastIsMoving { false };
|
bool _lastIsMoving { false };
|
||||||
|
|
||||||
|
bool _hoverReferenceCameraFacingIsCaptured { false };
|
||||||
|
glm::vec3 _hoverReferenceCameraFacing; // hmd sensor space
|
||||||
};
|
};
|
||||||
|
|
||||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||||
|
|
|
@ -9,29 +9,20 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QScriptValue>
|
#include <QScriptValue>
|
||||||
#include <QScrollArea>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "DomainHandler.h"
|
#include "DomainHandler.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "OffscreenUi.h"
|
#include "OffscreenUi.h"
|
||||||
#include "ui/ModelsBrowser.h"
|
|
||||||
#include "WebWindowClass.h"
|
#include "WebWindowClass.h"
|
||||||
|
|
||||||
#include "WindowScriptingInterface.h"
|
#include "WindowScriptingInterface.h"
|
||||||
|
|
||||||
WindowScriptingInterface::WindowScriptingInterface() :
|
WindowScriptingInterface::WindowScriptingInterface() {
|
||||||
_editDialog(NULL),
|
|
||||||
_nonBlockingFormActive(false),
|
|
||||||
_formResult(QDialog::Rejected)
|
|
||||||
{
|
|
||||||
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
|
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
|
||||||
connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
|
connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
|
||||||
|
@ -85,544 +76,31 @@ QScriptValue WindowScriptingInterface::getCursorPositionY() {
|
||||||
return QCursor::pos().y();
|
return QCursor::pos().y();
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::alert(const QString& message) {
|
|
||||||
QScriptValue retVal;
|
|
||||||
QMetaObject::invokeMethod(this, "showAlert", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(const QString&, message));
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
|
||||||
QScriptValue retVal;
|
|
||||||
QMetaObject::invokeMethod(this, "showConfirm", Qt::BlockingQueuedConnection,
|
|
||||||
Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(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,
|
|
||||||
Q_RETURN_ARG(QScriptValue, retVal),
|
|
||||||
Q_ARG(const QString&, message), Q_ARG(const QString&, defaultText));
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) {
|
|
||||||
QScriptValue retVal;
|
|
||||||
QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection,
|
|
||||||
Q_RETURN_ARG(QScriptValue, retVal),
|
|
||||||
Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter));
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) {
|
|
||||||
QScriptValue retVal;
|
|
||||||
QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection,
|
|
||||||
Q_RETURN_ARG(QScriptValue, retVal),
|
|
||||||
Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter),
|
|
||||||
Q_ARG(QFileDialog::AcceptMode, QFileDialog::AcceptSave));
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::s3Browse(const QString& nameFilter) {
|
|
||||||
QScriptValue retVal;
|
|
||||||
QMetaObject::invokeMethod(this, "showS3Browse", Qt::BlockingQueuedConnection,
|
|
||||||
Q_RETURN_ARG(QScriptValue, retVal),
|
|
||||||
Q_ARG(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
|
/// Display an alert box
|
||||||
/// \param const QString& message message to display
|
/// \param const QString& message message to display
|
||||||
/// \return QScriptValue::UndefinedValue
|
/// \return QScriptValue::UndefinedValue
|
||||||
QScriptValue WindowScriptingInterface::showAlert(const QString& message) {
|
void WindowScriptingInterface::alert(const QString& message) {
|
||||||
OffscreenUi::warning("", message);
|
OffscreenUi::warning("", message);
|
||||||
return QScriptValue::UndefinedValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display a confirmation box with the options 'Yes' and 'No'
|
/// Display a confirmation box with the options 'Yes' and 'No'
|
||||||
/// \param const QString& message message to display
|
/// \param const QString& message message to display
|
||||||
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
||||||
QScriptValue WindowScriptingInterface::showConfirm(const QString& message) {
|
QScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
||||||
bool confirm = false;
|
return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message)));
|
||||||
if (QMessageBox::Yes == OffscreenUi::question("", message)) {
|
|
||||||
confirm = true;
|
|
||||||
}
|
|
||||||
return QScriptValue(confirm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowScriptingInterface::chooseDirectory() {
|
|
||||||
QPushButton* button = reinterpret_cast<QPushButton*>(sender());
|
|
||||||
|
|
||||||
QString title = button->property("title").toString();
|
|
||||||
QString path = button->property("path").toString();
|
|
||||||
QRegExp displayAs = button->property("displayAs").toRegExp();
|
|
||||||
QRegExp validateAs = button->property("validateAs").toRegExp();
|
|
||||||
QString errorMessage = button->property("errorMessage").toString();
|
|
||||||
|
|
||||||
QString directory = QFileDialog::getExistingDirectory(button, title, path);
|
|
||||||
if (directory.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateAs.exactMatch(directory)) {
|
|
||||||
OffscreenUi::warning(NULL, "Invalid Directory", errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
button->setProperty("path", directory);
|
|
||||||
|
|
||||||
displayAs.indexIn(directory);
|
|
||||||
QString buttonText = displayAs.cap(1) != "" ? displayAs.cap(1) : ".";
|
|
||||||
button->setText(buttonText);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowScriptingInterface::inlineButtonClicked() {
|
|
||||||
QPushButton* button = reinterpret_cast<QPushButton*>(sender());
|
|
||||||
QString name = button->property("name").toString();
|
|
||||||
emit inlineButtonClicked(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString WindowScriptingInterface::jsRegExp2QtRegExp(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
|
/// Display a prompt with a text box
|
||||||
/// \param const QString& message message to display
|
/// \param const QString& message message to display
|
||||||
/// \param const QString& defaultText default text in the text box
|
/// \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.
|
/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise.
|
||||||
QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) {
|
QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {
|
||||||
QInputDialog promptDialog(qApp->getWindow());
|
bool ok = false;
|
||||||
promptDialog.setWindowTitle("");
|
QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok);
|
||||||
promptDialog.setLabelText(message);
|
return ok ? QScriptValue(result) : QScriptValue::NullValue;
|
||||||
promptDialog.setTextValue(defaultText);
|
|
||||||
promptDialog.setFixedSize(600, 200);
|
|
||||||
|
|
||||||
if (promptDialog.exec() == QDialog::Accepted) {
|
|
||||||
return QScriptValue(promptDialog.textValue());
|
|
||||||
}
|
|
||||||
return QScriptValue::NullValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display a file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
QString fixupPathForMac(const QString& directory) {
|
||||||
/// working directory.
|
|
||||||
/// \param const QString& title title of the window
|
|
||||||
/// \param const QString& directory directory to start the file browser at
|
|
||||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
|
||||||
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
|
||||||
QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QString& directory, const QString& nameFilter,
|
|
||||||
QFileDialog::AcceptMode acceptMode) {
|
|
||||||
// On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus
|
// On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus
|
||||||
// filename if the directory is valid.
|
// filename if the directory is valid.
|
||||||
QString path = "";
|
QString path = "";
|
||||||
|
@ -631,35 +109,31 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS
|
||||||
fileInfo.setFile(directory, "__HIFI_INVALID_FILE__");
|
fileInfo.setFile(directory, "__HIFI_INVALID_FILE__");
|
||||||
path = fileInfo.filePath();
|
path = fileInfo.filePath();
|
||||||
}
|
}
|
||||||
|
return path;
|
||||||
QFileDialog fileDialog(qApp->getWindow(), title, path, nameFilter);
|
|
||||||
fileDialog.setAcceptMode(acceptMode);
|
|
||||||
QUrl fileUrl(directory);
|
|
||||||
if (acceptMode == QFileDialog::AcceptSave) {
|
|
||||||
// TODO -- Setting this breaks the dialog on Linux. Does it help something on other platforms?
|
|
||||||
// fileDialog.setFileMode(QFileDialog::Directory);
|
|
||||||
fileDialog.selectFile(fileUrl.fileName());
|
|
||||||
}
|
|
||||||
if (fileDialog.exec()) {
|
|
||||||
return QScriptValue(fileDialog.selectedFiles().first());
|
|
||||||
}
|
|
||||||
return QScriptValue::NullValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display a browse window for S3 models
|
/// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||||
/// \param const QString& nameFilter filter to filter filenames
|
/// working directory.
|
||||||
|
/// \param const QString& title title of the window
|
||||||
|
/// \param const QString& directory directory to start the file browser at
|
||||||
|
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||||
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
||||||
QScriptValue WindowScriptingInterface::showS3Browse(const QString& nameFilter) {
|
QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||||
ModelsBrowser browser(FSTReader::ENTITY_MODEL);
|
QString path = fixupPathForMac(directory);
|
||||||
if (nameFilter != "") {
|
QString result = OffscreenUi::getOpenFileName(nullptr, title, path, nameFilter);
|
||||||
browser.setNameFilter(nameFilter);
|
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||||
}
|
}
|
||||||
QEventLoop loop;
|
|
||||||
connect(&browser, &ModelsBrowser::selected, &loop, &QEventLoop::quit);
|
/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||||
QMetaObject::invokeMethod(&browser, "browse", Qt::QueuedConnection);
|
/// working directory.
|
||||||
loop.exec();
|
/// \param const QString& title title of the window
|
||||||
|
/// \param const QString& directory directory to start the file browser at
|
||||||
return browser.getSelectedFile();
|
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||||
|
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
||||||
|
QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||||
|
QString path = fixupPathForMac(directory);
|
||||||
|
QString result = OffscreenUi::getSaveFileName(nullptr, title, path, nameFilter);
|
||||||
|
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WindowScriptingInterface::getInnerWidth() {
|
int WindowScriptingInterface::getInnerWidth() {
|
||||||
|
|
|
@ -12,12 +12,9 @@
|
||||||
#ifndef hifi_WindowScriptingInterface_h
|
#ifndef hifi_WindowScriptingInterface_h
|
||||||
#define hifi_WindowScriptingInterface_h
|
#define hifi_WindowScriptingInterface_h
|
||||||
|
|
||||||
#include <QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QScriptValue>
|
#include <QtCore/QString>
|
||||||
#include <QString>
|
#include <QtScript/QScriptValue>
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QLineEdit>
|
|
||||||
|
|
||||||
class WebWindowClass;
|
class WebWindowClass;
|
||||||
|
|
||||||
|
@ -41,61 +38,19 @@ public slots:
|
||||||
QScriptValue hasFocus();
|
QScriptValue hasFocus();
|
||||||
void setFocus();
|
void setFocus();
|
||||||
void raiseMainWindow();
|
void raiseMainWindow();
|
||||||
QScriptValue alert(const QString& message = "");
|
void alert(const QString& message = "");
|
||||||
QScriptValue confirm(const QString& message = "");
|
QScriptValue confirm(const QString& message = "");
|
||||||
QScriptValue form(const QString& title, QScriptValue array);
|
|
||||||
QScriptValue prompt(const QString& message = "", const QString& defaultText = "");
|
QScriptValue prompt(const QString& message = "", const QString& defaultText = "");
|
||||||
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||||
QScriptValue save(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:
|
signals:
|
||||||
void domainChanged(const QString& domainHostname);
|
void domainChanged(const QString& domainHostname);
|
||||||
void inlineButtonClicked(const QString& name);
|
|
||||||
void nonBlockingFormClosed();
|
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
void domainConnectionRefused(const QString& reason);
|
void domainConnectionRefused(const QString& reason);
|
||||||
|
|
||||||
private slots:
|
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);
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_WindowScriptingInterface_h
|
#endif // hifi_WindowScriptingInterface_h
|
||||||
|
|
|
@ -22,6 +22,16 @@ AvatarHashMap::AvatarHashMap() {
|
||||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
connect(DependencyManager::get<NodeList>().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QUuid> AvatarHashMap::getAvatarIdentifiers() {
|
||||||
|
QReadLocker locker(&_hashLock);
|
||||||
|
return _avatarHash.keys().toVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarData* AvatarHashMap::getAvatar(QUuid avatarID) {
|
||||||
|
// Null/Default-constructed QUuids will return MyAvatar
|
||||||
|
return getAvatarBySessionID(avatarID).get();
|
||||||
|
}
|
||||||
|
|
||||||
bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) {
|
bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) {
|
||||||
auto hashCopy = getHashCopy();
|
auto hashCopy = getHashCopy();
|
||||||
foreach(const AvatarSharedPointer& sharedAvatar, hashCopy) {
|
foreach(const AvatarSharedPointer& sharedAvatar, hashCopy) {
|
||||||
|
|
|
@ -34,6 +34,12 @@ public:
|
||||||
AvatarHash getHashCopy() { QReadLocker lock(&_hashLock); return _avatarHash; }
|
AvatarHash getHashCopy() { QReadLocker lock(&_hashLock); return _avatarHash; }
|
||||||
int size() { return _avatarHash.size(); }
|
int size() { return _avatarHash.size(); }
|
||||||
|
|
||||||
|
// Currently, your own avatar will be included as the null avatar id.
|
||||||
|
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
||||||
|
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||||
|
|
||||||
|
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void avatarAddedEvent(const QUuid& sessionUUID);
|
void avatarAddedEvent(const QUuid& sessionUUID);
|
||||||
void avatarRemovedEvent(const QUuid& sessionUUID);
|
void avatarRemovedEvent(const QUuid& sessionUUID);
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QRegularExpression>
|
||||||
|
|
||||||
|
|
||||||
QUrl FileDialogHelper::home() {
|
QUrl FileDialogHelper::home() {
|
||||||
|
@ -26,7 +28,11 @@ QString FileDialogHelper::urlToPath(const QUrl& url) {
|
||||||
return url.toLocalFile();
|
return url.toLocalFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileDialogHelper::validPath(const QString& path) {
|
bool FileDialogHelper::fileExists(const QString& path) {
|
||||||
|
return QFile(path).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDialogHelper::validPath(const QString& path) {
|
||||||
return QFile(path).exists();
|
return QFile(path).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,3 +44,62 @@ QUrl FileDialogHelper::pathToUrl(const QString& path) {
|
||||||
return QUrl::fromLocalFile(path);
|
return QUrl::fromLocalFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QUrl FileDialogHelper::saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters) {
|
||||||
|
qDebug() << "Calling save helper with " << saveText << " " << currentFolder << " " << selectionFilters;
|
||||||
|
|
||||||
|
QFileInfo fileInfo(saveText);
|
||||||
|
|
||||||
|
// Check if it's a relative path and if it is resolve to the absolute path
|
||||||
|
{
|
||||||
|
if (fileInfo.isRelative()) {
|
||||||
|
fileInfo = QFileInfo(currentFolder.toLocalFile() + "/" + fileInfo.filePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we need to append an extension, but only if the current resolved path isn't a directory
|
||||||
|
if (!fileInfo.isDir()) {
|
||||||
|
QString fileName = fileInfo.fileName();
|
||||||
|
if (!fileName.contains(".") && selectionFilters.size() == 1) {
|
||||||
|
const QRegularExpression extensionRe{ ".*(\\.[a-zA-Z0-9]+)$" };
|
||||||
|
QString filter = selectionFilters[0];
|
||||||
|
auto match = extensionRe.match(filter);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
fileInfo = QFileInfo(fileInfo.filePath() + match.captured(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QUrl::fromLocalFile(fileInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDialogHelper::urlIsDir(const QUrl& url) {
|
||||||
|
return QFileInfo(url.toLocalFile()).isDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDialogHelper::urlIsFile(const QUrl& url) {
|
||||||
|
return QFileInfo(url.toLocalFile()).isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDialogHelper::urlExists(const QUrl& url) {
|
||||||
|
return QFileInfo(url.toLocalFile()).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDialogHelper::urlIsWritable(const QUrl& url) {
|
||||||
|
QFileInfo fileInfo(url.toLocalFile());
|
||||||
|
// Is the file writable?
|
||||||
|
if (fileInfo.exists()) {
|
||||||
|
return fileInfo.isWritable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// No file, get the parent directory and check if writable
|
||||||
|
return QFileInfo(fileInfo.absoluteDir().absolutePath()).isWritable();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FileDialogHelper::drives() {
|
||||||
|
QStringList result;
|
||||||
|
for (const auto& drive : QDir::drives()) {
|
||||||
|
result << drive.absolutePath();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -47,10 +47,17 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE QUrl home();
|
Q_INVOKABLE QUrl home();
|
||||||
Q_INVOKABLE QStringList standardPath(StandardLocation location);
|
Q_INVOKABLE QStringList standardPath(StandardLocation location);
|
||||||
|
Q_INVOKABLE QStringList drives();
|
||||||
Q_INVOKABLE QString urlToPath(const QUrl& url);
|
Q_INVOKABLE QString urlToPath(const QUrl& url);
|
||||||
|
Q_INVOKABLE bool urlIsDir(const QUrl& url);
|
||||||
|
Q_INVOKABLE bool urlIsFile(const QUrl& url);
|
||||||
|
Q_INVOKABLE bool urlExists(const QUrl& url);
|
||||||
|
Q_INVOKABLE bool urlIsWritable(const QUrl& url);
|
||||||
|
Q_INVOKABLE bool fileExists(const QString& path);
|
||||||
Q_INVOKABLE bool validPath(const QString& path);
|
Q_INVOKABLE bool validPath(const QString& path);
|
||||||
Q_INVOKABLE bool validFolder(const QString& path);
|
Q_INVOKABLE bool validFolder(const QString& path);
|
||||||
Q_INVOKABLE QUrl pathToUrl(const QString& path);
|
Q_INVOKABLE QUrl pathToUrl(const QString& path);
|
||||||
|
Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,7 @@ void OffscreenUi::create(QOpenGLContext* context) {
|
||||||
OffscreenQmlSurface::create(context);
|
OffscreenQmlSurface::create(context);
|
||||||
auto rootContext = getRootContext();
|
auto rootContext = getRootContext();
|
||||||
|
|
||||||
|
rootContext->setContextProperty("OffscreenUi", this);
|
||||||
rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags());
|
rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags());
|
||||||
rootContext->setContextProperty("urlHandler", new UrlHandler());
|
rootContext->setContextProperty("urlHandler", new UrlHandler());
|
||||||
rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||||
|
@ -219,7 +220,7 @@ QQuickItem* OffscreenUi::createMessageBox(QMessageBox::Icon icon, const QString&
|
||||||
return qvariant_cast<QQuickItem*>(result);
|
return qvariant_cast<QQuickItem*>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::StandardButton OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {
|
int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {
|
||||||
if (!messageBox) {
|
if (!messageBox) {
|
||||||
return QMessageBox::NoButton;
|
return QMessageBox::NoButton;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +242,7 @@ QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, cons
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton));
|
return static_cast<QMessageBox::StandardButton>(waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text,
|
QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text,
|
||||||
|
@ -478,6 +479,26 @@ private slots:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QString OffscreenUi::fileDialog(const QVariantMap& properties) {
|
||||||
|
QVariant buildDialogResult;
|
||||||
|
bool invokeResult = QMetaObject::invokeMethod(_desktop, "fileDialog",
|
||||||
|
Q_RETURN_ARG(QVariant, buildDialogResult),
|
||||||
|
Q_ARG(QVariant, QVariant::fromValue(properties)));
|
||||||
|
|
||||||
|
if (!invokeResult) {
|
||||||
|
qWarning() << "Failed to create file open dialog";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant result = FileDialogListener(qvariant_cast<QQuickItem*>(buildDialogResult)).waitForResult();
|
||||||
|
if (!result.isValid()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
qDebug() << result.toString();
|
||||||
|
return result.toUrl().toLocalFile();
|
||||||
|
}
|
||||||
|
|
||||||
QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
|
QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QString result;
|
QString result;
|
||||||
|
@ -497,28 +518,40 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir,
|
||||||
map.insert("dir", QUrl::fromLocalFile(dir));
|
map.insert("dir", QUrl::fromLocalFile(dir));
|
||||||
map.insert("filter", filter);
|
map.insert("filter", filter);
|
||||||
map.insert("options", static_cast<int>(options));
|
map.insert("options", static_cast<int>(options));
|
||||||
|
return fileDialog(map);
|
||||||
|
}
|
||||||
|
|
||||||
QVariant buildDialogResult;
|
QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
|
||||||
bool invokeResult = QMetaObject::invokeMethod(_desktop, "fileOpenDialog",
|
if (QThread::currentThread() != thread()) {
|
||||||
Q_RETURN_ARG(QVariant, buildDialogResult),
|
QString result;
|
||||||
Q_ARG(QVariant, QVariant::fromValue(map)));
|
QMetaObject::invokeMethod(this, "fileSaveDialog", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QString, result),
|
||||||
if (!invokeResult) {
|
Q_ARG(QString, caption),
|
||||||
qWarning() << "Failed to create file open dialog";
|
Q_ARG(QString, dir),
|
||||||
return QString();
|
Q_ARG(QString, filter),
|
||||||
|
Q_ARG(QString*, selectedFilter),
|
||||||
|
Q_ARG(QFileDialog::Options, options));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant result = FileDialogListener(qvariant_cast<QQuickItem*>(buildDialogResult)).waitForResult();
|
// FIXME support returning the selected filter... somehow?
|
||||||
if (!result.isValid()) {
|
QVariantMap map;
|
||||||
return QString();
|
map.insert("caption", caption);
|
||||||
}
|
map.insert("dir", QUrl::fromLocalFile(dir));
|
||||||
qDebug() << result.toString();
|
map.insert("filter", filter);
|
||||||
return result.toUrl().toLocalFile();
|
map.insert("options", static_cast<int>(options));
|
||||||
|
map.insert("saveDialog", true);
|
||||||
|
|
||||||
|
return fileDialog(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
|
QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
|
||||||
return DependencyManager::get<OffscreenUi>()->fileOpenDialog(caption, dir, filter, selectedFilter, options);
|
return DependencyManager::get<OffscreenUi>()->fileOpenDialog(caption, dir, filter, selectedFilter, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
|
||||||
|
return DependencyManager::get<OffscreenUi>()->fileSaveDialog(caption, dir, filter, selectedFilter, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "OffscreenUi.moc"
|
#include "OffscreenUi.moc"
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
// Must be called from the main thread
|
// Must be called from the main thread
|
||||||
QQuickItem* createMessageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
|
QQuickItem* createMessageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
|
||||||
// Must be called from the main thread
|
// Must be called from the main thread
|
||||||
QMessageBox::StandardButton waitForMessageBoxResult(QQuickItem* messageBox);
|
Q_INVOKABLE int waitForMessageBoxResult(QQuickItem* messageBox);
|
||||||
|
|
||||||
/// Same design as QMessageBox::critical(), will block, returns result
|
/// Same design as QMessageBox::critical(), will block, returns result
|
||||||
static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text,
|
static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text,
|
||||||
|
@ -87,10 +87,13 @@ public:
|
||||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
|
|
||||||
// file dialog compatibility
|
|
||||||
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||||
|
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||||
|
|
||||||
// Compatibility with QFileDialog::getOpenFileName
|
// Compatibility with QFileDialog::getOpenFileName
|
||||||
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||||
|
// Compatibility with QFileDialog::getSaveFileName
|
||||||
|
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||||
|
|
||||||
|
|
||||||
// input dialog compatibility
|
// input dialog compatibility
|
||||||
|
@ -104,6 +107,8 @@ public:
|
||||||
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString fileDialog(const QVariantMap& properties);
|
||||||
|
|
||||||
QQuickItem* _desktop { nullptr };
|
QQuickItem* _desktop { nullptr };
|
||||||
QQuickItem* _toolWindow { nullptr };
|
QQuickItem* _toolWindow { nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,13 +118,7 @@ ApplicationWindow {
|
||||||
Button {
|
Button {
|
||||||
text: "Open File"
|
text: "Open File"
|
||||||
property var builder: Component {
|
property var builder: Component {
|
||||||
FileDialog {
|
FileDialog { }
|
||||||
folder: "file:///C:/users/bdavis";
|
|
||||||
filterModel: ListModel {
|
|
||||||
ListElement { text: "Javascript Files (*.js)"; filter: "*.js" }
|
|
||||||
ListElement { text: "All Files (*.*)"; filter: "*.*" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
Loading…
Reference in a new issue