// // 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 } // // Signals // signal windowClosed(); signal windowDestroyed(); signal mouseEntered(); signal mouseExited(); // // 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; children: [ swallower, frame, defocuser, content, activator ] // // 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? 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 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, 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(); } } } // 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 ? frame.decoration.width : window.width height: frame.decoration ? frame.decoration.height : window.height x: frame.decoration ? frame.decoration.anchors.leftMargin : 0 y: frame.decoration ? frame.decoration.anchors.topMargin : 0 propagateComposedEvents: true acceptedButtons: Qt.AllButtons enabled: window.visible onPressed: { window.raise(); mouse.accepted = false; } } // This mouse area defocuses the current control so that the HMD keyboard gets hidden. property var defocuser: MouseArea { width: frame.decoration ? frame.decoration.width : window.width height: frame.decoration ? frame.decoration.height : window.height x: frame.decoration ? frame.decoration.anchors.leftMargin : 0 y: frame.decoration ? frame.decoration.anchors.topMargin : 0 propagateComposedEvents: true acceptedButtons: Qt.AllButtons enabled: window.visible onPressed: { frame.forceActiveFocus(); 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 ? frame.decoration.width : window.width height: frame.decoration ? frame.decoration.height : window.height x: frame.decoration ? frame.decoration.anchors.leftMargin : 0 y: frame.decoration ? frame.decoration.anchors.topMargin : 0 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 { //window: window } // // Handlers // Component.onCompleted: { window.parentChanged.connect(raise); setDefaultFocus(); d.centerOrReposition(); d.updateVisibility(shown); } Component.onDestruction: { window.parentChanged.disconnect(raise); // Prevent warning on shutdown windowDestroyed(); } onXChanged: rectifier.begin(); onYChanged: rectifier.begin(); onShownChanged: d.updateVisibility(shown) onVisibleChanged: { enabled = visible if (visible && parent) { 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) { if (force) { window.raise(); } 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 setPinned() { pinned = !pinned } 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)) { shown = 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; } } onMouseEntered: console.log("Mouse entered " + window) onMouseExited: console.log("Mouse exited " + window) }