mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 20:08:56 +02:00
More cleanup & menu fixing
This commit is contained in:
parent
4ea5c11b73
commit
d2900200a1
22 changed files with 440 additions and 488 deletions
|
@ -16,8 +16,6 @@ Windows.Window {
|
||||||
destroyOnCloseButton: false
|
destroyOnCloseButton: false
|
||||||
property alias source: webview.url
|
property alias source: webview.url
|
||||||
|
|
||||||
function raiseWindow() { Desktop.raise(root) }
|
|
||||||
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
|
|
|
@ -21,16 +21,10 @@ Windows.Window {
|
||||||
destroyOnCloseButton: false
|
destroyOnCloseButton: false
|
||||||
property alias source: pageLoader.source
|
property alias source: pageLoader.source
|
||||||
|
|
||||||
function raiseWindow() { Desktop.raise(root) }
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: pageLoader
|
id: pageLoader
|
||||||
objectName: "Loader"
|
objectName: "Loader"
|
||||||
focus: true
|
focus: true
|
||||||
property var dialog: root
|
property var dialog: root
|
||||||
|
|
||||||
Keys.onPressed: {
|
|
||||||
console.log("QmlWindow pageLoader keypress")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // dialog
|
} // dialog
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
import Hifi 1.0 as Hifi
|
|
||||||
|
|
||||||
import QtQuick 2.4
|
|
||||||
import QtQuick.Controls 1.3
|
|
||||||
import QtQuick.Controls.Styles 1.3
|
|
||||||
|
|
||||||
import "controls"
|
|
||||||
import "styles"
|
|
||||||
|
|
||||||
|
|
||||||
Hifi.VrMenu {
|
|
||||||
id: root
|
|
||||||
HifiConstants { id: hifi }
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
objectName: "VrMenu"
|
|
||||||
enabled: false
|
|
||||||
opacity: 0.0
|
|
||||||
z: 10000
|
|
||||||
|
|
||||||
property int animationDuration: 200
|
|
||||||
property var models: []
|
|
||||||
property var columns: []
|
|
||||||
|
|
||||||
onEnabledChanged: {
|
|
||||||
if (enabled && columns.length == 0) {
|
|
||||||
pushColumn(rootMenu.items);
|
|
||||||
}
|
|
||||||
opacity = enabled ? 1.0 : 0.0
|
|
||||||
offscreenFlags.navigationFocused = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actual animator
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.InOutBounce
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpacityChanged: {
|
|
||||||
visible = (opacity != 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible) reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
property var menuBuilder: Component {
|
|
||||||
VrMenuView {
|
|
||||||
property int menuDepth: root.models.length - 1
|
|
||||||
model: root.models[menuDepth]
|
|
||||||
|
|
||||||
function fit(position, size, maxposition) {
|
|
||||||
var padding = 8;
|
|
||||||
if (position < padding) {
|
|
||||||
position = padding;
|
|
||||||
} else if (position + size + padding > maxposition) {
|
|
||||||
position = maxposition - (size + padding);
|
|
||||||
}
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (menuDepth === 0) {
|
|
||||||
x = lastMousePosition.x - 20
|
|
||||||
y = lastMousePosition.y - 20
|
|
||||||
} else {
|
|
||||||
var lastColumn = root.columns[menuDepth - 1]
|
|
||||||
x = lastColumn.x + 64;
|
|
||||||
y = lastMousePosition.y - height / 2;
|
|
||||||
}
|
|
||||||
x = fit(x, width, parent.width);
|
|
||||||
y = fit(y, height, parent.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelected: {
|
|
||||||
root.selectItem(menuDepth, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastColumn() {
|
|
||||||
return columns[root.columns.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushColumn(items) {
|
|
||||||
models.push(itemsToModel(items))
|
|
||||||
if (columns.length) {
|
|
||||||
var oldColumn = lastColumn();
|
|
||||||
//oldColumn.enabled = false
|
|
||||||
}
|
|
||||||
var newColumn = menuBuilder.createObject(root);
|
|
||||||
columns.push(newColumn);
|
|
||||||
forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function popColumn() {
|
|
||||||
if (columns.length > 0) {
|
|
||||||
var curColumn = columns.pop();
|
|
||||||
curColumn.visible = false;
|
|
||||||
curColumn.destroy();
|
|
||||||
models.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columns.length == 0) {
|
|
||||||
enabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
curColumn = lastColumn();
|
|
||||||
curColumn.enabled = true;
|
|
||||||
curColumn.opacity = 1.0;
|
|
||||||
curColumn.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function itemsToModel(items) {
|
|
||||||
var newListModel = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', root);
|
|
||||||
for (var i = 0; i < items.length; ++i) {
|
|
||||||
var item = items[i];
|
|
||||||
switch (item.type) {
|
|
||||||
case 2:
|
|
||||||
newListModel.append({"type":item.type, "name": item.title, "item": item})
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
newListModel.append({"type":item.type, "name": item.text, "item": item})
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
newListModel.append({"type":item.type, "name": "-----", "item": item})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newListModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectItem(depth, source) {
|
|
||||||
var popped = false;
|
|
||||||
while (depth + 1 < columns.length) {
|
|
||||||
popColumn()
|
|
||||||
popped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (source.type) {
|
|
||||||
case 2:
|
|
||||||
lastColumn().enabled = false
|
|
||||||
pushColumn(source.items)
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (!popped) source.trigger()
|
|
||||||
enabled = false
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
while (columns.length > 0) {
|
|
||||||
popColumn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
id: mouseArea
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onClicked: {
|
|
||||||
if (mouse.button == Qt.RightButton) {
|
|
||||||
root.popColumn();
|
|
||||||
} else {
|
|
||||||
root.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMenu(menu, newMenu) {
|
|
||||||
return menu.addMenu(newMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addItem(menu, newMenuItem) {
|
|
||||||
return menu.addItem(newMenuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertItem(menu, beforeItem, newMenuItem) {
|
|
||||||
for (var i = 0; i < menu.items.length; ++i) {
|
|
||||||
if (menu.items[i] === beforeItem) {
|
|
||||||
return menu.insertItem(i, newMenuItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addItem(menu, newMenuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeItem(menu, menuItem) {
|
|
||||||
menu.removeItem(menuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function previousItem() {
|
|
||||||
if (columns.length) {
|
|
||||||
lastColumn().incrementCurrentIndex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextItem() {
|
|
||||||
if (columns.length) {
|
|
||||||
lastColumn().decrementCurrentIndex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectCurrentItem() {
|
|
||||||
if (columns.length) {
|
|
||||||
var depth = columns.length - 1;
|
|
||||||
var index = lastColumn().currentIndex;
|
|
||||||
if (index >= 0) {
|
|
||||||
var model = models[depth];
|
|
||||||
var item = model.get(index).item;
|
|
||||||
selectItem(depth, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: previousItem();
|
|
||||||
Keys.onUpPressed: nextItem();
|
|
||||||
Keys.onSpacePressed: selectCurrentItem();
|
|
||||||
Keys.onReturnPressed: selectCurrentItem();
|
|
||||||
Keys.onRightPressed: selectCurrentItem();
|
|
||||||
Keys.onLeftPressed: popColumn();
|
|
||||||
Keys.onEscapePressed: popColumn();
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
import QtQuick 2.4
|
|
||||||
import QtQuick.Controls 1.3
|
|
||||||
import QtQuick.Controls.Styles 1.3
|
|
||||||
|
|
||||||
import "styles"
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: root
|
|
||||||
HifiConstants { id: hifi }
|
|
||||||
width: 128
|
|
||||||
height: count * 32
|
|
||||||
onEnabledChanged: recalcSize();
|
|
||||||
onVisibleChanged: recalcSize();
|
|
||||||
onCountChanged: recalcSize();
|
|
||||||
|
|
||||||
signal selected(var item)
|
|
||||||
|
|
||||||
highlight: Rectangle {
|
|
||||||
width: root.currentItem ? root.currentItem.width : 0
|
|
||||||
height: root.currentItem ? root.currentItem.height : 0
|
|
||||||
color: "lightsteelblue"; radius: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: VrMenuItem {
|
|
||||||
text: name
|
|
||||||
source: item
|
|
||||||
onImplicitHeightChanged: root.recalcSize()
|
|
||||||
onImplicitWidthChanged: root.recalcSize()
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: root.currentIndex = index
|
|
||||||
onClicked: root.selected(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function recalcSize() {
|
|
||||||
if (model.count !== count || !visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalIndex = currentIndex;
|
|
||||||
var maxWidth = width;
|
|
||||||
var newHeight = 0;
|
|
||||||
for (var i = 0; i < count; ++i) {
|
|
||||||
currentIndex = i;
|
|
||||||
if (!currentItem) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (currentItem && currentItem.implicitWidth > maxWidth) {
|
|
||||||
maxWidth = currentItem.implicitWidth
|
|
||||||
}
|
|
||||||
if (currentItem.visible) {
|
|
||||||
newHeight += currentItem.implicitHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maxWidth > width) {
|
|
||||||
width = maxWidth;
|
|
||||||
}
|
|
||||||
if (newHeight > height) {
|
|
||||||
height = newHeight
|
|
||||||
}
|
|
||||||
currentIndex = originalIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Border {
|
|
||||||
id: border
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: -8
|
|
||||||
z: parent.z - 1
|
|
||||||
border.color: hifi.colors.hifiBlue
|
|
||||||
color: hifi.colors.window
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -2,74 +2,34 @@ import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.Dialogs 1.2 as OriginalDialogs;
|
import QtQuick.Dialogs 1.2 as OriginalDialogs;
|
||||||
|
|
||||||
import "dialogs"
|
import "../dialogs"
|
||||||
|
import "../menus"
|
||||||
|
|
||||||
// This is our primary 'desktop' object to which all VR dialogs and
|
// This is our primary 'desktop' object to which all VR dialogs and
|
||||||
// windows will be childed.
|
// windows will be childed.
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: desktop
|
id: desktop
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
|
objectName: "desktop"
|
||||||
// Debugging help for figuring out focus issues
|
|
||||||
property var offscreenWindow;
|
|
||||||
onOffscreenWindowChanged: offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
|
||||||
function onWindowFocusChanged() {
|
|
||||||
console.log("Focus item is " + offscreenWindow.activeFocusItem);
|
|
||||||
var focusedItem = offscreenWindow.activeFocusItem ;
|
|
||||||
if (DebugQML && focusedItem) {
|
|
||||||
var rect = desktop.mapToItem(desktop, focusedItem.x, focusedItem.y, focusedItem.width, focusedItem.height);
|
|
||||||
focusDebugger.visible = true
|
|
||||||
focusDebugger.x = rect.x;
|
|
||||||
focusDebugger.y = rect.y;
|
|
||||||
focusDebugger.width = rect.width
|
|
||||||
focusDebugger.height = rect.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: focusDebugger;
|
|
||||||
z: 9999; visible: false; color: "red"
|
|
||||||
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allows QML/JS to find the desktop through the parent chain
|
// Allows QML/JS to find the desktop through the parent chain
|
||||||
property bool desktopRoot: true
|
property bool desktopRoot: true
|
||||||
|
|
||||||
// The VR version of the primary menu
|
// The VR version of the primary menu
|
||||||
property var rootMenu: Menu { objectName: "rootMenu" }
|
property var rootMenu: Menu {
|
||||||
|
id: rootMenu; objectName: "rootMenu"
|
||||||
// The tool window, one instance
|
Component.onCompleted: {
|
||||||
property alias toolWindow: toolWindow
|
console.log("ROOT_MENU " + rootMenu);
|
||||||
ToolWindow { id: toolWindow }
|
}
|
||||||
|
|
||||||
// FIXME support always on top flags
|
|
||||||
function raise(item) {
|
|
||||||
d.raiseWindow(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: messageDialogBuilder
|
|
||||||
MessageDialog { }
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: nativeMessageDialogBuilder
|
|
||||||
OriginalDialogs.MessageDialog { }
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageBox(properties) {
|
|
||||||
// Debugging: native message dialog for comparison
|
|
||||||
// nativeMessageDialogBuilder.createObject(desktop, properties);
|
|
||||||
return messageDialogBuilder.createObject(desktop, properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
readonly property int zBasisNormal: 0
|
readonly property int zBasisNormal: 0
|
||||||
readonly property int zBasisAlwaysOnTop: 4096
|
readonly property int zBasisAlwaysOnTop: 4096
|
||||||
readonly property int zBasisModal: 8192
|
readonly property int zBasisModal: 8192
|
||||||
|
readonly property var messageDialogBuilder: Component { MessageDialog { } }
|
||||||
|
readonly property var nativeMessageDialogBuilder: Component { OriginalDialogs.MessageDialog { } }
|
||||||
|
|
||||||
function findChild(item, name) {
|
function findChild(item, name) {
|
||||||
for (var i = 0; i < item.children.length; ++i) {
|
for (var i = 0; i < item.children.length; ++i) {
|
||||||
|
@ -203,6 +163,43 @@ FocusScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuMouseHandler { id: menuPopperUpper }
|
||||||
|
|
||||||
|
function raise(item) {
|
||||||
|
d.raiseWindow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageBox(properties) {
|
||||||
|
// Debugging: native message dialog for comparison
|
||||||
|
// d.nativeMessageDialogBuilder.createObject(desktop, properties);
|
||||||
|
return d.messageDialogBuilder.createObject(desktop, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupMenu(point) {
|
||||||
|
menuPopperUpper.popup(desktop, rootMenu.items, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMenu(point) {
|
||||||
|
menuPopperUpper.toggle(desktop, rootMenu.items, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEscapePressed: {
|
||||||
|
if (menuPopperUpper.closeLastMenu()) {
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.accepted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
if (menuPopperUpper.closeLastMenu()) {
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.accepted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function unfocusWindows() {
|
function unfocusWindows() {
|
||||||
var windows = d.getTopLevelWindows();
|
var windows = d.getTopLevelWindows();
|
||||||
for (var i = 0; i < windows.length; ++i) {
|
for (var i = 0; i < windows.length; ++i) {
|
||||||
|
@ -210,6 +207,27 @@ FocusScope {
|
||||||
}
|
}
|
||||||
desktop.focus = true;
|
desktop.focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debugging help for figuring out focus issues
|
||||||
|
property var offscreenWindow;
|
||||||
|
onOffscreenWindowChanged: offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
||||||
|
function onWindowFocusChanged() {
|
||||||
|
console.log("Focus item is " + offscreenWindow.activeFocusItem);
|
||||||
|
var focusedItem = offscreenWindow.activeFocusItem ;
|
||||||
|
if (DebugQML && focusedItem) {
|
||||||
|
var rect = desktop.mapToItem(null, focusedItem.x, focusedItem.y, focusedItem.width, focusedItem.height);
|
||||||
|
focusDebugger.visible = true
|
||||||
|
focusDebugger.x = rect.x;
|
||||||
|
focusDebugger.y = rect.y;
|
||||||
|
focusDebugger.width = rect.width
|
||||||
|
focusDebugger.height = rect.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: focusDebugger;
|
||||||
|
z: 9999; visible: false; color: "red"
|
||||||
|
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ Window {
|
||||||
resizable: true
|
resizable: true
|
||||||
destroyOnInvisible: true
|
destroyOnInvisible: true
|
||||||
x: 40; y: 40
|
x: 40; y: 40
|
||||||
|
implicitWidth: 384; implicitHeight: 640
|
||||||
|
|
||||||
property var scripts: ScriptDiscoveryService;
|
property var scripts: ScriptDiscoveryService;
|
||||||
property var scriptsModel: scripts.scriptsModelFilter
|
property var scriptsModel: scripts.scriptsModelFilter
|
||||||
|
@ -77,7 +78,7 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFromFile() {
|
function loadFromFile() {
|
||||||
var fileDialog = fileDialogBuilder.createObject(Desktop, { filterModel: fileFilters });
|
var fileDialog = fileDialogBuilder.createObject(desktop, { filterModel: fileFilters });
|
||||||
fileDialog.canceled.connect(function(){
|
fileDialog.canceled.connect(function(){
|
||||||
console.debug("Cancelled file open")
|
console.debug("Cancelled file open")
|
||||||
})
|
})
|
||||||
|
@ -90,7 +91,7 @@ Window {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "white"
|
color: "white"
|
||||||
implicitWidth: 384; implicitHeight: 640
|
anchors.fill: parent
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors { fill: parent; margins: 8 }
|
anchors { fill: parent; margins: 8 }
|
||||||
|
|
15
interface/resources/qml/hifi/Desktop.qml
Normal file
15
interface/resources/qml/hifi/Desktop.qml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
import "../desktop"
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Desktop {
|
||||||
|
id: desktop
|
||||||
|
|
||||||
|
// The tool window, one instance
|
||||||
|
property alias toolWindow: toolWindow
|
||||||
|
ToolWindow { id: toolWindow }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
149
interface/resources/qml/menus/MenuMouseHandler.qml
Normal file
149
interface/resources/qml/menus/MenuMouseHandler.qml
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
import "."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property int zBasis: 8192 - 1024
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: menuRoot;
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: d.topMenu !== null
|
||||||
|
onClicked: {
|
||||||
|
d.clearMenus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
property var menuStack: []
|
||||||
|
property var topMenu: null;
|
||||||
|
property var modelMaker: Component { ListModel { } }
|
||||||
|
property var menuViewMaker: Component {
|
||||||
|
VrMenuView {
|
||||||
|
id: subMenu
|
||||||
|
onSelected: d.handleSelection(subMenu, currentItem, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toModel(items) {
|
||||||
|
var result = modelMaker.createObject(desktop);
|
||||||
|
for (var i = 0; i < items.length; ++i) {
|
||||||
|
var item = items[i];
|
||||||
|
if (!item.visible) continue;
|
||||||
|
switch (item.type) {
|
||||||
|
case MenuItemType.Menu:
|
||||||
|
result.append({"name": item.title, "item": item})
|
||||||
|
break;
|
||||||
|
case MenuItemType.Item:
|
||||||
|
result.append({"name": item.text, "item": item})
|
||||||
|
break;
|
||||||
|
case MenuItemType.Separator:
|
||||||
|
result.append({"name": "", "item": item})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popMenu() {
|
||||||
|
if (menuStack.length) {
|
||||||
|
menuStack.pop().destroy();
|
||||||
|
}
|
||||||
|
if (menuStack.length) {
|
||||||
|
topMenu = menuStack[menuStack.length - 1];
|
||||||
|
topMenu.focus = true;
|
||||||
|
} else {
|
||||||
|
topMenu = null;
|
||||||
|
offscreenFlags.navigationFocused = false;
|
||||||
|
menuRoot.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushMenu(newMenu) {
|
||||||
|
menuStack.push(newMenu);
|
||||||
|
topMenu = newMenu;
|
||||||
|
topMenu.focus = true;
|
||||||
|
offscreenFlags.navigationFocused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearMenus() {
|
||||||
|
while (menuStack.length) {
|
||||||
|
popMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampMenuPosition(menu) {
|
||||||
|
var margins = 0;
|
||||||
|
if (menu.x < margins) {
|
||||||
|
menu.x = margins
|
||||||
|
} else if ((menu.x + menu.width + margins) > root.width) {
|
||||||
|
menu.x = root.width - (menu.width + margins);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu.y < 0) {
|
||||||
|
menu.y = margins
|
||||||
|
} else if ((menu.y + menu.height + margins) > root.height) {
|
||||||
|
menu.y = root.height - (menu.height + margins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMenu(items, targetPosition) {
|
||||||
|
var model = toModel(items);
|
||||||
|
var newMenu = menuViewMaker.createObject(menuRoot, { model: model, z: topMenu ? topMenu.z + 1 : zBasis });
|
||||||
|
if (targetPosition) {
|
||||||
|
newMenu.x = targetPosition.x
|
||||||
|
newMenu.y = targetPosition.y - newMenu.height / 3 * 1
|
||||||
|
}
|
||||||
|
clampMenuPosition(newMenu);
|
||||||
|
pushMenu(newMenu);
|
||||||
|
return newMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelection(parentMenu, selectedItem, item) {
|
||||||
|
while (topMenu && topMenu !== parentMenu) {
|
||||||
|
popMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.type) {
|
||||||
|
case MenuItemType.Menu:
|
||||||
|
var target = Qt.vector2d(topMenu.x, topMenu.y).plus(Qt.vector2d(selectedItem.x + 96, selectedItem.y));
|
||||||
|
buildMenu(item.items, target).objectName = item.title;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuItemType.Item:
|
||||||
|
console.log("Triggering " + item.text)
|
||||||
|
item.trigger();
|
||||||
|
clearMenus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function popup(parent, items, point) {
|
||||||
|
d.clearMenus();
|
||||||
|
menuRoot.enabled = true;
|
||||||
|
d.buildMenu(items, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(parent, items, point) {
|
||||||
|
if (d.topMenu) {
|
||||||
|
d.clearMenus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
popup(parent, items, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLastMenu() {
|
||||||
|
if (d.menuStack.length) {
|
||||||
|
d.popMenu();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,16 +1,13 @@
|
||||||
import QtQuick 2.4
|
import QtQuick 2.4
|
||||||
import QtQuick.Controls 1.3
|
import QtQuick.Controls 1.3
|
||||||
import QtQuick.Controls.Styles 1.3
|
import QtQuick.Controls.Styles 1.3
|
||||||
import "controls"
|
|
||||||
import "styles"
|
import "../controls"
|
||||||
|
import "../styles"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
HifiConstants {
|
HifiConstants { id: hifi }
|
||||||
id: hifi
|
|
||||||
}
|
|
||||||
|
|
||||||
// The model object
|
|
||||||
property alias text: label.text
|
property alias text: label.text
|
||||||
property var source
|
property var source
|
||||||
|
|
100
interface/resources/qml/menus/VrMenuView.qml
Normal file
100
interface/resources/qml/menus/VrMenuView.qml
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Controls 1.3
|
||||||
|
import QtQuick.Controls.Styles 1.3
|
||||||
|
|
||||||
|
import "../styles"
|
||||||
|
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
implicitHeight: border.height
|
||||||
|
implicitWidth: border.width
|
||||||
|
|
||||||
|
property alias currentItem: listView.currentItem
|
||||||
|
property alias model: listView.model
|
||||||
|
signal selected(var item)
|
||||||
|
|
||||||
|
|
||||||
|
Border {
|
||||||
|
id: border
|
||||||
|
anchors.fill: listView
|
||||||
|
anchors.margins: -8
|
||||||
|
border.color: hifi.colors.hifiBlue
|
||||||
|
color: hifi.colors.window
|
||||||
|
// color: "#7f7f7f7f"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
x: 8; y: 8
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
width: 128
|
||||||
|
height: count * 32
|
||||||
|
onEnabledChanged: recalcSize();
|
||||||
|
onVisibleChanged: recalcSize();
|
||||||
|
onCountChanged: recalcSize();
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
width: listView.currentItem ? listView.currentItem.width : 0
|
||||||
|
height: listView.currentItem ? listView.currentItem.height : 0
|
||||||
|
color: "lightsteelblue"; radius: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: VrMenuItem {
|
||||||
|
text: name
|
||||||
|
source: item
|
||||||
|
onImplicitHeightChanged: listView.recalcSize()
|
||||||
|
onImplicitWidthChanged: listView.recalcSize()
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: listView.currentIndex = index
|
||||||
|
onClicked: root.selected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalcSize() {
|
||||||
|
if (model.count !== count || !visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalIndex = currentIndex;
|
||||||
|
var maxWidth = width;
|
||||||
|
var newHeight = 0;
|
||||||
|
for (var i = 0; i < count; ++i) {
|
||||||
|
currentIndex = i;
|
||||||
|
if (!currentItem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (currentItem && currentItem.implicitWidth > maxWidth) {
|
||||||
|
maxWidth = currentItem.implicitWidth
|
||||||
|
}
|
||||||
|
if (currentItem.visible) {
|
||||||
|
newHeight += currentItem.implicitHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxWidth > width) {
|
||||||
|
width = maxWidth;
|
||||||
|
}
|
||||||
|
if (newHeight > height) {
|
||||||
|
height = newHeight
|
||||||
|
}
|
||||||
|
currentIndex = originalIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previousItem() { currentIndex = (currentIndex + count - 1) % count; }
|
||||||
|
function nextItem() { currentIndex = (currentIndex + count + 1) % count; }
|
||||||
|
function selectCurrentItem() { if (currentIndex != -1) root.selected(currentItem.source); }
|
||||||
|
|
||||||
|
Keys.onUpPressed: previousItem();
|
||||||
|
Keys.onDownPressed: nextItem();
|
||||||
|
Keys.onSpacePressed: selectCurrentItem();
|
||||||
|
Keys.onRightPressed: selectCurrentItem();
|
||||||
|
Keys.onReturnPressed: selectCurrentItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ Fadable {
|
||||||
|
|
||||||
function raise() {
|
function raise() {
|
||||||
if (visible && parent) {
|
if (visible && parent) {
|
||||||
Desktop.raise(window)
|
desktop.raise(window)
|
||||||
if (!focus) {
|
if (!focus) {
|
||||||
focus = true;
|
focus = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -793,7 +793,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||||
cycleCamera();
|
cycleCamera();
|
||||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||||
VrMenu::toggle(); // show context menu even on non-stereo displays
|
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QCursor::pos()));
|
||||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||||
auto oldPos = QCursor::pos();
|
auto oldPos = QCursor::pos();
|
||||||
auto newPos = oldPos;
|
auto newPos = oldPos;
|
||||||
|
@ -1176,7 +1176,6 @@ void Application::initializeUi() {
|
||||||
AddressBarDialog::registerType();
|
AddressBarDialog::registerType();
|
||||||
ErrorDialog::registerType();
|
ErrorDialog::registerType();
|
||||||
LoginDialog::registerType();
|
LoginDialog::registerType();
|
||||||
VrMenu::registerType();
|
|
||||||
Tooltip::registerType();
|
Tooltip::registerType();
|
||||||
UpdateDialog::registerType();
|
UpdateDialog::registerType();
|
||||||
|
|
||||||
|
@ -1186,7 +1185,7 @@ void Application::initializeUi() {
|
||||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||||
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
||||||
// support the window management and scripting proxies for VR use
|
// support the window management and scripting proxies for VR use
|
||||||
offscreenUi->createDesktop();
|
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
|
||||||
|
|
||||||
// FIXME either expose so that dialogs can set this themselves or
|
// FIXME either expose so that dialogs can set this themselves or
|
||||||
// do better detection in the offscreen UI of what has focus
|
// do better detection in the offscreen UI of what has focus
|
||||||
|
@ -1244,8 +1243,6 @@ void Application::initializeUi() {
|
||||||
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||||
|
|
||||||
_glWidget->installEventFilter(offscreenUi.data());
|
_glWidget->installEventFilter(offscreenUi.data());
|
||||||
VrMenu::load();
|
|
||||||
VrMenu::executeQueuedLambdas();
|
|
||||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||||
QPointF result = pt;
|
QPointF result = pt;
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
|
@ -2063,7 +2060,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
|
|
||||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
||||||
VrMenu::toggle(); // show context menu even on non-stereo displays
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QCursor::pos()));
|
||||||
}
|
}
|
||||||
|
|
||||||
_keysPressed.remove(event->key());
|
_keysPressed.remove(event->key());
|
||||||
|
|
|
@ -628,3 +628,30 @@ QQmlContext* OffscreenQmlSurface::getRootContext() {
|
||||||
return _qmlEngine->rootContext();
|
return _qmlEngine->rootContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(std::function<void()>);
|
||||||
|
static auto VoidLambdaType = qRegisterMetaType<std::function<void()>>();
|
||||||
|
Q_DECLARE_METATYPE(std::function<QVariant()>);
|
||||||
|
static auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
||||||
|
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "executeOnUiThread", blocking ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||||
|
Q_ARG(std::function<void()>, function));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant OffscreenQmlSurface::returnFromUiThread(std::function<QVariant()> function) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QVariant result;
|
||||||
|
QMetaObject::invokeMethod(this, "returnFromUiThread", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QVariant, result),
|
||||||
|
Q_ARG(std::function<QVariant()>, function));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return function();
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,9 @@ public:
|
||||||
return load(QUrl(qmlSourceFile), f);
|
return load(QUrl(qmlSourceFile), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void executeOnUiThread(std::function<void()> function, bool blocking = false);
|
||||||
|
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
|
||||||
|
|
||||||
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
||||||
// Optional values for event handling
|
// Optional values for event handling
|
||||||
void setProxyWindow(QWindow* window);
|
void setProxyWindow(QWindow* window);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <AbstractUriHandler.h>
|
#include <AbstractUriHandler.h>
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
|
|
||||||
|
#include "VrMenu.h"
|
||||||
|
|
||||||
// Needs to match the constants in resources/qml/Global.js
|
// Needs to match the constants in resources/qml/Global.js
|
||||||
class OffscreenFlags : public QObject {
|
class OffscreenFlags : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -192,7 +194,7 @@ QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, cons
|
||||||
map.insert("buttons", buttons.operator int());
|
map.insert("buttons", buttons.operator int());
|
||||||
map.insert("defaultButton", defaultButton);
|
map.insert("defaultButton", defaultButton);
|
||||||
QVariant result;
|
QVariant result;
|
||||||
bool invokeResult = QMetaObject::invokeMethod(getDesktop(), "messageBox",
|
bool invokeResult = QMetaObject::invokeMethod(_desktop, "messageBox",
|
||||||
Q_RETURN_ARG(QVariant, result),
|
Q_RETURN_ARG(QVariant, result),
|
||||||
Q_ARG(QVariant, QVariant::fromValue(map)));
|
Q_ARG(QVariant, QVariant::fromValue(map)));
|
||||||
|
|
||||||
|
@ -231,16 +233,22 @@ void OffscreenUi::setNavigationFocused(bool focused) {
|
||||||
offscreenFlags->setNavigationFocused(focused);
|
offscreenFlags->setNavigationFocused(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenUi::createDesktop() {
|
void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
if (_desktop) {
|
if (_desktop) {
|
||||||
qDebug() << "Desktop already created";
|
qDebug() << "Desktop already created";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
getRootContext()->setContextProperty("DebugQML", QVariant(false));
|
getRootContext()->setContextProperty("DebugQML", QVariant(false));
|
||||||
_desktop = dynamic_cast<QQuickItem*>(load("Root.qml"));
|
_desktop = dynamic_cast<QQuickItem*>(load(url));
|
||||||
Q_ASSERT(_desktop);
|
Q_ASSERT(_desktop);
|
||||||
getRootContext()->setContextProperty("Desktop", _desktop);
|
getRootContext()->setContextProperty("desktop", _desktop);
|
||||||
|
|
||||||
|
// Enable focus debugging
|
||||||
_desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
_desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||||
|
|
||||||
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
||||||
|
|
||||||
|
new VrMenu(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickItem* OffscreenUi::getDesktop() {
|
QQuickItem* OffscreenUi::getDesktop() {
|
||||||
|
@ -251,38 +259,15 @@ QQuickItem* OffscreenUi::getToolWindow() {
|
||||||
return _toolWindow;
|
return _toolWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::function<void()>);
|
|
||||||
static auto VoidLambdaType = qRegisterMetaType<std::function<void()>>();
|
|
||||||
Q_DECLARE_METATYPE(std::function<QVariant()>);
|
|
||||||
static auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
|
||||||
|
|
||||||
|
|
||||||
void OffscreenUi::executeOnUiThread(std::function<void()> function) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
QMetaObject::invokeMethod(this, "executeOnUiThread", Qt::QueuedConnection,
|
|
||||||
Q_ARG(std::function<void()>, function));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant OffscreenUi::returnFromUiThread(std::function<QVariant()> function) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
QVariant result;
|
|
||||||
QMetaObject::invokeMethod(this, "returnFromUiThread", Qt::BlockingQueuedConnection,
|
|
||||||
Q_RETURN_ARG(QVariant, result),
|
|
||||||
Q_ARG(std::function<QVariant()>, function));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return function();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OffscreenUi::unfocusWindows() {
|
void OffscreenUi::unfocusWindows() {
|
||||||
bool invokeResult = QMetaObject::invokeMethod(_desktop, "unfocusWindows");
|
bool invokeResult = QMetaObject::invokeMethod(_desktop, "unfocusWindows");
|
||||||
Q_ASSERT(invokeResult);
|
Q_ASSERT(invokeResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenUi::toggleMenu(const QPoint& screenPosition) {
|
||||||
|
auto virtualPos = mapToVirtualScreen(screenPosition, nullptr);
|
||||||
|
QMetaObject::invokeMethod(_desktop, "toggleMenu", Q_ARG(QVariant, virtualPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "OffscreenUi.moc"
|
#include "OffscreenUi.moc"
|
||||||
|
|
|
@ -27,18 +27,18 @@ class OffscreenUi : public OffscreenQmlSurface, public Dependency {
|
||||||
public:
|
public:
|
||||||
OffscreenUi();
|
OffscreenUi();
|
||||||
virtual void create(QOpenGLContext* context) override;
|
virtual void create(QOpenGLContext* context) override;
|
||||||
void createDesktop();
|
void createDesktop(const QUrl& url);
|
||||||
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||||
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||||
bool shouldSwallowShortcut(QEvent* event);
|
bool shouldSwallowShortcut(QEvent* event);
|
||||||
bool navigationFocused();
|
bool navigationFocused();
|
||||||
void setNavigationFocused(bool focused);
|
void setNavigationFocused(bool focused);
|
||||||
void unfocusWindows();
|
void unfocusWindows();
|
||||||
|
void toggleMenu(const QPoint& screenCoordinates);
|
||||||
|
|
||||||
QQuickItem* getDesktop();
|
QQuickItem* getDesktop();
|
||||||
QQuickItem* getToolWindow();
|
QQuickItem* getToolWindow();
|
||||||
|
|
||||||
Q_INVOKABLE void executeOnUiThread(std::function<void()> function);
|
|
||||||
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
|
|
||||||
|
|
||||||
/// Same design as QMessageBox::critical(), will block, returns result
|
/// Same design as QMessageBox::critical(), will block, returns result
|
||||||
static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text,
|
static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text,
|
||||||
|
|
|
@ -345,7 +345,7 @@ void QmlWindowClass::hasClosed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::raise() {
|
void QmlWindowClass::raise() {
|
||||||
QMetaObject::invokeMethod(asQuickItem(), "raiseWindow", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "QmlWindowClass.moc"
|
#include "QmlWindowClass.moc"
|
||||||
|
|
|
@ -36,7 +36,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuUserData(const MenuUserData&);
|
MenuUserData(const MenuUserData&);
|
||||||
|
|
||||||
void init(QObject* widgetObject, QObject* qmlObject) {
|
void init(QObject* widgetObject, QObject* qmlObject) {
|
||||||
widgetObject->setUserData(USER_DATA_ID, this);
|
widgetObject->setUserData(USER_DATA_ID, this);
|
||||||
qmlObject->setUserData(USER_DATA_ID, this);
|
qmlObject->setUserData(USER_DATA_ID, this);
|
||||||
|
@ -48,14 +47,6 @@ private:
|
||||||
|
|
||||||
const int MenuUserData::USER_DATA_ID = QObject::registerUserData();
|
const int MenuUserData::USER_DATA_ID = QObject::registerUserData();
|
||||||
|
|
||||||
HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) {
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
|
||||||
QObject* rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
|
|
||||||
Q_ASSERT(rootMenu);
|
|
||||||
static_cast<VrMenu*>(newItem)->setRootMenu(rootMenu);
|
|
||||||
context->setContextProperty("rootMenu", rootMenu);
|
|
||||||
});
|
|
||||||
|
|
||||||
VrMenu* VrMenu::_instance{ nullptr };
|
VrMenu* VrMenu::_instance{ nullptr };
|
||||||
static QQueue<std::function<void(VrMenu*)>> queuedLambdas;
|
static QQueue<std::function<void(VrMenu*)>> queuedLambdas;
|
||||||
|
|
||||||
|
@ -70,19 +61,18 @@ void VrMenu::executeOrQueue(std::function<void(VrMenu*)> f) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VrMenu::executeQueuedLambdas() {
|
|
||||||
Q_ASSERT(_instance);
|
VrMenu::VrMenu(QObject* parent) : QObject(parent) {
|
||||||
|
_instance = this;
|
||||||
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
_rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
|
||||||
|
offscreenUi->getRootContext()->setContextProperty("rootMenu", _rootMenu);
|
||||||
foreach(std::function<void(VrMenu*)> f, queuedLambdas) {
|
foreach(std::function<void(VrMenu*)> f, queuedLambdas) {
|
||||||
f(_instance);
|
f(this);
|
||||||
}
|
}
|
||||||
queuedLambdas.clear();
|
queuedLambdas.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) {
|
|
||||||
_instance = this;
|
|
||||||
this->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject* VrMenu::findMenuObject(const QString& menuOption) {
|
QObject* VrMenu::findMenuObject(const QString& menuOption) {
|
||||||
if (menuOption.isEmpty()) {
|
if (menuOption.isEmpty()) {
|
||||||
return _rootMenu;
|
return _rootMenu;
|
||||||
|
@ -91,10 +81,6 @@ QObject* VrMenu::findMenuObject(const QString& menuOption) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VrMenu::setRootMenu(QObject* rootMenu) {
|
|
||||||
_rootMenu = rootMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateQmlItemFromAction(QObject* target, QAction* source) {
|
void updateQmlItemFromAction(QObject* target, QAction* source) {
|
||||||
target->setProperty("checkable", source->isCheckable());
|
target->setProperty("checkable", source->isCheckable());
|
||||||
target->setProperty("enabled", source->isEnabled());
|
target->setProperty("enabled", source->isEnabled());
|
||||||
|
@ -116,9 +102,8 @@ void VrMenu::addMenu(QMenu* menu) {
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
}
|
}
|
||||||
QVariant returnedValue;
|
QVariant returnedValue;
|
||||||
bool invokeResult = QMetaObject::invokeMethod(this, "addMenu", Qt::DirectConnection,
|
bool invokeResult = QMetaObject::invokeMethod(qmlParent, "addMenu", Qt::DirectConnection,
|
||||||
Q_RETURN_ARG(QVariant, returnedValue),
|
Q_RETURN_ARG(QVariant, returnedValue),
|
||||||
Q_ARG(QVariant, QVariant::fromValue(qmlParent)),
|
|
||||||
Q_ARG(QVariant, QVariant::fromValue(menu->title())));
|
Q_ARG(QVariant, QVariant::fromValue(menu->title())));
|
||||||
Q_ASSERT(invokeResult);
|
Q_ASSERT(invokeResult);
|
||||||
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
||||||
|
@ -147,21 +132,24 @@ void bindActionToQmlAction(QObject* qmlAction, QAction* action) {
|
||||||
QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger()));
|
QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QQuickMenuItem;
|
||||||
|
|
||||||
void VrMenu::addAction(QMenu* menu, QAction* action) {
|
void VrMenu::addAction(QMenu* menu, QAction* action) {
|
||||||
Q_ASSERT(!MenuUserData::forObject(action));
|
Q_ASSERT(!MenuUserData::forObject(action));
|
||||||
Q_ASSERT(MenuUserData::forObject(menu));
|
Q_ASSERT(MenuUserData::forObject(menu));
|
||||||
MenuUserData* userData = MenuUserData::forObject(menu);
|
MenuUserData* userData = MenuUserData::forObject(menu);
|
||||||
QObject* menuQml = findMenuObject(userData->uuid.toString());
|
QObject* menuQml = findMenuObject(userData->uuid.toString());
|
||||||
Q_ASSERT(menuQml);
|
Q_ASSERT(menuQml);
|
||||||
QVariant returnedValue;
|
QQuickMenuItem* returnedValue { nullptr };
|
||||||
|
|
||||||
bool invokeResult = QMetaObject::invokeMethod(this, "addItem", Qt::DirectConnection,
|
qDebug() << menuQml;
|
||||||
Q_RETURN_ARG(QVariant, returnedValue),
|
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
|
||||||
Q_ARG(QVariant, QVariant::fromValue(menuQml)),
|
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
|
||||||
Q_ARG(QVariant, QVariant::fromValue(action->text())));
|
Q_ARG(QString, action->text()));
|
||||||
|
|
||||||
Q_ASSERT(invokeResult);
|
Q_ASSERT(invokeResult);
|
||||||
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
||||||
QObject* result = returnedValue.value<QObject*>();
|
QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();
|
||||||
Q_ASSERT(result);
|
Q_ASSERT(result);
|
||||||
// Bind the QML and Widget together
|
// Bind the QML and Widget together
|
||||||
bindActionToQmlAction(result, action);
|
bindActionToQmlAction(result, action);
|
||||||
|
@ -175,19 +163,19 @@ void VrMenu::insertAction(QAction* before, QAction* action) {
|
||||||
beforeQml = findMenuObject(beforeUserData->uuid.toString());
|
beforeQml = findMenuObject(beforeUserData->uuid.toString());
|
||||||
}
|
}
|
||||||
QObject* menu = beforeQml->parent();
|
QObject* menu = beforeQml->parent();
|
||||||
QVariant returnedValue;
|
QQuickMenuItem* returnedValue { nullptr };
|
||||||
bool invokeResult = QMetaObject::invokeMethod(this, "insertItem", Qt::DirectConnection,
|
// FIXME this needs to find the index of the beforeQml item and call insertItem(int, object)
|
||||||
Q_RETURN_ARG(QVariant, returnedValue),
|
bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection,
|
||||||
Q_ARG(QVariant, QVariant::fromValue(menu)),
|
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
|
||||||
Q_ARG(QVariant, QVariant::fromValue(beforeQml)),
|
Q_ARG(QString, action->text()));
|
||||||
Q_ARG(QVariant, QVariant::fromValue(action->text())));
|
|
||||||
Q_ASSERT(invokeResult);
|
Q_ASSERT(invokeResult);
|
||||||
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();
|
||||||
QObject* result = returnedValue.value<QObject*>();
|
|
||||||
Q_ASSERT(result);
|
Q_ASSERT(result);
|
||||||
bindActionToQmlAction(result, action);
|
bindActionToQmlAction(result, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QQuickMenuBase;
|
||||||
|
|
||||||
void VrMenu::removeAction(QAction* action) {
|
void VrMenu::removeAction(QAction* action) {
|
||||||
if (!action) {
|
if (!action) {
|
||||||
qWarning("Attempted to remove invalid menu action");
|
qWarning("Attempted to remove invalid menu action");
|
||||||
|
@ -198,12 +186,12 @@ void VrMenu::removeAction(QAction* action) {
|
||||||
qWarning("Attempted to remove menu action with no found QML object");
|
qWarning("Attempted to remove menu action with no found QML object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* item = findMenuObject(userData->uuid.toString());
|
QObject* item = findMenuObject(userData->uuid.toString());
|
||||||
QObject* menu = item->parent();
|
QObject* menu = item->parent();
|
||||||
// Proxy QuickItem requests through the QML layer
|
// Proxy QuickItem requests through the QML layer
|
||||||
bool invokeResult = QMetaObject::invokeMethod(this, "removeItem", Qt::DirectConnection,
|
QQuickMenuBase* qmlItem = reinterpret_cast<QQuickMenuBase*>(item);
|
||||||
Q_ARG(QVariant, QVariant::fromValue(menu)),
|
bool invokeResult = QMetaObject::invokeMethod(menu, "removeItem", Qt::DirectConnection,
|
||||||
Q_ARG(QVariant, QVariant::fromValue(item)));
|
Q_ARG(QQuickMenuBase*, qmlItem));
|
||||||
Q_ASSERT(invokeResult);
|
Q_ASSERT(invokeResult);
|
||||||
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,16 @@
|
||||||
#include "OffscreenUi.h"
|
#include "OffscreenUi.h"
|
||||||
|
|
||||||
// FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML
|
// FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML
|
||||||
class VrMenu : public QQuickItem {
|
class VrMenu : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
HIFI_QML_DECL_LAMBDA
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void executeOrQueue(std::function<void(VrMenu*)> f);
|
static void executeOrQueue(std::function<void(VrMenu*)> f);
|
||||||
static void executeQueuedLambdas();
|
VrMenu(QObject* parent = nullptr);
|
||||||
VrMenu(QQuickItem* parent = nullptr);
|
|
||||||
void addMenu(QMenu* menu);
|
void addMenu(QMenu* menu);
|
||||||
void addAction(QMenu* parent, QAction* action);
|
void addAction(QMenu* parent, QAction* action);
|
||||||
void insertAction(QAction* before, QAction* action);
|
void insertAction(QAction* before, QAction* action);
|
||||||
void removeAction(QAction* action);
|
void removeAction(QAction* action);
|
||||||
|
|
||||||
void setRootMenu(QObject* rootMenu);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QObject* _rootMenu{ nullptr };
|
QObject* _rootMenu{ nullptr };
|
||||||
QObject* findMenuObject(const QString& name);
|
QObject* findMenuObject(const QString& name);
|
||||||
|
|
|
@ -6,6 +6,8 @@ import Qt.labs.settings 1.0
|
||||||
import "../../../interface/resources/qml"
|
import "../../../interface/resources/qml"
|
||||||
import "../../../interface/resources/qml/windows"
|
import "../../../interface/resources/qml/windows"
|
||||||
import "../../../interface/resources/qml/dialogs"
|
import "../../../interface/resources/qml/dialogs"
|
||||||
|
import "../../../interface/resources/qml/hifi"
|
||||||
|
import "../../../interface/resources/qml/hifi/dialogs"
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
@ -36,7 +38,7 @@ ApplicationWindow {
|
||||||
return newListModel;
|
return newListModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Root {
|
Desktop {
|
||||||
id: desktop
|
id: desktop
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
StubMenu { id: stubMenu }
|
StubMenu { id: stubMenu }
|
||||||
|
@ -70,28 +72,6 @@ ApplicationWindow {
|
||||||
blue.enabled = !blue.enabled
|
blue.enabled = !blue.enabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Button {
|
|
||||||
// text: "add web tab"
|
|
||||||
// onClicked: {
|
|
||||||
// testButtons.urls.push("http://slashdot.org?" + testButtons.count++);
|
|
||||||
// testButtons.tabs.push(desktop.toolWindow.addWebTab({ title: "test", source: testButtons.urls[testButtons.urls.length - 1], width: 500, height: 720 }))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Button {
|
|
||||||
// text: "toggle tab visible"
|
|
||||||
// onClicked: {
|
|
||||||
// var lastUrl = testButtons.urls[testButtons.urls.length - 1];
|
|
||||||
// var tab = desktop.toolWindow.findTabForUrl(lastUrl);
|
|
||||||
// desktop.toolWindow.showTabForUrl(lastUrl, !tab.enabled)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Button {
|
|
||||||
// text: "Remove last tab"
|
|
||||||
// onClicked: {
|
|
||||||
// testButtons.tabs.pop();
|
|
||||||
// desktop.toolWindow.removeTabForUrl(testButtons.urls.pop());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Button {
|
Button {
|
||||||
text: "Show Long Error"
|
text: "Show Long Error"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -152,6 +132,11 @@ ApplicationWindow {
|
||||||
console.log(appWindow.activeFocusItem);
|
console.log(appWindow.activeFocusItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button {
|
||||||
|
text: "Preferences"
|
||||||
|
property var preferencesComponent: Component { PreferencesDialog { } }
|
||||||
|
onClicked: preferencesComponent.createObject(desktop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
|
|
|
@ -80,5 +80,10 @@ DISTFILES += \
|
||||||
../../interface/resources/qml/VrMenu.qml \
|
../../interface/resources/qml/VrMenu.qml \
|
||||||
../../interface/resources/qml/VrMenuItem.qml \
|
../../interface/resources/qml/VrMenuItem.qml \
|
||||||
../../interface/resources/qml/VrMenuView.qml \
|
../../interface/resources/qml/VrMenuView.qml \
|
||||||
../../interface/resources/qml/WebEntity.qml
|
../../interface/resources/qml/WebEntity.qml \
|
||||||
|
../../interface/resources/qml/desktop/Desktop.qml \
|
||||||
|
../../interface/resources/qml/hifi/Desktop.qml \
|
||||||
|
../../interface/resources/qml/menus/MenuMouseHandler.qml \
|
||||||
|
../../interface/resources/qml/menus/VrMenuItem.qml \
|
||||||
|
../../interface/resources/qml/menus/VrMenuView.qml
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue