mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 06:04:06 +02:00
Breaking up offscreen UI work
This commit is contained in:
parent
a4619c8e59
commit
99a6e1f86c
60 changed files with 2847 additions and 540 deletions
|
@ -128,7 +128,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
|||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer)
|
||||
render-utils entities-renderer ui)
|
||||
|
||||
add_dependency_external_projects(sdl2)
|
||||
|
||||
|
|
BIN
interface/resources/fonts/fontawesome-webfont.ttf
Normal file
BIN
interface/resources/fonts/fontawesome-webfont.ttf
Normal file
Binary file not shown.
|
@ -1,10 +1,9 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
CustomDialog {
|
||||
Dialog {
|
||||
title: "Go to..."
|
||||
objectName: "AddressBarDialog"
|
||||
height: 128
|
||||
|
@ -36,14 +35,14 @@ CustomDialog {
|
|||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
CustomBorder {
|
||||
Border {
|
||||
height: 64
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: goButton.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
CustomTextInput {
|
||||
TextInput {
|
||||
id: addressLine
|
||||
anchors.fill: parent
|
||||
helperText: "domain, location, @user, /x,y,z"
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtWebKit 3.0
|
||||
import "controls"
|
||||
|
||||
CustomDialog {
|
||||
title: "Test Dlg"
|
||||
Dialog {
|
||||
title: "Browser Window"
|
||||
id: testDialog
|
||||
objectName: "Browser"
|
||||
width: 1280
|
||||
|
@ -18,7 +16,6 @@ CustomDialog {
|
|||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
|
@ -30,16 +27,4 @@ CustomDialog {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
Button {
|
||||
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
|
||||
text: "Text"
|
||||
width: 128
|
||||
height: 64
|
||||
style: ButtonStyle {
|
||||
background: CustomBorder {
|
||||
anchors.fill: parent
|
||||
}
|
||||
label: CustomText {
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
color: control.enabled ? myPalette.text : myPalette.dark
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
TextArea {
|
||||
font.family: "Helvetica"
|
||||
font.pointSize: 18
|
||||
backgroundVisible: false
|
||||
readOnly: true
|
||||
}
|
||||
|
20
interface/resources/qml/HifiAction.qml
Normal file
20
interface/resources/qml/HifiAction.qml
Normal file
|
@ -0,0 +1,20 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
|
||||
Action {
|
||||
property string name
|
||||
objectName: name + "HifiAction"
|
||||
text: qsTr(name)
|
||||
|
||||
signal triggeredByName(string name);
|
||||
signal toggledByName(string name);
|
||||
|
||||
onTriggered: {
|
||||
triggeredByName(name);
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
toggledByName(name, checked);
|
||||
}
|
||||
}
|
||||
|
272
interface/resources/qml/HifiMenu.qml
Normal file
272
interface/resources/qml/HifiMenu.qml
Normal file
|
@ -0,0 +1,272 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Hifi.HifiMenu {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
objectName: "HifiMenu"
|
||||
enabled: false
|
||||
opacity: 0.0
|
||||
property int animationDuration: 200
|
||||
HifiPalette { id: hifiPalette }
|
||||
z: 10000
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled && columns.length == 0) {
|
||||
pushColumn(rootMenu.items);
|
||||
}
|
||||
opacity = enabled ? 1.0 : 0.0
|
||||
if (enabled) {
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.InOutBounce
|
||||
}
|
||||
}
|
||||
|
||||
onOpacityChanged: {
|
||||
visible = (opacity != 0.0);
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) reset();
|
||||
}
|
||||
|
||||
|
||||
property var menu: Menu {}
|
||||
property var models: []
|
||||
property var columns: []
|
||||
property var itemBuilder: Component {
|
||||
Text {
|
||||
SystemPalette { id: sp; colorGroup: SystemPalette.Active }
|
||||
id: thisText
|
||||
x: 32
|
||||
property var source
|
||||
property var root
|
||||
property var listViewIndex
|
||||
property var listView
|
||||
text: typedText()
|
||||
height: implicitHeight
|
||||
width: implicitWidth
|
||||
color: source.enabled ? "black" : "gray"
|
||||
|
||||
onImplicitWidthChanged: {
|
||||
if (listView) {
|
||||
listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64);
|
||||
listView.recalculateSize();
|
||||
}
|
||||
}
|
||||
|
||||
FontAwesome {
|
||||
visible: source.type == 1 && source.checkable
|
||||
x: -32
|
||||
text: (source.type == 1 && source.checked) ? "\uF05D" : "\uF10C"
|
||||
}
|
||||
|
||||
FontAwesome {
|
||||
visible: source.type == 2
|
||||
x: listView.width - 64
|
||||
text: "\uF0DA"
|
||||
}
|
||||
|
||||
|
||||
function typedText() {
|
||||
switch(source.type) {
|
||||
case 2:
|
||||
return source.title;
|
||||
case 1:
|
||||
return source.text;
|
||||
case 0:
|
||||
return "-----"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
acceptedButtons: Qt.LeftButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
width: listView.width
|
||||
onClicked: {
|
||||
listView.currentIndex = listViewIndex
|
||||
parent.root.selectItem(parent.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property var menuBuilder: Component {
|
||||
Border {
|
||||
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
|
||||
x: root.models.length == 1 ?
|
||||
(root.width / 2 - width / 2) :
|
||||
root.columns[root.models.length - 2].x + 60;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
border.color: hifiPalette.hifiBlue
|
||||
color: sysPalette.window
|
||||
|
||||
ListView {
|
||||
spacing: 6
|
||||
property int outerMargin: 8
|
||||
property real minWidth: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: outerMargin
|
||||
id: listView
|
||||
height: root.height
|
||||
currentIndex: -1
|
||||
|
||||
onCountChanged: {
|
||||
recalculateSize()
|
||||
}
|
||||
|
||||
function recalculateSize() {
|
||||
var newHeight = 0
|
||||
var newWidth = minWidth;
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var item = children[i];
|
||||
newHeight += item.height
|
||||
}
|
||||
parent.height = newHeight + outerMargin * 2;
|
||||
parent.width = newWidth + outerMargin * 2
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
width: listView.minWidth; height: 32
|
||||
color: sysPalette.highlight
|
||||
y: (listView.currentItem) ? listView.currentItem.y : 0;
|
||||
x: 32
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
easing.type: Easing.InOutQuint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property int columnIndex: root.models.length - 1
|
||||
model: root.models[columnIndex]
|
||||
delegate: Loader {
|
||||
id: loader
|
||||
sourceComponent: root.itemBuilder
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "root"
|
||||
value: root
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "source"
|
||||
value: modelData
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "listViewIndex"
|
||||
value: index
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "listView"
|
||||
value: listView
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function lastColumn() {
|
||||
return columns[root.columns.length - 1];
|
||||
}
|
||||
|
||||
function pushColumn(items) {
|
||||
models.push(items)
|
||||
if (columns.length) {
|
||||
var oldColumn = lastColumn();
|
||||
oldColumn.enabled = false;
|
||||
oldColumn.opacity = 0.5;
|
||||
}
|
||||
var newColumn = menuBuilder.createObject(root);
|
||||
columns.push(newColumn);
|
||||
newColumn.forceActiveFocus();
|
||||
}
|
||||
|
||||
function popColumn() {
|
||||
if (columns.length > 0) {
|
||||
var curColumn = columns.pop();
|
||||
console.log(curColumn);
|
||||
curColumn.visible = false;
|
||||
curColumn.destroy();
|
||||
models.pop();
|
||||
}
|
||||
|
||||
if (columns.length == 0) {
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
curColumn = lastColumn();
|
||||
curColumn.enabled = true;
|
||||
curColumn.opacity = 1.0;
|
||||
curColumn.forceActiveFocus();
|
||||
}
|
||||
|
||||
function selectItem(source) {
|
||||
switch (source.type) {
|
||||
case 2:
|
||||
pushColumn(source.items)
|
||||
break;
|
||||
case 1:
|
||||
source.trigger()
|
||||
enabled = false
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
while (columns.length > 0) {
|
||||
popColumn();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.popColumn()
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if (mouse.button == Qt.RightButton) {
|
||||
root.popColumn();
|
||||
} else {
|
||||
root.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import QtQuick 1.0
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
width: 64
|
||||
height: 64
|
||||
source: "file.svg"
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "hifiConstants.js" as HifiConstants
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
CustomDialog {
|
||||
Dialog {
|
||||
title: "Login"
|
||||
HifiPalette { id: hifiPalette }
|
||||
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
|
||||
objectName: "LoginDialog"
|
||||
height: 512
|
||||
|
@ -50,11 +50,11 @@ CustomDialog {
|
|||
source: "../images/hifi-logo.svg"
|
||||
}
|
||||
|
||||
CustomBorder {
|
||||
Border {
|
||||
width: 304
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
CustomTextInput {
|
||||
TextInput {
|
||||
id: username
|
||||
anchors.fill: parent
|
||||
helperText: "Username or Email"
|
||||
|
@ -67,11 +67,11 @@ CustomDialog {
|
|||
}
|
||||
}
|
||||
|
||||
CustomBorder {
|
||||
Border {
|
||||
width: 304
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
CustomTextInput {
|
||||
TextInput {
|
||||
id: password
|
||||
anchors.fill: parent
|
||||
echoMode: TextInput.Password
|
||||
|
@ -94,7 +94,7 @@ CustomDialog {
|
|||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
textFormat: Text.StyledText
|
||||
width: parent.width
|
||||
|
@ -117,7 +117,7 @@ CustomDialog {
|
|||
width: 192
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: HifiConstants.color
|
||||
color: hifiPalette.hifiBlue
|
||||
border.width: 0
|
||||
radius: 10
|
||||
|
||||
|
@ -142,7 +142,7 @@ CustomDialog {
|
|||
width: 32
|
||||
source: "../images/login.svg"
|
||||
}
|
||||
CustomText {
|
||||
Text {
|
||||
text: "Login"
|
||||
color: "white"
|
||||
width: 64
|
||||
|
@ -152,7 +152,7 @@ CustomDialog {
|
|||
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Text {
|
||||
width: parent.width
|
||||
height: 24
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
@ -160,7 +160,7 @@ CustomDialog {
|
|||
text:"Create Account"
|
||||
font.pointSize: 12
|
||||
font.bold: true
|
||||
color: HifiConstants.color
|
||||
color: hifiPalette.hifiBlue
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
@ -170,14 +170,14 @@ CustomDialog {
|
|||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Text {
|
||||
width: parent.width
|
||||
height: 24
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pointSize: 12
|
||||
text: "Recover Password"
|
||||
color: HifiConstants.color
|
||||
color: hifiPalette.hifiBlue
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
|
50
interface/resources/qml/MarketplaceDialog.qml
Normal file
50
interface/resources/qml/MarketplaceDialog.qml
Normal file
|
@ -0,0 +1,50 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtWebKit 3.0
|
||||
import "controls"
|
||||
|
||||
Dialog {
|
||||
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
|
||||
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
359
interface/resources/qml/MessageDialog.qml
Normal file
359
interface/resources/qml/MessageDialog.qml
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtQuick.Dialogs module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
*****************************************************************************/
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import "controls"
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
property real spacing: 8
|
||||
property real outerSpacing: 16
|
||||
|
||||
destroyOnCloseButton: true
|
||||
destroyOnInvisible: true
|
||||
implicitHeight: content.implicitHeight + outerSpacing * 2 + 48
|
||||
implicitWidth: Math.min(200, Math.max(mainText.implicitWidth, content.buttonsRowImplicitWidth) + outerSpacing * 2);
|
||||
|
||||
onImplicitHeightChanged: root.height = implicitHeight
|
||||
onImplicitWidthChanged: root.width = implicitWidth
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
||||
function calculateImplicitWidth() {
|
||||
if (buttons.visibleChildren.length < 2)
|
||||
return;
|
||||
var calcWidth = 0;
|
||||
for (var i = 0; i < buttons.visibleChildren.length; ++i) {
|
||||
calcWidth += Math.max(100, buttons.visibleChildren[i].implicitWidth) + root.spacing
|
||||
}
|
||||
content.buttonsRowImplicitWidth = outerSpacing + calcWidth + 48
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled) {
|
||||
content.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Hifi.MessageDialog {
|
||||
id: content
|
||||
clip: true
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: parent.topMargin + root.outerSpacing
|
||||
anchors.leftMargin: parent.margins + root.outerSpacing
|
||||
anchors.rightMargin: parent.margins + root.outerSpacing
|
||||
anchors.bottomMargin: parent.margins + root.outerSpacing
|
||||
implicitHeight: contentColumn.implicitHeight + outerSpacing * 2
|
||||
implicitWidth: Math.max(mainText.implicitWidth, buttonsRowImplicitWidth);
|
||||
property real buttonsRowImplicitWidth: Screen.pixelDensity * 50
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log("Key press at content")
|
||||
event.accepted = true
|
||||
if (event.modifiers === Qt.ControlModifier)
|
||||
switch (event.key) {
|
||||
case Qt.Key_A:
|
||||
console.log("Select All")
|
||||
detailedText.selectAll()
|
||||
break
|
||||
case Qt.Key_C:
|
||||
console.log("Copy")
|
||||
detailedText.copy()
|
||||
break
|
||||
case Qt.Key_Period:
|
||||
if (Qt.platform.os === "osx")
|
||||
reject()
|
||||
break
|
||||
} else switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
console.log("Rejecting")
|
||||
reject()
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
console.log("Accepting")
|
||||
accept()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onImplicitWidthChanged: root.width = implicitWidth
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = title
|
||||
}
|
||||
|
||||
onTitleChanged: {
|
||||
root.title = title
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
spacing: root.outerSpacing
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Math.max(icon.height, mainText.height + informativeText.height + root.spacing)
|
||||
Image {
|
||||
id: icon
|
||||
source: content.standardIconSource
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
onVisibleChildrenChanged: root.calculateImplicitWidth()
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
/*
|
||||
Rectangle {
|
||||
|
||||
}
|
||||
Component.onCompleted: calculateImplicitWidth()
|
||||
*/
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
Rectangle {
|
||||
color: "teal"
|
||||
|
@ -150,87 +147,4 @@ Rectangle {
|
|||
Rectangle { height: parent.height; width: 16; color: spd.highlightedText}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CustomDialog {
|
||||
title: "Test Dlg"
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
property int d: 100
|
||||
id: square
|
||||
objectName: "testRect"
|
||||
width: d
|
||||
height: d
|
||||
anchors.centerIn: parent
|
||||
color: "red"
|
||||
NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
|
||||
}
|
||||
|
||||
|
||||
CustomTextEdit {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
clip: true
|
||||
text: "test edit"
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: parent.titleSize + 12
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
x: 128
|
||||
y: 192
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 12
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
onClicked: {
|
||||
console.log("Click");
|
||||
if (square.visible) {
|
||||
square.visible = false
|
||||
} else {
|
||||
square.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
id: customButton2
|
||||
y: 192
|
||||
text: "Close"
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 12
|
||||
onClicked: {
|
||||
onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log("Key " + event.key);
|
||||
switch (event.key) {
|
||||
case Qt.Key_Q:
|
||||
if (Qt.ControlModifier == event.modifiers) {
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
|
||||
// This is our primary 'window' object to which all dialogs and controls will
|
||||
// be childed.
|
||||
Root {
|
||||
id: root
|
||||
width: 1280
|
||||
height: 720
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
|
9
interface/resources/qml/RootMenu.qml
Normal file
9
interface/resources/qml/RootMenu.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
|
||||
Item {
|
||||
Menu {
|
||||
id: root
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
|
||||
CustomDialog {
|
||||
Dialog {
|
||||
title: "Test Dialog"
|
||||
id: testDialog
|
||||
objectName: "TestDialog"
|
||||
|
@ -37,7 +36,7 @@ CustomDialog {
|
|||
}
|
||||
|
||||
|
||||
CustomTextEdit {
|
||||
TextEdit {
|
||||
id: edit
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
|
@ -49,7 +48,7 @@ CustomDialog {
|
|||
anchors.topMargin: 12
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
Button {
|
||||
x: 128
|
||||
y: 192
|
||||
text: "Test"
|
||||
|
@ -68,7 +67,7 @@ CustomDialog {
|
|||
}
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
Button {
|
||||
id: customButton2
|
||||
y: 192
|
||||
text: "Move"
|
||||
|
@ -92,15 +91,4 @@ CustomDialog {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3
|
||||
// Import local folder last so that our own control customizations override
|
||||
// the built in ones
|
||||
import "controls"
|
||||
|
||||
Root {
|
||||
id: root
|
||||
width: 1280
|
||||
height: 720
|
||||
anchors.fill: parent
|
||||
|
||||
CustomButton {
|
||||
onWidthChanged: {
|
||||
console.log("Root width: " + width)
|
||||
}
|
||||
onHeightChanged: {
|
||||
console.log("Root height: " + height)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Completed root")
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
|
||||
Button {
|
||||
id: messageBox
|
||||
anchors.right: createDialog.left
|
||||
anchors.rightMargin: 24
|
||||
|
@ -20,7 +35,7 @@ Root {
|
|||
}
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
Button {
|
||||
id: createDialog
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 24
|
||||
|
@ -28,8 +43,12 @@ Root {
|
|||
anchors.bottomMargin: 24
|
||||
text: "Create"
|
||||
onClicked: {
|
||||
root.loadChild("TestDialog.qml");
|
||||
root.loadChild("MenuTest.qml");
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log(event.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
var component;
|
||||
var instance;
|
||||
var parent;
|
||||
|
||||
function createObject(parentObject, url) {
|
||||
parent = parentObject;
|
||||
component = Qt.createComponent(url);
|
||||
if (component.status == Component.Ready)
|
||||
finishCreation();
|
||||
else
|
||||
component.statusChanged.connect(finishCreation);
|
||||
}
|
||||
|
||||
function finishCreation() {
|
||||
if (component.status == Component.Ready) {
|
||||
instance = component.createObject(parent, {"x": 100, "y": 100});
|
||||
if (instance == null) {
|
||||
// Error Handling
|
||||
console.log("Error creating object");
|
||||
} else {
|
||||
instance.enabled = true
|
||||
}
|
||||
} else if (component.status == Component.Error) {
|
||||
// Error Handling
|
||||
console.log("Error loading component:", component.errorString());
|
||||
} else {
|
||||
console.log("Unknown component status: " + component.status);
|
||||
}
|
||||
}
|
10
interface/resources/qml/controls/Button.qml
Normal file
10
interface/resources/qml/controls/Button.qml
Normal file
|
@ -0,0 +1,10 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3 as Original
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "."
|
||||
import "../styles"
|
||||
|
||||
Original.Button {
|
||||
style: ButtonStyle {
|
||||
}
|
||||
}
|
|
@ -1,40 +1,79 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "hifiConstants.js" as HifiConstants
|
||||
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
|
||||
*
|
||||
*/
|
||||
Item {
|
||||
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
|
||||
id: dialog
|
||||
width: 256
|
||||
height: 256
|
||||
scale: 0.0
|
||||
enabled: false
|
||||
id: root
|
||||
|
||||
HifiPalette { id: hifiPalette }
|
||||
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
|
||||
x: parent ? parent.width / 2 - width / 2 : 0
|
||||
y: parent ? parent.height / 2 - height / 2 : 0
|
||||
|
||||
property int animationDuration: 400
|
||||
property bool destroyOnInvisible: false
|
||||
property bool destroyOnCloseButton: true
|
||||
property bool resizable: false
|
||||
property int minX: 256
|
||||
property int minY: 256
|
||||
property int topMargin: root.height - clientBorder.height + 8
|
||||
property int margins: 8
|
||||
property string title
|
||||
property int titleSize: titleBorder.height + 12
|
||||
property string frameColor: hifiPalette.hifiBlue
|
||||
property string backgroundColor: sysPalette.window
|
||||
property string headerBackgroundColor: sysPalette.dark
|
||||
clip: true
|
||||
|
||||
/*
|
||||
* Support for animating the dialog in and out.
|
||||
*/
|
||||
enabled: false
|
||||
scale: 0.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.
|
||||
onEnabledChanged: {
|
||||
scale = enabled ? 1.0 : 0.0
|
||||
}
|
||||
|
||||
|
||||
// The actual animator
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.InOutBounce
|
||||
}
|
||||
}
|
||||
|
||||
// We remove any load the dialog might have on the QML by toggling it's
|
||||
// visibility based on the state of the animated property
|
||||
onScaleChanged: {
|
||||
visible = (scale != 0.0);
|
||||
}
|
||||
|
||||
// Some dialogs should be destroyed when they become invisible, so handle that
|
||||
onVisibleChanged: {
|
||||
if (!visible && destroyOnInvisible) {
|
||||
console.log("Destroying closed component");
|
||||
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 destoryed after the close
|
||||
// animation completes
|
||||
function close() {
|
||||
if (destroyOnCloseButton) {
|
||||
destroyOnInvisible = true
|
||||
|
@ -42,102 +81,14 @@ Item {
|
|||
enabled = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize support
|
||||
*/
|
||||
function deltaSize(dx, dy) {
|
||||
width = Math.max(width + dx, minX)
|
||||
height = Math.max(height + dy, minY)
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
//This specifies how long the animation takes
|
||||
duration: dialog.animationDuration
|
||||
//This selects an easing curve to interpolate with, the default is Easing.Linear
|
||||
easing.type: Easing.InOutBounce
|
||||
}
|
||||
}
|
||||
|
||||
property int topMargin: dialog.height - clientBorder.height + 8
|
||||
property int margins: 8
|
||||
property string title
|
||||
property int titleSize: titleBorder.height + 12
|
||||
property string frameColor: HifiConstants.color
|
||||
property string backgroundColor: myPalette.window
|
||||
property string headerBackgroundColor: myPalette.dark
|
||||
|
||||
CustomBorder {
|
||||
id: windowBorder
|
||||
anchors.fill: parent
|
||||
border.color: dialog.frameColor
|
||||
color: dialog.backgroundColor
|
||||
|
||||
CustomBorder {
|
||||
id: titleBorder
|
||||
height: 48
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
border.color: dialog.frameColor
|
||||
color: dialog.headerBackgroundColor
|
||||
|
||||
CustomText {
|
||||
id: titleText
|
||||
color: "white"
|
||||
text: dialog.title
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: titleDrag
|
||||
anchors.right: closeButton.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: 4
|
||||
drag {
|
||||
target: dialog
|
||||
minimumX: 0
|
||||
minimumY: 0
|
||||
maximumX: dialog.parent ? dialog.parent.width - dialog.width : 0
|
||||
maximumY: dialog.parent ? dialog.parent.height - dialog.height : 0
|
||||
}
|
||||
}
|
||||
Image {
|
||||
id: closeButton
|
||||
x: 360
|
||||
height: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
source: "../styles/close.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // header border
|
||||
|
||||
CustomBorder {
|
||||
id: clientBorder
|
||||
border.color: dialog.frameColor
|
||||
color: "#00000000"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.top: titleBorder.bottom
|
||||
anchors.topMargin: -titleBorder.border.width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
clip: true
|
||||
} // client border
|
||||
} // window border
|
||||
|
||||
MouseArea {
|
||||
id: sizeDrag
|
||||
property int startX
|
||||
|
@ -152,11 +103,91 @@ Item {
|
|||
startY = mouseY
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (pressed && dialog.resizable) {
|
||||
dialog.deltaSize((mouseX - startX), (mouseY - startY))
|
||||
if (pressed && root.resizable) {
|
||||
root.deltaSize((mouseX - startX), (mouseY - startY))
|
||||
startX = mouseX
|
||||
startY = mouseY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Window decorations, with a title bar and frames
|
||||
*/
|
||||
Border {
|
||||
id: windowBorder
|
||||
anchors.fill: parent
|
||||
border.color: root.frameColor
|
||||
color: root.backgroundColor
|
||||
|
||||
Border {
|
||||
id: titleBorder
|
||||
height: 48
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
border.color: root.frameColor
|
||||
color: root.headerBackgroundColor
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
// FIXME move all constant colors to our own palette class HifiPalette
|
||||
color: "white"
|
||||
text: root.title
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: titleDrag
|
||||
anchors.right: closeButton.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: 4
|
||||
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
|
||||
}
|
||||
}
|
||||
Image {
|
||||
id: closeButton
|
||||
x: 360
|
||||
height: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
source: "../../styles/close.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // header border
|
||||
|
||||
Border {
|
||||
id: clientBorder
|
||||
border.color: root.frameColor
|
||||
// FIXME move all constant colors to our own palette class HifiPalette
|
||||
color: "#00000000"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.top: titleBorder.bottom
|
||||
anchors.topMargin: -titleBorder.border.width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
clip: true
|
||||
} // client border
|
||||
} // window border
|
||||
|
||||
}
|
16
interface/resources/qml/controls/FontAwesome.qml
Normal file
16
interface/resources/qml/controls/FontAwesome.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
Text {
|
||||
id: root
|
||||
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
|
||||
property int size: 32
|
||||
width: size
|
||||
height: size
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.family: iconFont.name
|
||||
font.pointSize: 18
|
||||
}
|
||||
|
5
interface/resources/qml/controls/MenuButton.qml
Normal file
5
interface/resources/qml/controls/MenuButton.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3 as Original
|
||||
import "../styles"
|
||||
import "../controls"
|
||||
|
2
interface/resources/qml/controls/README.md
Normal file
2
interface/resources/qml/controls/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
These are our own custom controls with the same names as existing controls, but
|
||||
customized for readability / usability in VR.
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick 2.3 as Original
|
||||
|
||||
TextEdit {
|
||||
Original.Text {
|
||||
font.family: "Helvetica"
|
||||
font.pointSize: 18
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick 2.3 as Original
|
||||
|
||||
Text {
|
||||
Original.TextArea {
|
||||
font.family: "Helvetica"
|
||||
font.pointSize: 18
|
||||
}
|
7
interface/resources/qml/controls/TextEdit.qml
Normal file
7
interface/resources/qml/controls/TextEdit.qml
Normal file
|
@ -0,0 +1,7 @@
|
|||
import QtQuick 2.3 as Original
|
||||
|
||||
Original.TextEdit {
|
||||
font.family: "Helvetica"
|
||||
font.pointSize: 18
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import QtQuick.Controls 1.2
|
|||
|
||||
TextInput {
|
||||
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
|
||||
property string helperText: ""
|
||||
property string helperText
|
||||
font.family: "Helvetica"
|
||||
font.pointSize: 18
|
||||
width: 256
|
||||
|
@ -24,7 +24,7 @@ TextInput {
|
|||
id: helperText
|
||||
anchors.fill: parent
|
||||
font.pointSize: parent.font.pointSize
|
||||
font.family: "Helvetica"
|
||||
font.family: parent.font.family
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
text: parent.helperText
|
||||
color: myPalette.dark
|
|
@ -1,4 +0,0 @@
|
|||
var color = "#0e7077"
|
||||
var Colors = {
|
||||
hifiBlue: "#0e7077"
|
||||
}
|
BIN
interface/resources/qml/images/critical.png
Normal file
BIN
interface/resources/qml/images/critical.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 253 B |
BIN
interface/resources/qml/images/information.png
Normal file
BIN
interface/resources/qml/images/information.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 254 B |
BIN
interface/resources/qml/images/question.png
Normal file
BIN
interface/resources/qml/images/question.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 257 B |
BIN
interface/resources/qml/images/warning.png
Normal file
BIN
interface/resources/qml/images/warning.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 B |
|
@ -1,6 +1,5 @@
|
|||
import QtQuick 2.3
|
||||
|
||||
|
||||
Rectangle {
|
||||
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
|
||||
property int margin: 5
|
24
interface/resources/qml/styles/ButtonStyle.qml
Normal file
24
interface/resources/qml/styles/ButtonStyle.qml
Normal file
|
@ -0,0 +1,24 @@
|
|||
import QtQuick 2.4 as Original
|
||||
import QtQuick.Controls.Styles 1.3 as OriginalStyles
|
||||
import "."
|
||||
import "../controls"
|
||||
|
||||
OriginalStyles.ButtonStyle {
|
||||
Original.SystemPalette { id: myPalette; colorGroup: Original.SystemPalette.Active }
|
||||
padding {
|
||||
top: 8
|
||||
left: 12
|
||||
right: 12
|
||||
bottom: 8
|
||||
}
|
||||
background: Border {
|
||||
anchors.fill: parent
|
||||
}
|
||||
label: Text {
|
||||
renderType: Original.Text.NativeRendering
|
||||
verticalAlignment: Original.Text.AlignVCenter
|
||||
horizontalAlignment: Original.Text.AlignHCenter
|
||||
text: control.text
|
||||
color: control.enabled ? myPalette.text : myPalette.dark
|
||||
}
|
||||
}
|
5
interface/resources/qml/styles/HifiPalette.qml
Normal file
5
interface/resources/qml/styles/HifiPalette.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
import QtQuick 2.4
|
||||
|
||||
QtObject {
|
||||
property string hifiBlue: "#0e7077"
|
||||
}
|
15
interface/resources/qml/styles/IconButtonStyle.qml
Normal file
15
interface/resources/qml/styles/IconButtonStyle.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
ButtonStyle {
|
||||
background: Item { anchors.fill: parent }
|
||||
label: Text {
|
||||
id: icon
|
||||
width: height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.NativeRendering
|
||||
font.family: iconFont.name
|
||||
font.pointSize: 18
|
||||
property alias unicode: icon.text
|
||||
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
|
||||
text: control.text
|
||||
color: control.enabled ? "white" : "dimgray"
|
||||
}
|
||||
}
|
22
interface/resources/qml/styles/MenuButtonStyle.qml
Normal file
22
interface/resources/qml/styles/MenuButtonStyle.qml
Normal file
|
@ -0,0 +1,22 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "../controls"
|
||||
import "."
|
||||
|
||||
ButtonStyle {
|
||||
HifiPalette { id: hifiPalette }
|
||||
padding {
|
||||
top: 2
|
||||
left: 4
|
||||
right: 4
|
||||
bottom: 2
|
||||
}
|
||||
background: Item {}
|
||||
label: Text {
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
color: control.enabled ? "yellow" : "brown"
|
||||
}
|
||||
}
|
|
@ -144,6 +144,23 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
enum CustomEventTypes {
|
||||
Lambda = QEvent::User + 1
|
||||
};
|
||||
|
||||
class LambdaEvent : public QEvent {
|
||||
std::function<void()> _fun;
|
||||
public:
|
||||
LambdaEvent(const std::function<void()> & fun) :
|
||||
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
|
||||
}
|
||||
LambdaEvent(std::function<void()> && fun) :
|
||||
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
|
||||
}
|
||||
void call() { _fun(); }
|
||||
};
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Starfield information
|
||||
|
@ -707,6 +724,13 @@ void Application::initializeGL() {
|
|||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
||||
// The UI can't be created until the primary OpenGL
|
||||
// context is created, because it needs to share
|
||||
// texture resources
|
||||
initializeUi();
|
||||
qCDebug(interfaceapp, "Initialized Offscreen UI.");
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
init();
|
||||
qCDebug(interfaceapp, "init() complete.");
|
||||
|
||||
|
@ -735,17 +759,13 @@ void Application::initializeGL() {
|
|||
// update before the first render
|
||||
update(1.0f / _fps);
|
||||
|
||||
// The UI can't be created until the primary OpenGL
|
||||
// context is created, because it needs to share
|
||||
// texture resources
|
||||
initializeUi();
|
||||
|
||||
InfoView::showFirstTime(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Application::initializeUi() {
|
||||
AddressBarDialog::registerType();
|
||||
LoginDialog::registerType();
|
||||
MessageDialog::registerType();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_glWidget->context()->contextHandle());
|
||||
|
@ -753,6 +773,7 @@ void Application::initializeUi() {
|
|||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
offscreenUi->load("Root.qml");
|
||||
offscreenUi->load("RootMenu.qml");
|
||||
offscreenUi->setMouseTranslator([this](const QPointF& p){
|
||||
if (OculusManager::isConnected()) {
|
||||
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
|
||||
|
@ -964,6 +985,10 @@ bool Application::importSVOFromURL(const QString& urlString) {
|
|||
|
||||
bool Application::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case Lambda:
|
||||
((LambdaEvent*)event)->call();
|
||||
return true;
|
||||
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QUndoStack>
|
||||
#include <functional>
|
||||
|
||||
#include <AbstractScriptingServicesInterface.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
|
@ -147,6 +148,8 @@ public:
|
|||
Application(int& argc, char** argv, QElapsedTimer &startup_time);
|
||||
~Application();
|
||||
|
||||
void postLambdaEvent(std::function<void()> f);
|
||||
|
||||
void loadScripts();
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
|
|
|
@ -323,7 +323,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
|||
//Render magnifier, but dont show border for mouse magnifier
|
||||
glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
|
||||
_reticlePosition[MOUSE].y()));
|
||||
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
|
||||
with_each_texture(_overlays.getTexture(), 0, [&] {
|
||||
renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// OffscreenQmlDialog.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_OffscreenQmlDialog_h
|
||||
#define hifi_OffscreenQmlDialog_h
|
||||
|
||||
#include <QQuickItem>
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
#define QML_DIALOG_DECL \
|
||||
private: \
|
||||
static const QString NAME; \
|
||||
static const QUrl QML; \
|
||||
public: \
|
||||
static void registerType(); \
|
||||
static void show(); \
|
||||
static void toggle(); \
|
||||
private:
|
||||
|
||||
#define QML_DIALOG_DEF(x) \
|
||||
const QUrl x::QML = QUrl(#x ".qml"); \
|
||||
const QString x::NAME = #x; \
|
||||
\
|
||||
void x::registerType() { \
|
||||
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
|
||||
} \
|
||||
\
|
||||
void x::show() { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->show(QML, NAME); \
|
||||
} \
|
||||
\
|
||||
void x::toggle() { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->toggle(QML, NAME); \
|
||||
}
|
||||
|
||||
class OffscreenQmlDialog : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OffscreenQmlDialog(QQuickItem* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void hide();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,16 +14,26 @@
|
|||
#include <QVector>
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <QDir>
|
||||
#include "PathUtils.h"
|
||||
|
||||
|
||||
QString& PathUtils::resourcesPath() {
|
||||
#ifdef DEBUG
|
||||
static QString staticResourcePath;
|
||||
if (staticResourcePath.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
staticResourcePath = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
|
||||
}
|
||||
#else
|
||||
#ifdef Q_OS_MAC
|
||||
static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
|
||||
#else
|
||||
static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return staticResourcePath;
|
||||
}
|
||||
|
||||
|
|
12
libraries/ui/CMakeLists.txt
Normal file
12
libraries/ui/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
set(TARGET_NAME ui)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(OpenGL Network Qml Quick Script)
|
||||
|
||||
link_hifi_libraries(render-utils shared)
|
||||
|
||||
add_dependency_external_projects(glm)
|
||||
find_package(GLM REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
|
||||
|
279
libraries/ui/src/HifiMenu.cpp
Normal file
279
libraries/ui/src/HifiMenu.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
#include "HifiMenu.h"
|
||||
#include <QtQml>
|
||||
|
||||
// FIXME can this be made a class member?
|
||||
static const QString MENU_SUFFIX{ "__Menu" };
|
||||
|
||||
HIFI_QML_DEF_LAMBDA(HifiMenu, [=](QQmlContext* context, QObject* newItem) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
QObject * rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
|
||||
Q_ASSERT(rootMenu);
|
||||
static_cast<HifiMenu*>(newItem)->setRootMenu(rootMenu);
|
||||
context->setContextProperty("rootMenu", rootMenu);
|
||||
});
|
||||
|
||||
HifiMenu::HifiMenu(QQuickItem* parent) : QQuickItem(parent), _triggerMapper(this), _toggleMapper(this) {
|
||||
this->setEnabled(false);
|
||||
connect(&_triggerMapper, SIGNAL(mapped(QString)), this, SLOT(onTriggeredByName(const QString &)));
|
||||
connect(&_toggleMapper, SIGNAL(mapped(QString)), this, SLOT(onToggledByName(const QString &)));
|
||||
}
|
||||
|
||||
void HifiMenu::onTriggeredByName(const QString & name) {
|
||||
qDebug() << name << " triggered";
|
||||
if (_triggerActions.count(name)) {
|
||||
_triggerActions[name]();
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMenu::onToggledByName(const QString & name) {
|
||||
qDebug() << name << " toggled";
|
||||
if (_toggleActions.count(name)) {
|
||||
QObject* menu = findMenuObject(name);
|
||||
bool checked = menu->property("checked").toBool();
|
||||
_toggleActions[name](checked);
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMenu::setToggleAction(const QString & name, std::function<void(bool)> f) {
|
||||
_toggleActions[name] = f;
|
||||
}
|
||||
|
||||
void HifiMenu::setTriggerAction(const QString & name, std::function<void()> f) {
|
||||
_triggerActions[name] = f;
|
||||
}
|
||||
|
||||
QObject* addMenu(QObject* parent, const QString & text) {
|
||||
// FIXME add more checking here to ensure no name conflicts
|
||||
QVariant returnedValue;
|
||||
QMetaObject::invokeMethod(parent, "addMenu", Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QVariant, returnedValue),
|
||||
Q_ARG(QVariant, text));
|
||||
QObject* result = returnedValue.value<QObject*>();
|
||||
if (result) {
|
||||
result->setObjectName(text + MENU_SUFFIX);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class QQuickMenuItem;
|
||||
QObject* addItem(QObject* parent, const QString& text) {
|
||||
// FIXME add more checking here to ensure no name conflicts
|
||||
QQuickMenuItem* returnedValue{ nullptr };
|
||||
bool invokeResult = QMetaObject::invokeMethod(parent, "addItem", Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
|
||||
Q_ARG(QString, text));
|
||||
Q_ASSERT(invokeResult);
|
||||
QObject* result = reinterpret_cast<QObject*>(returnedValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
const QObject* HifiMenu::findMenuObject(const QString & menuOption) const {
|
||||
if (menuOption.isEmpty()) {
|
||||
return _rootMenu;
|
||||
}
|
||||
const QObject* result = _rootMenu->findChild<QObject*>(menuOption + MENU_SUFFIX);
|
||||
return result;
|
||||
}
|
||||
|
||||
QObject* HifiMenu::findMenuObject(const QString & menuOption) {
|
||||
if (menuOption.isEmpty()) {
|
||||
return _rootMenu;
|
||||
}
|
||||
QObject* result = _rootMenu->findChild<QObject*>(menuOption + MENU_SUFFIX);
|
||||
return result;
|
||||
}
|
||||
|
||||
void HifiMenu::addMenu(const QString & parentMenu, const QString & menuOption) {
|
||||
QObject* parent = findMenuObject(parentMenu);
|
||||
QObject* result = ::addMenu(parent, menuOption);
|
||||
Q_ASSERT(result);
|
||||
result->setObjectName(menuOption + MENU_SUFFIX);
|
||||
Q_ASSERT(findMenuObject(menuOption));
|
||||
}
|
||||
|
||||
void HifiMenu::removeMenu(const QString& menuName) {
|
||||
QObject* menu = findMenuObject(menuName);
|
||||
Q_ASSERT(menu);
|
||||
Q_ASSERT(menu != _rootMenu);
|
||||
QMetaObject::invokeMethod(menu->parent(), "removeItem",
|
||||
Q_ARG(QVariant, QVariant::fromValue(menu)));
|
||||
}
|
||||
|
||||
bool HifiMenu::menuExists(const QString& menuName) const {
|
||||
return findMenuObject(menuName);
|
||||
}
|
||||
|
||||
void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorName) {
|
||||
QObject * parent = findMenuObject(parentMenu);
|
||||
bool invokeResult = QMetaObject::invokeMethod(parent, "addSeparator", Qt::DirectConnection);
|
||||
Q_ASSERT(invokeResult);
|
||||
addItem(parentMenu, separatorName);
|
||||
enableItem(separatorName, false);
|
||||
}
|
||||
|
||||
void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) {
|
||||
}
|
||||
|
||||
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption) {
|
||||
QObject* parent = findMenuObject(parentMenu);
|
||||
Q_ASSERT(parent);
|
||||
QObject* result = ::addItem(parent, menuOption);
|
||||
Q_ASSERT(result);
|
||||
result->setObjectName(menuOption + MENU_SUFFIX);
|
||||
Q_ASSERT(findMenuObject(menuOption));
|
||||
|
||||
_triggerMapper.setMapping(result, menuOption);
|
||||
connect(result, SIGNAL(triggered()), &_triggerMapper, SLOT(map()));
|
||||
|
||||
_toggleMapper.setMapping(result, menuOption);
|
||||
connect(result, SIGNAL(toggled(bool)), &_toggleMapper, SLOT(map()));
|
||||
}
|
||||
|
||||
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, std::function<void()> f) {
|
||||
setTriggerAction(menuOption, f);
|
||||
addItem(parentMenu, menuOption);
|
||||
}
|
||||
|
||||
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, QObject* receiver, const char* slot) {
|
||||
addItem(parentMenu, menuOption);
|
||||
connectItem(menuOption, receiver, slot);
|
||||
}
|
||||
|
||||
void HifiMenu::removeItem(const QString& menuOption) {
|
||||
removeMenu(menuOption);
|
||||
}
|
||||
|
||||
bool HifiMenu::itemExists(const QString& menuName, const QString& menuitem) const {
|
||||
return findMenuObject(menuName);
|
||||
}
|
||||
|
||||
void HifiMenu::triggerItem(const QString& menuOption) {
|
||||
QObject* menuItem = findMenuObject(menuOption);
|
||||
Q_ASSERT(menuItem);
|
||||
Q_ASSERT(menuItem != _rootMenu);
|
||||
QMetaObject::invokeMethod(menuItem, "trigger");
|
||||
}
|
||||
|
||||
QHash<QString, QString> warned;
|
||||
void warn(const QString & menuOption) {
|
||||
if (!warned.contains(menuOption)) {
|
||||
warned[menuOption] = menuOption;
|
||||
qWarning() << "No menu item: " << menuOption;
|
||||
}
|
||||
}
|
||||
|
||||
bool HifiMenu::isChecked(const QString& menuOption) const {
|
||||
const QObject* menuItem = findMenuObject(menuOption);
|
||||
if (!menuItem) {
|
||||
warn(menuOption);
|
||||
return false;
|
||||
}
|
||||
return menuItem->property("checked").toBool();
|
||||
}
|
||||
|
||||
void HifiMenu::setChecked(const QString& menuOption, bool isChecked) {
|
||||
QObject* menuItem = findMenuObject(menuOption);
|
||||
if (!menuItem) {
|
||||
warn(menuOption);
|
||||
return;
|
||||
}
|
||||
if (menuItem->property("checked").toBool() != isChecked) {
|
||||
menuItem->setProperty("checked", QVariant::fromValue(isChecked));
|
||||
Q_ASSERT(menuItem->property("checked").toBool() == isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMenu::setCheckable(const QString& menuOption, bool checkable) {
|
||||
QObject* menuItem = findMenuObject(menuOption);
|
||||
if (!menuItem) {
|
||||
warn(menuOption);
|
||||
return;
|
||||
}
|
||||
|
||||
menuItem->setProperty("checkable", QVariant::fromValue(checkable));
|
||||
Q_ASSERT(menuItem->property("checkable").toBool() == checkable);
|
||||
}
|
||||
|
||||
void HifiMenu::setItemText(const QString& menuOption, const QString& text) {
|
||||
QObject* menuItem = findMenuObject(menuOption);
|
||||
if (!menuItem) {
|
||||
warn(menuOption);
|
||||
return;
|
||||
}
|
||||
if (menuItem->property("type").toInt() == 2) {
|
||||
menuItem->setProperty("title", QVariant::fromValue(text));
|
||||
} else {
|
||||
menuItem->setProperty("text", QVariant::fromValue(text));
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMenu::setRootMenu(QObject* rootMenu) {
|
||||
_rootMenu = rootMenu;
|
||||
}
|
||||
|
||||
void HifiMenu::enableItem(const QString & menuOption, bool enabled) {
|
||||
QObject* menuItem = findMenuObject(menuOption);
|
||||
if (!menuItem) {
|
||||
warn(menuOption);
|
||||
return;
|
||||
}
|
||||
menuItem->setProperty("enabled", QVariant::fromValue(enabled));
|
||||
}
|
||||
|
||||
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) {
|
||||
addItem(parentMenu, menuOption);
|
||||
setCheckable(menuOption);
|
||||
if (checked) {
|
||||
setChecked(menuOption, checked);
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function<void(bool)> f) {
|
||||
setToggleAction(menuOption, f);
|
||||
addCheckableItem(parentMenu, menuOption, checked);
|
||||
}
|
||||
|
||||
void HifiMenu::setItemVisible(const QString& menuOption, bool visible) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
if (result) {
|
||||
result->setProperty("visible", visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool HifiMenu::isItemVisible(const QString& menuOption) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
if (result) {
|
||||
return result->property("visible").toBool();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HifiMenu::setItemShortcut(const QString& menuOption, const QString& shortcut) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
if (result) {
|
||||
result->setProperty("shortcut", shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
QString HifiMenu::getItemShortcut(const QString& menuOption) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
if (result) {
|
||||
return result->property("shortcut").toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot) {
|
||||
addCheckableItem(parentMenu, menuOption, checked);
|
||||
connectItem(menuOption, receiver, slot);
|
||||
}
|
||||
|
||||
void HifiMenu::connectCheckable(const QString& menuOption, QObject* receiver, const char* slot) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
connect(result, SIGNAL(toggled(bool)), receiver, slot);
|
||||
}
|
||||
|
||||
void HifiMenu::connectItem(const QString& menuOption, QObject* receiver, const char* slot) {
|
||||
QObject* result = findMenuObject(menuOption);
|
||||
connect(result, SIGNAL(triggered()), receiver, slot);
|
||||
}
|
83
libraries/ui/src/HifiMenu.h
Normal file
83
libraries/ui/src/HifiMenu.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// MenuConstants.h
|
||||
//
|
||||
// Created by Bradley Austin Davis on 2015/04/21
|
||||
// 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_MenuContants_h
|
||||
#define hifi_MenuConstants_h
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QSignalMapper>
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
class HifiMenu : public QQuickItem {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL_LAMBDA
|
||||
|
||||
public:
|
||||
HifiMenu(QQuickItem* parent = nullptr);
|
||||
|
||||
void setToggleAction(const QString& name, std::function<void(bool)> f);
|
||||
void setTriggerAction(const QString& name, std::function<void()> f);
|
||||
|
||||
void addMenu(const QString& parentMenu, const QString& menuOption);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName) const;
|
||||
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
void addItem(const QString& parentMenu, const QString& menuOption);
|
||||
void addItem(const QString& parentMenu, const QString& menuOption, std::function<void()> f);
|
||||
void addItem(const QString& parentMenu, const QString& menuOption, QObject* receiver, const char* slot);
|
||||
|
||||
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked = false);
|
||||
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function<void(bool)> f);
|
||||
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot);
|
||||
void connectCheckable(const QString& menuOption, QObject* receiver, const char* slot);
|
||||
void connectItem(const QString& menuOption, QObject* receiver, const char* slot);
|
||||
|
||||
void removeItem(const QString& menuitem);
|
||||
bool itemExists(const QString& menuName, const QString& menuitem) const;
|
||||
void triggerItem(const QString& menuOption);
|
||||
void enableItem(const QString& menuOption, bool enabled = true);
|
||||
bool isChecked(const QString& menuOption) const;
|
||||
void setChecked(const QString& menuOption, bool checked = true);
|
||||
void setCheckable(const QString& menuOption, bool checkable = true);
|
||||
void setExclusiveGroup(const QString& menuOption, const QString& groupName);
|
||||
void setItemText(const QString& menuOption, const QString& text);
|
||||
void setItemVisible(const QString& menuOption, bool visible = true);
|
||||
bool isItemVisible(const QString& menuOption);
|
||||
|
||||
void setItemShortcut(const QString& menuOption, const QString& shortcut);
|
||||
QString getItemShortcut(const QString& menuOption);
|
||||
|
||||
void setRootMenu(QObject* rootMenu);
|
||||
|
||||
private slots:
|
||||
void onTriggeredByName(const QString& name);
|
||||
void onToggledByName(const QString& name);
|
||||
|
||||
protected:
|
||||
QHash<QString, std::function<void()>> _triggerActions;
|
||||
QHash<QString, std::function<void(bool)>> _toggleActions;
|
||||
QObject* findMenuObject(const QString& name);
|
||||
const QObject* findMenuObject(const QString& name) const;
|
||||
QObject* _rootMenu{ nullptr };
|
||||
QSignalMapper _triggerMapper;
|
||||
QSignalMapper _toggleMapper;
|
||||
};
|
||||
|
||||
#endif // hifi_MenuConstants_h
|
||||
|
||||
|
||||
|
||||
|
142
libraries/ui/src/MessageDialog.cpp
Normal file
142
libraries/ui/src/MessageDialog.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
//
|
||||
//
|
||||
// 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"
|
||||
|
||||
QML_DIALOG_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) {
|
||||
click(static_cast<StandardButton>(button),
|
||||
static_cast<QPlatformDialogHelper::ButtonRole>(
|
||||
QPlatformDialogHelper::buttonRole(static_cast<QPlatformDialogHelper::StandardButton>(button))));
|
||||
}
|
||||
|
||||
QUrl MessageDialog::standardIconSource() {
|
||||
switch (icon()) {
|
||||
case QMessageDialogOptions::Information:
|
||||
return QUrl("images/information.png");
|
||||
break;
|
||||
case QMessageDialogOptions::Warning:
|
||||
return QUrl("images/warning.png");
|
||||
break;
|
||||
case QMessageDialogOptions::Critical:
|
||||
return QUrl("images/critical.png");
|
||||
break;
|
||||
case QMessageDialogOptions::Question:
|
||||
return QUrl("images/question.png");
|
||||
break;
|
||||
default:
|
||||
return QUrl();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog::StandardButtons MessageDialog::standardButtons() const {
|
||||
return _buttons;
|
||||
}
|
||||
|
||||
MessageDialog::StandardButton MessageDialog::clickedButton() const {
|
||||
return _clickedButton;
|
||||
}
|
||||
|
||||
void MessageDialog::click(StandardButton button, QPlatformDialogHelper::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;
|
||||
}
|
98
libraries/ui/src/MessageDialog.h
Normal file
98
libraries/ui/src/MessageDialog.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// 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"
|
||||
#include <5.4.1/QtGui/qpa/qplatformdialoghelper.h>
|
||||
|
||||
class MessageDialog : public OffscreenQmlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_DIALOG_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(QUrl standardIconSource READ standardIconSource NOTIFY iconChanged)
|
||||
Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons NOTIFY standardButtonsChanged)
|
||||
Q_PROPERTY(StandardButton clickedButton READ clickedButton NOTIFY buttonClicked)
|
||||
|
||||
public:
|
||||
enum Icon {
|
||||
NoIcon = QMessageDialogOptions::NoIcon,
|
||||
Information = QMessageDialogOptions::Information,
|
||||
Warning = QMessageDialogOptions::Warning,
|
||||
Critical = QMessageDialogOptions::Critical,
|
||||
Question = QMessageDialogOptions::Question
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
QUrl standardIconSource();
|
||||
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, QPlatformDialogHelper::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
|
||||
|
||||
|
||||
|
||||
|
|
@ -13,6 +13,30 @@
|
|||
OffscreenQmlDialog::OffscreenQmlDialog(QQuickItem* parent)
|
||||
: QQuickItem(parent) { }
|
||||
|
||||
OffscreenQmlDialog::~OffscreenQmlDialog() {
|
||||
}
|
||||
|
||||
void OffscreenQmlDialog::hide() {
|
||||
static_cast<QQuickItem*>(parent())->setEnabled(false);
|
||||
}
|
||||
|
||||
QString OffscreenQmlDialog::title() const {
|
||||
return _title;
|
||||
}
|
||||
|
||||
void OffscreenQmlDialog::setTitle(const QString &arg) {
|
||||
if (arg != _title) {
|
||||
_title = arg;
|
||||
emit titleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void OffscreenQmlDialog::accept() {
|
||||
hide();
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void OffscreenQmlDialog::reject() {
|
||||
hide();
|
||||
emit rejected();
|
||||
}
|
104
libraries/ui/src/OffscreenQmlDialog.h
Normal file
104
libraries/ui/src/OffscreenQmlDialog.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// OffscreenQmlDialog.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_OffscreenQmlDialog_h
|
||||
#define hifi_OffscreenQmlDialog_h
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <5.4.1/QtGui/qpa/qplatformdialoghelper.h>
|
||||
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
#define QML_DIALOG_DECL \
|
||||
private: \
|
||||
static const QString NAME; \
|
||||
static const QUrl QML; \
|
||||
public: \
|
||||
static void registerType(); \
|
||||
static void show(std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}); \
|
||||
static void toggle(std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}); \
|
||||
private:
|
||||
|
||||
#define QML_DIALOG_DEF(x) \
|
||||
const QUrl x::QML = QUrl(#x ".qml"); \
|
||||
const QString x::NAME = #x; \
|
||||
\
|
||||
void x::registerType() { \
|
||||
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
|
||||
} \
|
||||
\
|
||||
void x::show(std::function<void(QQmlContext*, QObject*)> f) { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->show(QML, NAME, f); \
|
||||
} \
|
||||
\
|
||||
void x::toggle(std::function<void(QQmlContext*, QObject*)> f) { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->toggle(QML, NAME, f); \
|
||||
}
|
||||
|
||||
class OffscreenQmlDialog : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
|
||||
Q_ENUMS(StandardButton)
|
||||
Q_FLAGS(StandardButtons)
|
||||
|
||||
public:
|
||||
OffscreenQmlDialog(QQuickItem* parent = nullptr);
|
||||
virtual ~OffscreenQmlDialog();
|
||||
|
||||
enum StandardButton {
|
||||
NoButton = QPlatformDialogHelper::NoButton,
|
||||
Ok = QPlatformDialogHelper::Ok,
|
||||
Save = QPlatformDialogHelper::Save,
|
||||
SaveAll = QPlatformDialogHelper::SaveAll,
|
||||
Open = QPlatformDialogHelper::Open,
|
||||
Yes = QPlatformDialogHelper::Yes,
|
||||
YesToAll = QPlatformDialogHelper::YesToAll,
|
||||
No = QPlatformDialogHelper::No,
|
||||
NoToAll = QPlatformDialogHelper::NoToAll,
|
||||
Abort = QPlatformDialogHelper::Abort,
|
||||
Retry = QPlatformDialogHelper::Retry,
|
||||
Ignore = QPlatformDialogHelper::Ignore,
|
||||
Close = QPlatformDialogHelper::Close,
|
||||
Cancel = QPlatformDialogHelper::Cancel,
|
||||
Discard = QPlatformDialogHelper::Discard,
|
||||
Help = QPlatformDialogHelper::Help,
|
||||
Apply = QPlatformDialogHelper::Apply,
|
||||
Reset = QPlatformDialogHelper::Reset,
|
||||
RestoreDefaults = QPlatformDialogHelper::RestoreDefaults,
|
||||
NButtons
|
||||
};
|
||||
Q_DECLARE_FLAGS(StandardButtons, StandardButton)
|
||||
|
||||
protected:
|
||||
void hide();
|
||||
virtual void accept();
|
||||
virtual void reject();
|
||||
|
||||
public:
|
||||
QString title() const;
|
||||
void setTitle(const QString &arg);
|
||||
|
||||
signals:
|
||||
void accepted();
|
||||
void rejected();
|
||||
void titleChanged();
|
||||
|
||||
private:
|
||||
QString _title;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(OffscreenQmlDialog::StandardButtons)
|
||||
|
||||
#endif
|
|
@ -13,6 +13,11 @@
|
|||
#include <QOpenGLDebugLogger>
|
||||
#include <QGLWidget>
|
||||
#include <QtQml>
|
||||
#include "MessageDialog.h"
|
||||
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(offscreenFocus)
|
||||
Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
|
||||
|
||||
// Time between receiving a request to render the offscreen UI actually triggering
|
||||
// the render. Could possibly be increased depending on the framerate we expect to
|
||||
|
@ -92,10 +97,10 @@ void OffscreenUi::create(QOpenGLContext* shareContext) {
|
|||
|
||||
#ifdef DEBUG
|
||||
connect(_quickWindow, &QQuickWindow::focusObjectChanged, [this]{
|
||||
qDebug() << "New focus item " << _quickWindow->focusObject();
|
||||
qCDebug(offscreenFocus) << "New focus item " << _quickWindow->focusObject();
|
||||
});
|
||||
connect(_quickWindow, &QQuickWindow::activeFocusItemChanged, [this] {
|
||||
qDebug() << "New active focus item " << _quickWindow->activeFocusItem();
|
||||
qCDebug(offscreenFocus) << "New active focus item " << _quickWindow->activeFocusItem();
|
||||
});
|
||||
#endif
|
||||
|
||||
|
@ -117,36 +122,45 @@ void OffscreenUi::resize(const QSize& newSize) {
|
|||
qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio;
|
||||
_fboCache.setSize(newSize * pixelRatio);
|
||||
|
||||
if (_quickWindow) {
|
||||
_quickWindow->setGeometry(QRect(QPoint(), newSize));
|
||||
}
|
||||
|
||||
_quickWindow->contentItem()->setSize(newSize);
|
||||
|
||||
// Update our members
|
||||
if (_rootItem) {
|
||||
_rootItem->setSize(newSize);
|
||||
}
|
||||
|
||||
if (_quickWindow) {
|
||||
_quickWindow->setGeometry(QRect(QPoint(), newSize));
|
||||
}
|
||||
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
QQmlContext* OffscreenUi::qmlContext() {
|
||||
if (nullptr == _rootItem) {
|
||||
return _qmlComponent->creationContext();
|
||||
}
|
||||
return QQmlEngine::contextForObject(_rootItem);
|
||||
QQuickItem* OffscreenUi::getRootItem() {
|
||||
return _rootItem;
|
||||
}
|
||||
|
||||
//QQmlContext* OffscreenUi::qmlContext() {
|
||||
// if (nullptr == _rootItem) {
|
||||
// return _qmlComponent->creationContext();
|
||||
// }
|
||||
// return QQmlEngine::contextForObject(_rootItem);
|
||||
//}
|
||||
|
||||
void OffscreenUi::setBaseUrl(const QUrl& baseUrl) {
|
||||
_qmlEngine->setBaseUrl(baseUrl);
|
||||
}
|
||||
|
||||
void OffscreenUi::load(const QUrl& qmlSource, std::function<void(QQmlContext*)> f) {
|
||||
void OffscreenUi::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
qDebug() << "Loading QML from URL " << qmlSource;
|
||||
_qmlComponent->loadUrl(qmlSource);
|
||||
if (_qmlComponent->isLoading()) {
|
||||
connect(_qmlComponent, &QQmlComponent::statusChanged, this, []{});
|
||||
} else {
|
||||
finishQmlLoad();
|
||||
if (_qmlComponent->isLoading())
|
||||
connect(_qmlComponent, &QQmlComponent::statusChanged, this,
|
||||
[this, f](QQmlComponent::Status){
|
||||
finishQmlLoad(f);
|
||||
});
|
||||
else {
|
||||
finishQmlLoad(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,8 +177,8 @@ void OffscreenUi::requestRender() {
|
|||
}
|
||||
}
|
||||
|
||||
void OffscreenUi::finishQmlLoad() {
|
||||
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad);
|
||||
void OffscreenUi::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) {
|
||||
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (_qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = _qmlComponent->errors();
|
||||
foreach(const QQmlError &error, errorList) {
|
||||
|
@ -173,7 +187,8 @@ void OffscreenUi::finishQmlLoad() {
|
|||
return;
|
||||
}
|
||||
|
||||
QObject* newObject = _qmlComponent->create();
|
||||
QQmlContext * newContext = new QQmlContext(_qmlEngine, qApp);
|
||||
QObject* newObject = _qmlComponent->beginCreate(newContext);
|
||||
if (_qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = _qmlComponent->errors();
|
||||
foreach(const QQmlError &error, errorList)
|
||||
|
@ -184,9 +199,13 @@ void OffscreenUi::finishQmlLoad() {
|
|||
return;
|
||||
}
|
||||
|
||||
f(newContext, newObject);
|
||||
_qmlComponent->completeCreate();
|
||||
|
||||
QQuickItem* newItem = qobject_cast<QQuickItem*>(newObject);
|
||||
if (!newItem) {
|
||||
qWarning("run: Not a QQuickItem");
|
||||
return;
|
||||
delete newObject;
|
||||
if (!_rootItem) {
|
||||
qFatal("Unable to find root QQuickItem");
|
||||
|
@ -197,18 +216,17 @@ void OffscreenUi::finishQmlLoad() {
|
|||
// Make sure we make items focusable (critical for
|
||||
// supporting keyboard shortcuts)
|
||||
newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
|
||||
|
||||
if (!_rootItem) {
|
||||
// The root item is ready. Associate it with the window.
|
||||
_rootItem = newItem;
|
||||
_rootItem->setParentItem(_quickWindow->contentItem());
|
||||
_rootItem->setSize(_quickWindow->renderTargetSize());
|
||||
_rootItem->forceActiveFocus();
|
||||
} else {
|
||||
// Allow child windows to be destroyed from JS
|
||||
QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership);
|
||||
newItem->setParent(_rootItem);
|
||||
newItem->setParentItem(_rootItem);
|
||||
newItem->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,54 +408,62 @@ void OffscreenUi::setProxyWindow(QWindow* window) {
|
|||
_renderControl->_renderWindow = window;
|
||||
}
|
||||
|
||||
void OffscreenUi::show(const QUrl& url, const QString& name) {
|
||||
void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name);
|
||||
// First load?
|
||||
if (!item) {
|
||||
load(url);
|
||||
return;
|
||||
load(url, f);
|
||||
item = _rootItem->findChild<QQuickItem*>(name);
|
||||
}
|
||||
item->setEnabled(true);
|
||||
}
|
||||
|
||||
void OffscreenUi::toggle(const QUrl& url, const QString& name) {
|
||||
void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name);
|
||||
// First load?
|
||||
if (!item) {
|
||||
load(url);
|
||||
return;
|
||||
load(url, f);
|
||||
item = _rootItem->findChild<QQuickItem*>(name);
|
||||
}
|
||||
item->setEnabled(!item->isEnabled());
|
||||
}
|
||||
|
||||
void OffscreenUi::messageBox(const QString& title, const QString& text,
|
||||
ButtonCallback callback,
|
||||
QMessageBox::Icon icon,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback f) {
|
||||
QMessageBox::StandardButtons buttons) {
|
||||
MessageDialog::show([=](QQmlContext*ctx, QObject*item) {
|
||||
MessageDialog * pDialog = item->findChild<MessageDialog*>();
|
||||
pDialog->setIcon((MessageDialog::Icon)icon);
|
||||
pDialog->setTitle(title);
|
||||
pDialog->setText(text);
|
||||
pDialog->setStandardButtons(MessageDialog::StandardButtons((int)buttons));
|
||||
pDialog->setResultCallback(callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OffscreenUi::information(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback callback) {
|
||||
callback(QMessageBox::information(nullptr, title, text, buttons));
|
||||
ButtonCallback callback,
|
||||
QMessageBox::StandardButtons buttons) {
|
||||
messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Information, buttons);
|
||||
}
|
||||
|
||||
void OffscreenUi::question(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback callback) {
|
||||
callback(QMessageBox::question(nullptr, title, text, buttons));
|
||||
ButtonCallback callback,
|
||||
QMessageBox::StandardButtons buttons) {
|
||||
messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Question, buttons);
|
||||
}
|
||||
|
||||
void OffscreenUi::warning(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback callback) {
|
||||
callback(QMessageBox::warning(nullptr, title, text, buttons));
|
||||
ButtonCallback callback,
|
||||
QMessageBox::StandardButtons buttons) {
|
||||
messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Warning, buttons);
|
||||
}
|
||||
|
||||
void OffscreenUi::critical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback callback) {
|
||||
callback(QMessageBox::critical(nullptr, title, text, buttons));
|
||||
ButtonCallback callback,
|
||||
QMessageBox::StandardButtons buttons) {
|
||||
messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Critical, buttons);
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +32,69 @@
|
|||
#include "FboCache.h"
|
||||
#include <QQuickItem>
|
||||
|
||||
#define HIFI_QML_DECL \
|
||||
private: \
|
||||
static const QString NAME; \
|
||||
static const QUrl QML; \
|
||||
public: \
|
||||
static void registerType(); \
|
||||
static void show(std::function<void(QQmlContext*, QQuickItem *)> f = [](QQmlContext*, QQuickItem*) {}); \
|
||||
static void toggle(std::function<void(QQmlContext*, QQuickItem *)> f = [](QQmlContext*, QQuickItem*) {}); \
|
||||
static void load(std::function<void(QQmlContext*, QQuickItem *)> f = [](QQmlContext*, QQuickItem*) {}); \
|
||||
private:
|
||||
|
||||
#define HIFI_QML_DECL_LAMBDA \
|
||||
protected: \
|
||||
static const QString NAME; \
|
||||
static const QUrl QML; \
|
||||
public: \
|
||||
static void registerType(); \
|
||||
static void show(); \
|
||||
static void toggle(); \
|
||||
static void load(); \
|
||||
private:
|
||||
|
||||
#define HIFI_QML_DEF(x) \
|
||||
const QUrl x::QML = QUrl(#x ".qml"); \
|
||||
const QString x::NAME = #x; \
|
||||
\
|
||||
void x::registerType() { \
|
||||
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
|
||||
} \
|
||||
\
|
||||
void x::show(std::function<void(QQmlContext*, QQuickItem *)> f) { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->show(QML, NAME, f); \
|
||||
} \
|
||||
\
|
||||
void x::toggle(std::function<void(QQmlContext*, QQuickItem *)> f) { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->toggle(QML, NAME, f); \
|
||||
} \
|
||||
void x::load(std::function<void(QQmlContext*, QQuickItem *)> f) { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->load(QML, f); \
|
||||
}
|
||||
|
||||
#define HIFI_QML_DEF_LAMBDA(x, f) \
|
||||
const QUrl x::QML = QUrl(#x ".qml"); \
|
||||
const QString x::NAME = #x; \
|
||||
\
|
||||
void x::registerType() { \
|
||||
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
|
||||
} \
|
||||
void x::show() { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->show(QML, NAME, f); \
|
||||
} \
|
||||
void x::toggle() { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->toggle(QML, NAME, f); \
|
||||
} \
|
||||
void x::load() { \
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
|
||||
offscreenUi->load(QML, f); \
|
||||
}
|
||||
|
||||
class OffscreenUi : public OffscreenGlCanvas, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -59,16 +122,16 @@ public:
|
|||
virtual ~OffscreenUi();
|
||||
void create(QOpenGLContext* context);
|
||||
void resize(const QSize& size);
|
||||
void load(const QUrl& qmlSource, std::function<void(QQmlContext*)> f = [](QQmlContext*) {});
|
||||
void load(const QString& qmlSourceFile, std::function<void(QQmlContext*)> f = [](QQmlContext*) {}) {
|
||||
void load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
void load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
|
||||
load(QUrl(qmlSourceFile), f);
|
||||
}
|
||||
void show(const QUrl& url, const QString& name);
|
||||
void toggle(const QUrl& url, const QString& name);
|
||||
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||
void setBaseUrl(const QUrl& baseUrl);
|
||||
void addImportPath(const QString& path);
|
||||
QQmlContext* qmlContext();
|
||||
|
||||
//QQmlContext* getQmlContext();
|
||||
QQuickItem* getRootItem();
|
||||
void pause();
|
||||
void resume();
|
||||
bool isPaused() const;
|
||||
|
@ -86,31 +149,31 @@ public:
|
|||
static ButtonCallback NO_OP_CALLBACK;
|
||||
|
||||
static void messageBox(const QString& title, const QString& text,
|
||||
ButtonCallback f,
|
||||
QMessageBox::Icon icon,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
ButtonCallback f);
|
||||
QMessageBox::StandardButtons buttons);
|
||||
|
||||
static void information(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
ButtonCallback callback = NO_OP_CALLBACK);
|
||||
ButtonCallback callback = NO_OP_CALLBACK,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static void question(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
ButtonCallback callback = [](QMessageBox::StandardButton) {});
|
||||
ButtonCallback callback = NO_OP_CALLBACK,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No));
|
||||
|
||||
static void warning(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
ButtonCallback callback = [](QMessageBox::StandardButton) {});
|
||||
ButtonCallback callback = NO_OP_CALLBACK,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static void critical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
ButtonCallback callback = [](QMessageBox::StandardButton) {});
|
||||
ButtonCallback callback = NO_OP_CALLBACK,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
protected:
|
||||
|
||||
private slots:
|
||||
void updateQuick();
|
||||
void finishQmlLoad();
|
||||
void finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f);
|
||||
|
||||
public slots:
|
||||
void requestUpdate();
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "TextRenderer.h"
|
||||
#include "MatrixStack.h"
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QFile>
|
||||
|
@ -27,6 +26,7 @@
|
|||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QApplication>
|
||||
#include <QOpenGLDebugLogger>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
@ -80,6 +80,7 @@ const QString& getQmlDir() {
|
|||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
|
@ -88,24 +89,17 @@ class QTestWindow : public QWindow {
|
|||
QSize _size;
|
||||
TextRenderer* _textRenderer[4];
|
||||
RateCounter fps;
|
||||
int testQmlTexture{ 0 };
|
||||
//ProgramPtr _planeProgam;
|
||||
//ShapeWrapperPtr _planeShape;
|
||||
|
||||
protected:
|
||||
|
||||
void renderText();
|
||||
void renderQml();
|
||||
|
||||
private:
|
||||
void resizeWindow(const QSize& size) {
|
||||
_size = size;
|
||||
DependencyManager::get<OffscreenUi>()->resize(_size);
|
||||
}
|
||||
|
||||
public:
|
||||
QTestWindow() {
|
||||
DependencyManager::set<OffscreenUi>();
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
QSurfaceFormat format;
|
||||
|
@ -165,30 +159,10 @@ public:
|
|||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_context);
|
||||
// FIXME, need to switch to a QWindow for mouse and keyboard input to work
|
||||
offscreenUi->setProxyWindow(this);
|
||||
// "#0e7077"
|
||||
makeCurrent();
|
||||
|
||||
setFramePosition(QPoint(-1000, 0));
|
||||
resize(QSize(800, 600));
|
||||
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
|
||||
offscreenUi->load(QUrl("TestRoot.qml"));
|
||||
offscreenUi->addImportPath(getQmlDir());
|
||||
offscreenUi->addImportPath(".");
|
||||
|
||||
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
|
||||
offscreenUi->lockTexture(textureId);
|
||||
assert(!glGetError());
|
||||
GLuint oldTexture = testQmlTexture;
|
||||
testQmlTexture = textureId;
|
||||
if (oldTexture) {
|
||||
offscreenUi->releaseTexture(oldTexture);
|
||||
}
|
||||
});
|
||||
installEventFilter(offscreenUi.data());
|
||||
offscreenUi->resume();
|
||||
}
|
||||
|
||||
virtual ~QTestWindow() {
|
||||
|
@ -204,28 +178,6 @@ protected:
|
|||
void resizeEvent(QResizeEvent* ev) override {
|
||||
resizeWindow(ev->size());
|
||||
}
|
||||
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_L:
|
||||
if (event->modifiers() & Qt::CTRL) {
|
||||
DependencyManager::get<OffscreenUi>()->toggle(QString("TestDialog.qml"), "TestDialog");
|
||||
}
|
||||
break;
|
||||
}
|
||||
QWindow::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void moveEvent(QMoveEvent* event) {
|
||||
static qreal oldPixelRatio = 0.0;
|
||||
if (devicePixelRatio() != oldPixelRatio) {
|
||||
oldPixelRatio = devicePixelRatio();
|
||||
resizeWindow(size());
|
||||
}
|
||||
|
||||
QWindow::moveEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef SERIF_FONT_FAMILY
|
||||
|
@ -282,39 +234,16 @@ void QTestWindow::renderText() {
|
|||
}
|
||||
}
|
||||
|
||||
void QTestWindow::renderQml() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
if (testQmlTexture > 0) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
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);
|
||||
}
|
||||
glBegin(GL_QUADS);
|
||||
{
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(-1, -1);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(-1, 1);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(1, 1);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(1, -1);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void QTestWindow::draw() {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
makeCurrent();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio());
|
||||
|
||||
//renderText();
|
||||
renderQml();
|
||||
renderText();
|
||||
|
||||
_context->swapBuffers(this);
|
||||
glFinish();
|
||||
|
@ -327,10 +256,8 @@ void QTestWindow::draw() {
|
|||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
//QLoggingCategory::setFilterRules("qt.quick.mouse.debug = true");
|
||||
QGuiApplication app(argc, argv);
|
||||
QTestWindow window;
|
||||
|
||||
QTimer timer;
|
||||
timer.setInterval(1);
|
||||
app.connect(&timer, &QTimer::timeout, &app, [&] {
|
||||
|
|
15
tests/ui/CMakeLists.txt
Normal file
15
tests/ui/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
set(TARGET_NAME ui-tests)
|
||||
|
||||
setup_hifi_project(Widgets OpenGL Network Qml Quick Script)
|
||||
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(glew)
|
||||
find_package(GLEW REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib)
|
||||
endif()
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(ui render-utils gpu shared)
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
161
tests/ui/main.qml
Normal file
161
tests/ui/main.qml
Normal file
|
@ -0,0 +1,161 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
import "qml/UI.js" as UI
|
||||
import "qml"
|
||||
//import "/Users/bdavis/Git/hifi/interface/resources/qml"
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: true
|
||||
//title: "Qt Quick Controls Gallery"
|
||||
|
||||
MessageDialog {
|
||||
id: aboutDialog
|
||||
icon: StandardIcon.Information
|
||||
title: "About"
|
||||
text: "Qt Quick Controls Gallery"
|
||||
informativeText: "This example demonstrates most of the available Qt Quick Controls."
|
||||
}
|
||||
|
||||
Action {
|
||||
id: copyAction
|
||||
text: "&Copy"
|
||||
shortcut: StandardKey.Copy
|
||||
iconName: "edit-copy"
|
||||
enabled: (!!activeFocusItem && !!activeFocusItem["copy"])
|
||||
onTriggered: activeFocusItem.copy()
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cutAction
|
||||
text: "Cu&t"
|
||||
shortcut: StandardKey.Cut
|
||||
iconName: "edit-cut"
|
||||
enabled: (!!activeFocusItem && !!activeFocusItem["cut"])
|
||||
onTriggered: activeFocusItem.cut()
|
||||
}
|
||||
|
||||
Action {
|
||||
id: pasteAction
|
||||
text: "&Paste"
|
||||
shortcut: StandardKey.Paste
|
||||
iconName: "edit-paste"
|
||||
enabled: (!!activeFocusItem && !!activeFocusItem["paste"])
|
||||
onTriggered: activeFocusItem.paste()
|
||||
}
|
||||
|
||||
// toolBar: ToolBar {
|
||||
// RowLayout {
|
||||
// anchors.fill: parent
|
||||
// anchors.margins: spacing
|
||||
// Label {
|
||||
// text: UI.label
|
||||
// }
|
||||
// Item { Layout.fillWidth: true }
|
||||
// CheckBox {
|
||||
// id: enabler
|
||||
// text: "Enabled"
|
||||
// checked: true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// menuBar: MenuBar {
|
||||
// Menu {
|
||||
// title: "&File"
|
||||
// MenuItem {
|
||||
// text: "E&xit"
|
||||
// shortcut: StandardKey.Quit
|
||||
// onTriggered: Qt.quit()
|
||||
// }
|
||||
// }
|
||||
// Menu {
|
||||
// title: "&Edit"
|
||||
// visible: tabView.currentIndex == 2
|
||||
// MenuItem { action: cutAction }
|
||||
// MenuItem { action: copyAction }
|
||||
// MenuItem { action: pasteAction }
|
||||
// }
|
||||
// Menu {
|
||||
// title: "&Help"
|
||||
// MenuItem {
|
||||
// text: "About..."
|
||||
// onTriggered: aboutDialog.open()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
TabView {
|
||||
id: tabView
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: UI.margin
|
||||
tabPosition: UI.tabPosition
|
||||
|
||||
Layout.minimumWidth: 360
|
||||
Layout.minimumHeight: 360
|
||||
Layout.preferredWidth: 480
|
||||
Layout.preferredHeight: 640
|
||||
|
||||
Tab {
|
||||
title: "Buttons"
|
||||
ButtonPage {
|
||||
enabled: enabler.checked
|
||||
}
|
||||
}
|
||||
Tab {
|
||||
title: "Progress"
|
||||
ProgressPage {
|
||||
enabled: enabler.checked
|
||||
}
|
||||
}
|
||||
Tab {
|
||||
title: "Input"
|
||||
InputPage {
|
||||
enabled: enabler.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
128
tests/ui/qml/ButtonPage.qml
Normal file
128
tests/ui/qml/ButtonPage.qml
Normal file
|
@ -0,0 +1,128 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
ScrollView {
|
||||
id: page
|
||||
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
width: Math.max(page.viewport.width, grid.implicitWidth + 2 * grid.rowSpacing)
|
||||
height: Math.max(page.viewport.height, grid.implicitHeight + 2 * grid.columnSpacing)
|
||||
|
||||
GridLayout {
|
||||
id: grid
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: grid.rowSpacing
|
||||
anchors.rightMargin: grid.rowSpacing
|
||||
anchors.topMargin: grid.columnSpacing
|
||||
|
||||
columns: page.width < page.height ? 1 : 2
|
||||
|
||||
GroupBox {
|
||||
title: "Button"
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: grid.columns
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Button { text: "OK"; isDefault: true }
|
||||
Button { text: "Cancel" }
|
||||
Item { Layout.fillWidth: true }
|
||||
Button {
|
||||
text: "Attach"
|
||||
menu: Menu {
|
||||
MenuItem { text: "Image" }
|
||||
MenuItem { text: "Document" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "CheckBox"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
CheckBox { text: "E-mail"; checked: true }
|
||||
CheckBox { text: "Calendar"; checked: true }
|
||||
CheckBox { text: "Contacts" }
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "RadioButton"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ExclusiveGroup { id: radioGroup }
|
||||
RadioButton { text: "Portrait"; exclusiveGroup: radioGroup }
|
||||
RadioButton { text: "Landscape"; exclusiveGroup: radioGroup }
|
||||
RadioButton { text: "Automatic"; exclusiveGroup: radioGroup; checked: true }
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "Switch"
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: grid.columns
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
RowLayout {
|
||||
Label { text: "Wi-Fi"; Layout.fillWidth: true }
|
||||
Switch { checked: true }
|
||||
}
|
||||
RowLayout {
|
||||
Label { text: "Bluetooth"; Layout.fillWidth: true }
|
||||
Switch { checked: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
tests/ui/qml/InputPage.qml
Normal file
114
tests/ui/qml/InputPage.qml
Normal file
|
@ -0,0 +1,114 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
ScrollView {
|
||||
id: page
|
||||
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing)
|
||||
height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing)
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: column.spacing
|
||||
|
||||
GroupBox {
|
||||
title: "TextField"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
TextField { placeholderText: "..."; Layout.fillWidth: true; z: 1 }
|
||||
TextField { placeholderText: "Password"; echoMode: TextInput.Password; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "ComboBox"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ComboBox {
|
||||
model: Qt.fontFamilies()
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
editable: true
|
||||
model: ListModel {
|
||||
id: listModel
|
||||
ListElement { text: "Apple" }
|
||||
ListElement { text: "Banana" }
|
||||
ListElement { text: "Coconut" }
|
||||
ListElement { text: "Orange" }
|
||||
}
|
||||
onAccepted: {
|
||||
if (find(currentText) === -1) {
|
||||
listModel.append({text: editText})
|
||||
currentIndex = find(editText)
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "SpinBox"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
SpinBox { value: 99; Layout.fillWidth: true; z: 1 }
|
||||
SpinBox { decimals: 2; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
tests/ui/qml/ProgressPage.qml
Normal file
90
tests/ui/qml/ProgressPage.qml
Normal file
|
@ -0,0 +1,90 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
ScrollView {
|
||||
id: page
|
||||
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing)
|
||||
height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing)
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: column.spacing
|
||||
|
||||
GroupBox {
|
||||
title: "ProgressBar"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ProgressBar { indeterminate: true; Layout.fillWidth: true }
|
||||
ProgressBar { value: slider.value; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "Slider"
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Slider { id: slider; value: 0.5; Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "BusyIndicator"
|
||||
Layout.fillWidth: true
|
||||
BusyIndicator { running: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
tests/ui/qml/UI.js
Normal file
45
tests/ui/qml/UI.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Quick Controls module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
.pragma library
|
||||
|
||||
var margin = 2
|
||||
var tabPosition = Qt.TopEdge
|
||||
var label = ""
|
336
tests/ui/src/main.cpp
Normal file
336
tests/ui/src/main.cpp
Normal file
|
@ -0,0 +1,336 @@
|
|||
//
|
||||
// main.cpp
|
||||
// tests/render-utils/src
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "OffscreenUi.h"
|
||||
#include <QWindow>
|
||||
#include <QFile>
|
||||
#include <QTime>
|
||||
#include <QImage>
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QResizeEvent>
|
||||
#include <QLoggingCategory>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QApplication>
|
||||
#include <QOpenGLDebugLogger>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include <PathUtils.h>
|
||||
#include <QDir>
|
||||
|
||||
#include "MessageDialog.h"
|
||||
#include "HifiMenu.h"
|
||||
|
||||
class RateCounter {
|
||||
std::vector<float> times;
|
||||
QElapsedTimer timer;
|
||||
public:
|
||||
RateCounter() {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
times.clear();
|
||||
}
|
||||
|
||||
unsigned int 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 NAN;
|
||||
}
|
||||
return (float) count() / elapsed();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const QString & getQmlDir() {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/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;
|
||||
}
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow, private QOpenGLFunctions {
|
||||
Q_OBJECT
|
||||
|
||||
QOpenGLContext * _context{ nullptr };
|
||||
QSize _size;
|
||||
bool _altPressed{ false };
|
||||
RateCounter fps;
|
||||
QTimer _timer;
|
||||
int testQmlTexture{ 0 };
|
||||
|
||||
public:
|
||||
QObject * rootMenu;
|
||||
|
||||
QTestWindow() {
|
||||
_timer.setInterval(1);
|
||||
connect(&_timer, &QTimer::timeout, [=] {
|
||||
draw();
|
||||
});
|
||||
|
||||
DependencyManager::set<OffscreenUi>();
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(16);
|
||||
format.setStencilBufferSize(8);
|
||||
format.setVersion(4, 1);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
|
||||
setFormat(format);
|
||||
|
||||
_context = new QOpenGLContext;
|
||||
_context->setFormat(format);
|
||||
if (!_context->create()) {
|
||||
qFatal("Could not create OpenGL context");
|
||||
}
|
||||
|
||||
show();
|
||||
makeCurrent();
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
{
|
||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
||||
logger->initialize(); // initializes in the current context, i.e. ctx
|
||||
logger->enableMessages();
|
||||
connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) {
|
||||
qDebug() << debugMessage;
|
||||
});
|
||||
// logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
|
||||
}
|
||||
|
||||
qDebug() << (const char*)this->glGetString(GL_VERSION);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
MessageDialog::registerType();
|
||||
HifiMenu::registerType();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_context);
|
||||
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
|
||||
offscreenUi->lockTexture(textureId);
|
||||
assert(!glGetError());
|
||||
GLuint oldTexture = testQmlTexture;
|
||||
testQmlTexture = textureId;
|
||||
if (oldTexture) {
|
||||
offscreenUi->releaseTexture(oldTexture);
|
||||
}
|
||||
});
|
||||
|
||||
makeCurrent();
|
||||
|
||||
offscreenUi->setProxyWindow(this);
|
||||
setFramePosition(QPoint(-1000, 0));
|
||||
resize(QSize(800, 600));
|
||||
|
||||
#ifdef QML_CONTROL_GALLERY
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir()));
|
||||
offscreenUi->load(QUrl("main.qml"));
|
||||
#else
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
|
||||
offscreenUi->load(QUrl("TestRoot.qml"));
|
||||
offscreenUi->load(QUrl("RootMenu.qml"));
|
||||
HifiMenu::load();
|
||||
QObject* menuObject = offscreenUi->getRootItem()->findChild<QObject*>("HifiMenu");
|
||||
HifiMenu* menu = offscreenUi->getRootItem()->findChild<HifiMenu*>();
|
||||
menu->addMenu("", "File");
|
||||
menu->addMenuItem("File", "Quit", []{
|
||||
QApplication::quit();
|
||||
});
|
||||
menu->addCheckableMenuItem("File", "Toggle", false, [](bool toggled) {
|
||||
qDebug() << "Toggle is " << toggled;
|
||||
});
|
||||
menu->addMenu("", "Edit");
|
||||
menu->addMenuItem("Edit", "Undo");
|
||||
menu->addMenuItem("Edit", "Redo");
|
||||
menu->addMenuItem("Edit", "Copy");
|
||||
menu->addMenuItem("Edit", "Cut");
|
||||
menu->addMenuItem("Edit", "Paste");
|
||||
menu->addMenu("", "Long Menu Name...");
|
||||
#endif
|
||||
installEventFilter(offscreenUi.data());
|
||||
offscreenUi->resume();
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
virtual ~QTestWindow() {
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
}
|
||||
|
||||
private:
|
||||
void draw() {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
makeCurrent();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio());
|
||||
|
||||
renderQml();
|
||||
|
||||
_context->swapBuffers(this);
|
||||
glFinish();
|
||||
|
||||
fps.increment();
|
||||
if (fps.elapsed() >= 2.0f) {
|
||||
qDebug() << "FPS: " << fps.rate();
|
||||
fps.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void makeCurrent() {
|
||||
_context->makeCurrent(this);
|
||||
}
|
||||
|
||||
void renderQml();
|
||||
|
||||
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) {
|
||||
_altPressed = Qt::Key_Alt == event->key();
|
||||
switch (event->key()) {
|
||||
case Qt::Key_L:
|
||||
if (event->modifiers() & Qt::CTRL) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
HifiMenu * menu = offscreenUi->findChild<HifiMenu*>();
|
||||
menu->addMenuItem("", "Test 3");
|
||||
menu->addMenuItem("File", "Test 3");
|
||||
}
|
||||
break;
|
||||
case Qt::Key_K:
|
||||
if (event->modifiers() & Qt::CTRL) {
|
||||
OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){
|
||||
qDebug() << b;
|
||||
});
|
||||
}
|
||||
break;
|
||||
case Qt::Key_J:
|
||||
if (event->modifiers() & Qt::CTRL) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
|
||||
QMetaObject::invokeMethod(rootMenu, "popup");
|
||||
}
|
||||
break;
|
||||
}
|
||||
QWindow::keyPressEvent(event);
|
||||
}
|
||||
QQmlContext* menuContext{ nullptr };
|
||||
void keyReleaseEvent(QKeyEvent *event) {
|
||||
if (_altPressed && Qt::Key_Alt == event->key()) {
|
||||
HifiMenu::toggle();
|
||||
}
|
||||
}
|
||||
|
||||
void moveEvent(QMoveEvent *event) {
|
||||
static qreal oldPixelRatio = 0.0;
|
||||
if (devicePixelRatio() != oldPixelRatio) {
|
||||
oldPixelRatio = devicePixelRatio();
|
||||
resizeWindow(size());
|
||||
}
|
||||
QWindow::moveEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
void QTestWindow::renderQml() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
if (testQmlTexture > 0) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
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);
|
||||
}
|
||||
glBegin(GL_QUADS);
|
||||
{
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(-1, -1);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(-1, 1);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(1, 1);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(1, -1);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
const char * LOG_FILTER_RULES = R"V0G0N(
|
||||
*.debug=false
|
||||
qt.quick.mouse.debug=false
|
||||
)V0G0N";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QGuiApplication app(argc, argv);
|
||||
// QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
|
||||
QTestWindow window;
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
Loading…
Reference in a new issue