diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index bcd9478700..d85e1d137b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -680,11 +680,11 @@ void AudioMixer::domainSettingsRequestComplete() { void AudioMixer::broadcastMixes() { auto nodeList = DependencyManager::get(); - int nextFrame = 0; + int64_t nextFrame = 0; QElapsedTimer timer; timer.start(); - int usecToSleep = AudioConstants::NETWORK_FRAME_USECS; + int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS; const int TRAILING_AVERAGE_FRAMES = 100; int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; @@ -826,12 +826,7 @@ void AudioMixer::broadcastMixes() { break; } - usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; // ns to us - - if (usecToSleep > int(USECS_PER_SECOND)) { - qDebug() << "DANGER: amount to sleep is" << usecToSleep; - qDebug() << "NextFrame is" << nextFrame << "and timer nsecs elapsed is" << timer.nsecsElapsed(); - } + usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - (timer.nsecsElapsed() / 1000); if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 47790f8e0b..e794583c53 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -976,6 +976,187 @@ +
+ +
+ +
+
Text Content
+
+ +
+
+
+
Line Height
+
+ +
+
+
+
Text Color
+
+
+
R
+
G
+
B
+
+
+
+
Background Color
+
+
+
R
+
G
+
B
+ +
+
+ +
+ +
+ +
+ Stage Sun Model Enabled + + + +
+ +
+ +
+ +
+
Light Color
+
+
+
R
+
G
+
B
+
+
+ +
+
Light Intensity
+
+ +
+
+ +
+
Light Direction
+
+
Pitch
+
Yaw
+
Roll
+
+
+ +
+
Ambient Intensity
+
+ +
+
+ + +
+
Ambient URL
+
+ +
+
+ +
+ +
+ +
+
Stage Latitude
+
+ +
+
+
+
Stage Longitude
+
+ +
+
+
+
Stage Altitude
+
+ +
+
+ +
+ Automatically calculate stage hour and day from location and clock. + + + +
+ +
+
Stage Day
+
+ +
+
+
+
Stage Hour
+
+ +
+
+ +
+ +
+ +
+
Background Mode
+
+ +
+
+
+ +
+ +
+
Skybox Color
+
+
+
R
+
G
+
B
+
+
+ +
+
Skybox URL
+
+ +
+
+ +
+ +
+ +
+
Source URL
+
+ + +
+
+
@@ -1311,18 +1492,6 @@
-
- -
- -
-
Source URL
-
- -
-
- -
@@ -1365,41 +1534,7 @@ -
- -
- -
-
Text Content
-
- -
-
-
-
Line Height
-
- -
-
-
-
Text Color
-
-
-
R
-
G
-
B
-
-
-
-
Background Color
-
-
-
R
-
G
-
B
-
-
- +
@@ -1438,141 +1573,6 @@
- - -
- -
- -
- Stage Sun Model Enabled - - - -
- -
- -
- -
-
Light Color
-
-
-
R
-
G
-
B
-
-
- -
-
Light Intensity
-
- -
-
- -
-
Light Direction
-
-
Pitch
-
Yaw
-
Roll
-
-
- -
-
Ambient Intensity
-
- -
-
- - -
-
Ambient URL
-
- -
-
- -
- -
- -
-
Stage Latitude
-
- -
-
-
-
Stage Longitude
-
- -
-
-
-
Stage Altitude
-
- -
-
- -
- Automatically calculate stage hour and day from location and clock. - - - -
- -
-
Stage Day
-
- -
-
-
-
Stage Hour
-
- -
-
- -
- -
- -
-
Background Mode
-
- -
-
- - -
- -
- -
-
Skybox Color
-
-
-
R
-
G
-
B
-
-
-
-
Skybox URL
-
- -
-
- + \ No newline at end of file diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index ce79214537..49398c5009 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -236,18 +236,10 @@ "dimensions" ]); - ImageOverlay = generateOverlayClass(Overlay2D, "image", [ - "subImage", "imageURL" - ]); - Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [ "url", "subImage" ]); - TextOverlay = generateOverlayClass(Overlay2D, "text", [ - "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" - ]); - Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [ "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", "rightMargin", "bottomMargin" diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 7ab68cab1c..6c01580ea6 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -132,14 +132,15 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit this.y = y; this.width = 0; this.height = ToolBar.TITLE_BAR_HEIGHT; - this.back = this.back = Overlays.addOverlay("text", { - backgroundColor: { red: 255, green: 255, blue: 255 }, + this.backAlpha = 1.0; + this.back = Overlays.addOverlay("rectangle", { + color: { red: 255, green: 255, blue: 255 }, x: this.x, y: this.y, + radius: 4, width: this.width, height: this.height, - alpha: 1.0, - backgroundAlpha: 1.0, + alpha: this.backAlpha, visible: false }); this.spacing = []; @@ -246,10 +247,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit this.tools[tool].setAlpha(alpha); } if (this.back != null) { - Overlays.editOverlay(this.back, { - alpha: alpha, - backgroundAlpha: alpha - }); + this.backAlpha = alpha; + Overlays.editOverlay(this.back, { alpha: alpha }); } } else { this.tools[tool].setAlpha(alpha); @@ -258,9 +257,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit this.setBack = function(color, alpha) { if (color == null) { - Overlays.editOverlay(this.back, { - visible: false - }); + Overlays.editOverlay(this.back, { visible: false }); } else { Overlays.editOverlay(this.back, { width: this.width + @@ -268,8 +265,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit height: this.height + ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, visible: true, - backgroundColor: color, - backgroundAlpha: alpha + color: color, + alpha: alpha }); } } @@ -339,12 +336,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit that.hover = function (enable) { // Can be overriden or extended by clients. that.isHovering = enable; if (that.back) { - if (enable) { - that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha'); - } Overlays.editOverlay(this.back, { visible: enable, - backgroundAlpha: enable ? 0.5 : that.oldAlpha + alpha: enable ? 0.5 : that.backAlpha }); } }; diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index d510e7b966..42ff64abc6 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -145,7 +145,7 @@ "id": "rightHandGraspOpen", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_right.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_open_right.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -157,7 +157,7 @@ "id": "rightHandGraspClosed", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_right.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_closed_right.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -205,7 +205,7 @@ "id": "leftHandGraspOpen", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_left.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_open_left.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -217,7 +217,7 @@ "id": "leftHandGraspClosed", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_left.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/hydra_pose_closed_left.fbx", "startFrame": 10.0, "endFrame": 10.0, "timeScale": 1.0, @@ -420,7 +420,7 @@ "id": "idleStand", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx", "startFrame": 0.0, "endFrame": 90.0, "timeScale": 1.0, @@ -432,7 +432,7 @@ "id": "idleTalk", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/talk/talk.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/talk.fbx", "startFrame": 0.0, "endFrame": 801.0, "timeScale": 1.0, @@ -457,7 +457,7 @@ "id": "walkFwdShort", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_short_fwd.fbx", "startFrame": 0.0, "endFrame": 39.0, "timeScale": 1.0, @@ -469,7 +469,7 @@ "id": "walkFwdNormal", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_fwd.fbx", "startFrame": 0.0, "endFrame": 35.0, "timeScale": 1.0, @@ -481,7 +481,7 @@ "id": "walkFwdRun", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/run_fwd.fbx", "startFrame": 0.0, "endFrame": 21.0, "timeScale": 1.0, @@ -495,7 +495,7 @@ "id": "idleToWalkFwd", "type": "clip", "data": { - "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/idle_to_walk.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle_to_walk.fbx", "startFrame": 1.0, "endFrame": 19.0, "timeScale": 1.0, @@ -518,7 +518,7 @@ "id": "walkBwdShort", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_short_bwd.fbx", "startFrame": 0.0, "endFrame": 38.0, "timeScale": 1.0, @@ -530,7 +530,7 @@ "id": "walkBwdNormal", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/walk_bwd.fbx", "startFrame": 0.0, "endFrame": 36.0, "timeScale": 1.0, @@ -544,7 +544,7 @@ "id": "turnLeft", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/turn_left.fbx", "startFrame": 0.0, "endFrame": 28.0, "timeScale": 1.0, @@ -556,7 +556,7 @@ "id": "turnRight", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/turn_right.fbx", "startFrame": 0.0, "endFrame": 30.0, "timeScale": 1.0, @@ -579,7 +579,7 @@ "id": "strafeLeftShort", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_short_left.fbx", "startFrame": 0.0, "endFrame": 28.0, "timeScale": 1.0, @@ -591,7 +591,7 @@ "id": "strafeLeftNormal", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_left.fbx", "startFrame": 0.0, "endFrame": 30.0, "timeScale": 1.0, @@ -616,7 +616,7 @@ "id": "strafeRightShort", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_short_right.fbx", "startFrame": 0.0, "endFrame": 28.0, "timeScale": 1.0, @@ -628,7 +628,7 @@ "id": "strafeRightNormal", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/side_step_right.fbx", "startFrame": 0.0, "endFrame": 30.0, "timeScale": 1.0, @@ -642,7 +642,7 @@ "id": "awayIntro", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx", "startFrame": 0.0, "endFrame": 83.0, "timeScale": 1.0, @@ -654,7 +654,7 @@ "id": "away", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx", "startFrame": 83.0, "endFrame": 84.0, "timeScale": 1.0, @@ -666,7 +666,7 @@ "id": "awayOutro", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx", "startFrame": 84.0, "endFrame": 167.0, "timeScale": 1.0, @@ -678,7 +678,7 @@ "id": "fly", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/fly.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/fly.fbx", "startFrame": 1.0, "endFrame": 80.0, "timeScale": 1.0, @@ -700,7 +700,7 @@ "id": "userAnimA", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx", "startFrame": 0.0, "endFrame": 90.0, "timeScale": 1.0, @@ -712,7 +712,7 @@ "id": "userAnimB", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx", "startFrame": 0.0, "endFrame": 90.0, "timeScale": 1.0, diff --git a/interface/resources/qml/TextOverlayElement.qml b/interface/resources/qml/TextOverlayElement.qml deleted file mode 100644 index 0d9dd1eacc..0000000000 --- a/interface/resources/qml/TextOverlayElement.qml +++ /dev/null @@ -1,23 +0,0 @@ -import Hifi 1.0 -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -TextOverlayElement { - id: root - Rectangle { - color: root.backgroundColor - anchors.fill: parent - Text { - x: root.leftMargin - y: root.topMargin - id: text - objectName: "textElement" - text: root.text - color: root.textColor - font.family: root.fontFamily - font.pixelSize: root.fontSize - lineHeightMode: Text.FixedHeight - lineHeight: root.lineHeight - } - } -} diff --git a/interface/resources/qml/controls/ComboBox.qml b/interface/resources/qml/controls/ComboBox.qml index e22bc8e664..960ae8127a 100644 --- a/interface/resources/qml/controls/ComboBox.qml +++ b/interface/resources/qml/controls/ComboBox.qml @@ -91,8 +91,10 @@ FocusScope { id: popup parent: desktop anchors.fill: parent + z: desktop.zLevels.menu visible: false focus: true + MouseArea { anchors.fill: parent onClicked: hideList(); @@ -116,7 +118,7 @@ FocusScope { ListView { id: listView - height: textView.height * count + height: textField.height * count * 1.4 model: root.model highlight: Rectangle{ width: listView.currentItem ? listView.currentItem.width : 0 diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 8c4f73c200..79babcb87d 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -25,7 +25,7 @@ FocusScope { readonly property alias zLevels: zLevels QtObject { id: zLevels; - readonly property real normal: 0 + readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls. readonly property real top: 2000 readonly property real modal: 4000 readonly property real menu: 8000 @@ -252,11 +252,17 @@ FocusScope { return messageDialogBuilder.createObject(desktop, properties); } - Component { id: queryDialogBuilder; QueryDialog { } } - function queryBox(properties) { - return queryDialogBuilder.createObject(desktop, properties); + Component { id: inputDialogBuilder; QueryDialog { } } + function inputDialog(properties) { + return inputDialogBuilder.createObject(desktop, properties); } + Component { id: fileDialogBuilder; FileDialog { } } + function fileOpenDialog(properties) { + return fileDialogBuilder.createObject(desktop, properties); + } + + MenuMouseHandler { id: menuPopperUpper } function popupMenu(point) { menuPopperUpper.popup(desktop, rootMenu.items, point); diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 9d2278f460..353e2df494 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -12,6 +12,27 @@ import "fileDialog" //FIXME implement shortcuts for favorite location ModalWindow { id: root + resizable: true + width: 640 + height: 480 + + Settings { + category: "FileDialog" + property alias width: root.width + property alias height: root.height + property alias x: root.x + property alias y: root.y + } + + + // Set from OffscreenUi::getOpenFile() + property alias caption: root.title; + // Set from OffscreenUi::getOpenFile() + property alias dir: model.folder; + // Set from OffscreenUi::getOpenFile() + property alias filter: selectionType.filtersString; + // Set from OffscreenUi::getOpenFile() + property int options; // <-- FIXME unused property bool selectDirectory: false; property bool showHidden: false; @@ -19,17 +40,11 @@ ModalWindow { property bool multiSelect: false; // FIXME implement property bool saveDialog: false; + property var helper: fileDialogHelper + property alias model: fileTableView.model signal selectedFile(var file); signal canceled(); - resizable: true - width: 640 - height: 480 - - property var helper: fileDialogHelper - property alias model: fileTableView.model - property alias filterModel: selectionType.model - property alias folder: model.folder Rectangle { anchors.fill: parent @@ -120,6 +135,7 @@ ModalWindow { onDoubleClicked: navigateToRow(row); model: FolderListModel { id: model + nameFilters: selectionType.currentFilter showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory @@ -157,12 +173,10 @@ ModalWindow { readOnly: true } - ComboBox { + FileTypeSelection { id: selectionType anchors { bottom: buttonRow.top; bottomMargin: 8; right: parent.right; rightMargin: 8; left: buttonRow.left } visible: !selectDirectory - model: ListModel { ListElement { text: "All Files (*.*)"; filter: "*.*" } } -// onCurrentIndexChanged: model.nameFilters = [ filterModel.get(currentIndex).filter ] KeyNavigation.left: fileTableView KeyNavigation.right: openButton } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 35efd4d0d1..ca00da1aa3 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls" import "../styles" import "../windows" +import "messageDialog" // FIXME respect default button functionality ModalWindow { @@ -126,126 +127,23 @@ ModalWindow { onHeightChanged: d.resize(); onWidthChanged: d.resize(); layoutDirection: Qt.RightToLeft anchors { bottom: details.top; right: parent.right; margins: d.spacing * 2; bottomMargin: 0 } - Button { - id: okButton - text: qsTr("OK") - focus: root.defaultButton === OriginalDialogs.StandardButton.Ok - onClicked: root.click(OriginalDialogs.StandardButton.Ok) - visible: root.buttons & OriginalDialogs.StandardButton.Ok - - } - Button { - id: openButton - text: qsTr("Open") - focus: root.defaultButton === OriginalDialogs.StandardButton.Open - onClicked: root.click(OriginalDialogs.StandardButton.Open) - visible: root.buttons & OriginalDialogs.StandardButton.Open - } - Button { - id: saveButton - text: qsTr("Save") - focus: root.defaultButton === OriginalDialogs.StandardButton.Save - onClicked: root.click(OriginalDialogs.StandardButton.Save) - visible: root.buttons & OriginalDialogs.StandardButton.Save - } - Button { - id: saveAllButton - text: qsTr("Save All") - focus: root.defaultButton === OriginalDialogs.StandardButton.SaveAll - onClicked: root.click(OriginalDialogs.StandardButton.SaveAll) - visible: root.buttons & OriginalDialogs.StandardButton.SaveAll - } - Button { - id: retryButton - text: qsTr("Retry") - focus: root.defaultButton === OriginalDialogs.StandardButton.Retry - onClicked: root.click(OriginalDialogs.StandardButton.Retry) - visible: root.buttons & OriginalDialogs.StandardButton.Retry - } - Button { - id: ignoreButton - text: qsTr("Ignore") - focus: root.defaultButton === OriginalDialogs.StandardButton.Ignore - onClicked: root.click(OriginalDialogs.StandardButton.Ignore) - visible: root.buttons & OriginalDialogs.StandardButton.Ignore - } - Button { - id: applyButton - text: qsTr("Apply") - focus: root.defaultButton === OriginalDialogs.StandardButton.Apply - onClicked: root.click(OriginalDialogs.StandardButton.Apply) - visible: root.buttons & OriginalDialogs.StandardButton.Apply - } - Button { - id: yesButton - text: qsTr("Yes") - focus: root.defaultButton === OriginalDialogs.StandardButton.Yes - onClicked: root.click(OriginalDialogs.StandardButton.Yes) - visible: root.buttons & OriginalDialogs.StandardButton.Yes - } - Button { - id: yesAllButton - text: qsTr("Yes to All") - focus: root.defaultButton === OriginalDialogs.StandardButton.YesToAll - onClicked: root.click(OriginalDialogs.StandardButton.YesToAll) - visible: root.buttons & OriginalDialogs.StandardButton.YesToAll - } - Button { - id: noButton - text: qsTr("No") - focus: root.defaultButton === OriginalDialogs.StandardButton.No - onClicked: root.click(OriginalDialogs.StandardButton.No) - visible: root.buttons & OriginalDialogs.StandardButton.No - } - Button { - id: noAllButton - text: qsTr("No to All") - focus: root.defaultButton === OriginalDialogs.StandardButton.NoToAll - onClicked: root.click(OriginalDialogs.StandardButton.NoToAll) - visible: root.buttons & OriginalDialogs.StandardButton.NoToAll - } - Button { - id: discardButton - text: qsTr("Discard") - focus: root.defaultButton === OriginalDialogs.StandardButton.Discard - onClicked: root.click(OriginalDialogs.StandardButton.Discard) - visible: root.buttons & OriginalDialogs.StandardButton.Discard - } - Button { - id: resetButton - text: qsTr("Reset") - focus: root.defaultButton === OriginalDialogs.StandardButton.Reset - onClicked: root.click(OriginalDialogs.StandardButton.Reset) - visible: root.buttons & OriginalDialogs.StandardButton.Reset - } - Button { - id: restoreDefaultsButton - text: qsTr("Restore Defaults") - focus: root.defaultButton === OriginalDialogs.StandardButton.RestoreDefaults - onClicked: root.click(OriginalDialogs.StandardButton.RestoreDefaults) - visible: root.buttons & OriginalDialogs.StandardButton.RestoreDefaults - } - Button { - id: cancelButton - text: qsTr("Cancel") - focus: root.defaultButton === OriginalDialogs.StandardButton.Cancel - onClicked: root.click(OriginalDialogs.StandardButton.Cancel) - visible: root.buttons & OriginalDialogs.StandardButton.Cancel - } - Button { - id: abortButton - text: qsTr("Abort") - focus: root.defaultButton === OriginalDialogs.StandardButton.Abort - onClicked: root.click(OriginalDialogs.StandardButton.Abort) - visible: root.buttons & OriginalDialogs.StandardButton.Abort - } - Button { - id: closeButton - text: qsTr("Close") - focus: root.defaultButton === OriginalDialogs.StandardButton.Close - onClicked: root.click(OriginalDialogs.StandardButton.Close) - visible: root.buttons & OriginalDialogs.StandardButton.Close - } + 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; } + MessageDialogButton { dialog: root; text: qsTr("Close"); button: OriginalDialogs.StandardButton.Close; } Button { id: moreButton text: qsTr("Show Details...") @@ -253,13 +151,7 @@ ModalWindow { } visible: detailedText && detailedText.length > 0 } - Button { - id: helpButton - text: qsTr("Help") - focus: root.defaultButton === OriginalDialogs.StandardButton.Help - onClicked: root.click(OriginalDialogs.StandardButton.Help) - visible: root.buttons & OriginalDialogs.StandardButton.Help - } + MessageDialogButton { dialog: root; text: qsTr("Help"); button: OriginalDialogs.StandardButton.Help; } } Item { @@ -328,7 +220,7 @@ ModalWindow { case Qt.Key_Enter: case Qt.Key_Return: event.accepted = true - root.click(OriginalDialogs.StandardButton.Ok) + root.click(root.defaultButton) break } } diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 52fde34271..159bb95b5d 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -16,9 +16,17 @@ ModalWindow { signal selected(var result); signal canceled(); - property alias result: textResult.text + property var items; + property alias label: mainTextContainer.text + property var result; + // FIXME not current honored + property var current; + + // For text boxes property alias placeholderText: textResult.placeholderText - property alias text: mainTextContainer.text + + // For combo boxes + property bool editable: true; Rectangle { clip: true @@ -55,10 +63,20 @@ ModalWindow { anchors { top: mainTextContainer.bottom; bottom: buttons.top; left: parent.left; right: parent.right; margins: d.spacing } // FIXME make a text field type that can be bound to a history for autocompletion TextField { - focus: true id: textResult + focus: items ? false : true + visible: items ? false : true anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter } } + + VrControls.ComboBox { + id: comboBox + focus: items ? true : false + visible: items ? true : false + anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter } + model: items ? items : [] + } + } Flow { @@ -86,6 +104,7 @@ ModalWindow { text: qsTr("OK") shortcut: Qt.Key_Return onTriggered: { + root.result = items ? comboBox.currentText : textResult.text root.selected(root.result); root.destroy(); } diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/dialogs/RunningScripts.qml index eb56b59b66..8b148c6bef 100644 --- a/interface/resources/qml/dialogs/RunningScripts.qml +++ b/interface/resources/qml/dialogs/RunningScripts.qml @@ -72,23 +72,6 @@ Window { scripts.stopAllScripts(); } - Component { - id: fileDialogBuilder - FileDialog { } - } - - function loadFromFile() { - var fileDialog = fileDialogBuilder.createObject(desktop, { filterModel: fileFilters }); - fileDialog.canceled.connect(function(){ - console.debug("Cancelled file open") - }) - - fileDialog.selectedFile.connect(function(file){ - console.debug("Selected " + file) - scripts.loadOneScript(file); - }) - } - Rectangle { color: "white" anchors.fill: parent @@ -123,26 +106,42 @@ Window { } ListView { + id: listView clip: true anchors { fill: parent; margins: 0 } model: runningScriptsModel delegate: Rectangle { + id: rectangle + clip: true radius: 3 anchors { left: parent.left; right: parent.right } - height: scriptName.height + 12 - color: index % 2 ? "#ddd" : "#eee" + height: scriptName.height + 12 + (ListView.isCurrentItem ? scriptName.height + 6 : 0) + color: ListView.isCurrentItem ? "#39f" : + index % 2 ? "#ddd" : "#eee" Text { - anchors { left: parent.left; leftMargin: 4; verticalCenter: parent.verticalCenter } 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 + } + Row { - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: scriptName.verticalCenter anchors.right: parent.right anchors.rightMargin: 4 spacing: 4 @@ -185,31 +184,38 @@ Window { anchors.bottomMargin: 8 anchors.right: parent.right - // For some reason trigginer an API that enters - // an internal event loop directly from the button clicked - // trigger below causes the appliction to behave oddly. - // Most likely because the button onClicked handling is never - // completed until the function returns. - // FIXME find a better way of handling the input dialogs that - // doesn't trigger this. - Timer { - id: asyncAction - interval: 50 - repeat: false - running: false - onTriggered: ApplicationInterface.loadScriptURLDialog(); - } Button { text: "from URL"; - onClicked: { - focus = false; - asyncAction.running = true; + onClicked: fromUrlTimer.running = true; + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + Timer { + id: fromUrlTimer + interval: 5 + repeat: false + running: false + onTriggered: ApplicationInterface.loadScriptURLDialog(); } } + Button { text: "from Disk" - onClicked: loadFromFile(); + onClicked: fromDiskTimer.running = true; + + Timer { + id: fromDiskTimer + interval: 5 + repeat: false + running: false + onTriggered: ApplicationInterface.loadDialog(); + } } } diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml new file mode 100644 index 0000000000..a3ab652e32 --- /dev/null +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -0,0 +1,27 @@ +import QtQuick 2.5 + +import "../../controls" as VrControls + +VrControls.ComboBox { + id: root + property string filtersString: "All Files (*.*)"; + property var currentFilter: [ "*.*" ]; + + // Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain + // multiple filters separated by semicolons + // ex: "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" + model: filtersString.split(';;'); + + enabled: model.length > 1 + + onCurrentTextChanged: { + var globRegex = /\((.*)\)$/ + var globs = globRegex.exec(currentText); + if (!globs[1]) { + console.warn("Unable to parse filter " + currentText); + return; + } + currentFilter = globs[1].split(" "); + } +} + diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml new file mode 100644 index 0000000000..2bcb3d1f29 --- /dev/null +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -0,0 +1,12 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 + +Button { + property var dialog; + property int button: StandardButton.NoButton; + + focus: dialog.defaultButton === button + onClicked: dialog.click(button) + visible: dialog.buttons & button +} diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml index 7af5dc851e..cdeddb5886 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml @@ -1,5 +1,5 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 +import QtQuick 2.5 +import QtQuick.Controls 1.4 import QtWebEngine 1.1 import "../../windows" as Windows @@ -11,9 +11,7 @@ Windows.Window { HifiConstants { id: hifi } width: 900; height: 700 resizable: true - anchors.centerIn: parent modality: Qt.ApplicationModal - frame: Windows.ModalFrame {} Item { anchors.fill: parent diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml new file mode 100644 index 0000000000..b10b66f07c --- /dev/null +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -0,0 +1,78 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +import "." + +Overlay { + id: root + + Image { + id: image + property bool scaleFix: true; + property real xOffset: 0 + property real yOffset: 0 + property real imageScale: 1.0 + property var resizer: Timer { + interval: 50 + repeat: false + running: false + onTriggered: { + var targetAspect = root.width / root.height; + var sourceAspect = image.sourceSize.width / image.sourceSize.height; + if (sourceAspect <= targetAspect) { + if (root.width === image.sourceSize.width) { + return; + } + image.imageScale = root.width / image.sourceSize.width; + } else if (sourceAspect > targetAspect){ + if (root.height === image.sourceSize.height) { + return; + } + image.imageScale = root.height / image.sourceSize.height; + } + image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); + } + } + x: -1 * xOffset * imageScale + y: -1 * yOffset * imageScale + + onSourceSizeChanged: { + if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { + scaleFix = false; + resizer.start(); + } + } + } + + function updateSubImage(subImage) { + var keys = Object.keys(subImage); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = subImage[key]; + switch (key) { + case "x": image.xOffset = value; break; + case "y": image.yOffset = value; break; + } + } + } + + function updatePropertiesFromScript(properties) { + var keys = Object.keys(properties); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = properties[key]; + switch (key) { + case "height": root.height = value; break; + case "width": root.width = value; break; + case "x": root.x = value; break; + case "y": root.y = value; break; + case "visible": root.visible = value; break; + case "alpha": root.opacity = value; break; + case "imageURL": image.source = value; break; + case "subImage": updateSubImage(value); break; + default: console.log("OVERLAY Unhandled image property " + key); + } + } + } +} + diff --git a/interface/resources/qml/hifi/overlays/Overlay.qml b/interface/resources/qml/hifi/overlays/Overlay.qml new file mode 100644 index 0000000000..80f3233b69 --- /dev/null +++ b/interface/resources/qml/hifi/overlays/Overlay.qml @@ -0,0 +1,30 @@ +import Hifi 1.0 +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Item { + id: root + clip: true + + property int dumpDepth: 0; + + + function dumpObject(object) { + var keys = Object.keys(object); + var tabsString = ""; + for (var j = 0; j < dumpDepth; ++j) { + tabsString = tabsString + "\t"; + } + + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = object[key]; + console.log(tabsString + "OVERLAY Key " + key + " (" + typeof(value) + "): " + value); + if (typeof(value) === "object") { + ++dumpDepth; + dumpObject(value) + --dumpDepth; + } + } + } +} diff --git a/interface/resources/qml/hifi/overlays/RectangleOverlay.qml b/interface/resources/qml/hifi/overlays/RectangleOverlay.qml new file mode 100644 index 0000000000..fc4169103a --- /dev/null +++ b/interface/resources/qml/hifi/overlays/RectangleOverlay.qml @@ -0,0 +1,38 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +import "." + +Overlay { + id: root + + Rectangle { + id: rectangle + anchors.fill: parent + color: "black" + } + + function updatePropertiesFromScript(properties) { + var keys = Object.keys(properties); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = properties[key]; + console.log("OVERLAY rectangle property " + key + " set to value " + value); + switch (key) { + case "height": root.height = value; break; + case "width": root.width = value; break; + case "x": root.x = value; break; + case "y": root.y = value; break; + case "visible": root.visible = value; break; + case "alpha": rectangle.color.a = value; break; + case "color": rectangle.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.color.a); break; + case "borderAlpha": rectangle.border.color.a = value; break; + case "borderColor": rectangle.border.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.border.color.a); break; + case "borderWidth": rectangle.border.width = value; break; + case "radius": rectangle.radius = value; break; + default: console.log("OVERLAY Unhandled rectangle property " + key); + } + } + } +} + diff --git a/interface/resources/qml/hifi/overlays/TextOverlay.qml b/interface/resources/qml/hifi/overlays/TextOverlay.qml new file mode 100644 index 0000000000..ba5cafdf64 --- /dev/null +++ b/interface/resources/qml/hifi/overlays/TextOverlay.qml @@ -0,0 +1,54 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +import "." + +Overlay { + id: root + clip: true + Rectangle { + id: background; + anchors.fill: parent + color: "#B2000000" + + Text { + id: textField; + anchors { fill: parent; bottomMargin: textField.anchors.topMargin; rightMargin: textField.anchors.leftMargin; } + objectName: "textElement" + color: "white" + lineHeightMode: Text.FixedHeight + font.family: "Helvetica" + font.pixelSize: 18 + lineHeight: 18 + } + } + + + function updatePropertiesFromScript(properties) { + var keys = Object.keys(properties); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = properties[key]; + switch (key) { + case "height": root.height = value; break; + case "width": root.width = value; break; + case "x": root.x = value; break; + case "y": root.y = value; break; + case "visible": root.visible = value; break; + case "alpha": textField.color.a = value; break; + case "margin": textField.anchors.margins = value; break; + case "leftMargin": textField.anchors.leftMargin = value; break; + case "topMargin": textField.anchors.topMargin = value; break; + case "color": // fall through + case "textColor": textField.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, textField.color.a); break; + case "text": textField.text = value; break; + case "backgroundAlpha": background.color = Qt.rgba(background.color.r, background.color.g, background.color.b, value); break; + case "backgroundColor": background.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, background.color.a); break; + case "font": textField.font.pixelSize = value.size; break; + case "lineHeight": textField.lineHeight = value; break; + default: + console.log("OVERLAY text unhandled property " + key); + } + } + } +} diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index fc54a1b1a2..505e653940 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -20,12 +20,8 @@ Frame { Row { id: controlsRow - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: iconSize - anchors.topMargin: iconSize / 2 + anchors { right: parent.right; top: parent.top; rightMargin: iconSize; topMargin: iconSize / 2; } spacing: iconSize / 4 - FontAwesome { visible: false text: "\uf08d" @@ -54,6 +50,18 @@ Frame { } } } + + Text { + id: titleText + anchors { left: parent.left; leftMargin: iconSize; right: controlsRow.left; rightMargin: iconSize; top: parent.top; topMargin: iconSize / 2; } + text: window.title + elide: Text.ElideRight + font.bold: true + color: window.focus ? "white" : "gray" + style: Text.Outline; + styleColor: "black" + } } + } diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml index cbcf1a77f6..3cbc31fce6 100644 --- a/interface/resources/qml/windows/Frame.qml +++ b/interface/resources/qml/windows/Frame.qml @@ -25,7 +25,7 @@ Item { id: debugZ visible: DebugQML text: window ? "Z: " + window.z : "" - y: -height + y: window.height + 4 } function deltaSize(dx, dy) { diff --git a/interface/resources/qml/windows/ModalFrame.qml b/interface/resources/qml/windows/ModalFrame.qml index 135a27c647..09dbf04b5c 100644 --- a/interface/resources/qml/windows/ModalFrame.qml +++ b/interface/resources/qml/windows/ModalFrame.qml @@ -13,5 +13,16 @@ Frame { color: "#7f7f7f7f"; radius: 3; } + + Text { + y: -implicitHeight - iconSize / 2 + text: window.title + elide: Text.ElideRight + font.bold: true + color: window.focus ? "white" : "gray" + style: Text.Outline; + styleColor: "black" + } + } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8e0d46bc81..14e0ea2a6d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -406,6 +406,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), + _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _scaleMirror(1.0f), _rotateMirror(0.0f), @@ -4501,14 +4502,24 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject } void Application::loadDialog() { - // To be migratd to QML - QString fileNameString = QFileDialog::getOpenFileName( - _glWidget, tr("Open Script"), "", tr("JavaScript Files (*.js)")); - if (!fileNameString.isEmpty()) { + auto scriptEngines = DependencyManager::get(); + QString fileNameString = OffscreenUi::getOpenFileName( + _glWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); + if (!fileNameString.isEmpty() && QFile(fileNameString).exists()) { + setPreviousScriptLocation(QFileInfo(fileNameString).absolutePath()); DependencyManager::get()->loadScript(fileNameString, true, false, false, true); // Don't load from cache } } +QString Application::getPreviousScriptLocation() { + QString result = _previousScriptLocation.get(); + return result; +} + +void Application::setPreviousScriptLocation(const QString& location) { + _previousScriptLocation.set(location); +} + void Application::loadScriptURLDialog() { auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL"); if (!newScript.isEmpty()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 71946497d5..e4bfba7492 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -103,6 +103,9 @@ public: void postLambdaEvent(std::function f) override; + QString getPreviousScriptLocation(); + void setPreviousScriptLocation(const QString& previousScriptLocation); + void initializeGL(); void initializeUi(); void paintGL(); @@ -431,6 +434,7 @@ private: Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; + Setting::Handle _previousScriptLocation; Setting::Handle _fieldOfView; float _scaleMirror; diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 896a50acca..0a885493e3 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -20,12 +20,14 @@ #include #include +#include #include "MainWindow.h" #include "Menu.h" #include "InterfaceLogging.h" #include "Bookmarks.h" +#include Bookmarks::Bookmarks() { _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME; @@ -104,18 +106,13 @@ void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { } void Bookmarks::bookmarkLocation() { - QInputDialog bookmarkLocationDialog(qApp->getWindow()); - bookmarkLocationDialog.setWindowTitle("Bookmark Location"); - bookmarkLocationDialog.setLabelText("Name:"); - bookmarkLocationDialog.setInputMode(QInputDialog::TextInput); - bookmarkLocationDialog.resize(400, 200); - - if (bookmarkLocationDialog.exec() == QDialog::Rejected) { + bool ok = false; + auto bookmarkName = OffscreenUi::getText(nullptr, "Bookmark Location", "Name:", QLineEdit::Normal, QString(), &ok); + if (!ok) { return; } - QString bookmarkName = bookmarkLocationDialog.textValue().trimmed(); - bookmarkName = bookmarkName.replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); if (bookmarkName.length() == 0) { return; } @@ -125,13 +122,14 @@ void Bookmarks::bookmarkLocation() { Menu* menubar = Menu::getInstance(); if (contains(bookmarkName)) { - QMessageBox duplicateBookmarkMessage; - duplicateBookmarkMessage.setIcon(QMessageBox::Warning); - duplicateBookmarkMessage.setText("The bookmark name you entered already exists in your list."); - duplicateBookmarkMessage.setInformativeText("Would you like to overwrite it?"); - duplicateBookmarkMessage.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - duplicateBookmarkMessage.setDefaultButton(QMessageBox::Yes); - if (duplicateBookmarkMessage.exec() == QMessageBox::No) { + auto offscreenUi = DependencyManager::get(); + auto duplicateBookmarkMessage = offscreenUi->createMessageBox(QMessageBox::Warning, "Duplicate Bookmark", + "The bookmark name you entered already exists in your list.", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?"); + + auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage); + if (result != QMessageBox::Yes) { return; } removeLocationFromMenu(menubar, bookmarkName); @@ -157,19 +155,13 @@ void Bookmarks::deleteBookmark() { bookmarkList.append(menuItems[i]->text()); } - QInputDialog deleteBookmarkDialog(qApp->getWindow()); - deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); - deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); - deleteBookmarkDialog.resize(400, 400); - deleteBookmarkDialog.setOption(QInputDialog::UseListViewForComboBoxItems); - deleteBookmarkDialog.setComboBoxItems(bookmarkList); - deleteBookmarkDialog.setOkButtonText("Delete"); - - if (deleteBookmarkDialog.exec() == QDialog::Rejected) { + bool ok = false; + auto bookmarkName = OffscreenUi::getItem(nullptr, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok); + if (!ok) { return; } - QString bookmarkName = deleteBookmarkDialog.textValue().trimmed(); + bookmarkName = bookmarkName.trimmed(); if (bookmarkName.length() == 0) { return; } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 15da88023b..a57e53b384 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "AddressManager.h" @@ -83,6 +84,17 @@ int main(int argc, const char* argv[]) { #endif } + // Check OpenGL version. + // This is done separately from the main Application so that start-up and shut-down logic within the main Application is + // not made more complicated than it already is. + { + OpenGLVersionChecker openGLVersionChecker(argc, const_cast(argv)); + if (!openGLVersionChecker.isValidVersion()) { + qCDebug(interfaceapp, "Early exit due to OpenGL version."); + return 0; + } + } + QElapsedTimer startupTime; startupTime.start(); @@ -96,6 +108,7 @@ int main(int argc, const char* argv[]) { usecTimestampNowForceClockSkew(clockSkew); qCDebug(interfaceapp, "clockSkewOption=%s clockSkew=%d", clockSkewOption, clockSkew); } + // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index a261c208d7..df66d1e3e1 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -191,10 +191,10 @@ bool ScriptEditorWidget::save() { bool ScriptEditorWidget::saveAs() { auto scriptEngines = DependencyManager::get(); QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), - scriptEngines->getPreviousScriptLocation(), + qApp->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!fileName.isEmpty()) { - scriptEngines->setPreviousScriptLocation(fileName); + qApp->setPreviousScriptLocation(fileName); return saveFile(fileName); } else { return false; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index 919c8b64d3..45a2f3f440 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -87,12 +87,11 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { } void ScriptEditorWindow::loadScriptClicked() { - auto scriptEngines = DependencyManager::get(); QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), - scriptEngines->getPreviousScriptLocation(), + qApp->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!scriptName.isEmpty()) { - scriptEngines->setPreviousScriptLocation(scriptName); + qApp->setPreviousScriptLocation(scriptName); addScriptEditorWidget("loading...")->loadFile(scriptName); updateButtons(); } @@ -100,7 +99,7 @@ void ScriptEditorWindow::loadScriptClicked() { void ScriptEditorWindow::loadMenuAboutToShow() { _loadMenu->clear(); - QStringList runningScripts = DependencyManager::get()->getRunningScripts();; + QStringList runningScripts = DependencyManager::get()->getRunningScripts(); if (runningScripts.count() > 0) { QSignalMapper* signalMapper = new QSignalMapper(this); foreach (const QString& runningScript, runningScripts) { diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 8df62b9675..4077fb0ab5 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -17,159 +17,15 @@ QString const ImageOverlay::TYPE = "image"; +QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml")); -ImageOverlay::ImageOverlay() : - _imageURL(), - _renderImage(false), - _wantClipFromImage(false) -{ -} +ImageOverlay::ImageOverlay() + : QmlOverlay(URL) { } ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : - Overlay2D(imageOverlay), - _imageURL(imageOverlay->_imageURL), - _textureImage(imageOverlay->_textureImage), - _texture(imageOverlay->_texture), - _fromImage(imageOverlay->_fromImage), - _renderImage(imageOverlay->_renderImage), - _wantClipFromImage(imageOverlay->_wantClipFromImage) -{ -} + QmlOverlay(URL, imageOverlay) { } -// TODO: handle setting image multiple times, how do we manage releasing the bound texture? -void ImageOverlay::setImageURL(const QUrl& url) { - _imageURL = url; - if (url.isEmpty()) { - _isLoaded = true; - _renderImage = false; - _texture.clear(); - } else { - _isLoaded = false; - _renderImage = true; - } -} - -void ImageOverlay::render(RenderArgs* args) { - if (!_isLoaded && _renderImage) { - _isLoaded = true; - _texture = DependencyManager::get()->getTexture(_imageURL); - } - // If we are not visible or loaded, return. If we are trying to render an - // image but the texture hasn't loaded, return. - if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) { - return; - } - - auto geometryCache = DependencyManager::get(); - gpu::Batch& batch = *args->_batch; - geometryCache->useSimpleDrawPipeline(batch); - if (_renderImage) { - batch.setResourceTexture(0, _texture->getGPUTexture()); - } else { - batch.setResourceTexture(0, args->_whiteTexture); - } - - const float MAX_COLOR = 255.0f; - xColor color = getColor(); - float alpha = getAlpha(); - glm::vec4 quadColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - - int left = _bounds.left(); - int right = _bounds.right() + 1; - int top = _bounds.top(); - int bottom = _bounds.bottom() + 1; - - glm::vec2 topLeft(left, top); - glm::vec2 bottomRight(right, bottom); - - batch.setModelTransform(Transform()); - - // if for some reason our image is not over 0 width or height, don't attempt to render the image - if (_renderImage) { - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); - if (imageWidth > 0 && imageHeight > 0) { - QRect fromImage; - if (_wantClipFromImage) { - float scaleX = imageWidth / _texture->getOriginalWidth(); - float scaleY = imageHeight / _texture->getOriginalHeight(); - - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setY(scaleY * _fromImage.y()); - fromImage.setWidth(scaleX * _fromImage.width()); - fromImage.setHeight(scaleY * _fromImage.height()); - } - else { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } - - float x = fromImage.x() / imageWidth; - float y = fromImage.y() / imageHeight; - float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure - float h = fromImage.height() / imageHeight; - - glm::vec2 texCoordTopLeft(x, y); - glm::vec2 texCoordBottomRight(x + w, y + h); - glm::vec4 texcoordRect(texCoordTopLeft, w, h); - - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); - } else { - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor); - } - } else { - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor); - } -} - -void ImageOverlay::setProperties(const QScriptValue& properties) { - Overlay2D::setProperties(properties); - - QScriptValue subImageBounds = properties.property("subImage"); - if (subImageBounds.isValid()) { - QRect oldSubImageRect = _fromImage; - QRect subImageRect = _fromImage; - if (subImageBounds.property("x").isValid()) { - subImageRect.setX(subImageBounds.property("x").toVariant().toInt()); - } else { - subImageRect.setX(oldSubImageRect.x()); - } - if (subImageBounds.property("y").isValid()) { - subImageRect.setY(subImageBounds.property("y").toVariant().toInt()); - } else { - subImageRect.setY(oldSubImageRect.y()); - } - if (subImageBounds.property("width").isValid()) { - subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt()); - } else { - subImageRect.setWidth(oldSubImageRect.width()); - } - if (subImageBounds.property("height").isValid()) { - subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt()); - } else { - subImageRect.setHeight(oldSubImageRect.height()); - } - setClipFromSource(subImageRect); - } - - QScriptValue imageURL = properties.property("imageURL"); - if (imageURL.isValid()) { - setImageURL(imageURL.toVariant().toString()); - } -} - -QScriptValue ImageOverlay::getProperty(const QString& property) { - if (property == "subImage") { - return qRectToScriptValue(_scriptEngine, _fromImage); - } - if (property == "imageURL") { - return _imageURL.toString(); - } - - return Overlay2D::getProperty(property); -} ImageOverlay* ImageOverlay::createClone() const { return new ImageOverlay(this); } + diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 5698a73732..224cb42045 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -11,48 +11,22 @@ #ifndef hifi_ImageOverlay_h #define hifi_ImageOverlay_h -// include this before QGLWidget, which includes an earlier version of OpenGL -#include -#include #include -#include +#include "QmlOverlay.h" -#include "Overlay2D.h" - -class ImageOverlay : public Overlay2D { +class ImageOverlay : public QmlOverlay { Q_OBJECT public: static QString const TYPE; virtual QString getType() const { return TYPE; } + static QUrl const URL; ImageOverlay(); ImageOverlay(const ImageOverlay* imageOverlay); - virtual void render(RenderArgs* args); - - // getters - const QRect& getClipFromSource() const { return _fromImage; } - const QUrl& getImageURL() const { return _imageURL; } - - // setters - void setClipFromSource(const QRect& bounds) { _fromImage = bounds; _wantClipFromImage = true; } - void setImageURL(const QUrl& url); - virtual void setProperties(const QScriptValue& properties); - virtual QScriptValue getProperty(const QString& property); - - virtual ImageOverlay* createClone() const; - -private: - - QUrl _imageURL; - QImage _textureImage; - - NetworkTexturePointer _texture; - QRect _fromImage; // where from in the image to sample - bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle - bool _wantClipFromImage; + virtual ImageOverlay* createClone() const override; }; diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 9a19c71db6..4d3b5ae36f 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -39,6 +39,7 @@ public: virtual void render(RenderArgs* args) = 0; virtual AABox getBounds() const = 0; + virtual bool supportsGetProperty() const { return true; } virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index f6e6851c38..10298b8f15 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -30,6 +30,7 @@ #include "Sphere3DOverlay.h" #include "Grid3DOverlay.h" #include "TextOverlay.h" +#include "RectangleOverlay.h" #include "Text3DOverlay.h" #include "Web3DOverlay.h" #include @@ -175,6 +176,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = std::make_shared(); } else if (type == Web3DOverlay::TYPE) { thisOverlay = std::make_shared(); + } else if (type == RectangleOverlay::TYPE) { + thisOverlay = std::make_shared(); } if (thisOverlay) { @@ -373,7 +376,7 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop OverlayPropertyResult result; Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); - if (thisOverlay) { + if (thisOverlay && thisOverlay->supportsGetProperty()) { result.value = thisOverlay->getProperty(property); } return result; diff --git a/interface/src/ui/overlays/QmlOverlay.cpp b/interface/src/ui/overlays/QmlOverlay.cpp new file mode 100644 index 0000000000..e141d38f2b --- /dev/null +++ b/interface/src/ui/overlays/QmlOverlay.cpp @@ -0,0 +1,74 @@ +// +// Created by Bradley Austin Davis on 2016/01/27 +// Copyright 2013-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 +// + +#include "QmlOverlay.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Application.h" +#include "text/FontFamilies.h" + +QmlOverlay::QmlOverlay(const QUrl& url) { + buildQmlElement(url); +} + +QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay) + : Overlay2D(textOverlay) { + buildQmlElement(url); +} + +void QmlOverlay::buildQmlElement(const QUrl& url) { + auto offscreenUi = DependencyManager::get(); + offscreenUi->returnFromUiThread([=] { + offscreenUi->load(url, [=](QQmlContext* context, QObject* object) { + _qmlElement = dynamic_cast(object); + }); + while (!_qmlElement) { + qApp->processEvents(); + } + return QVariant(); + }); +} + +QmlOverlay::~QmlOverlay() { + if (_qmlElement) { + _qmlElement->deleteLater(); + _qmlElement = nullptr; + } +} + +void QmlOverlay::setProperties(const QScriptValue& properties) { + Overlay2D::setProperties(properties); + auto bounds = _bounds; + DependencyManager::get()->executeOnUiThread([=] { + _qmlElement->setX(bounds.left()); + _qmlElement->setY(bounds.top()); + _qmlElement->setWidth(bounds.width()); + _qmlElement->setHeight(bounds.height()); + }); + QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Q_ARG(QVariant, properties.toVariant())); +} + +void QmlOverlay::render(RenderArgs* args) { + if (!_qmlElement) { + return; + } + + if (_visible != _qmlElement->isVisible()) { + _qmlElement->setVisible(_visible); + } +} diff --git a/interface/src/ui/overlays/QmlOverlay.h b/interface/src/ui/overlays/QmlOverlay.h new file mode 100644 index 0000000000..75f1a391a3 --- /dev/null +++ b/interface/src/ui/overlays/QmlOverlay.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis on 2016/01/27 +// Copyright 2013-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 +// + +#ifndef hifi_QmlOverlay_h +#define hifi_QmlOverlay_h + +#include + +#include +#include "Overlay2D.h" + +class QQuickItem; + +class QmlOverlay : public Overlay2D { + Q_OBJECT + +public: + QmlOverlay(const QUrl& url); + QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay); + ~QmlOverlay(); + + // Cannot fetch properties from QML based overlays due to race conditions + bool supportsGetProperty() const override { return false; } + + void setProperties(const QScriptValue& properties) override; + void render(RenderArgs* args) override; + +private: + void buildQmlElement(const QUrl& url); + +protected: + QQuickItem* _qmlElement{ nullptr }; +}; + + +#endif // hifi_QmlOverlay_h diff --git a/interface/src/ui/overlays/RectangleOverlay.cpp b/interface/src/ui/overlays/RectangleOverlay.cpp new file mode 100644 index 0000000000..1487a4cb63 --- /dev/null +++ b/interface/src/ui/overlays/RectangleOverlay.cpp @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis on 2016/01/27 +// Copyright 2013-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 +// + +#include "RectangleOverlay.h" + +QString const RectangleOverlay::TYPE = "rectangle"; +QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml")); + +RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {} + +RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay) + : QmlOverlay(URL, rectangleOverlay) { } + +RectangleOverlay* RectangleOverlay::createClone() const { + return new RectangleOverlay(this); +} diff --git a/interface/src/ui/overlays/RectangleOverlay.h b/interface/src/ui/overlays/RectangleOverlay.h new file mode 100644 index 0000000000..06e2fb228c --- /dev/null +++ b/interface/src/ui/overlays/RectangleOverlay.h @@ -0,0 +1,27 @@ +// +// Created by Bradley Austin Davis on 2016/01/27 +// Copyright 2013-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 +// + +#ifndef hifi_RectangleOverlay_h +#define hifi_RectangleOverlay_h + +#include "QmlOverlay.h" + +class RectangleOverlay : public QmlOverlay { +public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + static QUrl const URL; + + RectangleOverlay(); + RectangleOverlay(const RectangleOverlay* RectangleOverlay); + + virtual RectangleOverlay* createClone() const; +}; + + +#endif // hifi_RectangleOverlay_h diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 6926e9b241..762d855a11 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -24,216 +24,21 @@ #include "Application.h" #include "text/FontFamilies.h" -#define TEXT_OVERLAY_PROPERTY(type, name, initialValue) \ - Q_PROPERTY(type name READ name WRITE set##name NOTIFY name##Changed) \ -public: \ - type name() { return _##name; }; \ - void set##name(const type& name) { \ - if (name != _##name) { \ - _##name = name; \ - emit name##Changed(); \ - } \ - } \ -private: \ - type _##name{ initialValue }; - - -class TextOverlayElement : public QQuickItem { - Q_OBJECT - HIFI_QML_DECL -private: - TEXT_OVERLAY_PROPERTY(QString, text, "") - TEXT_OVERLAY_PROPERTY(QString, fontFamily, SANS_FONT_FAMILY) - TEXT_OVERLAY_PROPERTY(QString, textColor, "#ffffffff") - TEXT_OVERLAY_PROPERTY(QString, backgroundColor, "#B2000000") - TEXT_OVERLAY_PROPERTY(qreal, fontSize, 18) - TEXT_OVERLAY_PROPERTY(qreal, lineHeight, 18) - TEXT_OVERLAY_PROPERTY(qreal, leftMargin, 0) - TEXT_OVERLAY_PROPERTY(qreal, topMargin, 0) - -public: - TextOverlayElement(QQuickItem* parent = nullptr) : QQuickItem(parent) { - } - -signals: - void textChanged(); - void fontFamilyChanged(); - void fontSizeChanged(); - void lineHeightChanged(); - void leftMarginChanged(); - void topMarginChanged(); - void textColorChanged(); - void backgroundColorChanged(); -}; - -HIFI_QML_DEF(TextOverlayElement) - -QString toQmlColor(const glm::vec4& v) { - QString templat("#%1%2%3%4"); - return templat. - arg((int)(v.a * 255), 2, 16, QChar('0')). - arg((int)(v.r * 255), 2, 16, QChar('0')). - arg((int)(v.g * 255), 2, 16, QChar('0')). - arg((int)(v.b * 255), 2, 16, QChar('0')); -} - QString const TextOverlay::TYPE = "text"; +QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml")); -TextOverlay::TextOverlay() : - _backgroundColor(DEFAULT_BACKGROUND_COLOR), - _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), - _leftMargin(DEFAULT_MARGIN), - _topMargin(DEFAULT_MARGIN), - _fontSize(DEFAULT_FONTSIZE) -{ - qApp->postLambdaEvent([=] { - static std::once_flag once; - std::call_once(once, [] { - TextOverlayElement::registerType(); - }); - auto offscreenUi = DependencyManager::get(); - TextOverlayElement::show([=](QQmlContext* context, QObject* object) { - _qmlElement = static_cast(object); - }); - }); - while (!_qmlElement) { - QThread::msleep(1); - } +TextOverlay::TextOverlay() : QmlOverlay(URL) { } + +TextOverlay::TextOverlay(const TextOverlay* textOverlay) + : QmlOverlay(URL, textOverlay) { } -TextOverlay::TextOverlay(const TextOverlay* textOverlay) : - Overlay2D(textOverlay), - _text(textOverlay->_text), - _backgroundColor(textOverlay->_backgroundColor), - _backgroundAlpha(textOverlay->_backgroundAlpha), - _leftMargin(textOverlay->_leftMargin), - _topMargin(textOverlay->_topMargin), - _fontSize(textOverlay->_fontSize) -{ - qApp->postLambdaEvent([=] { - auto offscreenUi = DependencyManager::get(); - TextOverlayElement::show([this](QQmlContext* context, QObject* object) { - _qmlElement = static_cast(object); - }); - }); - while (!_qmlElement) { - QThread::msleep(1); - } -} - -TextOverlay::~TextOverlay() { - if (_qmlElement) { - _qmlElement->deleteLater(); - } -} - -xColor TextOverlay::getBackgroundColor() { - if (_colorPulse == 0.0f) { - return _backgroundColor; - } - - float pulseLevel = updatePulse(); - xColor result = _backgroundColor; - if (_colorPulse < 0.0f) { - result.red *= (1.0f - pulseLevel); - result.green *= (1.0f - pulseLevel); - result.blue *= (1.0f - pulseLevel); - } else { - result.red *= pulseLevel; - result.green *= pulseLevel; - result.blue *= pulseLevel; - } - return result; -} - -void TextOverlay::render(RenderArgs* args) { - if (!_qmlElement) { - return; - } - if (_visible != _qmlElement->isVisible()) { - _qmlElement->setVisible(_visible); - } -} - - -void TextOverlay::setProperties(const QScriptValue& properties) { - Overlay2D::setProperties(properties); - _qmlElement->setX(_bounds.left()); - _qmlElement->setY(_bounds.top()); - _qmlElement->setWidth(_bounds.width()); - _qmlElement->setHeight(_bounds.height()); - _qmlElement->settextColor(toQmlColor(vec4(toGlm(_color), _alpha))); - QScriptValue font = properties.property("font"); - if (font.isObject()) { - if (font.property("size").isValid()) { - setFontSize(font.property("size").toInt32()); - } - QFont font(_qmlElement->fontFamily()); - font.setPixelSize(_qmlElement->fontSize()); - QFontMetrics fm(font); - _qmlElement->setlineHeight(fm.lineSpacing() * 1.2); - } - - QScriptValue text = properties.property("text"); - if (text.isValid()) { - setText(text.toVariant().toString()); - } - - QScriptValue backgroundColor = properties.property("backgroundColor"); - if (backgroundColor.isValid()) { - QScriptValue red = backgroundColor.property("red"); - QScriptValue green = backgroundColor.property("green"); - QScriptValue blue = backgroundColor.property("blue"); - if (red.isValid() && green.isValid() && blue.isValid()) { - _backgroundColor.red = red.toVariant().toInt(); - _backgroundColor.green = green.toVariant().toInt(); - _backgroundColor.blue = blue.toVariant().toInt(); - } - } - - if (properties.property("backgroundAlpha").isValid()) { - _backgroundAlpha = properties.property("backgroundAlpha").toVariant().toFloat(); - } - _qmlElement->setbackgroundColor(toQmlColor(vec4(toGlm(_backgroundColor), _backgroundAlpha))); - - if (properties.property("leftMargin").isValid()) { - setLeftMargin(properties.property("leftMargin").toVariant().toInt()); - } - - if (properties.property("topMargin").isValid()) { - setTopMargin(properties.property("topMargin").toVariant().toInt()); - } -} +TextOverlay::~TextOverlay() { } TextOverlay* TextOverlay::createClone() const { return new TextOverlay(this); } -QScriptValue TextOverlay::getProperty(const QString& property) { - if (property == "font") { - QScriptValue font = _scriptEngine->newObject(); - font.setProperty("size", _fontSize); - return font; - } - if (property == "text") { - return _text; - } - if (property == "backgroundColor") { - return xColorToScriptValue(_scriptEngine, _backgroundColor); - } - if (property == "backgroundAlpha") { - return _backgroundAlpha; - } - if (property == "leftMargin") { - return _leftMargin; - } - if (property == "topMargin") { - return _topMargin; - } - - return Overlay2D::getProperty(property); -} - QSizeF TextOverlay::textSize(const QString& text) const { int lines = 1; foreach(QChar c, text) { @@ -241,31 +46,15 @@ QSizeF TextOverlay::textSize(const QString& text) const { ++lines; } } - QFont font(_qmlElement->fontFamily()); - font.setPixelSize(_qmlElement->fontSize()); + QFont font(SANS_FONT_FAMILY); + font.setPixelSize(18); QFontMetrics fm(font); - QSizeF result = QSizeF(fm.width(text), _qmlElement->lineHeight() * lines); + QSizeF result = QSizeF(fm.width(text), 18 * lines); return result; } -void TextOverlay::setFontSize(int fontSize) { - _fontSize = fontSize; - _qmlElement->setfontSize(fontSize); -} -void TextOverlay::setText(const QString& text) { - _text = text; - _qmlElement->settext(text); -} - -void TextOverlay::setLeftMargin(int margin) { - _leftMargin = margin; - _qmlElement->setleftMargin(margin); -} - -void TextOverlay::setTopMargin(int margin) { - _topMargin = margin; - _qmlElement->settopMargin(margin); -} - -#include "TextOverlay.moc" +void TextOverlay::setTopMargin(float margin) {} +void TextOverlay::setLeftMargin(float margin) {} +void TextOverlay::setFontSize(float size) {} +void TextOverlay::setText(const QString& text) {} diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index a8e6967871..7c6e133ebd 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -11,59 +11,27 @@ #ifndef hifi_TextOverlay_h #define hifi_TextOverlay_h -#include +#include "QmlOverlay.h" -#include - -#include "Overlay2D.h" - -const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; -const float DEFAULT_BACKGROUND_ALPHA = 0.7f; -const int DEFAULT_MARGIN = 10; -const int DEFAULT_FONTSIZE = 12; -const int DEFAULT_FONT_WEIGHT = 50; - -class TextOverlayElement; - -class TextOverlay : public Overlay2D { - Q_OBJECT - +class TextOverlay : public QmlOverlay { public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + QString getType() const override { return TYPE; } + static QUrl const URL; + TextOverlay(); TextOverlay(const TextOverlay* textOverlay); ~TextOverlay(); - virtual void render(RenderArgs* args); - // getters - const QString& getText() const { return _text; } - int getLeftMargin() const { return _leftMargin; } - int getTopMargin() const { return _topMargin; } - xColor getBackgroundColor(); - float getBackgroundAlpha() const { return _backgroundAlpha; } - - // setters + void setTopMargin(float margin); + void setLeftMargin(float margin); + void setFontSize(float size); void setText(const QString& text); - void setLeftMargin(int margin); - void setTopMargin(int margin); - void setFontSize(int fontSize); - virtual void setProperties(const QScriptValue& properties); - virtual TextOverlay* createClone() const; - virtual QScriptValue getProperty(const QString& property); + TextOverlay* createClone() const; QSizeF textSize(const QString& text) const; // Pixels - -private: - TextOverlayElement* _qmlElement{ nullptr }; - QString _text; - xColor _backgroundColor; - float _backgroundAlpha; - int _leftMargin; - int _topMargin; - int _fontSize; }; diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 6c12c6aa15..d5fd3f15e7 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -29,7 +29,10 @@ namespace AudioConstants { const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample); const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / (float)AudioConstants::SAMPLE_RATE) * 1000.0f; + + // be careful with overflows when using this constant const int NETWORK_FRAME_USECS = static_cast(NETWORK_FRAME_MSECS * 1000.0f); + const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); } diff --git a/libraries/audio/src/AudioEditBuffer.h b/libraries/audio/src/AudioEditBuffer.h deleted file mode 100644 index 66639f20bb..0000000000 --- a/libraries/audio/src/AudioEditBuffer.h +++ /dev/null @@ -1,112 +0,0 @@ -// -// AudioEditBuffer.h -// hifi -// -// Created by Craig Hansen-Sturm on 8/29/14. -// Copyright 2014 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 -// - -#ifndef hifi_AudioEditBuffer_h -#define hifi_AudioEditBuffer_h - -template< typename T > -class AudioEditBuffer : public AudioFrameBuffer { - -public: - - AudioEditBuffer(); - AudioEditBuffer(const uint32_t channelCount, const uint32_t frameCount); - ~AudioEditBuffer(); - - bool getZeroCrossing(uint32_t start, bool direction, float32_t epsilon, uint32_t& zero); - - void linearFade(uint32_t start, uint32_t stop, bool increasing); - void exponentialFade(uint32_t start, uint32_t stop, bool increasing); -}; - -template< typename T > -AudioEditBuffer::AudioEditBuffer() : - AudioFrameBuffer() { -} - -template< typename T > -AudioEditBuffer::AudioEditBuffer(const uint32_t channelCount, const uint32_t frameCount) : - AudioFrameBuffer(channelCount, frameCount) { -} - -template< typename T > - AudioEditBuffer::~AudioEditBuffer() { -} - -template< typename T > -inline bool AudioEditBuffer::getZeroCrossing(uint32_t start, bool direction, float32_t epsilon, uint32_t& zero) { - - zero = this->_frameCount; - - if (direction) { // scan from the left - if (start < this->_frameCount) { - for (uint32_t i = start; i < this->_frameCount; ++i) { - for (uint32_t j = 0; j < this->_channelCount; ++j) { - if (this->_frameBuffer[j][i] >= -epsilon && this->_frameBuffer[j][i] <= epsilon) { - zero = i; - return true; - } - } - } - } - } else { // scan from the right - if (start != 0 && start < this->_frameCount) { - for (uint32_t i = start; i != 0; --i) { - for (uint32_t j = 0; j < this->_channelCount; ++j) { - if (this->_frameBuffer[j][i] >= -epsilon && this->_frameBuffer[j][i] <= epsilon) { - zero = i; - return true; - } - } - } - } - } - - return false; -} - -template< typename T > -inline void AudioEditBuffer::linearFade(uint32_t start, uint32_t stop, bool increasing) { - - if (start >= stop || start > this->_frameCount || stop > this->_frameCount ) { - return; - } - - uint32_t count = stop - start; - float32_t delta; - float32_t gain; - - if (increasing) { // 0.0 to 1.0f in delta increments - delta = 1.0f / (float32_t)count; - gain = 0.0f; - } else { // 1.0f to 0.0f in delta increments - delta = -1.0f / (float32_t)count; - gain = 1.0f; - } - - for (uint32_t i = start; i < stop; ++i) { - for (uint32_t j = 0; j < this->_channelCount; ++j) { - this->_frameBuffer[j][i] *= gain; - } - gain += delta; - } -} - -template< typename T > -inline void AudioEditBuffer::exponentialFade(uint32_t start, uint32_t stop, bool increasing) { - // TBD -} - -typedef AudioEditBuffer< float32_t > AudioEditBufferFloat32; -typedef AudioEditBuffer< int32_t > AudioEditBufferSInt32; - -#endif // hifi_AudioEditBuffer_h - diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 217b8b0d53..157e973f5b 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -102,6 +102,11 @@ void AudioInjector::restart() { // reset the current send offset to zero _currentSendOffset = 0; + // reset state to start sending from beginning again + _nextFrame = 0; + _frameTimer->invalidate(); + _hasSentFirstFrame = false; + // check our state to decide if we need extra handling for the restart request if (_state == State::Finished) { // we finished playing, need to reset state so we can get going again @@ -246,6 +251,11 @@ int64_t AudioInjector::injectNextFrame() { } } + if (!_frameTimer->isValid()) { + // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here + _frameTimer->restart(); + } + int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; if (!_options.loop) { // If we aren't looping, let's make sure we don't read past the end @@ -314,14 +324,16 @@ int64_t AudioInjector::injectNextFrame() { const int MAX_ALLOWED_FRAMES_TO_FALL_BEHIND = 7; int64_t currentTime = _frameTimer->nsecsElapsed() / 1000; auto currentFrameBasedOnElapsedTime = currentTime / AudioConstants::NETWORK_FRAME_USECS; + if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) { // If we are falling behind by more frames than our threshold, let's skip the frames ahead - qDebug() << "AudioInjector::injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; + qDebug() << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; _nextFrame = currentFrameBasedOnElapsedTime; _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size(); } int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS; + return std::max(INT64_C(0), playNextFrameAt - currentTime); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index acfbc2b04a..79e2e645dd 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -99,7 +99,7 @@ private: AbstractAudioInterface* _localAudioInterface { nullptr }; AudioInjectorLocalBuffer* _localBuffer { nullptr }; - int _nextFrame { 0 }; + int64_t _nextFrame { 0 }; std::unique_ptr _frameTimer { nullptr }; quint16 _outgoingSequenceNumber { 0 }; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index b91bddc553..032ad71145 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -79,6 +79,12 @@ void AudioInjectorManager::run() { if (_injectors.size() > 0) { // loop through the injectors in the map and send whatever frames need to go out auto front = _injectors.top(); + + // create an InjectorQueue to hold injectors to be queued + // this allows us to call processEvents even if a single injector wants to be re-queued immediately + std::vector heldInjectors; + heldInjectors.reserve(_injectors.size()); + while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { // either way we're popping this injector off - get a copy first auto injector = front.second; @@ -87,10 +93,10 @@ void AudioInjectorManager::run() { if (!injector.isNull()) { // this is an injector that's ready to go, have it send a frame now auto nextCallDelta = injector->injectNextFrame(); - + if (nextCallDelta >= 0 && !injector->isFinished()) { - // re-enqueue the injector with the correct timing - _injectors.emplace(usecTimestampNow() + nextCallDelta, injector); + // enqueue the injector with the correct timing in our holding queue + heldInjectors.emplace(heldInjectors.end(), usecTimestampNow() + nextCallDelta, injector); } } @@ -101,6 +107,12 @@ void AudioInjectorManager::run() { break; } } + + // if there are injectors in the holding queue, push them to our persistent queue now + while (!heldInjectors.empty()) { + _injectors.push(heldInjectors.back()); + heldInjectors.pop_back(); + } } } else { diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 12f63e0a12..ff80ba08a9 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -26,7 +26,6 @@ #include "AudioRingBuffer.h" #include "AudioFormat.h" #include "AudioBuffer.h" -#include "AudioEditBuffer.h" #include "AudioLogging.h" #include "Sound.h" @@ -69,7 +68,6 @@ void Sound::downloadFinished(const QByteArray& data) { interpretAsWav(rawAudioByteArray, outputAudioByteArray); downSample(outputAudioByteArray); - trimFrames(); } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw @@ -80,7 +78,6 @@ void Sound::downloadFinished(const QByteArray& data) { // Process as RAW file downSample(rawAudioByteArray); - trimFrames(); } else { qCDebug(audio) << "Unknown sound file type"; } @@ -98,11 +95,23 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample); - int numDestinationBytes = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample); - if (_isStereo && numSourceSamples % 2 != 0) { - numDestinationBytes += sizeof(AudioConstants::AudioSample); + if (_isStereo && numSourceSamples % 2 != 0){ + // in the unlikely case that we have stereo audio but we seem to be missing a sample + // (the sample for one channel is missing in a set of interleaved samples) + // then drop the odd sample + --numSourceSamples; } + int numDestinationSamples = numSourceSamples / 2.0f; + + if (_isStereo && numDestinationSamples % 2 != 0) { + // if this is stereo we need to make sure we produce stereo output + // which means we should have an even number of output samples + numDestinationSamples += 1; + } + + int numDestinationBytes = numDestinationSamples * sizeof(AudioConstants::AudioSample); + _byteArray.resize(numDestinationBytes); int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data(); @@ -129,26 +138,6 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { } } -void Sound::trimFrames() { - - const uint32_t inputFrameCount = _byteArray.size() / sizeof(int16_t); - const uint32_t trimCount = 1024; // number of leading and trailing frames to trim - - if (inputFrameCount <= (2 * trimCount)) { - return; - } - - int16_t* inputFrameData = (int16_t*)_byteArray.data(); - - AudioEditBufferFloat32 editBuffer(1, inputFrameCount); - editBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); - - editBuffer.linearFade(0, trimCount, true); - editBuffer.linearFade(inputFrameCount - trimCount, inputFrameCount, false); - - editBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); -} - // // Format description from https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ // diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 91dbef8c6a..6baf691957 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -38,7 +38,6 @@ private: bool _isStereo; bool _isReady; - void trimFrames(); void downSample(const QByteArray& rawAudioByteArray); void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index fe693d8c65..2e42e85122 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -19,6 +19,7 @@ #define GPU_LEGACY 0 #define GPU_CORE_41 410 #define GPU_CORE_43 430 +#define GPU_CORE_MINIMUM GPU_CORE_41 #if defined(__APPLE__) diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp new file mode 100644 index 0000000000..761c27a302 --- /dev/null +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -0,0 +1,61 @@ +// +// OpenGLVersionChecker.cpp +// libraries/gl/src/gl +// +// Created by David Rowe on 28 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 +// + +#include "OpenGLVersionChecker.h" + +#include +#include + +#include "Config.h" +#include "GLWidget.h" + +OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : + QApplication(argc, argv) +{ +} + +bool OpenGLVersionChecker::isValidVersion() { + bool valid = true; + + // Retrieve OpenGL version + GLWidget* glWidget = new GLWidget(); + glWidget->initializeGL(); + QString glVersion = QString((const char*)glGetString(GL_VERSION)); + delete glWidget; + + // Compare against minimum + // The GL_VERSION string begins with a version number in one of these forms: + // - major_number.minor_number + // - major_number.minor_number.release_number + // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml + QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); + int majorNumber = versionParts[0].toInt(); + int minorNumber = versionParts[1].toInt(); + int minimumMajorNumber = GPU_CORE_MINIMUM / 100; + int minimumMinorNumber = (GPU_CORE_MINIMUM - minimumMajorNumber * 100) / 10; + valid = (majorNumber > minimumMajorNumber + || (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber)); + + // Prompt user if below minimum + if (!valid) { + QMessageBox messageBox; + messageBox.setWindowTitle("OpenGL Version Too Low"); + messageBox.setIcon(QMessageBox::Warning); + messageBox.setText(QString().sprintf("Your OpenGL version of %i.%i is lower than the minimum of %i.%i.", + majorNumber, minorNumber, minimumMajorNumber, minimumMinorNumber)); + messageBox.setInformativeText("Press OK to exit; Ignore to continue."); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Ignore); + messageBox.setDefaultButton(QMessageBox::Ok); + valid = messageBox.exec() == QMessageBox::Ignore; + } + + return valid; +} diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.h b/libraries/gl/src/gl/OpenGLVersionChecker.h new file mode 100644 index 0000000000..3e16c3a32d --- /dev/null +++ b/libraries/gl/src/gl/OpenGLVersionChecker.h @@ -0,0 +1,25 @@ +// +// OpenGLVersionChecker.h +// libraries/gl/src/gl +// +// Created by David Rowe on 28 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 +// + +#ifndef hifi_OpenGLVersionChecker_h +#define hifi_OpenGLVersionChecker_h + +#include + +class OpenGLVersionChecker : public QApplication { + +public: + OpenGLVersionChecker(int& argc, char** argv); + + static bool isValidVersion(); +}; + +#endif // hifi_OpenGLVersionChecker_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b0cd8cce39..16277caace 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -490,6 +490,9 @@ void NodeList::processDomainServerList(QSharedPointer message) // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; + // emit our signal so listeners know we just heard from the DS + emit receivedDomainServerList(); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList); QDataStream packetStream(message->getMessage()); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 171ea0c6a7..4b196d5f7b 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -87,6 +87,7 @@ public slots: signals: void limitOfSilentDomainCheckInsReached(); + void receivedDomainServerList(); private slots: void stopKeepalivePingTimer(); void sendPendingDSPathQuery(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 303755c8f3..9277aa70cc 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -30,6 +30,10 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : connect(&_domainServerTimer, &QTimer::timeout, this, &ThreadedAssignment::checkInWithDomainServerOrExit); _domainServerTimer.setInterval(DOMAIN_SERVER_CHECK_IN_MSECS); + + // if the NL tells us we got a DS response, clear our member variable of queued check-ins + auto nodeList = DependencyManager::get(); + connect(nodeList.data(), &NodeList::receivedDomainServerList, this, &ThreadedAssignment::clearQueuedCheckIns); } void ThreadedAssignment::setFinished(bool isFinished) { @@ -103,11 +107,18 @@ void ThreadedAssignment::sendStatsPacket() { } void ThreadedAssignment::checkInWithDomainServerOrExit() { - if (DependencyManager::get()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + // verify that the number of queued check-ins is not >= our max + // the number of queued check-ins is cleared anytime we get a response from the domain-server + if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server" + << "Stopping the current assignment"; setFinished(true); } else { auto nodeList = DependencyManager::get(); QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn"); + + // increase the number of queued check ins + _numQueuedCheckIns++; } } diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 42d4903c2f..13b9b5bf79 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -33,6 +33,7 @@ public slots: virtual void run() = 0; Q_INVOKABLE virtual void stop() { setFinished(true); } virtual void sendStatsPacket(); + void clearQueuedCheckIns() { _numQueuedCheckIns = 0; } signals: void finished(); @@ -42,6 +43,7 @@ protected: bool _isFinished; QTimer _domainServerTimer; QTimer _statsTimer; + int _numQueuedCheckIns { 0 }; protected slots: void domainSettingsRequestFailed(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index eb46129777..d76d0a77de 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -27,6 +27,7 @@ #include "render/DrawStatus.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" +#include "ToneMappingEffect.h" #include "RenderDeferredTask.h" @@ -67,20 +68,6 @@ void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderCo DependencyManager::get()->render(renderContext); } -void ToneMappingDeferred::configure(const Config& config) { - if (config.exposure >= 0.0f) { - _toneMappingEffect.setExposure(config.exposure); - } - - if (config.curve >= 0) { - _toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve); - } -} - -void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - _toneMappingEffect.render(renderContext->args); -} - RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 414b019b31..8d773a9b21 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -16,8 +16,6 @@ #include "render/DrawTask.h" -#include "ToneMappingEffect.h" - class SetupDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); @@ -40,32 +38,6 @@ public: using JobModel = render::Job::Model; }; - -class ToneMappingConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled) - Q_PROPERTY(float exposure MEMBER exposure NOTIFY dirty); - Q_PROPERTY(int curve MEMBER curve NOTIFY dirty); -public: - ToneMappingConfig() : render::Job::Config(true) {} - - float exposure{ 0.0f }; - int curve{ 3 }; -signals: - void dirty(); -}; - -class ToneMappingDeferred { -public: - using Config = ToneMappingConfig; - using JobModel = render::Job::Model; - - void configure(const Config& config); - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - - ToneMappingEffect _toneMappingEffect; -}; - class DrawConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn) diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 55b7d41ab3..da11507965 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -145,4 +145,19 @@ void ToneMappingEffect::render(RenderArgs* args) { batch.setResourceTexture(ToneMappingEffect_LightingMapSlot, lightingBuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); }); -} \ No newline at end of file +} + + +void ToneMappingDeferred::configure(const Config& config) { + if (config.exposure >= 0.0f) { + _toneMappingEffect.setExposure(config.exposure); + } + + if (config.curve >= 0) { + _toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve); + } +} + +void ToneMappingDeferred::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { + _toneMappingEffect.render(renderContext->args); +} diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h index 20ee9024cf..3dd212c2dc 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -17,6 +17,8 @@ #include #include +#include + class RenderArgs; @@ -50,7 +52,7 @@ private: float _exposure = 0.0f; float _twoPowExposure = 1.0f; glm::vec2 spareA; - int _toneCurve = Filmic; + int _toneCurve = Gamma22; glm::vec3 spareB; Parameters() {} @@ -61,4 +63,33 @@ private: void init(); }; +class ToneMappingConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER enabled) + Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); + Q_PROPERTY(int curve MEMBER curve WRITE setCurve); +public: + ToneMappingConfig() : render::Job::Config(true) {} + + void setExposure(float newExposure) { exposure = std::max(0.0f, newExposure); emit dirty(); } + void setCurve(int newCurve) { curve = std::max((int)ToneMappingEffect::None, std::min((int)ToneMappingEffect::Filmic, newCurve)); emit dirty(); } + + + float exposure{ 0.0f }; + int curve{ ToneMappingEffect::Gamma22 }; +signals: + void dirty(); +}; + +class ToneMappingDeferred { +public: + using Config = ToneMappingConfig; + using JobModel = render::Job::Model; + + void configure(const Config& config); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + + ToneMappingEffect _toneMappingEffect; +}; + #endif // hifi_ToneMappingEffect_h diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 0a0b2de47e..6e8469a93f 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -34,8 +34,7 @@ ScriptsModel& getScriptsModel() { } ScriptEngines::ScriptEngines() - : _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), - _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION) + : _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION) { _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); @@ -445,14 +444,3 @@ void ScriptEngines::onScriptEngineError(const QString& scriptFilename) { qCDebug(scriptengine) << "Application::loadScript(), script failed to load..."; emit scriptLoadError(scriptFilename, ""); } - -QString ScriptEngines::getPreviousScriptLocation() const { - return _previousScriptLocation.get(); -} - -void ScriptEngines::setPreviousScriptLocation(const QString& previousScriptLocation) { - if (_previousScriptLocation.get() != previousScriptLocation) { - _previousScriptLocation.set(previousScriptLocation); - emit previousScriptLocationChanged(); - } -} diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 97afe9ac57..df60d6ff63 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -30,7 +30,6 @@ class ScriptEngines : public QObject, public Dependency { Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT) Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT) - Q_PROPERTY(QString previousScriptLocation READ getPreviousScriptLocation WRITE setPreviousScriptLocation NOTIFY previousScriptLocationChanged) public: using ScriptInitializer = std::function; @@ -48,9 +47,6 @@ public: QStringList getRunningScripts(); ScriptEngine* getScriptEngine(const QString& scriptHash); - QString getPreviousScriptLocation() const; - void setPreviousScriptLocation(const QString& previousScriptLocation); - ScriptsModel* scriptsModel() { return &_scriptsModel; }; ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; }; @@ -73,7 +69,6 @@ signals: void scriptCountChanged(); void scriptsReloading(); void scriptLoadError(const QString& filename, const QString& error); - void previousScriptLocationChanged(); protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); @@ -97,7 +92,6 @@ protected: std::atomic _stoppingAllScripts { false }; std::list _scriptInitializers; mutable Setting::Handle _scriptsLocationHandle; - mutable Setting::Handle _previousScriptLocation; ScriptsModel _scriptsModel; ScriptsModelFilter _scriptsModelFilter; }; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d7d28bef84..8e52507243 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -199,19 +199,7 @@ private slots: } }; -QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - if (QThread::currentThread() != thread()) { - QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton; - QMetaObject::invokeMethod(this, "messageBox", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QMessageBox::StandardButton, result), - Q_ARG(QMessageBox::Icon, icon), - Q_ARG(QString, title), - Q_ARG(QString, text), - Q_ARG(QMessageBox::StandardButtons, buttons), - Q_ARG(QMessageBox::StandardButton, defaultButton)); - return result; - } - +QQuickItem* OffscreenUi::createMessageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { QVariantMap map; map.insert("title", title); map.insert("text", text); @@ -225,12 +213,34 @@ QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, cons if (!invokeResult) { qWarning() << "Failed to create message box"; - return QMessageBox::StandardButton::NoButton; + return nullptr; + } + return qvariant_cast(result); +} + +QMessageBox::StandardButton OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) { + if (!messageBox) { + return QMessageBox::NoButton; } - QMessageBox::StandardButton resultButton = MessageBoxListener(qvariant_cast(result)).waitForButtonResult(); - qDebug() << "Message box got a result of " << resultButton; - return resultButton; + return MessageBoxListener(messageBox).waitForButtonResult(); +} + + +QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + if (QThread::currentThread() != thread()) { + QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton; + QMetaObject::invokeMethod(this, "messageBox", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QMessageBox::StandardButton, result), + Q_ARG(QMessageBox::Icon, icon), + Q_ARG(QString, title), + Q_ARG(QString, text), + Q_ARG(QMessageBox::StandardButtons, buttons), + Q_ARG(QMessageBox::StandardButton, defaultButton)); + return result; + } + + return waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton)); } QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text, @@ -273,6 +283,7 @@ private slots: // FIXME many input parameters currently ignored QString OffscreenUi::getText(void* ignored, const QString & title, const QString & label, QLineEdit::EchoMode mode, const QString & text, bool * ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints) { + if (ok) { *ok = false; } QVariant result = DependencyManager::get()->inputDialog(title, label, text).toString(); if (ok && result.isValid()) { *ok = true; @@ -280,35 +291,70 @@ QString OffscreenUi::getText(void* ignored, const QString & title, const QString return result.toString(); } +// FIXME many input parameters currently ignored +QString OffscreenUi::getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current, bool editable, bool * ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints) { + if (ok) { + *ok = false; + } -QVariant OffscreenUi::inputDialog(const QString& query, const QString& placeholderText, const QString& currentValue) { + auto offscreenUi = DependencyManager::get(); + auto inputDialog = offscreenUi->createInputDialog(title, label, current); + if (!inputDialog) { + return QString(); + } + inputDialog->setProperty("items", items); + inputDialog->setProperty("editable", editable); + + QVariant result = offscreenUi->waitForInputDialogResult(inputDialog); + if (!result.isValid()) { + return QString(); + } + + if (ok) { + *ok = true; + } + return result.toString(); +} + +QVariant OffscreenUi::inputDialog(const QString& title, const QString& label, const QVariant& current) { if (QThread::currentThread() != thread()) { QVariant result; - QMetaObject::invokeMethod(this, "queryBox", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(this, "inputDialog", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), - Q_ARG(QString, query), - Q_ARG(QString, placeholderText), - Q_ARG(QString, currentValue)); + Q_ARG(QString, title), + Q_ARG(QString, label), + Q_ARG(QVariant, current)); return result; } + return waitForInputDialogResult(createInputDialog(title, label, current)); +} + + +QQuickItem* OffscreenUi::createInputDialog(const QString& title, const QString& label, const QVariant& current) { QVariantMap map; - map.insert("text", query); - map.insert("placeholderText", placeholderText); - map.insert("result", currentValue); + map.insert("title", title); + map.insert("label", label); + map.insert("current", current); QVariant result; - bool invokeResult = QMetaObject::invokeMethod(_desktop, "queryBox", + bool invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, QVariant::fromValue(map))); if (!invokeResult) { qWarning() << "Failed to create message box"; - return QVariant(); + return nullptr; } - return InputDialogListener(qvariant_cast(result)).waitForResult(); + return qvariant_cast(result); } +QVariant OffscreenUi::waitForInputDialogResult(QQuickItem* inputDialog) { + if (!inputDialog) { + return QVariant(); + } + return InputDialogListener(inputDialog).waitForResult(); +} bool OffscreenUi::navigationFocused() { return offscreenFlags->isNavigationFocused(); @@ -412,4 +458,66 @@ void OffscreenUi::toggleMenu(const QPoint& screenPosition) { } +class FileDialogListener : public ModalDialogListener { + Q_OBJECT + + friend class OffscreenUi; + FileDialogListener(QQuickItem* messageBox) : ModalDialogListener(messageBox) { + if (_finished) { + return; + } + connect(_dialog, SIGNAL(selectedFile(QVariant)), this, SLOT(onSelectedFile(QVariant))); + } + +private slots: + void onSelectedFile(QVariant file) { + _result = file; + _finished = true; + disconnect(_dialog); + } +}; + +QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + QString result; + QMetaObject::invokeMethod(this, "fileOpenDialog", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return result; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + + QVariant buildDialogResult; + bool invokeResult = QMetaObject::invokeMethod(_desktop, "fileOpenDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(map))); + + if (!invokeResult) { + qWarning() << "Failed to create file open dialog"; + return QString(); + } + + QVariant result = FileDialogListener(qvariant_cast(buildDialogResult)).waitForResult(); + if (!result.isValid()) { + return QString(); + } + qDebug() << result.toString(); + return result.toUrl().toLocalFile(); +} + +QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); +} + + #include "OffscreenUi.moc" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 73307b38f7..ec5ba433cc 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -42,6 +42,13 @@ public: QQuickItem* getToolWindow(); + // Message box compatibility + Q_INVOKABLE QMessageBox::StandardButton messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); + // Must be called from the main thread + QQuickItem* createMessageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); + // Must be called from the main thread + QMessageBox::StandardButton waitForMessageBoxResult(QQuickItem* messageBox); + /// Same design as QMessageBox::critical(), will block, returns result static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, @@ -80,18 +87,21 @@ public: QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - Q_INVOKABLE QMessageBox::StandardButton messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); - Q_INVOKABLE QVariant inputDialog(const QString& query, const QString& placeholderText = QString(), const QString& currentValue = QString()); - - // FIXME implement - static QVariant query(const QString& query, const QString& placeholderText = QString(), const QString& currentValue = QString()); - - // FIXME implement + // file dialog compatibility + Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + + // input dialog compatibility + Q_INVOKABLE QVariant inputDialog(const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); + QQuickItem* createInputDialog(const QString& title, const QString& label, const QVariant& current); + QVariant waitForInputDialogResult(QQuickItem* inputDialog); + // Compatibility with QInputDialog::getText static QString getText(void* ignored, const QString & title, const QString & label, QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone); + // 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); private: QQuickItem* _desktop { nullptr }; diff --git a/tests/ui/qmlscratch.pro b/tests/ui/qmlscratch.pro index a4e4b33957..417d7dad5b 100644 --- a/tests/ui/qmlscratch.pro +++ b/tests/ui/qmlscratch.pro @@ -21,11 +21,13 @@ DISTFILES += \ ../../interface/resources/qml/dialogs/*.qml \ ../../interface/resources/qml/dialogs/fileDialog/*.qml \ ../../interface/resources/qml/dialogs/preferences/*.qml \ + ../../interface/resources/qml/dialogs/messageDialog/*.qml \ ../../interface/resources/qml/desktop/*.qml \ ../../interface/resources/qml/menus/*.qml \ ../../interface/resources/qml/styles/*.qml \ ../../interface/resources/qml/windows/*.qml \ ../../interface/resources/qml/hifi/*.qml \ ../../interface/resources/qml/hifi/dialogs/*.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/*.qml + ../../interface/resources/qml/hifi/dialogs/preferences/*.qml \ + ../../interface/resources/qml/hifi/overlays/*.qml