From 21534cbde08c6a18b85fcd8e8d3cef6745ff9b38 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 11 Jan 2016 22:04:05 -0800 Subject: [PATCH] Layout for running scripts --- interface/resources/qml/Browser.qml | 18 +- interface/resources/qml/Global.js | 3 +- interface/resources/qml/Root.qml | 16 +- .../resources/qml/dialogs/RunningScripts.qml | 205 +++++++ .../resources/qml/windows/DefaultFrame.qml | 93 ++++ interface/resources/qml/windows/Frame.qml | 8 + interface/resources/qml/windows/Window.qml | 181 +++++++ interface/src/Application.cpp | 33 +- interface/src/Application.h | 2 - interface/src/ui/RunningScriptsWidget.cpp | 222 -------- interface/src/ui/RunningScriptsWidget.h | 56 -- interface/ui/runningScriptsWidget.ui | 511 ------------------ libraries/ui/src/OffscreenUi.cpp | 32 +- libraries/ui/src/OffscreenUi.h | 1 + libraries/ui/src/QmlWindowClass.cpp | 18 +- 15 files changed, 550 insertions(+), 849 deletions(-) create mode 100644 interface/resources/qml/dialogs/RunningScripts.qml create mode 100644 interface/resources/qml/windows/DefaultFrame.qml create mode 100644 interface/resources/qml/windows/Frame.qml create mode 100644 interface/resources/qml/windows/Window.qml delete mode 100644 interface/src/ui/RunningScriptsWidget.cpp delete mode 100644 interface/src/ui/RunningScriptsWidget.h delete mode 100644 interface/ui/runningScriptsWidget.ui diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 8f3cd0e1e7..dc71eac1e7 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,18 +1,17 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtWebEngine 1.1 + import "controls" import "styles" +import "windows" -VrDialog { +Window { id: root HifiConstants { id: hifi } title: "Browser" resizable: true - contentImplicitWidth: clientArea.implicitWidth - contentImplicitHeight: clientArea.implicitHeight - backgroundColor: "#7f000000" - + destroyOnInvisible: true Component.onCompleted: { enabled = true @@ -27,13 +26,8 @@ VrDialog { } Item { - id: clientArea - implicitHeight: 600 - implicitWidth: 800 - x: root.clientX - y: root.clientY - width: root.clientWidth - height: root.clientHeight + width: 800 + height: 600 Rectangle { anchors.left: parent.left diff --git a/interface/resources/qml/Global.js b/interface/resources/qml/Global.js index d41dde3c61..fadf6d6c71 100644 --- a/interface/resources/qml/Global.js +++ b/interface/resources/qml/Global.js @@ -40,7 +40,8 @@ function getTopLevelWindows(item) { for (var i = 0; i < desktop.children.length; ++i) { var child = desktop.children[i]; - if (Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) { + if ((Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) || + child[Global.OFFSCREEN_WINDOW_OBJECT_NAME]) { var windowId = child.toString(); currentWindows.push(child) } diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 49262cb7db..247947b72c 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -1,13 +1,12 @@ -import Hifi 1.0 import QtQuick 2.5 import QtQuick.Controls 1.4 import "Global.js" as Global -// This is our primary 'window' object to which all dialogs and controls will -// be childed. -Root { - id: root +// This is our primary 'desktop' object to which all VR dialogs and +// windows will be childed. +Item { + id: desktop objectName: Global.OFFSCREEN_ROOT_OBJECT_NAME anchors.fill: parent; property var windows: []; @@ -16,11 +15,14 @@ Root { } onChildrenChanged: { - windows = Global.getTopLevelWindows(root); + windows = Global.getTopLevelWindows(desktop); } onParentChanged: { forceActiveFocus(); } -} + function raise(item) { + Global.raiseWindow(item); + } +} diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/dialogs/RunningScripts.qml new file mode 100644 index 0000000000..1b25823fa7 --- /dev/null +++ b/interface/resources/qml/dialogs/RunningScripts.qml @@ -0,0 +1,205 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import Qt.labs.settings 1.0 + +import "../styles" as Hifi +import "../controls" as HifiControls +import "../windows" + +Window { + id: root + objectName: "RunningScripts" + title: "Running Scripts" + resizable: true + destroyOnInvisible: true + enabled: false + x: 40; y: 40 + + property var scripts: ScriptDiscoveryService; + property var scriptsModel: scripts.scriptsModelFilter + property var runningScriptsModel: ListModel { } + + Settings { + category: "Overlay.RunningScripts" + property alias x: root.x + property alias y: root.y + } + + Connections { + target: ScriptDiscoveryService + onScriptCountChanged: updateRunningScripts(); + } + + Component.onCompleted: updateRunningScripts() + + function updateRunningScripts() { + var runningScripts = ScriptDiscoveryService.getRunning(); + runningScriptsModel.clear() + for (var i = 0; i < runningScripts.length; ++i) { + runningScriptsModel.append(runningScripts[i]); + } + } + + function loadScript(script) { + console.log("Load script " + script); + scripts.loadOneScript(script); + } + + function reloadScript(script) { + console.log("Reload script " + script); + scripts.stopScript(script, true); + } + + function stopScript(script) { + console.log("Stop script " + script); + scripts.stopScript(script); + } + + function reloadAll() { + console.log("Reload all scripts"); + scripts.reloadAllScripts(); + } + + function stopAll() { + console.log("Stop all scripts"); + scripts.stopAllScripts(); + } + + Rectangle { + color: "white" + implicitWidth: 384; implicitHeight: 640 + + Item { + anchors { fill: parent; margins: 8 } + Text { + id: title + font.bold: true + font.pointSize: 16 + color: "#0e7077" + text: "Currently Running" + } + + Row { + id: allButtons + anchors.top: title.bottom + anchors.topMargin: 8 + spacing: 8 + Button { text: "Reload all"; onClicked: reloadAll() } + Button { text: "Stop all"; onClicked: stopAll() } + } + + ListView { + clip: true + anchors { + top: allButtons.bottom; + left: parent.left; + right: parent.right; + topMargin: 8 + bottom: row1.top + bottomMargin: 8 + } + + model: runningScriptsModel + + delegate: Rectangle { + radius: 3 + anchors { left: parent.left; right: parent.right } + + height: scriptName.height + 12 + color: index % 2 ? "#ddd" : "#eee" + + Text { + anchors { left: parent.left; leftMargin: 4; verticalCenter: parent.verticalCenter } + id: scriptName + text: name + } + + Row { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 4 + spacing: 4 + HifiControls.FontAwesome { + text: "\uf021"; size: scriptName.height; + MouseArea { + anchors { fill: parent; margins: -2; } + onClicked: reloadScript(model.url) + } + } + HifiControls.FontAwesome { + size: scriptName.height; text: "\uf00d" + MouseArea { + anchors { fill: parent; margins: -2; } + onClicked: stopScript(model.url) + } + } + } + } + } + + + Text { + id: loadLabel + text: "Load Scripts" + font.bold: true + font.pointSize: 16 + color: "#0e7077" + + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.bottom: filterEdit.top + anchors.bottomMargin: 8 + } + + Row { + id: row1 + spacing: 8 + anchors.bottom: filterEdit.top + anchors.bottomMargin: 8 + anchors.right: parent.right + Button { text: "from URL" } + Button { text: "from Disk" } + } + + TextField { + id: filterEdit + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: treeView.top + anchors.bottomMargin: 8 + placeholderText: "filter" + onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$") + } + + TreeView { + id: treeView + height: 128 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + headerVisible: false + focus: true + onClicked: { + console.log("treeview clicked " + scriptsModel.data(index, 0x100)) + } + + onDoubleClicked: { + console.log("treeview double clicked" + scriptsModel.data(index, 0x100)) + isExpanded(index) ? collapse(index) : expand(index) + } + + onActivated: { + console.log("treeview activated!" + index) + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } + + model: scriptsModel + TableViewColumn { title: "Name"; role: "display"; } + } + } + } +} + diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml new file mode 100644 index 0000000000..d810ea8c77 --- /dev/null +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -0,0 +1,93 @@ +import QtQuick 2.5 + +import "." +import "../controls" + +Frame { + id: root + anchors { margins: -16; topMargin: parent.closable ? -32 : -16; } + + + MouseArea { + id: controlsArea + anchors.fill: desktopControls + hoverEnabled: true + drag.target: root.parent + propagateComposedEvents: true + onClicked: { + root.raise() + mouse.accepted = false; + } + + MouseArea { + id: sizeDrag + enabled: root.parent.resizable + property int startX + property int startY + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 16 + height: 16 + z: 1000 + hoverEnabled: true + onPressed: { + startX = mouseX + startY = mouseY + } + onPositionChanged: { + if (pressed) { + root.deltaSize((mouseX - startX), (mouseY - startY)) + startX = mouseX + startY = mouseY + } + } + } + } + + Rectangle { + id: desktopControls + // FIXME this doesn't work as expected + visible: root.parent.showFrame + anchors.fill: parent; + color: "#7f7f7f7f"; + radius: 3; + + + Row { + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 4 + anchors.topMargin: 4 + spacing: 4 + FontAwesome { + visible: false + text: "\uf08d" + style: Text.Outline; styleColor: "white" + size: 16 + rotation: !root.parent ? 90 : root.parent.pinned ? 0 : 90 + color: root.pinned ? "red" : "black" + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + onClicked: { root.pin(); mouse.accepted = false; } + } + } + + FontAwesome { + visible: root.parent.closable + text: closeClickArea.containsMouse ? "\uf057" : "\uf05c" + style: Text.Outline; + styleColor: "white" + color: closeClickArea.containsMouse ? "red" : "black" + size: 16 + MouseArea { + id: closeClickArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.close(); + } + } + } + } +} + diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml new file mode 100644 index 0000000000..cef2c5abc2 --- /dev/null +++ b/interface/resources/qml/windows/Frame.qml @@ -0,0 +1,8 @@ +import QtQuick 2.5 + +Item { + signal close() + signal pin() + signal raise() + signal deltaSize(real dx, real dy) +} diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml new file mode 100644 index 0000000000..2e1f0e24d5 --- /dev/null +++ b/interface/resources/qml/windows/Window.qml @@ -0,0 +1,181 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtGraphicalEffects 1.0 +import "." +import "../styles" + +FocusScope { + id: window + objectName: "topLevelWindow" + HifiConstants { id: hifi } + implicitHeight: frame.height + implicitWidth: frame.width + + property bool topLevelWindow: true + property string title + property bool showFrame: true + property bool closable: true + property bool destroyOnInvisible: false + property bool destroyOnCloseButton: true + property bool pinned: false + property bool resizable: false + property real minX: 320 + property real minY: 240; + default property var content + property var frame: DefaultFrame { anchors.fill: content } + property var blur: FastBlur { anchors.fill: content; source: content; visible: false; radius: 0} + //property var hoverDetector: MouseArea { anchors.fill: frame; hoverEnabled: true; propagateComposedEvents: true; } + //property bool mouseInWindow: hoverDetector.containsMouse + children: [ frame, content, blur ] + signal windowDestroyed(); + + QtObject { + id: d + property vector2d minPosition: Qt.vector2d(0, 0); + property vector2d maxPosition: Qt.vector2d(100, 100); + + function clamp(value, min, max) { + return Math.min(Math.max(value, min), max); + } + + function updateParentRect() { +// if (!frame) { return; } +// console.log(window.parent.width); +// console.log(frame.width); +// minPosition = Qt.vector2d(-frame.anchors.leftMargin, -frame.anchors.topMargin); +// maxPosition = Qt.vector2d( +// Math.max(minPosition.x, Desktop.width - frame.width + minPosition.x), +// Math.max(minPosition.y, Desktop.height - frame.height + minPosition.y)) +// console.log(maxPosition); + } + + function keepOnScreen() { + //window.x = clamp(x, minPosition.x, maxPosition.x); + //window.y = clamp(y, minPosition.y, maxPosition.y); + } + + onMinPositionChanged: keepOnScreen(); + onMaxPositionChanged: keepOnScreen(); + } + + Component.onCompleted: { + d.updateParentRect(); + raise(); + } + + Component.onDestruction: { + console.log("Destroyed " + window); + windowDestroyed(); + } + + onParentChanged: { + d.updateParentRect(); + raise(); + } + + onFrameChanged: d.updateParentRect(); + onWidthChanged: d.updateParentRect(); + onHeightChanged: d.updateParentRect(); + onXChanged: d.keepOnScreen(); + onYChanged: d.keepOnScreen(); + + Connections { + target: frame + onRaise: window.raise(); + onClose: window.close(); + onPin: window.pin(); + onDeltaSize: { + console.log("deltaSize") + content.width = Math.max(content.width + dx, minX) + content.height = Math.max(content.height + dy, minY) + } + } + + + function raise() { + if (enabled && parent) { + Desktop.raise(window) + if (!focus) { + focus = true; + } + } + } + + function pin() { + pinned = ! pinned + } + + // our close function performs the same way as the OffscreenUI class: + // don't do anything but manipulate the enabled flag and let the other + // mechanisms decide if the window should be destroyed after the close + // animation completes + function close() { + if (destroyOnCloseButton) { + destroyOnInvisible = true + } + enabled = false; + } + + onEnabledChanged: { + if (!enabled) { + if (blur) { + blur.visible = true; + } + if (content) { + content.visible = false; + } + } + + opacity = enabled ? 1.0 : 0.0 + // If the dialog is initially invisible, setting opacity doesn't + // trigger making it visible. + if (enabled) { + visible = true; + raise(); + } + } + + // The offscreen UI will enable an object, rather than manipulating it's + // visibility, so that we can do animations in both directions. Because + // visibility and enabled are boolean flags, they cannot be animated. So when + // enabled is change, we modify a property that can be animated, like scale or + // opacity, and then when the target animation value is reached, we can + // modify the visibility + + // The actual animator + Behavior on opacity { + NumberAnimation { + duration: hifi.effects.fadeInDuration + easing.type: Easing.OutCubic + } + } + + // Once we're transparent, disable the dialog's visibility + onOpacityChanged: { + visible = (opacity != 0.0); + if (opacity == 1.0) { + content.visible = true; + blur.visible = false; + } + } + + // Some dialogs should be destroyed when they become + // invisible, so handle that + onVisibleChanged: { + if (!visible && destroyOnInvisible) { + console.log("Destroying " + window); + destroy(); + } + } + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_W: + if (event.modifiers == Qt.ControlModifier) { + event.accepted = true + enabled = false + } + break; + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0b595cfd9..adcf5d305b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1194,7 +1194,10 @@ void Application::initializeUi() { offscreenUi->create(_offscreenContext->getContext()); offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - offscreenUi->load("Root.qml"); + // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to + // support the window management and scripting proxies for VR use + offscreenUi->createDesktop(); + // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); @@ -1227,8 +1230,6 @@ void Application::initializeUi() { #endif rootContext->setContextProperty("Overlays", &_overlays); - rootContext->setContextProperty("Desktop", DependencyManager::get().data()); - rootContext->setContextProperty("Window", DependencyManager::get().data()); rootContext->setContextProperty("Menu", MenuScriptingInterface::getInstance()); rootContext->setContextProperty("Stats", Stats::getInstance()); @@ -4530,18 +4531,20 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } void Application::toggleRunningScriptsWidget() { - if (_runningScriptsWidget->isVisible()) { - if (_runningScriptsWidget->hasFocus()) { - _runningScriptsWidget->hide(); - } else { - _runningScriptsWidget->raise(); - setActiveWindow(_runningScriptsWidget); - _runningScriptsWidget->setFocus(); - } - } else { - _runningScriptsWidget->show(); - _runningScriptsWidget->setFocus(); - } + static const QUrl url("dialogs/RunningScripts.qml"); + DependencyManager::get()->toggle(url, "RunningScripts"); + //if (_runningScriptsWidget->isVisible()) { + // if (_runningScriptsWidget->hasFocus()) { + // _runningScriptsWidget->hide(); + // } else { + // _runningScriptsWidget->raise(); + // setActiveWindow(_runningScriptsWidget); + // _runningScriptsWidget->setFocus(); + // } + //} else { + // _runningScriptsWidget->show(); + // _runningScriptsWidget->setFocus(); + //} } void Application::packageModel() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 9dd479bb5e..38b4a8845c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -61,7 +61,6 @@ #include "ui/OctreeStatsDialog.h" #include "ui/OverlayConductor.h" #include "ui/overlays/Overlays.h" -#include "ui/RunningScriptsWidget.h" #include "ui/SnapshotShareDialog.h" #include "ui/ToolWindow.h" #include "UndoStackScriptingInterface.h" @@ -467,7 +466,6 @@ private: TouchEvent _lastTouchEvent; - RunningScriptsWidget* _runningScriptsWidget { nullptr }; quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp deleted file mode 100644 index 1d2fe111f9..0000000000 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// -// RunningScriptsWidget.cpp -// interface/src/ui -// -// Created by Mohammed Nafees on 03/28/2014. -// Updated by Ryan Huffman on 05/13/2014. -// Copyright 2014 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 -// - -#include "ui_runningScriptsWidget.h" -#include "RunningScriptsWidget.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "Application.h" -#include "MainWindow.h" -#include "Menu.h" -#include "ScriptsModel.h" -#include "UIUtil.h" - -RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : - QWidget(parent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | - Qt::WindowCloseButtonHint), - ui(new Ui::RunningScriptsWidget), - _reloadSignalMapper(this), - _stopSignalMapper(this) { - ui->setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose, false); - - ui->filterLineEdit->installEventFilter(this); - - auto scriptEngines = DependencyManager::get().data(); - connect(scriptEngines->scriptsModelFilter(), &QSortFilterProxyModel::modelReset, - this, &RunningScriptsWidget::selectFirstInList); - - // FIXME: menu isn't prepared at this point. - //QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); - //ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); - ui->scriptTreeView->setModel(scriptEngines->scriptsModelFilter()); - - connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter); - connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList); - - connect(ui->reloadAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::reloadAllScripts); - connect(ui->stopAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::stopAllScripts); - connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, qApp, &Application::loadDialog); - connect(ui->loadScriptFromURLButton, &QPushButton::clicked, qApp, &Application::loadScriptURLDialog); - connect(&_reloadSignalMapper, static_cast(&QSignalMapper::mapped), - [scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName, true); }); - connect(&_stopSignalMapper, static_cast(&QSignalMapper::mapped), - [scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName); }); - - setRunningScripts(scriptEngines->getRunningScripts()); - connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this, scriptEngines] { - setRunningScripts(scriptEngines->getRunningScripts()); - }, Qt::QueuedConnection); - UIUtil::scaleWidgetFontSizes(this); -} - -RunningScriptsWidget::~RunningScriptsWidget() { - delete ui; -} - -void RunningScriptsWidget::updateFileFilter(const QString& filter) { - QRegExp regex("^.*" + QRegExp::escape(filter) + ".*$", Qt::CaseInsensitive); - DependencyManager::get()->scriptsModelFilter()->setFilterRegExp(regex); - selectFirstInList(); -} - -void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) { - auto scriptEngines = DependencyManager::get(); - QVariant scriptFile = scriptEngines->scriptsModelFilter()->data(index, ScriptsModel::ScriptPath); - scriptEngines->loadScript(scriptFile.toString()); -} - -void RunningScriptsWidget::loadSelectedScript() { - QModelIndex selectedIndex = ui->scriptTreeView->currentIndex(); - if (selectedIndex.isValid()) { - loadScriptFromList(selectedIndex); - } -} - -void RunningScriptsWidget::setRunningScripts(const QStringList& list) { - setUpdatesEnabled(false); - QLayoutItem* widget; - while ((widget = ui->scriptListWidget->layout()->takeAt(0)) != NULL) { - delete widget->widget(); - delete widget; - } - QHash hash; - const int CLOSE_ICON_HEIGHT = 12; - for (int i = 0; i < list.size(); i++) { - if (!hash.contains(list.at(i))) { - hash.insert(list.at(i), 1); - } - QWidget* row = new QWidget(ui->scriptListWidget); - row->setFont(ui->scriptListWidget->font()); - row->setLayout(new QHBoxLayout(row)); - - QUrl url = QUrl(list.at(i)); - QLabel* name = new QLabel(url.fileName(), row); - if (hash.find(list.at(i)).value() != 1) { - name->setText(name->text() + "(" + QString::number(hash.find(list.at(i)).value()) + ")"); - } - ++hash[list.at(i)]; - - QPushButton* reloadButton = new QPushButton(row); - reloadButton->setFlat(true); - reloadButton->setIcon( - QIcon(QPixmap(PathUtils::resourcesPath() + "images/reload-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT))); - reloadButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); - reloadButton->setStyleSheet("border: 0;"); - reloadButton->setCursor(Qt::PointingHandCursor); - connect(reloadButton, SIGNAL(clicked()), &_reloadSignalMapper, SLOT(map())); - _reloadSignalMapper.setMapping(reloadButton, url.toString()); - - QPushButton* closeButton = new QPushButton(row); - closeButton->setFlat(true); - closeButton->setIcon( - QIcon(QPixmap(PathUtils::resourcesPath() + "images/kill-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT))); - closeButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); - closeButton->setStyleSheet("border: 0;"); - closeButton->setCursor(Qt::PointingHandCursor); - connect(closeButton, SIGNAL(clicked()), &_stopSignalMapper, SLOT(map())); - _stopSignalMapper.setMapping(closeButton, url.toString()); - - row->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); - - row->layout()->setContentsMargins(4, 4, 4, 4); - row->layout()->setSpacing(0); - - row->layout()->addWidget(name); - row->layout()->addWidget(reloadButton); - row->layout()->addWidget(closeButton); - - row->setToolTip(url.toString()); - - QFrame* line = new QFrame(row); - line->setFrameShape(QFrame::HLine); - line->setStyleSheet("color: #E1E1E1; margin-left: 6px; margin-right: 6px;"); - - ui->scriptListWidget->layout()->addWidget(row); - ui->scriptListWidget->layout()->addWidget(line); - } - - - ui->noRunningScriptsLabel->setVisible(list.isEmpty()); - ui->reloadAllButton->setVisible(!list.isEmpty()); - ui->stopAllButton->setVisible(!list.isEmpty()); - - ui->scriptListWidget->updateGeometry(); - setUpdatesEnabled(true); - Application::processEvents(); - repaint(); -} - -void RunningScriptsWidget::showEvent(QShowEvent* event) { - if (!event->spontaneous()) { - ui->filterLineEdit->setFocus(); - } - - QRect parentGeometry = qApp->getDesirableApplicationGeometry(); - int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; - - setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin, - size().width(), parentWidget()->height() - topMargin); - - QWidget::showEvent(event); -} - -void RunningScriptsWidget::selectFirstInList() { - auto model = DependencyManager::get()->scriptsModelFilter(); - if (model->rowCount() > 0) { - ui->scriptTreeView->setCurrentIndex(model->index(0, 0)); - } -} - -bool RunningScriptsWidget::eventFilter(QObject* sender, QEvent* event) { - if (sender == ui->filterLineEdit) { - if (event->type() != QEvent::KeyPress) { - return false; - } - QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { - QModelIndex selectedIndex = ui->scriptTreeView->currentIndex(); - if (selectedIndex.isValid()) { - loadScriptFromList(selectedIndex); - } - event->accept(); - return true; - } - return false; - } - - return QWidget::eventFilter(sender, event); -} - -void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) { - if (keyEvent->key() == Qt::Key_Escape) { - return; - } else { - QWidget::keyPressEvent(keyEvent); - } -} - -void RunningScriptsWidget::allScriptsStopped() { - DependencyManager::get()->stopAllScripts(); -} diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h deleted file mode 100644 index b60586b5e7..0000000000 --- a/interface/src/ui/RunningScriptsWidget.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// RunningScriptsWidget.h -// interface/src/ui -// -// Created by Mohammed Nafees on 03/28/2014. -// Updated by Ryan Huffman on 05/13/2014. -// Copyright 2014 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 -// - -#ifndef hifi_RunningScriptsWidget_h -#define hifi_RunningScriptsWidget_h - -#include -#include -#include - -#include "ScriptsModel.h" -#include "ScriptsModelFilter.h" -#include "ScriptsTableWidget.h" - -namespace Ui { - class RunningScriptsWidget; -} - -class RunningScriptsWidget : public QWidget { - Q_OBJECT -public: - explicit RunningScriptsWidget(QWidget* parent = NULL); - ~RunningScriptsWidget(); - - void setRunningScripts(const QStringList& list); - -protected: - virtual bool eventFilter(QObject* sender, QEvent* event); - - virtual void keyPressEvent(QKeyEvent* event); - virtual void showEvent(QShowEvent* event); - -private slots: - void allScriptsStopped(); - void updateFileFilter(const QString& filter); - void loadScriptFromList(const QModelIndex& index); - void loadSelectedScript(); - void selectFirstInList(); - -private: - Ui::RunningScriptsWidget* ui; - QSignalMapper _reloadSignalMapper; - QSignalMapper _stopSignalMapper; - QVariantList getPublicChildNodes(TreeNodeFolder* parent); -}; - -#endif // hifi_RunningScriptsWidget_h diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui deleted file mode 100644 index d2484662c4..0000000000 --- a/interface/ui/runningScriptsWidget.ui +++ /dev/null @@ -1,511 +0,0 @@ - - - RunningScriptsWidget - - - - 0 - 0 - 364 - 728 - - - - - Helvetica,Arial,sans-serif - 13 - - - - Running Scripts - - - * { font-family: Helvetica, Arial, sans-serif; } - - - - - - - 0 - 0 - - - - - 0 - 141 - - - - - Helvetica,Arial,sans-serif - 13 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 6 - - - 0 - - - 6 - - - 0 - - - - - - Helvetica,Arial,sans-serif - 16 - 75 - false - true - - - - color: #0e7077; -font: bold 16pt; - - - - Currently Running - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 0 - 4 - - - - - - - - - 0 - 0 - - - - - Helvetica,Arial,sans-serif - 13 - - - - reloadStopButtonArea { padding: 0 } - - - - 6 - - - 0 - - - 6 - - - 0 - - - - - - Helvetica,Arial,sans-serif - 13 - - - - Reload all - - - - - - - - Helvetica,Arial,sans-serif - 13 - - - - Stop all - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - Helvetica,Arial,sans-serif - 13 - - - - - 0 - - - 6 - - - 0 - - - 6 - - - 0 - - - - - - 0 - 0 - - - - - Helvetica,Arial,sans-serif - 14 - - - - color: #5f5f5f; margin: 2px; - - - There are no scripts running. - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 0 - 0 - - - - - Helvetica,Arial,sans-serif - 14 - - - - Qt::LeftToRight - - - QFrame::NoFrame - - - 0 - - - Qt::ScrollBarAsNeeded - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 328 - 18 - - - - - 0 - 0 - - - - - Helvetica,Arial,sans-serif - 14 - - - - Qt::LeftToRight - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - Helvetica,Arial,sans-serif - 14 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - - - 6 - - - 6 - - - 6 - - - 6 - - - - - color: #0e7077; -font: bold 16pt; - - - Load Scripts - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - from URL - - - - - - - from Disk - - - - - - - - - - - 6 - - - 0 - - - 6 - - - 0 - - - - - - - - filter - - - true - - - - - - - - 0 - 0 - - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAlwaysOff - - - true - - - false - - - - - - - - - - - - - - diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 67e6b089d6..489fd87112 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -16,19 +16,14 @@ #include #include + #include "ErrorDialog.h" #include "MessageDialog.h" -class OffscreenUiRoot : public QQuickItem { - Q_OBJECT -public: - - OffscreenUiRoot(QQuickItem* parent = 0); - Q_INVOKABLE void information(const QString& title, const QString& text); - Q_INVOKABLE void loadChild(const QUrl& url) { - DependencyManager::get()->load(url); - } -}; +// Needs to match the constants in resources/qml/Global.js +static const QString OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot"; +static const QString OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow"; +static QQuickItem* _desktop { nullptr }; class OffscreenFlags : public QObject { Q_OBJECT @@ -105,15 +100,7 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { return false; } -OffscreenUiRoot::OffscreenUiRoot(QQuickItem* parent) : QQuickItem(parent) { -} - -void OffscreenUiRoot::information(const QString& title, const QString& text) { - OffscreenUi::information(title, text); -} - OffscreenUi::OffscreenUi() { - ::qmlRegisterType("Hifi", 1, 0, "Root"); } void OffscreenUi::create(QOpenGLContext* context) { @@ -258,4 +245,13 @@ void OffscreenUi::setNavigationFocused(bool focused) { offscreenFlags->setNavigationFocused(focused); } +void OffscreenUi::createDesktop() { + if (_desktop) { + qDebug() << "Desktop already created"; + } + _desktop = dynamic_cast(load("Root.qml")); + Q_ASSERT(_desktop); + getRootContext()->setContextProperty("Desktop", _desktop); +} + #include "OffscreenUi.moc" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 7063d25279..15d2871460 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -26,6 +26,7 @@ class OffscreenUi : public OffscreenQmlSurface, public Dependency { public: OffscreenUi(); virtual void create(QOpenGLContext* context) override; + void createDesktop(); void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); bool shouldSwallowShortcut(QEvent* event); diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 269ef6d6c9..0d5fc7fbf6 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -91,7 +91,7 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, const auto argumentCount = context->argumentCount(); QString url; QString title; - int width = 100, height = 100; + int width = -1, height = -1; bool visible = true; if (argumentCount > 1) { @@ -131,13 +131,19 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, url = QUrl::fromLocalFile(url).toString(); } - width = std::max(100, std::min(1280, width)); - height = std::max(100, std::min(720, height)); + if (width != -1 || height != -1) { + width = std::max(100, std::min(1280, width)); + height = std::max(100, std::min(720, height)); + } QmlWindowClass* retVal{ nullptr }; + auto offscreenUi = DependencyManager::get(); + qDebug() << "Clearing component cache"; + offscreenUi->getRootContext()->engine()->clearComponentCache(); + // Build the event bridge and wrapper on the main thread - QMetaObject::invokeMethod(DependencyManager::get().data(), "load", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection, Q_ARG(const QString&, qmlSource), Q_ARG(std::function, [&](QQmlContext* context, QObject* object) { setupServer(); @@ -147,7 +153,9 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, if (!title.isEmpty()) { retVal->setTitle(title); } - retVal->setSize(width, height); + if (width != -1 && height != -1) { + retVal->setSize(width, height); + } object->setProperty(SOURCE_PROPERTY, url); if (visible) { object->setProperty("enabled", true);