mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-05-07 04:50:02 +02:00
188 lines
5.7 KiB
QML
188 lines
5.7 KiB
QML
import QtQuick 2.5
|
|
import QtQuick.Controls 1.4
|
|
import QtGraphicalEffects 1.0
|
|
import "."
|
|
import "../styles"
|
|
|
|
FocusScope {
|
|
id: window
|
|
HifiConstants { id: hifi }
|
|
// The Window size is the size of the content, while the frame
|
|
// decorations can extend outside it. Windows should generally not be
|
|
// given explicit height / width, but rather be allowed to conform to
|
|
// their content
|
|
implicitHeight: content.height
|
|
implicitWidth: content.width
|
|
|
|
property bool topLevelWindow: true
|
|
property string title
|
|
// Should the window include a close control?
|
|
property bool closable: true
|
|
// Should hitting the close button hide or destroy the window?
|
|
property bool destroyOnCloseButton: true
|
|
// Should hiding the window destroy it or just hide it?
|
|
property bool destroyOnInvisible: false
|
|
// FIXME support for pinned / unpinned pending full design
|
|
// property bool pinnable: false
|
|
// property bool pinned: false
|
|
property bool resizable: false
|
|
property vector2d minSize: Qt.vector2d(100, 100)
|
|
property vector2d maxSize: Qt.vector2d(1280, 720)
|
|
|
|
// The content to place inside the window, determined by the client
|
|
default property var content
|
|
|
|
|
|
onContentChanged: {
|
|
if (content) {
|
|
content.anchors.fill = window
|
|
}
|
|
}
|
|
|
|
// Default to a standard frame. Can be overriden to provide custom
|
|
// frame styles, like a full desktop frame to simulate a modal window
|
|
property var frame: DefaultFrame {
|
|
z: -1
|
|
anchors.fill: parent
|
|
}
|
|
|
|
// This mouse area serves to raise the window. To function, it must live
|
|
// in the window and have a higher Z-order than the content, but follow
|
|
// the position and size of frame decoration
|
|
property var activator: MouseArea {
|
|
width: frame.decoration.width
|
|
height: frame.decoration.height
|
|
x: frame.decoration.anchors.margins
|
|
y: frame.decoration.anchors.topMargin
|
|
propagateComposedEvents: true
|
|
hoverEnabled: true
|
|
onPressed: { window.raise(); mouse.accepted = false; }
|
|
// Debugging visualization
|
|
// Rectangle { anchors.fill:parent; color: "#7f00ff00" }
|
|
}
|
|
|
|
children: [ frame, content, activator ]
|
|
signal windowDestroyed();
|
|
|
|
Component.onCompleted: {
|
|
fadeTargetProperty = visible ? 1.0 : 0.0
|
|
raise();
|
|
}
|
|
|
|
Component.onDestruction: {
|
|
content.destroy();
|
|
console.log("Destroyed " + window);
|
|
windowDestroyed();
|
|
}
|
|
onParentChanged: raise();
|
|
|
|
Connections {
|
|
target: frame
|
|
onRaise: window.raise();
|
|
onClose: window.close();
|
|
onPin: window.pin();
|
|
onDeltaSize: {
|
|
var newSize = Qt.vector2d(content.width + dx, content.height + dy);
|
|
newSize = clampVector(newSize, minSize, maxSize);
|
|
window.width = newSize.x
|
|
window.height = newSize.y
|
|
}
|
|
}
|
|
|
|
function raise() {
|
|
if (visible && parent) {
|
|
Desktop.raise(window)
|
|
if (!focus) {
|
|
focus = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
function close() {
|
|
console.log("Closing " + window)
|
|
if (destroyOnCloseButton) {
|
|
destroyOnInvisible = true
|
|
}
|
|
visible = false;
|
|
}
|
|
|
|
//
|
|
// Enable window visibility transitions
|
|
//
|
|
|
|
// The target property to animate, usually scale or opacity
|
|
property alias fadeTargetProperty: window.opacity
|
|
// always start the property at 0 to enable fade in on creation
|
|
opacity: 0
|
|
|
|
// Some dialogs should be destroyed when they become
|
|
// invisible, so handle that
|
|
onVisibleChanged: {
|
|
// If someone directly set the visibility to false
|
|
// toggle it back on and use the targetVisible flag to transition
|
|
// via fading.
|
|
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
|
|
var target = visible;
|
|
visible = !visible;
|
|
fadeTargetProperty = target ? 1.0 : 0.0;
|
|
return;
|
|
}
|
|
if (!visible && destroyOnInvisible) {
|
|
console.log("Destroying " + window);
|
|
destroy();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// The offscreen UI will enable an object, rather than manipulating it's
|
|
// visibility, so that we can do animations in both directions. Because
|
|
// visibility is a boolean flags, it cannot be animated. So when
|
|
// targetVisible is changed, we modify a property that can be animated,
|
|
// like scale or opacity, and then when the target animation value is reached,
|
|
// we can modify the visibility
|
|
|
|
// The actual animator
|
|
Behavior on fadeTargetProperty {
|
|
NumberAnimation {
|
|
duration: hifi.effects.fadeInDuration
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
// Once we're transparent, disable the dialog's visibility
|
|
onFadeTargetPropertyChanged: {
|
|
visible = (fadeTargetProperty != 0.0);
|
|
}
|
|
|
|
|
|
Keys.onPressed: {
|
|
switch(event.key) {
|
|
case Qt.Key_W:
|
|
if (event.modifiers === Qt.ControlModifier) {
|
|
event.accepted = true
|
|
visible = false
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
function clamp(value, min, max) {
|
|
return Math.min(Math.max(value, min), max);
|
|
}
|
|
|
|
function clampVector(value, min, max) {
|
|
return Qt.vector2d(
|
|
clamp(value.x, min.x, max.x),
|
|
clamp(value.y, min.y, max.y))
|
|
}
|
|
|
|
}
|