Pinned UI support

This commit is contained in:
Brad Davis 2016-05-31 20:23:58 -07:00
parent 068a462901
commit 8c13ff4ee1
50 changed files with 1003 additions and 1469 deletions

View file

@ -20,9 +20,10 @@ Window {
objectName: "AddressBarDialog"
frame: HiddenFrame {}
hideBackground: true
visible: false
destroyOnInvisible: false
shown: false
destroyOnHidden: false
resizable: false
scale: 1.25 // Make this dialog a little larger than normal
@ -145,14 +146,14 @@ Window {
if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text)
}
root.visible = false;
root.shown = false;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
root.visible = false
root.shown = false
event.accepted = true
break
case Qt.Key_Enter:

View file

@ -15,7 +15,7 @@ import Qt.labs.settings 1.0
import "styles-uit"
import "controls-uit" as HifiControls
import "windows-uit"
import "windows"
import "dialogs"
Window {
@ -23,7 +23,7 @@ Window {
objectName: "AssetServer"
title: "Asset Browser"
resizable: true
destroyOnInvisible: true
destroyOnHidden: true
implicitWidth: 384; implicitHeight: 640
minSize: Qt.vector2d(200, 300)

View file

@ -11,13 +11,13 @@ Window {
HifiConstants { id: hifi }
title: "Browser"
resizable: true
destroyOnInvisible: true
destroyOnHidden: true
width: 800
height: 600
property alias webView: webview
Component.onCompleted: {
visible = true
shown = true
addressBar.text = webview.url
}

View file

@ -12,7 +12,7 @@ import QtQuick 2.5
import Hifi 1.0 as Hifi
import "controls-uit"
import "windows-uit" as Windows
import "windows" as Windows
Windows.Window {
id: root

View file

@ -22,8 +22,9 @@ Window {
width: loginDialog.implicitWidth
// FIXME make movable
anchors.centerIn: parent
destroyOnInvisible: false
visible: false
destroyOnHidden: false
hideBackground: true
shown: false
LoginDialog {
id: loginDialog
@ -268,8 +269,8 @@ Window {
}
}
onVisibleChanged: {
if (!visible) {
onShownChanged: {
if (!shown) {
username.text = ""
password.text = ""
loginDialog.statusText = ""
@ -282,7 +283,7 @@ Window {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
root.visible = false;
root.shown = false;
event.accepted = true;
break;

View file

@ -13,7 +13,7 @@ import QtQuick.Controls 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import "windows-uit" as Windows
import "windows" as Windows
import "controls-uit" as Controls
import "styles-uit"
@ -22,7 +22,7 @@ Windows.Window {
HifiConstants { id: hifi }
title: "WebWindow"
resizable: true
visible: false
shown: false
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false
property alias source: webview.url

View file

@ -14,7 +14,7 @@ Windows.Window {
HifiConstants { id: hifi }
title: "QmlWindow"
resizable: true
visible: false
shown: false
focus: true
property var channel;
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer

View file

@ -15,7 +15,7 @@ import QtWebEngine 1.1
import QtWebChannel 1.0
import Qt.labs.settings 1.0
import "windows-uit"
import "windows"
import "controls-uit"
import "styles-uit"
@ -24,9 +24,9 @@ Window {
resizable: true
objectName: "ToolWindow"
destroyOnCloseButton: false
destroyOnInvisible: false
destroyOnHidden: false
closable: true
visible: false
shown: false
title: "Edit"
property alias tabView: tabView
implicitWidth: 520; implicitHeight: 695
@ -142,7 +142,7 @@ Window {
return;
}
}
visible = false;
shown = false;
}
function findIndexForUrl(source) {
@ -172,7 +172,7 @@ Window {
var tab = tabView.getTab(index);
if (newVisible) {
toolWindow.visible = true
toolWindow.shown = true
tab.enabled = true
} else {
tab.enabled = false;

View file

@ -15,7 +15,7 @@ import QtQuick.XmlListModel 2.0
import "../styles-uit"
import "../controls-uit" as HifiControls
import "../windows-uit"
import "../windows"
import "../hifi/models"
TableView {

View file

@ -52,6 +52,7 @@ FocusScope {
readonly property real menu: 8000
}
QtObject {
id: d
@ -93,6 +94,17 @@ FocusScope {
return item;
}
function findMatchingChildren(item, predicate) {
var results = [];
for (var i in item.children) {
var child = item.children[i];
if (predicate(child)) {
results.push(child);
}
}
return results;
}
function isTopLevelWindow(item) {
return item.topLevelWindow;
}
@ -106,19 +118,9 @@ FocusScope {
}
function getTopLevelWindows(predicate) {
var currentWindows = [];
if (!desktop) {
console.log("Could not find desktop for " + item)
return currentWindows;
}
for (var i = 0; i < desktop.children.length; ++i) {
var child = desktop.children[i];
if (isTopLevelWindow(child) && (!predicate || predicate(child))) {
currentWindows.push(child)
}
}
return currentWindows;
return findMatchingChildren(desktop, function(child) {
return (isTopLevelWindow(child) && (!predicate || predicate(child)));
});
}
function getDesktopWindow(item) {
@ -227,19 +229,9 @@ FocusScope {
}
function getRepositionChildren(predicate) {
var currentWindows = [];
if (!desktop) {
console.log("Could not find desktop");
return currentWindows;
}
for (var i = 0; i < desktop.children.length; ++i) {
var child = desktop.children[i];
if (child.shouldReposition === true && (!predicate || predicate(child))) {
currentWindows.push(child)
}
}
return currentWindows;
return findMatchingChildren(desktop, function(child) {
return (child.shouldReposition === true && (!predicate || predicate(child)));
});
}
function repositionAll() {
@ -265,6 +257,35 @@ FocusScope {
}
}
property bool pinned: false
property var hiddenChildren: []
function togglePinned() {
pinned = !pinned
}
onPinnedChanged: {
if (pinned) {
hiddenChildren = d.findMatchingChildren(desktop, function(child){
return !d.isTopLevelWindow(child) && child.visible;
});
hiddenChildren.forEach(function(child){
child.visible = false;
});
} else {
hiddenChildren.forEach(function(child){
if (child) {
child.visible = true;
}
});
hiddenChildren = [];
}
}
onShowDesktop: pinned = false
function raise(item) {
var targetWindow = d.getDesktopWindow(item);
if (!targetWindow) {
@ -422,7 +443,6 @@ FocusScope {
event.accepted = false;
}
function unfocusWindows() {
var windows = d.getTopLevelWindows();
for (var i = 0; i < windows.length; ++i) {

View file

@ -18,7 +18,7 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
import ".."
import "../controls-uit"
import "../styles-uit"
import "../windows-uit"
import "../windows"
import "fileDialog"
@ -724,7 +724,7 @@ ModalWindow {
Action {
id: cancelAction
text: "Cancel"
onTriggered: { canceled(); root.visible = false; }
onTriggered: { canceled(); root.shown = false; }
}
}

View file

@ -14,7 +14,7 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../controls-uit"
import "../styles-uit"
import "../windows-uit"
import "../windows"
import "messageDialog"
@ -24,7 +24,7 @@ ModalWindow {
implicitWidth: 640
implicitHeight: 320
destroyOnCloseButton: true
destroyOnInvisible: true
destroyOnHidden: true
visible: true
signal selected(int button);

View file

@ -13,14 +13,14 @@ import QtQuick.Controls 1.4
import "../controls-uit" as HifiControls
import "../styles-uit"
import "../windows-uit"
import "../windows"
import "preferences"
Window {
id: root
title: "Preferences"
resizable: true
destroyOnInvisible: true
destroyOnHidden: true
width: 500
height: 577
property var sections: []

View file

@ -14,7 +14,7 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../controls-uit"
import "../styles-uit"
import "../windows-uit"
import "../windows"
ModalWindow {
id: root

View file

@ -12,7 +12,7 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1
import "../../windows-uit" as Windows
import "../../windows" as Windows
import "../../controls-uit" as Controls
import "../../styles-uit"

View file

@ -6,7 +6,7 @@ import QtQuick.Controls.Styles 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows-uit"
import "../../windows"
import "attachments"
Window {
@ -16,7 +16,7 @@ Window {
width: 600
height: 600
resizable: true
destroyOnInvisible: true
destroyOnHidden: true
minSize: Qt.vector2d(400, 500)
HifiConstants { id: hifi }

View file

@ -9,7 +9,7 @@ import "../models"
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows-uit"
import "../../windows"
Window {
id: root

View file

@ -15,14 +15,14 @@ import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows-uit"
import "../../windows"
Window {
id: root
objectName: "RunningScripts"
title: "Running Scripts"
resizable: true
destroyOnInvisible: true
destroyOnHidden: true
implicitWidth: 400
implicitHeight: isHMD ? 695 : 728
minSize: Qt.vector2d(200, 300)
@ -34,6 +34,9 @@ Window {
property var runningScriptsModel: ListModel { }
property bool isHMD: false
onVisibleChanged: console.log("Running scripts visible changed to " + visible)
onShownChanged: console.log("Running scripts visible changed to " + visible)
Settings {
category: "Overlay.RunningScripts"
property alias x: root.x

View file

@ -8,7 +8,7 @@ import "."
import ".."
import "../../../styles-uit"
import "../../../controls-uit" as HifiControls
import "../../../windows-uit"
import "../../../windows"
Item {
height: column.height + 2 * 8

View file

@ -3,7 +3,7 @@ import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControls
import "../../../windows-uit"
import "../../../windows"
Item {
id: root

View file

@ -1,119 +0,0 @@
//
// DefaultFrame.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "."
import "../styles-uit"
Frame {
HifiConstants { id: hifi }
Rectangle {
// Dialog frame
id: frameContent
readonly property int iconSize: hifi.dimensions.frameIconSize
readonly property int frameMargin: 9
readonly property int frameMarginLeft: frameMargin
readonly property int frameMarginRight: frameMargin
readonly property int frameMarginTop: 2 * frameMargin + iconSize
readonly property int frameMarginBottom: iconSize + 11
anchors {
topMargin: -frameMarginTop
leftMargin: -frameMarginLeft
rightMargin: -frameMarginRight
bottomMargin: -frameMarginBottom
}
anchors.fill: parent
color: hifi.colors.baseGrayHighlight40
border {
width: hifi.dimensions.borderWidth
color: hifi.colors.faintGray50
}
radius: hifi.dimensions.borderRadius
// Enable dragging of the window
MouseArea {
anchors.fill: parent
drag.target: window
}
Row {
id: controlsRow
anchors {
right: parent.right;
top: parent.top;
topMargin: frameContent.frameMargin + 1 // Move down a little to visually align with the title
rightMargin: frameContent.frameMarginRight;
}
spacing: frameContent.iconSize / 4
HiFiGlyphs {
// "Pin" button
visible: false
text: (frame.pinned && !pinClickArea.containsMouse) || (!frame.pinned && pinClickArea.containsMouse) ? hifi.glyphs.pinInverted : hifi.glyphs.pin
color: pinClickArea.containsMouse && !pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
size: frameContent.iconSize
MouseArea {
id: pinClickArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onClicked: { frame.pin(); mouse.accepted = false; }
}
}
HiFiGlyphs {
// "Close" button
visible: window ? window.closable : false
text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
size: frameContent.iconSize
MouseArea {
id: closeClickArea
anchors.fill: parent
hoverEnabled: true
onClicked: window.visible = false;
}
}
}
RalewayRegular {
// Title
id: titleText
anchors {
left: parent.left
leftMargin: frameContent.frameMarginLeft + hifi.dimensions.contentMargin.x
right: controlsRow.left
rightMargin: frameContent.iconSize
top: parent.top
topMargin: frameContent.frameMargin
}
text: window ? window.title : ""
color: hifi.colors.white
size: hifi.fontSizes.overlayTitle
}
DropShadow {
source: titleText
anchors.fill: titleText
horizontalOffset: 2
verticalOffset: 2
samples: 2
color: hifi.colors.baseGrayShadow60
visible: (window && window.focus)
cached: true
}
}
}

View file

@ -1,60 +0,0 @@
//
// Fadable.qml
//
// Created by Bradley Austin Davis on 15 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "../styles-uit"
// Enable window visibility transitions
FocusScope {
id: root
HifiConstants { id: hifi }
Component.onCompleted: {
fadeTargetProperty = visible ? 1.0 : 0.0
}
// The target property to animate, usually scale or opacity
property alias fadeTargetProperty: root.opacity
// always start the property at 0 to enable fade in on creation
fadeTargetProperty: 0
// DO NOT set visible to false or when derived types override it it
// will short circuit the fade in on initial visibility
// visible: false <--- NO
// Some dialogs should be destroyed when they become
// invisible, so handle that
onVisibleChanged: {
// If someone directly set the visibility to false
// toggle it back on and use the targetVisible flag to transition
// via fading.
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
var target = visible;
visible = !visible;
fadeTargetProperty = target ? 1.0 : 0.0;
return;
}
}
// The actual animator
Behavior on fadeTargetProperty {
NumberAnimation {
duration: hifi.effects.fadeInDuration
easing.type: Easing.InOutCubic
}
}
// Once we're transparent, disable the dialog's visibility
onFadeTargetPropertyChanged: {
visible = (fadeTargetProperty != 0.0);
}
}

View file

@ -1,133 +0,0 @@
//
// Frame.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "../styles-uit"
import "../js/Utils.js" as Utils
Item {
id: frame
HifiConstants { id: hifi }
default property var decoration
property bool gradientsSupported: desktop.gradientsSupported
readonly property int frameMarginLeft: frameContent.frameMarginLeft
readonly property int frameMarginRight: frameContent.frameMarginRight
readonly property int frameMarginTop: frameContent.frameMarginTop
readonly property int frameMarginBottom: frameContent.frameMarginBottom
// Frames always fill their parents, but their decorations may extend
// beyond the window via negative margin sizes
anchors.fill: parent
children: [
focusShadow,
decoration,
sizeOutline,
debugZ,
sizeDrag
]
Text {
id: debugZ
visible: DebugQML
text: window ? "Z: " + window.z : ""
y: window ? window.height + 4 : 0
}
function deltaSize(dx, dy) {
var newSize = Qt.vector2d(window.width + dx, window.height + dy);
newSize = Utils.clampVector(newSize, window.minSize, window.maxSize);
window.width = newSize.x
window.height = newSize.y
}
RadialGradient {
id: focusShadow
width: 1.66 * window.width
height: 1.66 * window.height
x: (window.width - width) / 2
y: window.height / 2 - 0.375 * height
visible: gradientsSupported && window && window.focus && pane.visible
gradient: Gradient {
// GradientStop position 0.5 is at full circumference of circle that fits inside the square.
GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity
GradientStop { position: 0.333; color: "#1f000000" } // black, 12% opacity
GradientStop { position: 0.5; color: "#00000000" } // black, 0% opacity
GradientStop { position: 1.0; color: "#00000000" }
}
cached: true
}
Rectangle {
id: sizeOutline
x: -frameMarginLeft
y: -frameMarginTop
width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0
height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0
color: hifi.colors.baseGrayHighlight15
border.width: 3
border.color: hifi.colors.white50
radius: hifi.dimensions.borderRadius
visible: window ? !pane.visible : false
}
MouseArea {
// Resize handle
id: sizeDrag
width: hifi.dimensions.frameIconSize
height: hifi.dimensions.frameIconSize
enabled: window ? window.resizable : false
hoverEnabled: true
x: window ? window.width + frameMarginRight - hifi.dimensions.frameIconSize : 0
y: window ? window.height + 4 : 0
property vector2d pressOrigin
property vector2d sizeOrigin
property bool hid: false
onPressed: {
//console.log("Pressed on size")
pressOrigin = Qt.vector2d(mouseX, mouseY)
sizeOrigin = Qt.vector2d(window.content.width, window.content.height)
hid = false;
}
onReleased: {
if (hid) {
pane.visible = true
frameContent.visible = true
hid = false;
}
}
onPositionChanged: {
if (pressed) {
if (pane.visible) {
pane.visible = false;
frameContent.visible = false
hid = true;
}
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
frame.deltaSize(delta.x, delta.y)
}
}
HiFiGlyphs {
visible: sizeDrag.enabled
x: -11 // Move a little to visually align
y: window.modality == Qt.ApplicationModal ? -6 : -4
text: hifi.glyphs.resizeHandle
size: hifi.dimensions.frameIconSize + 10
color: sizeDrag.containsMouse || sizeDrag.pressed
? hifi.colors.white
: (window.colorScheme == hifi.colorSchemes.dark ? hifi.colors.white50 : hifi.colors.lightGrayText80)
}
}
}

View file

@ -1,98 +0,0 @@
//
// ModalFrame.qml
//
// Created by Bradley Austin Davis on 15 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "."
import "../controls-uit"
import "../styles-uit"
Frame {
HifiConstants { id: hifi }
Rectangle {
id: frameContent
readonly property bool hasTitle: window.title != ""
readonly property int frameMarginLeft: hifi.dimensions.modalDialogMargin.x
readonly property int frameMarginRight: hifi.dimensions.modalDialogMargin.x
readonly property int frameMarginTop: hifi.dimensions.modalDialogMargin.y + (frameContent.hasTitle ? hifi.dimensions.modalDialogTitleHeight + 10 : 0)
readonly property int frameMarginBottom: hifi.dimensions.modalDialogMargin.y
signal frameClicked();
anchors {
fill: parent
topMargin: -frameMarginTop
leftMargin: -frameMarginLeft
rightMargin: -frameMarginRight
bottomMargin: -frameMarginBottom
}
border {
width: hifi.dimensions.borderWidth
color: hifi.colors.lightGrayText80
}
radius: hifi.dimensions.borderRadius
color: hifi.colors.faintGray
// Enable dragging of the window
MouseArea {
anchors.fill: parent
drag.target: window
enabled: window.draggable
onClicked: window.frameClicked();
}
Item {
visible: frameContent.hasTitle
anchors.fill: parent
anchors {
topMargin: -parent.anchors.topMargin
leftMargin: -parent.anchors.leftMargin
rightMargin: -parent.anchors.rightMargin
}
Item {
width: title.width + (icon.text !== "" ? icon.width + hifi.dimensions.contentSpacing.x : 0)
x: (parent.width - width) / 2
onWidthChanged: window.titleWidth = width
HiFiGlyphs {
id: icon
text: window.iconText ? window.iconText : ""
size: window.iconSize ? window.iconSize : 30
color: hifi.colors.lightGray
visible: text != ""
anchors.verticalCenter: title.verticalCenter
anchors.left: parent.left
}
RalewayRegular {
id: title
text: window.title
elide: Text.ElideRight
color: hifi.colors.baseGrayHighlight
size: hifi.fontSizes.overlayTitle
y: -hifi.dimensions.modalDialogTitleHeight
anchors.right: parent.right
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: hifi.colors.lightGray
}
}
}
}

View file

@ -1,28 +0,0 @@
//
// ModalWindow.qml
//
// Created by Bradley Austin Davis on 22 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "."
Window {
id: window
modality: Qt.ApplicationModal
destroyOnCloseButton: true
destroyOnInvisible: true
frame: ModalFrame { }
property int colorScheme: hifi.colorSchemes.light
property bool draggable: false
signal frameClicked();
anchors.centerIn: draggable ? undefined : parent
}

View file

@ -1,343 +0,0 @@
//
// Window.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "."
import "../styles-uit"
// FIXME how do I set the initial position of a window without
// overriding places where the a individual client of the window
// might be setting the position with a Settings{} element?
// FIXME how to I enable dragging without allowing the window to lay outside
// of the desktop? How do I ensure when the desktop resizes all the windows
// are still at least partially visible?
Fadable {
id: window
HifiConstants { id: hifi }
// The Window size is the size of the content, while the frame
// decorations can extend outside it.
implicitHeight: content ? content.height : 0
implicitWidth: content ? content.width : 0
x: desktop.invalid_position; y: desktop.invalid_position;
enabled: visible
signal windowDestroyed();
property int modality: Qt.NonModal
readonly property bool topLevelWindow: true
property string title
// Should the window be closable control?
property bool closable: true
// Should the window try to remain on top of other windows?
property bool alwaysOnTop: false
// Should hitting the close button hide or destroy the window?
property bool destroyOnCloseButton: true
// Should hiding the window destroy it or just hide it?
property bool destroyOnInvisible: false
// FIXME support for pinned / unpinned pending full design
// property bool pinnable: false
// property bool pinned: false
property bool resizable: false
property bool gradientsSupported: desktop.gradientsSupported
property int colorScheme: hifi.colorSchemes.dark
property vector2d minSize: Qt.vector2d(100, 100)
property vector2d maxSize: Qt.vector2d(1280, 800)
// The content to place inside the window, determined by the client
default property var content
property var footer: Item { } // Optional static footer at the bottom of the dialog.
function setDefaultFocus() {} // Default function; can be overridden by dialogs.
property var rectifier: Timer {
property bool executing: false;
interval: 100
repeat: false
running: false
onTriggered: {
executing = true;
x = Math.floor(x);
y = Math.floor(y);
executing = false;
}
function begin() {
if (!executing) {
restart();
}
}
}
onXChanged: rectifier.begin();
onYChanged: rectifier.begin();
// This mouse area serves to raise the window. To function, it must live
// in the window and have a higher Z-order than the content, but follow
// the position and size of frame decoration
property var activator: MouseArea {
width: frame.decoration.width
height: frame.decoration.height
x: frame.decoration.anchors.leftMargin
y: frame.decoration.anchors.topMargin
propagateComposedEvents: true
acceptedButtons: Qt.AllButtons
enabled: window.visible
onPressed: {
//console.log("Pressed on activator area");
window.raise();
mouse.accepted = false;
}
}
// This mouse area serves to swallow mouse events while the mouse is over the window
// to prevent things like mouse wheel events from reaching the application and changing
// the camera if the user is scrolling through a list and gets to the end.
property var swallower: MouseArea {
width: frame.decoration.width
height: frame.decoration.height
x: frame.decoration.anchors.leftMargin
y: frame.decoration.anchors.topMargin
hoverEnabled: true
acceptedButtons: Qt.AllButtons
enabled: window.visible
onClicked: {}
onDoubleClicked: {}
onPressAndHold: {}
onReleased: {}
onWheel: {}
}
// Default to a standard frame. Can be overriden to provide custom
// frame styles, like a full desktop frame to simulate a modal window
property var frame: DefaultFrame { }
// Scrollable window content.
property var pane: Item {
property bool isScrolling: scrollView.height < scrollView.contentItem.height
property int contentWidth: scrollView.width - (isScrolling ? 10 : 0)
property int scrollHeight: scrollView.height
anchors.fill: parent
anchors.rightMargin: isScrolling ? 11 : 0
Rectangle {
id: contentBackground
anchors.fill: parent
anchors.rightMargin: parent.isScrolling ? 11 : 0
color: hifi.colors.baseGray
visible: modality != Qt.ApplicationModal
}
LinearGradient {
visible: gradientsSupported && modality != Qt.ApplicationModal
anchors.top: contentBackground.bottom
anchors.left: contentBackground.left
width: contentBackground.width - 1
height: 4
start: Qt.point(0, 0)
end: Qt.point(0, 4)
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.darkGray }
GradientStop { position: 1.0; color: hifi.colors.darkGray0 }
}
cached: true
}
ScrollView {
id: scrollView
contentItem: content
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
anchors.fill: parent
anchors.rightMargin: parent.isScrolling ? 1 : 0
anchors.bottomMargin: footer.height > 0 ? footerPane.height : 0
style: ScrollViewStyle {
padding.right: -7 // Move to right away from content.
handle: Item {
implicitWidth: 8
Rectangle {
radius: 4
color: hifi.colors.white30
anchors {
fill: parent
leftMargin: 2 // Finesse size and position.
topMargin: 1
bottomMargin: 1
}
}
}
scrollBarBackground: Item {
implicitWidth: 10
Rectangle {
color: hifi.colors.darkGray30
radius: 4
anchors {
fill: parent
topMargin: -1 // Finesse size
bottomMargin: -2
}
}
}
incrementControl: Item {
visible: false
}
decrementControl: Item {
visible: false
}
}
}
Rectangle {
// Optional non-scrolling footer.
id: footerPane
anchors {
left: parent.left
bottom: parent.bottom
}
width: parent.contentWidth
height: footer.height + 2 * hifi.dimensions.contentSpacing.y + 3
color: hifi.colors.baseGray
visible: footer.height > 0
Item {
// Horizontal rule.
anchors.fill: parent
Rectangle {
width: parent.width
height: 1
y: 1 // Stop displaying content just above horizontal rule/=.
color: hifi.colors.baseGrayShadow
}
Rectangle {
width: parent.width
height: 1
y: 2
color: hifi.colors.baseGrayHighlight
}
}
Item {
anchors.fill: parent
anchors.topMargin: 3 // Horizontal rule.
children: [ footer ]
}
}
}
children: [ swallower, frame, pane, activator ]
Component.onCompleted: {
window.parentChanged.connect(raise);
raise();
setDefaultFocus();
centerOrReposition();
}
Component.onDestruction: {
window.parentChanged.disconnect(raise); // Prevent warning on shutdown
windowDestroyed();
}
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
destroy();
return;
}
if (visible) {
raise();
}
enabled = visible
if (visible && parent) {
centerOrReposition();
}
}
function centerOrReposition() {
if (x == desktop.invalid_position && y == desktop.invalid_position) {
desktop.centerOnVisible(window);
} else {
desktop.repositionOnVisible(window);
}
}
function raise() {
if (visible && parent) {
desktop.raise(window)
}
}
function pin() {
// pinned = ! pinned
}
// our close function performs the same way as the OffscreenUI class:
// don't do anything but manipulate the targetVisible flag and let the other
// mechanisms decide if the window should be destroyed after the close
// animation completes
// FIXME using this close function messes up the visibility signals received by the
// type and it's derived types
// function close() {
// console.log("Closing " + window)
// if (destroyOnCloseButton) {
// destroyOnInvisible = true
// }
// visible = false;
// }
function framedRect() {
if (!frame || !frame.decoration) {
return Qt.rect(0, 0, window.width, window.height)
}
return Qt.rect(frame.decoration.anchors.leftMargin, frame.decoration.anchors.topMargin,
window.width - frame.decoration.anchors.leftMargin - frame.decoration.anchors.rightMargin,
window.height - frame.decoration.anchors.topMargin - frame.decoration.anchors.bottomMargin)
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Control:
case Qt.Key_Shift:
case Qt.Key_Meta:
case Qt.Key_Alt:
break;
case Qt.Key_W:
if (window.closable && (event.modifiers === Qt.ControlModifier)) {
visible = false
event.accepted = true
}
// fall through
default:
// Consume unmodified keyboard entries while the window is focused, to prevent them
// from propagating to the application
if (event.modifiers === Qt.NoModifier) {
event.accepted = true;
}
break;
}
}
}

View file

@ -1,20 +1,48 @@
//
// DefaultFrame.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "."
import "../controls"
import "../styles-uit"
Frame {
id: frame
property bool wideTopMargin: (window && (window.closable || window.title));
HifiConstants { id: hifi }
Rectangle {
anchors { margins: -iconSize; topMargin: -iconSize * (wideTopMargin ? 2 : 1); }
anchors.fill: parent;
color: "#7f7f7f7f";
radius: 3;
// Dialog frame
id: frameContent
// Allow dragging of the window
readonly property int iconSize: hifi.dimensions.frameIconSize
readonly property int frameMargin: 9
readonly property int frameMarginLeft: frameMargin
readonly property int frameMarginRight: frameMargin
readonly property int frameMarginTop: 2 * frameMargin + iconSize
readonly property int frameMarginBottom: iconSize + 11
anchors {
topMargin: -frameMarginTop
leftMargin: -frameMarginLeft
rightMargin: -frameMarginRight
bottomMargin: -frameMarginBottom
}
anchors.fill: parent
color: hifi.colors.baseGrayHighlight40
border {
width: hifi.dimensions.borderWidth
color: hifi.colors.faintGray50
}
radius: hifi.dimensions.borderRadius
// Enable dragging of the window
MouseArea {
anchors.fill: parent
drag.target: window
@ -22,48 +50,70 @@ Frame {
Row {
id: controlsRow
anchors { right: parent.right; top: parent.top; rightMargin: iconSize; topMargin: iconSize / 2; }
spacing: iconSize / 4
FontAwesome {
visible: false
text: "\uf08d"
style: Text.Outline; styleColor: "white"
size: frame.iconSize
rotation: !frame.parent ? 90 : frame.parent.pinned ? 0 : 90
color: frame.pinned ? "red" : "black"
anchors {
right: parent.right;
top: parent.top;
topMargin: frameContent.frameMargin + 1 // Move down a little to visually align with the title
rightMargin: frameContent.frameMarginRight;
}
spacing: frameContent.iconSize / 4
HiFiGlyphs {
// "Pin" button
visible: window.pinnable
text: window.pinned ? hifi.glyphs.pinInverted : hifi.glyphs.pin
color: pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
size: frameContent.iconSize
MouseArea {
id: pinClickArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onClicked: { frame.pin(); mouse.accepted = false; }
onClicked: window.pinned = !window.pinned;
}
}
FontAwesome {
HiFiGlyphs {
// "Close" button
visible: window ? window.closable : false
text: closeClickArea.containsMouse ? "\uf057" : "\uf05c"
style: Text.Outline;
styleColor: "white"
color: closeClickArea.containsMouse ? "red" : "black"
size: frame.iconSize
text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
size: frameContent.iconSize
MouseArea {
id: closeClickArea
anchors.fill: parent
hoverEnabled: true
onClicked: window.visible = false;
onClicked: window.shown = false;
}
}
}
Text {
RalewayRegular {
// Title
id: titleText
anchors { left: parent.left; leftMargin: iconSize; right: controlsRow.left; rightMargin: iconSize; top: parent.top; topMargin: iconSize / 2; }
anchors {
left: parent.left
leftMargin: frameContent.frameMarginLeft + hifi.dimensions.contentMargin.x
right: controlsRow.left
rightMargin: frameContent.iconSize
top: parent.top
topMargin: frameContent.frameMargin
}
text: window ? window.title : ""
elide: Text.ElideRight
font.bold: true
color: (window && window.focus) ? "white" : "gray"
style: Text.Outline;
styleColor: "black"
color: hifi.colors.white
size: hifi.fontSizes.overlayTitle
}
DropShadow {
source: titleText
anchors.fill: titleText
horizontalOffset: 2
verticalOffset: 2
samples: 2
color: hifi.colors.baseGrayShadow60
visible: (window && window.focus)
cached: true
}
}
}

View file

@ -1,8 +1,18 @@
//
// Fadable.qml
//
// Created by Bradley Austin Davis on 15 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "."
import "../styles"
import "../styles-uit"
// Enable window visibility transitions
FocusScope {
@ -13,6 +23,7 @@ FocusScope {
fadeTargetProperty = visible ? 1.0 : 0.0
}
property var completionCallback;
// The target property to animate, usually scale or opacity
property alias fadeTargetProperty: root.opacity
// always start the property at 0 to enable fade in on creation
@ -33,6 +44,13 @@ FocusScope {
fadeTargetProperty = target ? 1.0 : 0.0;
return;
}
// Now handle completions
if (completionCallback) {
completionCallback();
completionCallback = undefined;
}
}
// The actual animator
@ -43,8 +61,17 @@ FocusScope {
}
}
// Once we're transparent, disable the dialog's visibility
onFadeTargetPropertyChanged: {
visible = (fadeTargetProperty != 0.0);
}
function fadeIn(callback) {
completionCallback = callback;
fadeTargetProperty = 1.0;
}
function fadeOut(callback) {
completionCallback = callback;
fadeTargetProperty = 0.0;
}
}

View file

@ -1,24 +1,42 @@
import QtQuick 2.5
//
// Frame.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import "../controls"
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "../styles-uit"
import "../js/Utils.js" as Utils
Item {
id: frame
HifiConstants { id: hifi }
default property var decoration
property bool gradientsSupported: desktop.gradientsSupported
readonly property int frameMarginLeft: frameContent.frameMarginLeft
readonly property int frameMarginRight: frameContent.frameMarginRight
readonly property int frameMarginTop: frameContent.frameMarginTop
readonly property int frameMarginBottom: frameContent.frameMarginBottom
// Frames always fill their parents, but their decorations may extend
// beyond the window via negative margin sizes
anchors.fill: parent
// Convenience accessor for the window
property alias window: frame.parent
readonly property int iconSize: 24
default property var decoration;
children: [
focusShadow,
decoration,
sizeOutline,
debugZ,
sizeDrag,
sizeDrag
]
Text {
@ -35,57 +53,81 @@ Item {
window.height = newSize.y
}
RadialGradient {
id: focusShadow
width: 1.66 * window.width
height: 1.66 * window.height
x: (window.width - width) / 2
y: window.height / 2 - 0.375 * height
visible: gradientsSupported && window && window.focus && pane.visible
gradient: Gradient {
// GradientStop position 0.5 is at full circumference of circle that fits inside the square.
GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity
GradientStop { position: 0.333; color: "#1f000000" } // black, 12% opacity
GradientStop { position: 0.5; color: "#00000000" } // black, 0% opacity
GradientStop { position: 1.0; color: "#00000000" }
}
cached: true
}
Rectangle {
id: sizeOutline
width: window ? window.width : 0
height: window ? window.height : 0
color: "#00000000"
border.width: 4
radius: 10
visible: window ? !window.content.visible : false
x: -frameMarginLeft
y: -frameMarginTop
width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0
height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0
color: hifi.colors.baseGrayHighlight15
border.width: 3
border.color: hifi.colors.white50
radius: hifi.dimensions.borderRadius
visible: window ? !pane.visible : false
}
MouseArea {
// Resize handle
id: sizeDrag
width: iconSize
height: iconSize
width: hifi.dimensions.frameIconSize
height: hifi.dimensions.frameIconSize
enabled: window ? window.resizable : false
x: window ? window.width : 0
y: window ? window.height : 0
hoverEnabled: true
x: window ? window.width + frameMarginRight - hifi.dimensions.frameIconSize : 0
y: window ? window.height + 4 : 0
property vector2d pressOrigin
property vector2d sizeOrigin
property bool hid: false
onPressed: {
console.log("Pressed on size")
//console.log("Pressed on size")
pressOrigin = Qt.vector2d(mouseX, mouseY)
sizeOrigin = Qt.vector2d(window.content.width, window.content.height)
hid = false;
}
onReleased: {
if (hid) {
window.content.visible = true
pane.visible = true
frameContent.visible = true
hid = false;
}
}
onPositionChanged: {
if (pressed) {
if (window.content.visible) {
window.content.visible = false;
if (pane.visible) {
pane.visible = false;
frameContent.visible = false
hid = true;
}
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
frame.deltaSize(delta.x, delta.y)
}
}
FontAwesome {
HiFiGlyphs {
visible: sizeDrag.enabled
rotation: -45
anchors { centerIn: parent }
horizontalAlignment: Text.AlignHCenter
text: "\uf07d"
size: iconSize / 3 * 2
style: Text.Outline; styleColor: "white"
x: -11 // Move a little to visually align
y: window.modality == Qt.ApplicationModal ? -6 : -4
text: hifi.glyphs.resizeHandle
size: hifi.dimensions.frameIconSize + 10
color: sizeDrag.containsMouse || sizeDrag.pressed
? hifi.colors.white
: (window.colorScheme == hifi.colorSchemes.dark ? hifi.colors.white50 : hifi.colors.lightGrayText80)
}
}
}

View file

@ -2,7 +2,7 @@ import QtQuick 2.5
import "."
Frame {
Item {
id: frame
Item { anchors.fill: parent }

View file

@ -1,36 +1,98 @@
//
// ModalFrame.qml
//
// Created by Bradley Austin Davis on 15 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "."
import "../controls"
import "../controls-uit"
import "../styles-uit"
Frame {
id: frame
HifiConstants { id: hifi }
Item {
anchors.fill: parent
Rectangle {
id: frameContent
Rectangle {
id: background
anchors.fill: parent
anchors.margins: -4096
visible: window.visible
color: "#7f7f7f7f";
radius: 3;
readonly property bool hasTitle: window.title != ""
readonly property int frameMarginLeft: hifi.dimensions.modalDialogMargin.x
readonly property int frameMarginRight: hifi.dimensions.modalDialogMargin.x
readonly property int frameMarginTop: hifi.dimensions.modalDialogMargin.y + (frameContent.hasTitle ? hifi.dimensions.modalDialogTitleHeight + 10 : 0)
readonly property int frameMarginBottom: hifi.dimensions.modalDialogMargin.y
signal frameClicked();
anchors {
fill: parent
topMargin: -frameMarginTop
leftMargin: -frameMarginLeft
rightMargin: -frameMarginRight
bottomMargin: -frameMarginBottom
}
Text {
y: -implicitHeight - iconSize / 2
text: window.title
elide: Text.ElideRight
font.bold: true
color: window.focus ? "white" : "gray"
style: Text.Outline;
styleColor: "black"
border {
width: hifi.dimensions.borderWidth
color: hifi.colors.lightGrayText80
}
radius: hifi.dimensions.borderRadius
color: hifi.colors.faintGray
// Enable dragging of the window
MouseArea {
anchors.fill: parent
drag.target: window
enabled: window.draggable
onClicked: window.frameClicked();
}
Item {
visible: frameContent.hasTitle
anchors.fill: parent
anchors {
topMargin: -parent.anchors.topMargin
leftMargin: -parent.anchors.leftMargin
rightMargin: -parent.anchors.rightMargin
}
Item {
width: title.width + (icon.text !== "" ? icon.width + hifi.dimensions.contentSpacing.x : 0)
x: (parent.width - width) / 2
onWidthChanged: window.titleWidth = width
HiFiGlyphs {
id: icon
text: window.iconText ? window.iconText : ""
size: window.iconSize ? window.iconSize : 30
color: hifi.colors.lightGray
visible: text != ""
anchors.verticalCenter: title.verticalCenter
anchors.left: parent.left
}
RalewayRegular {
id: title
text: window.title
elide: Text.ElideRight
color: hifi.colors.baseGrayHighlight
size: hifi.fontSizes.overlayTitle
y: -hifi.dimensions.modalDialogTitleHeight
anchors.right: parent.right
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: hifi.colors.lightGray
}
}
}
}

View file

@ -1,14 +1,28 @@
//
// ModalWindow.qml
//
// Created by Bradley Austin Davis on 22 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "."
Window {
id: root
anchors.centerIn: parent
id: window
modality: Qt.ApplicationModal
destroyOnCloseButton: true
destroyOnInvisible: true
frame: ModalFrame{}
frame: ModalFrame { }
property int colorScheme: hifi.colorSchemes.light
property bool draggable: false
signal frameClicked();
anchors.centerIn: draggable ? undefined : parent
}

View file

@ -1,9 +1,20 @@
//
// Window.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "."
import "../styles"
import "../styles-uit"
// FIXME how do I set the initial position of a window without
// overriding places where the a individual client of the window
@ -15,16 +26,36 @@ import "../styles"
Fadable {
id: window
HifiConstants { id: hifi }
//
// Signals
//
signal windowDestroyed();
//
// Native properties
//
// The Window size is the size of the content, while the frame
// decorations can extend outside it.
implicitHeight: content ? content.height : 0
implicitWidth: content ? content.width : 0
x: desktop.invalid_position; y: desktop.invalid_position;
enabled: visible
children: [ swallower, frame, pane, activator ]
signal windowDestroyed();
//
// Custom properties
//
property int modality: Qt.NonModal
// Corresponds to the window shown / hidden state AS DISTINCT from window visibility.
// Window visibility should NOT be used as a proxy for any other behavior.
property bool shown: true
// FIXME workaround to deal with the face that some visual items are defined here,
// when they should be moved to a frame derived type
property bool hideBackground: false
visible: shown
enabled: visible
readonly property bool topLevelWindow: true
property string title
// Should the window be closable control?
@ -34,17 +65,23 @@ Fadable {
// Should hitting the close button hide or destroy the window?
property bool destroyOnCloseButton: true
// Should hiding the window destroy it or just hide it?
property bool destroyOnInvisible: false
// FIXME support for pinned / unpinned pending full design
// property bool pinnable: false
// property bool pinned: false
property bool destroyOnHidden: false
property bool pinnable: true
property bool pinned: false
property bool resizable: false
property bool gradientsSupported: desktop.gradientsSupported
property int colorScheme: hifi.colorSchemes.dark
property vector2d minSize: Qt.vector2d(100, 100)
property vector2d maxSize: Qt.vector2d(1280, 720)
property vector2d maxSize: Qt.vector2d(1280, 800)
// The content to place inside the window, determined by the client
default property var content
property var footer: Item { } // Optional static footer at the bottom of the dialog.
function setDefaultFocus() {} // Default function; can be overridden by dialogs.
property var rectifier: Timer {
property bool executing: false;
interval: 100
@ -65,20 +102,15 @@ Fadable {
}
}
onXChanged: rectifier.begin();
onYChanged: rectifier.begin();
// This mouse area serves to raise the window. To function, it must live
// in the window and have a higher Z-order than the content, but follow
// the position and size of frame decoration
property var activator: MouseArea {
width: frame.decoration.width
height: frame.decoration.height
x: frame.decoration.anchors.margins
x: frame.decoration.anchors.leftMargin
y: frame.decoration.anchors.topMargin
propagateComposedEvents: true
hoverEnabled: true
acceptedButtons: Qt.AllButtons
enabled: window.visible
onPressed: {
@ -94,7 +126,7 @@ Fadable {
property var swallower: MouseArea {
width: frame.decoration.width
height: frame.decoration.height
x: frame.decoration.anchors.margins
x: frame.decoration.anchors.leftMargin
y: frame.decoration.anchors.topMargin
hoverEnabled: true
acceptedButtons: Qt.AllButtons
@ -106,71 +138,241 @@ Fadable {
onWheel: {}
}
// Default to a standard frame. Can be overriden to provide custom
// frame styles, like a full desktop frame to simulate a modal window
property var frame: DefaultFrame { }
// Scrollable window content.
// FIXME this should not define any visual content in this type. The base window
// type should only consist of logic sized areas, with nothing drawn (although the
// default value for the frame property does include visual decorations)
property var pane: Item {
property bool isScrolling: scrollView.height < scrollView.contentItem.height
property int contentWidth: scrollView.width - (isScrolling ? 10 : 0)
property int scrollHeight: scrollView.height
children: [ swallower, frame, content, activator ]
anchors.fill: parent
anchors.rightMargin: isScrolling ? 11 : 0
Rectangle {
id: contentBackground
anchors.fill: parent
anchors.rightMargin: parent.isScrolling ? 11 : 0
color: hifi.colors.baseGray
visible: !window.hideBackground && modality != Qt.ApplicationModal
}
LinearGradient {
visible: !window.hideBackground && gradientsSupported && modality != Qt.ApplicationModal
anchors.top: contentBackground.bottom
anchors.left: contentBackground.left
width: contentBackground.width - 1
height: 4
start: Qt.point(0, 0)
end: Qt.point(0, 4)
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.darkGray }
GradientStop { position: 1.0; color: hifi.colors.darkGray0 }
}
cached: true
}
ScrollView {
id: scrollView
contentItem: content
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
anchors.fill: parent
anchors.rightMargin: parent.isScrolling ? 1 : 0
anchors.bottomMargin: footer.height > 0 ? footerPane.height : 0
style: ScrollViewStyle {
padding.right: -7 // Move to right away from content.
handle: Item {
implicitWidth: 8
Rectangle {
radius: 4
color: hifi.colors.white30
anchors {
fill: parent
leftMargin: 2 // Finesse size and position.
topMargin: 1
bottomMargin: 1
}
}
}
scrollBarBackground: Item {
implicitWidth: 10
Rectangle {
color: hifi.colors.darkGray30
radius: 4
anchors {
fill: parent
topMargin: -1 // Finesse size
bottomMargin: -2
}
}
}
incrementControl: Item {
visible: false
}
decrementControl: Item {
visible: false
}
}
}
Rectangle {
// Optional non-scrolling footer.
id: footerPane
anchors {
left: parent.left
bottom: parent.bottom
}
width: parent.contentWidth
height: footer.height + 2 * hifi.dimensions.contentSpacing.y + 3
color: hifi.colors.baseGray
visible: footer.height > 0
Item {
// Horizontal rule.
anchors.fill: parent
Rectangle {
width: parent.width
height: 1
y: 1 // Stop displaying content just above horizontal rule/=.
color: hifi.colors.baseGrayShadow
}
Rectangle {
width: parent.width
height: 1
y: 2
color: hifi.colors.baseGrayHighlight
}
}
Item {
anchors.fill: parent
anchors.topMargin: 3 // Horizontal rule.
children: [ footer ]
}
}
}
//
// Handlers
//
Component.onCompleted: {
window.parentChanged.connect(raise);
raise();
centerOrReposition();
setDefaultFocus();
d.centerOrReposition();
d.updateVisibility(shown);
}
Component.onDestruction: {
window.parentChanged.disconnect(raise); // Prevent warning on shutdown
windowDestroyed();
}
function centerOrReposition() {
if (x == desktop.invalid_position && y == desktop.invalid_position) {
desktop.centerOnVisible(window);
} else {
desktop.repositionOnVisible(window);
}
}
onXChanged: rectifier.begin();
onYChanged: rectifier.begin();
onShownChanged: d.updateVisibility(shown)
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
destroy();
return;
}
if (visible) {
raise();
}
enabled = visible
if (visible && parent) {
centerOrReposition();
d.centerOrReposition();
}
}
QtObject {
id: d
readonly property alias pinned: window.pinned
readonly property alias shown: window.shown
readonly property alias modality: window.modality;
function getTargetVisibility() {
if (!window.shown) {
return false;
}
if (modality !== Qt.NonModal) {
return true;
}
if (pinned) {
return true;
}
if (desktop && !desktop.pinned) {
return true;
}
return false;
}
// The force flag causes all windows to fade back in, because a window was shown
readonly property alias visible: window.visible
function updateVisibility(force) {
if (force && !pinned && desktop.pinned) {
// Change the pinned state (which in turn will call us again)
desktop.pinned = false;
return;
}
var targetVisibility = getTargetVisibility();
if (targetVisibility === visible) {
return;
}
if (targetVisibility) {
fadeIn(function() {
if (force) {
window.raise();
}
});
} else {
fadeOut(function() {
if (!window.shown && window.destroyOnHidden) {
window.destroy();
}
});
}
}
function centerOrReposition() {
if (x == desktop.invalid_position && y == desktop.invalid_position) {
desktop.centerOnVisible(window);
} else {
desktop.repositionOnVisible(window);
}
}
}
// When the desktop pinned state changes, automatically handle the current windows
Connections { target: desktop; onPinnedChanged: d.updateVisibility() }
function raise() {
if (visible && parent) {
desktop.raise(window)
}
}
function pin() {
// pinned = ! pinned
function setPinned() {
pinned = !pinned
}
// our close function performs the same way as the OffscreenUI class:
// don't do anything but manipulate the targetVisible flag and let the other
// mechanisms decide if the window should be destroyed after the close
// animation completes
// FIXME using this close function messes up the visibility signals received by the
// type and it's derived types
// function close() {
// console.log("Closing " + window)
// if (destroyOnCloseButton) {
// destroyOnInvisible = true
// }
// visible = false;
// }
function framedRect() {
if (!frame || !frame.decoration) {
return Qt.rect(0, 0, window.width, window.height)
@ -180,7 +382,6 @@ Fadable {
window.height - frame.decoration.anchors.topMargin - frame.decoration.anchors.bottomMargin)
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Control:
@ -189,10 +390,9 @@ Fadable {
case Qt.Key_Alt:
break;
case Qt.Key_W:
if (window.closable && (event.modifiers === Qt.ControlModifier)) {
visible = false
shown = false
event.accepted = true
}
// fall through

View file

@ -2057,7 +2057,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_X:
if (isShifted && isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getRootContext()->engine()->clearComponentCache();
offscreenUi->togglePinned();
//offscreenUi->getRootContext()->engine()->clearComponentCache();
//OffscreenUi::information("Debugging", "Component cache cleared");
// placeholder for dialogs being converted to QML.
}

View file

@ -96,7 +96,7 @@ void OverlayConductor::updateMode() {
myAvatar->reset(true, false, false);
}
if (_wantsOverlays) {
setEnabled(!nowDriving, false);
setEnabled(!nowDriving);
}
_driving = nowDriving;
} // Else haven't accumulated enough time in new mode, but keep timing.
@ -114,18 +114,14 @@ void OverlayConductor::updateMode() {
case SITTING: {
// enter the SITTING state
// place the overlay at origin
Transform identity;
qApp->getApplicationCompositor().setModelTransform(identity);
qApp->getApplicationCompositor().setModelTransform(Transform());
break;
}
case STANDING: { // STANDING mode is not currently used.
// enter the STANDING state
// place the overlay at the current hmd position in world space
auto camMat = cancelOutRollAndPitch(myAvatar->getSensorToWorldMatrix() * qApp->getHMDSensorPose());
Transform t;
t.setTranslation(extractTranslation(camMat));
t.setRotation(glm::quat_cast(camMat));
qApp->getApplicationCompositor().setModelTransform(t);
qApp->getApplicationCompositor().setModelTransform(Transform(camMat));
break;
}
@ -139,54 +135,21 @@ void OverlayConductor::updateMode() {
}
void OverlayConductor::setEnabled(bool enabled, bool toggleQmlEvents) {
void OverlayConductor::setEnabled(bool enabled) {
if (enabled == _enabled) {
return;
}
if (toggleQmlEvents) { // Could recurse on us with the wrong toggleQmlEvents flag, and not need in the !toggleQmlEvent case anyway.
Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, enabled);
}
_enabled = enabled; // set the new value
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->setPinned(!_enabled);
// if the new state is visible/enabled...
if (_enabled) {
// alpha fadeIn the overlay mesh.
qApp->getApplicationCompositor().fadeIn();
// enable mouse clicks from script
qApp->getOverlays().enable();
// enable QML events
if (toggleQmlEvents) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getRootItem()->setEnabled(true);
}
if (_mode == STANDING) {
// place the overlay at the current hmd position in world space
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto camMat = cancelOutRollAndPitch(myAvatar->getSensorToWorldMatrix() * qApp->getHMDSensorPose());
Transform t;
t.setTranslation(extractTranslation(camMat));
t.setRotation(glm::quat_cast(camMat));
qApp->getApplicationCompositor().setModelTransform(t);
}
} else { // other wise, if the new state is hidden/not enabled
// alpha fadeOut the overlay mesh.
qApp->getApplicationCompositor().fadeOut();
// disable mouse clicks from script
qApp->getOverlays().disable();
// disable QML events
if (toggleQmlEvents) { // I'd really rather always do this, but it looses drive state. bugzid:501
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getRootItem()->setEnabled(false);
}
}
if (_enabled && _mode == STANDING) {
// place the overlay at the current hmd position in world space
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto camMat = cancelOutRollAndPitch(myAvatar->getSensorToWorldMatrix() * qApp->getHMDSensorPose());
qApp->getApplicationCompositor().setModelTransform(Transform(camMat));
}
}
bool OverlayConductor::getEnabled() const {

View file

@ -17,7 +17,7 @@ public:
~OverlayConductor();
void update(float dt);
void setEnabled(bool enable, bool toggleQmlEvents = true);
void setEnabled(bool enable);
bool getEnabled() const;
private:

View file

@ -346,7 +346,7 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
float uiRadius = _hmdUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
float instersectionDistance;
if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){
@ -407,60 +407,6 @@ void CompositorHelper::updateTooltips() {
//}
}
static const float FADE_DURATION = 500.0f;
static const float FADE_IN_ALPHA = 1.0f;
static const float FADE_OUT_ALPHA = 0.0f;
void CompositorHelper::startFadeFailsafe(float endValue) {
_fadeStarted = usecTimestampNow();
_fadeFailsafeEndValue = endValue;
const int SLIGHT_DELAY = 10;
QTimer::singleShot(FADE_DURATION + SLIGHT_DELAY, [this]{
checkFadeFailsafe();
});
}
void CompositorHelper::checkFadeFailsafe() {
auto elapsedInFade = usecTimestampNow() - _fadeStarted;
if (elapsedInFade > FADE_DURATION) {
setAlpha(_fadeFailsafeEndValue);
}
}
void CompositorHelper::fadeIn() {
_fadeInAlpha = true;
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
_alphaPropertyAnimation->setEndValue(FADE_IN_ALPHA);
_alphaPropertyAnimation->start();
// Sometimes, this "QPropertyAnimation" fails to complete the animation, and we end up with a partially faded
// state. So we will also have this fail-safe, where we record the timestamp of the fadeRequest, and the target
// value of the fade, and if after that time we still haven't faded all the way, we will kick it to the final
// fade value
startFadeFailsafe(FADE_IN_ALPHA);
}
void CompositorHelper::fadeOut() {
_fadeInAlpha = false;
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
_alphaPropertyAnimation->setEndValue(FADE_OUT_ALPHA);
_alphaPropertyAnimation->start();
startFadeFailsafe(FADE_OUT_ALPHA);
}
void CompositorHelper::toggle() {
if (_fadeInAlpha) {
fadeOut();
} else {
fadeIn();
}
}
glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::vec3& headPosition) const {
glm::mat4 result;
if (isHMD()) {

View file

@ -38,7 +38,6 @@ const float MAGNIFY_MULT = 2.0f;
class CompositorHelper : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha)
Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
public:
static const uvec2 VIRTUAL_SCREEN_SIZE;
@ -75,13 +74,6 @@ public:
void setModelTransform(const Transform& transform) { _modelTransform = transform; }
const Transform& getModelTransform() const { return _modelTransform; }
void fadeIn();
void fadeOut();
void toggle();
float getAlpha() const { return _alpha; }
void setAlpha(float alpha) { _alpha = alpha; }
bool getReticleVisible() const { return _reticleVisible; }
void setReticleVisible(bool visible) { _reticleVisible = visible; }
@ -113,7 +105,7 @@ public:
void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; _currentFrame = frame; }
void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; }
signals:
void allowMouseCaptureChanged();
@ -127,7 +119,6 @@ private:
DisplayPluginPointer _currentDisplayPlugin;
glm::mat4 _currentCamera;
uint32_t _currentFrame { 0 };
QWidget* _renderingWidget{ nullptr };
//// Support for hovering and tooltips
@ -143,17 +134,7 @@ private:
float _textureFov { VIRTUAL_UI_TARGET_FOV.y };
float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
float _alpha { 1.0f };
float _prevAlpha { 1.0f };
float _fadeInAlpha { true };
float _oculusUIRadius { 1.0f };
quint64 _fadeStarted { 0 };
float _fadeFailsafeEndValue { 1.0f };
void checkFadeFailsafe();
void startFadeFailsafe(float endValue);
int _reticleQuad;
float _hmdUIRadius { 1.0f };
int _previousBorderWidth { -1 };
int _previousBorderHeight { -1 };

View file

@ -445,25 +445,18 @@ void OpenGLDisplayPlugin::compositeOverlay() {
useProgram(_program);
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
// Overlay draw
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
// Overlay draw
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
}
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositePointer() {
@ -471,24 +464,16 @@ void OpenGLDisplayPlugin::compositePointer() {
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
}
});
} else {
drawUnitQuad();
}
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositeScene() {

View file

@ -254,23 +254,15 @@ void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
// check the alpha
useProgram(_program);
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye));
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye));
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
}
void HmdDisplayPlugin::compositePointer() {
@ -280,25 +272,19 @@ void HmdDisplayPlugin::compositePointer() {
// check the alpha
useProgram(_program);
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]);
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]);
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
}
void HmdDisplayPlugin::internalPresent() {

View file

@ -22,10 +22,6 @@ QString ErrorDialog::text() const {
return _text;
}
void ErrorDialog::setVisible(bool v) {
OffscreenQmlDialog::setVisible(v);
}
void ErrorDialog::setText(const QString& arg) {
if (arg != _text) {
_text = arg;

View file

@ -30,7 +30,6 @@ public:
QString text() const;
public slots:
virtual void setVisible(bool v);
void setText(const QString& arg);
signals:

View file

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

View file

@ -121,32 +121,28 @@ void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(
load(url, f);
item = getRootItem()->findChild<QQuickItem*>(name);
}
if (item) {
item->setVisible(true);
QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(true);
}
}
void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
// Already loaded?
if (item) {
emit showDesktop();
item->setVisible(!item->isVisible());
if (!item) {
show(url, name, f);
return;
}
load(url, f);
item = getRootItem()->findChild<QQuickItem*>(name);
if (item && !item->isVisible()) {
emit showDesktop();
item->setVisible(true);
}
// Already loaded, so just flip the bit
QQmlProperty shownProperty(item, OFFSCREEN_VISIBILITY_PROPERTY);
shownProperty.write(!shownProperty.read().toBool());
}
void OffscreenUi::hide(const QString& name) {
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
if (item) {
item->setVisible(false);
QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(false);
}
}
@ -345,6 +341,20 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q
return waitForInputDialogResult(createInputDialog(icon, title, label, current));
}
void OffscreenUi::togglePinned() {
bool invokeResult = QMetaObject::invokeMethod(_desktop, "togglePinned");
if (!invokeResult) {
qWarning() << "Failed to toggle window visibility";
}
}
void OffscreenUi::setPinned(bool pinned) {
bool invokeResult = QMetaObject::invokeMethod(_desktop, "setPinned", Q_ARG(QVariant, pinned));
if (!invokeResult) {
qWarning() << "Failed to set window visibility";
}
}
void OffscreenUi::addMenuInitializer(std::function<void(VrMenu*)> f) {
if (!_vrMenu) {
_queuedMenuInitializers.push_back(f);

View file

@ -28,6 +28,8 @@
class VrMenu;
#define OFFSCREEN_VISIBILITY_PROPERTY "shown"
class OffscreenUi : public OffscreenQmlSurface, public Dependency {
Q_OBJECT
@ -44,6 +46,13 @@ public:
void setNavigationFocused(bool focused);
void unfocusWindows();
void toggleMenu(const QPoint& screenCoordinates);
// Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag
void setPinned(bool pinned = true);
void togglePinned();
bool eventFilter(QObject* originalDestination, QEvent* event) override;
void addMenuInitializer(std::function<void(VrMenu*)> f);

View file

@ -163,8 +163,7 @@ void QmlWindowClass::setVisible(bool visible) {
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
} else {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setVisible(visible);
//emit visibilityChanged(visible);
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
});
}
}

View file

@ -47,10 +47,6 @@ void Tooltip::setImageURL(const QString& imageURL) {
}
}
void Tooltip::setVisible(bool visible) {
QQuickItem::setVisible(visible);
}
QString Tooltip::showTip(const QString& title, const QString& description) {
const QString newTipId = QUuid().createUuid().toString();

View file

@ -39,8 +39,6 @@ public:
static void closeTip(const QString& tipId);
public slots:
virtual void setVisible(bool v);
void setTitle(const QString& title);
void setDescription(const QString& description);
void setImageURL(const QString& imageURL);

View file

@ -5,7 +5,7 @@ import Qt.labs.settings 1.0
import "../../../interface/resources/qml"
//import "../../../interface/resources/qml/windows"
import "../../../interface/resources/qml/windows-uit"
import "../../../interface/resources/qml/windows"
import "../../../interface/resources/qml/dialogs"
import "../../../interface/resources/qml/hifi"
import "../../../interface/resources/qml/hifi/dialogs"
@ -17,6 +17,267 @@ ApplicationWindow {
width: 1280
height: 800
title: qsTr("Scratch App")
toolBar: Row {
id: testButtons
anchors { margins: 8; left: parent.left; top: parent.top }
spacing: 8
property int count: 0
property var tabs: [];
property var urls: [];
// Window visibility
Button {
text: "restore all"
onClicked: {
for (var i = 0; i < desktop.windows.length; ++i) {
desktop.windows[i].shown = true
}
}
}
Button {
text: "toggle blue visible"
onClicked: {
blue.shown = !blue.shown
}
}
Button {
text: "toggle blue enabled"
onClicked: {
blue.enabled = !blue.enabled
}
}
Button {
text: "toggle desktop"
onClicked: desktop.toggleVisible()
}
// Error alerts
/*
Button {
// Message without title.
text: "Show Error"
onClicked: {
var messageBox = desktop.messageBox({
text: "Diagnostic cycle will be complete in 30 seconds",
icon: hifi.icons.critical,
});
messageBox.selected.connect(function(button) {
console.log("You clicked " + button)
})
}
}
Button {
// detailedText is not currently used anywhere in Interface but it is easier to leave in and style good enough.
text: "Show Long Error"
onClicked: {
desktop.messageBox({
informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ",
text: "Baloney",
icon: hifi.icons.warning,
detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a"
});
}
}
*/
// query
/*
// There is no such desktop.queryBox() function; may need to update test to cover QueryDialog.qml?
Button {
text: "Show Query"
onClicked: {
var queryBox = desktop.queryBox({
text: "Have you stopped beating your wife?",
placeholderText: "Are you sure?",
// icon: hifi.icons.critical,
});
queryBox.selected.connect(function(result) {
console.log("User responded with " + result);
});
queryBox.canceled.connect(function() {
console.log("User cancelled query box ");
})
}
}
*/
// file dialog
Button {
text: "Open Directory"
property var builder: Component {
FileDialog { selectDirectory: true }
}
onClicked: {
var fileDialog = builder.createObject(desktop);
fileDialog.canceled.connect(function(){
console.log("Cancelled")
})
fileDialog.selectedFile.connect(function(file){
console.log("Selected " + file)
})
}
}
Button {
text: "Open File"
property var builder: Component {
FileDialog {
title: "Open File"
filter: "All Files (*.*)"
//filter: "HTML files (*.html);;Other(*.png)"
}
}
onClicked: {
var fileDialog = builder.createObject(desktop);
fileDialog.canceled.connect(function(){
console.log("Cancelled")
})
fileDialog.selectedFile.connect(function(file){
console.log("Selected " + file)
})
}
}
/*
*/
// tabs
/*
Button {
text: "Add Tab"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo" });
desktop.toolWindow.showTabForUrl("Foo", true);
}
}
Button {
text: "Add Tab 2"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 2" });
desktop.toolWindow.showTabForUrl("Foo 2", true);
}
}
Button {
text: "Add Tab 3"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 3" });
desktop.toolWindow.showTabForUrl("Foo 3", true);
}
}
Button {
text: "Destroy Tab"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.removeTabForUrl("Foo");
}
}
*/
// Hifi specific stuff
/*
Button {
// Shows the dialog with preferences sections but not each section's preference items
// because Preferences.preferencesByCategory() method is not stubbed out.
text: "Settings > General..."
property var builder: Component {
GeneralPreferencesDialog { }
}
onClicked: {
var runningScripts = builder.createObject(desktop);
}
}
Button {
text: "Running Scripts"
property var builder: Component {
RunningScripts { }
}
onClicked: {
var runningScripts = builder.createObject(desktop);
}
}
Button {
text: "Attachments"
property var builder: Component {
AttachmentsDialog { }
}
onClicked: {
var attachmentsDialog = builder.createObject(desktop);
}
}
Button {
// Replicates message box that pops up after selecting new avatar. Includes title.
text: "Confirm Avatar"
onClicked: {
var messageBox = desktop.messageBox({
title: "Set Avatar",
text: "Would you like to use 'Albert' for your avatar?",
icon: hifi.icons.question, // Test question icon
//icon: hifi.icons.information, // Test informaton icon
//icon: hifi.icons.warning, // Test warning icon
//icon: hifi.icons.critical, // Test critical icon
//icon: hifi.icons.none, // Test no icon
buttons: OriginalDialogs.StandardButton.Ok + OriginalDialogs.StandardButton.Cancel,
defaultButton: OriginalDialogs.StandardButton.Ok
});
messageBox.selected.connect(function(button) {
console.log("You clicked " + button)
})
}
}
*/
// bookmarks
/*
Button {
text: "Bookmark Location"
onClicked: {
desktop.inputDialog({
title: "Bookmark Location",
icon: hifi.icons.placemark,
label: "Name"
});
}
}
Button {
text: "Delete Bookmark"
onClicked: {
desktop.inputDialog({
title: "Delete Bookmark",
icon: hifi.icons.placemark,
label: "Select the bookmark to delete",
items: ["Bookmark A", "Bookmark B", "Bookmark C"]
});
}
}
Button {
text: "Duplicate Bookmark"
onClicked: {
desktop.messageBox({
title: "Duplicate Bookmark",
icon: hifi.icons.warning,
text: "The bookmark name you entered alread exists in yoru list.",
informativeText: "Would you like to overwrite it?",
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
defaultButton: OriginalDialogs.StandardButton.Yes
});
}
}
*/
}
HifiConstants { id: hifi }
@ -35,249 +296,12 @@ ApplicationWindow {
}
*/
Row {
id: testButtons
anchors { margins: 8; left: parent.left; top: parent.top }
spacing: 8
property int count: 0
property var tabs: [];
property var urls: [];
Button {
// Shows the dialog with preferences sections but not each section's preference items
// because Preferences.preferencesByCategory() method is not stubbed out.
text: "Settings > General..."
property var builder: Component {
GeneralPreferencesDialog { }
}
onClicked: {
var runningScripts = builder.createObject(desktop);
}
}
Button {
text: "Running Scripts"
property var builder: Component {
RunningScripts { }
}
onClicked: {
var runningScripts = builder.createObject(desktop);
}
}
Button {
text: "Attachments"
property var builder: Component {
AttachmentsDialog { }
}
onClicked: {
var attachmentsDialog = builder.createObject(desktop);
}
}
/*
Button {
text: "restore all"
onClicked: {
for (var i = 0; i < desktop.windows.length; ++i) {
desktop.windows[i].visible = true
}
}
}
Button {
text: "toggle blue visible"
onClicked: {
blue.visible = !blue.visible
}
}
Button {
text: "toggle blue enabled"
onClicked: {
blue.enabled = !blue.enabled
}
}
*/
Button {
// Replicates message box that pops up after selecting new avatar. Includes title.
text: "Confirm Avatar"
onClicked: {
var messageBox = desktop.messageBox({
title: "Set Avatar",
text: "Would you like to use 'Albert' for your avatar?",
icon: hifi.icons.question, // Test question icon
//icon: hifi.icons.information, // Test informaton icon
//icon: hifi.icons.warning, // Test warning icon
//icon: hifi.icons.critical, // Test critical icon
//icon: hifi.icons.none, // Test no icon
buttons: OriginalDialogs.StandardButton.Ok + OriginalDialogs.StandardButton.Cancel,
defaultButton: OriginalDialogs.StandardButton.Ok
});
messageBox.selected.connect(function(button) {
console.log("You clicked " + button)
})
}
}
Button {
// Message without title.
text: "Show Error"
onClicked: {
var messageBox = desktop.messageBox({
text: "Diagnostic cycle will be complete in 30 seconds",
icon: hifi.icons.critical,
});
messageBox.selected.connect(function(button) {
console.log("You clicked " + button)
})
}
}
Button {
// detailedText is not currently used anywhere in Interface but it is easier to leave in and style good enough.
text: "Show Long Error"
onClicked: {
desktop.messageBox({
informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ",
text: "Baloney",
icon: hifi.icons.warning,
detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a"
});
}
}
Button {
text: "Bookmark Location"
onClicked: {
desktop.inputDialog({
title: "Bookmark Location",
icon: hifi.icons.placemark,
label: "Name"
});
}
}
Button {
text: "Delete Bookmark"
onClicked: {
desktop.inputDialog({
title: "Delete Bookmark",
icon: hifi.icons.placemark,
label: "Select the bookmark to delete",
items: ["Bookmark A", "Bookmark B", "Bookmark C"]
});
}
}
Button {
text: "Duplicate Bookmark"
onClicked: {
desktop.messageBox({
title: "Duplicate Bookmark",
icon: hifi.icons.warning,
text: "The bookmark name you entered alread exists in yoru list.",
informativeText: "Would you like to overwrite it?",
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
defaultButton: OriginalDialogs.StandardButton.Yes
});
}
}
/*
// There is no such desktop.queryBox() function; may need to update test to cover QueryDialog.qml?
Button {
text: "Show Query"
onClicked: {
var queryBox = desktop.queryBox({
text: "Have you stopped beating your wife?",
placeholderText: "Are you sure?",
// icon: hifi.icons.critical,
});
queryBox.selected.connect(function(result) {
console.log("User responded with " + result);
});
queryBox.canceled.connect(function() {
console.log("User cancelled query box ");
})
}
}
*/
Button {
text: "Open Directory"
property var builder: Component {
FileDialog { selectDirectory: true }
}
onClicked: {
var fileDialog = builder.createObject(desktop);
fileDialog.canceled.connect(function(){
console.log("Cancelled")
})
fileDialog.selectedFile.connect(function(file){
console.log("Selected " + file)
})
}
}
Button {
text: "Open File"
property var builder: Component {
FileDialog {
title: "Open File"
filter: "All Files (*.*)"
//filter: "HTML files (*.html);;Other(*.png)"
}
}
onClicked: {
var fileDialog = builder.createObject(desktop);
fileDialog.canceled.connect(function(){
console.log("Cancelled")
})
fileDialog.selectedFile.connect(function(file){
console.log("Selected " + file)
})
}
}
Button {
text: "Add Tab"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo" });
desktop.toolWindow.showTabForUrl("Foo", true);
}
}
Button {
text: "Add Tab 2"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 2" });
desktop.toolWindow.showTabForUrl("Foo 2", true);
}
}
Button {
text: "Add Tab 3"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.addWebTab({ source: "Foo 3" });
desktop.toolWindow.showTabForUrl("Foo 3", true);
}
}
Button {
text: "Destroy Tab"
onClicked: {
console.log(desktop.toolWindow);
desktop.toolWindow.removeTabForUrl("Foo");
}
}
}
/*
Window {
id: blue
closable: true
visible: true
resizable: true
destroyOnInvisible: false
destroyOnHidden: false
width: 100; height: 100
x: 1280 / 2; y: 720 / 2
@ -296,32 +320,33 @@ ApplicationWindow {
Component.onDestruction: console.log("Blue destroyed")
}
}
*/
/*
Rectangle { width: 100; height: 100; x: 100; y: 100; color: "#00f" }
Window {
id: green
alwaysOnTop: true
frame: HiddenFrame{}
hideBackground: true
closable: true
visible: true
resizable: false
x: 1280 / 2; y: 720 / 2
Settings {
category: "TestWindow.Green"
property alias x: green.x
property alias y: green.y
property alias width: green.width
property alias height: green.height
}
width: 100; height: 100
Rectangle { anchors.fill: parent; color: "green" }
Rectangle {
color: "#0f0"
width: green.width;
height: green.height;
}
}
/*
Window {
id: yellow
objectName: "Yellow"
closable: true
visible: true
resizable: true
x: 100; y: 100
width: 100; height: 100
Rectangle {
anchors.fill: parent
@ -329,7 +354,7 @@ ApplicationWindow {
color: "yellow"
}
}
*/
*/
}
Action {

View file

@ -18,6 +18,7 @@ DISTFILES += \
qml/*.qml \
../../interface/resources/qml/*.qml \
../../interface/resources/qml/controls/*.qml \
../../interface/resources/qml/controls-uit/*.qml \
../../interface/resources/qml/dialogs/*.qml \
../../interface/resources/qml/dialogs/fileDialog/*.qml \
../../interface/resources/qml/dialogs/preferences/*.qml \
@ -25,9 +26,9 @@ DISTFILES += \
../../interface/resources/qml/desktop/*.qml \
../../interface/resources/qml/menus/*.qml \
../../interface/resources/qml/styles/*.qml \
../../interface/resources/qml/styles-uit/*.qml \
../../interface/resources/qml/windows/*.qml \
../../interface/resources/qml/hifi/*.qml \
../../interface/resources/qml/hifi/dialogs/*.qml \
../../interface/resources/qml/hifi/dialogs/preferences/*.qml \
../../interface/resources/qml/hifi/overlays/*.qml