Cleanup of QML windowing and message boxes

This commit is contained in:
Brad Davis 2016-01-14 11:50:36 -08:00
parent bb02a1eda2
commit 746c388f7a
59 changed files with 3279 additions and 2893 deletions

View file

@ -12,37 +12,37 @@ import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
import "windows"
DialogContainer {
Window {
id: root
HifiConstants { id: hifi }
z: 1000
anchors.centerIn: parent
objectName: "AddressBarDialog"
property bool destroyOnInvisible: false
property real scale: 1.25 // Make this dialog a little larger than normal
destroyOnInvisible: false
resizable: false
scale: 1.25 // Make this dialog a little larger than normal
implicitWidth: addressBarDialog.implicitWidth
implicitHeight: addressBarDialog.implicitHeight
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property int maximumX: parent ? parent.width - width : 0
property int maximumY: parent ? parent.height - height : 0
Rectangle {
id: dragRegion
visible: dragMouseArea.containsMouse
}
width: addressBarDialog.implicitWidth
height: addressBarDialog.implicitHeight
AddressBarDialog {
id: addressBarDialog
z: dragRegion.z + 1
implicitWidth: backgroundImage.width
implicitHeight: backgroundImage.height
Timer {
running: root.visible
interval: 500
repeat: true
onTriggered: {
if (root.enabled && !addressLine.activeFocus) {
addressLine.forceActiveFocus()
}
}
}
Image {
id: backgroundImage
@ -52,45 +52,9 @@ DialogContainer {
property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area
property int inputAreaStep: (height - inputAreaHeight) / 2
MouseArea {
id: dragMouseArea
// Drag the icon
width: parent.height
height: parent.height
x: 0
y: 0
hoverEnabled: true
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}
MouseArea {
// Drag the input rectangle
width: parent.width - parent.height
height: parent.inputAreaHeight
x: parent.height
y: parent.inputAreaStep
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}
Image {
id: backArrow
source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/left-arrow-disabled.svg"
anchors {
fill: parent
leftMargin: parent.height + hifi.layout.spacing + 6
@ -110,9 +74,7 @@ DialogContainer {
Image {
id: forwardArrow
source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/right-arrow-disabled.svg"
anchors {
fill: parent
leftMargin: parent.height + hifi.layout.spacing * 9
@ -130,6 +92,7 @@ DialogContainer {
}
}
// FIXME replace with TextField
TextInput {
id: addressLine
@ -139,61 +102,35 @@ DialogContainer {
rightMargin: hifi.layout.spacing * 2
topMargin: parent.inputAreaStep + hifi.layout.spacing
bottomMargin: parent.inputAreaStep + hifi.layout.spacing
}
font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75
helperText: "Go to: place, @user, /path, network address"
onAccepted: {
event.accepted = true // Generates erroneous error in program log, "ReferenceError: event is not defined".
addressBarDialog.loadAddress(addressLine.text)
}
}
}
}
onEnabledChanged: {
if (enabled) {
addressLine.forceActiveFocus()
}
}
Timer {
running: root.enabled
interval: 500
repeat: true
onTriggered: {
if (root.enabled && !addressLine.activeFocus) {
addressLine.forceActiveFocus()
}
}
}
onVisibleChanged: {
if (!visible) {
if (visible) {
addressLine.forceActiveFocus()
} else {
addressLine.text = ""
}
}
function toggleOrGo() {
if (addressLine.text == "") {
enabled = false
} else {
if (addressLine.text != "") {
addressBarDialog.loadAddress(addressLine.text)
}
root.close();
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
if (enabled) {
enabled = false
event.accepted = true
}
root.close()
event.accepted = true
break
case Qt.Key_Enter:
case Qt.Key_Return:

View file

@ -12,23 +12,23 @@ Window {
title: "Browser"
resizable: true
destroyOnInvisible: true
width: 800
height: 600
Component.onCompleted: {
enabled = true
visible = true
addressBar.text = webview.url
}
onParentChanged: {
if (visible && enabled) {
if (visible) {
addressBar.forceActiveFocus();
addressBar.selectAll()
}
}
Item {
width: 800
height: 600
anchors.fill: parent
Rectangle {
anchors.left: parent.left
anchors.right: parent.right

View file

@ -1,112 +0,0 @@
//
// ErrorDialog.qml
//
// Created by David Rowe on 30 May 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
DialogContainer {
id: root
HifiConstants { id: hifi }
Component.onCompleted: {
enabled = true
}
onParentChanged: {
if (visible && enabled) {
forceActiveFocus()
}
}
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
ErrorDialog {
id: content
implicitWidth: box.width
implicitHeight: icon.height + hifi.layout.spacing * 2
Border {
id: box
width: 512
color: "#ebebeb"
radius: 2
border.width: 1
border.color: "#000000"
Image {
id: icon
source: "../images/address-bar-error-icon.svg"
width: 40
height: 40
anchors {
left: parent.left
leftMargin: hifi.layout.spacing
verticalCenter: parent.verticalCenter
}
}
Text {
id: messageText
font.pixelSize: hifi.fonts.pixelSize * 0.6
font.weight: Font.Bold
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
text: content.text
}
Image {
source: "../images/address-bar-error-close.svg"
width: 20
height: 20
anchors {
right: parent.right
rightMargin: hifi.layout.spacing * 2
verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
cursorShape: "PointingHandCursor"
onClicked: {
content.accept()
}
}
}
}
}
Keys.onPressed: {
if (!enabled) {
return
}
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
content.accept()
break
}
}
}

View file

@ -1,179 +0,0 @@
var OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot"
var OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow"
function findChild(item, name) {
for (var i = 0; i < item.children.length; ++i) {
if (item.children[i].objectName === name) {
return item.children[i];
}
}
return null;
}
function findParentMatching(item, predicate) {
while (item) {
if (predicate(item)) {
break;
}
item = item.parent;
}
return item;
}
function findParentByName(item, name) {
return findParentMatching(item, function(item) {
var testName = name;
var result = (item.name === testName);
return result;
});
}
function findRootMenu(item) {
item = getDesktop(item);
return item ? item.rootMenu : null;
}
function isDesktop(item) {
return item.desktopRoot;
}
function isTopLevelWindow(item) {
return item.topLevelWindow;
}
function getTopLevelWindows(item) {
var desktop = getDesktop(item);
var currentWindows = [];
if (!desktop) {
console.log("Could not find desktop for " + item)
return currentWindows;
}
for (var i = 0; i < desktop.children.length; ++i) {
var child = desktop.children[i];
if (isTopLevelWindow(child)) {
var windowId = child.toString();
currentWindows.push(child)
}
}
return currentWindows;
}
function getDesktop(item) {
return findParentMatching(item, isDesktop);
}
function getDesktopWindow(item) {
return findParentMatching(item, isTopLevelWindow)
}
function closeWindow(item) {
item = findDialog(item);
if (item) {
item.enabled = false
} else {
console.warn("Could not find top level dialog")
}
}
function findMenuChild(menu, childName) {
if (!menu) {
return null;
}
if (menu.type !== 2) {
console.warn("Tried to find child of a non-menu");
return null;
}
var items = menu.items;
var count = items.length;
for (var i = 0; i < count; ++i) {
var child = items[i];
var name;
switch (child.type) {
case 2:
name = child.title;
break;
case 1:
name = child.text;
break;
default:
break;
}
if (name && name === childName) {
return child;
}
}
}
function findMenu(rootMenu, path) {
if ('string' === typeof(path)) {
path = [ path ]
}
var currentMenu = rootMenu;
for (var i = 0; currentMenu && i < path.length; ++i) {
currentMenu = findMenuChild(currentMenu, path[i]);
}
return currentMenu;
}
function findInRootMenu(item, path) {
return findMenu(findRootMenu(item), path);
}
function menuItemsToListModel(parent, items) {
var newListModel = Qt.createQmlObject('import QtQuick 2.5; ListModel {}', parent);
for (var i = 0; i < items.length; ++i) {
var item = items[i];
switch (item.type) {
case 2:
newListModel.append({"type":item.type, "name": item.title, "item": item})
break;
case 1:
newListModel.append({"type":item.type, "name": item.text, "item": item})
break;
case 0:
newListModel.append({"type":item.type, "name": "-----", "item": item})
break;
}
}
return newListModel;
}
function raiseWindow(item) {
var targetWindow = getDesktopWindow(item);
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
var desktop = getDesktop(targetWindow);
if (!desktop) {
console.warn("Could not find desktop for window " + targetWindow);
return;
}
var maxZ = 0;
var minZ = 100000;
var windows = desktop.windows;
windows.sort(function(a, b){
return a.z - b.z;
});
var lastZ = -1;
var lastTargetZ = -1;
for (var i = 0; i < windows.length; ++i) {
var window = windows[i];
if (window.z > lastZ) {
lastZ = window.z;
++lastTargetZ;
}
window.z = lastTargetZ;
}
targetWindow.z = lastTargetZ + 1;
}

View file

@ -1,11 +1,10 @@
import QtQuick 2.5
import Hifi 1.0 as Hifi
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.3
import QtWebEngine 1.1
import "controls"
VrDialog {
import "controls"
import "windows" as Windows
Windows.Window {
id: root
width: 800
height: 800
@ -13,12 +12,10 @@ VrDialog {
Hifi.InfoView {
id: infoView
// Fille the client area
// Fill the client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
WebEngineView {
WebView {
id: webview
objectName: "WebView"
anchors.fill: parent

View file

@ -12,29 +12,23 @@ import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
import "windows"
DialogContainer {
Window {
id: root
HifiConstants { id: hifi }
objectName: "LoginDialog"
property bool destroyOnInvisible: false
implicitWidth: loginDialog.implicitWidth
implicitHeight: loginDialog.implicitHeight
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property int maximumX: parent ? parent.width - width : 0
property int maximumY: parent ? parent.height - height : 0
height: loginDialog.implicitHeight
width: loginDialog.implicitWidth
// FIXME make movable
anchors.centerIn: parent
destroyOnInvisible: false
visible: false
LoginDialog {
id: loginDialog
implicitWidth: backgroundRectangle.width
implicitHeight: backgroundRectangle.height
readonly property int inputWidth: 500
readonly property int inputHeight: 60
readonly property int borderWidth: 30
@ -47,46 +41,8 @@ DialogContainer {
width: loginDialog.inputWidth + loginDialog.borderWidth * 2
height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2
radius: loginDialog.closeMargin * 2
color: "#2c86b1"
opacity: 0.85
MouseArea {
width: parent.width
height: parent.height
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
drag {
target: root
minimumX: 0
minimumY: 0
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY : 0
}
}
}
Image {
id: closeIcon
source: "../images/login-close.svg"
width: 20
height: 20
anchors {
top: backgroundRectangle.top
right: backgroundRectangle.right
topMargin: loginDialog.closeMargin
rightMargin: loginDialog.closeMargin
}
MouseArea {
anchors.fill: parent
cursorShape: "PointingHandCursor"
onClicked: {
root.enabled = false
}
}
}
Column {
@ -324,18 +280,13 @@ DialogContainer {
}
}
onOpacityChanged: {
// Set focus once animation is completed so that focus is set at start-up when not logged in
if (opacity == 1.0) {
username.forceActiveFocus()
}
}
onVisibleChanged: {
if (!visible) {
username.text = ""
password.text = ""
loginDialog.statusText = ""
} else {
username.forceActiveFocus()
}
}
@ -343,11 +294,10 @@ DialogContainer {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
if (enabled) {
enabled = false
event.accepted = true
}
break
root.close();
event.accepted = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
if (username.activeFocus) {

View file

@ -1,45 +0,0 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.3
import QtWebEngine 1.1
import "controls"
VrDialog {
title: "Test Dlg"
id: testDialog
objectName: "Browser"
width: 720
height: 720
resizable: true
MarketplaceDialog {
id: marketplaceDialog
}
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
WebEngineView {
objectName: "WebView"
id: webview
url: "https://metaverse.highfidelity.com/marketplace"
anchors.fill: parent
onNavigationRequested: {
console.log(request.url)
if (!marketplaceDialog.navigationRequested(request.url)) {
console.log("Application absorbed the request")
request.action = WebView.IgnoreRequest;
return;
}
console.log("Application passed on the request")
request.action = WebView.AcceptRequest;
return;
}
}
}
}

View file

@ -1,337 +0,0 @@
import Hifi 1.0 as Hifi
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import "controls"
import "styles"
VrDialog {
id: root
HifiConstants { id: hifi }
property real spacing: hifi.layout.spacing
property real outerSpacing: hifi.layout.spacing * 2
destroyOnCloseButton: true
destroyOnInvisible: true
contentImplicitWidth: content.implicitWidth
contentImplicitHeight: content.implicitHeight
Component.onCompleted: {
enabled = true
}
onParentChanged: {
if (visible && enabled) {
forceActiveFocus();
}
}
Hifi.MessageDialog {
id: content
clip: true
x: root.clientX
y: root.clientY
implicitHeight: contentColumn.implicitHeight + outerSpacing * 2
implicitWidth: mainText.implicitWidth + outerSpacing * 2
Component.onCompleted: {
root.title = title
}
onTitleChanged: {
root.title = title
}
Column {
anchors.fill: parent
anchors.margins: 8
id: contentColumn
spacing: root.outerSpacing
Item {
width: parent.width
height: Math.max(icon.height, mainText.height + informativeText.height + root.spacing)
FontAwesome {
id: icon
width: content.icon ? 48 : 0
height: content.icon ? 48 : 0
visible: content.icon ? true : false
font.pixelSize: 48
verticalAlignment: Text.AlignTop
horizontalAlignment: Text.AlignLeft
color: iconColor()
text: iconSymbol()
function iconSymbol() {
switch (content.icon) {
case Hifi.MessageDialog.Information:
return "\uF05A"
case Hifi.MessageDialog.Question:
return "\uF059"
case Hifi.MessageDialog.Warning:
return "\uF071"
case Hifi.MessageDialog.Critical:
return "\uF057"
default:
break;
}
return content.icon;
}
function iconColor() {
switch (content.icon) {
case Hifi.MessageDialog.Information:
case Hifi.MessageDialog.Question:
return "blue"
case Hifi.MessageDialog.Warning:
case Hifi.MessageDialog.Critical:
return "red"
default:
break
}
return "black"
}
}
Text {
id: mainText
anchors {
left: icon.right
leftMargin: root.spacing
right: parent.right
}
text: content.text
font.pointSize: 14
font.weight: Font.Bold
wrapMode: Text.WordWrap
}
Text {
id: informativeText
anchors {
left: icon.right
right: parent.right
top: mainText.bottom
leftMargin: root.spacing
topMargin: root.spacing
}
text: content.informativeText
font.pointSize: 14
wrapMode: Text.WordWrap
}
}
Flow {
id: buttons
spacing: root.spacing
layoutDirection: Qt.RightToLeft
width: parent.width
Button {
id: okButton
text: qsTr("OK")
onClicked: content.click(StandardButton.Ok)
visible: content.standardButtons & StandardButton.Ok
}
Button {
id: openButton
text: qsTr("Open")
onClicked: content.click(StandardButton.Open)
visible: content.standardButtons & StandardButton.Open
}
Button {
id: saveButton
text: qsTr("Save")
onClicked: content.click(StandardButton.Save)
visible: content.standardButtons & StandardButton.Save
}
Button {
id: saveAllButton
text: qsTr("Save All")
onClicked: content.click(StandardButton.SaveAll)
visible: content.standardButtons & StandardButton.SaveAll
}
Button {
id: retryButton
text: qsTr("Retry")
onClicked: content.click(StandardButton.Retry)
visible: content.standardButtons & StandardButton.Retry
}
Button {
id: ignoreButton
text: qsTr("Ignore")
onClicked: content.click(StandardButton.Ignore)
visible: content.standardButtons & StandardButton.Ignore
}
Button {
id: applyButton
text: qsTr("Apply")
onClicked: content.click(StandardButton.Apply)
visible: content.standardButtons & StandardButton.Apply
}
Button {
id: yesButton
text: qsTr("Yes")
onClicked: content.click(StandardButton.Yes)
visible: content.standardButtons & StandardButton.Yes
}
Button {
id: yesAllButton
text: qsTr("Yes to All")
onClicked: content.click(StandardButton.YesToAll)
visible: content.standardButtons & StandardButton.YesToAll
}
Button {
id: noButton
text: qsTr("No")
onClicked: content.click(StandardButton.No)
visible: content.standardButtons & StandardButton.No
}
Button {
id: noAllButton
text: qsTr("No to All")
onClicked: content.click(StandardButton.NoToAll)
visible: content.standardButtons & StandardButton.NoToAll
}
Button {
id: discardButton
text: qsTr("Discard")
onClicked: content.click(StandardButton.Discard)
visible: content.standardButtons & StandardButton.Discard
}
Button {
id: resetButton
text: qsTr("Reset")
onClicked: content.click(StandardButton.Reset)
visible: content.standardButtons & StandardButton.Reset
}
Button {
id: restoreDefaultsButton
text: qsTr("Restore Defaults")
onClicked: content.click(StandardButton.RestoreDefaults)
visible: content.standardButtons & StandardButton.RestoreDefaults
}
Button {
id: cancelButton
text: qsTr("Cancel")
onClicked: content.click(StandardButton.Cancel)
visible: content.standardButtons & StandardButton.Cancel
}
Button {
id: abortButton
text: qsTr("Abort")
onClicked: content.click(StandardButton.Abort)
visible: content.standardButtons & StandardButton.Abort
}
Button {
id: closeButton
text: qsTr("Close")
onClicked: content.click(StandardButton.Close)
visible: content.standardButtons & StandardButton.Close
}
Button {
id: moreButton
text: qsTr("Show Details...")
onClicked: content.state = (content.state === "" ? "expanded" : "")
visible: content.detailedText.length > 0
}
Button {
id: helpButton
text: qsTr("Help")
onClicked: content.click(StandardButton.Help)
visible: content.standardButtons & StandardButton.Help
}
}
}
Item {
id: details
width: parent.width
implicitHeight: detailedText.implicitHeight + root.spacing
height: 0
clip: true
anchors {
left: parent.left
right: parent.right
top: contentColumn.bottom
topMargin: root.spacing
leftMargin: root.outerSpacing
rightMargin: root.outerSpacing
}
Flickable {
id: flickable
contentHeight: detailedText.height
anchors.fill: parent
anchors.topMargin: root.spacing
anchors.bottomMargin: root.outerSpacing
TextEdit {
id: detailedText
text: content.detailedText
width: details.width
wrapMode: Text.WordWrap
readOnly: true
selectByMouse: true
}
}
}
states: [
State {
name: "expanded"
PropertyChanges {
target: details
height: root.height - contentColumn.height - root.spacing - root.outerSpacing
}
PropertyChanges {
target: content
implicitHeight: contentColumn.implicitHeight + root.spacing * 2 +
detailedText.implicitHeight + root.outerSpacing * 2
}
PropertyChanges {
target: moreButton
text: qsTr("Hide Details")
}
}
]
}
Keys.onPressed: {
if (!enabled) {
return
}
if (event.modifiers === Qt.ControlModifier)
switch (event.key) {
case Qt.Key_A:
event.accepted = true
detailedText.selectAll()
break
case Qt.Key_C:
event.accepted = true
detailedText.copy()
break
case Qt.Key_Period:
if (Qt.platform.os === "osx") {
event.accepted = true
content.reject()
}
break
} else switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
event.accepted = true
content.reject()
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
content.accept()
break
}
}
}

View file

@ -1,105 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/11/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
VrDialog {
id: root
HifiConstants { id: hifi }
property real spacing: hifi.layout.spacing
property real outerSpacing: hifi.layout.spacing * 2
objectName: "RecorderDialog"
destroyOnInvisible: false
destroyOnCloseButton: false
contentImplicitWidth: recorderDialog.width
contentImplicitHeight: recorderDialog.height
RecorderDialog {
id: recorderDialog
x: root.clientX; y: root.clientY
width: 800
height: 128
signal play()
signal rewind()
onPlay: {
console.log("Pressed play")
player.isPlaying = !player.isPlaying
}
onRewind: {
console.log("Pressed rewind")
player.position = 0
}
Row {
height: 32
ButtonAwesome {
id: cmdRecord
visible: root.showRecordButton
width: 32; height: 32
text: "\uf111"
iconColor: "red"
onClicked: {
console.log("Pressed record")
status.text = "Recording";
}
}
}
Text {
id: status
anchors.top: parent.top
anchors.right: parent.right
width: 128
text: "Idle"
}
Player {
id: player
y: root.clientY + 64
height: 64
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
// onClicked: {
// if (recordTimer.running) {
// recordTimer.stop();
// }
// recordTimer.start();
// }
Timer {
id: recordTimer;
interval: 1000; running: false; repeat: false
onTriggered: {
console.log("Recording: " + MyAvatar.isRecording())
MyAvatar.startRecording();
console.log("Recording: " + MyAvatar.isRecording())
}
}
}
Component.onCompleted: {
player.play.connect(play)
player.rewind.connect(rewind)
}
}
}

View file

@ -1,14 +1,19 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs;
import "Global.js" as Global
import "dialogs"
// This is our primary 'desktop' object to which all VR dialogs and
// windows will be childed.
// This is our primary 'desktop' object to which all VR dialogs and
// windows will be childed.
Item {
id: desktop
anchors.fill: parent;
onParentChanged: forceActiveFocus();
// Debugging help for figuring out focus issues
property var offscreenWindow;
onOffscreenWindowChanged: offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
function onWindowFocusChanged() { console.log("Focus item is " + offscreenWindow.activeFocusItem); }
// Allows QML/JS to find the desktop through the parent chain
property bool desktopRoot: true
@ -16,16 +21,241 @@ Item {
// The VR version of the primary menu
property var rootMenu: Menu { objectName: "rootMenu" }
// List of all top level windows
property var windows: [];
onChildrenChanged: windows = Global.getTopLevelWindows(desktop);
// The tool window, one instance
property alias toolWindow: toolWindow
ToolWindow { id: toolWindow }
// FIXME support always on top flags
function raise(item) {
Global.raiseWindow(item);
d.raiseWindow(item);
}
Component {
id: messageDialogBuilder
MessageDialog { }
}
Component {
id: nativeMessageDialogBuilder
OriginalDialogs.MessageDialog { }
}
function messageBox(properties) {
// Debugging: native message dialog for comparison
// nativeMessageDialogBuilder.createObject(desktop, properties);
return messageDialogBuilder.createObject(desktop, properties);
}
QtObject {
id: d
readonly property int zBasisNormal: 0
readonly property int zBasisAlwaysOnTop: 4096
readonly property int zBasisModal: 8192
function findChild(item, name) {
for (var i = 0; i < item.children.length; ++i) {
if (item.children[i].objectName === name) {
return item.children[i];
}
}
return null;
}
function findParentMatching(item, predicate) {
while (item) {
if (predicate(item)) {
break;
}
item = item.parent;
}
return item;
}
function isDesktop(item) {
return item.desktopRoot;
}
function isTopLevelWindow(item) {
return item.topLevelWindow;
}
function isAlwaysOnTopWindow(window) {
return window.alwaysOnTop;
}
function isModalWindow(window) {
return window.modality !== Qt.NonModal;
}
function getTopLevelWindows(predicate) {
var currentWindows = [];
if (!desktop) {
console.log("Could not find desktop for " + item)
return currentWindows;
}
for (var i = 0; i < desktop.children.length; ++i) {
var child = desktop.children[i];
if (isTopLevelWindow(child) && (!predicate || predicate(child))) {
currentWindows.push(child)
}
}
return currentWindows;
}
function getDesktopWindow(item) {
return findParentMatching(item, isTopLevelWindow)
}
function fixupZOrder(windows, basis, topWindow) {
windows.sort(function(a, b){
return a.z - b.z;
});
if ((topWindow.z >= basis) && (windows[windows.length - 1] === topWindow)) {
return;
}
var lastZ = -1;
var lastTargetZ = basis - 1;
for (var i = 0; i < windows.length; ++i) {
var window = windows[i];
if (!window.visible) {
continue
}
if (topWindow && (topWindow === window)) {
continue
}
if (window.z > lastZ) {
lastZ = window.z;
++lastTargetZ;
}
if (DebugQML) {
console.log("Assigning z order " + lastTargetZ + " to " + window)
}
window.z = lastTargetZ;
}
if (topWindow) {
++lastTargetZ;
if (DebugQML) {
console.log("Assigning z order " + lastTargetZ + " to " + topWindow)
}
topWindow.z = lastTargetZ;
}
return lastTargetZ;
}
function raiseWindow(item) {
var targetWindow = getDesktopWindow(item);
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
if (!desktop) {
console.warn("Could not find desktop for window " + targetWindow);
return;
}
var predicate;
var zBasis;
if (isModalWindow(targetWindow)) {
predicate = isModalWindow;
zBasis = zBasisModal
} else if (isAlwaysOnTopWindow(targetWindow)) {
predicate = function(window) {
return (isAlwaysOnTopWindow(window) && !isModalWindow(window));
}
zBasis = zBasisAlwaysOnTop
} else {
predicate = function(window) {
return (!isAlwaysOnTopWindow(window) && !isModalWindow(window));
}
zBasis = zBasisNormal
}
var windows = getTopLevelWindows(predicate);
fixupZOrder(windows, zBasis, targetWindow);
}
//function findMenuChild(menu, childName) {
// if (!menu) {
// return null;
// }
// if (menu.type !== 2) {
// console.warn("Tried to find child of a non-menu");
// return null;
// }
// var items = menu.items;
// var count = items.length;
// for (var i = 0; i < count; ++i) {
// var child = items[i];
// var name;
// switch (child.type) {
// case 2:
// name = child.title;
// break;
// case 1:
// name = child.text;
// break;
// default:
// break;
// }
// if (name && name === childName) {
// return child;
// }
// }
//}
//function findMenu(rootMenu, path) {
// if ('string' === typeof(path)) {
// path = [ path ]
// }
// var currentMenu = rootMenu;
// for (var i = 0; currentMenu && i < path.length; ++i) {
// currentMenu = findMenuChild(currentMenu, path[i]);
// }
// return currentMenu;
//}
//function findInRootMenu(item, path) {
// return findMenu(findRootMenu(item), path);
//}
//function menuItemsToListModel(parent, items) {
// var newListModel = Qt.createQmlObject('import QtQuick 2.5; ListModel {}', parent);
// for (var i = 0; i < items.length; ++i) {
// var item = items[i];
// switch (item.type) {
// case 2:
// newListModel.append({"type":item.type, "name": item.title, "item": item})
// break;
// case 1:
// newListModel.append({"type":item.type, "name": item.text, "item": item})
// break;
// case 0:
// newListModel.append({"type":item.type, "name": "-----", "item": item})
// break;
// }
// }
// return newListModel;
//}
}
}

View file

@ -15,6 +15,7 @@ Windows.Window {
destroyOnInvisible: false
closable: false
visible: false
width: 384; height: 640;
property string newTabSource
property alias tabView: tabView
onParentChanged: {
@ -43,7 +44,8 @@ Windows.Window {
}
TabView {
id: tabView; width: 384; height: 640;
anchors.fill: parent
id: tabView;
onCountChanged: {
if (0 == count) {
toolWindow.visible = false
@ -73,13 +75,37 @@ Windows.Window {
return i;
}
}
console.warn("Could not find tab for " + source);
return -1;
}
function findTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
return tabView.getTab(index);
}
function showTabForUrl(source, newVisible) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
if (newVisible) {
toolWindow.visible = true
tab.enabled = true
} else {
tab.enabled = false;
updateVisiblity();
}
}
function removeTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
console.warn("Could not find tab for " + source);
return;
}
tabView.removeTab(index);

View file

@ -5,21 +5,16 @@ import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import "controls"
import "styles"
import "windows"
DialogContainer {
Window {
id: root
HifiConstants { id: hifi }
objectName: "UpdateDialog"
implicitWidth: updateDialog.implicitWidth
implicitHeight: updateDialog.implicitHeight
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property int maximumX: parent ? parent.width - width : 0
property int maximumY: parent ? parent.height - height : 0
width: updateDialog.implicitWidth
height: updateDialog.implicitHeight
resizable: false
anchors.centerIn: parent
UpdateDialog {
id: updateDialog

View file

@ -1,9 +1,27 @@
import QtQuick 2.3
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "."
import "../styles"
Original.Button {
style: ButtonStyle { }
style: ButtonStyle {
HifiConstants { id: hifi }
padding {
top: 8
left: 12
right: 12
bottom: 8
}
background: Border {
anchors.fill: parent
}
label: Text {
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}
}

View file

@ -1,99 +0,0 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
import "../styles"
Item {
id: root
objectName: "topLevelWindow"
HifiConstants { id: hifi }
implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth
implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2
property string title
property int titleSize: titleBorder.height + 12
property string frameColor: hifi.colors.hifiBlue
property string backgroundColor: hifi.colors.dialogBackground
property bool active: false
property real contentImplicitWidth: 800
property real contentImplicitHeight: 800
property alias titleBorder: titleBorder
readonly property alias titleX: titleBorder.x
readonly property alias titleY: titleBorder.y
readonly property alias titleWidth: titleBorder.width
readonly property alias titleHeight: titleBorder.height
// readonly property real borderWidth: hifi.styles.borderWidth
readonly property real borderWidth: 0
property alias clientBorder: clientBorder
readonly property real clientX: clientBorder.x + borderWidth
readonly property real clientY: clientBorder.y + borderWidth
readonly property real clientWidth: clientBorder.width - borderWidth * 2
readonly property real clientHeight: clientBorder.height - borderWidth * 2
/*
* Window decorations, with a title bar and frames
*/
Border {
id: windowBorder
anchors.fill: parent
border.color: root.frameColor
border.width: 0
color: "#00000000"
Border {
id: titleBorder
height: hifi.layout.windowTitleHeight
anchors.right: parent.right
anchors.left: parent.left
border.color: root.frameColor
border.width: 0
clip: true
color: root.active ?
hifi.colors.activeWindow.headerBackground :
hifi.colors.inactiveWindow.headerBackground
Text {
id: titleText
color: root.active ?
hifi.colors.activeWindow.headerText :
hifi.colors.inactiveWindow.headerText
text: root.title
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
} // header border
// These two rectangles hide the curves between the title area
// and the client area
Rectangle {
y: titleBorder.height - titleBorder.radius
height: titleBorder.radius
width: titleBorder.width
color: titleBorder.color
visible: borderWidth == 0
}
Rectangle {
y: titleBorder.height
width: clientBorder.width
height: clientBorder.radius
color: clientBorder.color
}
Border {
id: clientBorder
border.width: 0
border.color: root.frameColor
color: root.backgroundColor
anchors.bottom: parent.bottom
anchors.top: titleBorder.bottom
anchors.topMargin: -titleBorder.border.width
anchors.right: parent.right
anchors.left: parent.left
} // client border
} // window border
}

View file

@ -1,65 +0,0 @@
//
// DialogCommon.qml
//
// Created by David Rowe on 3 Jun 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "../styles"
Item {
id: root
property bool destroyOnInvisible: true
// The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions.
// Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property
// that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the
// visibility.
enabled: false
opacity: 0.0
onEnabledChanged: {
opacity = enabled ? 1.0 : 0.0
}
Behavior on opacity {
// Animate opacity.
NumberAnimation {
duration: hifi.effects.fadeInDuration
easing.type: Easing.OutCubic
}
}
onOpacityChanged: {
// Once we're transparent, disable the dialog's visibility.
visible = (opacity != 0.0)
}
onVisibleChanged: {
if (!visible) {
// Some dialogs should be destroyed when they become invisible.
if (destroyOnInvisible) {
destroy()
}
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (event.modifiers == Qt.ControlModifier) {
enabled = false
event.accepted = true
}
break
}
}
}

View file

@ -1,179 +0,0 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
import "../styles"
/*
* FIXME Need to create a client property here so that objects can be
* placed in it without having to think about positioning within the outer
* window.
*
* Examine the QML ApplicationWindow.qml source for how it does this
*
*/
DialogBase {
id: root
HifiConstants { id: hifi }
// FIXME better placement via a window manager
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property bool destroyOnInvisible: false
property bool destroyOnCloseButton: true
property bool resizable: false
property int animationDuration: hifi.effects.fadeInDuration
property int minX: 256
property int minY: 256
readonly property int topMargin: root.height - clientBorder.height + hifi.layout.spacing
/*
* Support for animating the dialog in and out.
*/
enabled: false
opacity: 1.0
// The offscreen UI will enable an object, rather than manipulating it's
// visibility, so that we can do animations in both directions. Because
// visibility and enabled are boolean flags, they cannot be animated. So when
// enabled is change, we modify a property that can be animated, like scale or
// opacity, and then when the target animation value is reached, we can
// modify the visibility
onEnabledChanged: {
opacity = enabled ? 1.0 : 0.0
// If the dialog is initially invisible, setting opacity doesn't
// trigger making it visible.
if (enabled) {
visible = true;
if (root.parent) {
Desktop.raise(root);
}
}
}
onParentChanged: {
if (enabled && parent) {
Global.raiseWindow(root);
}
}
// The actual animator
Behavior on opacity {
NumberAnimation {
duration: animationDuration
easing.type: Easing.OutCubic
}
}
// Once we're transparent, disable the dialog's visibility
onOpacityChanged: {
visible = (opacity != 0.0);
}
Component.onCompleted: {
if (visible) {
Global.raiseWindow(root);
}
}
// Some dialogs should be destroyed when they become invisible,
// so handle that
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
destroy();
}
}
// our close function performs the same way as the OffscreenUI class:
// don't do anything but manipulate the enabled flag and let the other
// mechanisms decide if the window should be destroyed after the close
// animation completes
function close() {
if (destroyOnCloseButton) {
destroyOnInvisible = true
}
enabled = false;
}
/*
* Resize support
*/
function deltaSize(dx, dy) {
width = Math.max(width + dx, minX)
height = Math.max(height + dy, minY)
}
MouseArea {
id: sizeDrag
enabled: root.resizable
property int startX
property int startY
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 16
height: 16
z: 1000
hoverEnabled: true
onPressed: {
startX = mouseX
startY = mouseY
}
onPositionChanged: {
if (pressed) {
root.deltaSize((mouseX - startX), (mouseY - startY))
startX = mouseX
startY = mouseY
}
}
}
MouseArea {
id: titleDrag
x: root.titleX
y: root.titleY
width: root.titleWidth
height: root.titleHeight
onClicked: Global.raiseWindow(root)
drag {
target: root
minimumX: 0
minimumY: 0
maximumX: root.parent ? root.parent.width - root.width : 0
maximumY: root.parent ? root.parent.height - root.height : 0
}
Row {
id: windowControls
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: hifi.layout.spacing
FontAwesome {
id: icon
anchors.verticalCenter: parent.verticalCenter
size: root.titleHeight - hifi.layout.spacing * 2
color: "red"
text: "\uf00d"
MouseArea {
anchors.margins: hifi.layout.spacing / 2
anchors.fill: parent
onClicked: {
root.close();
}
}
}
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
enabled = false
}
break;
}
}
}

View file

@ -4,17 +4,200 @@ import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0
import ".."
import "."
import "../windows"
import "../styles"
// Work in progress....
DialogBase {
Window {
id: root
Constants { id: vr }
property string settingsName: ""
HifiConstants { id: hifi }
signal selectedFile(var file);
signal canceled();
anchors.centerIn: parent
resizable: true
width: 640
height: 480
modality: Qt.ApplicationModal
property string settingsName: ""
property alias folder: folderModel.folder
property alias filterModel: selectionType.model
Rectangle {
anchors.fill: parent
color: "white"
Settings {
// fixme, combine with a property to allow different saved locations
category: "FileOpenLastFolder." + settingsName
property alias folder: folderModel.folder
}
TextField {
id: currentDirectory
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 8
readOnly: true
text: folderModel.folder
}
Component {
id: fileItemDelegate
Item {
clip: true
Text {
x: 3
id: columnText
anchors.verticalCenter: parent.verticalCenter
// font.pointSize: 12
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
elide: styleData.elideMode
text: getText();
font.italic: folderModel.get(styleData.row, "fileIsDir") ? true : false
Connections {
target: tableView
//onCurrentRowChanged: columnText.color = (tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor)
}
function getText() {
switch (styleData.column) {
//case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss");
case 2: return folderModel.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value);
default: return styleData.value;
}
}
function formatSize(size) {
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
var suffixIndex = 0
while ((size / 1024.0) > 1.1) {
size /= 1024.0;
++suffixIndex;
}
size = Math.round(size*1000)/1000;
size = size.toLocaleString()
return size + " " + suffixes[suffixIndex];
}
}
}
}
TableView {
id: tableView
focus: true
model: FolderListModel {
id: folderModel
showDotAndDotDot: true
showDirsFirst: true
onFolderChanged: {
tableView.currentRow = -1;
tableView.positionViewAtRow(0, ListView.Beginning)
}
}
anchors.top: currentDirectory.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: selectionType.top
anchors.margins: 8
itemDelegate: fileItemDelegate
// rowDelegate: Rectangle {
// id: rowDelegate
// color: styleData.selected ? "#7f0000ff" : "#00000000"
// Connections { target: folderModel; onFolderChanged: rowDelegate.visible = false }
// Connections { target: tableView; onCurrentRowChanged: rowDelegate.visible = true; }
// }
TableViewColumn {
role: "fileName"
title: "Name"
width: 400
}
TableViewColumn {
role: "fileModified"
title: "Date Modified"
width: 200
}
TableViewColumn {
role: "fileSize"
title: "Size"
width: 200
}
Keys.onReturnPressed: selectCurrentFile();
onDoubleClicked: { currentRow = row; selectCurrentFile(); }
onCurrentRowChanged: currentSelection.text = model.get(currentRow, "fileName");
KeyNavigation.left: cancelButton
KeyNavigation.right: selectionType
}
TextField {
id: currentSelection
anchors.right: selectionType.left
anchors.rightMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
anchors.top: selectionType.top
}
ComboBox {
id: selectionType
anchors.bottom: buttonRow.top
anchors.bottomMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: buttonRow.left
model: ListModel {
ListElement { text: "All Files (*.*)"; filter: "*.*" }
}
onCurrentIndexChanged: {
folderModel.nameFilters = [ filterModel.get(currentIndex).filter ]
}
KeyNavigation.left: tableView
KeyNavigation.right: openButton
}
Row {
id: buttonRow
anchors.right: parent.right
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
layoutDirection: Qt.RightToLeft
spacing: 8
Button {
id: cancelButton
text: "Cancel"
KeyNavigation.up: selectionType
KeyNavigation.left: openButton
KeyNavigation.right: tableView.contentItem
Keys.onReturnPressed: { canceled(); root.enabled = false }
onClicked: { canceled(); close() }
}
Button {
id: openButton
text: "Open"
enabled: tableView.currentRow != -1 && !folderModel.get(tableView.currentRow, "fileIsDir")
onClicked: selectCurrentFile();
Keys.onReturnPressed: selectCurrentFile();
KeyNavigation.up: selectionType
KeyNavigation.left: selectionType
KeyNavigation.right: cancelButton
}
}
}
function selectCurrentFile() {
var row = tableView.currentRow
console.log("Selecting row " + row)
@ -25,200 +208,11 @@ DialogBase {
folderModel.folder = folderModel.get(row, "fileURL");
} else {
selectedFile(folderModel.get(row, "fileURL"));
enabled = false
close();
}
}
property var folderModel: FolderListModel {
id: folderModel
showDotAndDotDot: true
showDirsFirst: true
folder: "file:///C:/";
onFolderChanged: tableView.currentRow = 0
}
property var filterModel: ListModel {
ListElement {
text: "All Files (*.*)"
filter: "*"
}
ListElement {
text: "Javascript Files (*.js)"
filter: "*.js"
}
ListElement {
text: "QML Files (*.qml)"
filter: "*.qml"
}
}
Settings {
// fixme, combine with a property to allow different saved locations
category: "FileOpenLastFolder." + settingsName
property alias folder: folderModel.folder
}
Rectangle {
id: currentDirectoryWrapper
anchors.left: parent.left
anchors.leftMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
anchors.top: parent.top
anchors.topMargin: 8
height: currentDirectory.implicitHeight + 8
radius: vr.styles.radius
color: vr.controls.colors.inputBackground
TextEdit {
enabled: false
id: currentDirectory
text: folderModel.folder
anchors {
fill: parent
leftMargin: 8
rightMargin: 8
topMargin: 4
bottomMargin: 4
}
}
}
TableView {
id: tableView
focus: true
model: folderModel
anchors.top: currentDirectoryWrapper.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: selectionType.top
anchors.margins: 8
itemDelegate: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
font.family: vr.fonts.lightFontName
font.pointSize: 12
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
elide: styleData.elideMode
text: getText();
function getText() {
switch (styleData.column) {
//case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss");
case 2: return folderModel.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value);
default: return styleData.value;
}
}
function formatSize(size) {
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
var suffixIndex = 0
while ((size / 1024.0) > 1.1) {
size /= 1024.0;
++suffixIndex;
}
size = Math.round(size*1000)/1000;
size = size.toLocaleString()
return size + " " + suffixes[suffixIndex];
}
}
}
TableViewColumn {
role: "fileName"
title: "Name"
width: 400
}
TableViewColumn {
role: "fileModified"
title: "Date Modified"
width: 200
}
TableViewColumn {
role: "fileSize"
title: "Size"
width: 200
}
function selectItem(row) {
selectCurrentFile()
}
Keys.onReturnPressed: selectCurrentFile();
onDoubleClicked: { currentRow = row; selectCurrentFile(); }
onCurrentRowChanged: currentSelection.text = model.get(currentRow, "fileName");
KeyNavigation.left: cancelButton
KeyNavigation.right: selectionType
}
Rectangle {
anchors.right: selectionType.left
anchors.rightMargin: 8
anchors.top: selectionType.top
anchors.bottom: selectionType.bottom
anchors.left: parent.left
anchors.leftMargin: 8
radius: 8
color: "#eee"
Text {
id: currentSelection
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 8
anchors.verticalCenter: parent.verticalCenter
font.family: vr.fonts.mainFontName
font.pointSize: 18
}
}
ComboBox {
id: selectionType
anchors.bottom: buttonRow.top
anchors.bottomMargin: 8
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: buttonRow.left
model: filterModel
onCurrentIndexChanged: folderModel.nameFilters = [
filterModel.get(currentIndex).filter
]
KeyNavigation.left: tableView
KeyNavigation.right: openButton
}
Row {
id: buttonRow
anchors.right: parent.right
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
layoutDirection: Qt.RightToLeft
spacing: 8
Button {
id: cancelButton
text: "Cancel"
KeyNavigation.up: selectionType
KeyNavigation.left: openButton
KeyNavigation.right: tableView.contentItem
Keys.onReturnPressed: { canceled(); root.enabled = false }
onClicked: { canceled(); root.enabled = false }
}
Button {
id: openButton
text: "Open"
enabled: tableView.currentRow != -1 && !folderModel.get(tableView.currentRow, "fileIsDir")
KeyNavigation.up: selectionType
KeyNavigation.left: selectionType
KeyNavigation.right: cancelButton
onClicked: selectCurrentFile();
Keys.onReturnPressed: selectedFile(folderModel.get(tableView.currentRow, "fileURL"))
}
}
Keys.onPressed: {
if (event.key === Qt.Key_Backspace && folderModel.parentFolder && folderModel.parentFolder != "") {

View file

@ -0,0 +1,341 @@
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../controls"
import "../styles"
import "../windows"
// FIXME respect default button functionality
// FIXME force active focus at all times (modal dialog)
Window {
id: root
HifiConstants { id: hifi }
implicitWidth: 640
implicitHeight: 320
destroyOnCloseButton: true
destroyOnInvisible: true
visible: true
modality: Qt.ApplicationModal
anchors.centerIn: parent
frame: ModalFrame {}
signal selected(int button);
function click(button) {
console.log("User clicked " + button)
clickedButton = button;
selected(button);
destroy();
}
property alias detailedText: detailedText.text
property alias text: mainTextContainer.text
property alias informativeText: informativeTextContainer.text
onIconChanged: iconHolder.updateIcon();
property int buttons: OriginalDialogs.StandardButton.Ok
property int icon: OriginalDialogs.StandardIcon.NoIcon
property int defaultButton: OriginalDialogs.StandardButton.NoButton;
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
focus: defaultButton === OriginalDialogs.StandardButton.NoButton
Rectangle {
id: messageBox
clip: true
anchors.fill: parent
radius: 4
color: "white"
QtObject {
id: d
readonly property real spacing: hifi.layout.spacing
readonly property real outerSpacing: hifi.layout.spacing * 2
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int minHeight: 160
readonly property int maxHeight: 720
function resize() {
var targetWidth = iconHolder.width + mainTextContainer.width + d.spacing * 6
var targetHeight = mainTextContainer.implicitHeight + informativeTextContainer.implicitHeight + d.spacing * 8 + buttons.height + details.height
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
}
}
FontAwesome {
id: iconHolder
size: 48
anchors {
left: parent.left
top: parent.top
margins: d.spacing * 2
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
style: Text.Outline; styleColor: "black"
Component.onCompleted: updateIcon();
function updateIcon() {
if (!root) {
return;
}
switch (root.icon) {
case OriginalDialogs.StandardIcon.Information:
text = "\uF05A";
color = "blue";
break;
case OriginalDialogs.StandardIcon.Question:
text = "\uF059"
color = "blue";
break;
case OriginalDialogs.StandardIcon.Warning:
text = "\uF071"
color = "yellow";
break;
case OriginalDialogs.StandardIcon.Critical:
text = "\uF057"
color = "red"
break;
default:
text = ""
}
visible = (text != "");
}
}
Text {
id: mainTextContainer
onHeightChanged: d.resize(); onWidthChanged: d.resize();
wrapMode: Text.WordWrap
font { pointSize: 14; weight: Font.Bold }
anchors { left: iconHolder.right; top: parent.top; margins: d.spacing * 2 }
}
Text {
id: informativeTextContainer
onHeightChanged: d.resize(); onWidthChanged: d.resize();
wrapMode: Text.WordWrap
font.pointSize: 11
anchors { top: mainTextContainer.bottom; right: parent.right; left: iconHolder.right; margins: d.spacing * 2 }
}
Flow {
id: buttons
focus: true
spacing: d.spacing
onHeightChanged: d.resize(); onWidthChanged: d.resize();
layoutDirection: Qt.RightToLeft
anchors { bottom: details.top; right: parent.right; margins: d.spacing * 2; bottomMargin: 0 }
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
}
Button {
id: moreButton
text: qsTr("Show Details...")
onClicked: { content.state = (content.state === "" ? "expanded" : "")
}
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
}
}
Item {
id: details
width: parent.width
implicitHeight: detailedText.implicitHeight + root.spacing
height: 0
clip: true
anchors { bottom: parent.bottom; left: parent.left; right: parent.right; margins: d.spacing * 2 }
Flickable {
id: flickable
contentHeight: detailedText.height
anchors.fill: parent
anchors.topMargin: root.spacing
anchors.bottomMargin: root.outerSpacing
TextEdit {
id: detailedText
width: details.width
wrapMode: Text.WordWrap
readOnly: true
selectByMouse: true
}
}
}
states: [
State {
name: "expanded"
PropertyChanges { target: root; anchors.fill: undefined }
PropertyChanges { target: details; height: 120 }
PropertyChanges { target: moreButton; text: qsTr("Hide Details") }
}
]
onStateChanged: d.resize()
}
Keys.onPressed: {
if (!visible) {
return
}
if (event.modifiers === Qt.ControlModifier)
switch (event.key) {
case Qt.Key_A:
event.accepted = true
detailedText.selectAll()
break
case Qt.Key_C:
event.accepted = true
detailedText.copy()
break
case Qt.Key_Period:
if (Qt.platform.os === "osx") {
event.accepted = true
content.reject()
}
break
} else switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
event.accepted = true
root.click(OriginalDialogs.StandardButton.Cancel)
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
root.click(OriginalDialogs.StandardButton.Ok)
break
}
}
}

View file

@ -13,7 +13,6 @@ Window {
title: "Running Scripts"
resizable: true
destroyOnInvisible: true
enabled: false
x: 40; y: 40
property var scripts: ScriptDiscoveryService;
@ -202,7 +201,6 @@ Window {
TextField {
id: selectedScript
enabled: true
readOnly: true
anchors.left: parent.left
anchors.right: loadButton.left

View file

@ -0,0 +1,167 @@
import QtQuick 2.5
QtObject {
readonly property string aboutApp: "About Interface";
readonly property string addRemoveFriends: "Add/Remove Friends...";
readonly property string addressBar: "Show Address Bar";
readonly property string animations: "Animations...";
readonly property string animDebugDrawAnimPose: "Debug Draw Animation";
readonly property string animDebugDrawDefaultPose: "Debug Draw Default Pose";
readonly property string animDebugDrawPosition: "Debug Draw Position";
readonly property string antialiasing: "Antialiasing";
readonly property string assetMigration: "ATP Asset Migration";
readonly property string atmosphere: "Atmosphere";
readonly property string attachments: "Attachments...";
readonly property string audioNetworkStats: "Audio Network Stats";
readonly property string audioNoiseReduction: "Audio Noise Reduction";
readonly property string audioScope: "Show Scope";
readonly property string audioScopeFiftyFrames: "Fifty";
readonly property string audioScopeFiveFrames: "Five";
readonly property string audioScopeFrames: "Display Frames";
readonly property string audioScopePause: "Pause Scope";
readonly property string audioScopeTwentyFrames: "Twenty";
readonly property string audioStatsShowInjectedStreams: "Audio Stats Show Injected Streams";
readonly property string audioTools: "Show Level Meter";
readonly property string autoMuteAudio: "Auto Mute Microphone";
readonly property string avatarReceiveStats: "Show Receive Stats";
readonly property string back: "Back";
readonly property string bandwidthDetails: "Bandwidth Details";
readonly property string binaryEyelidControl: "Binary Eyelid Control";
readonly property string bookmarkLocation: "Bookmark Location";
readonly property string bookmarks: "Bookmarks";
readonly property string cachesSize: "RAM Caches Size";
readonly property string calibrateCamera: "Calibrate Camera";
readonly property string cameraEntityMode: "Entity Mode";
readonly property string centerPlayerInView: "Center Player In View";
readonly property string chat: "Chat...";
readonly property string collisions: "Collisions";
readonly property string comfortMode: "Comfort Mode";
readonly property string connexion: "Activate 3D Connexion Devices";
readonly property string console_: "Console...";
readonly property string controlWithSpeech: "Control With Speech";
readonly property string copyAddress: "Copy Address to Clipboard";
readonly property string copyPath: "Copy Path to Clipboard";
readonly property string coupleEyelids: "Couple Eyelids";
readonly property string crashInterface: "Crash Interface";
readonly property string debugAmbientOcclusion: "Debug Ambient Occlusion";
readonly property string decreaseAvatarSize: "Decrease Avatar Size";
readonly property string deleteBookmark: "Delete Bookmark...";
readonly property string disableActivityLogger: "Disable Activity Logger";
readonly property string disableEyelidAdjustment: "Disable Eyelid Adjustment";
readonly property string disableLightEntities: "Disable Light Entities";
readonly property string disableNackPackets: "Disable Entity NACK Packets";
readonly property string diskCacheEditor: "Disk Cache Editor";
readonly property string displayCrashOptions: "Display Crash Options";
readonly property string displayHandTargets: "Show Hand Targets";
readonly property string displayModelBounds: "Display Model Bounds";
readonly property string displayModelTriangles: "Display Model Triangles";
readonly property string displayModelElementChildProxies: "Display Model Element Children";
readonly property string displayModelElementProxy: "Display Model Element Bounds";
readonly property string displayDebugTimingDetails: "Display Timing Details";
readonly property string dontDoPrecisionPicking: "Don't Do Precision Picking";
readonly property string dontRenderEntitiesAsScene: "Don't Render Entities as Scene";
readonly property string echoLocalAudio: "Echo Local Audio";
readonly property string echoServerAudio: "Echo Server Audio";
readonly property string enable3DTVMode: "Enable 3DTV Mode";
readonly property string enableCharacterController: "Enable avatar collisions";
readonly property string expandMyAvatarSimulateTiming: "Expand /myAvatar/simulation";
readonly property string expandMyAvatarTiming: "Expand /myAvatar";
readonly property string expandOtherAvatarTiming: "Expand /otherAvatar";
readonly property string expandPaintGLTiming: "Expand /paintGL";
readonly property string expandUpdateTiming: "Expand /update";
readonly property string faceshift: "Faceshift";
readonly property string firstPerson: "First Person";
readonly property string fivePointCalibration: "5 Point Calibration";
readonly property string fixGaze: "Fix Gaze (no saccade)";
readonly property string forward: "Forward";
readonly property string frameTimer: "Show Timer";
readonly property string fullscreenMirror: "Mirror";
readonly property string glowWhenSpeaking: "Glow When Speaking";
readonly property string help: "Help...";
readonly property string increaseAvatarSize: "Increase Avatar Size";
readonly property string independentMode: "Independent Mode";
readonly property string inputMenu: "Avatar>Input Devices";
readonly property string keyboardMotorControl: "Enable Keyboard Motor Control";
readonly property string leapMotionOnHMD: "Leap Motion on HMD";
readonly property string loadScript: "Open and Run Script File...";
readonly property string loadScriptURL: "Open and Run Script from URL...";
readonly property string lodTools: "LOD Tools";
readonly property string login: "Login";
readonly property string log: "Log";
readonly property string logExtraTimings: "Log Extra Timing Details";
readonly property string lowVelocityFilter: "Low Velocity Filter";
readonly property string meshVisible: "Draw Mesh";
readonly property string miniMirror: "Mini Mirror";
readonly property string muteAudio: "Mute Microphone";
readonly property string muteEnvironment: "Mute Environment";
readonly property string muteFaceTracking: "Mute Face Tracking";
readonly property string namesAboveHeads: "Names Above Heads";
readonly property string noFaceTracking: "None";
readonly property string octreeStats: "Entity Statistics";
readonly property string onePointCalibration: "1 Point Calibration";
readonly property string onlyDisplayTopTen: "Only Display Top Ten";
readonly property string outputMenu: "Display";
readonly property string packageModel: "Package Model...";
readonly property string pair: "Pair";
readonly property string physicsShowOwned: "Highlight Simulation Ownership";
readonly property string physicsShowHulls: "Draw Collision Hulls";
readonly property string pipelineWarnings: "Log Render Pipeline Warnings";
readonly property string preferences: "General...";
readonly property string quit: "Quit";
readonly property string reloadAllScripts: "Reload All Scripts";
readonly property string reloadContent: "Reload Content (Clears all caches)";
readonly property string renderBoundingCollisionShapes: "Show Bounding Collision Shapes";
readonly property string renderFocusIndicator: "Show Eye Focus";
readonly property string renderLookAtTargets: "Show Look-at Targets";
readonly property string renderLookAtVectors: "Show Look-at Vectors";
readonly property string renderResolution: "Scale Resolution";
readonly property string renderResolutionOne: "1";
readonly property string renderResolutionTwoThird: "2/3";
readonly property string renderResolutionHalf: "1/2";
readonly property string renderResolutionThird: "1/3";
readonly property string renderResolutionQuarter: "1/4";
readonly property string renderAmbientLight: "Ambient Light";
readonly property string renderAmbientLightGlobal: "Global";
readonly property string renderAmbientLight0: "OLD_TOWN_SQUARE";
readonly property string renderAmbientLight1: "GRACE_CATHEDRAL";
readonly property string renderAmbientLight2: "EUCALYPTUS_GROVE";
readonly property string renderAmbientLight3: "ST_PETERS_BASILICA";
readonly property string renderAmbientLight4: "UFFIZI_GALLERY";
readonly property string renderAmbientLight5: "GALILEOS_TOMB";
readonly property string renderAmbientLight6: "VINE_STREET_KITCHEN";
readonly property string renderAmbientLight7: "BREEZEWAY";
readonly property string renderAmbientLight8: "CAMPUS_SUNSET";
readonly property string renderAmbientLight9: "FUNSTON_BEACH_SUNSET";
readonly property string resetAvatarSize: "Reset Avatar Size";
readonly property string resetSensors: "Reset Sensors";
readonly property string runningScripts: "Running Scripts...";
readonly property string runTimingTests: "Run Timing Tests";
readonly property string scriptEditor: "Script Editor...";
readonly property string scriptedMotorControl: "Enable Scripted Motor Control";
readonly property string showDSConnectTable: "Show Domain Connection Timing";
readonly property string showBordersEntityNodes: "Show Entity Nodes";
readonly property string showRealtimeEntityStats: "Show Realtime Entity Stats";
readonly property string showWhosLookingAtMe: "Show Who's Looking at Me";
readonly property string standingHMDSensorMode: "Standing HMD Sensor Mode";
readonly property string simulateEyeTracking: "Simulate";
readonly property string sMIEyeTracking: "SMI Eye Tracking";
readonly property string stars: "Stars";
readonly property string stats: "Stats";
readonly property string stopAllScripts: "Stop All Scripts";
readonly property string suppressShortTimings: "Suppress Timings Less than 10ms";
readonly property string thirdPerson: "Third Person";
readonly property string threePointCalibration: "3 Point Calibration";
readonly property string throttleFPSIfNotFocus: "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
readonly property string toolWindow: "Tool Window";
readonly property string transmitterDrive: "Transmitter Drive";
readonly property string turnWithHead: "Turn using Head";
readonly property string uploadAsset: "Upload File to Asset Server";
readonly property string useAudioForMouth: "Use Audio for Mouth";
readonly property string useCamera: "Use Camera";
readonly property string velocityFilter: "Velocity Filter";
readonly property string visibleToEveryone: "Everyone";
readonly property string visibleToFriends: "Friends";
readonly property string visibleToNoOne: "No one";
readonly property string worldAxes: "World Axes";
}

View file

@ -1,23 +0,0 @@
import QtQuick 2.4 as Original
import QtQuick.Controls.Styles 1.3 as OriginalStyles
import "."
import "../controls"
OriginalStyles.ButtonStyle {
HifiConstants { id: hifi }
padding {
top: 8
left: 12
right: 12
bottom: 8
}
background: Border {
anchors.fill: parent
}
label: Text {
verticalAlignment: Original.Text.AlignVCenter
horizontalAlignment: Original.Text.AlignHCenter
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}

View file

@ -1,11 +0,0 @@
import QtQuick 2.4
Item {
property string hifiBlue: "#0e7077"
property alias colors: colorsObj
Item {
id: colorsObj
property string hifiRed: "red"
}
}

View file

@ -1,10 +0,0 @@
ButtonStyle {
background: Item { anchors.fill: parent }
label: FontAwesome {
id: icon
font.pointSize: 18
property alias unicode: text
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}

View file

@ -8,27 +8,10 @@ Frame {
// The frame fills the parent, which should be the size of the content.
// The frame decorations use negative anchor margins to extend beyond
anchors.fill: parent
// Size of the controls
readonly property real iconSize: 24;
// Convenience accessor for the window
property alias window: frame.parent
// FIXME needed?
property alias decoration: decoration
Rectangle {
anchors { margins: -4 }
visible: !decoration.visible
anchors.fill: parent;
color: "#7f7f7f7f";
radius: 3;
}
Rectangle {
id: decoration
anchors { margins: -iconSize; topMargin: -iconSize * (window.closable ? 2 : 1); }
// FIXME doesn't work
// visible: window.activator.containsMouse
anchors.fill: parent;
color: "#7f7f7f7f";
radius: 3;
@ -36,14 +19,9 @@ Frame {
// Allow dragging of the window
MouseArea {
id: dragMouseArea
hoverEnabled: true
anchors.fill: parent
drag {
target: window
// minimumX: (decoration.width - window.width) * -1
// minimumY: 0
// maximumX: (window.parent.width - window.width) - 2 * (decoration.width - window.width)
// maximumY: (window.parent.height - window.height) - 2 * (decoration.height - window.height)
}
drag.target: window
}
Row {
@ -53,6 +31,7 @@ Frame {
anchors.rightMargin: iconSize
anchors.topMargin: iconSize / 2
spacing: iconSize / 4
FontAwesome {
visible: false
text: "\uf08d"
@ -66,7 +45,6 @@ Frame {
onClicked: { frame.pin(); mouse.accepted = false; }
}
}
FontAwesome {
visible: window.closable
text: closeClickArea.containsMouse ? "\uf057" : "\uf05c"
@ -82,56 +60,6 @@ Frame {
}
}
}
// Allow sizing of the window
// FIXME works in native QML, doesn't work in Interface
MouseArea {
id: sizeDrag
width: iconSize
height: iconSize
anchors {
right: decoration.right;
bottom: decoration.bottom
bottomMargin: iconSize * 2
}
property vector2d pressOrigin
property vector2d sizeOrigin
property bool hid: false
onPressed: {
console.log("Pressed on size")
pressOrigin = Qt.vector2d(mouseX, mouseY)
sizeOrigin = Qt.vector2d(window.content.width, window.content.height)
hid = false;
}
onReleased: {
if (hid) {
window.content.visible = true
hid = false;
}
}
onPositionChanged: {
if (pressed) {
if (window.content.visible) {
window.content.visible = false;
hid = true;
}
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
frame.deltaSize(delta.x, delta.y)
}
}
}
FontAwesome {
visible: window.resizable
rotation: -45
anchors { centerIn: sizeDrag }
horizontalAlignment: Text.AlignHCenter
text: "\uf07d"
size: iconSize / 3 * 2
style: Text.Outline; styleColor: "white"
}
}
}

View file

@ -0,0 +1,57 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "."
import "../styles"
// Enable window visibility transitions
FocusScope {
id: root
HifiConstants { id: hifi }
// Should hiding the window destroy it or just hide it?
property bool destroyOnInvisible: false
Component.onCompleted: {
fadeTargetProperty = visible ? 1.0 : 0.0
}
// The target property to animate, usually scale or opacity
property alias fadeTargetProperty: root.opacity
// always start the property at 0 to enable fade in on creation
fadeTargetProperty: 0
// DO NOT set visible to false or when derived types override it it
// will short circuit the fade in on initial visibility
// visible: false <--- NO
// Some dialogs should be destroyed when they become
// invisible, so handle that
onVisibleChanged: {
// If someone directly set the visibility to false
// toggle it back on and use the targetVisible flag to transition
// via fading.
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
var target = visible;
visible = !visible;
fadeTargetProperty = target ? 1.0 : 0.0;
return;
}
if (!visible && destroyOnInvisible) {
destroy();
}
}
// The actual animator
Behavior on fadeTargetProperty {
NumberAnimation {
duration: hifi.effects.fadeInDuration
easing.type: Easing.InOutCubic
}
}
// Once we're transparent, disable the dialog's visibility
onFadeTargetPropertyChanged: {
visible = (fadeTargetProperty != 0.0);
}
}

View file

@ -1,8 +1,95 @@
import QtQuick 2.5
import "../controls"
Item {
signal close()
signal pin()
signal raise()
signal deltaSize(real dx, real dy)
id: frame
// Convenience accessor for the window
property alias window: frame.parent
readonly property int iconSize: 24
default property var decoration;
children: [
decoration,
sizeOutline,
debugZ,
sizeDrag,
]
Text {
id: debugZ
visible: DebugQML
text: "Z: " + window.z
y: -height
}
function close() {
window.close();
}
function raise() {
window.raise();
}
function deltaSize(dx, dy) {
var newSize = Qt.vector2d(window.width + dx, window.height + dy);
newSize = clampVector(newSize, window.minSize, window.maxSize);
window.width = newSize.x
window.height = newSize.y
}
Rectangle {
id: sizeOutline
width: window.width
height: window.height
color: "#00000000"
border.width: 4
radius: 10
visible: !window.content.visible
}
MouseArea {
id: sizeDrag
width: iconSize
height: iconSize
enabled: window.resizable
x: window.width
y: window.height
property vector2d pressOrigin
property vector2d sizeOrigin
property bool hid: false
onPressed: {
console.log("Pressed on size")
pressOrigin = Qt.vector2d(mouseX, mouseY)
sizeOrigin = Qt.vector2d(window.content.width, window.content.height)
hid = false;
}
onReleased: {
if (hid) {
window.content.visible = true
hid = false;
}
}
onPositionChanged: {
if (pressed) {
if (window.content.visible) {
window.content.visible = false;
hid = true;
}
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
frame.deltaSize(delta.x, delta.y)
}
}
FontAwesome {
visible: sizeDrag.enabled
rotation: -45
anchors { centerIn: parent }
horizontalAlignment: Text.AlignHCenter
text: "\uf07d"
size: iconSize / 3 * 2
style: Text.Outline; styleColor: "white"
}
}
}

View file

@ -0,0 +1,29 @@
import QtQuick 2.5
import "."
import "../controls"
Frame {
id: frame
// The frame fills the parent, which should be the size of the content.
// The frame decorations use negative anchor margins to extend beyond
anchors.fill: parent
property alias window: frame.parent
Rectangle {
anchors.fill: parent
anchors.margins: -4096
visible: window.visible
color: "#7f7f7f7f";
radius: 3;
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
onClicked: { }
onDoubleClicked: {}
onPressAndHold: {}
onReleased: {}
}
}
}

View file

@ -1,53 +1,48 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "."
import "../styles"
FocusScope {
// FIXME how do I set the initial position of a window without
// overriding places where the a individual client of the window
// might be setting the position with a Settings{} element?
// FIXME how to I enable dragging without allowing the window to lay outside
// of the desktop? How do I ensure when the desktop resizes all the windows
// are still at least partially visible?
Fadable {
id: window
HifiConstants { id: hifi }
// The Window size is the size of the content, while the frame
// decorations can extend outside it. Windows should generally not be
// given explicit height / width, but rather be allowed to conform to
// their content
// decorations can extend outside it.
implicitHeight: content.height
implicitWidth: content.width
property bool topLevelWindow: true
property int modality: Qt.NonModal
readonly property bool topLevelWindow: true
property string title
// Should the window include a close control?
// Should the window be closable control?
property bool closable: true
// Should the window try to remain on top of other windows?
property bool alwaysOnTop: false
// Should hitting the close button hide or destroy the window?
property bool destroyOnCloseButton: true
// Should hiding the window destroy it or just hide it?
property bool destroyOnInvisible: false
// FIXME support for pinned / unpinned pending full design
// property bool pinnable: false
// property bool pinned: false
property bool resizable: false
property vector2d minSize: Qt.vector2d(100, 100)
property vector2d maxSize: Qt.vector2d(1280, 720)
enabled: visible
// The content to place inside the window, determined by the client
default property var content
onContentChanged: {
if (content) {
content.anchors.fill = window
}
}
// Default to a standard frame. Can be overriden to provide custom
// frame styles, like a full desktop frame to simulate a modal window
property var frame: DefaultFrame {
z: -1
anchors.fill: parent
}
// This mouse area serves to raise the window. To function, it must live
// in the window and have a higher Z-order than the content, but follow
// This mouse area serves to raise the window. To function, it must live
// in the window and have a higher Z-order than the content, but follow
// the position and size of frame decoration
property var activator: MouseArea {
width: frame.decoration.width
@ -56,18 +51,48 @@ FocusScope {
y: frame.decoration.anchors.topMargin
propagateComposedEvents: true
hoverEnabled: true
onPressed: { window.raise(); mouse.accepted = false; }
// Debugging visualization
// Rectangle { anchors.fill:parent; color: "#7f00ff00" }
acceptedButtons: Qt.AllButtons
onPressed: {
//console.log("Pressed on activator area");
window.raise();
mouse.accepted = false;
}
// Debugging
// onEntered: console.log("activator entered")
// onExited: console.log("activator exited")
// onContainsMouseChanged: console.log("Activator contains mouse " + containsMouse)
// onPositionChanged: console.log("Activator mouse position " + mouse.x + " x " + mouse.y)
// Rectangle { anchors.fill:parent; color: "#7f00ff00" }
}
signal windowDestroyed();
// Default to a standard frame. Can be overriden to provide custom
// frame styles, like a full desktop frame to simulate a modal window
property var frame;
Component {
id: defaultFrameBuilder;
DefaultFrame { anchors.fill: parent }
}
Component {
id: modalFrameBuilder;
ModalFrame { anchors.fill: parent }
}
Component.onCompleted: {
if (!frame) {
if (modality === Qt.NonModal) {
frame = defaultFrameBuilder.createObject(window);
} else {
frame = modalFrameBuilder.createObject(window);
}
}
raise();
}
children: [ frame, content, activator ]
signal windowDestroyed();
Component.onCompleted: {
fadeTargetProperty = visible ? 1.0 : 0.0
raise();
}
Component.onDestruction: {
content.destroy();
@ -76,17 +101,15 @@ FocusScope {
}
onParentChanged: raise();
Connections {
target: frame
onRaise: window.raise();
onClose: window.close();
onPin: window.pin();
onDeltaSize: {
var newSize = Qt.vector2d(content.width + dx, content.height + dy);
newSize = clampVector(newSize, minSize, maxSize);
window.width = newSize.x
window.height = newSize.y
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
destroy();
return;
}
if (visible) {
raise();
}
enabled = visible
}
function raise() {
@ -114,67 +137,6 @@ FocusScope {
visible = false;
}
//
// Enable window visibility transitions
//
// The target property to animate, usually scale or opacity
property alias fadeTargetProperty: window.opacity
// always start the property at 0 to enable fade in on creation
opacity: 0
// Some dialogs should be destroyed when they become
// invisible, so handle that
onVisibleChanged: {
// If someone directly set the visibility to false
// toggle it back on and use the targetVisible flag to transition
// via fading.
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
var target = visible;
visible = !visible;
fadeTargetProperty = target ? 1.0 : 0.0;
return;
}
if (!visible && destroyOnInvisible) {
console.log("Destroying " + window);
destroy();
return;
}
}
// The offscreen UI will enable an object, rather than manipulating it's
// visibility, so that we can do animations in both directions. Because
// visibility is a boolean flags, it cannot be animated. So when
// targetVisible is changed, we modify a property that can be animated,
// like scale or opacity, and then when the target animation value is reached,
// we can modify the visibility
// The actual animator
Behavior on fadeTargetProperty {
NumberAnimation {
duration: hifi.effects.fadeInDuration
easing.type: Easing.OutCubic
}
}
// Once we're transparent, disable the dialog's visibility
onFadeTargetPropertyChanged: {
visible = (fadeTargetProperty != 0.0);
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (event.modifiers === Qt.ControlModifier) {
event.accepted = true
visible = false
}
break;
}
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
@ -185,4 +147,14 @@ FocusScope {
clamp(value.y, min.y, max.y))
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (window.closable && (event.modifiers === Qt.ControlModifier)) {
visible = false
event.accepted = true
}
break
}
}
}

View file

@ -80,7 +80,6 @@
#include <controllers/StateController.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <MessageDialog.h>
#include <MessagesClient.h>
#include <ModelEntityItem.h>
#include <NetworkAccessManager.h>
@ -149,7 +148,6 @@
#endif
#include "Stars.h"
#include "ui/AddressBarDialog.h"
#include "ui/RecorderDialog.h"
#include "ui/AvatarInputs.h"
#include "ui/AssetUploadDialogFactory.h"
#include "ui/DataWebDialog.h"
@ -1176,10 +1174,8 @@ void Application::initializeGL() {
void Application::initializeUi() {
AddressBarDialog::registerType();
RecorderDialog::registerType();
ErrorDialog::registerType();
LoginDialog::registerType();
MessageDialog::registerType();
VrMenu::registerType();
Tooltip::registerType();
UpdateDialog::registerType();
@ -1843,14 +1839,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_X:
if (isMeta && isShifted) {
// auto offscreenUi = DependencyManager::get<OffscreenUi>();
// offscreenUi->load("TestControllers.qml");
RecorderDialog::toggle();
}
break;
case Qt::Key_L:
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::Log);
@ -4544,7 +4532,7 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name)
void Application::toggleRunningScriptsWidget() {
static const QUrl url("dialogs/RunningScripts.qml");
DependencyManager::get<OffscreenUi>()->toggle(url, "RunningScripts");
DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");
//if (_runningScriptsWidget->isVisible()) {
// if (_runningScriptsWidget->hasFocus()) {
// _runningScriptsWidget->hide();

View file

@ -168,9 +168,9 @@ QScriptValue WindowScriptingInterface::showAlert(const QString& message) {
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
QScriptValue WindowScriptingInterface::showConfirm(const QString& message) {
bool confirm = false;
OffscreenUi::question("", message, [&](QMessageBox::StandardButton response){
confirm = (response == QMessageBox::Yes);
});
if (QMessageBox::Yes == OffscreenUi::question("", message)) {
confirm = true;
}
return QScriptValue(confirm);
}

View file

@ -61,10 +61,10 @@ void AddressBarDialog::loadForward() {
}
void AddressBarDialog::displayAddressOfflineMessage() {
OffscreenUi::error("That user or place is currently offline");
OffscreenUi::critical("", "That user or place is currently offline");
}
void AddressBarDialog::displayAddressNotFoundMessage() {
OffscreenUi::error("There is no address information for that user or place");
OffscreenUi::critical("", "There is no address information for that user or place");
}

View file

@ -147,10 +147,6 @@ void DialogsManager::lodTools() {
_lodToolsDialog->raise();
}
void DialogsManager::toggleToolWindow() {
DependencyManager::get<OffscreenUi>()->toggleToolWindow();
}
void DialogsManager::hmdTools(bool showTools) {
if (showTools) {
if (!_hmdToolsDialog) {

View file

@ -66,7 +66,6 @@ signals:
void addressBarToggled();
private slots:
void toggleToolWindow();
void hmdToolsClosed();
private:

View file

@ -1,29 +0,0 @@
//
// MarketplaceDialog.cpp
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "MarketplaceDialog.h"
#include "DependencyManager.h"
HIFI_QML_DEF(MarketplaceDialog)
MarketplaceDialog::MarketplaceDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) {
}
bool MarketplaceDialog::navigationRequested(const QString& url) {
qDebug() << url;
if (qApp->canAcceptURL(url)) {
if (qApp->acceptURL(url)) {
return false; // we handled it, so QWebPage doesn't need to handle it
}
}
return true;
}

View file

@ -1,29 +0,0 @@
//
// MarketplaceDialog.h
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_MarketplaceDialog_h
#define hifi_MarketplaceDialog_h
#include <OffscreenQmlDialog.h>
class MarketplaceDialog : public OffscreenQmlDialog
{
Q_OBJECT
HIFI_QML_DECL
public:
MarketplaceDialog(QQuickItem* parent = nullptr);
Q_INVOKABLE bool navigationRequested(const QString& url);
};
#endif

View file

@ -1,22 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/11/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RecorderDialog.h"
#include <QMessageBox>
#include "DependencyManager.h"
HIFI_QML_DEF(RecorderDialog)
RecorderDialog::RecorderDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) {
}
void RecorderDialog::hide() {
((QQuickItem*)parent())->setEnabled(false);
}

View file

@ -1,28 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/11/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_RecorderDialog_h
#define hifi_RecorderDialog_h
#include <OffscreenQmlDialog.h>
class RecorderDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
public:
RecorderDialog(QQuickItem* parent = nullptr);
signals:
protected:
void hide();
};
#endif

View file

@ -1,119 +0,0 @@
//
// MessageDialog.cpp
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MessageDialog.h"
HIFI_QML_DEF(MessageDialog)
MessageDialog::MessageDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) {
_buttons = StandardButtons(Ok | Cancel);
}
MessageDialog::~MessageDialog() {
}
QString MessageDialog::text() const {
return _text;
}
QString MessageDialog::informativeText() const {
return _informativeText;
}
QString MessageDialog::detailedText() const {
return _detailedText;
}
MessageDialog::Icon MessageDialog::icon() const {
return _icon;
}
void MessageDialog::setVisible(bool v) {
OffscreenQmlDialog::setVisible(v);
}
void MessageDialog::setText(const QString& arg) {
if (arg != _text) {
_text = arg;
emit textChanged();
}
}
void MessageDialog::setInformativeText(const QString& arg) {
if (arg != _informativeText) {
_informativeText = arg;
emit informativeTextChanged();
}
}
void MessageDialog::setDetailedText(const QString& arg) {
if (arg != _detailedText) {
_detailedText = arg;
emit detailedTextChanged();
}
}
void MessageDialog::setIcon(MessageDialog::Icon icon) {
if (icon != _icon) {
_icon = icon;
emit iconChanged();
}
}
void MessageDialog::setStandardButtons(StandardButtons buttons) {
if (buttons != _buttons) {
_buttons = buttons;
emit standardButtonsChanged();
}
}
void MessageDialog::click(StandardButton button) {
// FIXME try to do it more like the standard dialog
click(StandardButton(button), ButtonRole::NoRole);
}
MessageDialog::StandardButtons MessageDialog::standardButtons() const {
return _buttons;
}
MessageDialog::StandardButton MessageDialog::clickedButton() const {
return _clickedButton;
}
void MessageDialog::click(StandardButton button, ButtonRole) {
_clickedButton = button;
if (_resultCallback) {
_resultCallback(QMessageBox::StandardButton(_clickedButton));
}
hide();
}
void MessageDialog::accept() {
// enter key is treated like OK
if (_clickedButton == NoButton)
_clickedButton = Ok;
if (_resultCallback) {
_resultCallback(QMessageBox::StandardButton(_clickedButton));
}
OffscreenQmlDialog::accept();
}
void MessageDialog::reject() {
// escape key is treated like cancel
if (_clickedButton == NoButton)
_clickedButton = Cancel;
if (_resultCallback) {
_resultCallback(QMessageBox::StandardButton(_clickedButton));
}
OffscreenQmlDialog::reject();
}
void MessageDialog::setResultCallback(OffscreenUi::ButtonCallback callback) {
_resultCallback = callback;
}

View file

@ -1,100 +0,0 @@
//
// MessageDialog.h
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_MessageDialog_h
#define hifi_MessageDialog_h
#include "OffscreenQmlDialog.h"
class MessageDialog : public OffscreenQmlDialog
{
Q_OBJECT
HIFI_QML_DECL
private:
Q_ENUMS(Icon)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged)
Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText NOTIFY detailedTextChanged)
Q_PROPERTY(Icon icon READ icon WRITE setIcon NOTIFY iconChanged)
Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons NOTIFY standardButtonsChanged)
Q_PROPERTY(StandardButton clickedButton READ clickedButton NOTIFY buttonClicked)
public:
enum Icon { NoIcon, Information, Warning, Critical, Question };
enum ButtonRole {
// keep this in sync with QDialogButtonBox::ButtonRole and QPlatformDialogHelper::ButtonRole
InvalidRole = -1,
AcceptRole,
RejectRole,
DestructiveRole,
ActionRole,
HelpRole,
YesRole,
NoRole,
ResetRole,
ApplyRole,
NRoles
};
MessageDialog(QQuickItem* parent = 0);
virtual ~MessageDialog();
QString text() const;
QString informativeText() const;
QString detailedText() const;
Icon icon() const;
public slots:
virtual void setVisible(bool v);
void setText(const QString& arg);
void setInformativeText(const QString& arg);
void setDetailedText(const QString& arg);
void setIcon(Icon icon);
void setStandardButtons(StandardButtons buttons);
void setResultCallback(OffscreenUi::ButtonCallback callback);
void click(StandardButton button);
StandardButtons standardButtons() const;
StandardButton clickedButton() const;
signals:
void textChanged();
void informativeTextChanged();
void detailedTextChanged();
void iconChanged();
void standardButtonsChanged();
void buttonClicked();
void discard();
void help();
void yes();
void no();
void apply();
void reset();
protected slots:
virtual void click(StandardButton button, ButtonRole);
virtual void accept();
virtual void reject();
private:
QString _title;
QString _text;
QString _informativeText;
QString _detailedText;
Icon _icon{ Information };
StandardButtons _buttons;
StandardButton _clickedButton{ NoButton };
OffscreenUi::ButtonCallback _resultCallback;
};
#endif // hifi_MessageDialog_h

View file

@ -17,7 +17,7 @@ OffscreenQmlDialog::~OffscreenQmlDialog() {
}
void OffscreenQmlDialog::hide() {
static_cast<QQuickItem*>(parent())->setEnabled(false);
static_cast<QQuickItem*>(parent())->setVisible(false);
}
QString OffscreenQmlDialog::title() const {

View file

@ -16,10 +16,6 @@
#include <AbstractUriHandler.h>
#include <AccountManager.h>
#include "ErrorDialog.h"
#include "MessageDialog.h"
// Needs to match the constants in resources/qml/Global.js
class OffscreenFlags : public QObject {
Q_OBJECT
@ -117,7 +113,7 @@ void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(
item = getRootItem()->findChild<QQuickItem*>(name);
}
if (item) {
item->setEnabled(true);
item->setVisible(true);
}
}
@ -129,109 +125,103 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
item = getRootItem()->findChild<QQuickItem*>(name);
}
if (item) {
item->setEnabled(!item->isEnabled());
item->setVisible(!item->isVisible());
}
}
void OffscreenUi::messageBox(const QString& title, const QString& text,
ButtonCallback callback,
QMessageBox::Icon icon,
QMessageBox::StandardButtons buttons) {
MessageDialog* pDialog{ nullptr };
MessageDialog::show([&](QQmlContext* ctx, QObject* item) {
pDialog = item->findChild<MessageDialog*>();
pDialog->setIcon((MessageDialog::Icon)icon);
pDialog->setTitle(title);
pDialog->setText(text);
pDialog->setStandardButtons(MessageDialog::StandardButtons(static_cast<int>(buttons)));
pDialog->setResultCallback(callback);
});
pDialog->setEnabled(true);
}
class MessageBoxListener : public QObject {
Q_OBJECT
void OffscreenUi::information(const QString& title, const QString& text,
ButtonCallback callback,
QMessageBox::StandardButtons buttons) {
messageBox(title, text, callback,
static_cast<QMessageBox::Icon>(MessageDialog::Information), buttons);
}
friend class OffscreenUi;
void OffscreenUi::question(const QString& title, const QString& text,
ButtonCallback callback,
QMessageBox::StandardButtons buttons) {
bool waiting = true;
ButtonCallback blockingCallback = [&](QMessageBox::StandardButton response){
callback(response); // call the actual callback
waiting = false;
};
messageBox(title, text, blockingCallback,
static_cast<QMessageBox::Icon>(MessageDialog::Question), buttons);
// block until the call back has been called
while (waiting) {
QCoreApplication::processEvents();
MessageBoxListener(QQuickItem* messageBox) : _messageBox(messageBox) {
if (!_messageBox) {
_finished = true;
return;
}
connect(_messageBox, SIGNAL(selected(int)), this, SLOT(onSelected(int)));
connect(_messageBox, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
}
~MessageBoxListener() {
disconnect(_messageBox);
}
QMessageBox::StandardButton waitForResult() {
while (!_finished) {
QCoreApplication::processEvents();
}
return _result;
}
private slots:
void onSelected(int button) {
_result = static_cast<QMessageBox::StandardButton>(button);
_finished = true;
disconnect(_messageBox);
}
void onDestroyed() {
_finished = true;
disconnect(_messageBox);
}
private:
bool _finished { false };
QMessageBox::StandardButton _result { QMessageBox::StandardButton::NoButton };
QQuickItem* const _messageBox;
};
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;
}
QVariantMap map;
map.insert("title", title);
map.insert("text", text);
map.insert("icon", icon);
map.insert("buttons", buttons.operator int());
map.insert("defaultButton", defaultButton);
QVariant result;
bool invokeResult = QMetaObject::invokeMethod(getDesktop(), "messageBox",
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, QVariant::fromValue(map)));
if (!invokeResult) {
qWarning() << "Failed to create message box";
return QMessageBox::StandardButton::NoButton;
}
auto resultButton = MessageBoxListener(qvariant_cast<QQuickItem*>(result)).waitForResult();
qDebug() << "Message box got a result of " << resultButton;
return resultButton;
}
QMessageBox::StandardButton OffscreenUi::question(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
QMessageBox::StandardButton result = defaultButton;
OffscreenUi::question(title, text, [&](QMessageBox::StandardButton response){
result = response;
}, buttons);
return result;
}
void OffscreenUi::warning(const QString& title, const QString& text,
ButtonCallback callback,
QMessageBox::StandardButtons buttons) {
messageBox(title, text, callback,
static_cast<QMessageBox::Icon>(MessageDialog::Warning), buttons);
}
QMessageBox::StandardButton OffscreenUi::warning(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
bool waiting = true;
QMessageBox::StandardButton result = defaultButton;
OffscreenUi::warning(title, text, [&](QMessageBox::StandardButton response){
result = response;
waiting = false;
}, buttons);
// block until the call back has been called
while (waiting) {
QCoreApplication::processEvents();
}
return result;
return DependencyManager::get<OffscreenUi>()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton);
}
void OffscreenUi::critical(const QString& title, const QString& text,
ButtonCallback callback,
QMessageBox::StandardButtons buttons) {
messageBox(title, text, callback,
static_cast<QMessageBox::Icon>(MessageDialog::Critical), buttons);
QMessageBox::StandardButton OffscreenUi::information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
return DependencyManager::get<OffscreenUi>()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton);
}
void OffscreenUi::error(const QString& text) {
ErrorDialog* pDialog{ nullptr };
ErrorDialog::show([&](QQmlContext* ctx, QObject* item) {
pDialog = item->findChild<ErrorDialog*>();
pDialog->setText(text);
});
pDialog->setEnabled(true);
QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
return DependencyManager::get<OffscreenUi>()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton);
}
QMessageBox::StandardButton OffscreenUi::warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
return DependencyManager::get<OffscreenUi>()->messageBox(QMessageBox::Icon::Critical, title, text, buttons, defaultButton);
}
OffscreenUi::ButtonCallback OffscreenUi::NO_OP_CALLBACK = [](QMessageBox::StandardButton) {};
bool OffscreenUi::navigationFocused() {
return offscreenFlags->isNavigationFocused();
@ -245,16 +235,14 @@ void OffscreenUi::createDesktop() {
if (_desktop) {
qDebug() << "Desktop already created";
}
getRootContext()->setContextProperty("DebugQML", false);
_desktop = dynamic_cast<QQuickItem*>(load("Root.qml"));
Q_ASSERT(_desktop);
getRootContext()->setContextProperty("Desktop", _desktop);
_desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
}
void OffscreenUi::toggleToolWindow() {
_toolWindow->setEnabled(!_toolWindow->isEnabled());
}
QQuickItem* OffscreenUi::getDesktop() {
return _desktop;
}

View file

@ -40,45 +40,46 @@ public:
Q_INVOKABLE void executeOnUiThread(std::function<void()> function);
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
// Messagebox replacement functions
using ButtonCallback = std::function<void(QMessageBox::StandardButton)>;
static ButtonCallback NO_OP_CALLBACK;
static void messageBox(const QString& title, const QString& text,
ButtonCallback f,
QMessageBox::Icon icon,
QMessageBox::StandardButtons buttons);
static void information(const QString& title, const QString& text,
ButtonCallback callback = NO_OP_CALLBACK,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
/// Note: will block until user clicks a response to the question
static void question(const QString& title, const QString& text,
ButtonCallback callback = NO_OP_CALLBACK,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No));
/// 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,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
return critical(title, text, buttons, defaultButton);
}
/// Same design as QMessageBox::information(), will block, returns result
static QMessageBox::StandardButton information(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
return information(title, text, buttons, defaultButton);
}
/// Same design as QMessageBox::question(), will block, returns result
static QMessageBox::StandardButton question(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
static void warning(const QString& title, const QString& text,
ButtonCallback callback = NO_OP_CALLBACK,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static QMessageBox::StandardButton question(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
return question(title, text, buttons, defaultButton);
}
/// Same design as QMessageBox::warning(), will block, returns result
static QMessageBox::StandardButton warning(void* ignored, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
return warning(title, text, buttons, defaultButton);
}
static QMessageBox::StandardButton critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
static QMessageBox::StandardButton information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
static QMessageBox::StandardButton question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
static QMessageBox::StandardButton warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
static void critical(const QString& title, const QString& text,
ButtonCallback callback = NO_OP_CALLBACK,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
QMessageBox::StandardButton messageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
static void error(const QString& text); // Interim dialog in new style
void toggleToolWindow();
private:
QQuickItem* _desktop { nullptr };
QQuickItem* _toolWindow { nullptr };

View file

@ -250,16 +250,13 @@ QQuickItem* QmlWindowClass::asQuickItem() const {
}
void QmlWindowClass::setVisible(bool visible) {
// For tool window tabs we special case visiblility as enable / disable of the tab, not the window
// The tool window itself has special logic based on whether any tabs are enabled
QQuickItem* targetWindow = asQuickItem();
if (_toolWindow) {
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow);
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetTab->setEnabled(visible);
//emit visibilityChanged(visible);
});
// For tool window tabs we special case visibility as a function call on the tab parent
// The tool window itself has special logic based on whether any tabs are visible
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
} else {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setVisible(visible);
//emit visibilityChanged(visible);
@ -282,7 +279,6 @@ bool QmlWindowClass::isVisible() const {
}
}
glm::vec2 QmlWindowClass::getPosition() const {
QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {

View file

@ -1,33 +1 @@
set(TARGET_NAME "ui-test")
# This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Network OpenGL Qml Quick Script WebChannel WebEngine WebSockets)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
if (WIN32)
target_link_libraries(${TARGET_NAME} wsock32.lib opengl32.lib Winmm.lib)
# Issue causes build failure unless we add this directory.
# See https://bugreports.qt.io/browse/QTBUG-43351
add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
endif()
# link in the shared libraries
link_hifi_libraries(shared networking gl gpu ui)
# copy the resources files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/qml"
$<TARGET_FILE_DIR:${TARGET_NAME}>/qml
)
target_glew()
if (WIN32)
set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/../../interface/resources/qml")
endif()
package_libraries_for_deployment()
# This folder contains code for testing the QML UI using Qt Creator. It is not intended to be included in the CMake project

734
tests/ui/qml/StubMenu.qml Normal file
View file

@ -0,0 +1,734 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../interface/resources/qml/hifi"
Menu {
property var menuOption: MenuOption {}
Item {
Action {
id: login;
text: menuOption.login
}
Action {
id: update;
text: "Update";
enabled: false
}
Action {
id: crashReporter;
text: "Crash Reporter...";
enabled: false
}
Action {
id: help;
text: menuOption.help
onTriggered: Application.showHelp()
}
Action {
id: aboutApp;
text: menuOption.aboutApp
}
Action {
id: quit;
text: menuOption.quit
}
ExclusiveGroup { id: renderResolutionGroup }
Action {
id: renderResolutionOne;
exclusiveGroup: renderResolutionGroup;
text: menuOption.renderResolutionOne;
checkable: true;
checked: true
}
Action {
id: renderResolutionTwoThird;
exclusiveGroup: renderResolutionGroup;
text: menuOption.renderResolutionTwoThird;
checkable: true
}
Action {
id: renderResolutionHalf;
exclusiveGroup: renderResolutionGroup;
text: menuOption.renderResolutionHalf;
checkable: true
}
Action {
id: renderResolutionThird;
exclusiveGroup: renderResolutionGroup;
text: menuOption.renderResolutionThird;
checkable: true
}
Action {
id: renderResolutionQuarter;
exclusiveGroup: renderResolutionGroup;
text: menuOption.renderResolutionQuarter;
checkable: true
}
ExclusiveGroup { id: ambientLightGroup }
Action {
id: renderAmbientLightGlobal;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLightGlobal;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight0;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight0;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight1;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight1;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight2;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight2;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight3;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight3;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight4;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight4;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight5;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight5;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight6;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight6;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight7;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight7;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight8;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight8;
checkable: true;
checked: true
}
Action {
id: renderAmbientLight9;
exclusiveGroup: ambientLightGroup;
text: menuOption.renderAmbientLight9;
checkable: true;
checked: true
}
Action {
id: preferences
shortcut: StandardKey.Preferences
text: menuOption.preferences
onTriggered: dialogsManager.editPreferences()
}
}
Menu {
title: "File"
MenuItem {
action: login
}
MenuItem {
action: update
}
MenuItem {
action: help
}
MenuItem {
action: crashReporter
}
MenuItem {
action: aboutApp
}
MenuItem {
action: quit
}
}
Menu {
title: "Edit"
MenuItem {
text: "Undo" }
MenuItem {
text: "Redo" }
MenuItem {
text: menuOption.runningScripts
}
MenuItem {
text: menuOption.loadScript
}
MenuItem {
text: menuOption.loadScriptURL
}
MenuItem {
text: menuOption.stopAllScripts
}
MenuItem {
text: menuOption.reloadAllScripts
}
MenuItem {
text: menuOption.scriptEditor
}
MenuItem {
text: menuOption.console_
}
MenuItem {
text: menuOption.reloadContent
}
MenuItem {
text: menuOption.packageModel
}
}
Menu {
title: "Audio"
MenuItem {
text: menuOption.muteAudio;
checkable: true
}
MenuItem {
text: menuOption.audioTools;
checkable: true
}
}
Menu {
title: "Avatar"
// Avatar > Attachments...
MenuItem {
text: menuOption.attachments
}
Menu {
title: "Size"
// Avatar > Size > Increase
MenuItem {
text: menuOption.increaseAvatarSize
}
// Avatar > Size > Decrease
MenuItem {
text: menuOption.decreaseAvatarSize
}
// Avatar > Size > Reset
MenuItem {
text: menuOption.resetAvatarSize
}
}
// Avatar > Reset Sensors
MenuItem {
text: menuOption.resetSensors
}
}
Menu {
title: "Display"
}
Menu {
title: "View"
ExclusiveGroup {
id: cameraModeGroup
}
MenuItem {
text: menuOption.firstPerson;
checkable: true;
exclusiveGroup: cameraModeGroup
}
MenuItem {
text: menuOption.thirdPerson;
checkable: true;
exclusiveGroup: cameraModeGroup
}
MenuItem {
text: menuOption.fullscreenMirror;
checkable: true;
exclusiveGroup: cameraModeGroup
}
MenuItem {
text: menuOption.independentMode;
checkable: true;
exclusiveGroup: cameraModeGroup
}
MenuItem {
text: menuOption.cameraEntityMode;
checkable: true;
exclusiveGroup: cameraModeGroup
}
MenuSeparator{}
MenuItem {
text: menuOption.miniMirror;
checkable: true
}
}
Menu {
title: "Navigate"
MenuItem {
text: "Home" }
MenuItem {
text: menuOption.addressBar
}
MenuItem {
text: "Directory" }
MenuItem {
text: menuOption.copyAddress
}
MenuItem {
text: menuOption.copyPath
}
}
Menu {
title: "Settings"
MenuItem {
text: "Advanced Menus" }
MenuItem {
text: "Developer Menus" }
MenuItem {
text: menuOption.preferences
}
MenuItem {
text: "Avatar..." }
MenuItem {
text: "Audio..." }
MenuItem {
text: "LOD..." }
MenuItem {
text: menuOption.inputMenu
}
}
Menu {
title: "Developer"
Menu {
title: "Render"
MenuItem {
text: menuOption.atmosphere;
checkable: true
}
MenuItem {
text: menuOption.worldAxes;
checkable: true
}
MenuItem {
text: menuOption.debugAmbientOcclusion;
checkable: true
}
MenuItem {
text: menuOption.antialiasing;
checkable: true
}
MenuItem {
text: menuOption.stars;
checkable: true
}
Menu {
title: menuOption.renderAmbientLight
MenuItem {
action: renderAmbientLightGlobal; }
MenuItem {
action: renderAmbientLight0; }
MenuItem {
action: renderAmbientLight1; }
MenuItem {
action: renderAmbientLight2; }
MenuItem {
action: renderAmbientLight3; }
MenuItem {
action: renderAmbientLight4; }
MenuItem {
action: renderAmbientLight5; }
MenuItem {
action: renderAmbientLight6; }
MenuItem {
action: renderAmbientLight7; }
MenuItem {
action: renderAmbientLight8; }
MenuItem {
action: renderAmbientLight9; }
}
MenuItem {
text: menuOption.throttleFPSIfNotFocus;
checkable: true
}
Menu {
title: menuOption.renderResolution
MenuItem {
action: renderResolutionOne
}
MenuItem {
action: renderResolutionTwoThird
}
MenuItem {
action: renderResolutionHalf
}
MenuItem {
action: renderResolutionThird
}
MenuItem {
action: renderResolutionQuarter
}
}
MenuItem {
text: menuOption.lodTools
}
}
Menu {
title: "Assets"
MenuItem {
text: menuOption.uploadAsset
}
MenuItem {
text: menuOption.assetMigration
}
}
Menu {
title: "Avatar"
Menu {
title: "Face Tracking"
MenuItem {
text: menuOption.noFaceTracking;
checkable: true
}
MenuItem {
text: menuOption.faceshift;
checkable: true
}
MenuItem {
text: menuOption.useCamera;
checkable: true
}
MenuSeparator{}
MenuItem {
text: menuOption.binaryEyelidControl;
checkable: true
}
MenuItem {
text: menuOption.coupleEyelids;
checkable: true
}
MenuItem {
text: menuOption.useAudioForMouth;
checkable: true
}
MenuItem {
text: menuOption.velocityFilter;
checkable: true
}
MenuItem {
text: menuOption.calibrateCamera
}
MenuSeparator{}
MenuItem {
text: menuOption.muteFaceTracking;
checkable: true
}
MenuItem {
text: menuOption.autoMuteAudio;
checkable: true
}
}
Menu {
title: "Eye Tracking"
MenuItem {
text: menuOption.sMIEyeTracking;
checkable: true
}
Menu {
title: "Calibrate"
MenuItem {
text: menuOption.onePointCalibration
}
MenuItem {
text: menuOption.threePointCalibration
}
MenuItem {
text: menuOption.fivePointCalibration
}
}
MenuItem {
text: menuOption.simulateEyeTracking;
checkable: true
}
}
MenuItem {
text: menuOption.avatarReceiveStats;
checkable: true
}
MenuItem {
text: menuOption.renderBoundingCollisionShapes;
checkable: true
}
MenuItem {
text: menuOption.renderLookAtVectors;
checkable: true
}
MenuItem {
text: menuOption.renderLookAtTargets;
checkable: true
}
MenuItem {
text: menuOption.renderFocusIndicator;
checkable: true
}
MenuItem {
text: menuOption.showWhosLookingAtMe;
checkable: true
}
MenuItem {
text: menuOption.fixGaze;
checkable: true
}
MenuItem {
text: menuOption.animDebugDrawDefaultPose;
checkable: true
}
MenuItem {
text: menuOption.animDebugDrawAnimPose;
checkable: true
}
MenuItem {
text: menuOption.animDebugDrawPosition;
checkable: true
}
MenuItem {
text: menuOption.meshVisible;
checkable: true
}
MenuItem {
text: menuOption.disableEyelidAdjustment;
checkable: true
}
MenuItem {
text: menuOption.turnWithHead;
checkable: true
}
MenuItem {
text: menuOption.comfortMode;
checkable: true
}
MenuItem {
text: menuOption.keyboardMotorControl;
checkable: true
}
MenuItem {
text: menuOption.scriptedMotorControl;
checkable: true
}
MenuItem {
text: menuOption.enableCharacterController;
checkable: true
}
}
Menu {
title: "Hands"
MenuItem {
text: menuOption.displayHandTargets;
checkable: true
}
MenuItem {
text: menuOption.lowVelocityFilter;
checkable: true
}
Menu {
title: "Leap Motion"
MenuItem {
text: menuOption.leapMotionOnHMD;
checkable: true
}
}
}
Menu {
title: "Entities"
MenuItem {
text: menuOption.octreeStats
}
MenuItem {
text: menuOption.showRealtimeEntityStats;
checkable: true
}
}
Menu {
title: "Network"
MenuItem {
text: menuOption.reloadContent
}
MenuItem {
text: menuOption.disableNackPackets;
checkable: true
}
MenuItem {
text: menuOption.disableActivityLogger;
checkable: true
}
MenuItem {
text: menuOption.cachesSize
}
MenuItem {
text: menuOption.diskCacheEditor
}
MenuItem {
text: menuOption.showDSConnectTable
}
MenuItem {
text: menuOption.bandwidthDetails
}
}
Menu {
title: "Timing"
Menu {
title: "Performance Timer"
MenuItem {
text: menuOption.displayDebugTimingDetails;
checkable: true
}
MenuItem {
text: menuOption.onlyDisplayTopTen;
checkable: true
}
MenuItem {
text: menuOption.expandUpdateTiming;
checkable: true
}
MenuItem {
text: menuOption.expandMyAvatarTiming;
checkable: true
}
MenuItem {
text: menuOption.expandMyAvatarSimulateTiming;
checkable: true
}
MenuItem {
text: menuOption.expandOtherAvatarTiming;
checkable: true
}
MenuItem {
text: menuOption.expandPaintGLTiming;
checkable: true
}
}
MenuItem {
text: menuOption.frameTimer;
checkable: true
}
MenuItem {
text: menuOption.runTimingTests
}
MenuItem {
text: menuOption.pipelineWarnings;
checkable: true
}
MenuItem {
text: menuOption.logExtraTimings;
checkable: true
}
MenuItem {
text: menuOption.suppressShortTimings;
checkable: true
}
}
Menu {
title: "Audio"
MenuItem {
text: menuOption.audioNoiseReduction;
checkable: true
}
MenuItem {
text: menuOption.echoServerAudio;
checkable: true
}
MenuItem {
text: menuOption.echoLocalAudio;
checkable: true
}
MenuItem {
text: menuOption.muteEnvironment
}
Menu {
title: "Audio"
MenuItem {
text: menuOption.audioScope;
checkable: true
}
MenuItem {
text: menuOption.audioScopePause;
checkable: true
}
Menu {
title: "Display Frames"
ExclusiveGroup {
id: audioScopeFramesGroup
}
MenuItem {
exclusiveGroup: audioScopeFramesGroup;
text: menuOption.audioScopeFiveFrames;
checkable: true
}
MenuItem {
exclusiveGroup: audioScopeFramesGroup;
text: menuOption.audioScopeTwentyFrames;
checkable: true
}
MenuItem {
exclusiveGroup: audioScopeFramesGroup;
text: menuOption.audioScopeFiftyFrames;
checkable: true
}
}
MenuItem {
text: menuOption.audioNetworkStats
}
}
}
Menu {
title: "Physics"
MenuItem {
text: menuOption.physicsShowOwned;
checkable: true
}
MenuItem {
text: menuOption.physicsShowHulls;
checkable: true
}
}
MenuItem {
text: menuOption.displayCrashOptions;
checkable: true
}
MenuItem {
text: menuOption.crashInterface
}
MenuItem {
text: menuOption.log
}
MenuItem {
text: menuOption.stats;
checkable: true
}
}
}

View file

@ -20,7 +20,7 @@ Item {
Item {
objectName: "ScriptDiscoveryService"
property var scriptsModelFilter: scriptsModel
//property var scriptsModelFilter: scriptsModel
signal scriptCountChanged()
property var _runningScripts:[
{ name: "wireFrameTest.js", url: "foo/wireframetest.js", path: "foo/wireframetest.js", local: true },
@ -106,7 +106,7 @@ Item {
function closeDialog(item) {
item = findDialog(item);
if (item) {
item.enabled = false
item.visible = false
} else {
console.warn("Could not find top level dialog")
}

View file

@ -42,7 +42,6 @@ HifiControls.VrDialog {
}
Component.onCompleted: {
enabled = true
var xboxRegex = /^GamePad/;
var hydraRegex = /^Hydra/;
for (var prop in Controller.Hardware) {

336
tests/ui/qml/main.qml Normal file
View file

@ -0,0 +1,336 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import Qt.labs.settings 1.0
import "../../../interface/resources/qml"
import "../../../interface/resources/qml/windows"
import "../../../interface/resources/qml/dialogs"
ApplicationWindow {
id: appWindow
visible: true
width: 1280
height: 720
title: qsTr("Scratch App")
Component { id: listModelBuilder; ListModel{} }
function menuItemsToModel(menu) {
var items = menu.items
var newListModel = listModelBuilder.createObject(desktop);
for (var i = 0; i < items.length; ++i) {
var item = items[i];
switch (item.type) {
case 2:
newListModel.append({"type":item.type, "name": item.title, "item": item})
break;
case 1:
newListModel.append({"type":item.type, "name": item.text, "item": item})
break;
case 0:
newListModel.append({"type":item.type, "name": "-----", "item": item})
break;
}
}
return newListModel;
}
Root {
id: desktop
anchors.fill: parent
StubMenu { id: stubMenu }
Row {
id: testButtons
anchors { margins: 8; left: parent.left; top: parent.top }
spacing: 8
property int count: 0
property var tabs: [];
property var urls: [];
Button {
text: "restore all"
onClicked: {
for (var i = 0; i < desktop.windows.length; ++i) {
desktop.windows[i].visible = true
}
}
}
Button {
text: "toggle blue visible"
onClicked: {
blue.visible = !blue.visible
}
}
Button {
text: "toggle blue enabled"
onClicked: {
blue.enabled = !blue.enabled
}
}
// Button {
// text: "add web tab"
// onClicked: {
// testButtons.urls.push("http://slashdot.org?" + testButtons.count++);
// testButtons.tabs.push(desktop.toolWindow.addWebTab({ title: "test", source: testButtons.urls[testButtons.urls.length - 1], width: 500, height: 720 }))
// }
// }
// Button {
// text: "toggle tab visible"
// onClicked: {
// var lastUrl = testButtons.urls[testButtons.urls.length - 1];
// var tab = desktop.toolWindow.findTabForUrl(lastUrl);
// desktop.toolWindow.showTabForUrl(lastUrl, !tab.enabled)
// }
// }
// Button {
// text: "Remove last tab"
// onClicked: {
// testButtons.tabs.pop();
// desktop.toolWindow.removeTabForUrl(testButtons.urls.pop());
// }
// }
Button {
text: "Show Long Error"
onClicked: {
desktop.messageBox({
informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ",
text: "Baloney",
icon: OriginalDialogs.StandardIcon.Warning,
detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a"
});
}
}
Button {
text: "Show Error"
onClicked: {
desktop.messageBox({
text: "Diagnostic cycle will be complete in 30 seconds",
icon: OriginalDialogs.StandardIcon.Critical,
});
}
}
Button {
text: "Open File"
property var builder: Component {
FileDialog { }
}
ListModel {
id: jsFilters
ListElement { text: "Javascript Files (*.js)"; filter: "*.js" }
ListElement { text: "All Files (*.*)"; filter: "*.*" }
}
onClicked: {
var fileDialogProperties = {
filterModel: jsFilters
}
var fileDialog = builder.createObject(desktop, fileDialogProperties);
fileDialog.canceled.connect(function(){
console.log("Cancelled")
})
fileDialog.selectedFile.connect(function(file){
console.log("Selected " + file)
})
}
}
Button {
text: "Focus Test"
onClicked: {
var item = desktop;
while (item) {
console.log(item);
item = item.parent;
}
item = appWindow
while (item) {
console.log(item);
item = item.parent;
}
console.log(appWindow.activeFocusItem);
}
}
}
Window {
id: blue
closable: true
visible: true
resizable: true
destroyOnInvisible: false
width: 100; height: 100
x: 1280 / 2; y: 720 / 2
Settings {
category: "TestWindow.Blue"
property alias x: blue.x
property alias y: blue.y
property alias width: blue.width
property alias height: blue.height
}
Rectangle {
anchors.fill: parent
visible: true
color: "blue"
Component.onDestruction: console.log("Blue destroyed")
}
}
Window {
id: green
alwaysOnTop: true
closable: true
visible: true
resizable: false
x: 1280 / 2; y: 720 / 2
Settings {
category: "TestWindow.Green"
property alias x: green.x
property alias y: green.y
property alias width: green.width
property alias height: green.height
}
width: 100; height: 100
Rectangle { anchors.fill: parent; color: "green" }
}
Window {
id: yellow
closable: true
visible: true
resizable: true
width: 100; height: 100
Rectangle {
anchors.fill: parent
visible: true
color: "yellow"
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
Component {
id: menuBuilder
VrMenuView { }
}
onClicked: {
console.log("zzz")
var menuItems = menuItemsToModel(stubMenu);
var newMenu = menuBuilder.createObject(desktop, { source: stubMenu, items: menuItems });
newMenu.x = mouseX
newMenu.y = mouseY
}
}
}
/*
Arcane.Test {
anchors.centerIn: parent
height: 600; width: 600
}
Item {
id: desktop
anchors.fill: parent
objectName: Desktop._OFFSCREEN_ROOT_OBJECT_NAME
property bool uiVisible: true
property variant toolbars: { "_root" : null }
focus: true
Rectangle {
id: root
Vr.Constants { id: vr }
implicitWidth: 384; implicitHeight: 640
anchors.centerIn: parent
color: vr.windows.colors.background
border.color: vr.controls.colors.background
border.width: vr.styles.borderWidth
radius: vr.styles.borderRadius
RunningScripts { }
}
FileDialog {
id: fileDialog
width: 800; height: 600
anchors.centerIn: parent
onSelectedFile: console.log("Chose file " + file)
}
Timer {
id: timer
running: false
interval: 100
onTriggered: wireFrameContainer.enabled = true
}
Item {
id: wireFrameContainer
objectName: Desktop._OFFSCREEN_DIALOG_OBJECT_NAME
anchors.fill: parent
onEnabledChanged: if (!enabled) timer.running = true
NewUi.Main {
id: wireFrame
anchors.fill: parent
property var offscreenFlags: Item {
property bool navigationFocused: false
}
property var urlHandler: Item {
function fixupUrl(url) {
var urlString = url.toString();
if (urlString.indexOf("https://metaverse.highfidelity.com/") !== -1 &&
urlString.indexOf("access_token") === -1) {
console.log("metaverse URL, fixing")
return urlString + "?access_token=875885020b1d5f1ea694ce971c8601fa33ffd77f61851be01ed1e3fde8cabbe9"
}
return url
}
function canHandleUrl(url) {
var urlString = url.toString();
if (urlString.indexOf("hifi://") === 0) {
console.log("Can handle hifi addresses: " + urlString)
return true;
}
if (urlString.indexOf(".svo.json?") !== -1) {
console.log("Can handle svo json addresses: " + urlString)
return true;
}
if (urlString.indexOf(".js?") !== -1) {
console.log("Can handle javascript addresses: " + urlString)
return true;
}
return false
}
function handleUrl(url) {
return true
}
}
property var addressManager: Item {
function navigate(url) {
console.log("Navigate to: " + url);
}
}
}
}
Keys.onMenuPressed: desktop.uiVisible = !desktop.uiVisible
Keys.onEscapePressed: desktop.uiVisible = !desktop.uiVisible
}
*/
}

84
tests/ui/qmlscratch.pro Normal file
View file

@ -0,0 +1,84 @@
TEMPLATE = app
QT += gui qml quick xml webengine widgets
CONFIG += c++11
SOURCES += src/main.cpp
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
DISTFILES += \
qml/UI.js \
qml/main.qml \
qml/Palettes.qml \
qml/StubMenu.qml \
qml/Stubs.qml \
qml/TestControllers.qml \
qml/TestDialog.qml \
qml/TestMenu.qml \
qml/TestRoot.qml \
qml/controlDemo/ButtonPage.qml \
qml/controlDemo/InputPage.qml \
qml/controlDemo/main.qml \
qml/controlDemo/ProgressPage.qml \
../../interface/resources/qml/controller/hydra/HydraButtons.qml \
../../interface/resources/qml/controller/hydra/HydraStick.qml \
../../interface/resources/qml/controller/xbox/DPad.qml \
../../interface/resources/qml/controller/xbox/LeftAnalogStick.qml \
../../interface/resources/qml/controller/xbox/RightAnalogStick.qml \
../../interface/resources/qml/controller/xbox/XboxButtons.qml \
../../interface/resources/qml/controller/AnalogButton.qml \
../../interface/resources/qml/controller/AnalogStick.qml \
../../interface/resources/qml/controller/Hydra.qml \
../../interface/resources/qml/controller/Standard.qml \
../../interface/resources/qml/controller/ToggleButton.qml \
../../interface/resources/qml/controller/Xbox.qml \
../../interface/resources/qml/controls/Button.qml \
../../interface/resources/qml/controls/ButtonAwesome.qml \
../../interface/resources/qml/controls/CheckBox.qml \
../../interface/resources/qml/controls/FontAwesome.qml \
../../interface/resources/qml/controls/Player.qml \
../../interface/resources/qml/controls/Slider.qml \
../../interface/resources/qml/controls/Spacer.qml \
../../interface/resources/qml/controls/SpinBox.qml \
../../interface/resources/qml/controls/Text.qml \
../../interface/resources/qml/controls/TextAndSlider.qml \
../../interface/resources/qml/controls/TextAndSpinBox.qml \
../../interface/resources/qml/controls/TextArea.qml \
../../interface/resources/qml/controls/TextEdit.qml \
../../interface/resources/qml/controls/TextHeader.qml \
../../interface/resources/qml/controls/TextInput.qml \
../../interface/resources/qml/controls/TextInputAndButton.qml \
../../interface/resources/qml/controls/WebView.qml \
../../interface/resources/qml/dialogs/FileDialog.qml \
../../interface/resources/qml/dialogs/MessageDialog.qml \
../../interface/resources/qml/dialogs/RunningScripts.qml \
../../interface/resources/qml/hifi/MenuOption.qml \
../../interface/resources/qml/styles/Border.qml \
../../interface/resources/qml/styles/HifiConstants.qml \
../../interface/resources/qml/windows/DefaultFrame.qml \
../../interface/resources/qml/windows/Fadable.qml \
../../interface/resources/qml/windows/Frame.qml \
../../interface/resources/qml/windows/ModalFrame.qml \
../../interface/resources/qml/windows/Window.qml \
../../interface/resources/qml/AddressBarDialog.qml \
../../interface/resources/qml/AvatarInputs.qml \
../../interface/resources/qml/Browser.qml \
../../interface/resources/qml/InfoView.qml \
../../interface/resources/qml/LoginDialog.qml \
../../interface/resources/qml/QmlWebWindow.qml \
../../interface/resources/qml/QmlWindow.qml \
../../interface/resources/qml/Root.qml \
../../interface/resources/qml/ScrollingGraph.qml \
../../interface/resources/qml/Stats.qml \
../../interface/resources/qml/TextOverlayElement.qml \
../../interface/resources/qml/Tooltip.qml \
../../interface/resources/qml/ToolWindow.qml \
../../interface/resources/qml/UpdateDialog.qml \
../../interface/resources/qml/VrMenu.qml \
../../interface/resources/qml/VrMenuItem.qml \
../../interface/resources/qml/VrMenuView.qml \
../../interface/resources/qml/WebEntity.qml

View file

@ -1,645 +1,65 @@
//
// Created by Bradley Austin Davis on 2015-04-22
// Copyright 2013-2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtWebEngine>
#include <QFileSystemModel>
#include <gl/Config.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLHelpers.h>
#include <memory>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QElapsedTimer>
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <QtCore/QUuid>
#include <QtGui/QWindow>
#include <QtGui/QImage>
#include <QtGui/QGuiApplication>
#include <QtGui/QResizeEvent>
#include <QtGui/QScreen>
#include <gl/QOpenGLContextWrapper.h>
#include <QtScript/QScriptEngine>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlApplicationEngine>
#include <GLMHelpers.h>
#include <gl/OffscreenGLCanvas.h>
#include <OffscreenUi.h>
#include <PathUtils.h>
#include <PathUtils.h>
#include <MessageDialog.h>
#include <VrMenu.h>
#include <InfoView.h>
#include <QmlWebWindowClass.h>
#include <RegisteredMetaTypes.h>
const QString& getResourcesDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
qDebug() << "Resources Path: " << dir;
}
return dir;
QString getRelativeDir(const QString& relativePath = ".") {
QDir path(__FILE__); path.cdUp();
auto result = path.absoluteFilePath(relativePath);
result = path.cleanPath(result) + "/";
return result;
}
const QString& getExamplesDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../../../examples/")) + "/";
qDebug() << "Resources Path: " << dir;
}
return dir;
QString getTestQmlDir() {
return getRelativeDir("../qml");
}
const QString& getInterfaceQmlDir() {
static QString dir;
if (dir.isEmpty()) {
dir = getResourcesDir() + "qml/";
qDebug() << "Qml Path: " << dir;
}
return dir;
}
const QString& getTestQmlDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
qDebug() << "Qml Test Path: " << dir;
}
return dir;
QString getInterfaceQmlDir() {
return getRelativeDir("/");
}
class RateCounter {
std::vector<float> times;
QElapsedTimer timer;
public:
RateCounter() {
timer.start();
void setChild(QQmlApplicationEngine& engine, const char* name) {
for (auto obj : engine.rootObjects()) {
auto child = obj->findChild<QObject*>(QString(name));
if (child) {
engine.rootContext()->setContextProperty(name, child);
return;
}
void reset() {
times.clear();
}
size_t count() const {
return times.size() - 1;
}
float elapsed() const {
if (times.size() < 1) {
return 0.0f;
}
float elapsed = *times.rbegin() - *times.begin();
return elapsed;
}
void increment() {
times.push_back(timer.elapsed() / 1000.0f);
}
float rate() const {
if (elapsed() == 0.0f) {
return 0.0f;
}
return (float) count() / elapsed();
}
};
extern QOpenGLContext* qt_gl_global_share_context();
static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) {
if (engine.hasUncaughtException()) {
const auto backtrace = engine.uncaughtExceptionBacktrace();
const auto exception = engine.uncaughtException().toString();
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
engine.clearExceptions();
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
if (!backtrace.empty()) {
static const auto lineSeparator = "\n ";
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
}
qWarning() << qPrintable(message);
return true;
}
return false;
}
qWarning() << "Could not find object named " << name;
}
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0f / 60.0f) * 1000 * 1000) + 0.5f);
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
QString message = "";
for (int i = 0; i < context->argumentCount(); i++) {
if (i > 0) {
message += " ";
}
message += context->argument(i).toString();
}
qDebug().noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
message = message.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("'", "\\'");
engine->evaluate("Script.print('" + message + "')");
return QScriptValue();
void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath) {
QString resolvedPath = getRelativeDir("../qml");
QUrl resolvedUrl = QUrl::fromLocalFile(resolvedPath);
resolvedPath = resolvedUrl.toString();
engine.addImportPath(resolvedPath);
}
class ScriptEngine : public QScriptEngine {
Q_OBJECT
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
app.setOrganizationName("Some Company");
app.setOrganizationDomain("somecompany.com");
app.setApplicationName("Amazing Application");
QDir::setCurrent(getRelativeDir(".."));
public:
void loadFile(const QString& scriptPath) {
if (_isRunning) {
return;
}
qDebug() << "Loading script from " << scriptPath;
_fileNameString = scriptPath;
QtWebEngine::initialize();
QFile file(scriptPath);
if (file.exists()) {
file.open(QIODevice::ReadOnly);
_scriptContents = file.readAll();
} else {
qFatal("Missing file ");
}
runInThread();
}
QQmlApplicationEngine engine;
addImportPath(engine, "../qml");
addImportPath(engine, "../../../interface/resources/qml");
engine.load(QUrl(QStringLiteral("qml/Stubs.qml")));
Q_INVOKABLE void stop() {
if (!_isFinished) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stop");
return;
}
_isFinished = true;
if (_wantSignals) {
emit runningStateChanged();
}
}
}
setChild(engine, "rootMenu");
setChild(engine, "Account");
setChild(engine, "Desktop");
setChild(engine, "ScriptDiscoveryService");
setChild(engine, "MenuHelper");
setChild(engine, "urlHandler");
engine.rootContext()->setContextProperty("DebugQML", true);
Q_INVOKABLE void print(const QString& message) {
if (_wantSignals) {
emit printedMessage(message);
}
}
Q_INVOKABLE QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) {
// create the timer, add it to the map, and start it
QTimer* newTimer = new QTimer(this);
newTimer->setSingleShot(isSingleShot);
connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
// make sure the timer stops when the script does
connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
_timerFunctionMap.insert(newTimer, function);
newTimer->start(intervalMS);
return newTimer;
}
Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS) {
return setupTimerWithInterval(function, intervalMS, false);
}
Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS) {
return setupTimerWithInterval(function, timeoutMS, true);
}
private:
void runInThread() {
QThread* workerThread = new QThread();
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
connect(workerThread, &QThread::started, this, &ScriptEngine::run);
connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater);
connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit);
moveToThread(workerThread);
workerThread->start();
}
void init() {
_isInitialized = true;
registerMetaTypes(this);
registerGlobalObject("Script", this);
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
qScriptRegisterSequenceMetaType<QVector<QString>>(this);
globalObject().setProperty("OverlayWebWindow", newFunction(QmlWebWindowClass::constructor));
QScriptValue printConstructorValue = newFunction(debugPrint);
globalObject().setProperty("print", printConstructorValue);
}
void timerFired() {
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
if (!callingTimer->isActive()) {
// this timer is done, we can kill it
_timerFunctionMap.remove(callingTimer);
delete callingTimer;
}
// call the associated JS function, if it exists
if (timerFunction.isValid()) {
timerFunction.call();
}
}
void run() {
if (!_isInitialized) {
init();
}
_isRunning = true;
if (_wantSignals) {
emit runningStateChanged();
}
QScriptValue result = evaluate(_scriptContents, _fileNameString);
QElapsedTimer startTime;
startTime.start();
int thisFrame = 0;
qint64 lastUpdate = usecTimestampNow();
while (!_isFinished) {
int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
if (usecToSleep > 0) {
usleep(usecToSleep);
}
if (_isFinished) {
break;
}
QCoreApplication::processEvents();
if (_isFinished) {
break;
}
qint64 now = usecTimestampNow();
float deltaTime = (float)(now - lastUpdate) / (float)USECS_PER_SECOND;
if (!_isFinished) {
if (_wantSignals) {
emit update(deltaTime);
}
}
lastUpdate = now;
// Debug and clear exceptions
hadUncaughtExceptions(*this, _fileNameString);
}
if (_wantSignals) {
emit scriptEnding();
}
if (_wantSignals) {
emit finished(_fileNameString, this);
}
_isRunning = false;
if (_wantSignals) {
emit runningStateChanged();
emit doneRunning();
}
}
void registerGlobalObject(const QString& name, QObject* object) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerGlobalObject",
Q_ARG(const QString&, name),
Q_ARG(QObject*, object));
return;
}
if (!globalObject().property(name).isValid()) {
if (object) {
QScriptValue value = newQObject(object);
globalObject().setProperty(name, value);
} else {
globalObject().setProperty(name, QScriptValue());
}
}
}
void registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
globalObject().setProperty(name, scriptFun);
}
void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
QScriptValue object = globalObject().property(parent);
if (object.isValid()) {
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
object.setProperty(name, scriptFun);
}
}
signals:
void scriptLoaded(const QString& scriptFilename);
void errorLoadingScript(const QString& scriptFilename);
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString, ScriptEngine* engine);
void cleanupMenuItem(const QString& menuItemString);
void printedMessage(const QString& message);
void errorMessage(const QString& message);
void runningStateChanged();
void evaluationFinished(QScriptValue result, bool isException);
void loadScript(const QString& scriptName, bool isUserLoaded);
void reloadScript(const QString& scriptName, bool isUserLoaded);
void doneRunning();
private:
QString _scriptContents;
QString _fileNameString;
QString _parentURL;
bool _isInitialized { false };
std::atomic<bool> _isFinished { false };
std::atomic<bool> _isRunning { false };
bool _wantSignals { true };
QHash<QTimer*, QScriptValue> _timerFunctionMap;
};
ScriptEngine* loadScript(const QString& scriptFilename) {
ScriptEngine* scriptEngine = new ScriptEngine();
scriptEngine->loadFile(scriptFilename);
return scriptEngine;
//engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml")));
engine.load(QUrl(QStringLiteral("qml/main.qml")));
return app.exec();
}
OffscreenGLCanvas* _chromiumShareContext { nullptr };
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
// Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow {
Q_OBJECT
QOpenGLContextWrapper* _context{ nullptr };
QSize _size;
bool _altPressed{ false };
RateCounter fps;
QTimer _timer;
int testQmlTexture{ 0 };
ProgramPtr _program;
ShapeWrapperPtr _plane;
QScriptEngine* _scriptEngine { nullptr };
public:
QObject* rootMenu;
QTestWindow() {
_scriptEngine = new ScriptEngine();
_timer.setInterval(1);
QObject::connect(&_timer, &QTimer::timeout, this, &QTestWindow::draw);
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create();
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
{
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
setFormat(format);
_context = new QOpenGLContextWrapper();
_context->setFormat(format);
_context->setShareContext(_chromiumShareContext->getContext());
}
if (!_context->create()) {
qFatal("Could not create OpenGL context");
}
show();
makeCurrent();
glewExperimental = true;
glewInit();
glGetError();
using namespace oglplus;
Context::Enable(Capability::Blend);
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
Context::ClearColor(0.2f, 0.2f, 0.2f, 1);
MessageDialog::registerType();
InfoView::registerType();
auto offscreenUi = DependencyManager::set<OffscreenUi>();
{
offscreenUi->create(_context->getContext());
offscreenUi->setProxyWindow(this);
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
testQmlTexture = textureId;
});
makeCurrent();
}
auto primaryScreen = QGuiApplication::primaryScreen();
auto targetScreen = primaryScreen;
auto screens = QGuiApplication::screens();
if (screens.size() > 1) {
for (auto screen : screens) {
if (screen != targetScreen) {
targetScreen = screen;
break;
}
}
}
auto rect = targetScreen->availableGeometry();
rect.setWidth(rect.width() * 0.8f);
rect.setHeight(rect.height() * 0.8f);
rect.moveTo(QPoint(20, 20));
setGeometry(rect);
#ifdef QML_CONTROL_GALLERY
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir()));
offscreenUi->load(QUrl("main.qml"));
#else
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getInterfaceQmlDir()));
offscreenUi->load(QUrl("TestRoot.qml"));
#endif
installEventFilter(offscreenUi.data());
offscreenUi->resume();
_timer.start();
}
virtual ~QTestWindow() {
DependencyManager::destroy<OffscreenUi>();
}
private:
void draw() {
if (!isVisible()) {
return;
}
makeCurrent();
auto error = glGetError();
if (error != GL_NO_ERROR) {
qDebug() << "GL error in entering draw " << error;
}
using namespace oglplus;
Context::Clear().ColorBuffer().DepthBuffer();
ivec2 size(_size.width(), _size.height());
size *= devicePixelRatio();
size = glm::max(size, ivec2(100, 100));
Context::Viewport(size.x, size.y);
if (!_program) {
_program = loadDefaultShader();
_plane = loadPlane(_program);
}
if (testQmlTexture > 0) {
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
_program->Bind();
_plane->Use();
_plane->Draw();
_context->swapBuffers(this);
fps.increment();
if (fps.elapsed() >= 10.0f) {
qDebug() << "FPS: " << fps.rate();
fps.reset();
}
}
void makeCurrent() {
_context->makeCurrent(this);
}
void resizeWindow(const QSize & size) {
_size = size;
DependencyManager::get<OffscreenUi>()->resize(_size);
}
protected:
void resizeEvent(QResizeEvent* ev) override {
resizeWindow(ev->size());
}
void keyPressEvent(QKeyEvent* event) override {
_altPressed = Qt::Key_Alt == event->key();
switch (event->key()) {
case Qt::Key_B:
if (event->modifiers() & Qt::CTRL) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
}
break;
case Qt::Key_J:
if (event->modifiers() & Qt::CTRL) {
loadScript(getExamplesDir() + "tests/qmlWebTest.js");
}
break;
case Qt::Key_K:
if (event->modifiers() & Qt::CTRL) {
OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){
qDebug() << b;
});
}
break;
}
QWindow::keyPressEvent(event);
}
void moveEvent(QMoveEvent* event) override {
static qreal oldPixelRatio = 0.0;
if (devicePixelRatio() != oldPixelRatio) {
oldPixelRatio = devicePixelRatio();
resizeWindow(size());
}
QWindow::moveEvent(event);
}
};
const char * LOG_FILTER_RULES = R"V0G0N(
hifi.offscreen.focus.debug=false
qt.quick.mouse.debug=false
)V0G0N";
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = message;
#ifdef Q_OS_WIN
if (!logMessage.isEmpty()) {
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
}
#endif
}
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
qInstallMessageHandler(messageHandler);
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
QTestWindow window;
app.exec();
return 0;
}
#include "main.moc"

643
tests/ui/src/oldmain.cpp Normal file
View file

@ -0,0 +1,643 @@
//
// Created by Bradley Austin Davis on 2015-04-22
// Copyright 2013-2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gl/Config.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLHelpers.h>
#include <memory>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QElapsedTimer>
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <QtCore/QUuid>
#include <QtGui/QWindow>
#include <QtGui/QImage>
#include <QtGui/QGuiApplication>
#include <QtGui/QResizeEvent>
#include <QtGui/QScreen>
#include <gl/QOpenGLContextWrapper.h>
#include <QtScript/QScriptEngine>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlApplicationEngine>
#include <GLMHelpers.h>
#include <gl/OffscreenGLCanvas.h>
#include <OffscreenUi.h>
#include <PathUtils.h>
#include <PathUtils.h>
#include <VrMenu.h>
#include <InfoView.h>
#include <QmlWebWindowClass.h>
#include <RegisteredMetaTypes.h>
const QString& getResourcesDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
qDebug() << "Resources Path: " << dir;
}
return dir;
}
const QString& getExamplesDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../../../examples/")) + "/";
qDebug() << "Resources Path: " << dir;
}
return dir;
}
const QString& getInterfaceQmlDir() {
static QString dir;
if (dir.isEmpty()) {
dir = getResourcesDir() + "qml/";
qDebug() << "Qml Path: " << dir;
}
return dir;
}
const QString& getTestQmlDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
qDebug() << "Qml Test Path: " << dir;
}
return dir;
}
class RateCounter {
std::vector<float> times;
QElapsedTimer timer;
public:
RateCounter() {
timer.start();
}
void reset() {
times.clear();
}
size_t count() const {
return times.size() - 1;
}
float elapsed() const {
if (times.size() < 1) {
return 0.0f;
}
float elapsed = *times.rbegin() - *times.begin();
return elapsed;
}
void increment() {
times.push_back(timer.elapsed() / 1000.0f);
}
float rate() const {
if (elapsed() == 0.0f) {
return 0.0f;
}
return (float) count() / elapsed();
}
};
extern QOpenGLContext* qt_gl_global_share_context();
static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) {
if (engine.hasUncaughtException()) {
const auto backtrace = engine.uncaughtExceptionBacktrace();
const auto exception = engine.uncaughtException().toString();
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
engine.clearExceptions();
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
if (!backtrace.empty()) {
static const auto lineSeparator = "\n ";
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
}
qWarning() << qPrintable(message);
return true;
}
return false;
}
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0f / 60.0f) * 1000 * 1000) + 0.5f);
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
QString message = "";
for (int i = 0; i < context->argumentCount(); i++) {
if (i > 0) {
message += " ";
}
message += context->argument(i).toString();
}
qDebug().noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
message = message.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("'", "\\'");
engine->evaluate("Script.print('" + message + "')");
return QScriptValue();
}
class ScriptEngine : public QScriptEngine {
Q_OBJECT
public:
void loadFile(const QString& scriptPath) {
if (_isRunning) {
return;
}
qDebug() << "Loading script from " << scriptPath;
_fileNameString = scriptPath;
QFile file(scriptPath);
if (file.exists()) {
file.open(QIODevice::ReadOnly);
_scriptContents = file.readAll();
} else {
qFatal("Missing file ");
}
runInThread();
}
Q_INVOKABLE void stop() {
if (!_isFinished) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stop");
return;
}
_isFinished = true;
if (_wantSignals) {
emit runningStateChanged();
}
}
}
Q_INVOKABLE void print(const QString& message) {
if (_wantSignals) {
emit printedMessage(message);
}
}
Q_INVOKABLE QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) {
// create the timer, add it to the map, and start it
QTimer* newTimer = new QTimer(this);
newTimer->setSingleShot(isSingleShot);
connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
// make sure the timer stops when the script does
connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
_timerFunctionMap.insert(newTimer, function);
newTimer->start(intervalMS);
return newTimer;
}
Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS) {
return setupTimerWithInterval(function, intervalMS, false);
}
Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS) {
return setupTimerWithInterval(function, timeoutMS, true);
}
private:
void runInThread() {
QThread* workerThread = new QThread();
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
connect(workerThread, &QThread::started, this, &ScriptEngine::run);
connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater);
connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit);
moveToThread(workerThread);
workerThread->start();
}
void init() {
_isInitialized = true;
registerMetaTypes(this);
registerGlobalObject("Script", this);
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
qScriptRegisterSequenceMetaType<QVector<QString>>(this);
globalObject().setProperty("OverlayWebWindow", newFunction(QmlWebWindowClass::constructor));
QScriptValue printConstructorValue = newFunction(debugPrint);
globalObject().setProperty("print", printConstructorValue);
}
void timerFired() {
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
if (!callingTimer->isActive()) {
// this timer is done, we can kill it
_timerFunctionMap.remove(callingTimer);
delete callingTimer;
}
// call the associated JS function, if it exists
if (timerFunction.isValid()) {
timerFunction.call();
}
}
void run() {
if (!_isInitialized) {
init();
}
_isRunning = true;
if (_wantSignals) {
emit runningStateChanged();
}
QScriptValue result = evaluate(_scriptContents, _fileNameString);
QElapsedTimer startTime;
startTime.start();
int thisFrame = 0;
qint64 lastUpdate = usecTimestampNow();
while (!_isFinished) {
int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
if (usecToSleep > 0) {
usleep(usecToSleep);
}
if (_isFinished) {
break;
}
QCoreApplication::processEvents();
if (_isFinished) {
break;
}
qint64 now = usecTimestampNow();
float deltaTime = (float)(now - lastUpdate) / (float)USECS_PER_SECOND;
if (!_isFinished) {
if (_wantSignals) {
emit update(deltaTime);
}
}
lastUpdate = now;
// Debug and clear exceptions
hadUncaughtExceptions(*this, _fileNameString);
}
if (_wantSignals) {
emit scriptEnding();
}
if (_wantSignals) {
emit finished(_fileNameString, this);
}
_isRunning = false;
if (_wantSignals) {
emit runningStateChanged();
emit doneRunning();
}
}
void registerGlobalObject(const QString& name, QObject* object) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerGlobalObject",
Q_ARG(const QString&, name),
Q_ARG(QObject*, object));
return;
}
if (!globalObject().property(name).isValid()) {
if (object) {
QScriptValue value = newQObject(object);
globalObject().setProperty(name, value);
} else {
globalObject().setProperty(name, QScriptValue());
}
}
}
void registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
globalObject().setProperty(name, scriptFun);
}
void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "registerFunction",
Q_ARG(const QString&, name),
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
Q_ARG(int, numArguments));
return;
}
QScriptValue object = globalObject().property(parent);
if (object.isValid()) {
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
object.setProperty(name, scriptFun);
}
}
signals:
void scriptLoaded(const QString& scriptFilename);
void errorLoadingScript(const QString& scriptFilename);
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString, ScriptEngine* engine);
void cleanupMenuItem(const QString& menuItemString);
void printedMessage(const QString& message);
void errorMessage(const QString& message);
void runningStateChanged();
void evaluationFinished(QScriptValue result, bool isException);
void loadScript(const QString& scriptName, bool isUserLoaded);
void reloadScript(const QString& scriptName, bool isUserLoaded);
void doneRunning();
private:
QString _scriptContents;
QString _fileNameString;
QString _parentURL;
bool _isInitialized { false };
std::atomic<bool> _isFinished { false };
std::atomic<bool> _isRunning { false };
bool _wantSignals { true };
QHash<QTimer*, QScriptValue> _timerFunctionMap;
};
ScriptEngine* loadScript(const QString& scriptFilename) {
ScriptEngine* scriptEngine = new ScriptEngine();
scriptEngine->loadFile(scriptFilename);
return scriptEngine;
}
OffscreenGLCanvas* _chromiumShareContext { nullptr };
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
// Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow {
Q_OBJECT
QOpenGLContextWrapper* _context{ nullptr };
QSize _size;
bool _altPressed{ false };
RateCounter fps;
QTimer _timer;
int testQmlTexture{ 0 };
ProgramPtr _program;
ShapeWrapperPtr _plane;
QScriptEngine* _scriptEngine { nullptr };
public:
QObject* rootMenu;
QTestWindow() {
_scriptEngine = new ScriptEngine();
_timer.setInterval(1);
QObject::connect(&_timer, &QTimer::timeout, this, &QTestWindow::draw);
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create();
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
{
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
setFormat(format);
_context = new QOpenGLContextWrapper();
_context->setFormat(format);
_context->setShareContext(_chromiumShareContext->getContext());
}
if (!_context->create()) {
qFatal("Could not create OpenGL context");
}
show();
makeCurrent();
glewExperimental = true;
glewInit();
glGetError();
using namespace oglplus;
Context::Enable(Capability::Blend);
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
Context::ClearColor(0.2f, 0.2f, 0.2f, 1);
InfoView::registerType();
auto offscreenUi = DependencyManager::set<OffscreenUi>();
{
offscreenUi->create(_context->getContext());
offscreenUi->setProxyWindow(this);
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
testQmlTexture = textureId;
});
makeCurrent();
}
auto primaryScreen = QGuiApplication::primaryScreen();
auto targetScreen = primaryScreen;
auto screens = QGuiApplication::screens();
if (screens.size() > 1) {
for (auto screen : screens) {
if (screen != targetScreen) {
targetScreen = screen;
break;
}
}
}
auto rect = targetScreen->availableGeometry();
rect.setWidth(rect.width() * 0.8f);
rect.setHeight(rect.height() * 0.8f);
rect.moveTo(QPoint(20, 20));
setGeometry(rect);
#ifdef QML_CONTROL_GALLERY
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir()));
offscreenUi->load(QUrl("main.qml"));
#else
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getInterfaceQmlDir()));
offscreenUi->load(QUrl("TestRoot.qml"));
#endif
installEventFilter(offscreenUi.data());
offscreenUi->resume();
_timer.start();
}
virtual ~QTestWindow() {
DependencyManager::destroy<OffscreenUi>();
}
private:
void draw() {
if (!isVisible()) {
return;
}
makeCurrent();
auto error = glGetError();
if (error != GL_NO_ERROR) {
qDebug() << "GL error in entering draw " << error;
}
using namespace oglplus;
Context::Clear().ColorBuffer().DepthBuffer();
ivec2 size(_size.width(), _size.height());
size *= devicePixelRatio();
size = glm::max(size, ivec2(100, 100));
Context::Viewport(size.x, size.y);
if (!_program) {
_program = loadDefaultShader();
_plane = loadPlane(_program);
}
if (testQmlTexture > 0) {
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
_program->Bind();
_plane->Use();
_plane->Draw();
_context->swapBuffers(this);
fps.increment();
if (fps.elapsed() >= 10.0f) {
qDebug() << "FPS: " << fps.rate();
fps.reset();
}
}
void makeCurrent() {
_context->makeCurrent(this);
}
void resizeWindow(const QSize & size) {
_size = size;
DependencyManager::get<OffscreenUi>()->resize(_size);
}
protected:
void resizeEvent(QResizeEvent* ev) override {
resizeWindow(ev->size());
}
void keyPressEvent(QKeyEvent* event) override {
_altPressed = Qt::Key_Alt == event->key();
switch (event->key()) {
case Qt::Key_B:
if (event->modifiers() & Qt::CTRL) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
}
break;
case Qt::Key_J:
if (event->modifiers() & Qt::CTRL) {
loadScript(getExamplesDir() + "tests/qmlWebTest.js");
}
break;
case Qt::Key_K:
if (event->modifiers() & Qt::CTRL) {
OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){
qDebug() << b;
});
}
break;
}
QWindow::keyPressEvent(event);
}
void moveEvent(QMoveEvent* event) override {
static qreal oldPixelRatio = 0.0;
if (devicePixelRatio() != oldPixelRatio) {
oldPixelRatio = devicePixelRatio();
resizeWindow(size());
}
QWindow::moveEvent(event);
}
};
const char * LOG_FILTER_RULES = R"V0G0N(
hifi.offscreen.focus.debug=false
qt.quick.mouse.debug=false
)V0G0N";
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = message;
#ifdef Q_OS_WIN
if (!logMessage.isEmpty()) {
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
}
#endif
}
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
qInstallMessageHandler(messageHandler);
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
QTestWindow window;
app.exec();
return 0;
}
#include "main.moc"