diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f2e9fdb36e..1fd6045fe0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -564,7 +564,7 @@ function MyController(hand) { "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } - + this.particleBeamObject = Entities.addEntity(particleBeamPropertiesObject); }; @@ -1588,7 +1588,9 @@ function MyController(hand) { ids.forEach(function(id) { var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { + if (!props || + !props.boundingBox || + props.name === 'pointer') { return; } var entityMinPoint = props.boundingBox.brn; diff --git a/examples/depthReticle.js b/examples/depthReticle.js new file mode 100644 index 0000000000..a60e61d07c --- /dev/null +++ b/examples/depthReticle.js @@ -0,0 +1,47 @@ +// depthReticle.js +// examples +// +// Created by Brad Hefta-Gaub on 2/23/16. +// Copyright 2016 High Fidelity, Inc. +// +// When used in HMD, this script will make the reticle depth track to any clickable item in view. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var APPARENT_2D_OVERLAY_DEPTH = 1.0; +var APPARENT_MAXIMUM_DEPTH = 100.0; // this is a depth at which things all seem sufficiently distant +var lastDepthCheckTime = 0; + +Script.update.connect(function(deltaTime) { + var TIME_BETWEEN_DEPTH_CHECKS = 100; + var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; + if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { + var reticlePosition = Reticle.position; + + // first check the 2D Overlays + if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(reticlePosition)) { + Reticle.setDepth(APPARENT_2D_OVERLAY_DEPTH); + } else { + var pickRay = Camera.computePickRay(reticlePosition.x, reticlePosition.y); + + // Then check the 3D overlays + var result = Overlays.findRayIntersection(pickRay); + + if (!result.intersects) { + // finally check the entities + result = Entities.findRayIntersection(pickRay, true); + } + + // If either the overlays or entities intersect, then set the reticle depth to + // the distance of intersection + if (result.intersects) { + Reticle.setDepth(result.distance); + } else { + // if nothing intersects... set the depth to some sufficiently large depth + Reticle.setDepth(APPARENT_MAXIMUM_DEPTH); + } + } + } +}); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 826101a1fe..fdbe7d7c38 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -75,8 +75,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { decimals = decimals == undefined ? 4 : decimals; return function() { - var value = +this.value; - value = +value.toFixed(decimals); + var value = parseFloat(this.value).toFixed(decimals); EventBridge.emitWebEvent( '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}' diff --git a/examples/particle_explorer/particleExplorerTool.js b/examples/particle_explorer/particleExplorerTool.js index 20dfa6f1ba..007eb717ec 100644 --- a/examples/particle_explorer/particleExplorerTool.js +++ b/examples/particle_explorer/particleExplorerTool.js @@ -32,11 +32,9 @@ ParticleExplorerTool = function() { that.destroyWebView = function() { if (!that.webView) { - print("EBL CAN'ZT CLOSE WEB VIEW- IT DOESNT EXISTS!") return; } - print("EBL CLOSING WEB VIEW") that.webView.close(); that.webView = null; that.activeParticleEntity = 0; diff --git a/examples/tests/particleOrientationTest.js b/examples/tests/particleOrientationTest.js new file mode 100644 index 0000000000..7fe670f4b7 --- /dev/null +++ b/examples/tests/particleOrientationTest.js @@ -0,0 +1,101 @@ +// +// particleOrientationTest.js +// examples/tests +// +// Created by Piper.Peppercorn. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var emitterBone = 'Head' +var particleEntities = []; + +function emitter(jointName) { + var jointID = MyAvatar.jointNames.indexOf(jointName); + var newEmitter = Entities.addEntity({ + name: 'particleEmitter ' + jointName, + type: 'ParticleEffect', + emitterShouldTrail: true, + textures: 'https://dl.dropboxusercontent.com/u/96759331/ParticleTest.png', + position: Vec3.sum(MyAvatar.getAbsoluteJointRotationInObjectFrame(jointID), MyAvatar.position), + parentJointIndex: jointID, + position: MyAvatar.getJointPosition(jointName), + color: { + red: 255, + green: 255, + blue: 255 + }, + isEmitting: 1, + maxParticles: 1, + lifespan: 2.0 + , + emitRate: 1, + emitSpeed: 0.0, + speedSpread: 0.0, + /* + emitOrientation: { + x: -0.7035577893257141, + y: -0.000015259007341228426, + z: -0.000015259007341228426, + w: 1.7106381058692932 + }, + */ + emitOrientation: { + x:0, + y: 0, + z: 0, + w: Math.PI + }, + emitRadiusStart: 0, + polarStart: 0, + polarFinish: 0, + azimuthFinish: 3.1415927410125732, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: 0, + y: 0, + z: 0 + }, + particleRadius: 2.0, + radiusSpread: 1.0, + radiusStart: 2.0, + radiusFinish: 2.0, + colorSpread: { + red: 0, + green: 0, + blue: 0 + }, + colorStart: { + red: 255, + green: 255, + blue: 255 + }, + colorFinish: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + alphaSpread: 0, + alphaStart: 1, + alphaFinish: 1 + }); + return newEmitter; +} + + +Script.scriptEnding.connect(function() { + for (var i = 0; i < particleEntities.length; i++) { + // Fixes a crash on shutdown: + Entities.editEntity(particleEntities[i], { parentID: '' }); + Entities.deleteEntity(particleEntities[i]); + } +}); + +particleEntities.push(emitter(emitterBone)); diff --git a/interface/resources/config/render.json b/interface/resources/config/render.json index 092530d864..414d94e11e 100644 --- a/interface/resources/config/render.json +++ b/interface/resources/config/render.json @@ -9,11 +9,6 @@ "Enabled": { "enabled": true } - }, - "Antialiasing": { - "Enabled": { - "enabled": true - } } } } diff --git a/interface/resources/fonts/AnonymousPro-Regular.ttf b/interface/resources/fonts/AnonymousPro-Regular.ttf new file mode 100644 index 0000000000..06aafc0673 Binary files /dev/null and b/interface/resources/fonts/AnonymousPro-Regular.ttf differ diff --git a/interface/resources/fonts/AnonymousPro.license b/interface/resources/fonts/AnonymousPro.license new file mode 100644 index 0000000000..5e8b7385e7 --- /dev/null +++ b/interface/resources/fonts/AnonymousPro.license @@ -0,0 +1,93 @@ +Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com), +with Reserved Font Name Anonymous Pro. +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/interface/resources/fonts/FiraSans-SemiBold.ttf b/interface/resources/fonts/FiraSans-SemiBold.ttf new file mode 100644 index 0000000000..821a43d7fd Binary files /dev/null and b/interface/resources/fonts/FiraSans-SemiBold.ttf differ diff --git a/interface/resources/fonts/FiraSans.license b/interface/resources/fonts/FiraSans.license new file mode 100644 index 0000000000..d444ea92b6 --- /dev/null +++ b/interface/resources/fonts/FiraSans.license @@ -0,0 +1,94 @@ +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/interface/resources/fonts/Raleway-Bold.ttf b/interface/resources/fonts/Raleway-Bold.ttf new file mode 100644 index 0000000000..38c099cc85 Binary files /dev/null and b/interface/resources/fonts/Raleway-Bold.ttf differ diff --git a/interface/resources/fonts/Raleway-Light.ttf b/interface/resources/fonts/Raleway-Light.ttf new file mode 100644 index 0000000000..91aa0c701f Binary files /dev/null and b/interface/resources/fonts/Raleway-Light.ttf differ diff --git a/interface/resources/fonts/Raleway-Regular.ttf b/interface/resources/fonts/Raleway-Regular.ttf new file mode 100644 index 0000000000..e570a2d5c3 Binary files /dev/null and b/interface/resources/fonts/Raleway-Regular.ttf differ diff --git a/interface/resources/fonts/Raleway-SemiBold.ttf b/interface/resources/fonts/Raleway-SemiBold.ttf new file mode 100644 index 0000000000..ed0a8b9941 Binary files /dev/null and b/interface/resources/fonts/Raleway-SemiBold.ttf differ diff --git a/interface/resources/fonts/Raleway.license b/interface/resources/fonts/Raleway.license new file mode 100644 index 0000000000..1c9779ddcd --- /dev/null +++ b/interface/resources/fonts/Raleway.license @@ -0,0 +1,94 @@ +Copyright (c) 2010, Matt McInerney (matt@pixelspread.com), +Copyright (c) 2011, Pablo Impallari (www.impallari.com|impallari@gmail.com), +Copyright (c) 2011, Rodrigo Fuenzalida (www.rfuenzalida.com|hello@rfuenzalida.com), with Reserved Font Name Raleway +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf new file mode 100644 index 0000000000..54a23dce07 Binary files /dev/null and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index d2f78191ea..72f92f4d2d 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -5,6 +5,8 @@ import QtQuick.Controls 1.2 Item { anchors.fill: parent anchors.leftMargin: 300 + objectName: "StatsItem" + Hifi.Stats { id: root objectName: "Stats" @@ -27,6 +29,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { @@ -83,6 +86,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: pingCol @@ -123,6 +127,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: geoCol @@ -172,6 +177,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: octreeCol @@ -186,42 +192,29 @@ Item { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; - text: "\tItems Rendered Opaque: " + root.opaqueRendered + - " / Translucent: " + root.translucentRendered + - " / Shadow: " + root.shadowRendered + - " / Other: " + root.otherRendered; + text: "Items rendered / considered: " + + root.itemRendered + " / " + root.itemConsidered; } Text { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; - text: "\tOpaque considered: " + root.opaqueConsidered + - " / Out of view: " + root.opaqueOutOfView + - " / Too small: " + root.opaqueTooSmall; + text: " out of view: " + root.itemOutOfView + + " too small: " + root.itemTooSmall; } Text { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; - text: "\tShadow considered: " + root.shadowConsidered + - " / Out of view: " + root.shadowOutOfView + - " / Too small: " + root.shadowTooSmall; + text: "Shadows rendered / considered: " + + root.shadowRendered + " / " + root.shadowConsidered; } Text { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; - text: "\tTranslucent considered: " + root.translucentConsidered + - " / Out of view: " + root.translucentOutOfView + - " / Too small: " + root.translucentTooSmall; - } - Text { - color: root.fontColor; - font.pixelSize: root.fontSize - visible: root.expanded; - text: "\tOther considered: " + root.otherConsidered + - " / Out of view: " + root.otherOutOfView + - " / Too small: " + root.otherTooSmall; + text: " out of view: " + root.shadowOutOfView + + " too small: " + root.shadowTooSmall; } Text { color: root.fontColor; diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml index a7d45b9984..404d1c1e78 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -20,8 +20,10 @@ Windows.Window { property string newTabSource property alias tabView: tabView onParentChanged: { - x = 120; - y = 120; + if (parent) { + x = 120; + y = 120; + } } Settings { diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml new file mode 100644 index 0000000000..9c7aee722b --- /dev/null +++ b/interface/resources/qml/controls-uit/Button.qml @@ -0,0 +1,54 @@ +// +// Button.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Original +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" + +Original.Button { + id: button + property int color: 0 + width: 120 + height: 30 + + style: ButtonStyle { + + background: Rectangle { + radius: hifi.buttons.radius + gradient: Gradient { + GradientStop { + position: 0.2 + color: enabled + ? (!pressed && button.color != hifi.buttons.black || (!hovered || pressed) && button.color == hifi.buttons.black + ? hifi.buttons.colorStart[button.color] : hifi.buttons.colorFinish[button.color]) + : hifi.buttons.colorStart[hifi.buttons.white] + } + GradientStop { + position: 1.0 + color: enabled + ? ((!hovered || pressed) && button.color != hifi.buttons.black || !pressed && button.color == hifi.buttons.black + ? hifi.buttons.colorFinish[button.color] : hifi.buttons.colorStart[button.color]) + : hifi.buttons.colorFinish[hifi.buttons.white] + } + } + } + + label: RalewayBold { + font.capitalization: Font.AllUppercase + color: enabled ? hifi.buttons.textColor[button.color] : hifi.colors.lightGrayText + size: hifi.fontSizes.buttonLabel + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + } + } +} diff --git a/interface/resources/qml/controls-uit/StaticSection.qml b/interface/resources/qml/controls-uit/StaticSection.qml new file mode 100644 index 0000000000..a4857635f1 --- /dev/null +++ b/interface/resources/qml/controls-uit/StaticSection.qml @@ -0,0 +1,65 @@ +// +// StaticSection.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../styles-uit" + +Column { + property string name: "Static Section" + property bool hasSeparator: false + + spacing: hifi.dimensions.contentSpacing.y + + anchors { + left: parent.left + leftMargin: hifi.dimensions.contentMargin.x + right: parent.right + rightMargin: hifi.dimensions.contentMargin.x + } + + VerticalSpacer { } + + Item { + visible: hasSeparator + anchors.top: sectionName.top + + Rectangle { + width: frame.width + height: 1 + color: hifi.colors.baseGrayShadow + x: -hifi.dimensions.contentMargin.x + anchors.bottom: highlight.top + } + + Rectangle { + id: highlight + width: frame.width + height: 1 + color: hifi.colors.baseGrayHighlight + x: -hifi.dimensions.contentMargin.x + anchors.bottom: parent.top + } + } + + RalewayRegular { + id: sectionName + text: parent.name + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + color: hifi.colors.lightGrayText + verticalAlignment: Text.AlignBottom + height: { + if (hasSeparator) { + hifi.dimensions.contentMargin.y + } + } + } +} diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml new file mode 100644 index 0000000000..fc6c310612 --- /dev/null +++ b/interface/resources/qml/controls-uit/Table.qml @@ -0,0 +1,181 @@ +// +// Table.qml +// +// Created by David Rowe on 18 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" + +TableView { + id: tableView + + property var tableModel: ListModel { } + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + model: tableModel + + TableViewColumn { + role: "name" + } + + anchors { left: parent.left; right: parent.right } + + headerVisible: false + headerDelegate: Item { } // Fix OSX QML bug that displays scrollbar starting too low. + + // Use rectangle to draw border with rounded corners. + frameVisible: false + Rectangle { + color: "#00000000" + anchors { fill: parent; margins: -2 } + radius: hifi.dimensions.borderRadius + border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + border.width: 2 + } + anchors.margins: 2 // Shrink TableView to lie within border. + + backgroundVisible: true + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + + style: TableViewStyle { + // Needed in order for rows to keep displaying rows after end of table entries. + backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven + alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + handle: Item { + id: scrollbarHandle + implicitWidth: 6 + Rectangle { + anchors { + fill: parent + leftMargin: 2 // Move it right + rightMargin: -2 // "" + topMargin: 3 // Shrink vertically + bottomMargin: 3 // "" + } + radius: 3 + color: hifi.colors.tableScrollHandle + } + } + + scrollBarBackground: Item { + implicitWidth: 10 + Rectangle { + anchors { + fill: parent + margins: -1 // Expand + } + color: hifi.colors.baseGrayHighlight + } + + Rectangle { + anchors { + fill: parent + margins: 1 // Shrink + } + radius: 4 + color: hifi.colors.tableScrollBackground + } + } + + incrementControl: Item { + visible: false + } + + decrementControl: Item { + visible: false + } + } + + rowDelegate: Rectangle { + height: (styleData.selected ? 1.8 : 1) * hifi.dimensions.tableRowHeight + color: styleData.selected + ? hifi.colors.primaryHighlight + : tableView.isLightColorScheme + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + } + + itemDelegate: Item { + anchors { + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + } + + FiraSansSemiBold { + id: textItem + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 3 + } + + // FIXME: Put reload item in tableModel passed in from RunningScripts. + HiFiGlyphs { + id: reloadButton + text: hifi.glyphs.reloadSmall + color: parent.color + anchors { + top: parent.top + right: stopButton.left + verticalCenter: parent.verticalCenter + } + MouseArea { + anchors { fill: parent; margins: -2 } + onClicked: reloadScript(model.url) + } + } + + // FIXME: Put stop item in tableModel passed in from RunningScripts. + HiFiGlyphs { + id: stopButton + text: hifi.glyphs.closeSmall + color: parent.color + anchors { + top: parent.top + right: parent.right + verticalCenter: parent.verticalCenter + } + MouseArea { + anchors { fill: parent; margins: -2 } + onClicked: stopScript(model.url) + } + } + } + + // FIXME: Automatically use aux. information from tableModel + FiraSansSemiBold { + text: tableModel.get(styleData.row) ? tableModel.get(styleData.row).url : "" + elide: Text.ElideMiddle + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + top: textItem.bottom + left: parent.left + right: parent.right + } + visible: styleData.selected + } + } +} diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controls-uit/TextEdit.qml new file mode 100644 index 0000000000..5edc65a6e5 --- /dev/null +++ b/interface/resources/qml/controls-uit/TextEdit.qml @@ -0,0 +1,25 @@ +// +// TextEdit.qml +// +// Created by Bradley Austin Davis on 24 Apr 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "../styles-uit" + +TextEdit { + + property real size: 32 + + FontLoader { id: ralewaySemibold; source: "../../fonts/Raleway-Semibold.ttf"; } + font.family: ralewaySemibold.name + font.pointSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml new file mode 100644 index 0000000000..fa1f7c97ad --- /dev/null +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -0,0 +1,59 @@ +// +// TextField.qml +// +// Created by David Rowe on 17 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" + +TextField { + id: textField + + property int colorScheme: hifi.colorSchemes.light + property string label: "" + + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + font.family: firaSansSemiBold.name + font.pixelSize: hifi.fontSizes.textFieldInput + height: implicitHeight + 4 // Make surrounding box higher so that highlight is vertically centered. + placeholderText: textField.label // Instead of separate label (see below). + + style: TextFieldStyle { + textColor: textField.colorScheme == hifi.colorSchemes.light + ? (textField.focus ? hifi.colors.black : hifi.colors.lightGray) + : (textField.focus ? hifi.colors.white : hifi.colors.lightGrayText) + background: Rectangle { + color: textField.colorScheme == hifi.colorSchemes.light + ? (textField.focus ? hifi.colors.white : hifi.colors.lightGray) + : (textField.focus ? hifi.colors.black : hifi.colors.baseGrayShadow) + border.color: hifi.colors.primaryHighlight + border.width: textField.focus ? 1 : 0 + } + placeholderTextColor: hifi.colors.lightGray + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + padding.left: hifi.dimensions.textPadding + padding.right: hifi.dimensions.textPadding + } + + /* + // Separate label instead of placeholderText. + RalewaySemibold { + text: textField.label + size: hifi.fontSizes.inputLabel + color: hifi.colors.lightGrayText + anchors.left: parent.left + anchors.bottom: parent.top + anchors.bottomMargin: 4 + visible: label != "" + } + */ +} diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml new file mode 100644 index 0000000000..d100cdcd44 --- /dev/null +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -0,0 +1,147 @@ +// +// Table.qml +// +// Created by David Rowe on 17 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" + +TreeView { + id: treeView + + property var treeModel: ListModel { } + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + model: treeModel + + TableViewColumn { + role: "display"; + } + + anchors { left: parent.left; right: parent.right } + + headerVisible: false + headerDelegate: Item { } // Fix OSX QML bug that displays scrollbar starting too low. + + // Use rectangle to draw border with rounded corners. + frameVisible: false + Rectangle { + color: "#00000000" + anchors.fill: parent + radius: hifi.dimensions.borderRadius + border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + border.width: 2 + anchors.margins: -2 + } + anchors.margins: 2 // Shrink TreeView to lie within border. + + backgroundVisible: true + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + + style: TreeViewStyle { + // Needed in order for rows to keep displaying rows after end of table entries. + backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven + alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + branchDelegate: HiFiGlyphs { + text: styleData.isExpanded ? hifi.glyphs.disclosureCollapse : hifi.glyphs.disclosureExpand + size: hifi.fontSizes.tableText * 2.5 // tableText is in points; proportionately scale to pixels + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding / 2 + } + } + + handle: Item { + id: scrollbarHandle + implicitWidth: 6 + Rectangle { + anchors { + fill: parent + leftMargin: 2 // Move it right + rightMargin: -2 // "" + topMargin: 3 // Shrink vertically + bottomMargin: 3 // "" + } + radius: 3 + color: hifi.colors.tableScrollHandle + } + } + + scrollBarBackground: Item { + implicitWidth: 10 + Rectangle { + anchors { + fill: parent + margins: -1 // Expand + } + color: hifi.colors.baseGrayHighlight + } + + Rectangle { + anchors { + fill: parent + margins: 1 // Shrink + } + radius: 4 + color: hifi.colors.tableScrollBackground + } + } + + incrementControl: Item { + visible: false + } + + decrementControl: Item { + visible: false + } + } + + rowDelegate: Rectangle { + height: hifi.dimensions.tableRowHeight + color: styleData.selected + ? hifi.colors.primaryHighlight + : treeView.isLightColorScheme + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + } + + itemDelegate: FiraSansSemiBold { + anchors { + left: parent ? parent.left : undefined + leftMargin: (2 + styleData.depth) * hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent ? parent.verticalCenter : undefined + } + + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + } + + onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) + + onActivated: { + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } +} diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml new file mode 100644 index 0000000000..a3a0276f8f --- /dev/null +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -0,0 +1,18 @@ +// +// VerticalSpacer.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../styles-uit" + +Item { + width: 1 // Must be non-zero + height: hifi.dimensions.contentSpacing.y +} diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 0286c45ac3..d2a3d7f7b9 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -1,21 +1,33 @@ +// +// Desktop.qml +// +// Created by Bradley Austin Davis on 15 Apr 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs; import "../dialogs" import "../menus" import "../js/Utils.js" as Utils -// This is our primary 'desktop' object to which all VR dialogs and -// windows will be childed. +// This is our primary 'desktop' object to which all VR dialogs and windows are childed. FocusScope { id: desktop - anchors.fill: parent; objectName: "desktop" + anchors.fill: parent onHeightChanged: d.repositionAll(); onWidthChanged: d.repositionAll(); + // Controls and windows can trigger this signal to ensure the desktop becomes visible + // when they're opened. + signal showDesktop(); + // Allows QML/JS to find the desktop through the parent chain property bool desktopRoot: true @@ -148,12 +160,12 @@ FocusScope { } Component.onCompleted: { - offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged); + //offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged); focusHack.start(); } function onWindowFocusChanged() { - console.log("Focus item is " + offscreenWindow.activeFocusItem); + //console.log("Focus item is " + offscreenWindow.activeFocusItem); // FIXME this needs more testing before it can go into production // and I already cant produce any way to have a modal dialog lose focus @@ -217,6 +229,8 @@ FocusScope { } reposition(targetWindow); + + showDesktop(); } function reposition(item) { @@ -304,6 +318,7 @@ FocusScope { Rectangle { id: focusDebugger; + objectName: "focusDebugger" z: 9999; visible: false; color: "red" ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 } } @@ -314,8 +329,5 @@ FocusScope { enabled: DebugQML onTriggered: focusDebugger.visible = !focusDebugger.visible } - + } - - - diff --git a/interface/resources/qml/desktop/FocusHack.qml b/interface/resources/qml/desktop/FocusHack.qml index 7514705983..3868ccc14f 100644 --- a/interface/resources/qml/desktop/FocusHack.qml +++ b/interface/resources/qml/desktop/FocusHack.qml @@ -1,7 +1,18 @@ +// +// FocusHack.qml +// +// Created by Bradley Austin Davis on 21 Jan 2015 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 FocusScope { id: root + objectName: "FocusHack" TextInput { id: textInput; @@ -21,6 +32,3 @@ FocusScope { focusTimer.running = true; } } - - - diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 3b7cc2c9a8..26ed406b02 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -1,13 +1,23 @@ +// +// Desktop.qml +// +// Created by Bradley Austin Davis on 25 Apr 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 -import QtQuick.Controls 1.2 +import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../controls" -import "../styles" -import "../windows" +import "../controls-uit" +import "../styles-uit" +import "../windows-uit" + import "messageDialog" -// FIXME respect default button functionality ModalWindow { id: root HifiConstants { id: hifi } @@ -32,96 +42,91 @@ ModalWindow { property alias detailedText: detailedText.text property alias text: mainTextContainer.text property alias informativeText: informativeTextContainer.text - onIconChanged: iconHolder.updateIcon(); + onIconChanged: updateIcon(); property int buttons: OriginalDialogs.StandardButton.Ok property int icon: OriginalDialogs.StandardIcon.NoIcon + property string iconText: "" property int defaultButton: OriginalDialogs.StandardButton.NoButton; property int clickedButton: OriginalDialogs.StandardButton.NoButton; focus: defaultButton === OriginalDialogs.StandardButton.NoButton - Rectangle { + function updateIcon() { + if (!root) { + return; + } + switch (root.icon) { + case OriginalDialogs.StandardIcon.Information: + iconText = "\uF05A"; + break; + case OriginalDialogs.StandardIcon.Question: + iconText = "\uF059" + break; + case OriginalDialogs.StandardIcon.Warning: + iconText = "\uF071" + break; + case OriginalDialogs.StandardIcon.Critical: + iconText = "\uF057" + break; + default: + iconText = "" + } + } + + Item { id: messageBox clip: true - anchors.fill: parent - radius: 4 - color: "white" + width: pane.width + height: pane.height QtObject { id: d - readonly property real spacing: hifi.layout.spacing - readonly property real outerSpacing: hifi.layout.spacing * 2 + readonly property real spacing: hifi.dimensions.contentSpacing.x + readonly property real outerSpacing: hifi.dimensions.contentSpacing.y readonly property int minWidth: 480 readonly property int maxWdith: 1280 - readonly property int minHeight: 160 + readonly property int minHeight: 120 readonly property int maxHeight: 720 function resize() { - var targetWidth = iconHolder.width + mainTextContainer.width + d.spacing * 6 - var targetHeight = mainTextContainer.implicitHeight + informativeTextContainer.implicitHeight + d.spacing * 8 + buttons.height + details.height + var targetWidth = mainTextContainer.width + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + + (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0) + + buttons.height + + (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0) root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth) root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight) } } - FontAwesome { - id: iconHolder - size: 48 - anchors { - left: parent.left - top: parent.top - margins: d.spacing * 2 - } - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - style: Text.Outline; styleColor: "black" - Component.onCompleted: updateIcon(); - function updateIcon() { - if (!root) { - return; - } - switch (root.icon) { - case OriginalDialogs.StandardIcon.Information: - text = "\uF05A"; - color = "blue"; - break; - - case OriginalDialogs.StandardIcon.Question: - text = "\uF059" - color = "blue"; - break; - - case OriginalDialogs.StandardIcon.Warning: - text = "\uF071" - color = "yellow"; - break; - - case OriginalDialogs.StandardIcon.Critical: - text = "\uF057" - color = "red" - break; - - default: - text = "" - } - visible = (text != ""); - } - } - - Text { + RalewaySemibold { id: mainTextContainer onHeightChanged: d.resize(); onWidthChanged: d.resize(); wrapMode: Text.WordWrap - font { pointSize: 14; weight: Font.Bold } - anchors { left: iconHolder.right; top: parent.top; margins: d.spacing * 2 } + size: hifi.fontSizes.menuItem + color: hifi.colors.baseGrayHighlight + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight } - Text { + RalewaySemibold { id: informativeTextContainer onHeightChanged: d.resize(); onWidthChanged: d.resize(); wrapMode: Text.WordWrap - font.pointSize: 11 - anchors { top: mainTextContainer.bottom; right: parent.right; left: iconHolder.right; margins: d.spacing * 2 } + size: hifi.fontSizes.menuItem + color: hifi.colors.baseGrayHighlight + anchors { + top: mainTextContainer.bottom + left: parent.left + right: parent.right + margins: 0 + topMargin: text != "" ? hifi.dimensions.contentSpacing.y : 0 + } } Flow { @@ -130,29 +135,35 @@ ModalWindow { spacing: d.spacing onHeightChanged: d.resize(); onWidthChanged: d.resize(); layoutDirection: Qt.RightToLeft - anchors { bottom: details.top; right: parent.right; margins: d.spacing * 2; bottomMargin: 0 } - MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; } - MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; } - MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; } - MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; } - MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; } - MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; } - MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; } - MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; } - MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; } - MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; } - MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; } - MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; } - MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; } - MessageDialogButton { dialog: root; text: qsTr("Restore Defaults"); button: OriginalDialogs.StandardButton.RestoreDefaults; } - MessageDialogButton { dialog: root; text: qsTr("Cancel"); button: OriginalDialogs.StandardButton.Cancel; } - MessageDialogButton { dialog: root; text: qsTr("Abort"); button: OriginalDialogs.StandardButton.Abort; } + anchors { + top: informativeTextContainer.text == "" ? mainTextContainer.bottom : informativeTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } MessageDialogButton { dialog: root; text: qsTr("Close"); button: OriginalDialogs.StandardButton.Close; } + MessageDialogButton { dialog: root; text: qsTr("Abort"); button: OriginalDialogs.StandardButton.Abort; } + MessageDialogButton { dialog: root; text: qsTr("Cancel"); button: OriginalDialogs.StandardButton.Cancel; } + MessageDialogButton { dialog: root; text: qsTr("Restore Defaults"); button: OriginalDialogs.StandardButton.RestoreDefaults; } + MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; } + MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; } + MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; } + MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; } + MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; } + MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; } + MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; } + MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; } + MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; } + MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; } + MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; } + MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; } + MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; } + Button { id: moreButton text: qsTr("Show Details...") - onClicked: { content.state = (content.state === "" ? "expanded" : "") - } + width: 160 + onClicked: { content.state = (content.state === "" ? "expanded" : "") } visible: detailedText && detailedText.length > 0 } MessageDialogButton { dialog: root; text: qsTr("Help"); button: OriginalDialogs.StandardButton.Help; } @@ -161,10 +172,16 @@ ModalWindow { Item { id: details width: parent.width - implicitHeight: detailedText.implicitHeight + root.spacing + implicitHeight: detailedText.implicitHeight height: 0 clip: true - anchors { bottom: parent.bottom; left: parent.left; right: parent.right; margins: d.spacing * 2 } + anchors { + top: buttons.bottom + left: parent.left; + right: parent.right; + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } Flickable { id: flickable contentHeight: detailedText.height @@ -173,10 +190,13 @@ ModalWindow { anchors.bottomMargin: root.outerSpacing TextEdit { id: detailedText + size: hifi.fontSizes.menuItem + color: hifi.colors.baseGrayHighlight width: details.width wrapMode: Text.WordWrap readOnly: true selectByMouse: true + anchors.margins: 0 } } } @@ -190,6 +210,7 @@ ModalWindow { } ] + Component.onCompleted: updateIcon(); onStateChanged: d.resize() } diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index 2bcb3d1f29..b7ff9354eb 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -1,11 +1,24 @@ +// +// MessageDialogButton.qml +// +// Created by Bradley Austin Davis on 29 Jan 2016 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 +import "../../controls-uit" + Button { property var dialog; property int button: StandardButton.NoButton; + color: dialog.defaultButton === button ? hifi.buttons.blue : hifi.buttons.white focus: dialog.defaultButton === button onClicked: dialog.click(button) visible: dialog.buttons & button diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 5227d3cb2e..409c468878 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -8,6 +8,16 @@ import ".." Desktop { id: desktop + MouseArea { + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + scrollGestureEnabled: false // we don't need/want these + onEntered: ApplicationCompositor.reticleOverDesktop = true + onExited: ApplicationCompositor.reticleOverDesktop = false + acceptedButtons: Qt.NoButton + } + Component.onCompleted: { WebEngine.settings.javascriptCanOpenWindows = true; WebEngine.settings.javascriptCanAccessClipboard = false; diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 8834c1d8bc..55b8946805 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -1,11 +1,21 @@ +// +// RunningScripts.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles" as Hifi -import "../../controls" as HifiControls -import "../../windows" +import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../windows-uit" Window { id: root @@ -15,6 +25,9 @@ Window { destroyOnInvisible: true x: 40; y: 40 implicitWidth: 384; implicitHeight: 640 + minSize: Qt.vector2d(200, 300) + + HifiConstants { id: hifi } property var scripts: ScriptDiscoveryService; property var scriptsModel: scripts.scriptsModelFilter @@ -33,6 +46,11 @@ Window { Component.onCompleted: updateRunningScripts() + function setDefaultFocus() { + // Work around FocusScope of scrollable window. + filterEdit.forceActiveFocus(); + } + function updateRunningScripts() { var runningScripts = ScriptDiscoveryService.getRunning(); runningScriptsModel.clear() @@ -66,126 +84,50 @@ Window { scripts.stopAllScripts(); } - Rectangle { - color: "white" - anchors.fill: parent + Column { + width: pane.contentWidth - Item { - anchors { fill: parent; margins: 8 } - Text { - id: title - font.bold: true - font.pointSize: 16 - color: "#0e7077" - text: "Currently Running" - } + HifiControls.StaticSection { + name: "Currently Running" Row { - id: allButtons - anchors.top: title.bottom - anchors.topMargin: 8 - spacing: 8 - Button { text: "Reload all"; onClicked: reloadAll() } - Button { text: "Stop all"; onClicked: stopAll() } - } + spacing: hifi.dimensions.contentSpacing.x - ScrollView { - onActiveFocusChanged: if (activeFocus && listView.currentItem) { listView.currentItem.forceActiveFocus(); } - anchors { - top: allButtons.bottom; - left: parent.left; - right: parent.right; - topMargin: 8 - bottom: row1.top - bottomMargin: 8 + HifiControls.Button { + text: "Reload all" + color: hifi.buttons.black + onClicked: reloadAll() } - ListView { - id: listView - clip: true - anchors { fill: parent; margins: 0 } - model: runningScriptsModel - delegate: FocusScope { - id: scope - anchors { left: parent.left; right: parent.right } - height: scriptName.height + 12 + (ListView.isCurrentItem ? scriptName.height + 6 : 0) - Keys.onDownPressed: listView.incrementCurrentIndex() - Keys.onUpPressed: listView.decrementCurrentIndex() - Rectangle { - id: rectangle - anchors.fill: parent - clip: true - radius: 3 - color: scope.ListView.isCurrentItem ? "#79f" : - index % 2 ? "#ddd" : "#eee" - - Text { - id: scriptName - anchors { left: parent.left; leftMargin: 4; top: parent.top; topMargin:6 } - text: name - } - - Text { - id: scriptUrl - anchors { left: scriptName.left; right: parent.right; rightMargin: 4; top: scriptName.bottom; topMargin: 6 } - text: url - elide: Text.ElideMiddle - } - - MouseArea { - anchors.fill: parent - onClicked: { listView.currentIndex = index; scope.forceActiveFocus(); } - } - - Row { - anchors.verticalCenter: scriptName.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 4 - spacing: 4 - HifiControls.FontAwesome { - text: "\uf021"; size: scriptName.height; - MouseArea { - anchors { fill: parent; margins: -2; } - onClicked: reloadScript(model.url) - } - } - HifiControls.FontAwesome { - size: scriptName.height; text: "\uf00d" - MouseArea { - anchors { fill: parent; margins: -2; } - onClicked: stopScript(model.url) - } - } - } - } - } + HifiControls.Button { + text: "Stop all" + color: hifi.buttons.red + onClicked: stopAll() } } - Text { - id: loadLabel - text: "Load Scripts" - font.bold: true - font.pointSize: 16 - color: "#0e7077" - + HifiControls.Table { + tableModel: runningScriptsModel + height: 185 + colorScheme: hifi.colorSchemes.dark anchors.left: parent.left - anchors.leftMargin: 0 - anchors.bottom: filterEdit.top - anchors.bottomMargin: 8 + anchors.right: parent.right } + } + + HifiControls.StaticSection { + name: "Load Scripts" + hasSeparator: true Row { - id: row1 - spacing: 8 - anchors.bottom: filterEdit.top - anchors.bottomMargin: 8 + spacing: hifi.dimensions.contentSpacing.x anchors.right: parent.right - - Button { - text: "from URL"; - onClicked: fromUrlTimer.running = true; + HifiControls.Button { + text: "from URL" + color: hifi.buttons.black + height: 26 + onClicked: fromUrlTimer.running = true // For some reason trigginer an API that enters // an internal event loop directly from the button clicked @@ -203,9 +145,11 @@ Window { } } - Button { + HifiControls.Button { text: "from Disk" - onClicked: fromDiskTimer.running = true; + color: hifi.buttons.black + height: 26 + onClicked: fromDiskTimer.running = true Timer { id: fromDiskTimer @@ -221,66 +165,32 @@ Window { id: filterEdit anchors.left: parent.left anchors.right: parent.right - anchors.bottom: treeView.top - anchors.bottomMargin: 8 - placeholderText: "filter" focus: true + colorScheme: hifi.colorSchemes.dark + //placeholderText: "filter" + label: "Filter" onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i") Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i") } - TreeView { + HifiControls.Tree { id: treeView - height: 128 - anchors.bottom: loadButton.top - anchors.bottomMargin: 8 + height: 155 + treeModel: scriptsModel + colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.right: parent.right - headerVisible: false - // FIXME doesn't work? - onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) - // FIXME not triggered by double click? - onActivated: { - var path = scriptsModel.data(index, 0x100) - if (path) { - loadScript(path) - } - } - model: scriptsModel - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: treeView.foo(); - } - - function foo() { - var localRect = Qt.rect(0, 0, width, height); - var rect = desktop.mapFromItem(treeView, 0, 0, width, height) - console.log("Local Rect " + localRect) - console.log("Rect " + rect) - console.log("Desktop size " + Qt.size(desktop.width, desktop.height)); - } - - TableViewColumn { - title: "Name"; - role: "display"; - // delegate: Text { - // text: styleData.value - // renderType: Text.QtRendering - // elite: styleData.elideMode - // } - } } HifiControls.TextField { id: selectedScript - readOnly: true anchors.left: parent.left - anchors.right: loadButton.left - anchors.rightMargin: 8 - anchors.bottom: loadButton.bottom - anchors.top: loadButton.top + anchors.right: parent.right + anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x + + colorScheme: hifi.colorSchemes.dark + readOnly: true + Connections { target: treeView onCurrentIndexChanged: { @@ -290,19 +200,28 @@ Window { } else { selectedScript.text = "" } - } } } - Button { - id: loadButton - anchors.bottom: parent.bottom + Item { + // Take the loadButton out of the column flow. + id: loadButtonContainer + anchors.top: selectedScript.top anchors.right: parent.right - text: "Load" - enabled: selectedScript.text != "" - onClicked: root.loadScript(selectedScript.text) + + HifiControls.Button { + id: loadButton + anchors.right: parent.right + + text: "Load" + color: hifi.buttons.blue + enabled: selectedScript.text != "" + onClicked: root.loadScript(selectedScript.text) + } } + + HifiControls.VerticalSpacer { } } } } diff --git a/interface/resources/qml/menus/MenuMouseHandler.qml b/interface/resources/qml/menus/MenuMouseHandler.qml index bb2cc7d459..6f507dd445 100644 --- a/interface/resources/qml/menus/MenuMouseHandler.qml +++ b/interface/resources/qml/menus/MenuMouseHandler.qml @@ -6,9 +6,11 @@ import "." Item { id: root anchors.fill: parent + objectName: "MouseMenuHandlerItem" MouseArea { id: menuRoot; + objectName: "MouseMenuHandlerMouseArea" anchors.fill: parent enabled: d.topMenu !== null onClicked: { diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/styles-uit/AnonymousProRegular.qml new file mode 100644 index 0000000000..789689973b --- /dev/null +++ b/interface/resources/qml/styles-uit/AnonymousProRegular.qml @@ -0,0 +1,23 @@ +// +// AnonymousProRegular.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: anonymousProRegular; source: "../../fonts/AnonymousPro-Regular.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: anonymousProRegular.name +} diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml new file mode 100644 index 0000000000..ddbeff7d90 --- /dev/null +++ b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml @@ -0,0 +1,23 @@ +// +// FiraSansSemiBold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: firaSansSemiBold.name +} diff --git a/interface/resources/qml/styles-uit/FontAwesome.qml b/interface/resources/qml/styles-uit/FontAwesome.qml new file mode 100644 index 0000000000..5c03ef09b2 --- /dev/null +++ b/interface/resources/qml/styles-uit/FontAwesome.qml @@ -0,0 +1,26 @@ +// +// FontAwesome.qml +// +// Created by Bradley Austin Davis on 24 Apr 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; } + property int size: 32 + width: size + height: size + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: iconFont.name +} + diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml new file mode 100644 index 0000000000..d0dae746be --- /dev/null +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -0,0 +1,25 @@ +// +// HiFiGlyphs.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: hiFiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; } + property int size: 32 + font.pixelSize: size + width: size + height: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: hiFiGlyphs.name +} diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml new file mode 100644 index 0000000000..77e74163c3 --- /dev/null +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -0,0 +1,134 @@ +// +// HiFiConstants.qml +// +// Created by Bradley Austin Davis on 28 Apr 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +Item { + readonly property alias colors: colors + readonly property alias colorSchemes: colorSchemes + readonly property alias dimensions: dimensions + readonly property alias fontSizes: fontSizes + readonly property alias glyphs: glyphs + readonly property alias buttons: buttons + readonly property alias effects: effects + + Item { + id: colors + + // Base colors + readonly property color baseGray: "#404040" + readonly property color darkGray: "#121212" + readonly property color baseGrayShadow: "#252525" + readonly property color baseGrayHighlight: "#575757" + readonly property color lightGray: "#6a6a6a" + readonly property color lightGrayText: "#afafaf" + readonly property color faintGray: "#e3e3e3" + readonly property color primaryHighlight: "#00b4ef" + readonly property color blueAccent: "#1080b8" + readonly property color redHighlight: "#e2334d" + readonly property color redAccent: "#b70a37" + readonly property color greenHighlight: "#1ac567" + readonly property color greenShadow: "#2c8e72" + // Semitransparent + readonly property color darkGray30: "#4d121212" + readonly property color darkGray0: "#00121212" + readonly property color baseGrayShadow60: "#99252525" + readonly property color baseGrayHighlight40: "#66575757" + readonly property color baseGrayHighlight15: "#26575757" + readonly property color lightGrayText80: "#ccafafaf" + readonly property color faintGray50: "#80e3e3e3" + + // Other colors + readonly property color white: "#ffffff" + readonly property color black: "#000000" + // Semitransparent + readonly property color white50: "#80ffffff" + readonly property color white30: "#4dffffff" + readonly property color white25: "#40ffffff" + + // Control specific colors + readonly property color tableRowLightOdd: white50 + readonly property color tableRowLightEven: "#1a575757" + readonly property color tableRowDarkOdd: "#80393939" + readonly property color tableRowDarkEven: "#a6181818" + readonly property color tableScrollHandle: "#707070" + readonly property color tableScrollBackground: "#323232" + } + + Item { + id: colorSchemes + readonly property int light: 0 + readonly property int dark: 1 + } + + Item { + id: dimensions + readonly property bool largeScreen: Screen.width >= 1920 && Screen.height >= 1080 + readonly property real borderRadius: largeScreen ? 7.5 : 5.0 + readonly property real borderWidth: largeScreen ? 2 : 1 + readonly property vector2d contentMargin: Qt.vector2d(12, 24) + readonly property vector2d contentSpacing: Qt.vector2d(8, 12) + readonly property real textPadding: 8 + readonly property real tablePadding: 12 + readonly property real tableRowHeight: largeScreen ? 26 : 23 + readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30) + readonly property real modalDialogTitleHeight: 40 + } + + Item { + id: fontSizes // In pixels + readonly property real overlayTitle: dimensions.largeScreen? 18 : 14 + readonly property real tabName: dimensions.largeScreen? 12 : 10 + readonly property real sectionName: dimensions.largeScreen? 12 : 10 + readonly property real inputLabel: dimensions.largeScreen? 14 : 10 + readonly property real textFieldInput: dimensions.largeScreen? 15 : 12 + readonly property real tableText: dimensions.largeScreen? 15 : 12 + readonly property real buttonLabel: dimensions.largeScreen? 13 : 9 + readonly property real iconButton: dimensions.largeScreen? 13 : 9 + readonly property real listItem: dimensions.largeScreen? 15 : 11 + readonly property real tabularData: dimensions.largeScreen? 15 : 11 + readonly property real logs: dimensions.largeScreen? 16 : 12 + readonly property real code: dimensions.largeScreen? 16 : 12 + readonly property real rootMenu: dimensions.largeScreen? 15 : 11 + readonly property real menuItem: dimensions.largeScreen? 15 : 11 + readonly property real shortcutText: dimensions.largeScreen? 13 : 9 + } + + Item { + id: glyphs + readonly property string close: "w" + readonly property string closeInverted: "x" + readonly property string closeSmall: "C" + readonly property string disclosureCollapse: "Z" + readonly property string disclosureExpand: "B" + readonly property string pin: "y" + readonly property string pinInverted: "z" + readonly property string reloadSmall: "a" + readonly property string resizeHandle: "A" + } + + Item { + id: buttons + readonly property int white: 0 + readonly property int blue: 1 + readonly property int red: 2 + readonly property int black: 3 + readonly property var textColor: [ colors.darkGray, colors.white, colors.white, colors.white ] + readonly property var colorStart: [ "#ffffff", "#00b4ef", "#d42043", "#343434" ] + readonly property var colorFinish: [ "#afafaf", "#1080b8", "#94132e", "#000000" ] + readonly property int radius: 5 + } + + QtObject { + id: effects + readonly property int fadeInDuration: 300 + } +} diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/styles-uit/RalewayBold.qml new file mode 100644 index 0000000000..97a6a4c208 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayBold.qml @@ -0,0 +1,24 @@ +// +// RalewayBold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: ralewayBold; source: "../../fonts/Raleway-Bold.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: ralewayBold.name + font.bold: true // Font seems to need this in order to display bold. +} diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/styles-uit/RalewayLight.qml new file mode 100644 index 0000000000..913918afd7 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayLight.qml @@ -0,0 +1,23 @@ +// +// RalewayLight.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: ralewayLight; source: "../../fonts/Raleway-Light.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: ralewayLight.name +} diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/styles-uit/RalewayRegular.qml new file mode 100644 index 0000000000..1ed5f122dc --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayRegular.qml @@ -0,0 +1,23 @@ +// +// RalewayRegular.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: ralewayRegular.name +} diff --git a/interface/resources/qml/styles-uit/RalewaySemibold.qml b/interface/resources/qml/styles-uit/RalewaySemibold.qml new file mode 100644 index 0000000000..3648e6bab9 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewaySemibold.qml @@ -0,0 +1,23 @@ +// +// RalewaySemibold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: ralewaySemibold; source: "../../fonts/Raleway-Semibold.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: ralewaySemibold.name +} diff --git a/interface/resources/qml/windows-uit/DefaultFrame.qml b/interface/resources/qml/windows-uit/DefaultFrame.qml new file mode 100644 index 0000000000..4e4c2df8f2 --- /dev/null +++ b/interface/resources/qml/windows-uit/DefaultFrame.qml @@ -0,0 +1,111 @@ +// +// DefaultFrame.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import "." +import "../styles-uit" + +Frame { + HifiConstants { id: hifi } + + Rectangle { + // Dialog frame + id: frameContent + anchors { + topMargin: -frameMarginTop + leftMargin: -frameMarginLeft + rightMargin: -frameMarginRight + bottomMargin: -frameMarginBottom + } + anchors.fill: parent + color: hifi.colors.baseGrayHighlight40 + border { + width: hifi.dimensions.borderWidth + color: hifi.colors.faintGray50 + } + radius: hifi.dimensions.borderRadius + + // Allow dragging of the window + MouseArea { + anchors.fill: parent + drag.target: window + } + + Row { + id: controlsRow + anchors { + right: parent.right; + top: parent.top; + topMargin: frameMargin + 1 // Move down a little to visually align with the title + rightMargin: frameMarginRight; + } + spacing: iconSize / 4 + + HiFiGlyphs { + // "Pin" button + visible: false + text: (frame.pinned && !pinClickArea.containsMouse) || (!frame.pinned && pinClickArea.containsMouse) ? hifi.glyphs.pinInverted : hifi.glyphs.pin + color: pinClickArea.containsMouse && !pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white + size: iconSize + MouseArea { + id: pinClickArea + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + onClicked: { frame.pin(); mouse.accepted = false; } + } + } + + HiFiGlyphs { + // "Close" button + visible: window ? window.closable : false + text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close + color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white + size: iconSize + MouseArea { + id: closeClickArea + anchors.fill: parent + hoverEnabled: true + onClicked: window.visible = false; + } + } + } + + RalewayRegular { + // Title + id: titleText + anchors { + left: parent.left + leftMargin: frameMarginLeft + hifi.dimensions.contentMargin.x + right: controlsRow.left + rightMargin: iconSize + top: parent.top + topMargin: frameMargin + } + text: window ? window.title : "" + color: hifi.colors.white + size: hifi.fontSizes.overlayTitle + } + + DropShadow { + source: titleText + anchors.fill: titleText + horizontalOffset: 1 + verticalOffset: 1 + samples: 2 + color: hifi.colors.baseGrayShadow60 + visible: (window && window.focus) + cached: true + } + } +} + diff --git a/interface/resources/qml/windows-uit/Fadable.qml b/interface/resources/qml/windows-uit/Fadable.qml new file mode 100644 index 0000000000..34990c2147 --- /dev/null +++ b/interface/resources/qml/windows-uit/Fadable.qml @@ -0,0 +1,60 @@ +// +// Fadable.qml +// +// Created by Bradley Austin Davis on 15 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtGraphicalEffects 1.0 + +import "../styles-uit" + +// Enable window visibility transitions +FocusScope { + id: root + HifiConstants { id: hifi } + + Component.onCompleted: { + fadeTargetProperty = visible ? 1.0 : 0.0 + } + + // The target property to animate, usually scale or opacity + property alias fadeTargetProperty: root.opacity + // always start the property at 0 to enable fade in on creation + fadeTargetProperty: 0 + // DO NOT set visible to false or when derived types override it it + // will short circuit the fade in on initial visibility + // visible: false <--- NO + + // Some dialogs should be destroyed when they become + // invisible, so handle that + onVisibleChanged: { + // If someone directly set the visibility to false + // toggle it back on and use the targetVisible flag to transition + // via fading. + if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) { + var target = visible; + visible = !visible; + fadeTargetProperty = target ? 1.0 : 0.0; + return; + } + } + + // The actual animator + Behavior on fadeTargetProperty { + NumberAnimation { + duration: hifi.effects.fadeInDuration + easing.type: Easing.InOutCubic + } + } + + // Once we're transparent, disable the dialog's visibility + onFadeTargetPropertyChanged: { + visible = (fadeTargetProperty != 0.0); + } +} diff --git a/interface/resources/qml/windows-uit/Frame.qml b/interface/resources/qml/windows-uit/Frame.qml new file mode 100644 index 0000000000..918ee21eec --- /dev/null +++ b/interface/resources/qml/windows-uit/Frame.qml @@ -0,0 +1,133 @@ +// +// Frame.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import "../styles-uit" +import "../js/Utils.js" as Utils + +Item { + id: frame + HifiConstants { id: hifi } + + default property var decoration + + readonly property int iconSize: 22 + readonly property int frameMargin: 9 + readonly property int frameMarginLeft: frameMargin + readonly property int frameMarginRight: frameMargin + readonly property int frameMarginTop: 2 * frameMargin + iconSize + readonly property int frameMarginBottom: iconSize + 11 + + // Frames always fill their parents, but their decorations may extend + // beyond the window via negative margin sizes + anchors.fill: parent + + children: [ + focusShadow, + decoration, + sizeOutline, + debugZ, + sizeDrag + ] + + Text { + id: debugZ + visible: DebugQML + text: window ? "Z: " + window.z : "" + y: window ? window.height + 4 : 0 + } + + function deltaSize(dx, dy) { + var newSize = Qt.vector2d(window.width + dx, window.height + dy); + newSize = Utils.clampVector(newSize, window.minSize, window.maxSize); + window.width = newSize.x + window.height = newSize.y + } + + RadialGradient { + id: focusShadow + width: 1.66 * window.width + height: 1.66 * window.height + x: (window.width - width) / 2 + y: window.height / 2 - 0.375 * height + // FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX. + // Check again when have a later version of QtQuick. + visible: window && window.focus && pane.visible && Qt.platform.os != "osx" + gradient: Gradient { + // GradientStop position 0.5 is at full circumference of circle that fits inside the square. + GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity + GradientStop { position: 0.333; color: "#1f000000" } // black, 12% opacity + GradientStop { position: 0.5; color: "#00000000" } // black, 0% opacity + GradientStop { position: 1.0; color: "#00000000" } + } + cached: true + } + + Rectangle { + id: sizeOutline + x: -frameMarginLeft + y: -frameMarginTop + width: window ? window.width + frameMarginLeft + frameMarginRight : 0 + height: window ? window.height + frameMarginTop + frameMarginBottom : 0 + color: hifi.colors.baseGrayHighlight15 + border.width: 3 + border.color: hifi.colors.white50 + radius: hifi.dimensions.borderRadius + visible: window ? !pane.visible : false + } + + MouseArea { + // Resize handle + id: sizeDrag + width: iconSize + height: iconSize + enabled: window ? window.resizable : false + hoverEnabled: true + x: window ? window.width + frameMarginRight - iconSize : 0 + y: window ? window.height + 4 : 0 + property vector2d pressOrigin + property vector2d sizeOrigin + property bool hid: false + onPressed: { + //console.log("Pressed on size") + pressOrigin = Qt.vector2d(mouseX, mouseY) + sizeOrigin = Qt.vector2d(window.content.width, window.content.height) + hid = false; + } + onReleased: { + if (hid) { + pane.visible = true + frameContent.visible = true + hid = false; + } + } + onPositionChanged: { + if (pressed) { + if (pane.visible) { + pane.visible = false; + frameContent.visible = false + hid = true; + } + var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin); + frame.deltaSize(delta.x, delta.y) + } + } + HiFiGlyphs { + visible: sizeDrag.enabled + x: -11 // Move a little to visually align + y: -4 // "" + text: hifi.glyphs.resizeHandle + size: iconSize + 10 + color: sizeDrag.containsMouse || sizeDrag.pressed ? hifi.colors.white : hifi.colors.white50 + } + } +} diff --git a/interface/resources/qml/windows-uit/ModalFrame.qml b/interface/resources/qml/windows-uit/ModalFrame.qml new file mode 100644 index 0000000000..d6c05c8f91 --- /dev/null +++ b/interface/resources/qml/windows-uit/ModalFrame.qml @@ -0,0 +1,81 @@ +// +// ModalFrame.qml +// +// Created by Bradley Austin Davis on 15 Jan 2016 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "." +import "../controls-uit" +import "../styles-uit" + +Frame { + HifiConstants { id: hifi } + + Rectangle { + id: modalFrame + + readonly property bool hasTitle: window.title != "" + + anchors { + fill: parent + topMargin: -hifi.dimensions.modalDialogMargin.y - (modalFrame.hasTitle ? hifi.dimensions.modalDialogTitleHeight + 10 : 0) + leftMargin: -hifi.dimensions.modalDialogMargin.x + rightMargin: -hifi.dimensions.modalDialogMargin.x + bottomMargin: -hifi.dimensions.modalDialogMargin.y + } + + border { + width: hifi.dimensions.borderWidth + color: hifi.colors.lightGrayText80 + } + radius: hifi.dimensions.borderRadius + color: hifi.colors.faintGray + + Item { + visible: modalFrame.hasTitle + anchors.fill: parent + anchors { + topMargin: -parent.anchors.topMargin + leftMargin: -parent.anchors.leftMargin + rightMargin: -parent.anchors.rightMargin + } + + Item { + width: title.width + (window.iconText !== "" ? icon.width + hifi.dimensions.contentSpacing.x : 0) + x: (parent.width - width) / 2 + + FontAwesome { + id: icon + text: window.iconText + size: 30 + color: hifi.colors.lightGrayText + visible: text != "" + y: -hifi.dimensions.modalDialogTitleHeight - 5 + anchors.left: parent.left + } + RalewayRegular { + id: title + text: window.title + elide: Text.ElideRight + color: hifi.colors.baseGrayHighlight + size: hifi.fontSizes.overlayTitle + y: -hifi.dimensions.modalDialogTitleHeight + anchors.right: parent.right + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: hifi.colors.lightGray + } + } + } +} diff --git a/interface/resources/qml/windows-uit/ModalWindow.qml b/interface/resources/qml/windows-uit/ModalWindow.qml new file mode 100644 index 0000000000..af099eb275 --- /dev/null +++ b/interface/resources/qml/windows-uit/ModalWindow.qml @@ -0,0 +1,22 @@ +// +// ModalWindow.qml +// +// Created by Bradley Austin Davis on 22 Jan 2016 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "." + +Window { + id: window + anchors.centerIn: parent + modality: Qt.ApplicationModal + destroyOnCloseButton: true + destroyOnInvisible: true + frame: ModalFrame{} +} diff --git a/interface/resources/qml/windows-uit/Window.qml b/interface/resources/qml/windows-uit/Window.qml new file mode 100644 index 0000000000..ae73a53d65 --- /dev/null +++ b/interface/resources/qml/windows-uit/Window.qml @@ -0,0 +1,281 @@ +// +// Window.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtGraphicalEffects 1.0 + +import "." +import "../styles-uit" + +// FIXME how do I set the initial position of a window without +// overriding places where the a individual client of the window +// might be setting the position with a Settings{} element? + +// FIXME how to I enable dragging without allowing the window to lay outside +// of the desktop? How do I ensure when the desktop resizes all the windows +// are still at least partially visible? +Fadable { + id: window + HifiConstants { id: hifi } + + // The Window size is the size of the content, while the frame + // decorations can extend outside it. + implicitHeight: content ? content.height : 0 + implicitWidth: content ? content.width : 0 + x: -1; y: -1 + enabled: visible + + signal windowDestroyed(); + + property int modality: Qt.NonModal + readonly property bool topLevelWindow: true + property string title + // Should the window be closable control? + property bool closable: true + // Should the window try to remain on top of other windows? + property bool alwaysOnTop: false + // Should hitting the close button hide or destroy the window? + property bool destroyOnCloseButton: true + // Should hiding the window destroy it or just hide it? + property bool destroyOnInvisible: false + // FIXME support for pinned / unpinned pending full design + // property bool pinnable: false + // property bool pinned: false + property bool resizable: false + + property vector2d minSize: Qt.vector2d(100, 100) + property vector2d maxSize: Qt.vector2d(1280, 720) + + // The content to place inside the window, determined by the client + default property var content + + function setDefaultFocus() {} // Default function; can be overridden by dialogs. + + property var rectifier: Timer { + property bool executing: false; + interval: 100 + repeat: false + running: false + + onTriggered: { + executing = true; + x = Math.floor(x); + y = Math.floor(y); + executing = false; + } + + function begin() { + if (!executing) { + restart(); + } + } + } + onXChanged: rectifier.begin(); + onYChanged: rectifier.begin(); + + // This mouse area serves to raise the window. To function, it must live + // in the window and have a higher Z-order than the content, but follow + // the position and size of frame decoration + property var activator: MouseArea { + width: frame.decoration.width + height: frame.decoration.height + x: frame.decoration.anchors.leftMargin + y: frame.decoration.anchors.topMargin + propagateComposedEvents: true + acceptedButtons: Qt.AllButtons + enabled: window.visible + onPressed: { + //console.log("Pressed on activator area"); + window.raise(); + mouse.accepted = false; + } + } + + // This mouse area serves to swallow mouse events while the mouse is over the window + // to prevent things like mouse wheel events from reaching the application and changing + // the camera if the user is scrolling through a list and gets to the end. + property var swallower: MouseArea { + width: frame.decoration.width + height: frame.decoration.height + x: frame.decoration.anchors.leftMargin + y: frame.decoration.anchors.topMargin + hoverEnabled: true + acceptedButtons: Qt.AllButtons + enabled: window.visible + onClicked: {} + onDoubleClicked: {} + onPressAndHold: {} + onReleased: {} + onWheel: {} + } + + // Default to a standard frame. Can be overriden to provide custom + // frame styles, like a full desktop frame to simulate a modal window + property var frame: DefaultFrame { } + + // Scrollable window content. + property var pane: Item { + property bool isScrolling: scrollView.height < scrollView.contentItem.height + property int contentWidth: scrollView.width - (isScrolling ? 11 : 0) + + anchors.fill: parent + anchors.rightMargin: isScrolling ? 11 : 0 + + Rectangle { + id: contentBackground + anchors.fill: parent + anchors.rightMargin: parent.isScrolling ? 11 : 0 + color: hifi.colors.baseGray + visible: modality != Qt.ApplicationModal + } + + LinearGradient { + // FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX. + // Check again when have a later version of QtQuick. + visible: modality != Qt.ApplicationModal && Qt.platform.os != "osx" + anchors.top: contentBackground.bottom + anchors.left: contentBackground.left + width: contentBackground.width - 1 + height: 4 + start: Qt.point(0, 0) + end: Qt.point(0, 4) + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.darkGray } + GradientStop { position: 1.0; color: hifi.colors.darkGray0 } + } + cached: true + } + + ScrollView { + id: scrollView + contentItem: content + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + anchors.fill: parent + anchors.rightMargin: parent.isScrolling ? 1 : 0 + + style: ScrollViewStyle { + + padding.right: -7 // Move to right away from content. + + handle: Item { + implicitWidth: 8 + Rectangle { + radius: 4 + color: hifi.colors.white30 + anchors { + fill: parent + leftMargin: 2 // Finesse size and position. + topMargin: 1 + bottomMargin: 1 + } + } + } + + scrollBarBackground: Item { + implicitWidth: 10 + Rectangle { + color: hifi.colors.darkGray30 + radius: 4 + anchors { + fill: parent + topMargin: -1 // Finesse size + bottomMargin: -2 + } + } + } + + incrementControl: Item { + visible: false + } + + decrementControl: Item { + visible: false + } + } + } + } + children: [ swallower, frame, pane, activator ] + + Component.onCompleted: { raise(); setDefaultFocus(); } + Component.onDestruction: windowDestroyed(); + onParentChanged: raise(); + + onVisibleChanged: { + if (!visible && destroyOnInvisible) { + destroy(); + return; + } + if (visible) { + raise(); + } + enabled = visible + } + + function raise() { + if (visible && parent) { + desktop.raise(window) + } + } + + function pin() { +// pinned = ! pinned + } + + // our close function performs the same way as the OffscreenUI class: + // don't do anything but manipulate the targetVisible flag and let the other + // mechanisms decide if the window should be destroyed after the close + // animation completes + // FIXME using this close function messes up the visibility signals received by the + // type and it's derived types +// function close() { +// console.log("Closing " + window) +// if (destroyOnCloseButton) { +// destroyOnInvisible = true +// } +// visible = false; +// } + + function framedRect() { + if (!frame || !frame.decoration) { + return Qt.rect(0, 0, window.width, window.height) + } + return Qt.rect(frame.decoration.anchors.leftMargin, frame.decoration.anchors.topMargin, + window.width - frame.decoration.anchors.leftMargin - frame.decoration.anchors.rightMargin, + window.height - frame.decoration.anchors.topMargin - frame.decoration.anchors.bottomMargin) + } + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_Control: + case Qt.Key_Shift: + case Qt.Key_Meta: + case Qt.Key_Alt: + break; + + case Qt.Key_W: + if (window.closable && (event.modifiers === Qt.ControlModifier)) { + visible = false + event.accepted = true + } + // fall through + + default: + // Consume unmodified keyboard entries while the window is focused, to prevent them + // from propagating to the application + if (event.modifiers === Qt.NoModifier) { + event.accepted = true; + } + break; + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64cd586e13..7314bce0bd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1198,7 +1198,8 @@ void Application::initializeUi() { // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // support the window management and scripting proxies for VR use offscreenUi->createDesktop(QString("hifi/Desktop.qml")); - + connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); + // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); @@ -1259,6 +1260,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); rootContext->setContextProperty("Reticle", _compositor.getReticleInterface()); + rootContext->setContextProperty("ApplicationCompositor", &_compositor); + _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { QPointF result = pt; @@ -3772,8 +3775,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // The pending changes collecting the changes here render::PendingChanges pendingChanges; + // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities // Background rendering decision - if (BackgroundRenderData::_item == 0) { + if (!render::Item::isValidID(BackgroundRenderData::_item)) { auto backgroundRenderData = make_shared(); auto backgroundRenderPayload = make_shared(backgroundRenderData); BackgroundRenderData::_item = _main3DScene->allocateID(); @@ -3798,8 +3802,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } } + // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities // Make sure the WorldBox is in the scene - if (WorldBoxRenderData::_item == 0) { + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { auto worldBoxRenderData = make_shared(); auto worldBoxRenderPayload = make_shared(worldBoxRenderData); @@ -3814,9 +3819,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } // Setup the current Zone Entity lighting and skybox - // Fixme: We need a better soution through an actual render item !!! { - DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight()); + // FIXME: Use a zone setting to determine the ambient light mode + DependencyManager::get()->setAmbientLightMode(-1); auto skyStage = DependencyManager::get()->getSkyStage(); DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); @@ -4307,7 +4312,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { case FSTReader::HEAD_AND_BODY_MODEL: ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar", "Would you like to use '" + modelName + "' for your avatar?", - QMessageBox::Ok | QMessageBox::Cancel); + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); break; default: @@ -4627,34 +4632,6 @@ float Application::getRenderResolutionScale() const { } } -int Application::getRenderAmbientLight() const { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLightGlobal)) { - return -1; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight0)) { - return 0; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight1)) { - return 1; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight2)) { - return 2; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight3)) { - return 3; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight4)) { - return 4; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight5)) { - return 5; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight6)) { - return 6; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight7)) { - return 7; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight8)) { - return 8; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight9)) { - return 9; - } else { - return -1; - } -} - void Application::notifyPacketVersionMismatch() { if (!_notifiedPacketVersionMismatchThisDomain) { _notifiedPacketVersionMismatchThisDomain = true; @@ -5036,27 +5013,11 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de // controller pose is in Avatar frame. glm::vec3 position = pose.getTranslation(); glm::quat rotation = pose.getRotation(); + glm::vec3 rawVelocity = pose.getVelocity(); + glm::vec3 angularVelocity = pose.getAngularVelocity(); - // Compute current velocity from position change - glm::vec3 rawVelocity; - if (deltaTime > 0.0f) { - rawVelocity = (position - palm.getRawPosition()) / deltaTime; - } else { - rawVelocity = glm::vec3(0.0f); - } - palm.setRawVelocity(rawVelocity); // meters/sec - - // Angular Velocity of Palm - glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); - glm::vec3 angularVelocity(0.0f); - float rotationAngle = glm::angle(deltaRotation); - if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { - angularVelocity = glm::normalize(glm::axis(deltaRotation)); - angularVelocity *= (rotationAngle / deltaTime); - palm.setRawAngularVelocity(angularVelocity); - } else { - palm.setRawAngularVelocity(glm::vec3(0.0f)); - } + palm.setRawVelocity(rawVelocity); + palm.setRawAngularVelocity(angularVelocity); if (controller::InputDevice::getLowVelocityFilter()) { // Use a velocity sensitive filter to damp small motions and preserve large ones with @@ -5128,3 +5089,9 @@ void Application::readArgumentsFromLocalSocket() { qApp->openUrl(QString::fromUtf8(message)); } } + +void Application::showDesktop() { + if (!_overlayConductor.getEnabled()) { + _overlayConductor.setEnabled(true); + } +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 96bafce23f..51b90bcb99 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -280,6 +280,7 @@ public slots: void runTests(); private slots: + void showDesktop(); void clearDomainOctreeDetails(); void idle(uint64_t now); void aboutToQuit(); @@ -351,7 +352,6 @@ private: void checkSkeleton(); void initializeAcceptedFiles(); - int getRenderAmbientLight() const; void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 48afdb4622..9c7788851c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -312,12 +312,6 @@ Menu::Menu() { DependencyManager::get()->toggle(QString("hifi/dialogs/AudioPreferencesDialog.qml"), "AudioPreferencesDialog"); }); - // Settings > Graphics... - action = addActionToQMenuAndActionHash(settingsMenu, "Graphics..."); - connect(action, &QAction::triggered, [] { - DependencyManager::get()->toggle(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"), "GraphicsPreferencesDialog"); - }); - // Settings > LOD...-- FIXME: needs implementation action = addActionToQMenuAndActionHash(settingsMenu, "LOD..."); connect(action, &QAction::triggered, [] { @@ -345,27 +339,17 @@ Menu::Menu() { // Developer menu ---------------------------------- MenuWrapper* developerMenu = addMenu("Developer", "Developer"); + // Developer > Graphics... + action = addActionToQMenuAndActionHash(developerMenu, "Graphics..."); + connect(action, &QAction::triggered, [] { + DependencyManager::get()->toggle(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"), "GraphicsPreferencesDialog"); + }); + // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true); - // Developer > Render > Ambient Light - MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); - QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); - ambientLightGroup->setExclusive(true); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight0, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight1, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight2, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight3, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight4, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight5, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight6, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - // Developer > Render > Throttle FPS If Not Focus addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 51a1542490..44a24ee895 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -266,18 +266,6 @@ namespace MenuOption { const QString RenderResolutionHalf = "1/2"; const QString RenderResolutionThird = "1/3"; const QString RenderResolutionQuarter = "1/4"; - const QString RenderAmbientLight = "Ambient Light"; - const QString RenderAmbientLightGlobal = "Global"; - const QString RenderAmbientLight0 = "OLD_TOWN_SQUARE"; - const QString RenderAmbientLight1 = "GRACE_CATHEDRAL"; - const QString RenderAmbientLight2 = "EUCALYPTUS_GROVE"; - const QString RenderAmbientLight3 = "ST_PETERS_BASILICA"; - const QString RenderAmbientLight4 = "UFFIZI_GALLERY"; - const QString RenderAmbientLight5 = "GALILEOS_TOMB"; - const QString RenderAmbientLight6 = "VINE_STREET_KITCHEN"; - const QString RenderAmbientLight7 = "BREEZEWAY"; - const QString RenderAmbientLight8 = "CAMPUS_SUNSET"; - const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5a92f98fde..4cbfc2af17 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -315,6 +315,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemID); + render::Item::clearID(_renderItemID); _skeletonModel.removeFromScene(scene, pendingChanges); getHead()->getFaceModel().removeFromScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { @@ -323,7 +324,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr>(_renderItemID, [](render::Payload& p) {}); } } @@ -930,35 +931,6 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } - - // AJT: TODO REMOVE - /* - // make sure we have as many models as attachments - while (_attachmentModels.size() < attachmentData.size()) { - Model* model = nullptr; - if (_unusedAttachments.size() > 0) { - model = _unusedAttachments.takeFirst(); - } else { - model = new Model(std::make_shared(), this); - } - model->init(); - _attachmentModels.append(model); - } - while (_attachmentModels.size() > attachmentData.size()) { - auto attachmentModel = _attachmentModels.back(); - _attachmentModels.pop_back(); - _attachmentsToRemove.push_back(attachmentModel); - } - */ - - /* - // update the urls - for (int i = 0; i < attachmentData.size(); i++) { - _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); - _attachmentModels[i]->setSnapModelToCenter(true); - _attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale); - } - */ } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 71bd1b1f82..87990b941e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -93,7 +93,8 @@ void AvatarActionHold::prepareForPhysicsSimulation() { activateBody(true); } -std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) { +std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position, + glm::vec3& linearVelocity, glm::vec3& angularVelocity) { auto avatarManager = DependencyManager::get(); auto holdingAvatar = std::static_pointer_cast(avatarManager->getAvatarBySessionID(_holderID)); @@ -103,19 +104,22 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve withReadLock([&]{ bool isRightHand = (_hand == "right"); - glm::vec3 palmPosition { Vectors::ZERO }; - glm::quat palmRotation { Quaternions::IDENTITY }; + glm::vec3 palmPosition; + glm::quat palmRotation; + glm::vec3 palmLinearVelocity; + glm::vec3 palmAngularVelocity; + + PalmData palmData = holdingAvatar->getHand()->getCopyOfPalmData(isRightHand ? HandData::RightHand : HandData::LeftHand); + + // TODO: adjust according to _relativePosition and _relativeRotation? + linearVelocity = palmData.getVelocity(); + angularVelocity = palmData.getAngularVelocity(); if (_ignoreIK && holdingAvatar->isMyAvatar()) { // We cannot ignore other avatars IK and this is not the point of this option // This is meant to make the grabbing behavior more reactive. - if (isRightHand) { - palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getPosition(); - palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::RightHand).getRotation(); - } else { - palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition(); - palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation(); - } + palmPosition = palmData.getPosition(); + palmRotation = palmData.getRotation(); } else if (holdingAvatar->isMyAvatar()) { glm::vec3 avatarRigidBodyPosition; glm::quat avatarRigidBodyRotation; @@ -153,14 +157,19 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve rotation = palmRotation * _relativeRotation; position = palmPosition + rotation * _relativePosition; + + // update linearVelocity based on offset via _relativePosition; + linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); }); return holdingAvatar; } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - glm::quat rotation { Quaternions::IDENTITY }; - glm::vec3 position { Vectors::ZERO }; + glm::quat rotation; + glm::vec3 position; + glm::vec3 linearVelocity; + glm::vec3 angularVelocity; bool valid = false; int holdCount = 0; @@ -173,7 +182,8 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { std::shared_ptr holdAction = std::static_pointer_cast(action); glm::quat rotationForAction; glm::vec3 positionForAction; - std::shared_ptr holdingAvatar = holdAction->getTarget(rotationForAction, positionForAction); + glm::vec3 linearVelocityForAction, angularVelocityForAction; + std::shared_ptr holdingAvatar = holdAction->getTarget(rotationForAction, positionForAction, linearVelocityForAction, angularVelocityForAction); if (holdingAvatar) { holdCount ++; if (holdAction.get() == this) { @@ -183,15 +193,21 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { } position += positionForAction; + linearVelocity += linearVelocityForAction; + angularVelocity += angularVelocityForAction; } } if (valid && holdCount > 0) { position /= holdCount; + linearVelocity /= holdCount; + angularVelocity /= holdCount; withWriteLock([&]{ _positionalTarget = position; _rotationalTarget = rotation; + _linearVelocityTarget = linearVelocity; + _angularVelocityTarget = angularVelocity; _positionalTargetSet = true; _rotationalTargetSet = true; _active = true; @@ -225,15 +241,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { withWriteLock([&]{ if (_kinematicSetVelocity) { - if (_previousSet) { - // smooth velocity over 2 frames - glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget; - glm::vec3 positionalVelocity = - (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); - rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); - _previousPositionalDelta = positionalDelta; - _previousDeltaTimeStep = deltaTimeStep; - } + rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); + rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget)); } btTransform worldTrans = rigidBody->getWorldTransform(); diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 7646f87238..1c3a4386bc 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -36,7 +36,8 @@ public: virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); } bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); - std::shared_ptr getTarget(glm::quat& rotation, glm::vec3& position); + std::shared_ptr getTarget(glm::quat& rotation, glm::vec3& position, + glm::vec3& linearVelocity, glm::vec3& angularVelocity); virtual void prepareForPhysicsSimulation() override; @@ -50,6 +51,9 @@ private: QString _hand { "right" }; QUuid _holderID; + glm::vec3 _linearVelocityTarget; + glm::vec3 _angularVelocityTarget; + bool _kinematic { false }; bool _kinematicSetVelocity { false }; bool _previousSet { false }; @@ -57,7 +61,6 @@ private: glm::vec3 _previousPositionalTarget; glm::quat _previousRotationalTarget; - float _previousDeltaTimeStep = 0.0f; glm::vec3 _previousPositionalDelta; glm::vec3 _palmOffsetFromRigidBody; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 56584df44e..6318a9bb1e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,8 +123,11 @@ MyAvatar::MyAvatar(RigPointer rig) : } // connect to AddressManager signal for location jumps - connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, - this, &MyAvatar::goToLocation); + connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, + [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){ + goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation); + }); + _characterController.setEnabled(true); _bodySensorMatrix = deriveBodyFromHMDSensor(); @@ -1666,6 +1669,41 @@ void MyAvatar::resetSize() { qCDebug(interfaceapp, "Reset scale to %f", (double)_targetScale); } + +void MyAvatar::goToLocation(const QVariant& propertiesVar) { + qCDebug(interfaceapp, "MyAvatar QML goToLocation"); + auto properties = propertiesVar.toMap(); + if (!properties.contains("position")) { + qCWarning(interfaceapp, "goToLocation called without a position variable"); + return; + } + + bool validPosition; + glm::vec3 v = vec3FromVariant(properties["position"], validPosition); + if (!validPosition) { + qCWarning(interfaceapp, "goToLocation called with invalid position variable"); + return; + } + bool validOrientation = false; + glm::quat q; + if (properties.contains("orientation")) { + q = quatFromVariant(properties["orientation"], validOrientation); + if (!validOrientation) { + glm::vec3 eulerOrientation = vec3FromVariant(properties["orientation"], validOrientation); + q = glm::quat(eulerOrientation); + if (!validOrientation) { + qCWarning(interfaceapp, "goToLocation called with invalid orientation variable"); + } + } + } + + if (validOrientation) { + goToLocation(v, true, q); + } else { + goToLocation(v); + } +} + void MyAvatar::goToLocation(const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation) { @@ -1873,7 +1911,7 @@ bool MyAvatar::FollowHelper::shouldActivate(const MyAvatar& myAvatar, const glm: const float CYLINDER_RADIUS = 0.15f; glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); - glm::vec3 radialOffset(offset.x, 0.0f, offset.y); + glm::vec3 radialOffset(offset.x, 0.0f, offset.z); float radialDistance = glm::length(radialOffset); return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM) || (radialDistance > CYLINDER_RADIUS); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8c84299e08..637baa3c22 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -253,6 +253,7 @@ public slots: void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); + void goToLocation(const QVariant& properties); // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index a2fe95d649..5c6e4c9819 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -283,16 +283,43 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int //Mouse Pointer if (getReticleVisible()) { - glm::mat4 overlayXfm; - _modelTransform.getMatrix(overlayXfm); + if (getReticleDepth() != 1.0f) { + // calculate the "apparent location" based on the depth and the current ray + glm::vec3 origin, direction; + auto reticlePosition = getReticlePosition(); + computeHmdPickRay(reticlePosition, origin, direction); + auto apparentPosition = origin + (direction * getReticleDepth()); - auto reticlePosition = getReticlePosition(); - glm::vec2 projection = overlayToSpherical(reticlePosition); - float cursorDepth = getReticleDepth(); - mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); + // same code as used to render for apparent location + auto myCamera = qApp->getCamera(); + mat4 cameraMat = myCamera->getTransform(); + auto UITransform = cameraMat * glm::inverse(headPose); + auto relativePosition4 = glm::inverse(UITransform) * vec4(apparentPosition, 1); + auto relativePosition = vec3(relativePosition4) / relativePosition4.w; + auto relativeDistance = glm::length(relativePosition); + + // look at borrowed from overlays + float elevation = -asinf(relativePosition.y / glm::length(relativePosition)); + float azimuth = atan2f(relativePosition.x, relativePosition.z); + glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, 0, -1)); // this extra *quat(vec3(0,0,-1)) was required to get the quad to flip this seems like we could optimize + + Transform transform; + transform.setTranslation(relativePosition); + transform.setScale(reticleScale); + transform.postScale(relativeDistance); // scale not quite working, distant things too large + transform.setRotation(faceCamera); + batch.setModelTransform(transform); + } else { + glm::mat4 overlayXfm; + _modelTransform.getMatrix(overlayXfm); + + auto reticlePosition = getReticlePosition(); + glm::vec2 projection = overlayToSpherical(reticlePosition); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + } geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); } }); @@ -300,7 +327,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y); } return event->localPos(); @@ -354,7 +381,7 @@ bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { // If we're in HMD mode if (shouldCaptureMouse()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); auto newPosition = QCursor::pos(); auto changeInRealMouse = newPosition - _lastKnownRealMouse; auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse); @@ -368,9 +395,9 @@ bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { return false; // let the caller know to process the event } -glm::vec2 ApplicationCompositor::getReticlePosition() { +glm::vec2 ApplicationCompositor::getReticlePosition() const { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); return _reticlePositionInHMD; } return toGlm(QCursor::pos()); @@ -378,7 +405,7 @@ glm::vec2 ApplicationCompositor::getReticlePosition() { void ApplicationCompositor::setReticlePosition(glm::vec2 position, bool sendFakeEvent) { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); const float MOUSE_EXTENTS_VERT_ANGULAR_SIZE = 170.0f; // 5deg from poles const float MOUSE_EXTENTS_VERT_PIXELS = VIRTUAL_SCREEN_SIZE_Y * (MOUSE_EXTENTS_VERT_ANGULAR_SIZE / DEFAULT_HMD_UI_VERT_ANGULAR_SIZE); const float MOUSE_EXTENTS_HORZ_ANGULAR_SIZE = 360.0f; // full sphere diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 32a30c6fc6..7bdb97727f 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -29,6 +29,7 @@ class RenderArgs; class ReticleInterface; +const float DEFAULT_RETICLE_DEPTH = 1.0f; // FIXME - probably should be based on UI radius const float MAGNIFY_WIDTH = 220.0f; const float MAGNIFY_HEIGHT = 100.0f; @@ -46,6 +47,7 @@ class ApplicationCompositor : public QObject { Q_OBJECT Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha) + Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop) public: ApplicationCompositor(); ~ApplicationCompositor(); @@ -83,13 +85,14 @@ public: float getAlpha() const { return _alpha; } void setAlpha(float alpha) { _alpha = alpha; } - bool getReticleVisible() { return _reticleVisible; } + bool getReticleVisible() const { return _reticleVisible; } void setReticleVisible(bool visible) { _reticleVisible = visible; } - float getReticleDepth() { return _reticleDepth; } + float getReticleDepth() const { return _reticleDepth; } void setReticleDepth(float depth) { _reticleDepth = depth; } + void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; } - glm::vec2 getReticlePosition(); + glm::vec2 getReticlePosition() const; void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true); glm::vec2 getReticleMaximumPosition() const; @@ -103,7 +106,12 @@ public: bool shouldCaptureMouse() const; + /// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false + bool getReticleOverDesktop() const { return _isOverDesktop; } + void setReticleOverDesktop(bool value) { _isOverDesktop = value; } + private: + bool _isOverDesktop { true }; void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); @@ -147,11 +155,13 @@ private: // application specific position, when it's in desktop mode, the reticle position will simply move // the system mouse. glm::vec2 _reticlePositionInHMD { 0.0f, 0.0f }; - mutable QMutex _reticlePositionInHMDLock{ QMutex::Recursive }; + mutable QMutex _reticleLock { QMutex::Recursive }; QPointF _lastKnownRealMouse; bool _ignoreMouseMove { false }; + bool _reticleOverQml { false }; + ReticleInterface* _reticleInterface; }; @@ -163,11 +173,13 @@ class ReticleInterface : public QObject { Q_PROPERTY(float depth READ getDepth WRITE setDepth) Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition) Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) + Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) public: ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {} Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); } + Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); } Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); } Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); } @@ -179,6 +191,7 @@ public: Q_INVOKABLE void setPosition(glm::vec2 position) { _compositor->setReticlePosition(position); } Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + private: ApplicationCompositor* _compositor; }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 93bacdd402..78c2405ade 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -330,15 +330,6 @@ void setupPreferences() { preferences->addPreference(preference); } - auto antialiasingConfig = renderConfig->getConfig(); - { - auto getter = [antialiasingConfig]()->QString { return antialiasingConfig->getPreset(); }; - auto setter = [antialiasingConfig](QString preset) { antialiasingConfig->setPreset(preset); }; - auto preference = new ComboBoxPreference(RENDER, "Antialiasing", getter, setter); - preference->setItems(antialiasingConfig->getPresetList()); - preferences->addPreference(preference); - } - auto shadowConfig = renderConfig->getConfig(); { auto getter = [shadowConfig]()->QString { return shadowConfig->getPreset(); }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index c9c4c68dc9..e566f513ac 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -342,22 +342,14 @@ void Stats::setRenderDetails(const RenderDetails& details) { STAT_UPDATE(triangles, details._trianglesRendered); STAT_UPDATE(materialSwitches, details._materialSwitches); if (_expanded) { - STAT_UPDATE(opaqueConsidered, (int)details._opaque._considered); - STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView); - STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall); - STAT_UPDATE(opaqueRendered, (int)details._opaque._rendered); - STAT_UPDATE(shadowConsidered, (int)details._shadow._considered); + STAT_UPDATE(itemConsidered, details._item._considered); + STAT_UPDATE(itemOutOfView, details._item._outOfView); + STAT_UPDATE(itemTooSmall, details._item._tooSmall); + STAT_UPDATE(itemRendered, details._item._rendered); + STAT_UPDATE(shadowConsidered, details._shadow._considered); STAT_UPDATE(shadowOutOfView, details._shadow._outOfView); STAT_UPDATE(shadowTooSmall, details._shadow._tooSmall); - STAT_UPDATE(shadowRendered, (int)details._shadow._rendered); - STAT_UPDATE(translucentConsidered, (int)details._translucent._considered); - STAT_UPDATE(translucentOutOfView, details._translucent._outOfView); - STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall); - STAT_UPDATE(translucentRendered, (int)details._translucent._rendered); - STAT_UPDATE(otherConsidered, (int)details._other._considered); - STAT_UPDATE(otherOutOfView, details._other._outOfView); - STAT_UPDATE(otherTooSmall, details._other._tooSmall); - STAT_UPDATE(otherRendered, (int)details._other._rendered); + STAT_UPDATE(shadowRendered, details._shadow._rendered); } } diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index ba3c8f2391..3476087852 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -59,22 +59,14 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, triangles, 0) STATS_PROPERTY(int, quads, 0) STATS_PROPERTY(int, materialSwitches, 0) - STATS_PROPERTY(int, opaqueConsidered, 0) - STATS_PROPERTY(int, opaqueOutOfView, 0) - STATS_PROPERTY(int, opaqueTooSmall, 0) - STATS_PROPERTY(int, opaqueRendered, 0) + STATS_PROPERTY(int, itemConsidered, 0) + STATS_PROPERTY(int, itemOutOfView, 0) + STATS_PROPERTY(int, itemTooSmall, 0) + STATS_PROPERTY(int, itemRendered, 0) STATS_PROPERTY(int, shadowConsidered, 0) STATS_PROPERTY(int, shadowOutOfView, 0) STATS_PROPERTY(int, shadowTooSmall, 0) STATS_PROPERTY(int, shadowRendered, 0) - STATS_PROPERTY(int, translucentConsidered, 0) - STATS_PROPERTY(int, translucentOutOfView, 0) - STATS_PROPERTY(int, translucentTooSmall, 0) - STATS_PROPERTY(int, translucentRendered, 0) - STATS_PROPERTY(int, otherConsidered, 0) - STATS_PROPERTY(int, otherOutOfView, 0) - STATS_PROPERTY(int, otherTooSmall, 0) - STATS_PROPERTY(int, otherRendered, 0) STATS_PROPERTY(QString, sendingMode, QString()) STATS_PROPERTY(QString, packetStats, QString()) STATS_PROPERTY(QString, lodStatus, QString()) @@ -145,22 +137,14 @@ signals: void trianglesChanged(); void quadsChanged(); void materialSwitchesChanged(); - void opaqueConsideredChanged(); - void opaqueOutOfViewChanged(); - void opaqueTooSmallChanged(); - void opaqueRenderedChanged(); + void itemConsideredChanged(); + void itemOutOfViewChanged(); + void itemTooSmallChanged(); + void itemRenderedChanged(); void shadowConsideredChanged(); void shadowOutOfViewChanged(); void shadowTooSmallChanged(); void shadowRenderedChanged(); - void translucentConsideredChanged(); - void translucentOutOfViewChanged(); - void translucentTooSmallChanged(); - void translucentRenderedChanged(); - void otherConsideredChanged(); - void otherOutOfViewChanged(); - void otherTooSmallChanged(); - void otherRenderedChanged(); void sendingModeChanged(); void packetStatsChanged(); void lodStatusChanged(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 3738f829f3..a06a9b0605 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -45,7 +45,7 @@ void ModelOverlay::update(float deltatime) { _updateModel = false; _model.setSnapModelToCenter(true); - _model.setScale(getScale()); + _model.setScale(getDimensions()); _model.setRotation(getRotation()); _model.setTranslation(getPosition()); _model.setURL(_url); @@ -82,22 +82,6 @@ void ModelOverlay::render(RenderArgs* args) { if (!_visible) { return; } - - /* - if (_model.isActive()) { - if (_model.isRenderable()) { - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - _model.render(args, getAlpha()); - if (glower) { - delete glower; - } - } - } - */ } void ModelOverlay::setProperties(const QScriptValue &properties) { @@ -107,10 +91,19 @@ void ModelOverlay::setProperties(const QScriptValue &properties) { Volume3DOverlay::setProperties(properties); - if (position != getPosition() || rotation != getRotation() || scale != getDimensions()) { - _model.setScaleToFit(true, getScale()); + if (position != getPosition() || rotation != getRotation()) { _updateModel = true; } + + if (scale != getDimensions()) { + auto newScale = getDimensions(); + if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) { + setDimensions(scale); + } else { + _model.setScaleToFit(true, getDimensions()); + _updateModel = true; + } + } QScriptValue urlValue = properties.property("url"); if (urlValue.isValid() && urlValue.isString()) { @@ -140,7 +133,7 @@ QScriptValue ModelOverlay::getProperty(const QString& property) { if (property == "url") { return _url.toString(); } - if (property == "dimensions") { + if (property == "dimensions" || property == "scale" || property == "size") { return vec3toScriptValue(_scriptEngine, _model.getScaleToFitDimensions()); } if (property == "textures") { diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 1e3390e49f..64b5e2aedf 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -229,6 +229,6 @@ bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemID); - _renderItemID = render::Item::INVALID_ITEM_ID; + render::Item::clearID(_renderItemID); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b53a5a0d05..2bc1286419 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -94,7 +94,7 @@ void Overlays::cleanupOverlaysToDelete() { Overlay::Pointer overlay = _overlaysToDelete.takeLast(); auto itemID = overlay->getRenderItemID(); - if (itemID != render::Item::INVALID_ITEM_ID) { + if (render::Item::isValidID(itemID)) { overlay->removeFromScene(overlay, scene, pendingChanges); } } while (!_overlaysToDelete.isEmpty()); @@ -241,7 +241,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { render::ItemKey itemKey = render::payloadGetKey(thisOverlay); if (itemKey != oldItemKey) { auto itemID = thisOverlay->getRenderItemID(); - if (itemID != render::Item::INVALID_ITEM_ID) { + if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; pendingChanges.resortItem(itemID, oldItemKey, itemKey); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 157e973f5b..ad6b436d02 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -104,7 +104,9 @@ void AudioInjector::restart() { // reset state to start sending from beginning again _nextFrame = 0; - _frameTimer->invalidate(); + if (_frameTimer) { + _frameTimer->invalidate(); + } _hasSentFirstFrame = false; // check our state to decide if we need extra handling for the restart request @@ -122,7 +124,9 @@ void AudioInjector::restart() { injectLocally(); } else { // wake the AudioInjectorManager back up if it's stuck waiting - injectorManager->restartFinishedInjector(this); + if (!injectorManager->restartFinishedInjector(this)) { + _state = State::Finished; // we're not playing, so reset the state used by isPlaying. + } } } } diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 032ad71145..b39da1c148 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -129,6 +129,15 @@ void AudioInjectorManager::run() { static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInjector time to send frame, with sufficient padding +bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a lock. + if (_injectors.size() >= MAX_INJECTORS_PER_THREAD) { + qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" + << MAX_INJECTORS_PER_THREAD << "current audio injectors."; + return true; + } + return false; +} + bool AudioInjectorManager::threadInjector(AudioInjector* injector) { if (_shouldStop) { qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; @@ -138,8 +147,9 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { // guard the injectors vector with a mutex Lock lock(_injectorsMutex); - // check if we'll be able to thread this injector (do we have < max concurrent injectors) - if (_injectors.size() < MAX_INJECTORS_PER_THREAD) { + if (wouldExceedLimits()) { + return false; + } else { if (!_thread) { createThread(); } @@ -156,23 +166,22 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { _injectorReady.notify_one(); return true; - } else { - // unable to thread this injector, at the max - qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" - << MAX_INJECTORS_PER_THREAD << "current audio injectors."; - return false; } } -void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { +bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { if (!_shouldStop) { // guard the injectors vector with a mutex Lock lock(_injectorsMutex); + if (wouldExceedLimits()) { + return false; + } // add the injector to the queue with a send timestamp of now _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); // notify our wait condition so we can inject two frames for this injector immediately _injectorReady.notify_one(); } + return true; } diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 91648fff39..de5537856e 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -50,8 +50,9 @@ private: using Lock = std::unique_lock; bool threadInjector(AudioInjector* injector); - void restartFinishedInjector(AudioInjector* injector); + bool restartFinishedInjector(AudioInjector* injector); void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } + bool wouldExceedLimits(); AudioInjectorManager() {}; AudioInjectorManager(const AudioInjectorManager&) = delete; diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 34ed610f80..63af43e399 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -93,6 +93,7 @@ public: PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand); glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } + glm::vec3 getAngularVelocity() const { return _owningHandData->localToWorldDirection(_rawAngularVelocity); } const glm::vec3& getRawPosition() const { return _rawPosition; } bool isActive() const { return _isActive; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 7b3ea8edaa..30d3f9d83c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -66,10 +66,11 @@ public: void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myItem); + render::Item::clearID(_myItem); } void notifyChanged() { - if (_myItem == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_myItem)) { return; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 89f819b0da..e3870705c9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -251,6 +251,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myMetaItem); + render::Item::clearID(_myMetaItem); if (_model) { _model->removeFromScene(scene, pendingChanges); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index e62dc82988..4d8eebf05b 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -179,6 +179,7 @@ void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemId); _scene = nullptr; + render::Item::clearID(_renderItemId); }; void RenderableParticleEffectEntityItem::update(const quint64& now) { @@ -199,7 +200,8 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) { } void RenderableParticleEffectEntityItem::updateRenderItem() { - if (!_scene) { + // this 2 tests are synonyms for this class, but we would like to get rid of the _scene pointer ultimately + if (!_scene || !render::Item::isValidID(_renderItemId)) { return; } if (!getVisible()) { @@ -312,7 +314,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { } void RenderableParticleEffectEntityItem::notifyBoundChanged() { - if (_renderItemId == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_renderItemId)) { return; } render::PendingChanges pendingChanges; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1029f3b3af..ef777df403 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -572,6 +572,7 @@ void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myItem); + render::Item::clearID(_myItem); } namespace render { diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 56771296db..44345ac0a2 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -230,6 +230,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myMetaItem); + render::Item::clearID(_myMetaItem); if (_model) { _model->removeFromScene(scene, pendingChanges); } @@ -237,7 +238,7 @@ void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shar void RenderableZoneEntityItem::notifyBoundChanged() { - if (_myMetaItem == render::Item::INVALID_ITEM_ID) { + if (!render::Item::isValidID(_myMetaItem)) { return; } render::PendingChanges pendingChanges; diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 4d819d389f..bbd7a1daa7 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -44,11 +44,12 @@ out vec4 varColor; out vec2 varTexcoord; const int NUM_VERTICES_PER_PARTICLE = 4; +// This ordering ensures that un-rotated particles render upright in the wiewer. const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE]( - vec4(-1.0, -1.0, 0.0, 0.0), - vec4(1.0, -1.0, 0.0, 0.0), vec4(-1.0, 1.0, 0.0, 0.0), - vec4(1.0, 1.0, 0.0, 0.0) + vec4(-1.0, -1.0, 0.0, 0.0), + vec4(1.0, 1.0, 0.0, 0.0), + vec4(1.0, -1.0, 0.0, 0.0) ); float bezierInterpolate(float y1, float y2, float y3, float u) { @@ -115,7 +116,8 @@ void main(void) { float seed = inColor.y; // Pass the texcoord and the z texcoord is representing the texture icon - varTexcoord = vec2((UNIT_QUAD[twoTriID].xy + 1.0) * 0.5); + // Offset for corrected vertex ordering. + varTexcoord = vec2((UNIT_QUAD[twoTriID].xy -1.0) * vec2(0.5, -0.5)); varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age); // anchor point in eye space diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 11164afbbb..ff33246b3f 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -260,10 +260,22 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest netRequest(url); QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest); + if (!qApp) { + return netReply; + } QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); // Nothing is going to happen on this whole run thread until we get this - netReply->waitForReadyRead(-1); // so we might as well block this thread waiting for the response, rather than + + bool aboutToQuit { false }; + auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] { + aboutToQuit = true; + }); + static const int WAIT_TIMEOUT_MS = 500; + while (qApp && !aboutToQuit && !netReply->isReadable()) { + netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than + } + QObject::disconnect(connection); return netReply; // trying to sync later on. } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index e22feb694d..6ec162585b 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -315,6 +315,8 @@ OffscreenQmlSurface::OffscreenQmlSurface() { } OffscreenQmlSurface::~OffscreenQmlSurface() { + QObject::disconnect(&_updateTimer); + QObject::disconnect(qApp); _renderer->stop(); delete _rootItem; delete _renderer; @@ -322,6 +324,10 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { delete _qmlEngine; } +void OffscreenQmlSurface::onAboutToQuit() { + QObject::disconnect(&_updateTimer); +} + void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { _renderer = new OffscreenQmlRenderer(this, shareContext); _renderer->_renderControl->_renderWindow = _proxyWindow; @@ -334,12 +340,9 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. _updateTimer.setInterval(MIN_TIMER_MS); - connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); - QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this]{ - disconnect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); - }); + QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); + QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); _updateTimer.start(); - _qmlComponent = new QQmlComponent(_qmlEngine); _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 5641d16a30..9142e7f2ef 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -74,6 +74,7 @@ signals: public slots: void requestUpdate(); void requestRender(); + void onAboutToQuit(); private: QObject* finishQmlLoad(std::function f); diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 57ada84607..4527094322 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -21,6 +21,17 @@ class NLPacket : public udt::Packet { Q_OBJECT public: + + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Packet Type | Packet Version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Node UUID | Hash (only if verified) | Optional (only if sourced) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // NLPacket Header Format + // this is used by the Octree classes - must be known at compile time static const int MAX_PACKET_HEADER_SIZE = sizeof(udt::Packet::SequenceNumberAndBitField) + sizeof(udt::Packet::MessageNumberAndBitField) + diff --git a/libraries/networking/src/udt/Constants.h b/libraries/networking/src/udt/Constants.h index 9f5f1db883..98569ead5a 100644 --- a/libraries/networking/src/udt/Constants.h +++ b/libraries/networking/src/udt/Constants.h @@ -26,10 +26,65 @@ namespace udt { static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576; static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576; static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000; - static const int SEQUENCE_NUMBER_BITS = sizeof(SequenceNumber) * 8; - static const int MESSAGE_LINE_NUMBER_BITS = 32; - static const int MESSAGE_NUMBER_BITS = 30; - static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 1); + + + // Header constants + + // Bit sizes (in order) + static const int CONTROL_BIT_SIZE = 1; + static const int RELIABILITY_BIT_SIZE = 1; + static const int MESSAGE_BIT_SIZE = 1; + static const int OBFUSCATION_LEVEL_SIZE = 2; + static const int SEQUENCE_NUMBER_SIZE= 27; + + static const int PACKET_POSITION_SIZE = 2; + static const int MESSAGE_NUMBER_SIZE = 30; + + static const int MESSAGE_PART_NUMBER_SIZE = 32; + + // Offsets + static const int SEQUENCE_NUMBER_OFFSET = 0; + static const int OBFUSCATION_LEVEL_OFFSET = SEQUENCE_NUMBER_OFFSET + SEQUENCE_NUMBER_SIZE; + static const int MESSAGE_BIT_OFFSET = OBFUSCATION_LEVEL_OFFSET + OBFUSCATION_LEVEL_SIZE; + static const int RELIABILITY_BIT_OFFSET = MESSAGE_BIT_OFFSET + MESSAGE_BIT_SIZE; + static const int CONTROL_BIT_OFFSET = RELIABILITY_BIT_OFFSET + RELIABILITY_BIT_SIZE; + + static const int MESSAGE_NUMBER_OFFSET = 0; + static const int PACKET_POSITION_OFFSET = MESSAGE_NUMBER_OFFSET + MESSAGE_NUMBER_SIZE; + + static const int MESSAGE_PART_NUMBER_OFFSET = 0; + + // Masks + static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << CONTROL_BIT_OFFSET; + static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << RELIABILITY_BIT_OFFSET; + static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << MESSAGE_BIT_OFFSET; + static const uint32_t OBFUSCATION_LEVEL_MASK = uint32_t(3) << OBFUSCATION_LEVEL_OFFSET; + static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK | OBFUSCATION_LEVEL_MASK; + static const uint32_t SEQUENCE_NUMBER_MASK = ~BIT_FIELD_MASK; + + static const uint32_t PACKET_POSITION_MASK = uint32_t(3) << PACKET_POSITION_OFFSET; + static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK; + + static const uint32_t MESSAGE_PART_NUMBER_MASK = ~uint32_t(0); + + + // Static checks + static_assert(CONTROL_BIT_SIZE + RELIABILITY_BIT_SIZE + MESSAGE_BIT_SIZE + + OBFUSCATION_LEVEL_SIZE + SEQUENCE_NUMBER_SIZE == 32, "Sequence number line size incorrect"); + static_assert(PACKET_POSITION_SIZE + MESSAGE_NUMBER_SIZE == 32, "Message number line size incorrect"); + static_assert(MESSAGE_PART_NUMBER_SIZE == 32, "Message part number line size incorrect"); + + static_assert(CONTROL_BIT_MASK == 0x80000000, "CONTROL_BIT_MASK incorrect"); + static_assert(RELIABILITY_BIT_MASK == 0x40000000, "RELIABILITY_BIT_MASK incorrect"); + static_assert(MESSAGE_BIT_MASK == 0x20000000, "MESSAGE_BIT_MASK incorrect"); + static_assert(OBFUSCATION_LEVEL_MASK == 0x18000000, "OBFUSCATION_LEVEL_MASK incorrect"); + static_assert(BIT_FIELD_MASK == 0xF8000000, "BIT_FIELD_MASK incorrect"); + static_assert(SEQUENCE_NUMBER_MASK == 0x07FFFFFF, "SEQUENCE_NUMBER_MASK incorrect"); + + static_assert(PACKET_POSITION_MASK == 0xC0000000, "PACKET_POSITION_MASK incorrect"); + static_assert(MESSAGE_NUMBER_MASK == 0x3FFFFFFF, "MESSAGE_NUMBER_MASK incorrect"); + + static_assert(MESSAGE_PART_NUMBER_MASK == 0xFFFFFFFF, "MESSAGE_PART_NUMBER_MASK incorrect"); } #endif // hifi_udt_Constants_h diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index e068727e6f..088de49981 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -11,10 +11,35 @@ #include "Packet.h" +#include + +#include + using namespace udt; static int packetMetaTypeId = qRegisterMetaType("Packet*"); +using Key = uint64_t; +static const std::array KEYS {{ + 0x0, + 0x6362726973736574, + 0x7362697261726461, + 0x72687566666d616e, +}}; + +void xorHelper(char* start, int size, Key key) { + const auto end = start + size; + + auto p = start; + for (; p + sizeof(Key) < end; p += sizeof(Key)) { + *reinterpret_cast(p) ^= key; + } + + for (int i = 0; p < end; ++p || ++i) { + *p ^= *(reinterpret_cast(&key) + i); + } +} + int Packet::localHeaderSize(bool isPartOfMessage) { return sizeof(Packet::SequenceNumberAndBitField) + (isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0); @@ -69,44 +94,48 @@ Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& se readHeader(); adjustPayloadStartAndCapacity(Packet::localHeaderSize(_isPartOfMessage), _payloadSize > 0); + + if (getObfuscationLevel() != Packet::NoObfuscation) { +#ifdef UDT_CONNECTION_DEBUG + QString debugString = "Unobfuscating packet %1 with level %2"; + debugString = debugString.arg(QString::number((uint32_t)getSequenceNumber()), + QString::number(getObfuscationLevel())); + + if (isPartOfMessage()) { + debugString += "\n"; + debugString += " Message Number: %1, Part Number: %2."; + debugString = debugString.arg(QString::number(getMessageNumber()), + QString::number(getMessagePartNumber())); + } + + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Unobfuscating packet .*"); + qDebug() << qPrintable(debugString); +#endif + + obfuscate(NoObfuscation); // Undo obfuscation + } } -Packet::Packet(const Packet& other) : - BasePacket(other) -{ - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; +Packet::Packet(const Packet& other) : BasePacket(other) { + copyMembers(other); } Packet& Packet::operator=(const Packet& other) { BasePacket::operator=(other); - - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; + + copyMembers(other); return *this; } -Packet::Packet(Packet&& other) : - BasePacket(std::move(other)) -{ - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; - _packetPosition = other._packetPosition; - _messageNumber = other._messageNumber; +Packet::Packet(Packet&& other) : BasePacket(std::move(other)) { + copyMembers(other); } Packet& Packet::operator=(Packet&& other) { BasePacket::operator=(std::move(other)); - - _isReliable = other._isReliable; - _isPartOfMessage = other._isPartOfMessage; - _sequenceNumber = other._sequenceNumber; - _packetPosition = other._packetPosition; - _messageNumber = other._messageNumber; + + copyMembers(other); return *this; } @@ -124,13 +153,27 @@ void Packet::writeSequenceNumber(SequenceNumber sequenceNumber) const { writeHeader(); } -static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 2); -static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 3); -static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK; +void Packet::obfuscate(ObfuscationLevel level) { + auto obfuscationKey = KEYS[getObfuscationLevel()] ^ KEYS[level]; // Undo old and apply new one. + if (obfuscationKey != 0) { + xorHelper(getData() + localHeaderSize(isPartOfMessage()), + getDataSize() - localHeaderSize(isPartOfMessage()), obfuscationKey); -static const uint8_t PACKET_POSITION_OFFSET = 30; -static const uint32_t PACKET_POSITION_MASK = uint32_t(0x03) << PACKET_POSITION_OFFSET; -static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK; + // Update members and header + _obfuscationLevel = level; + writeHeader(); + } +} + +void Packet::copyMembers(const Packet& other) { + _isReliable = other._isReliable; + _isPartOfMessage = other._isPartOfMessage; + _obfuscationLevel = other._obfuscationLevel; + _sequenceNumber = other._sequenceNumber; + _packetPosition = other._packetPosition; + _messageNumber = other._messageNumber; + _messagePartNumber = other._messagePartNumber; +} void Packet::readHeader() const { SequenceNumberAndBitField* seqNumBitField = reinterpret_cast(_packet.get()); @@ -139,10 +182,12 @@ void Packet::readHeader() const { _isReliable = (bool) (*seqNumBitField & RELIABILITY_BIT_MASK); // Only keep reliability bit _isPartOfMessage = (bool) (*seqNumBitField & MESSAGE_BIT_MASK); // Only keep message bit - _sequenceNumber = SequenceNumber{ *seqNumBitField & ~BIT_FIELD_MASK }; // Remove the bit field + _obfuscationLevel = (ObfuscationLevel)((*seqNumBitField & OBFUSCATION_LEVEL_MASK) >> OBFUSCATION_LEVEL_OFFSET); + _sequenceNumber = SequenceNumber{ *seqNumBitField & SEQUENCE_NUMBER_MASK }; // Remove the bit field if (_isPartOfMessage) { MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1; + _messageNumber = *messageNumberAndBitField & MESSAGE_NUMBER_MASK; _packetPosition = static_cast(*messageNumberAndBitField >> PACKET_POSITION_OFFSET); @@ -163,6 +208,10 @@ void Packet::writeHeader() const { if (_isReliable) { *seqNumBitField |= RELIABILITY_BIT_MASK; } + + if (_obfuscationLevel != NoObfuscation) { + *seqNumBitField |= (_obfuscationLevel << OBFUSCATION_LEVEL_OFFSET); + } if (_isPartOfMessage) { *seqNumBitField |= MESSAGE_BIT_MASK; diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index 02d2c3d9bd..cad5cccb0e 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -25,6 +25,25 @@ namespace udt { class Packet : public BasePacket { Q_OBJECT public: + // Packet Header Format + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |C|R|M| O | Sequence Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | P | Message Number | Optional (only if M = 1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Message Part Number | Optional (only if M = 1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // C: Control bit + // R: Reliable bit + // M: Message bit + // O: Obfuscation level + // P: Position bits + + // NOTE: The SequenceNumber is only actually 29 bits to leave room for a bit field using SequenceNumberAndBitField = uint32_t; @@ -35,12 +54,20 @@ public: // Use same size as MessageNumberAndBitField so we can use the enum with bitwise operations enum PacketPosition : MessageNumberAndBitField { - ONLY = 0x0, - FIRST = 0x2, - MIDDLE = 0x3, - LAST = 0x1 + ONLY = 0x0, // 00 + FIRST = 0x2, // 10 + MIDDLE = 0x3, // 11 + LAST = 0x1 // 01 }; - + + // Use same size as SequenceNumberAndBitField so we can use the enum with bitwise operations + enum ObfuscationLevel : SequenceNumberAndBitField { + NoObfuscation = 0x0, // 00 + ObfuscationL1 = 0x1, // 01 + ObfuscationL2 = 0x2, // 10 + ObfuscationL3 = 0x3, // 11 + }; + static std::unique_ptr create(qint64 size = -1, bool isReliable = false, bool isPartOfMessage = false); static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); @@ -56,7 +83,8 @@ public: bool isPartOfMessage() const { return _isPartOfMessage; } bool isReliable() const { return _isReliable; } - + + ObfuscationLevel getObfuscationLevel() const { return _obfuscationLevel; } SequenceNumber getSequenceNumber() const { return _sequenceNumber; } MessageNumber getMessageNumber() const { return _messageNumber; } PacketPosition getPacketPosition() const { return _packetPosition; } @@ -64,6 +92,7 @@ public: void writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber); void writeSequenceNumber(SequenceNumber sequenceNumber) const; + void obfuscate(ObfuscationLevel level); protected: Packet(qint64 size, bool isReliable = false, bool isPartOfMessage = false); @@ -76,6 +105,8 @@ protected: Packet& operator=(Packet&& other); private: + void copyMembers(const Packet& other); + // Header readers - these read data to member variables after pulling packet off wire void readHeader() const; void writeHeader() const; @@ -83,6 +114,7 @@ private: // Simple holders to prevent multiple reading and bitwise ops mutable bool _isReliable { false }; mutable bool _isPartOfMessage { false }; + mutable ObfuscationLevel _obfuscationLevel { NoObfuscation }; mutable SequenceNumber _sequenceNumber { 0 }; mutable MessageNumber _messageNumber { 0 }; mutable PacketPosition _packetPosition { PacketPosition::ONLY }; diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp index bc3a1796e2..8ff2333a20 100644 --- a/libraries/networking/src/udt/PacketQueue.cpp +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -16,7 +16,7 @@ using namespace udt; MessageNumber PacketQueue::getNextMessageNumber() { - static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_BITS; + static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE; _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; return _currentMessageNumber; } diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 4d6e431b45..18269d1d43 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "../NetworkLogging.h" @@ -225,7 +226,9 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, { // Insert the packet we have just sent in the sent list QWriteLocker locker(&_sentLock); - _sentPackets[newPacket->getSequenceNumber()].swap(newPacket); + auto& entry = _sentPackets[newPacket->getSequenceNumber()]; + entry.first = 0; // No resend + entry.second.swap(newPacket); } Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list"); @@ -354,14 +357,46 @@ bool SendQueue::maybeResendPacket() { auto it = _sentPackets.find(resendNumber); if (it != _sentPackets.end()) { + auto& entry = it->second; // we found the packet - grab it - auto& resendPacket = *(it->second); - - // send it off - sendPacket(resendPacket); - - // unlock the sent packets - sentLocker.unlock(); + auto& resendPacket = *(entry.second); + ++entry.first; // Add 1 resend + + Packet::ObfuscationLevel level = (Packet::ObfuscationLevel)(entry.first < 2 ? 0 : (entry.first - 2) % 4); + + if (level != Packet::NoObfuscation) { +#ifdef UDT_CONNECTION_DEBUG + QString debugString = "Obfuscating packet %1 with level %2"; + debugString = debugString.arg(QString::number((uint32_t)resendPacket.getSequenceNumber()), + QString::number(level)); + if (resendPacket.isPartOfMessage()) { + debugString += "\n"; + debugString += " Message Number: %1, Part Number: %2."; + debugString = debugString.arg(QString::number(resendPacket.getMessageNumber()), + QString::number(resendPacket.getMessagePartNumber())); + } + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Obfuscating packet .*"); + qCritical() << qPrintable(debugString); +#endif + + // Create copy of the packet + auto packet = Packet::createCopy(resendPacket); + + // unlock the sent packets + sentLocker.unlock(); + + // Obfuscate packet + packet->obfuscate(level); + + // send it off + sendPacket(*packet); + } else { + // send it off + sendPacket(resendPacket); + + // unlock the sent packets + sentLocker.unlock(); + } emit packetRetransmitted(); diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 5428e7a26d..da8f5e6c3f 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -126,7 +126,8 @@ private: LossList _naks; // Sequence numbers of packets to resend mutable QReadWriteLock _sentLock; // Protects the sent packet list - std::unordered_map> _sentPackets; // Packets waiting for ACK. + using PacketResendPair = std::pair>; // Number of resend + packet ptr + std::unordered_map _sentPackets; // Packets waiting for ACK. std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable std::atomic _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client diff --git a/libraries/networking/src/udt/SequenceNumber.h b/libraries/networking/src/udt/SequenceNumber.h index a75f3478b8..3abc80bdd8 100644 --- a/libraries/networking/src/udt/SequenceNumber.h +++ b/libraries/networking/src/udt/SequenceNumber.h @@ -24,9 +24,9 @@ public: using Type = int32_t; using UType = uint32_t; - // Values are for 29 bit SequenceNumber - static const Type THRESHOLD = 0x0FFFFFFF; // threshold for comparing sequence numbers - static const Type MAX = 0x1FFFFFFF; // maximum sequence number used in UDT + // Values are for 27 bit SequenceNumber + static const Type THRESHOLD = 0x03FFFFFF; // threshold for comparing sequence numbers + static const Type MAX = 0x07FFFFFF; // maximum sequence number used in UDT SequenceNumber() = default; SequenceNumber(const SequenceNumber& other) : _value(other._value) {} diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 8fb5db3853..1eb7c04331 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -277,7 +277,7 @@ void Socket::readPendingDatagrams() { if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number auto& connection = findOrCreateConnection(senderSockAddr); - + if (!connection.processReceivedSequenceNumber(packet->getSequenceNumber(), packet->getDataSize(), packet->getPayloadSize())) { diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index 6185ed07dc..6386622675 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -16,11 +16,11 @@ #include "render/DrawTask.h" -class AntiAliasingConfig : public render::Job::Config::Persistent { +class AntiAliasingConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled) public: - AntiAliasingConfig() : render::Job::Config::Persistent("Antialiasing", false) {} + AntiAliasingConfig() : render::Job::Config(true) {} }; class Antialiasing { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3b741ac734..c29c37140e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -43,7 +43,7 @@ Model::Model(RigPointer rig, QObject* parent) : _rotation(), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), - _scaleToFitDimensions(0.0f), + _scaleToFitDimensions(1.0f), _scaledToFit(false), _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), @@ -909,6 +909,14 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRes } } +glm::vec3 Model::getScaleToFitDimensions() const { + if (_scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER && + _scaleToFitDimensions.z == FAKE_DIMENSION_PLACEHOLDER) { + return glm::vec3(_scaleToFitDimensions.x); + } + return _scaleToFitDimensions; +} + void Model::scaleToFit() { // If our _scaleToFitDimensions.y/z are FAKE_DIMENSION_PLACEHOLDER then it means our // user asked to scale us in a fixed aspect ratio to a single largest dimension, but diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 2c138fa7be..424eed1e66 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -197,7 +197,7 @@ public: /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit - const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to + glm::vec3 getScaleToFitDimensions() const; /// the dimensions model is scaled to, including inferred y/z void setCauterizeBones(bool flag) { _cauterizeBones = flag; } bool getCauterizeBones() const { return _cauterizeBones; } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 13a24fc16d..31f40f5285 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -56,7 +56,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Fetch and cull the items from the scene auto sceneFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); const auto sceneSelection = addJob("FetchSceneSelection", sceneFilter); - const auto culledSceneSelection = addJob("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::OPAQUE_ITEM, sceneFilter); + const auto culledSceneSelection = addJob("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::ITEM, sceneFilter); // Multi filter visible items into different buckets const int NUM_FILTERS = 3; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 32188e4cc9..f8057c164b 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -108,7 +108,7 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task(std::make_sha const auto fetchedItems = addJob("FetchShadowMap"); // CPU: Cull against KeyLight frustum (nearby viewing camera) - const auto culledItems = addJob>("CullShadowMap", fetchedItems, cullFunctor); + const auto culledItems = addJob>("CullShadowMap", fetchedItems, cullFunctor); // CPU: Sort by pipeline const auto sortedShapes = addJob("PipelineSortShadowSort", culledItems); diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf index d1c50b2c58..7b241a3ebf 100644 --- a/libraries/render-utils/src/fxaa.slf +++ b/libraries/render-utils/src/fxaa.slf @@ -44,11 +44,11 @@ void main() { // fetch raw RGB values for nearby locations // sampling pattern is "five on a die" (each diagonal direction and the center) // computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed - vec3 rgbNW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; - vec3 rgbNE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; - vec3 rgbSW = texture2D(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; - vec3 rgbSE = texture2D(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; - vec3 rgbM = texture2D(colorTexture, varTexcoord).xyz; + vec3 rgbNW = texture(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbNE = texture(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbSW = texture(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbSE = texture(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbM = texture(colorTexture, varTexcoord).xyz; // convert RGB values to luminance vec3 luma = vec3(0.299, 0.587, 0.114); @@ -76,11 +76,11 @@ void main() { // perform additional texture sampling perpendicular to gradient vec3 rgbA = (1.0 / 2.0) * ( - texture2D(colorTexture, varTexcoord + dir * (1.0 / 3.0 - 0.5)).xyz + - texture2D(colorTexture, varTexcoord + dir * (2.0 / 3.0 - 0.5)).xyz); + texture(colorTexture, varTexcoord + dir * (1.0 / 3.0 - 0.5)).xyz + + texture(colorTexture, varTexcoord + dir * (2.0 / 3.0 - 0.5)).xyz); vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( - texture2D(colorTexture, varTexcoord + dir * (0.0 / 3.0 - 0.5)).xyz + - texture2D(colorTexture, varTexcoord + dir * (3.0 / 3.0 - 0.5)).xyz); + texture(colorTexture, varTexcoord + dir * (0.0 / 3.0 - 0.5)).xyz + + texture(colorTexture, varTexcoord + dir * (3.0 / 3.0 - 0.5)).xyz); float lumaB = dot(rgbB, luma); // compare luma of new samples to the luma range of the original neighborhood diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index cf4e825ea3..3fc6bffbd3 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -28,7 +28,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc RenderArgs* args = renderContext->args; ViewFrustum* frustum = args->_viewFrustum; - details._considered += inItems.size(); + details._considered += (int)inItems.size(); // Culling / LOD for (auto item : inItems) { @@ -59,7 +59,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc details._outOfView++; } } - details._rendered += outItems.size(); + details._rendered += (int)outItems.size(); } struct ItemBoundSort { @@ -202,7 +202,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re auto& scene = sceneContext->_scene; auto& details = args->_details.edit(_detailType); - details._considered += inSelection.numItems(); + details._considered += (int)inSelection.numItems(); // Eventually use a frozen frustum auto argFrustum = args->_viewFrustum; @@ -325,7 +325,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re } } - details._rendered += outItems.size(); + details._rendered += (int)outItems.size(); // Restore frustum if using the frozen one: diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 9a52bb06ff..734d683ad7 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -138,7 +138,7 @@ namespace render { _cullFunctor{ cullFunctor } {} CullFunctor _cullFunctor; - RenderDetails::Type _detailType{ RenderDetails::OPAQUE_ITEM }; + RenderDetails::Type _detailType{ RenderDetails::OTHER }; ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 74c832a0c4..8aaaf07300 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -217,6 +217,10 @@ public: static const ID INVALID_ITEM_ID = 0; static const ItemCell INVALID_CELL = -1; + // Convenient function to clear an ID or check it s valid + static void clearID(ID& id) { id = INVALID_ITEM_ID; } + static bool isValidID(const ID id) { return id != INVALID_ITEM_ID; } + // Bound is the AABBox fully containing this item typedef AABox Bound; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a65fe7417d..738e313f8f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -144,7 +144,12 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam ScriptEngine::~ScriptEngine() { qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename(); - DependencyManager::get()->removeScriptEngine(this); + auto scriptEngines = DependencyManager::get(); + if (scriptEngines) { + scriptEngines->removeScriptEngine(this); + } else { + qCWarning(scriptengine) << "Script destroyed after ScriptEngines!"; + } } void ScriptEngine::disconnectNonEssentialSignals() { diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index c2c35df957..a1aae35063 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -9,10 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include "RegisteredMetaTypes.h" @@ -91,6 +94,51 @@ QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector()) { + v = glm::vec3(object.toFloat()); + valid = true; + } else if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + v.x = qvec3.x(); + v.y = qvec3.y(); + v.z = qvec3.z(); + valid = true; + } else { + auto map = object.toMap(); + auto x = map["x"]; + auto y = map["y"]; + auto z = map["z"]; + if (!x.isValid()) { + x = map["width"]; + } + if (!y.isValid()) { + y = map["height"]; + } + if (!y.isValid()) { + z = map["depth"]; + } + + if (x.canConvert() && y.canConvert() && z.canConvert()) { + v.x = x.toFloat(); + v.y = y.toFloat(); + v.z = z.toFloat(); + valid = true; + } + } + return v; +} + +glm::vec3 vec3FromVariant(const QVariant &object) { + bool valid = false; + return vec3FromVariant(object, valid); +} + + QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat &quat) { QScriptValue obj = engine->newObject(); if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) { @@ -111,6 +159,42 @@ void quatFromScriptValue(const QScriptValue &object, glm::quat &quat) { quat.w = object.property("w").toVariant().toFloat(); } +glm::quat quatFromVariant(const QVariant &object, bool& isValid) { + glm::quat q; + if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + q.x = qvec3.x(); + q.y = qvec3.y(); + q.z = qvec3.z(); + q.w = qvec3.scalar(); + isValid = true; + } else { + auto map = object.toMap(); + q.x = map["x"].toFloat(&isValid); + if (!isValid) { + return glm::quat(); + } + q.y = map["y"].toFloat(&isValid); + if (!isValid) { + return glm::quat(); + } + q.z = map["z"].toFloat(&isValid); + if (!isValid) { + return glm::quat(); + } + q.w = map["w"].toFloat(&isValid); + if (!isValid) { + return glm::quat(); + } + } + return q; +} + +glm::quat quatFromVariant(const QVariant &object) { + bool valid = false; + return quatFromVariant(object, valid); +} + QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 81314cef69..2bec5b025e 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -41,12 +41,18 @@ void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); +glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); +glm::vec3 vec3FromVariant(const QVariant &object); + QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2); void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); +glm::quat quatFromVariant(const QVariant &object, bool& isValid); +glm::quat quatFromVariant(const QVariant &object); + QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect); void qRectFromScriptValue(const QScriptValue& object, QRect& rect); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index f9fc6eb66b..341553b743 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -35,36 +35,31 @@ class ShapePipeline; class RenderDetails { public: enum Type { - OPAQUE_ITEM, - SHADOW_ITEM, - TRANSLUCENT_ITEM, - OTHER_ITEM + ITEM, + SHADOW, + OTHER }; struct Item { - size_t _considered = 0; - size_t _rendered = 0; + int _considered = 0; int _outOfView = 0; int _tooSmall = 0; + int _rendered = 0; }; int _materialSwitches = 0; int _trianglesRendered = 0; - Item _opaque; + Item _item; Item _shadow; - Item _translucent; Item _other; Item& edit(Type type) { switch (type) { - case OPAQUE_ITEM: - return _opaque; - case SHADOW_ITEM: + case SHADOW: return _shadow; - case TRANSLUCENT_ITEM: - return _translucent; - case OTHER_ITEM: + case ITEM: + return _item; default: return _other; } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index fa40fedb9b..1c41827795 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -112,6 +112,7 @@ void OffscreenUi::create(QOpenGLContext* context) { } void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { + emit showDesktop(); QQuickItem* item = getRootItem()->findChild(name); // First load? if (!item) { @@ -127,6 +128,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); // Already loaded? if (item) { + emit showDesktop(); item->setVisible(!item->isVisible()); return; } @@ -134,6 +136,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); if (item && !item->isVisible()) { + emit showDesktop(); item->setVisible(true); } } @@ -251,15 +254,15 @@ QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QS } QMessageBox::StandardButton OffscreenUi::information(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - return DependencyManager::get()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton); + return DependencyManager::get()->messageBox(QMessageBox::Icon::Information, title, text, buttons, defaultButton); } QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - return DependencyManager::get()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton); + return DependencyManager::get()->messageBox(QMessageBox::Icon::Question, title, text, buttons, defaultButton); } QMessageBox::StandardButton OffscreenUi::warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - return DependencyManager::get()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton); + return DependencyManager::get()->messageBox(QMessageBox::Icon::Warning, title, text, buttons, defaultButton); } @@ -439,6 +442,8 @@ void OffscreenUi::createDesktop(const QUrl& url) { new VrMenu(this); new KeyboardFocusHack(); + + connect(_desktop, SIGNAL(showDesktop()), this, SLOT(showDesktop())); } QQuickItem* OffscreenUi::getDesktop() { diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index de479853f3..32f7a31d17 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -63,7 +63,7 @@ public: } /// Same design as QMessageBox::question(), will block, returns result static QMessageBox::StandardButton question(void* ignored, const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return question(title, text, buttons, defaultButton); } @@ -106,6 +106,9 @@ public: // Compatibility with QInputDialog::getItem 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); +signals: + void showDesktop(); + private: QString fileDialog(const QVariantMap& properties); diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index d1ef660532..6609b376b1 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -124,18 +124,25 @@ void VrMenu::addMenu(QMenu* menu) { new MenuUserData(menu, result); auto menuAction = menu->menuAction(); updateQmlItemFromAction(result, menuAction); - QObject::connect(menuAction, &QAction::changed, [=] { + auto connection = QObject::connect(menuAction, &QAction::changed, [=] { updateQmlItemFromAction(result, menuAction); }); + QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { + QObject::disconnect(connection); + }); } void bindActionToQmlAction(QObject* qmlAction, QAction* action) { new MenuUserData(action, qmlAction); updateQmlItemFromAction(qmlAction, action); - QObject::connect(action, &QAction::changed, [=] { + auto connection = QObject::connect(action, &QAction::changed, [=] { updateQmlItemFromAction(qmlAction, action); }); + QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { + QObject::disconnect(connection); + }); + QObject::connect(action, &QAction::toggled, [=](bool checked) { qmlAction->setProperty("checked", checked); }); diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index aad61e4bea..9fdce3add4 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -113,7 +113,6 @@ void SixenseManager::deactivate() { _container->removeMenu(MENU_PATH); _inputDevice->_poseStateMap.clear(); - _inputDevice->_collectedSamples.clear(); if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); @@ -158,7 +157,6 @@ void SixenseManager::InputDevice::update(float deltaTime, const controller::Inpu _axisStateMap.clear(); _buttonPressedMap.clear(); _poseStateMap.clear(); - _collectedSamples.clear(); } return; } @@ -209,13 +207,10 @@ void SixenseManager::InputDevice::update(float deltaTime, const controller::Inpu rawPoses[i] = controller::Pose(position, rotation, Vectors::ZERO, Vectors::ZERO); } else { _poseStateMap.clear(); - _collectedSamples.clear(); } } else { auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; _poseStateMap[hand] = controller::Pose(); - _collectedSamples[hand].first.clear(); - _collectedSamples[hand].second.clear(); } } @@ -481,29 +476,24 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, const control glm::vec3 velocity(0.0f); glm::vec3 angularVelocity(0.0f); - if (prevPose.isValid() && deltaTime > std::numeric_limits::epsilon()) { - auto& samples = _collectedSamples[hand]; - - velocity = (pos - prevPose.getTranslation()) / deltaTime; - samples.first.addSample(velocity); - velocity = samples.first.average; - - auto deltaRot = glm::normalize(rot * glm::conjugate(prevPose.getRotation())); - auto axis = glm::axis(deltaRot); - auto speed = glm::angle(deltaRot) / deltaTime; - assert(!glm::isnan(speed)); - angularVelocity = speed * axis; - samples.second.addSample(angularVelocity); - angularVelocity = samples.second.average; - } else if (!prevPose.isValid()) { - _collectedSamples[hand].first.clear(); - _collectedSamples[hand].second.clear(); - } + glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; // transform pose into avatar frame. - glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; - auto avatarPose = controller::Pose(pos, rot, velocity, angularVelocity).transform(controllerToAvatar); - _poseStateMap[hand] = avatarPose; + auto nextPose = controller::Pose(pos, rot, velocity, angularVelocity).transform(controllerToAvatar); + + if (prevPose.isValid() && (deltaTime > std::numeric_limits::epsilon())) { + nextPose.velocity = (nextPose.getTranslation() - prevPose.getTranslation()) / deltaTime; + + glm::quat deltaRotation = nextPose.getRotation() * glm::inverse(prevPose.getRotation()); + float rotationAngle = glm::angle(deltaRotation); + if (rotationAngle > EPSILON) { + nextPose.angularVelocity = glm::normalize(glm::axis(deltaRotation)); + nextPose.angularVelocity *= (rotationAngle / deltaTime); + } + + } + _poseStateMap[hand] = nextPose; + #endif // HAVE_SIXENSE } diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index fc74b83532..5106c87836 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -52,11 +52,6 @@ private: static const glm::vec3 DEFAULT_AVATAR_POSITION; static const float CONTROLLER_THRESHOLD; - template - using SampleAverage = MovingAverage; - using Samples = std::pair, SampleAverage>; - using MovingAverageMap = std::map; - class InputDevice : public controller::InputDevice { public: InputDevice() : controller::InputDevice("Hydra") {} @@ -75,8 +70,6 @@ private: friend class SixenseManager; - MovingAverageMap _collectedSamples; - int _calibrationState { CALIBRATION_STATE_IDLE }; // these are calibration results glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index efd230bc28..559ea5bcfb 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "OpenVrHelpers.h" @@ -32,6 +33,8 @@ const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probab static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; +vec3 _trackedDeviceLinearVelocities[vr::k_unMaxTrackedDeviceCount]; +vec3 _trackedDeviceAngularVelocities[vr::k_unMaxTrackedDeviceCount]; static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; @@ -119,6 +122,8 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const { for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { _trackedDevicePose[i] = predictedTrackedDevicePose[i]; _trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking); + _trackedDeviceLinearVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vVelocity)); + _trackedDeviceAngularVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vAngularVelocity)); } return _trackedDevicePoseMat4[0]; } diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index e4a34c53b7..26179fb757 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -13,7 +13,7 @@ #include vr::IVRSystem* acquireOpenVrSystem(); -void releaseOpenVrSystem(); +void releaseOpenVrSystem(); template void openvr_for_each_eye(F f) { @@ -25,6 +25,10 @@ inline mat4 toGlm(const vr::HmdMatrix44_t& m) { return glm::transpose(glm::make_mat4(&m.m[0][0])); } +inline vec3 toGlm(const vr::HmdVector3_t& v) { + return vec3(v.v[0], v.v[1], v.v[2]); +} + inline mat4 toGlm(const vr::HmdMatrix34_t& m) { mat4 result = mat4( m.m[0][0], m.m[1][0], m.m[2][0], 0.0, diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 720a6d48c6..071d5fd631 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -29,6 +29,8 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; +extern vec3 _trackedDeviceLinearVelocities[vr::k_unMaxTrackedDeviceCount]; +extern vec3 _trackedDeviceAngularVelocities[vr::k_unMaxTrackedDeviceCount]; vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); @@ -249,10 +251,11 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle numTrackedControllers++; bool left = numTrackedControllers == 2; - const mat4& mat = _trackedDevicePoseMat4[device]; - if (!jointsCaptured) { - handlePoseEvent(inputCalibrationData, mat, numTrackedControllers - 1); + const mat4& mat = _trackedDevicePoseMat4[device]; + const vec3 linearVelocity = _trackedDeviceLinearVelocities[device]; + const vec3 angularVelocity = _trackedDeviceAngularVelocities[device]; + handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, numTrackedControllers - 1); } // handle inputs @@ -314,7 +317,9 @@ void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool } } -void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, bool left) { +void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, + const mat4& mat, const vec3& linearVelocity, + const vec3& angularVelocity, bool left) { // When the sensor-to-world rotation is identity the coordinate axes look like this: // // user @@ -379,16 +384,22 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const controller::Input static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; - glm::vec3 position = extractTranslation(mat); - glm::quat rotation = glm::quat_cast(mat); + auto translationOffset = (left ? leftTranslationOffset : rightTranslationOffset); + auto rotationOffset = (left ? leftRotationOffset : rightRotationOffset); - position += rotation * (left ? leftTranslationOffset : rightTranslationOffset); - rotation = rotation * (left ? leftRotationOffset : rightRotationOffset); + glm::vec3 position = extractTranslation(mat); + glm::quat rotation = glm::normalize(glm::quat_cast(mat)); + + position += rotation * translationOffset; + rotation = rotation * rotationOffset; // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; - auto avatarPose = controller::Pose(position, rotation).transform(controllerToAvatar); - _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose; + auto avatarPose = controller::Pose(position, rotation); + // handle change in velocity due to translationOffset + avatarPose.velocity = linearVelocity + glm::cross(angularVelocity, position - extractTranslation(mat)); + avatarPose.angularVelocity = angularVelocity; + _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); } controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 282c8e41a5..51339cd465 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -46,7 +46,7 @@ public: void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges); void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } - + private: class InputDevice : public controller::InputDevice { public: @@ -60,7 +60,8 @@ private: void handleButtonEvent(uint32_t button, bool pressed, bool left); void handleAxisEvent(uint32_t axis, float x, float y, bool left); - void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, bool left); + void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, + const vec3& linearVelocity, const vec3& angularVelocity, bool left); int _trackedControllers { 0 }; vr::IVRSystem*& _hmd; @@ -68,8 +69,8 @@ private: }; void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - - + + bool _registeredWithInputMapper { false }; bool _modelLoaded { false }; diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index 02d8841486..0bdfdb64b9 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -4,7 +4,8 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 import "../../../interface/resources/qml" -import "../../../interface/resources/qml/windows" +//import "../../../interface/resources/qml/windows" +import "../../../interface/resources/qml/windows-uit" import "../../../interface/resources/qml/dialogs" import "../../../interface/resources/qml/hifi" import "../../../interface/resources/qml/hifi/dialogs" @@ -13,14 +14,14 @@ ApplicationWindow { id: appWindow visible: true width: 1280 - height: 720 + height: 800 title: qsTr("Scratch App") Desktop { id: desktop anchors.fill: parent rootMenu: StubMenu { id: rootMenu } - Component.onCompleted: offscreenWindow = appWindow + //Component.onCompleted: offscreenWindow = appWindow MouseArea { anchors.fill: parent @@ -36,6 +37,27 @@ ApplicationWindow { property var tabs: []; property var urls: []; + + Button { + text: "Running Scripts" + property var builder: Component { + RunningScripts { } + } + onClicked: { + var runningScripts = builder.createObject(desktop); + } + } + + Button { + text: "Attachments" + property var builder: Component { + AttachmentsDialog { } + } + onClicked: { + var attachmentsDialog = builder.createObject(desktop); + } + } + /* Button { text: "restore all" @@ -59,17 +81,27 @@ ApplicationWindow { } */ Button { - text: "Show Long Error" + // Replicates message box that pops up after selecting new avatar. Includes title. + text: "Confirm Avatar" onClicked: { - desktop.messageBox({ - informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ", - text: "Baloney", - icon: OriginalDialogs.StandardIcon.Warning, - detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a" + var messageBox = desktop.messageBox({ + title: "Set Avatar", + text: "Would you like to use 'Albert' for your avatar?", + icon: OriginalDialogs.StandardIcon.Question, // Test question icon + //icon: OriginalDialogs.StandardIcon.Information, // Test informaton icon + //icon: OriginalDialogs.StandardIcon.Warning, // Test warning icon + //icon: OriginalDialogs.StandardIcon.Critical, // Test critical icon + //icon: OriginalDialogs.StandardIcon.NoIcon, // Test no icon + buttons: OriginalDialogs.StandardButton.Ok + OriginalDialogs.StandardButton.Cancel, + defaultButton: OriginalDialogs.StandardButton.Ok }); + messageBox.selected.connect(function(button) { + console.log("You clicked " + button) + }) } } Button { + // Message without title. text: "Show Error" onClicked: { var messageBox = desktop.messageBox({ @@ -81,6 +113,20 @@ ApplicationWindow { }) } } + Button { + // detailedText is not currently used anywhere in Interface but it is easier to leave in and style good enough. + text: "Show Long Error" + onClicked: { + desktop.messageBox({ + informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ", + text: "Baloney", + icon: OriginalDialogs.StandardIcon.Warning, + detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a" + }); + } + } + /* + // There is no such desktop.queryBox() function; may need to update test to cover QueryDialog.qml? Button { text: "Show Query" onClicked: { @@ -98,6 +144,7 @@ ApplicationWindow { }) } } + */ Button { text: "Open Directory" property var builder: Component { @@ -150,6 +197,7 @@ ApplicationWindow { } } + /* Window { id: blue closable: true @@ -174,6 +222,7 @@ ApplicationWindow { Component.onDestruction: console.log("Blue destroyed") } } + */ /* Window { id: green