More cleanup & menu fixing

This commit is contained in:
Brad Davis 2016-01-18 12:24:11 -08:00
parent 4ea5c11b73
commit d2900200a1
22 changed files with 440 additions and 488 deletions

View file

@ -16,8 +16,6 @@ Windows.Window {
destroyOnCloseButton: false
property alias source: webview.url
function raiseWindow() { Desktop.raise(root) }
Controls.WebView {
id: webview
url: "about:blank"

View file

@ -21,16 +21,10 @@ Windows.Window {
destroyOnCloseButton: false
property alias source: pageLoader.source
function raiseWindow() { Desktop.raise(root) }
Loader {
id: pageLoader
objectName: "Loader"
focus: true
property var dialog: root
Keys.onPressed: {
console.log("QmlWindow pageLoader keypress")
}
}
} // dialog

View file

@ -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();
}

View file

@ -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
}
}

View file

@ -2,74 +2,34 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
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
// windows will be childed.
FocusScope {
id: desktop
anchors.fill: parent;
// 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 }
}
objectName: "desktop"
// Allows QML/JS to find the desktop through the parent chain
property bool desktopRoot: true
// The VR version of the primary menu
property var rootMenu: Menu { objectName: "rootMenu" }
// The tool window, one instance
property alias toolWindow: toolWindow
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);
property var rootMenu: Menu {
id: rootMenu; objectName: "rootMenu"
Component.onCompleted: {
console.log("ROOT_MENU " + rootMenu);
}
}
QtObject {
id: d
readonly property int zBasisNormal: 0
readonly property int zBasisAlwaysOnTop: 4096
readonly property int zBasisModal: 8192
readonly property var messageDialogBuilder: Component { MessageDialog { } }
readonly property var nativeMessageDialogBuilder: Component { OriginalDialogs.MessageDialog { } }
function findChild(item, name) {
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() {
var windows = d.getTopLevelWindows();
for (var i = 0; i < windows.length; ++i) {
@ -210,6 +207,27 @@ FocusScope {
}
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 }
}
}

View file

@ -14,6 +14,7 @@ Window {
resizable: true
destroyOnInvisible: true
x: 40; y: 40
implicitWidth: 384; implicitHeight: 640
property var scripts: ScriptDiscoveryService;
property var scriptsModel: scripts.scriptsModelFilter
@ -77,7 +78,7 @@ Window {
}
function loadFromFile() {
var fileDialog = fileDialogBuilder.createObject(Desktop, { filterModel: fileFilters });
var fileDialog = fileDialogBuilder.createObject(desktop, { filterModel: fileFilters });
fileDialog.canceled.connect(function(){
console.debug("Cancelled file open")
})
@ -90,7 +91,7 @@ Window {
Rectangle {
color: "white"
implicitWidth: 384; implicitHeight: 640
anchors.fill: parent
Item {
anchors { fill: parent; margins: 8 }

View 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 }
}

View 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;
}
}

View file

@ -1,16 +1,13 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import "controls"
import "styles"
import "../controls"
import "../styles"
Item {
id: root
HifiConstants {
id: hifi
}
// The model object
HifiConstants { id: hifi }
property alias text: label.text
property var source

View 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();
}
}

View file

@ -114,7 +114,7 @@ Fadable {
function raise() {
if (visible && parent) {
Desktop.raise(window)
desktop.raise(window)
if (!focus) {
focus = true;
}

View file

@ -793,7 +793,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
cycleCamera();
} 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)) {
auto oldPos = QCursor::pos();
auto newPos = oldPos;
@ -1176,7 +1176,6 @@ void Application::initializeUi() {
AddressBarDialog::registerType();
ErrorDialog::registerType();
LoginDialog::registerType();
VrMenu::registerType();
Tooltip::registerType();
UpdateDialog::registerType();
@ -1186,7 +1185,7 @@ void Application::initializeUi() {
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
// 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
// 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());
_glWidget->installEventFilter(offscreenUi.data());
VrMenu::load();
VrMenu::executeQueuedLambdas();
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
QPointF result = pt;
auto displayPlugin = getActiveDisplayPlugin();
@ -2063,7 +2060,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
void Application::keyReleaseEvent(QKeyEvent* event) {
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());

View file

@ -628,3 +628,30 @@ QQmlContext* OffscreenQmlSurface::getRootContext() {
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();
}

View file

@ -45,6 +45,9 @@ public:
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; }
// Optional values for event handling
void setProxyWindow(QWindow* window);

View file

@ -16,6 +16,8 @@
#include <AbstractUriHandler.h>
#include <AccountManager.h>
#include "VrMenu.h"
// Needs to match the constants in resources/qml/Global.js
class OffscreenFlags : public QObject {
Q_OBJECT
@ -192,7 +194,7 @@ QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, cons
map.insert("buttons", buttons.operator int());
map.insert("defaultButton", defaultButton);
QVariant result;
bool invokeResult = QMetaObject::invokeMethod(getDesktop(), "messageBox",
bool invokeResult = QMetaObject::invokeMethod(_desktop, "messageBox",
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, QVariant::fromValue(map)));
@ -231,16 +233,22 @@ void OffscreenUi::setNavigationFocused(bool focused) {
offscreenFlags->setNavigationFocused(focused);
}
void OffscreenUi::createDesktop() {
void OffscreenUi::createDesktop(const QUrl& url) {
if (_desktop) {
qDebug() << "Desktop already created";
return;
}
getRootContext()->setContextProperty("DebugQML", QVariant(false));
_desktop = dynamic_cast<QQuickItem*>(load("Root.qml"));
_desktop = dynamic_cast<QQuickItem*>(load(url));
Q_ASSERT(_desktop);
getRootContext()->setContextProperty("Desktop", _desktop);
getRootContext()->setContextProperty("desktop", _desktop);
// Enable focus debugging
_desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
new VrMenu(this);
}
QQuickItem* OffscreenUi::getDesktop() {
@ -251,38 +259,15 @@ QQuickItem* OffscreenUi::getToolWindow() {
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() {
bool invokeResult = QMetaObject::invokeMethod(_desktop, "unfocusWindows");
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"

View file

@ -27,18 +27,18 @@ class OffscreenUi : public OffscreenQmlSurface, public Dependency {
public:
OffscreenUi();
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 toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
bool shouldSwallowShortcut(QEvent* event);
bool navigationFocused();
void setNavigationFocused(bool focused);
void unfocusWindows();
void toggleMenu(const QPoint& screenCoordinates);
QQuickItem* getDesktop();
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
static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text,

View file

@ -345,7 +345,7 @@ void QmlWindowClass::hasClosed() {
}
void QmlWindowClass::raise() {
QMetaObject::invokeMethod(asQuickItem(), "raiseWindow", Qt::QueuedConnection);
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection);
}
#include "QmlWindowClass.moc"

View file

@ -36,7 +36,6 @@ public:
private:
MenuUserData(const MenuUserData&);
void init(QObject* widgetObject, QObject* qmlObject) {
widgetObject->setUserData(USER_DATA_ID, this);
qmlObject->setUserData(USER_DATA_ID, this);
@ -48,14 +47,6 @@ private:
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 };
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) {
f(_instance);
f(this);
}
queuedLambdas.clear();
}
VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) {
_instance = this;
this->setEnabled(false);
}
QObject* VrMenu::findMenuObject(const QString& menuOption) {
if (menuOption.isEmpty()) {
return _rootMenu;
@ -91,10 +81,6 @@ QObject* VrMenu::findMenuObject(const QString& menuOption) {
return result;
}
void VrMenu::setRootMenu(QObject* rootMenu) {
_rootMenu = rootMenu;
}
void updateQmlItemFromAction(QObject* target, QAction* source) {
target->setProperty("checkable", source->isCheckable());
target->setProperty("enabled", source->isEnabled());
@ -116,9 +102,8 @@ void VrMenu::addMenu(QMenu* menu) {
Q_ASSERT(false);
}
QVariant returnedValue;
bool invokeResult = QMetaObject::invokeMethod(this, "addMenu", Qt::DirectConnection,
bool invokeResult = QMetaObject::invokeMethod(qmlParent, "addMenu", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, QVariant::fromValue(qmlParent)),
Q_ARG(QVariant, QVariant::fromValue(menu->title())));
Q_ASSERT(invokeResult);
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()));
}
class QQuickMenuItem;
void VrMenu::addAction(QMenu* menu, QAction* action) {
Q_ASSERT(!MenuUserData::forObject(action));
Q_ASSERT(MenuUserData::forObject(menu));
MenuUserData* userData = MenuUserData::forObject(menu);
QObject* menuQml = findMenuObject(userData->uuid.toString());
Q_ASSERT(menuQml);
QVariant returnedValue;
QQuickMenuItem* returnedValue { nullptr };
bool invokeResult = QMetaObject::invokeMethod(this, "addItem", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, QVariant::fromValue(menuQml)),
Q_ARG(QVariant, QVariant::fromValue(action->text())));
qDebug() << menuQml;
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
Q_ARG(QString, action->text()));
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 = returnedValue.value<QObject*>();
QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();
Q_ASSERT(result);
// Bind the QML and Widget together
bindActionToQmlAction(result, action);
@ -175,19 +163,19 @@ void VrMenu::insertAction(QAction* before, QAction* action) {
beforeQml = findMenuObject(beforeUserData->uuid.toString());
}
QObject* menu = beforeQml->parent();
QVariant returnedValue;
bool invokeResult = QMetaObject::invokeMethod(this, "insertItem", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, QVariant::fromValue(menu)),
Q_ARG(QVariant, QVariant::fromValue(beforeQml)),
Q_ARG(QVariant, QVariant::fromValue(action->text())));
QQuickMenuItem* returnedValue { nullptr };
// FIXME this needs to find the index of the beforeQml item and call insertItem(int, object)
bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection,
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
Q_ARG(QString, action->text()));
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 = returnedValue.value<QObject*>();
QObject* result = reinterpret_cast<QObject*>(returnedValue); // returnedValue.value<QObject*>();
Q_ASSERT(result);
bindActionToQmlAction(result, action);
}
class QQuickMenuBase;
void VrMenu::removeAction(QAction* action) {
if (!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");
return;
}
QObject* item = findMenuObject(userData->uuid.toString());
QObject* menu = item->parent();
// Proxy QuickItem requests through the QML layer
bool invokeResult = QMetaObject::invokeMethod(this, "removeItem", Qt::DirectConnection,
Q_ARG(QVariant, QVariant::fromValue(menu)),
Q_ARG(QVariant, QVariant::fromValue(item)));
QQuickMenuBase* qmlItem = reinterpret_cast<QQuickMenuBase*>(item);
bool invokeResult = QMetaObject::invokeMethod(menu, "removeItem", Qt::DirectConnection,
Q_ARG(QQuickMenuBase*, qmlItem));
Q_ASSERT(invokeResult);
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
}

View file

@ -21,21 +21,16 @@
#include "OffscreenUi.h"
// 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
HIFI_QML_DECL_LAMBDA
public:
static void executeOrQueue(std::function<void(VrMenu*)> f);
static void executeQueuedLambdas();
VrMenu(QQuickItem* parent = nullptr);
VrMenu(QObject* parent = nullptr);
void addMenu(QMenu* menu);
void addAction(QMenu* parent, QAction* action);
void insertAction(QAction* before, QAction* action);
void removeAction(QAction* action);
void setRootMenu(QObject* rootMenu);
protected:
QObject* _rootMenu{ nullptr };
QObject* findMenuObject(const QString& name);

View file

@ -6,6 +6,8 @@ import Qt.labs.settings 1.0
import "../../../interface/resources/qml"
import "../../../interface/resources/qml/windows"
import "../../../interface/resources/qml/dialogs"
import "../../../interface/resources/qml/hifi"
import "../../../interface/resources/qml/hifi/dialogs"
ApplicationWindow {
id: appWindow
@ -36,7 +38,7 @@ ApplicationWindow {
return newListModel;
}
Root {
Desktop {
id: desktop
anchors.fill: parent
StubMenu { id: stubMenu }
@ -70,28 +72,6 @@ ApplicationWindow {
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 {
text: "Show Long Error"
onClicked: {
@ -152,6 +132,11 @@ ApplicationWindow {
console.log(appWindow.activeFocusItem);
}
}
Button {
text: "Preferences"
property var preferencesComponent: Component { PreferencesDialog { } }
onClicked: preferencesComponent.createObject(desktop);
}
}
Window {

View file

@ -80,5 +80,10 @@ DISTFILES += \
../../interface/resources/qml/VrMenu.qml \
../../interface/resources/qml/VrMenuItem.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