From 075bcba49f2fb88b542e2d5e688759a89093231c Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 9 Jan 2018 23:48:40 +0100 Subject: [PATCH 01/39] AddressBar trigger implemented. Fixed warnings on non existing context properties. Fixed warnings about non notifyable property --- interface/src/Menu.cpp | 2 +- interface/src/ui/AddressBarDialog.h | 2 +- interface/src/ui/DialogsManager.cpp | 22 ++++++++++++++++++++-- interface/src/ui/DialogsManager.h | 4 +++- interface/src/ui/overlays/Web3DOverlay.cpp | 7 +++++++ 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d295e96867..f7f9e5480a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -278,7 +278,7 @@ Menu::Menu() { // Navigate > Show Address Bar addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, - dialogsManager.data(), SLOT(showAddressBar())); + dialogsManager.data(), SLOT(toggleAddressBar())); // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here. auto locationBookmarks = DependencyManager::get(); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 66f208ca90..7806436774 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) - Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl CONSTANT) public: AddressBarDialog(QQuickItem* parent = nullptr); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ff2d4868df..310a4cc1cd 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -58,7 +58,7 @@ void DialogsManager::showAddressBar() { hmd->openTablet(); } qApp->setKeyboardFocusOverlay(hmd->getCurrentTabletScreenID()); - emit addressBarShown(true); + setAddressBarVisible(true); } void DialogsManager::hideAddressBar() { @@ -71,7 +71,7 @@ void DialogsManager::hideAddressBar() { hmd->closeTablet(); } qApp->setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - emit addressBarShown(false); + setAddressBarVisible(false); } void DialogsManager::showFeed() { @@ -157,6 +157,24 @@ void DialogsManager::hmdToolsClosed() { } } +void DialogsManager::toggleAddressBar() { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + + const bool addressBarLoaded = tablet->isPathLoaded(TABLET_ADDRESS_DIALOG); + + if (_addressBarVisible || addressBarLoaded) { + hideAddressBar(); + } else { + showAddressBar(); + } +} + +void DialogsManager::setAddressBarVisible(bool addressBarVisible) { + _addressBarVisible = addressBarVisible; + emit addressBarShown(_addressBarVisible); +} + void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 3a40b15a3b..f17ac39a7e 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -39,6 +39,7 @@ public: QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } + void setAddressBarVisible(bool addressBarVisible); public slots: void showAddressBar(); @@ -52,6 +53,7 @@ public slots: void hmdTools(bool showTools); void showDomainConnectionDialog(); void showTestingResults(); + void toggleAddressBar(); // Application Update void showUpdateDialog(); @@ -78,7 +80,7 @@ private: QPointer _octreeStatsDialog; QPointer _testingDialog; QPointer _domainConnectionDialog; - bool _closeAddressBar { false }; + bool _addressBarVisible { false }; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a5da5e99b6..cdf09ffe19 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -72,6 +72,13 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface); connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface); connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); + + //need to be intialized before Tablet 1st open + _webSurface = DependencyManager::get()->acquire(_url); + _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : From 24f1fc35460313449e37b0794af30b71c34e7cc3 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Wed, 10 Jan 2018 22:30:43 +0000 Subject: [PATCH 02/39] fix Ui keyboard navigation various fixes to allow some support for keyboard navigation in message boxes and dialogs --- .../resources/qml/controls-uit/Button.qml | 4 +- .../resources/qml/dialogs/FileDialog.qml | 2 +- .../resources/qml/dialogs/MessageDialog.qml | 42 ++++++++++++++----- .../resources/qml/dialogs/QueryDialog.qml | 38 +++++++++++++---- .../messageDialog/MessageDialogButton.qml | 17 ++++++-- .../scripting/WindowScriptingInterface.cpp | 6 +-- 6 files changed, 83 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 02c6181952..dde07f45ea 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -57,7 +57,7 @@ Original.Button { hifi.buttons.disabledColorStart[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { + } else if (control.hovered || control.focus) { hifi.buttons.hoveredColor[control.color] } else { hifi.buttons.colorStart[control.color] @@ -71,7 +71,7 @@ Original.Button { hifi.buttons.disabledColorFinish[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { + } else if (control.hovered || control.focus) { hifi.buttons.hoveredColor[control.color] } else { hifi.buttons.colorFinish[control.color] diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index b9633104d5..581ac37a74 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -704,7 +704,7 @@ ModalWindow { KeyNavigation.up: selectionType KeyNavigation.left: openButton KeyNavigation.right: fileTableView.contentItem - Keys.onReturnPressed: { canceled(); root.enabled = false } + Keys.onReturnPressed: { cancelAction.trigger() } } } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 40c5a01e15..79d4b009db 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -37,6 +37,16 @@ ModalWindow { return OffscreenUi.waitForMessageBoxResult(root); } + Keys.onRightPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + yesButton.forceActiveFocus() + } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + okButton.forceActiveFocus() + } + Keys.onTabPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + yesButton.forceActiveFocus() + } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + okButton.forceActiveFocus() + } property alias detailedText: detailedText.text property alias text: mainTextContainer.text property alias informativeText: informativeTextContainer.text @@ -47,7 +57,6 @@ ModalWindow { onIconChanged: updateIcon(); property int defaultButton: OriginalDialogs.StandardButton.NoButton; property int clickedButton: OriginalDialogs.StandardButton.NoButton; - focus: defaultButton === OriginalDialogs.StandardButton.NoButton property int titleWidth: 0 onTitleWidthChanged: d.resize(); @@ -134,16 +143,35 @@ ModalWindow { MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; } MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; } MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; } - MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; } + MessageDialogButton { + id: noButton + dialog: root + text: qsTr("No") + button: OriginalDialogs.StandardButton.No + KeyNavigation.left: yesButton + KeyNavigation.backtab: yesButton + } MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; } - MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; } + MessageDialogButton { + id: yesButton + dialog: root + text: qsTr("Yes") + button: OriginalDialogs.StandardButton.Yes + KeyNavigation.right: noButton + KeyNavigation.tab: noButton + } MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; } MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; } MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; } MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; } MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; } MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; } - MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; } + MessageDialogButton { + id: okButton + dialog: root + text: qsTr("OK") + button: OriginalDialogs.StandardButton.Ok + } Button { id: moreButton @@ -230,12 +258,6 @@ ModalWindow { event.accepted = true root.click(OriginalDialogs.StandardButton.Cancel) break - - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - root.click(root.defaultButton) - break } } } diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 9a38c3f0d6..a5ec9828e2 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -95,19 +95,19 @@ ModalWindow { TextField { id: textResult label: root.label - focus: items ? false : true visible: items ? false : true anchors { left: parent.left; right: parent.right; bottom: parent.bottom } + KeyNavigation.down: acceptButton + KeyNavigation.tab: acceptButton } ComboBox { id: comboBox label: root.label - focus: true visible: items ? true : false anchors { left: parent.left @@ -115,6 +115,8 @@ ModalWindow { bottom: parent.bottom } model: items ? items : [] + KeyNavigation.down: acceptButton + KeyNavigation.tab: acceptButton } } @@ -135,7 +137,6 @@ ModalWindow { Flow { id: buttons - focus: true spacing: hifi.dimensions.contentSpacing.x onHeightChanged: d.resize(); onWidthChanged: d.resize(); layoutDirection: Qt.RightToLeft @@ -145,8 +146,21 @@ ModalWindow { margins: 0 bottomMargin: hifi.dimensions.contentSpacing.y } - Button { action: cancelAction } - Button { action: acceptAction } + Button { + id: cancelButton + action: cancelAction + KeyNavigation.left: acceptButton + KeyNavigation.up: items ? comboBox : textResult + KeyNavigation.backtab: acceptButton + } + Button { + id: acceptButton + action: acceptAction + KeyNavigation.right: cancelButton + KeyNavigation.up: items ? comboBox : textResult + KeyNavigation.tab: cancelButton + KeyNavigation.backtab: items ? comboBox : textResult + } } Action { @@ -184,7 +198,13 @@ ModalWindow { case Qt.Key_Return: case Qt.Key_Enter: - acceptAction.trigger() + if (acceptButton.focus) { + acceptAction.trigger() + } else if(cancelButton.focus) { + cancelAction.trigger() + } else if(comboBox.focus || comboBox.popup.focus) { + comboBox.showList() + } event.accepted = true; break; } @@ -194,6 +214,10 @@ ModalWindow { keyboardEnabled = HMD.active; updateIcon(); d.resize(); - textResult.forceActiveFocus(); + if (items) { + comboBox.forceActiveFocus() + } else { + textResult.forceActiveFocus() + } } } diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index b7ff9354eb..bdb42aba61 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -16,10 +16,21 @@ import "../../controls-uit" Button { property var dialog; - property int button: StandardButton.NoButton; + property int button: StandardButton.Ok; - color: dialog.defaultButton === button ? hifi.buttons.blue : hifi.buttons.white - focus: dialog.defaultButton === button + color: focus ? hifi.buttons.blue : hifi.buttons.white onClicked: dialog.click(button) visible: dialog.buttons & button + Keys.onPressed: { + if (!focus) { + return + } + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + dialog.click(button) + break + } + } } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4b355653b6..300297a074 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -103,14 +103,14 @@ void WindowScriptingInterface::raiseMainWindow() { /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue void WindowScriptingInterface::alert(const QString& message) { - OffscreenUi::asyncWarning("", message); + OffscreenUi::asyncWarning("", message, QMessageBox::Ok, QMessageBox::Ok); } /// Display a confirmation box with the options 'Yes' and 'No' /// \param const QString& message message to display /// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise QScriptValue WindowScriptingInterface::confirm(const QString& message) { - return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No))); + return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))); } /// Display a prompt with a text box @@ -353,7 +353,7 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); } -/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid +/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid /// directory the browser will start at the root directory. /// \param const QString& title title of the window /// \param const QString& directory directory to start the asset browser at From cede8ed597d0b096eda2b9bfe502972a8f622f87 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Thu, 18 Jan 2018 19:56:55 +0000 Subject: [PATCH 03/39] add focused color keyboard navigation uses focusedColor --- interface/resources/qml/controls-uit/Button.qml | 8 ++++++-- interface/resources/qml/dialogs/MessageDialog.qml | 8 ++++---- interface/resources/qml/dialogs/QueryDialog.qml | 4 ++-- interface/resources/qml/styles-uit/HifiConstants.qml | 1 + 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index dde07f45ea..e4360f8cf5 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -57,8 +57,10 @@ Original.Button { hifi.buttons.disabledColorStart[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered || control.focus) { + } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorStart[control.color] } @@ -71,8 +73,10 @@ Original.Button { hifi.buttons.disabledColorFinish[control.colorScheme] } else if (control.pressed) { hifi.buttons.pressedColor[control.color] - } else if (control.hovered || control.focus) { + } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorFinish[control.color] } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 79d4b009db..351df6dc8a 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -37,14 +37,14 @@ ModalWindow { return OffscreenUi.waitForMessageBoxResult(root); } - Keys.onRightPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onRightPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { yesButton.forceActiveFocus() - } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { okButton.forceActiveFocus() } - Keys.onTabPressed: if(defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onTabPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { yesButton.forceActiveFocus() - } else if(defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { okButton.forceActiveFocus() } property alias detailedText: detailedText.text diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index a5ec9828e2..b5de5362f2 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -200,9 +200,9 @@ ModalWindow { case Qt.Key_Enter: if (acceptButton.focus) { acceptAction.trigger() - } else if(cancelButton.focus) { + } else if (cancelButton.focus) { cancelAction.trigger() - } else if(comboBox.focus || comboBox.popup.focus) { + } else if (comboBox.focus || comboBox.popup.focus) { comboBox.showList() } event.accepted = true; diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cf800cb62e..5da587ea57 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -219,6 +219,7 @@ Item { readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow] From faf18fcaa89f58cf6b7b3826aa28c7919d4a19f2 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 01:49:24 +0000 Subject: [PATCH 04/39] improve file dialog navigation --- .../resources/qml/dialogs/FileDialog.qml | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 581ac37a74..42d6bf4261 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -102,7 +102,17 @@ ModalWindow { } }); - fileTableView.forceActiveFocus(); + focusTimer.start(); + } + + Timer { + id: focusTimer + interval: 10 + running: false + repeat: false + onTriggered: { + fileTableView.contentItem.forceActiveFocus(); + } } Item { @@ -122,7 +132,9 @@ ModalWindow { drag.target: root onClicked: { d.clearSelection(); - frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden. + // Defocus text field so that the keyboard gets hidden. + // Clicking also breaks keyboard navigation apart from backtabbing to cancel + frame.forceActiveFocus(); } } @@ -142,6 +154,12 @@ ModalWindow { size: 30 enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } GlyphButton { @@ -152,6 +170,12 @@ ModalWindow { width: height enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } } @@ -220,9 +244,15 @@ ModalWindow { d.currentSelectionUrl = helper.pathToUrl(currentText); } fileTableModel.folder = folder; - fileTableView.forceActiveFocus(); } } + + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: fileTableView.contentItem + KeyNavigation.tab: fileTableView.contentItem + KeyNavigation.backtab: fileTableView.contentItem + KeyNavigation.left: fileTableView.contentItem + KeyNavigation.right: fileTableView.contentItem } QtObject { @@ -638,6 +668,8 @@ ModalWindow { break; } } + + KeyNavigation.tab: root.saveDialog ? currentSelection : openButton } TextField { @@ -654,6 +686,10 @@ ModalWindow { activeFocusOnTab: !readOnly onActiveFocusChanged: if (activeFocus) { selectAll(); } onAccepted: okAction.trigger(); + KeyNavigation.up: fileTableView.contentItem + KeyNavigation.down: openButton + KeyNavigation.tab: openButton + KeyNavigation.backtab: fileTableView.contentItem } FileTypeSelection { @@ -664,8 +700,6 @@ ModalWindow { right: parent.right } visible: !selectDirectory && filtersCount > 1 - KeyNavigation.left: fileTableView - KeyNavigation.right: openButton } Keyboard { @@ -693,18 +727,18 @@ ModalWindow { color: hifi.buttons.blue action: okAction Keys.onReturnPressed: okAction.trigger() - KeyNavigation.up: selectionType - KeyNavigation.left: selectionType KeyNavigation.right: cancelButton + KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem + KeyNavigation.tab: cancelButton } Button { id: cancelButton action: cancelAction - KeyNavigation.up: selectionType - KeyNavigation.left: openButton - KeyNavigation.right: fileTableView.contentItem Keys.onReturnPressed: { cancelAction.trigger() } + KeyNavigation.left: openButton + KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem + KeyNavigation.backtab: openButton } } From e69fdba6ad5879dadc2340b276a40383c40427a6 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:18:29 +0000 Subject: [PATCH 05/39] add padding to sorting glyph the file dialog sorting glyphs are clipping on the right margin for me, adding 10 extra pixels fixes it --- interface/resources/qml/controls-uit/Table.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 1443cd5d6e..a3e4113d08 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -93,7 +93,7 @@ TableView { size: hifi.fontSizes.tableHeadingIcon anchors { left: titleText.right - leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 5 : 0) + leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 15 : 10) right: parent.right rightMargin: hifi.dimensions.tablePadding verticalCenter: titleText.verticalCenter From 44740d5716cba750ff515910f1836833e4bf423a Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:27:49 +0000 Subject: [PATCH 06/39] add sound on button focus changed Feels more consistent with hovering --- interface/resources/qml/controls-uit/Button.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index e4360f8cf5..926e9c4fe5 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -32,6 +32,12 @@ Original.Button { Tablet.playSound(TabletEnums.ButtonHover); } } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } onClicked: { Tablet.playSound(TabletEnums.ButtonClick); From f1147948cb50ef3a28f2c4ff56a44723cd358699 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:28:27 +0000 Subject: [PATCH 07/39] add focused color to GlyphButton --- interface/resources/qml/controls-uit/GlyphButton.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index 9c23171ee1..024c131a09 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -31,6 +31,12 @@ Original.Button { } } + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + onClicked: { Tablet.playSound(TabletEnums.ButtonClick); } @@ -50,6 +56,8 @@ Original.Button { hifi.buttons.pressedColor[control.color] } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorStart[control.color] } @@ -64,6 +72,8 @@ Original.Button { hifi.buttons.pressedColor[control.color] } else if (control.hovered) { hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] } else { hifi.buttons.colorFinish[control.color] } From 4c27538fa2ad39f82322d2038fc0e514f75174f8 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 19 Jan 2018 12:30:00 +0000 Subject: [PATCH 08/39] change file dialog keyboard navigation Allowed navigating with keyboard to home and up buttons in file dialog. --- .../resources/qml/dialogs/FileDialog.qml | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 42d6bf4261..145c0b2a30 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -154,12 +154,11 @@ ModalWindow { size: 30 enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: fileTableView.contentItem - KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: fileTableView.contentItem - KeyNavigation.left: fileTableView.contentItem - KeyNavigation.right: fileTableView.contentItem + Keys.onReturnPressed: { d.navigateUp(); } + KeyNavigation.tab: homeButton + KeyNavigation.backtab: upButton + KeyNavigation.left: upButton + KeyNavigation.right: homeButton } GlyphButton { @@ -170,12 +169,10 @@ ModalWindow { width: height enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: fileTableView.contentItem + Keys.onReturnPressed: { d.navigateHome(); } KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: fileTableView.contentItem - KeyNavigation.left: fileTableView.contentItem - KeyNavigation.right: fileTableView.contentItem + KeyNavigation.backtab: upButton + KeyNavigation.left: upButton } } @@ -505,7 +502,6 @@ ModalWindow { } headerVisible: !selectDirectory onDoubleClicked: navigateToRow(row); - focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); @@ -582,7 +578,7 @@ ModalWindow { resizable: true } TableViewColumn { - id: fileMofifiedColumn + id: fileModifiedColumn role: "fileModified" title: "Date" width: 0.3 * fileTableView.width @@ -593,7 +589,7 @@ ModalWindow { TableViewColumn { role: "fileSize" title: "Size" - width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width + width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width movable: false resizable: true visible: !selectDirectory @@ -668,7 +664,7 @@ ModalWindow { break; } } - + KeyNavigation.tab: root.saveDialog ? currentSelection : openButton } From 0f7f58417bc2ffbaa1a31e918a1b5b928edb0fbf Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 19 Jan 2018 19:11:12 +0100 Subject: [PATCH 09/39] JS scripting console auto-complete --- interface/CMakeLists.txt | 10 + interface/src/ui/JSConsole.cpp | 279 ++++++++++++++---- interface/src/ui/JSConsole.h | 16 +- .../src/UsersScriptingInterface.h | 2 +- tools/jsdoc/config.json | 5 +- tools/jsdoc/package.json | 7 + tools/jsdoc/plugins/hifi.js | 8 + tools/jsdoc/plugins/hifiJSONExport.js | 14 + 8 files changed, 285 insertions(+), 56 deletions(-) create mode 100644 tools/jsdoc/package.json create mode 100644 tools/jsdoc/plugins/hifiJSONExport.js diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 21225756b4..fbc40f70c2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -349,6 +349,16 @@ endif() add_bugsplat() +# generate the JSDoc JSON for the JSConsole auto-completer +add_custom_command(TARGET ${TARGET_NAME} #POST_BUILD + COMMAND npm install + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc +) +add_custom_command(TARGET ${TARGET_NAME} + COMMAND node_modules/.bin/jsdoc . -c config.json + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc +) + if (WIN32) set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index c5f8b54ebd..7f68a205e6 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -12,10 +12,11 @@ #include "JSConsole.h" #include -#include #include #include #include +#include +#include #include #include @@ -61,12 +62,64 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } +void JSConsole::readAPI() { + QFile file(PathUtils::resourcesPath() + "auto-complete/export.json"); + file.open(QFile::ReadOnly); + auto json = QTextStream(&file).readAll().toUtf8(); + _apiDocs = QJsonDocument::fromJson(json).array(); +} + +QStandardItem* getAutoCompleteItem(QJsonValue propertyObject) { + auto propertyItem = new QStandardItem(propertyObject.toObject().value("name").toString()); + propertyItem->setData(propertyObject.toVariant()); + return propertyItem; +} + +QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { + QString memberOfProperty = nullptr; + + auto model = new QStandardItemModel(this); + + if (memberOf != nullptr) { + foreach(auto doc, _apiDocs) { + auto object = doc.toObject(); + if (object.value("name").toString() == memberOf && object.value("scope").toString() == "global" && + object.value("kind").toString() == "namespace") { + + memberOfProperty = object.value("longname").toString(); + + auto properties = doc.toObject().value("properties").toArray(); + foreach(auto propertyObject, properties) { + model->appendRow(getAutoCompleteItem(propertyObject)); + } + } + } + if (memberOfProperty == nullptr) { + return nullptr; + } + } + + foreach(auto doc, _apiDocs) { + auto object = doc.toObject(); + auto scope = object.value("scope"); + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { + model->appendRow(getAutoCompleteItem(doc)); + } + } + model->sort(0); + return model; +} + JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), - _commandHistory(_readLines(_savedHistoryFilename)) { + _commandHistory(_readLines(_savedHistoryFilename)), + _completer(new QCompleter(this)) { + + readAPI(); + _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -78,38 +131,75 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : setStyleSheet(styleSheet.readAll()); } - connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom())); - connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput())); + connect(_ui->scrollArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, &JSConsole::scrollToBottom); + connect(_ui->promptTextEdit, &QTextEdit::textChanged, this, &JSConsole::resizeTextInput); + + _completer->setWidget(_ui->promptTextEdit); + _completer->setModel(getAutoCompleteModel(nullptr)); + _completer->setModelSorting(QCompleter::CaseSensitivelySortedModel); + _completer->setMaxVisibleItems(12); + _completer->setFilterMode(Qt::MatchStartsWith); + _completer->setWrapAround(false); + _completer->setCompletionMode(QCompleter::PopupCompletion); + _completer->setCaseSensitivity(Qt::CaseSensitive); + + QListView *listView = new QListView(); + listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView->setSelectionBehavior(QAbstractItemView::SelectRows); + listView->setSelectionMode(QAbstractItemView::SingleSelection); + listView->setModelColumn(_completer->completionColumn()); + + _completer->setPopup(listView); + _completer->popup()->installEventFilter(this); + QObject::connect(_completer, static_cast(&QCompleter::activated), this, + &JSConsole::insertCompletion); + + QObject::connect(_completer, static_cast(&QCompleter::highlighted), this, + &JSConsole::highlightedCompletion); setScriptEngine(scriptEngine); resizeTextInput(); - connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished())); + connect(&_executeWatcher, &QFutureWatcher::finished, this, &JSConsole::commandFinished); +} + +void JSConsole::insertCompletion(const QModelIndex& completion) { + auto completionString = completion.data().toString(); + QTextCursor tc = _ui->promptTextEdit->textCursor(); + int extra = completionString.length() - _completer->completionPrefix().length(); + tc.movePosition(QTextCursor::Left); + tc.movePosition(QTextCursor::EndOfWord); + tc.insertText(completionString.right(extra)); + _ui->promptTextEdit->setTextCursor(tc); +} + +void JSConsole::highlightedCompletion(const QModelIndex& completion) { + qDebug() << "Highlighted " << completion.data().toString(); + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); + + // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); } JSConsole::~JSConsole() { if (_scriptEngine) { - disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + disconnect(_scriptEngine.data(), nullptr, this, nullptr); _scriptEngine.reset(); } delete _ui; } void JSConsole::setScriptEngine(const ScriptEnginePointer& scriptEngine) { - if (_scriptEngine == scriptEngine && scriptEngine != NULL) { + if (_scriptEngine == scriptEngine && scriptEngine != nullptr) { return; } - if (_scriptEngine != NULL) { - disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + if (_scriptEngine != nullptr) { + disconnect(_scriptEngine.data(), nullptr, this, nullptr); _scriptEngine.reset(); } - // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine + // if scriptEngine is nullptr then create one and keep track of it using _ownScriptEngine if (scriptEngine.isNull()) { _scriptEngine = DependencyManager::get()->loadScript(_consoleFileName, false); } else { @@ -199,45 +289,132 @@ void JSConsole::showEvent(QShowEvent* event) { } bool JSConsole::eventFilter(QObject* sender, QEvent* event) { - if (sender == _ui->promptTextEdit) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); - int key = keyEvent->key(); + if ((sender == _ui->promptTextEdit || sender == _completer->popup()) && event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + int key = keyEvent->key(); - if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { - if (keyEvent->modifiers() & Qt::ShiftModifier) { - // If the shift key is being used then treat it as a regular return/enter. If this isn't done, - // a new QTextBlock isn't created. - keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier); - } else { - QString command = _ui->promptTextEdit->toPlainText().replace("\r\n","\n").trimmed(); - - if (!command.isEmpty()) { - QTextCursor cursor = _ui->promptTextEdit->textCursor(); - _ui->promptTextEdit->clear(); - - executeCommand(command); - } - - return true; - } - } else if (key == Qt::Key_Down) { - // Go to the next command in history if the cursor is at the last line of the current command. - int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); - int blockCount = _ui->promptTextEdit->document()->blockCount(); - if (blockNumber == blockCount - 1) { - setToNextCommandInHistory(); - return true; - } - } else if (key == Qt::Key_Up) { - // Go to the previous command in history if the cursor is at the first line of the current command. - int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); - if (blockNumber == 0) { - setToPreviousCommandInHistory(); - return true; - } + if (_completer->popup()->isVisible()) { + // The following keys are forwarded by the completer to the widget + switch (key) { + case Qt::Key_Space: + case Qt::Key_Enter: + case Qt::Key_Return: + insertCompletion(_completer->currentIndex()); + _completer->popup()->hide(); + return true; + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Backtab: + qDebug() << "test"; + keyEvent->ignore();//setAccepted(false); + return false; // let the completer do default behavior + default: + return false; } } + + if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { + if (keyEvent->modifiers() & Qt::ShiftModifier) { + // If the shift key is being used then treat it as a regular return/enter. If this isn't done, + // a new QTextBlock isn't created. + keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier); + } else { + QString command = _ui->promptTextEdit->toPlainText().replace("\r\n", "\n").trimmed(); + + if (!command.isEmpty()) { + QTextCursor cursor = _ui->promptTextEdit->textCursor(); + _ui->promptTextEdit->clear(); + + executeCommand(command); + } + + return true; + } + } else if (key == Qt::Key_Down) { + // Go to the next command in history if the cursor is at the last line of the current command. + int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); + int blockCount = _ui->promptTextEdit->document()->blockCount(); + if (blockNumber == blockCount - 1) { + setToNextCommandInHistory(); + return true; + } + } else if (key == Qt::Key_Up) { + // Go to the previous command in history if the cursor is at the first line of the current command. + int blockNumber = _ui->promptTextEdit->textCursor().blockNumber(); + if (blockNumber == 0) { + setToPreviousCommandInHistory(); + return true; + } + } + } else if ((sender == _ui->promptTextEdit || sender == _completer->popup()) && event->type() == QEvent::KeyRelease) { + QKeyEvent* keyEvent = static_cast(event); + int key = keyEvent->key(); + + // completer shortcut (CTRL + SPACE) + bool isCompleterShortcut = ((keyEvent->modifiers() & Qt::ControlModifier) && key == Qt::Key_Space) || + key == Qt::Key_Period; + if (_completer->popup()->isVisible() || isCompleterShortcut) { + + const bool ctrlOrShift = keyEvent->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); + if (ctrlOrShift && keyEvent->text().isEmpty()) { + return false; + } + + static QString eow("~!@#$%^&*()+{}|:\"<>?,/;'[]\\-="); // end of word + bool hasModifier = (keyEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift; + + + if (!isCompleterShortcut && (!keyEvent->text().isEmpty() && eow.contains(keyEvent->text().right(1)))) { + qDebug() << "eow contains " << keyEvent->text().right(1) << " full text: " << keyEvent->text(); + _completer->popup()->hide(); + return false; + } + qDebug() << "auto completing"; + + auto textCursor = _ui->promptTextEdit->textCursor(); + + textCursor.select(QTextCursor::WordUnderCursor); + + QString completionPrefix = textCursor.selectedText(); + + auto leftOfCursor = _ui->promptTextEdit->toPlainText().left(textCursor.position()); + qDebug() << "leftOfCursor" << leftOfCursor; + + // RegEx [3] [4] + // (Module.subModule).(property/subModule) + + const int MODULE_INDEX = 3; + const int PROPERTY_INDEX = 4; + // TODO: disallow invalid characters on left of property + QRegExp regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); + int pos = regExp.indexIn(leftOfCursor); + auto rexExpCapturedTexts = regExp.capturedTexts(); + auto memberOf = rexExpCapturedTexts[MODULE_INDEX]; + completionPrefix = rexExpCapturedTexts[PROPERTY_INDEX]; + bool switchedModule = false; + if (memberOf != _completerModule) { + _completerModule = memberOf; + auto autoCompleteModel = getAutoCompleteModel(memberOf); + if (autoCompleteModel == nullptr) { + _completer->popup()->hide(); + return false; + } + _completer->setModel(autoCompleteModel); + _completer->popup()->installEventFilter(this); + switchedModule = true; + } + + if (switchedModule || completionPrefix != _completer->completionPrefix()) { + _completer->setCompletionPrefix(completionPrefix); + qDebug() << "Set completion prefix to:" << completionPrefix; + _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0)); + } + auto cursorRect = _ui->promptTextEdit->cursorRect(); + cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + + _completer->popup()->verticalScrollBar()->sizeHint().width()); + _completer->complete(cursorRect); + return false; + } } return false; } @@ -321,7 +498,7 @@ void JSConsole::appendMessage(const QString& gutter, const QString& message) { void JSConsole::clear() { QLayoutItem* item; - while ((item = _ui->logArea->layout()->takeAt(0)) != NULL) { + while ((item = _ui->logArea->layout()->takeAt(0)) != nullptr) { delete item->widget(); delete item; } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 4b6409a76f..eeb3601886 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -12,12 +12,11 @@ #ifndef hifi_JSConsole_h #define hifi_JSConsole_h -#include -#include #include #include -#include #include +#include +#include #include "ui_console.h" #include "ScriptEngine.h" @@ -54,12 +53,20 @@ protected slots: void handleError(const QString& message, const QString& scriptName); void commandFinished(); +private slots: + void insertCompletion(const QModelIndex& completion); + void highlightedCompletion(const QModelIndex& completion); + private: void appendMessage(const QString& gutter, const QString& message); void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); + void readAPI(); + + QStandardItemModel* getAutoCompleteModel(const QString& memberOf = nullptr); + QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; @@ -68,6 +75,9 @@ private: QString _rootCommand; ScriptEnginePointer _scriptEngine; static const QString _consoleFileName; + QJsonArray _apiDocs; + QCompleter* _completer; + QString _completerModule {""}; }; diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 2d33bbca14..a4e741a797 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -150,7 +150,7 @@ signals: /**jsdoc * Notifies scripts that a user has disconnected from the domain - * @function Users.avatar.avatarDisconnected + * @function Users.avatarDisconnected * @param {nodeID} NodeID The session ID of the avatar that has disconnected */ void avatarDisconnected(const QUuid& nodeID); diff --git a/tools/jsdoc/config.json b/tools/jsdoc/config.json index 0fb833d015..a24e248661 100644 --- a/tools/jsdoc/config.json +++ b/tools/jsdoc/config.json @@ -4,5 +4,8 @@ "outputSourceFiles": false } }, - "plugins": ["plugins/hifi"] + "plugins": [ + "plugins/hifi", + "plugins/hifiJSONExport" + ] } diff --git a/tools/jsdoc/package.json b/tools/jsdoc/package.json new file mode 100644 index 0000000000..215ceec177 --- /dev/null +++ b/tools/jsdoc/package.json @@ -0,0 +1,7 @@ +{ + "name": "hifiJSDoc", + "dependencies": { + "jsdoc": "^3.5.5" + }, + "private": true +} diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 3af5fbeee3..afa3285c37 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -47,5 +47,13 @@ exports.handlers = { } }); }); + + fs.writeFile("out/hifiJSDoc.js", e.source, function(err) { + if (err) { + return console.log(err); + } + + console.log("The Hifi JSDoc JS was saved!"); + }); } }; diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js new file mode 100644 index 0000000000..7948fd2673 --- /dev/null +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -0,0 +1,14 @@ +exports.handlers = { + processingComplete: function(e) { + var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); + const fs = require('fs'); + doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); + fs.writeFile("out/hifiJSDoc.json", JSON.stringify(doclets, null, 4), function(err) { + if (err) { + return console.log(err); + } + + console.log("The Hifi JSDoc JSON was saved!"); + }); + } +}; From a31fe7546ad2500a2544fc694697a92634c3c8bd Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 19 Jan 2018 19:45:25 +0100 Subject: [PATCH 10/39] - remove debug - fix API export JSON filename - fix warnings --- interface/src/ui/JSConsole.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 7f68a205e6..3100b47882 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -63,7 +63,7 @@ void _writeLines(const QString& filename, const QList& lines) { } void JSConsole::readAPI() { - QFile file(PathUtils::resourcesPath() + "auto-complete/export.json"); + QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); file.open(QFile::ReadOnly); auto json = QTextStream(&file).readAll().toUtf8(); _apiDocs = QJsonDocument::fromJson(json).array(); @@ -302,18 +302,12 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { insertCompletion(_completer->currentIndex()); _completer->popup()->hide(); return true; - case Qt::Key_Escape: - case Qt::Key_Tab: - case Qt::Key_Backtab: - qDebug() << "test"; - keyEvent->ignore();//setAccepted(false); - return false; // let the completer do default behavior default: return false; } } - if ((key == Qt::Key_Return || key == Qt::Key_Enter)) { + if (key == Qt::Key_Return || key == Qt::Key_Enter) { if (keyEvent->modifiers() & Qt::ShiftModifier) { // If the shift key is being used then treat it as a regular return/enter. If this isn't done, // a new QTextBlock isn't created. @@ -361,15 +355,11 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { } static QString eow("~!@#$%^&*()+{}|:\"<>?,/;'[]\\-="); // end of word - bool hasModifier = (keyEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift; - if (!isCompleterShortcut && (!keyEvent->text().isEmpty() && eow.contains(keyEvent->text().right(1)))) { - qDebug() << "eow contains " << keyEvent->text().right(1) << " full text: " << keyEvent->text(); _completer->popup()->hide(); return false; } - qDebug() << "auto completing"; auto textCursor = _ui->promptTextEdit->textCursor(); @@ -378,7 +368,6 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { QString completionPrefix = textCursor.selectedText(); auto leftOfCursor = _ui->promptTextEdit->toPlainText().left(textCursor.position()); - qDebug() << "leftOfCursor" << leftOfCursor; // RegEx [3] [4] // (Module.subModule).(property/subModule) @@ -387,7 +376,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { const int PROPERTY_INDEX = 4; // TODO: disallow invalid characters on left of property QRegExp regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); - int pos = regExp.indexIn(leftOfCursor); + regExp.indexIn(leftOfCursor); auto rexExpCapturedTexts = regExp.capturedTexts(); auto memberOf = rexExpCapturedTexts[MODULE_INDEX]; completionPrefix = rexExpCapturedTexts[PROPERTY_INDEX]; @@ -399,14 +388,13 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { _completer->popup()->hide(); return false; } - _completer->setModel(autoCompleteModel); + _completer->setModel(autoCompleteModel); _completer->popup()->installEventFilter(this); switchedModule = true; } if (switchedModule || completionPrefix != _completer->completionPrefix()) { _completer->setCompletionPrefix(completionPrefix); - qDebug() << "Set completion prefix to:" << completionPrefix; _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0)); } auto cursorRect = _ui->promptTextEdit->cursorRect(); From 483074b11e2dfd80f144e74aa0287c623c7d401d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 17:59:01 +0100 Subject: [PATCH 11/39] jsdoc cmake fixes --- interface/CMakeLists.txt | 19 +++++++++---------- tools/CMakeLists.txt | 4 +++- tools/jsdoc/CMakeLists.txt | 14 ++++++++++++++ tools/jsdoc/plugins/hifi.js | 9 ++++++--- tools/jsdoc/plugins/hifiJSONExport.js | 4 +++- 5 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 tools/jsdoc/CMakeLists.txt diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fbc40f70c2..07788e5865 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -279,6 +279,9 @@ endif(UNIX) # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) +# require JSDoc to be build before interface is deployed (Console Auto-complete) +add_dependencies(${TARGET_NAME} jsdoc) + if (APPLE) # link in required OS X frameworks and include the right GL headers find_library(OpenGL OpenGL) @@ -299,6 +302,9 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/../Resources/auto-complete" ) # call the fixup_interface macro to add required bundling commands for installation @@ -313,6 +319,9 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/resources/auto-complete" ) # link target to external libraries @@ -349,16 +358,6 @@ endif() add_bugsplat() -# generate the JSDoc JSON for the JSConsole auto-completer -add_custom_command(TARGET ${TARGET_NAME} #POST_BUILD - COMMAND npm install - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc -) -add_custom_command(TARGET ${TARGET_NAME} - COMMAND node_modules/.bin/jsdoc . -c config.json - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/jsdoc -) - if (WIN32) set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 16446c5071..ac920d930d 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,6 +2,9 @@ add_subdirectory(scribe) set_target_properties(scribe PROPERTIES FOLDER "Tools") +add_subdirectory(jsdoc) +set_target_properties(jsdoc PROPERTIES FOLDER "Tools") + if (BUILD_TOOLS) add_subdirectory(udt-test) set_target_properties(udt-test PROPERTIES FOLDER "Tools") @@ -27,4 +30,3 @@ if (BUILD_TOOLS) add_subdirectory(auto-tester) set_target_properties(auto-tester PROPERTIES FOLDER "Tools") endif() - diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt new file mode 100644 index 0000000000..9a9883a6ad --- /dev/null +++ b/tools/jsdoc/CMakeLists.txt @@ -0,0 +1,14 @@ +set(TARGET_NAME jsdoc) + +add_custom_target(${TARGET_NAME}) + +SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) + +add_custom_command(TARGET ${TARGET_NAME} + COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH}) + WORKING_DIRECTORY ${JSDOC_WORKING_DIR} + COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" +) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index afa3285c37..c434ecc0d6 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -10,6 +10,8 @@ function endsWith(path, exts) { exports.handlers = { beforeParse: function(e) { + const pathTools = require('path'); + var rootFolder = pathTools.dirname(e.filename); console.log("Scanning hifi source for jsdoc comments..."); // directories to scan for jsdoc comments @@ -34,9 +36,10 @@ exports.handlers = { const fs = require('fs'); dirList.forEach(function (dir) { - var files = fs.readdirSync(dir) + var joinedDir = pathTools.join(rootFolder, dir); + var files = fs.readdirSync(joinedDir) files.forEach(function (file) { - var path = dir + "/" + file; + var path = pathTools.join(joinedDir, file); if (fs.lstatSync(path).isFile() && endsWith(path, exts)) { var data = fs.readFileSync(path, "utf8"); var reg = /(\/\*\*jsdoc(.|[\r\n])*?\*\/)/gm; @@ -48,7 +51,7 @@ exports.handlers = { }); }); - fs.writeFile("out/hifiJSDoc.js", e.source, function(err) { + fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.js"), e.source, function(err) { if (err) { return console.log(err); } diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js index 7948fd2673..5531d3ff25 100644 --- a/tools/jsdoc/plugins/hifiJSONExport.js +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -1,9 +1,11 @@ exports.handlers = { processingComplete: function(e) { + const pathTools = require('path'); + var rootFolder = pathTools.join(__dirname, '..'); var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); const fs = require('fs'); doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); - fs.writeFile("out/hifiJSDoc.json", JSON.stringify(doclets, null, 4), function(err) { + fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { if (err) { return console.log(err); } From 58216b947c1de891a6b948000f9030412e0e2f09 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 18:54:24 +0100 Subject: [PATCH 12/39] removal of unnecessary JS export (for now) --- tools/jsdoc/plugins/hifi.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c434ecc0d6..bb556814e8 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -50,13 +50,5 @@ exports.handlers = { } }); }); - - fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.js"), e.source, function(err) { - if (err) { - return console.log(err); - } - - console.log("The Hifi JSDoc JS was saved!"); - }); } }; From 4ff3c9e0468ca69daf67bc86016100ac238e9beb Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 19:00:45 +0100 Subject: [PATCH 13/39] fix jsdoc output folder --- tools/jsdoc/CMakeLists.txt | 3 ++- tools/jsdoc/plugins/hifiJSONExport.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 9a9883a6ad..265d8e9913 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -5,10 +5,11 @@ add_custom_target(${TARGET_NAME}) SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) +file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH}) + COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) diff --git a/tools/jsdoc/plugins/hifiJSONExport.js b/tools/jsdoc/plugins/hifiJSONExport.js index 5531d3ff25..cd14c9faad 100644 --- a/tools/jsdoc/plugins/hifiJSONExport.js +++ b/tools/jsdoc/plugins/hifiJSONExport.js @@ -1,11 +1,14 @@ exports.handlers = { processingComplete: function(e) { const pathTools = require('path'); - var rootFolder = pathTools.join(__dirname, '..'); + var outputFolder = pathTools.join(__dirname, '../out'); var doclets = e.doclets.map(doclet => Object.assign({}, doclet)); const fs = require('fs'); + if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder); + } doclets.map(doclet => {delete doclet.meta; delete doclet.comment}); - fs.writeFile(pathTools.join(rootFolder, "out/hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { + fs.writeFile(pathTools.join(outputFolder, "hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) { if (err) { return console.log(err); } From f4e6606c8d407256686dd557a1bea82fc936ec5d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Jan 2018 19:01:13 +0100 Subject: [PATCH 14/39] fix MyAvatar JS API typo --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 58b49b61ff..086c76dd1a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -81,7 +81,7 @@ class MyAvatar : public Avatar { * and MyAvatar.customListenOrientation properties. * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position * of audio spatialization listener. - * @property customListenOreintation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation + * @property customListenOrientation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation * of the audio spatialization listener. * @property audioListenerModeHead {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener * around the avatar's head. From 5885569644b4290e29c58e9aaa16175b0f923d3e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 01:29:52 +0100 Subject: [PATCH 15/39] fix UNIX jsdoc command order --- tools/jsdoc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 265d8e9913..36c2af4265 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -9,7 +9,7 @@ file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) & (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND (npm --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From 67effd1ad190ebdd0417591ac0e50fefe2031908 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 02:15:31 +0100 Subject: [PATCH 16/39] create folder --- interface/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 07788e5865..130848fc08 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -302,6 +302,8 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/../Resources/auto-complete" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "$/../Resources/auto-complete" @@ -319,6 +321,8 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/resources/auto-complete" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "$/resources/auto-complete" From 79dbde6a6ff07189c351c8c25639887e3bdc7f13 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 23 Jan 2018 23:01:59 +0100 Subject: [PATCH 17/39] optional auto-complete build (requires NPM installed) --- cmake/macros/FindNPM.cmake | 14 ++++++++++++++ interface/CMakeLists.txt | 35 +++++++++++++++++++++++++---------- tools/CMakeLists.txt | 7 +++++-- tools/jsdoc/CMakeLists.txt | 4 +++- 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 cmake/macros/FindNPM.cmake diff --git a/cmake/macros/FindNPM.cmake b/cmake/macros/FindNPM.cmake new file mode 100644 index 0000000000..c66114f878 --- /dev/null +++ b/cmake/macros/FindNPM.cmake @@ -0,0 +1,14 @@ +# +# FindNPM.cmake +# cmake/macros +# +# Created by Thijs Wenker on 01/23/18. +# Copyright 2018 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html +# + +macro(find_npm) + find_program(NPM_EXECUTABLE "npm") +endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 130848fc08..b03a4637d0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -41,6 +41,8 @@ endif() file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") +find_npm() + # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) # Use .cpp and .h files as is. @@ -297,22 +299,38 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") + if (NPM_EXECUTABLE) + set(EXTRA_COPY_COMMANDS + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/../Resources/auto-complete" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/../Resources/auto-complete" + ) + endif() + # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/../Resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/../Resources/auto-complete" + + ${EXTRA_COPY_COMMANDS} ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() else (APPLE) + if (NPM_EXECUTABLE) + set(EXTRA_COPY_COMMANDS + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$/resources/auto-complete" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" + "$/resources/auto-complete" + ) + endif() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory @@ -321,11 +339,8 @@ else (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/resources/auto-complete" + + ${EXTRA_COPY_COMMANDS} ) # link target to external libraries diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ac920d930d..53d7fc2836 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,8 +2,11 @@ add_subdirectory(scribe) set_target_properties(scribe PROPERTIES FOLDER "Tools") -add_subdirectory(jsdoc) -set_target_properties(jsdoc PROPERTIES FOLDER "Tools") +find_npm() +if (NPM_EXECUTABLE) + add_subdirectory(jsdoc) + set_target_properties(jsdoc PROPERTIES FOLDER "Tools") +endif() if (BUILD_TOOLS) add_subdirectory(udt-test) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 36c2af4265..b52d229130 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -2,6 +2,8 @@ set(TARGET_NAME jsdoc) add_custom_target(${TARGET_NAME}) +find_npm() + SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) @@ -9,7 +11,7 @@ file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (npm --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND (${NPM_EXECUTABLE} --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From c46a1150006690ab8353bd888c0add5b55ec77cc Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 24 Jan 2018 01:45:53 +0100 Subject: [PATCH 18/39] attempt to fix ubuntu build --- tools/jsdoc/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index b52d229130..292523a813 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -4,14 +4,14 @@ add_custom_target(${TARGET_NAME}) find_npm() -SET(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) +set(JSDOC_WORKING_DIR ${CMAKE_SOURCE_DIR}/tools/jsdoc) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/node_modules/.bin/jsdoc JSDOC_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/config.json JSDOC_CONFIG_PATH) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR}/out OUTPUT_DIR) file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} - COMMAND (${NPM_EXECUTABLE} --no-progress install) && (${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR}) + COMMAND ${NPM_EXECUTABLE} --no-progress install && ${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR} WORKING_DIRECTORY ${JSDOC_WORKING_DIR} COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" ) From 6149630ccd52c4695de7a611f03e1731226a5e8f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 16:36:08 -0800 Subject: [PATCH 19/39] don't manually highlight tablet buttons from swipeview, since the tablet button highligh themselves --- .../resources/qml/hifi/tablet/TabletButton.qml | 1 - .../resources/qml/hifi/tablet/TabletHome.qml | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 4d443fb97c..5f49f9f10b 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -158,7 +158,6 @@ Item { gridView.currentIndex = buttonIndex tabletButton.isEntered = true; Tablet.playSound(TabletEnums.ButtonHover); - if (tabletButton.isActive) { tabletButton.state = "hover active state"; } else { diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 524bbf5f4d..88d8194c49 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -144,22 +144,7 @@ Item { bottomMargin: 0 } - function setButtonState(buttonIndex, buttonstate) { - if (buttonIndex < 0 || gridView.contentItem === undefined - || gridView.contentItem.children.length - 1 < buttonIndex) { - return; - } - var itemat = gridView.contentItem.children[buttonIndex].children[0]; - if (itemat.isActive) { - itemat.state = "active state"; - } else { - itemat.state = buttonstate; - } - } - onCurrentIndexChanged: { - setButtonState(previousGridIndex, "base state"); - setButtonState(currentIndex, "hover state"); previousGridIndex = currentIndex } From c73c0487a09938c925493b406340311e4096c0a8 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 24 Jan 2018 16:41:19 -0800 Subject: [PATCH 20/39] min diff --- interface/resources/qml/hifi/tablet/TabletButton.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 5f49f9f10b..4d443fb97c 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -158,6 +158,7 @@ Item { gridView.currentIndex = buttonIndex tabletButton.isEntered = true; Tablet.playSound(TabletEnums.ButtonHover); + if (tabletButton.isActive) { tabletButton.state = "hover active state"; } else { From 179c21acf4b3c35752dfd648de52c47700fea982 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 22:46:30 +0100 Subject: [PATCH 21/39] jsdoc info on selected auto-complete entry --- interface/src/ui/JSConsole.cpp | 145 ++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 3100b47882..1ca1ac2842 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,21 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; const QString GUTTER_ERROR = "X"; +const QString JSDOC_LINE_SEPARATOR = "\r"; + +const QString JSDOC_STYLE = + ""; + const QString JSConsole::_consoleFileName { "about:console" }; const QString JSON_KEY = "entries"; @@ -50,7 +66,7 @@ QList _readLines(const QString& filename) { // TODO: check root["version"] return root[JSON_KEY].toVariant().toStringList(); } - + void _writeLines(const QString& filename, const QList& lines) { QFile file(filename); file.open(QFile::WriteOnly); @@ -62,6 +78,10 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } +QString _jsdocTypeToString(QJsonValue jsdocType) { + return jsdocType.toObject().value("names").toVariant().toStringList().join("/"); +} + void JSConsole::readAPI() { QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); file.open(QFile::ReadOnly); @@ -102,7 +122,10 @@ QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { foreach(auto doc, _apiDocs) { auto object = doc.toObject(); auto scope = object.value("scope"); - if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || + (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty && + object.value("kind").toString() != "typedef")) { + model->appendRow(getAutoCompleteItem(doc)); } } @@ -166,20 +189,119 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : } void JSConsole::insertCompletion(const QModelIndex& completion) { + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); + auto kind = jsdocObject.value("kind").toString(); auto completionString = completion.data().toString(); - QTextCursor tc = _ui->promptTextEdit->textCursor(); + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + // automatically add the parenthesis/parentheses for the functions + completionString += params.isEmpty() ? "()" : "("; + } + QTextCursor textCursor = _ui->promptTextEdit->textCursor(); int extra = completionString.length() - _completer->completionPrefix().length(); - tc.movePosition(QTextCursor::Left); - tc.movePosition(QTextCursor::EndOfWord); - tc.insertText(completionString.right(extra)); - _ui->promptTextEdit->setTextCursor(tc); + textCursor.movePosition(QTextCursor::Left); + textCursor.movePosition(QTextCursor::EndOfWord); + textCursor.insertText(completionString.right(extra)); + _ui->promptTextEdit->setTextCursor(textCursor); } void JSConsole::highlightedCompletion(const QModelIndex& completion) { - qDebug() << "Highlighted " << completion.data().toString(); auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); - - // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); + QString memberOf = ""; + if (!_completerModule.isEmpty()) { + memberOf = _completerModule + "."; + } + auto name = memberOf + "" + jsdocObject.value("name").toString() + ""; + auto description = jsdocObject.value("description").toString(); + auto examples = jsdocObject.value("examples").toArray(); + auto kind = jsdocObject.value("kind").toString(); + QString returnTypeText = ""; + + QString paramsTable = ""; + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + returnTypeText = _jsdocTypeToString(jsdocObject.value("returns").toArray().at(0).toObject().value("type")) + " "; + } + name += "("; + if (!params.isEmpty()) { + bool hasDefaultParam = false; + bool hasOptionalParam = false; + bool firstItem = true; + foreach(auto param, params) { + auto paramObject = param.toObject(); + if (!hasOptionalParam && paramObject.value("optional").toBool(false)) { + hasOptionalParam = true; + name += "["; + } + if (!firstItem) { + name += ", "; + } else { + firstItem = false; + } + name += paramObject.value("name").toString(); + if (!hasDefaultParam && !paramObject.value("defaultvalue").isUndefined()) { + hasDefaultParam = true; + } + } + if (hasOptionalParam) { + name += "]"; + } + + paramsTable += ""; + if (hasDefaultParam) { + paramsTable += ""; + } + paramsTable += ""; + foreach(auto param, params) { + auto paramObject = param.toObject(); + paramsTable += ""; + } + + paramsTable += "
NameTypeDefaultDescription
" + paramObject.value("name").toString() + "" + + _jsdocTypeToString(paramObject.value("type")) + ""; + if (hasDefaultParam) { + paramsTable += paramObject.value("defaultvalue").toVariant().toString() + ""; + } + paramsTable += paramObject.value("description").toString() + "
"; + } + name += ")"; + } else if (!jsdocObject.value("type").isUndefined()){ + returnTypeText = _jsdocTypeToString(jsdocObject.value("type")) + " "; + } + auto popupText = JSDOC_STYLE + "" + returnTypeText + name + ""; + auto descriptionText = "

" + description.replace(JSDOC_LINE_SEPARATOR, "
") + "

"; + + popupText += descriptionText; + popupText += paramsTable; + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + foreach(auto returnEntry, returns.toArray()) { + auto returnsObject = returnEntry.toObject(); + auto returnsDescription = returnsObject.value("description").toString().replace(JSDOC_LINE_SEPARATOR, "
"); + popupText += "

Returns

" + returnsDescription + "

Type
" +
+                _jsdocTypeToString(returnsObject.value("type")) + "
"; + } + } + + if (!examples.isEmpty()) { + popupText += "

Examples

"; + foreach(auto example, examples) { + auto exampleText = example.toString(); + auto exampleLines = exampleText.split(JSDOC_LINE_SEPARATOR); + foreach(auto exampleLine, exampleLines) { + if (exampleLine.contains("")) { + popupText += exampleLine.replace("caption>", "h5>"); + } else { + popupText += "
" + exampleLine + "\n
"; + } + } + } + } + + QToolTip::showText(QPoint(_completer->popup()->pos().x() + _completer->popup()->width(), _completer->popup()->pos().y()), + popupText, _completer->popup()); } JSConsole::~JSConsole() { @@ -299,7 +421,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { case Qt::Key_Space: case Qt::Key_Enter: case Qt::Key_Return: - insertCompletion(_completer->currentIndex()); + insertCompletion(_completer->popup()->currentIndex()); _completer->popup()->hide(); return true; default: @@ -401,6 +523,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + _completer->popup()->verticalScrollBar()->sizeHint().width()); _completer->complete(cursorRect); + highlightedCompletion(_completer->popup()->currentIndex()); return false; } } From ce50380698285dde03ffccf639e795be51aeed9d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 22:47:35 +0100 Subject: [PATCH 22/39] fix JSAPI typo --- libraries/entities/src/EntityScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 095a821482..9342f96e07 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -139,7 +139,7 @@ public slots: Q_INVOKABLE bool canRezTmpCertified(); /**jsdoc - * @function Entities.canWriteAsseets + * @function Entities.canWriteAssets * @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server */ Q_INVOKABLE bool canWriteAssets(); From 2874a3d2cab9f46aa1dd9e4fbb1092f5029e7012 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 25 Jan 2018 23:39:32 +0100 Subject: [PATCH 23/39] fix android build (skip auto-complete when BUILD_TOOLS is FALSE) --- interface/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 96d04c784d..d0d89919d5 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -276,8 +276,10 @@ endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) -# require JSDoc to be build before interface is deployed (Console Auto-complete) -add_dependencies(${TARGET_NAME} jsdoc) +if (BUILD_TOOLS) + # require JSDoc to be build before interface is deployed (Console Auto-complete) + add_dependencies(${TARGET_NAME} jsdoc) +endif() if (APPLE) # link in required OS X frameworks and include the right GL headers @@ -294,7 +296,7 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") - if (NPM_EXECUTABLE) + if (BUILD_TOOLS AND NPM_EXECUTABLE) set(EXTRA_COPY_COMMANDS COMMAND "${CMAKE_COMMAND}" -E make_directory "$/../Resources/auto-complete" @@ -317,7 +319,7 @@ if (APPLE) fixup_interface() else() - if (NPM_EXECUTABLE) + if (BUILD_TOOLS AND NPM_EXECUTABLE) set(EXTRA_COPY_COMMANDS COMMAND "${CMAKE_COMMAND}" -E make_directory "$/resources/auto-complete" From f3e20e72cb51f032ef024c9dbbbf339b2f72d761 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 25 Jan 2018 15:09:03 -0800 Subject: [PATCH 24/39] really fix multi-highlighing --- interface/resources/qml/hifi/tablet/TabletHome.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 88d8194c49..f56d4f73be 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -148,6 +148,18 @@ Item { previousGridIndex = currentIndex } + onMovementStarted: { + if (currentIndex < 0 || gridView.currentItem === undefined || gridView.contentItem.children.length - 1 < currentIndex) { + return; + } + var button = gridView.contentItem.children[currentIndex].children[0]; + if (button.isActive) { + button.state = "active state"; + } else { + button.state = "base state"; + } + } + cellWidth: width/3 cellHeight: cellWidth flow: GridView.LeftToRight From faf8350369e26b627a1c6b574cbac2578061bef8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 25 Jan 2018 15:57:48 -0800 Subject: [PATCH 25/39] Bug fix for avatar neck cauterization The basic dual-quaternion skinning algorithm does not handle non-rigid transformations like scale well. Because we only use scaling for head cauterization, we special case this by passing in a cauterization factor, as well as a cauterization position to the vertex shader. If a vertex is flagged as cauterized, we slam it to equal the cauterization position. Although, not as smooth as the previous method, it seems to work well enough on the avatar's I've tested. --- .../render-utils/src/CauterizedModel.cpp | 2 + libraries/render-utils/src/Model.h | 10 +++- libraries/render-utils/src/Skinning.slh | 50 +++++++++++++++---- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index e3f26a43d8..0b8c636678 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -115,6 +115,7 @@ void CauterizedModel::updateClusterMatrices() { Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterTransforms[j].setCauterizationParameters(0.0f, jointPose.trans()); #else auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -151,6 +152,7 @@ void CauterizedModel::updateClusterMatrices() { Transform clusterTransform; Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterTransforms[j].setCauterizationParameters(1.0f, cauterizePose.trans()); #else glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 560aa82f0c..17ac251459 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -263,26 +263,34 @@ public: _scale.x = p.scale().x; _scale.y = p.scale().y; _scale.z = p.scale().z; + _scale.w = 0.0f; _dq = DualQuaternion(p.rot(), p.trans()); } TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { _scale.x = scale.x; _scale.y = scale.y; _scale.z = scale.z; + _scale.w = 0.0f; _dq = DualQuaternion(rot, trans); } TransformDualQuaternion(const Transform& transform) { _scale = glm::vec4(transform.getScale(), 0.0f); + _scale.w = 0.0f; _dq = DualQuaternion(transform.getRotation(), transform.getTranslation()); } glm::vec3 getScale() const { return glm::vec3(_scale); } glm::quat getRotation() const { return _dq.getRotation(); } glm::vec3 getTranslation() const { return _dq.getTranslation(); } glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRotation(), getTranslation()); }; + + void setCauterizationParameters(float cauterizationAmount, const glm::vec3& cauterizedPosition) { + _scale.w = cauterizationAmount; + _cauterizedPosition = glm::vec4(cauterizedPosition, 1.0f); + } protected: glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; DualQuaternion _dq; - glm::vec4 _padding; + glm::vec4 _cauterizedPosition { 0.0f, 0.0f, 0.0f, 1.0f }; }; #endif diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index a7edfb14a6..702813366f 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -58,17 +58,19 @@ mat4 dualQuatToMat4(vec4 real, vec4 dual) { void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -79,6 +81,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -88,25 +91,34 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this joint is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -117,6 +129,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -126,7 +139,15 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this joint is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } + skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -134,18 +155,20 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { // linearly blend scale and dual quaternion components - vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - vec3 scale = vec3(clusterMatrix[0]); + vec4 scale = clusterMatrix[0]; vec4 real = clusterMatrix[1]; vec4 dual = clusterMatrix[2]; + vec4 cauterizedPos = clusterMatrix[3]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -156,6 +179,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; dAccum += dual * dqClusterWeight; + cAccum += cauterizedPos * clusterWeight; } // normalize dual quaternion @@ -165,7 +189,15 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + + // an sAccum.w of > 0 indicates that this vertex is cauterized. + if (sAccum.w > 0.1) { + skinnedPosition = cAccum; + } else { + sAccum.w = 1.0; + skinnedPosition = m * (sAccum * inPosition); + } + skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); } From 78d72a1703155554af3ff21f073a2b5dbe782cf6 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 25 Jan 2018 16:03:05 -0800 Subject: [PATCH 26/39] remove unnecessary invokeMethod from ResourceCache::getResource --- libraries/networking/src/ResourceCache.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index a3ac995bcf..7ba7cca96d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -340,14 +340,6 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& return resource; } - if (QThread::currentThread() != thread()) { - qCDebug(networking) << "Fetching asynchronously:" << url; - QMetaObject::invokeMethod(this, "getResource", - Q_ARG(QUrl, url), Q_ARG(QUrl, fallback)); - // Cannot use extra parameter as it might be freed before the invocation - return QSharedPointer(); - } - if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { return getResource(fallback, QUrl()); } @@ -358,6 +350,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& extra); resource->setSelf(resource); resource->setCache(this); + resource->moveToThread(qApp->thread()); connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); { QWriteLocker locker(&_resourcesLock); From be42f0cfb1c4d66aba476ee040000104a1850b95 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 26 Jan 2018 13:24:44 -0800 Subject: [PATCH 27/39] finally fix the multihighlighting --- interface/resources/qml/hifi/tablet/TabletHome.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index f56d4f73be..cfff3a3273 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -127,7 +127,7 @@ Item { GridView { id: gridView - + flickableDirection: Flickable.AutoFlickIfNeeded keyNavigationEnabled: false highlightFollowsCurrentItem: false From 43eaa02ef069f560b8fb1d5477b2c132722026da Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 26 Jan 2018 14:01:33 -0800 Subject: [PATCH 28/39] updated Skinning.slh comment and constant. --- libraries/render-utils/src/Skinning.slh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 702813366f..6048ba4ade 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -92,8 +92,11 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this joint is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; @@ -140,8 +143,11 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this joint is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; @@ -190,8 +196,11 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // conversion from dual quaternion to 4x4 matrix. mat4 m = dualQuatToMat4(rAccum, dAccum); - // an sAccum.w of > 0 indicates that this vertex is cauterized. - if (sAccum.w > 0.1) { + // sAccum.w indicates the amount of cauterization for this vertex. + // 0 indicates no cauterization and 1 indicates full cauterization. + // TODO: make this cauterization smoother or implement full dual-quaternion scale support. + const float CAUTERIZATION_THRESHOLD = 0.1; + if (sAccum.w > CAUTERIZATION_THRESHOLD) { skinnedPosition = cAccum; } else { sAccum.w = 1.0; From 898c99e5b83a7b5315f626e152bcb82933daecb9 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 27 Jan 2018 19:50:52 +0100 Subject: [PATCH 29/39] append file jsdoc json file to resources.rcc resource file --- cmake/macros/GenerateQrc.cmake | 9 ++++++- interface/CMakeLists.txt | 49 +++++++++++++--------------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/cmake/macros/GenerateQrc.cmake b/cmake/macros/GenerateQrc.cmake index 9bf530b2a2..5e2be71c82 100644 --- a/cmake/macros/GenerateQrc.cmake +++ b/cmake/macros/GenerateQrc.cmake @@ -1,7 +1,7 @@ function(GENERATE_QRC) set(oneValueArgs OUTPUT PREFIX PATH) - set(multiValueArgs GLOBS) + set(multiValueArgs CUSTOM_PATHS GLOBS) cmake_parse_arguments(GENERATE_QRC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) if ("${GENERATE_QRC_PREFIX}" STREQUAL "") set(QRC_PREFIX_PATH /) @@ -20,6 +20,13 @@ function(GENERATE_QRC) endforeach() endforeach() + foreach(CUSTOM_PATH ${GENERATE_QRC_CUSTOM_PATHS}) + string(REPLACE "=" ";" CUSTOM_PATH ${CUSTOM_PATH}) + list(GET CUSTOM_PATH 0 IMPORT_PATH) + list(GET CUSTOM_PATH 1 LOCAL_PATH) + set(QRC_CONTENTS "${QRC_CONTENTS}${IMPORT_PATH}\n") + endforeach() + set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE) configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT}) endfunction() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d0d89919d5..fc3bc4a0d1 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -11,9 +11,22 @@ function(JOIN VALUES GLUE OUTPUT) set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() +set(CUSTOM_INTERFACE_QRC_PATHS "") + +function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) + list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") + set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) +endfunction() + +find_npm() + +if (BUILD_TOOLS AND NPM_EXECUTABLE) + add_custom_qrc_path(CUSTOM_INTERFACE_QRC_PATHS "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" "auto-complete/hifiJSDoc.json") +endif () + set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) -generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *) +generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources CUSTOM_PATHS ${CUSTOM_INTERFACE_QRC_PATHS} GLOBS *) add_custom_command( OUTPUT ${RESOURCES_RCC} @@ -53,8 +66,6 @@ file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") list(APPEND INTERFACE_SRCS ${RESOURCES_RCC}) -find_npm() - # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) # Use .cpp and .h files as is. @@ -165,6 +176,11 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () +if (BUILD_TOOLS AND NPM_EXECUTABLE) + # require JSDoc to be build before interface is deployed (Console Auto-complete) + add_dependencies(resources jsdoc) +endif() + add_dependencies(${TARGET_NAME} resources) if (WIN32) @@ -276,11 +292,6 @@ endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) -if (BUILD_TOOLS) - # require JSDoc to be build before interface is deployed (Console Auto-complete) - add_dependencies(${TARGET_NAME} jsdoc) -endif() - if (APPLE) # link in required OS X frameworks and include the right GL headers find_library(OpenGL OpenGL) @@ -296,38 +307,18 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") - if (BUILD_TOOLS AND NPM_EXECUTABLE) - set(EXTRA_COPY_COMMANDS - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/../Resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/../Resources/auto-complete" - ) - endif() # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/../Resources/scripts" - - ${EXTRA_COPY_COMMANDS} ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() else() - if (BUILD_TOOLS AND NPM_EXECUTABLE) - set(EXTRA_COPY_COMMANDS - COMMAND "${CMAKE_COMMAND}" -E make_directory - "$/resources/auto-complete" - COMMAND "${CMAKE_COMMAND}" -E copy - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out/hifiJSDoc.json" - "$/resources/auto-complete" - ) - endif() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different @@ -344,8 +335,6 @@ else() COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" - - ${EXTRA_COPY_COMMANDS} ) # link target to external libraries From 8fdbac521fc6b010248921dfb0d63bc2e6bee0a5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 27 Jan 2018 19:58:15 +0100 Subject: [PATCH 30/39] move ADD_CUSTOM_QRC_PATH function to its own file --- cmake/macros/AddCustomQrcPath.cmake | 7 +++++++ interface/CMakeLists.txt | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 cmake/macros/AddCustomQrcPath.cmake diff --git a/cmake/macros/AddCustomQrcPath.cmake b/cmake/macros/AddCustomQrcPath.cmake new file mode 100644 index 0000000000..6bd54baa4d --- /dev/null +++ b/cmake/macros/AddCustomQrcPath.cmake @@ -0,0 +1,7 @@ +# adds a custom path and local path to the inserted CUSTOM_PATHS_VAR list which +# can be given to the GENERATE_QRC command + +function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) + list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") + set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) +endfunction() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fc3bc4a0d1..a49019e775 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -13,11 +13,6 @@ endfunction() set(CUSTOM_INTERFACE_QRC_PATHS "") -function(ADD_CUSTOM_QRC_PATH CUSTOM_PATHS_VAR IMPORT_PATH LOCAL_PATH) - list(APPEND ${CUSTOM_PATHS_VAR} "${IMPORT_PATH}=${LOCAL_PATH}") - set(${CUSTOM_PATHS_VAR} ${${CUSTOM_PATHS_VAR}} PARENT_SCOPE) -endfunction() - find_npm() if (BUILD_TOOLS AND NPM_EXECUTABLE) From 68fd9eafa8c3fd7409d410750be1cae162ec89b1 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 29 Jan 2018 10:23:24 -0800 Subject: [PATCH 31/39] fix entity parenting crash --- libraries/entities/src/EntityTree.cpp | 15 +++++++++++++-- libraries/entities/src/EntityTree.h | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 15fc3256bd..91694c86aa 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1678,14 +1678,23 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } } +QVector EntityTree::getEntitiesParentFixup() const { + QReadLocker locker(&_needsParentFixupLock); + return _needsParentFixup; +} + +void EntityTree::setNeedsParentFixup(QVector entitiesFixup) { + QWriteLocker locker(&_needsParentFixupLock); + _needsParentFixup = entitiesFixup; +} void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; - QWriteLocker locker(&_needsParentFixupLock); + QVector entitiesParentFixup = getEntitiesParentFixup(); - QMutableVectorIterator iter(_needsParentFixup); + QMutableVectorIterator iter(entitiesParentFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1748,6 +1757,8 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } + + setNeedsParentFixup(entitiesParentFixup); } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 11a747d624..fa44fd37b8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,6 +385,8 @@ private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); + QVector getEntitiesParentFixup() const; + void setNeedsParentFixup(QVector entitiesFixup); std::shared_ptr _myAvatar{ nullptr }; }; From 280264d7edec00af0dd7309fca5ad6937287920a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 30 Jan 2018 20:46:27 +1300 Subject: [PATCH 32/39] Add location.domainID as a synonym for location.domainId And deprecate location.domainId. --- .../src/entities/EntityServer.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 2 +- libraries/networking/src/AddressManager.h | 9 ++++++--- scripts/system/pal.js | 2 +- scripts/system/snapshot.js | 18 +++++++++--------- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index ecdf14ebec..f72832f902 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -453,7 +453,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - QString thisDomainID = DependencyManager::get()->getDomainId().remove(QRegExp("\\{|\\}")); + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index fa5507fcf7..4e1354d3aa 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -776,7 +776,7 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } -QString AddressManager::getDomainId() const { +QString AddressManager::getDomainID() const { return DependencyManager::get()->getDomainHandler().getUUID().toString(); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 0173a1fad7..6c9f06d2dd 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -35,9 +35,11 @@ const QString GET_PLACE = "/api/v1/places/%1"; * The location API provides facilities related to your current location in the metaverse. * * @namespace location - * @property {Uuid} domainId - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not + * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not * connected to the domain. * Read-only. + * @property {Uuid} domainId - Synonym for domainId. Read-only. Deprecated: This property + * is deprecated and will soon be removed. * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "AvatarIsland", * localhost, or an IP address). * Read-only. @@ -66,7 +68,8 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) Q_PROPERTY(QString placename READ getPlaceName) - Q_PROPERTY(QString domainId READ getDomainId) + Q_PROPERTY(QString domainID READ getDomainID) + Q_PROPERTY(QString domainId READ getDomainID) public: /**jsdoc @@ -164,7 +167,7 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; } - QString getDomainId() const; + QString getDomainID() const; const QString& getHost() const { return _host; } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 1b93bdde32..b551276e91 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -493,7 +493,7 @@ function populateNearbyUserList(selectData, oldAudioData) { data.push(avatarPalDatum); print('PAL data:', JSON.stringify(avatarPalDatum)); }); - getConnectionData(false, location.domainId); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain). + getConnectionData(false, location.domainID); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain). conserveResources = Object.keys(avatarsOfInterest).length > 20; sendToQml({ method: 'nearbyUsers', params: data }); if (selectData) { diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index dad642075f..ae8ef52a15 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -337,7 +337,7 @@ function fillImageDataFromPrevious() { containsGif: previousAnimatedSnapPath !== "", processingGif: false, shouldUpload: false, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), isLoggedIn: isLoggedIn }; imageData = []; @@ -416,7 +416,7 @@ function snapshotUploaded(isError, reply) { } isUploadingPrintableStill = false; } -var href, domainId; +var href, domainID; function takeSnapshot() { tablet.emitScriptEvent(JSON.stringify({ type: "snapshot", @@ -443,11 +443,11 @@ function takeSnapshot() { MyAvatar.setClearOverlayWhenMoving(false); // We will record snapshots based on the starting location. That could change, e.g., when recording a .gif. - // Even the domainId could change (e.g., if the user falls into a teleporter while recording). + // Even the domainID could change (e.g., if the user falls into a teleporter while recording). href = location.href; Settings.setValue("previousSnapshotHref", href); - domainId = location.domainId; - Settings.setValue("previousSnapshotDomainID", domainId); + domainID = location.domainID; + Settings.setValue("previousSnapshotDomainID", domainID); maybeDeleteSnapshotStories(); @@ -548,7 +548,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { } HMD.openTablet(); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: false, processingGif: false, @@ -594,7 +594,7 @@ function processingGifStarted(pathStillSnapshot) { } HMD.openTablet(); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: true, @@ -622,13 +622,13 @@ function processingGifCompleted(pathAnimatedSnapshot) { Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); - isDomainOpen(domainId, function (canShare) { + isDomainOpen(domainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: false, canShare: canShare, isLoggedIn: isLoggedIn, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), }; imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ From bed2ea052df8dbe4f08b501dd0646b0eeb905d7a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 Jan 2018 09:44:49 -0800 Subject: [PATCH 33/39] better solution --- libraries/entities/src/EntityTree.cpp | 18 +++--------------- libraries/entities/src/EntityTree.h | 2 -- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8dad126d30..f485f4121e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -65,7 +65,7 @@ public: EntityTree::EntityTree(bool shouldReaverage) : - Octree(shouldReaverage) + Octree(shouldReaverage), _needsParentFixupLock(QReadWriteLock::Recursive) { resetClientEditStats(); @@ -1679,23 +1679,13 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } } -QVector EntityTree::getEntitiesParentFixup() const { - QReadLocker locker(&_needsParentFixupLock); - return _needsParentFixup; -} - -void EntityTree::setNeedsParentFixup(QVector entitiesFixup) { - QWriteLocker locker(&_needsParentFixupLock); - _needsParentFixup = entitiesFixup; -} - void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; - QVector entitiesParentFixup = getEntitiesParentFixup(); + QWriteLocker locker(&_needsParentFixupLock); - QMutableVectorIterator iter(entitiesParentFixup); + QMutableVectorIterator iter(_needsParentFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1758,8 +1748,6 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } - - setNeedsParentFixup(entitiesParentFixup); } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index f4ff7ad118..8cb89d6493 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,8 +385,6 @@ private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); - QVector getEntitiesParentFixup() const; - void setNeedsParentFixup(QVector entitiesFixup); std::shared_ptr _myAvatar{ nullptr }; }; From 4c0a1732874c205d3235f1ae28536c8b648f31d7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 Jan 2018 10:23:19 -0800 Subject: [PATCH 34/39] trying another solution --- libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f485f4121e..bf29f3bec9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -65,7 +65,7 @@ public: EntityTree::EntityTree(bool shouldReaverage) : - Octree(shouldReaverage), _needsParentFixupLock(QReadWriteLock::Recursive) + Octree(shouldReaverage) { resetClientEditStats(); @@ -1682,10 +1682,14 @@ void EntityTree::entityChanged(EntityItemPointer entity) { void EntityTree::fixupNeedsParentFixups() { PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; + QVector entitiesToFixup; + { + QWriteLocker locker(&_needsParentFixupLock); + entitiesToFixup = _needsParentFixup; + _needsParentFixup.clear(); + } - QWriteLocker locker(&_needsParentFixupLock); - - QMutableVectorIterator iter(_needsParentFixup); + QMutableVectorIterator iter(entitiesToFixup); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); EntityItemPointer entity = entityWP.lock(); @@ -1748,6 +1752,12 @@ void EntityTree::fixupNeedsParentFixups() { PerformanceTimer perfTimer("recurseTreeWithOperator"); recurseTreeWithOperator(&moveOperator); } + + { + QWriteLocker locker(&_needsParentFixupLock); + // add back the entities that did not get fixup + _needsParentFixup.append(entitiesToFixup); + } } void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { From d155d191982cab3c1a06f84f64db0105f9353a22 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 30 Jan 2018 17:08:10 -0800 Subject: [PATCH 35/39] Fix cmake install cmd --- cmake/macros/AddCrashpad.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/macros/AddCrashpad.cmake b/cmake/macros/AddCrashpad.cmake index bf59418f37..7d161be7f0 100644 --- a/cmake/macros/AddCrashpad.cmake +++ b/cmake/macros/AddCrashpad.cmake @@ -51,8 +51,8 @@ macro(add_crashpad) ) install( PROGRAMS ${CRASHPAD_HANDLER_EXE_PATH} - DESTINATION ${CLIENT_COMPONENT} - COMPONENT ${INTERFACE_INSTALL_DIR} + DESTINATION ${INTERFACE_INSTALL_DIR} + COMPONENT ${CLIENT_COMPONENT} ) endif () endmacro() From 97729e8f410f1fdbcae09539a7ac0286c24d24af Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 30 Jan 2018 17:10:06 -0800 Subject: [PATCH 36/39] Cleanup bad install --- cmake/templates/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 8abb202bd4..c1bfebe2c4 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -823,6 +823,7 @@ Section "-Core installation" Delete "$INSTDIR\server-console.exe" RMDir /r "$INSTDIR\locales" RMDir /r "$INSTDIR\resources\app" + RMDir /r "$INSTDIR\client" Delete "$INSTDIR\resources\atom.asar" Delete "$INSTDIR\build-info.json" Delete "$INSTDIR\content_resources_200_percent.pak" From 9893273d33f892f4d11c9423ce486e8d32905d84 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 30 Jan 2018 21:11:48 -0800 Subject: [PATCH 37/39] Trying to fix AMD crash in QML rendering --- libraries/gl/src/gl/Context.cpp | 40 +++++++++++---------- libraries/gl/src/gl/Context.h | 1 + libraries/gl/src/gl/OffscreenGLCanvas.cpp | 24 +++++++++++++ libraries/gl/src/gl/OffscreenGLCanvas.h | 5 ++- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 2 ++ 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index 8acbada5aa..309839808e 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -23,25 +23,31 @@ #include #include #include "GLLogging.h" -#include "Config.h" - -#ifdef Q_OS_WIN - -#if defined(DEBUG) || defined(USE_GLES) -static bool enableDebugLogger = true; -#else -static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); -static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); -#endif - -#endif - #include "Config.h" #include "GLHelpers.h" using namespace gl; +bool Context::enableDebugLogger() { +#if defined(DEBUG) || defined(USE_GLES) + static bool enableDebugLogger = true; +#else + static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); + static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +#endif + static std::once_flag once; + std::call_once(once, [&] { + // If the previous run crashed, force GL debug logging on + if (qApp->property(hifi::properties::CRASHED).toBool()) { + enableDebugLogger = true; + } + }); + return enableDebugLogger; +} + + + std::atomic Context::_totalSwapchainMemoryUsage { 0 }; size_t Context::getSwapchainMemoryUsage() { return _totalSwapchainMemoryUsage.load(); } @@ -245,10 +251,6 @@ void Context::create() { // Create a temporary context to initialize glew static std::once_flag once; std::call_once(once, [&] { - // If the previous run crashed, force GL debug logging on - if (qApp->property(hifi::properties::CRASHED).toBool()) { - enableDebugLogger = true; - } auto hdc = GetDC(hwnd); setupPixelFormatSimple(hdc); auto glrc = wglCreateContext(hdc); @@ -328,7 +330,7 @@ void Context::create() { contextAttribs.push_back(WGL_CONTEXT_CORE_PROFILE_BIT_ARB); #endif contextAttribs.push_back(WGL_CONTEXT_FLAGS_ARB); - if (enableDebugLogger) { + if (enableDebugLogger()) { contextAttribs.push_back(WGL_CONTEXT_DEBUG_BIT_ARB); } else { contextAttribs.push_back(0); @@ -350,7 +352,7 @@ void Context::create() { if (!makeCurrent()) { throw std::runtime_error("Could not make context current"); } - if (enableDebugLogger) { + if (enableDebugLogger()) { glDebugMessageCallback(debugMessageCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); } diff --git a/libraries/gl/src/gl/Context.h b/libraries/gl/src/gl/Context.h index 4f0747a86f..b6160cbd6c 100644 --- a/libraries/gl/src/gl/Context.h +++ b/libraries/gl/src/gl/Context.h @@ -47,6 +47,7 @@ namespace gl { Context(const Context& other); public: + static bool enableDebugLogger(); Context(); Context(QWindow* window); void release(); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 8a476c45ad..380ba085cc 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Context.h" #include "GLHelpers.h" @@ -68,9 +69,32 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { } #endif + if (gl::Context::enableDebugLogger()) { + _context->makeCurrent(_offscreenSurface); + QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); + connect(logger, &QOpenGLDebugLogger::messageLogged, this, &OffscreenGLCanvas::onMessageLogged); + logger->initialize(); + logger->enableMessages(); + logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); + _context->doneCurrent(); + } + return true; } +void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) { + auto severity = debugMessage.severity(); + switch (severity) { + case QOpenGLDebugMessage::NotificationSeverity: + case QOpenGLDebugMessage::LowSeverity: + return; + default: + break; + } + qDebug(glLogging) << debugMessage; + return; +} + bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); std::call_once(_reportOnce, []{ diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 583aa7c60f..be0e0d9678 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -17,7 +17,7 @@ class QOpenGLContext; class QOffscreenSurface; -class QOpenGLDebugLogger; +class QOpenGLDebugMessage; class OffscreenGLCanvas : public QObject { public: @@ -32,6 +32,9 @@ public: } QObject* getContextObject(); +private slots: + void onMessageLogged(const QOpenGLDebugMessage &debugMessage); + protected: std::once_flag _reportOnce; QOpenGLContext* _context{ nullptr }; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index db3df34dc5..b56de957d2 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -543,6 +543,7 @@ void OffscreenQmlSurface::cleanup() { void OffscreenQmlSurface::render() { #if !defined(DISABLE_QML) + if (nsightActive()) { return; } @@ -565,6 +566,7 @@ void OffscreenQmlSurface::render() { glBindTexture(GL_TEXTURE_2D, texture); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); + glFinish(); { From ef699c2a78a5848f4c628bded0221193d03c7621 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 31 Jan 2018 10:38:20 -0800 Subject: [PATCH 38/39] Better fix for AMD crashing (hopefully --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 40 ++++++++++++++------- libraries/ui/src/ui/OffscreenQmlSurface.h | 4 +++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index b56de957d2..6ff0a7d416 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -566,19 +566,22 @@ void OffscreenQmlSurface::render() { glBindTexture(GL_TEXTURE_2D, texture); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); - glFinish(); { // If the most recent texture was unused, we can directly recycle it - if (_latestTextureAndFence.first) { - offscreenTextures.releaseTexture(_latestTextureAndFence); - _latestTextureAndFence = { 0, 0 }; - } - - _latestTextureAndFence = { texture, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Fence will be used in another thread / context, so a flush is required glFlush(); + + { + Lock lock(_latestTextureAndFenceMutex); + if (_latestTextureAndFence.first) { + offscreenTextures.releaseTexture(_latestTextureAndFence); + _latestTextureAndFence = { 0, 0 }; + } + _latestTextureAndFence = { texture, fence}; + } } _quickWindow->resetOpenGLState(); @@ -590,13 +593,21 @@ void OffscreenQmlSurface::render() { bool OffscreenQmlSurface::fetchTexture(TextureAndFence& textureAndFence) { textureAndFence = { 0, 0 }; + // Lock free early check if (0 == _latestTextureAndFence.first) { return false; } // Ensure writes to the latest texture are complete before before returning it for reading - textureAndFence = _latestTextureAndFence; - _latestTextureAndFence = { 0, 0 }; + { + Lock lock(_latestTextureAndFenceMutex); + // Double check inside the lock + if (0 == _latestTextureAndFence.first) { + return false; + } + textureAndFence = _latestTextureAndFence; + _latestTextureAndFence = { 0, 0 }; + } return true; } @@ -815,10 +826,13 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { // Release hold on the textures of the old size if (uvec2() != _size) { - // If the most recent texture was unused, we can directly recycle it - if (_latestTextureAndFence.first) { - offscreenTextures.releaseTexture(_latestTextureAndFence); - _latestTextureAndFence = { 0, 0 }; + { + Lock lock(_latestTextureAndFenceMutex); + // If the most recent texture was unused, we can directly recycle it + if (_latestTextureAndFence.first) { + offscreenTextures.releaseTexture(_latestTextureAndFence); + _latestTextureAndFence = { 0, 0 }; + } } offscreenTextures.releaseSize(_size); } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 7aeac8d7c3..08c7ca6bf8 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -167,6 +167,9 @@ public slots: bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release = false); private: + using Mutex = std::mutex; + using Lock = std::unique_lock; + QQuickWindow* _quickWindow { nullptr }; QQmlContext* _qmlContext { nullptr }; QQuickItem* _rootItem { nullptr }; @@ -188,6 +191,7 @@ private: #endif // Texture management + Mutex _latestTextureAndFenceMutex; TextureAndFence _latestTextureAndFence { 0, 0 }; bool _render { false }; From a49097789a2e5fb67d0526b1e77ad0fb2d4c8505 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 31 Jan 2018 13:32:19 -0800 Subject: [PATCH 39/39] =?UTF-8?q?Revert=20"AddressBar=20trigger=20implemen?= =?UTF-8?q?ted.=20Fixed=20warnings=20on=20non=20existing=20contex=E2=80=A6?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface/src/Menu.cpp | 2 +- interface/src/ui/AddressBarDialog.h | 2 +- interface/src/ui/DialogsManager.cpp | 22 ++-------------------- interface/src/ui/DialogsManager.h | 4 +--- interface/src/ui/overlays/Web3DOverlay.cpp | 7 ------- 5 files changed, 5 insertions(+), 32 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f3d8ea2344..50e25287f1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -282,7 +282,7 @@ Menu::Menu() { // Navigate > Show Address Bar addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, - dialogsManager.data(), SLOT(toggleAddressBar())); + dialogsManager.data(), SLOT(showAddressBar())); // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here. auto locationBookmarks = DependencyManager::get(); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 7806436774..66f208ca90 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) - Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl CONSTANT) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) public: AddressBarDialog(QQuickItem* parent = nullptr); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 310a4cc1cd..ff2d4868df 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -58,7 +58,7 @@ void DialogsManager::showAddressBar() { hmd->openTablet(); } qApp->setKeyboardFocusOverlay(hmd->getCurrentTabletScreenID()); - setAddressBarVisible(true); + emit addressBarShown(true); } void DialogsManager::hideAddressBar() { @@ -71,7 +71,7 @@ void DialogsManager::hideAddressBar() { hmd->closeTablet(); } qApp->setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - setAddressBarVisible(false); + emit addressBarShown(false); } void DialogsManager::showFeed() { @@ -157,24 +157,6 @@ void DialogsManager::hmdToolsClosed() { } } -void DialogsManager::toggleAddressBar() { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - - const bool addressBarLoaded = tablet->isPathLoaded(TABLET_ADDRESS_DIALOG); - - if (_addressBarVisible || addressBarLoaded) { - hideAddressBar(); - } else { - showAddressBar(); - } -} - -void DialogsManager::setAddressBarVisible(bool addressBarVisible) { - _addressBarVisible = addressBarVisible; - emit addressBarShown(_addressBarVisible); -} - void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index f17ac39a7e..3a40b15a3b 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -39,7 +39,6 @@ public: QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } - void setAddressBarVisible(bool addressBarVisible); public slots: void showAddressBar(); @@ -53,7 +52,6 @@ public slots: void hmdTools(bool showTools); void showDomainConnectionDialog(); void showTestingResults(); - void toggleAddressBar(); // Application Update void showUpdateDialog(); @@ -80,7 +78,7 @@ private: QPointer _octreeStatsDialog; QPointer _testingDialog; QPointer _domainConnectionDialog; - bool _addressBarVisible { false }; + bool _closeAddressBar { false }; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 076b0f0114..8121b985cb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -72,13 +72,6 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface); connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface); connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); - - //need to be intialized before Tablet 1st open - _webSurface = DependencyManager::get()->acquire(_url); - _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :