mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-09 11:12:40 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into pastel
This commit is contained in:
commit
0885b00ab8
40 changed files with 733 additions and 178 deletions
|
@ -453,7 +453,7 @@ void EntityServer::domainSettingsRequestFailed() {
|
|||
void EntityServer::startDynamicDomainVerification() {
|
||||
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
||||
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainId().remove(QRegExp("\\{|\\}"));
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
||||
|
|
|
@ -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()
|
||||
|
|
7
cmake/macros/AddCustomQrcPath.cmake
Normal file
7
cmake/macros/AddCustomQrcPath.cmake
Normal file
|
@ -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()
|
14
cmake/macros/FindNPM.cmake
Normal file
14
cmake/macros/FindNPM.cmake
Normal file
|
@ -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()
|
|
@ -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}<file alias=\"${LOCAL_PATH}\">${IMPORT_PATH}</file>\n")
|
||||
endforeach()
|
||||
|
||||
set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE)
|
||||
configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT})
|
||||
endfunction()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -11,9 +11,17 @@ function(JOIN VALUES GLUE OUTPUT)
|
|||
set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(CUSTOM_INTERFACE_QRC_PATHS "")
|
||||
|
||||
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}
|
||||
|
@ -163,6 +171,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)
|
||||
|
@ -290,6 +303,7 @@ if (APPLE)
|
|||
|
||||
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources")
|
||||
|
||||
|
||||
# copy script files beside the executable
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
|
|
|
@ -32,6 +32,12 @@ Original.Button {
|
|||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
@ -59,6 +65,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]
|
||||
}
|
||||
|
@ -73,6 +81,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]
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -110,7 +110,17 @@ ModalWindow {
|
|||
}
|
||||
});
|
||||
|
||||
fileTableView.forceActiveFocus();
|
||||
focusTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: focusTimer
|
||||
interval: 10
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
fileTableView.contentItem.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -130,7 +140,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +162,11 @@ ModalWindow {
|
|||
size: 30
|
||||
enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""
|
||||
onClicked: d.navigateUp();
|
||||
Keys.onReturnPressed: { d.navigateUp(); }
|
||||
KeyNavigation.tab: homeButton
|
||||
KeyNavigation.backtab: upButton
|
||||
KeyNavigation.left: upButton
|
||||
KeyNavigation.right: homeButton
|
||||
}
|
||||
|
||||
GlyphButton {
|
||||
|
@ -160,6 +177,10 @@ ModalWindow {
|
|||
width: height
|
||||
enabled: d.homeDestination ? true : false
|
||||
onClicked: d.navigateHome();
|
||||
Keys.onReturnPressed: { d.navigateHome(); }
|
||||
KeyNavigation.tab: fileTableView.contentItem
|
||||
KeyNavigation.backtab: upButton
|
||||
KeyNavigation.left: upButton
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,9 +249,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 {
|
||||
|
@ -483,7 +510,6 @@ ModalWindow {
|
|||
}
|
||||
headerVisible: !selectDirectory
|
||||
onDoubleClicked: navigateToRow(row);
|
||||
focus: true
|
||||
Keys.onReturnPressed: navigateToCurrentRow();
|
||||
Keys.onEnterPressed: navigateToCurrentRow();
|
||||
|
||||
|
@ -560,7 +586,7 @@ ModalWindow {
|
|||
resizable: true
|
||||
}
|
||||
TableViewColumn {
|
||||
id: fileMofifiedColumn
|
||||
id: fileModifiedColumn
|
||||
role: "fileModified"
|
||||
title: "Date"
|
||||
width: 0.3 * fileTableView.width
|
||||
|
@ -571,7 +597,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
|
||||
|
@ -649,6 +675,8 @@ ModalWindow {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: root.saveDialog ? currentSelection : openButton
|
||||
}
|
||||
|
||||
TextField {
|
||||
|
@ -665,6 +693,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 {
|
||||
|
@ -675,8 +707,6 @@ ModalWindow {
|
|||
right: parent.right
|
||||
}
|
||||
visible: !selectDirectory && filtersCount > 1
|
||||
KeyNavigation.left: fileTableView
|
||||
KeyNavigation.right: openButton
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
|
@ -704,18 +734,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
|
||||
Keys.onReturnPressed: { cancelAction.trigger() }
|
||||
KeyNavigation.left: openButton
|
||||
KeyNavigation.right: fileTableView.contentItem
|
||||
Keys.onReturnPressed: { canceled(); root.enabled = false }
|
||||
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
|
||||
KeyNavigation.backtab: openButton
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ Item {
|
|||
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
flickableDirection: Flickable.AutoFlickIfNeeded
|
||||
keyNavigationEnabled: false
|
||||
highlightFollowsCurrentItem: false
|
||||
|
||||
|
@ -144,23 +144,20 @@ 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: {
|
||||
previousGridIndex = currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
setButtonState(previousGridIndex, "base state");
|
||||
setButtonState(currentIndex, "hover state");
|
||||
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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -105,14 +105,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
|
||||
|
@ -354,7 +354,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
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
#include "JSConsole.h"
|
||||
|
||||
#include <QFuture>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QStringListModel>
|
||||
#include <QListView>
|
||||
#include <QToolTip>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <ScriptEngines.h>
|
||||
|
@ -38,6 +40,21 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;";
|
|||
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\"><</span>";
|
||||
const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
|
||||
|
||||
const QString JSDOC_LINE_SEPARATOR = "\r";
|
||||
|
||||
const QString JSDOC_STYLE =
|
||||
"<style type=\"text/css\"> \
|
||||
.code { \
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace \
|
||||
} \
|
||||
pre, code { \
|
||||
display: inline; \
|
||||
} \
|
||||
.no-wrap { \
|
||||
white-space: nowrap; \
|
||||
} \
|
||||
</style>";
|
||||
|
||||
const QString JSConsole::_consoleFileName { "about:console" };
|
||||
|
||||
const QString JSON_KEY = "entries";
|
||||
|
@ -49,7 +66,7 @@ QList<QString> _readLines(const QString& filename) {
|
|||
// TODO: check root["version"]
|
||||
return root[JSON_KEY].toVariant().toStringList();
|
||||
}
|
||||
|
||||
|
||||
void _writeLines(const QString& filename, const QList<QString>& lines) {
|
||||
QFile file(filename);
|
||||
file.open(QFile::WriteOnly);
|
||||
|
@ -61,12 +78,71 @@ void _writeLines(const QString& filename, const QList<QString>& 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);
|
||||
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 &&
|
||||
object.value("kind").toString() != "typedef")) {
|
||||
|
||||
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 +154,174 @@ 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<void(QCompleter::*)(const QModelIndex&)>(&QCompleter::activated), this,
|
||||
&JSConsole::insertCompletion);
|
||||
|
||||
QObject::connect(_completer, static_cast<void(QCompleter::*)(const QModelIndex&)>(&QCompleter::highlighted), this,
|
||||
&JSConsole::highlightedCompletion);
|
||||
|
||||
setScriptEngine(scriptEngine);
|
||||
|
||||
resizeTextInput();
|
||||
|
||||
connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished()));
|
||||
connect(&_executeWatcher, &QFutureWatcher<QScriptValue>::finished, this, &JSConsole::commandFinished);
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
textCursor.movePosition(QTextCursor::Left);
|
||||
textCursor.movePosition(QTextCursor::EndOfWord);
|
||||
textCursor.insertText(completionString.right(extra));
|
||||
_ui->promptTextEdit->setTextCursor(textCursor);
|
||||
}
|
||||
|
||||
void JSConsole::highlightedCompletion(const QModelIndex& completion) {
|
||||
auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject();
|
||||
QString memberOf = "";
|
||||
if (!_completerModule.isEmpty()) {
|
||||
memberOf = _completerModule + ".";
|
||||
}
|
||||
auto name = memberOf + "<b>" + jsdocObject.value("name").toString() + "</b>";
|
||||
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 += "<i>[";
|
||||
}
|
||||
if (!firstItem) {
|
||||
name += ", ";
|
||||
} else {
|
||||
firstItem = false;
|
||||
}
|
||||
name += paramObject.value("name").toString();
|
||||
if (!hasDefaultParam && !paramObject.value("defaultvalue").isUndefined()) {
|
||||
hasDefaultParam = true;
|
||||
}
|
||||
}
|
||||
if (hasOptionalParam) {
|
||||
name += "]</i>";
|
||||
}
|
||||
|
||||
paramsTable += "<table border=\"1\" cellpadding=\"10\"><thead><tr><th>Name</th><th>Type</th>";
|
||||
if (hasDefaultParam) {
|
||||
paramsTable += "<th>Default</th>";
|
||||
}
|
||||
paramsTable += "<th>Description</th></tr></thead><tbody>";
|
||||
foreach(auto param, params) {
|
||||
auto paramObject = param.toObject();
|
||||
paramsTable += "<tr><td>" + paramObject.value("name").toString() + "</td><td>" +
|
||||
_jsdocTypeToString(paramObject.value("type")) + "</td><td>";
|
||||
if (hasDefaultParam) {
|
||||
paramsTable += paramObject.value("defaultvalue").toVariant().toString() + "</td><td>";
|
||||
}
|
||||
paramsTable += paramObject.value("description").toString() + "</td></tr>";
|
||||
}
|
||||
|
||||
paramsTable += "</tbody></table>";
|
||||
}
|
||||
name += ")";
|
||||
} else if (!jsdocObject.value("type").isUndefined()){
|
||||
returnTypeText = _jsdocTypeToString(jsdocObject.value("type")) + " ";
|
||||
}
|
||||
auto popupText = JSDOC_STYLE + "<span class=\"no-wrap\">" + returnTypeText + name + "</span>";
|
||||
auto descriptionText = "<p>" + description.replace(JSDOC_LINE_SEPARATOR, "<br>") + "</p>";
|
||||
|
||||
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, "<br>");
|
||||
popupText += "<h4>Returns</h4><p>" + returnsDescription + "</p><h5>Type</h5><pre><code>" +
|
||||
_jsdocTypeToString(returnsObject.value("type")) + "</code></pre>";
|
||||
}
|
||||
}
|
||||
|
||||
if (!examples.isEmpty()) {
|
||||
popupText += "<h4>Examples</h4>";
|
||||
foreach(auto example, examples) {
|
||||
auto exampleText = example.toString();
|
||||
auto exampleLines = exampleText.split(JSDOC_LINE_SEPARATOR);
|
||||
foreach(auto exampleLine, exampleLines) {
|
||||
if (exampleLine.contains("<caption>")) {
|
||||
popupText += exampleLine.replace("caption>", "h5>");
|
||||
} else {
|
||||
popupText += "<pre><code>" + exampleLine + "\n</code></pre>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QToolTip::showText(QPoint(_completer->popup()->pos().x() + _completer->popup()->width(), _completer->popup()->pos().y()),
|
||||
popupText, _completer->popup());
|
||||
}
|
||||
|
||||
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<ScriptEngines>()->loadScript(_consoleFileName, false);
|
||||
} else {
|
||||
|
@ -199,45 +411,121 @@ 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<QKeyEvent*>(event);
|
||||
int key = keyEvent->key();
|
||||
if ((sender == _ui->promptTextEdit || sender == _completer->popup()) && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(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->popup()->currentIndex());
|
||||
_completer->popup()->hide();
|
||||
return true;
|
||||
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<QKeyEvent*>(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
|
||||
|
||||
if (!isCompleterShortcut && (!keyEvent->text().isEmpty() && eow.contains(keyEvent->text().right(1)))) {
|
||||
_completer->popup()->hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto textCursor = _ui->promptTextEdit->textCursor();
|
||||
|
||||
textCursor.select(QTextCursor::WordUnderCursor);
|
||||
|
||||
QString completionPrefix = textCursor.selectedText();
|
||||
|
||||
auto leftOfCursor = _ui->promptTextEdit->toPlainText().left(textCursor.position());
|
||||
|
||||
// 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_]*)$");
|
||||
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);
|
||||
_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);
|
||||
highlightedCompletion(_completer->popup()->currentIndex());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -321,7 +609,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;
|
||||
}
|
||||
|
|
|
@ -12,12 +12,11 @@
|
|||
#ifndef hifi_JSConsole_h
|
||||
#define hifi_JSConsole_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QEvent>
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QSharedPointer>
|
||||
#include <QCompleter>
|
||||
#include <QtCore/QJsonArray>
|
||||
|
||||
#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<QScriptValue> _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 {""};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1679,14 +1679,17 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void EntityTree::fixupNeedsParentFixups() {
|
||||
PROFILE_RANGE(simulation_physics, "FixupParents");
|
||||
MovingEntitiesOperator moveOperator;
|
||||
QVector<EntityItemWeakPointer> entitiesToFixup;
|
||||
{
|
||||
QWriteLocker locker(&_needsParentFixupLock);
|
||||
entitiesToFixup = _needsParentFixup;
|
||||
_needsParentFixup.clear();
|
||||
}
|
||||
|
||||
QWriteLocker locker(&_needsParentFixupLock);
|
||||
|
||||
QMutableVectorIterator<EntityItemWeakPointer> iter(_needsParentFixup);
|
||||
QMutableVectorIterator<EntityItemWeakPointer> iter(entitiesToFixup);
|
||||
while (iter.hasNext()) {
|
||||
EntityItemWeakPointer entityWP = iter.next();
|
||||
EntityItemPointer entity = entityWP.lock();
|
||||
|
@ -1749,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) {
|
||||
|
|
|
@ -23,25 +23,31 @@
|
|||
#include <shared/GlobalAppProperties.h>
|
||||
#include <GLMHelpers.h>
|
||||
#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<size_t> 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);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace gl {
|
|||
Context(const Context& other);
|
||||
|
||||
public:
|
||||
static bool enableDebugLogger();
|
||||
Context();
|
||||
Context(QWindow* window);
|
||||
void release();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QOffscreenSurface>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QOpenGLDebugLogger>
|
||||
|
||||
#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, []{
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -776,7 +776,7 @@ void AddressManager::copyPath() {
|
|||
QApplication::clipboard()->setText(currentPath());
|
||||
}
|
||||
|
||||
QString AddressManager::getDomainId() const {
|
||||
QString AddressManager::getDomainID() const {
|
||||
return DependencyManager::get<NodeList>()->getDomainHandler().getUUID().toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
* <em>Read-only.</em>
|
||||
* @property {Uuid} domainId - Synonym for <code>domainId</code>. <em>Read-only.</em> <strong>Deprecated:</strong> This property
|
||||
* is deprecated and will soon be removed.
|
||||
* @property {string} hostname - The name of the domain for your current metaverse address (e.g., <code>"AvatarIsland"</code>,
|
||||
* <code>localhost</code>, or an IP address).
|
||||
* <em>Read-only.</em>
|
||||
|
@ -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; }
|
||||
|
||||
|
|
|
@ -340,14 +340,6 @@ QSharedPointer<Resource> 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<Resource>();
|
||||
}
|
||||
|
||||
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
|
||||
return getResource(fallback, QUrl());
|
||||
}
|
||||
|
@ -358,6 +350,7 @@ QSharedPointer<Resource> 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -543,6 +543,7 @@ void OffscreenQmlSurface::cleanup() {
|
|||
|
||||
void OffscreenQmlSurface::render() {
|
||||
#if !defined(DISABLE_QML)
|
||||
|
||||
if (nsightActive()) {
|
||||
return;
|
||||
}
|
||||
|
@ -569,14 +570,18 @@ void OffscreenQmlSurface::render() {
|
|||
|
||||
{
|
||||
// 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();
|
||||
|
@ -588,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;
|
||||
}
|
||||
|
||||
|
@ -813,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);
|
||||
}
|
||||
|
|
|
@ -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<std::mutex>;
|
||||
|
||||
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 };
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
add_subdirectory(scribe)
|
||||
set_target_properties(scribe 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)
|
||||
set_target_properties(udt-test PROPERTIES FOLDER "Tools")
|
||||
|
@ -27,4 +33,3 @@ if (BUILD_TOOLS)
|
|||
add_subdirectory(auto-tester)
|
||||
set_target_properties(auto-tester PROPERTIES FOLDER "Tools")
|
||||
endif()
|
||||
|
||||
|
|
17
tools/jsdoc/CMakeLists.txt
Normal file
17
tools/jsdoc/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
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)
|
||||
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}
|
||||
WORKING_DIRECTORY ${JSDOC_WORKING_DIR}
|
||||
COMMENT "generate the JSDoc JSON for the JSConsole auto-completer"
|
||||
)
|
|
@ -4,5 +4,8 @@
|
|||
"outputSourceFiles": false
|
||||
}
|
||||
},
|
||||
"plugins": ["plugins/hifi"]
|
||||
"plugins": [
|
||||
"plugins/hifi",
|
||||
"plugins/hifiJSONExport"
|
||||
]
|
||||
}
|
||||
|
|
7
tools/jsdoc/package.json
Normal file
7
tools/jsdoc/package.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "hifiJSDoc",
|
||||
"dependencies": {
|
||||
"jsdoc": "^3.5.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
|
@ -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;
|
||||
|
|
19
tools/jsdoc/plugins/hifiJSONExport.js
Normal file
19
tools/jsdoc/plugins/hifiJSONExport.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
exports.handlers = {
|
||||
processingComplete: function(e) {
|
||||
const pathTools = require('path');
|
||||
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(outputFolder, "hifiJSDoc.json"), JSON.stringify(doclets, null, 4), function(err) {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
|
||||
console.log("The Hifi JSDoc JSON was saved!");
|
||||
});
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue