//// InteractiveWindow.qml//// Created by Thijs Wenker on 2018-06-25// Copyright 2018 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.3 import "windows" as Windowsimport "controls"import controlsUit 1.0 as Controlsimport "styles"import stylesUit 1.0 Windows.Window { id: root; HifiConstants { id: hifi } title: "InteractiveWindow"; resizable: true; // Virtual window visibility shown: false; focus: true; property var channel; // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer destroyOnCloseButton: false; signal selfDestruct(); property var additionalFlags: 0; property var overrideFlags: 0; property var source; property var dynamicContent; property var nativeWindow; // custom visibility flag for interactiveWindow to proxy virtualWindow.shown / nativeWindow.visible property var interactiveWindowVisible: true; property point interactiveWindowPosition; property size interactiveWindowSize; // Keyboard control properties in case needed by QML content. property bool keyboardEnabled: false; property bool keyboardRaised: false; property bool punctuationMode: false; property int presentationMode: 0; property var initialized: false; onSourceChanged: { if (dynamicContent) { dynamicContent.destroy(); dynamicContent = null; } QmlSurface.load(source, contentHolder, function(newObject) { dynamicContent = newObject; updateInteractiveWindowSizeForMode(); if (dynamicContent && dynamicContent.anchors) { dynamicContent.anchors.fill = contentHolder; } }); } Timer { id: timer interval: 500; repeat: false; onTriggered: { updateContentParent(); } } function updateInteractiveWindowPositionForMode() { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { x = interactiveWindowPosition.x; y = interactiveWindowPosition.y; } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { if (interactiveWindowPosition.x === 0 && interactiveWindowPosition.y === 0) { // default position for native window in center of main application window nativeWindow.x = Math.floor(Window.x + (Window.innerWidth / 2) - (interactiveWindowSize.width / 2)); nativeWindow.y = Math.floor(Window.y + (Window.innerHeight / 2) - (interactiveWindowSize.height / 2)); } else { nativeWindow.x = interactiveWindowPosition.x; nativeWindow.y = interactiveWindowPosition.y; } } } function updateInteractiveWindowSizeForMode() { root.width = interactiveWindowSize.width; root.height = interactiveWindowSize.height; contentHolder.width = interactiveWindowSize.width; contentHolder.height = interactiveWindowSize.height; if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.width = interactiveWindowSize.width; nativeWindow.height = interactiveWindowSize.height; } } function updateContentParent() { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { contentHolder.parent = root; } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { contentHolder.parent = nativeWindow.contentItem; } } function setupPresentationMode() { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { if (nativeWindow) { nativeWindow.setVisible(false); } updateInteractiveWindowPositionForMode(); shown = interactiveWindowVisible; } else if (presentationMode === Desktop.PresentationMode.NATIVE) { shown = false; if (nativeWindow) { updateInteractiveWindowPositionForMode(); nativeWindow.setVisible(interactiveWindowVisible); } } else if (presentationMode === modeNotSet) { console.error("presentationMode should be set."); } } Component.onCompleted: { x = interactiveWindowPosition.x; y = interactiveWindowPosition.y; width = interactiveWindowSize.width; height = interactiveWindowSize.height; nativeWindow = Qt.createQmlObject(' import QtQuick 2.3; import QtQuick.Window 2.3; Window { id: root; width: interactiveWindowSize.width height: interactiveWindowSize.height // fix for missing content on OSX initial startup with a non-maximized interface window. It seems that in this case, we cannot update // the content parent during creation of the Window root. This added delay will update the parent after the root has finished loading. Component.onCompleted: { timer.start(); } Rectangle { color: hifi.colors.baseGray anchors.fill: parent } }', root, 'InteractiveWindow.qml->nativeWindow'); nativeWindow.title = root.title; var nativeWindowFlags = Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint; // only use the always on top feature for non Windows OS if (Qt.platform.os !== "windows" && (root.additionalFlags & Desktop.ALWAYS_ON_TOP)) { nativeWindowFlags |= Qt.WindowStaysOnTopHint; } nativeWindow.flags = root.overrideFlags || nativeWindowFlags; nativeWindow.x = interactiveWindowPosition.x; nativeWindow.y = interactiveWindowPosition.y; nativeWindow.width = interactiveWindowSize.width; nativeWindow.height = interactiveWindowSize.height; nativeWindow.xChanged.connect(function() { if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y); } }); nativeWindow.yChanged.connect(function() { if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y); } }); nativeWindow.widthChanged.connect(function() { if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height); } }); nativeWindow.heightChanged.connect(function() { if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height); } }); nativeWindow.closing.connect(function(closeEvent) { closeEvent.accepted = false; windowClosed(); }); // finally set the initial window mode: setupPresentationMode(); updateContentParent(); initialized = true; } // Handle message traffic from the script that launched us to the loaded QML function fromScript(message) { if (root.dynamicContent && root.dynamicContent.fromScript) { root.dynamicContent.fromScript(message); } } function show() { interactiveWindowVisible = true; raiseWindow(); } function raiseWindow() { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { raise(); } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.raise(); } } // Handle message traffic from our loaded QML to the script that launched us signal sendToScript(var message); // Children of this InteractiveWindow Item are able to request a new width and height // for the parent Item (this one) and its associated C++ InteractiveWindow using these methods. function onRequestNewWidth(newWidth) { interactiveWindowSize.width = newWidth; updateInteractiveWindowSizeForMode(); } function onRequestNewHeight(newHeight) { interactiveWindowSize.height = newHeight; updateInteractiveWindowSizeForMode(); } // These signals are used to forward key-related events from the QML to the C++. signal keyPressEvent(int key, int modifiers); signal keyReleaseEvent(int key, int modifiers); onDynamicContentChanged: { if (dynamicContent && dynamicContent.sendToScript) { dynamicContent.sendToScript.connect(sendToScript); } if (dynamicContent && dynamicContent.requestNewWidth) { dynamicContent.requestNewWidth.connect(onRequestNewWidth); } if (dynamicContent && dynamicContent.requestNewHeight) { dynamicContent.requestNewHeight.connect(onRequestNewHeight); } if (dynamicContent && dynamicContent.keyPressEvent) { dynamicContent.keyPressEvent.connect(keyPressEvent); } if (dynamicContent && dynamicContent.keyReleaseEvent) { dynamicContent.keyReleaseEvent.connect(keyReleaseEvent); } } onInteractiveWindowVisibleChanged: { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { shown = interactiveWindowVisible; } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { if (!nativeWindow.visible && interactiveWindowVisible) { nativeWindow.showNormal(); } else { nativeWindow.setVisible(interactiveWindowVisible); } } } onTitleChanged: { if (nativeWindow) { nativeWindow.title = title; } } onXChanged: { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowPosition = Qt.point(x, interactiveWindowPosition.y); } } onYChanged: { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, y); } } onWidthChanged: { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowSize = Qt.size(width, interactiveWindowSize.height); } } onHeightChanged: { if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowSize = Qt.size(interactiveWindowSize.width, height); } } onPresentationModeChanged: { if (initialized) { setupPresentationMode(); updateContentParent(); } } onWindowClosed: { // set invisible on close, to make it not re-appear unintended after switching PresentationMode interactiveWindowVisible = false; if ((root.additionalFlags & Desktop.CLOSE_BUTTON_HIDES) !== Desktop.CLOSE_BUTTON_HIDES) { selfDestruct(); } } Item { id: contentHolder anchors.fill: parent }}
//
// InteractiveWindow.qml
//
// Created by Thijs Wenker on 2018-06-25
// Copyright 2018 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.3
import "windows" as Windows
import "controls"
import controlsUit 1.0 as Controls
import "styles"
import stylesUit 1.0
Windows.Window {
id: root;
HifiConstants { id: hifi }
title: "InteractiveWindow";
resizable: true;
// Virtual window visibility
shown: false;
focus: true;
property var channel;
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false;
signal selfDestruct();
property var additionalFlags: 0;
property var overrideFlags: 0;
property var source;
property var dynamicContent;
property var nativeWindow;
// custom visibility flag for interactiveWindow to proxy virtualWindow.shown / nativeWindow.visible
property var interactiveWindowVisible: true;
property point interactiveWindowPosition;
property size interactiveWindowSize;
// Keyboard control properties in case needed by QML content.
property bool keyboardEnabled: false;
property bool keyboardRaised: false;
property bool punctuationMode: false;
property int presentationMode: 0;
property var initialized: false;
onSourceChanged: {
if (dynamicContent) {
dynamicContent.destroy();
dynamicContent = null;
}
QmlSurface.load(source, contentHolder, function(newObject) {
dynamicContent = newObject;
updateInteractiveWindowSizeForMode();
if (dynamicContent && dynamicContent.anchors) {
dynamicContent.anchors.fill = contentHolder;
}
});
}
Timer {
id: timer
interval: 500;
repeat: false;
onTriggered: {
updateContentParent();
}
}
function updateInteractiveWindowPositionForMode() {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
x = interactiveWindowPosition.x;
y = interactiveWindowPosition.y;
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
if (interactiveWindowPosition.x === 0 && interactiveWindowPosition.y === 0) {
// default position for native window in center of main application window
nativeWindow.x = Math.floor(Window.x + (Window.innerWidth / 2) - (interactiveWindowSize.width / 2));
nativeWindow.y = Math.floor(Window.y + (Window.innerHeight / 2) - (interactiveWindowSize.height / 2));
} else {
nativeWindow.x = interactiveWindowPosition.x;
nativeWindow.y = interactiveWindowPosition.y;
}
}
}
function updateInteractiveWindowSizeForMode() {
root.width = interactiveWindowSize.width;
root.height = interactiveWindowSize.height;
contentHolder.width = interactiveWindowSize.width;
contentHolder.height = interactiveWindowSize.height;
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
nativeWindow.width = interactiveWindowSize.width;
nativeWindow.height = interactiveWindowSize.height;
}
}
function updateContentParent() {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
contentHolder.parent = root;
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
contentHolder.parent = nativeWindow.contentItem;
}
}
function setupPresentationMode() {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
if (nativeWindow) {
nativeWindow.setVisible(false);
}
updateInteractiveWindowPositionForMode();
shown = interactiveWindowVisible;
} else if (presentationMode === Desktop.PresentationMode.NATIVE) {
shown = false;
if (nativeWindow) {
updateInteractiveWindowPositionForMode();
nativeWindow.setVisible(interactiveWindowVisible);
}
} else if (presentationMode === modeNotSet) {
console.error("presentationMode should be set.");
}
}
Component.onCompleted: {
x = interactiveWindowPosition.x;
y = interactiveWindowPosition.y;
width = interactiveWindowSize.width;
height = interactiveWindowSize.height;
nativeWindow = Qt.createQmlObject('
import QtQuick 2.3;
import QtQuick.Window 2.3;
Window {
id: root;
width: interactiveWindowSize.width
height: interactiveWindowSize.height
// fix for missing content on OSX initial startup with a non-maximized interface window. It seems that in this case, we cannot update
// the content parent during creation of the Window root. This added delay will update the parent after the root has finished loading.
Component.onCompleted: {
timer.start();
}
Rectangle {
color: hifi.colors.baseGray
anchors.fill: parent
}
}', root, 'InteractiveWindow.qml->nativeWindow');
nativeWindow.title = root.title;
var nativeWindowFlags = Qt.Window |
Qt.WindowTitleHint |
Qt.WindowSystemMenuHint |
Qt.WindowCloseButtonHint |
Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint;
// only use the always on top feature for non Windows OS
if (Qt.platform.os !== "windows" && (root.additionalFlags & Desktop.ALWAYS_ON_TOP)) {
nativeWindowFlags |= Qt.WindowStaysOnTopHint;
}
nativeWindow.flags = root.overrideFlags || nativeWindowFlags;
nativeWindow.x = interactiveWindowPosition.x;
nativeWindow.y = interactiveWindowPosition.y;
nativeWindow.width = interactiveWindowSize.width;
nativeWindow.height = interactiveWindowSize.height;
nativeWindow.xChanged.connect(function() {
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y);
}
});
nativeWindow.yChanged.connect(function() {
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y);
}
});
nativeWindow.widthChanged.connect(function() {
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height);
}
});
nativeWindow.heightChanged.connect(function() {
if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) {
interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height);
}
});
nativeWindow.closing.connect(function(closeEvent) {
closeEvent.accepted = false;
windowClosed();
});
// finally set the initial window mode:
setupPresentationMode();
updateContentParent();
initialized = true;
}
// Handle message traffic from the script that launched us to the loaded QML
function fromScript(message) {
if (root.dynamicContent && root.dynamicContent.fromScript) {
root.dynamicContent.fromScript(message);
}
}
function show() {
interactiveWindowVisible = true;
raiseWindow();
}
function raiseWindow() {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
raise();
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
nativeWindow.raise();
}
}
// Handle message traffic from our loaded QML to the script that launched us
signal sendToScript(var message);
// Children of this InteractiveWindow Item are able to request a new width and height
// for the parent Item (this one) and its associated C++ InteractiveWindow using these methods.
function onRequestNewWidth(newWidth) {
interactiveWindowSize.width = newWidth;
updateInteractiveWindowSizeForMode();
}
function onRequestNewHeight(newHeight) {
interactiveWindowSize.height = newHeight;
updateInteractiveWindowSizeForMode();
}
// These signals are used to forward key-related events from the QML to the C++.
signal keyPressEvent(int key, int modifiers);
signal keyReleaseEvent(int key, int modifiers);
onDynamicContentChanged: {
if (dynamicContent && dynamicContent.sendToScript) {
dynamicContent.sendToScript.connect(sendToScript);
}
if (dynamicContent && dynamicContent.requestNewWidth) {
dynamicContent.requestNewWidth.connect(onRequestNewWidth);
}
if (dynamicContent && dynamicContent.requestNewHeight) {
dynamicContent.requestNewHeight.connect(onRequestNewHeight);
}
if (dynamicContent && dynamicContent.keyPressEvent) {
dynamicContent.keyPressEvent.connect(keyPressEvent);
}
if (dynamicContent && dynamicContent.keyReleaseEvent) {
dynamicContent.keyReleaseEvent.connect(keyReleaseEvent);
}
}
onInteractiveWindowVisibleChanged: {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
shown = interactiveWindowVisible;
} else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) {
if (!nativeWindow.visible && interactiveWindowVisible) {
nativeWindow.showNormal();
} else {
nativeWindow.setVisible(interactiveWindowVisible);
}
}
}
onTitleChanged: {
if (nativeWindow) {
nativeWindow.title = title;
}
}
onXChanged: {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
interactiveWindowPosition = Qt.point(x, interactiveWindowPosition.y);
}
}
onYChanged: {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, y);
}
}
onWidthChanged: {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
interactiveWindowSize = Qt.size(width, interactiveWindowSize.height);
}
}
onHeightChanged: {
if (presentationMode === Desktop.PresentationMode.VIRTUAL) {
interactiveWindowSize = Qt.size(interactiveWindowSize.width, height);
}
}
onPresentationModeChanged: {
if (initialized) {
setupPresentationMode();
updateContentParent();
}
}
onWindowClosed: {
// set invisible on close, to make it not re-appear unintended after switching PresentationMode
interactiveWindowVisible = false;
if ((root.additionalFlags & Desktop.CLOSE_BUTTON_HIDES) !== Desktop.CLOSE_BUTTON_HIDES) {
selfDestruct();
}
}
Item {
id: contentHolder
anchors.fill: parent
}
}