mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 13:53:38 +02:00
Merge branch 'tablet-ui' into 21197
This commit is contained in:
commit
0069569e96
17 changed files with 1217 additions and 95 deletions
339
interface/resources/qml/dialogs/TabletCustomQueryDialog.qml
Normal file
339
interface/resources/qml/dialogs/TabletCustomQueryDialog.qml
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
//
|
||||||
|
// TabletCustomQueryDialog.qml
|
||||||
|
//
|
||||||
|
// Created by Vlad Stelmahovsky on 3/27/17
|
||||||
|
// Copyright 2017 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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
|
|
||||||
|
import "../controls-uit" as ControlsUIT
|
||||||
|
import "../styles-uit"
|
||||||
|
import "../windows"
|
||||||
|
|
||||||
|
TabletModalWindow {
|
||||||
|
id: root;
|
||||||
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
|
y: hifi.dimensions.tabletMenuHeader
|
||||||
|
|
||||||
|
title: ""
|
||||||
|
visible: true;
|
||||||
|
property bool keyboardOverride: true
|
||||||
|
|
||||||
|
signal selected(var result);
|
||||||
|
signal canceled();
|
||||||
|
|
||||||
|
property int icon: hifi.icons.none;
|
||||||
|
property string iconText: "";
|
||||||
|
property int iconSize: 35;
|
||||||
|
onIconChanged: updateIcon();
|
||||||
|
|
||||||
|
property var textInput;
|
||||||
|
property var comboBox;
|
||||||
|
property var checkBox;
|
||||||
|
onTextInputChanged: {
|
||||||
|
if (textInput && textInput.text !== undefined) {
|
||||||
|
textField.text = textInput.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onComboBoxChanged: {
|
||||||
|
if (comboBox && comboBox.index !== undefined) {
|
||||||
|
comboBoxField.currentIndex = comboBox.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCheckBoxChanged: {
|
||||||
|
if (checkBox && checkBox.checked !== undefined) {
|
||||||
|
checkBoxField.checked = checkBox.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool keyboardEnabled: false
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
onKeyboardRaisedChanged: d.resize();
|
||||||
|
|
||||||
|
property var warning: "";
|
||||||
|
property var result;
|
||||||
|
|
||||||
|
property var implicitCheckState: null;
|
||||||
|
|
||||||
|
property int titleWidth: 0;
|
||||||
|
onTitleWidthChanged: d.resize();
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateIcon() {
|
||||||
|
if (!root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
iconText = hifi.glyphForIcon(root.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCheckbox() {
|
||||||
|
if (checkBox.disableForItems) {
|
||||||
|
var currentItemInDisableList = false;
|
||||||
|
for (var i in checkBox.disableForItems) {
|
||||||
|
if (comboBoxField.currentIndex === checkBox.disableForItems[i]) {
|
||||||
|
currentItemInDisableList = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentItemInDisableList) {
|
||||||
|
checkBoxField.enabled = false;
|
||||||
|
if (checkBox.checkStateOnDisable !== null && checkBox.checkStateOnDisable !== undefined) {
|
||||||
|
root.implicitCheckState = checkBoxField.checked;
|
||||||
|
checkBoxField.checked = checkBox.checkStateOnDisable;
|
||||||
|
}
|
||||||
|
root.warning = checkBox.warningOnDisable;
|
||||||
|
} else {
|
||||||
|
checkBoxField.enabled = true;
|
||||||
|
if (root.implicitCheckState !== null) {
|
||||||
|
checkBoxField.checked = root.implicitCheckState;
|
||||||
|
root.implicitCheckState = null;
|
||||||
|
}
|
||||||
|
root.warning = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TabletModalFrame {
|
||||||
|
id: modalWindowItem
|
||||||
|
width: parent.width - 12
|
||||||
|
height: 240
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d;
|
||||||
|
readonly property int minWidth: 470
|
||||||
|
readonly property int maxWidth: 470
|
||||||
|
readonly property int minHeight: 240
|
||||||
|
readonly property int maxHeight: 720
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
var targetWidth = Math.max(titleWidth, 470);
|
||||||
|
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
|
||||||
|
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
|
||||||
|
(buttons.height + 3 * hifi.dimensions.contentSpacing.y) +
|
||||||
|
((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0);
|
||||||
|
|
||||||
|
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
|
||||||
|
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
|
||||||
|
d.maxHeight : targetHeight);
|
||||||
|
if (checkBoxField.visible && comboBoxField.visible) {
|
||||||
|
checkBoxField.width = extraInputs.width / 2;
|
||||||
|
comboBoxField.width = extraInputs.width / 2;
|
||||||
|
} else if (!checkBoxField.visible && comboBoxField.visible) {
|
||||||
|
comboBoxField.width = extraInputs.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Item {
|
||||||
|
// anchors {
|
||||||
|
// // top: parent.top;
|
||||||
|
// // bottom: extraInputs.visible ? extraInputs.top : buttons.top;
|
||||||
|
// left: parent.left;
|
||||||
|
// right: parent.right;
|
||||||
|
// topMargin: 20
|
||||||
|
// }
|
||||||
|
|
||||||
|
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||||
|
ControlsUIT.TextField {
|
||||||
|
id: textField;
|
||||||
|
label: root.textInput.label;
|
||||||
|
focus: root.textInput ? true : false;
|
||||||
|
visible: root.textInput ? true : false;
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
leftMargin: 5; rightMargin: 5;
|
||||||
|
topMargin: textField.controlHeight - textField.height + 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlsUIT.Keyboard {
|
||||||
|
id: keyboard
|
||||||
|
raised: keyboardEnabled && keyboardRaised
|
||||||
|
numeric: punctuationMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 5; rightMargin: 5;
|
||||||
|
top: textField.bottom
|
||||||
|
topMargin: raised ? hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: extraInputs;
|
||||||
|
visible: Boolean(root.checkBox || root.comboBox);
|
||||||
|
anchors {
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
leftMargin: 5; rightMargin: 5;
|
||||||
|
top: keyboard.bottom;
|
||||||
|
topMargin: hifi.dimensions.contentSpacing.y + 20;
|
||||||
|
}
|
||||||
|
height: comboBoxField.controlHeight;
|
||||||
|
onHeightChanged: d.resize();
|
||||||
|
onWidthChanged: d.resize();
|
||||||
|
|
||||||
|
ControlsUIT.CheckBox {
|
||||||
|
id: checkBoxField;
|
||||||
|
text: root.checkBox.label;
|
||||||
|
focus: Boolean(root.checkBox);
|
||||||
|
visible: Boolean(root.checkBox);
|
||||||
|
// anchors {
|
||||||
|
// left: parent.left;
|
||||||
|
// bottom: parent.bottom;
|
||||||
|
// leftMargin: 6; // Magic number to align with warning icon
|
||||||
|
// bottomMargin: 6;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlsUIT.TabletComboBox {
|
||||||
|
id: comboBoxField;
|
||||||
|
label: root.comboBox.label;
|
||||||
|
focus: Boolean(root.comboBox);
|
||||||
|
visible: Boolean(root.comboBox);
|
||||||
|
// anchors {
|
||||||
|
// right: parent.right;
|
||||||
|
// bottom: parent.bottom;
|
||||||
|
// }
|
||||||
|
model: root.comboBox ? root.comboBox.items : [];
|
||||||
|
onAccepted: {
|
||||||
|
updateCheckbox();
|
||||||
|
focus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttons;
|
||||||
|
focus: true;
|
||||||
|
spacing: hifi.dimensions.contentSpacing.x;
|
||||||
|
layoutDirection: Qt.RightToLeft;
|
||||||
|
onHeightChanged: d.resize();
|
||||||
|
onWidthChanged: {
|
||||||
|
d.resize();
|
||||||
|
resizeWarningText();
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom;
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||||
|
leftMargin: 5; rightMargin: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeWarningText() {
|
||||||
|
var rowWidth = buttons.width;
|
||||||
|
var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2;
|
||||||
|
var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x;
|
||||||
|
warningText.width = rowWidth - buttonsWidth - warningIconWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlsUIT.Button {
|
||||||
|
id: cancelButton;
|
||||||
|
action: cancelAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlsUIT.Button {
|
||||||
|
id: acceptButton;
|
||||||
|
action: acceptAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: warningText;
|
||||||
|
visible: Boolean(root.warning);
|
||||||
|
text: root.warning;
|
||||||
|
wrapMode: Text.WordWrap;
|
||||||
|
font.italic: true;
|
||||||
|
maximumLineCount: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: warningIcon;
|
||||||
|
visible: Boolean(root.warning);
|
||||||
|
text: hifi.glyphs.alert;
|
||||||
|
size: hifi.dimensions.controlLineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }//column
|
||||||
|
Action {
|
||||||
|
id: cancelAction;
|
||||||
|
text: qsTr("Cancel");
|
||||||
|
shortcut: Qt.Key_Escape;
|
||||||
|
onTriggered: {
|
||||||
|
root.result = null;
|
||||||
|
root.canceled();
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: acceptAction;
|
||||||
|
text: qsTr("Add");
|
||||||
|
shortcut: Qt.Key_Return;
|
||||||
|
onTriggered: {
|
||||||
|
var result = {};
|
||||||
|
if (textInput) {
|
||||||
|
result.textInput = textField.text;
|
||||||
|
}
|
||||||
|
if (comboBox) {
|
||||||
|
result.comboBox = comboBoxField.currentIndex;
|
||||||
|
result.comboBoxText = comboBoxField.currentText;
|
||||||
|
}
|
||||||
|
if (checkBox) {
|
||||||
|
result.checkBox = checkBoxField.enabled ? checkBoxField.checked : null;
|
||||||
|
}
|
||||||
|
root.result = JSON.stringify(result);
|
||||||
|
root.selected(root.result);
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
if (!visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
case Qt.Key_Back:
|
||||||
|
cancelAction.trigger();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt.Key_Return:
|
||||||
|
case Qt.Key_Enter:
|
||||||
|
acceptAction.trigger();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
keyboardEnabled = HMD.active;
|
||||||
|
updateIcon();
|
||||||
|
updateCheckbox();
|
||||||
|
d.resize();
|
||||||
|
textField.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
|
@ -151,11 +151,12 @@ TabletModalWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*TabletComboBox {
|
TabletComboBox {
|
||||||
id: pathSelector
|
id: pathSelector
|
||||||
anchors {
|
z: 10
|
||||||
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: hifi.dimensions.contentMargin.y
|
topMargin: (fileDialogItem.hasTitle ? (fileDialogItem.frameMarginTop + hifi.dimensions.modalDialogMargin.y) : hifi.dimension.modalDialogMargin.y)
|
||||||
left: navControls.right
|
left: navControls.right
|
||||||
leftMargin: hifi.dimensions.contentSpacing.x
|
leftMargin: hifi.dimensions.contentSpacing.x
|
||||||
right: parent.right
|
right: parent.right
|
||||||
|
@ -219,7 +220,7 @@ TabletModalWindow {
|
||||||
fileTableView.forceActiveFocus();
|
fileTableView.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
614
interface/resources/qml/hifi/dialogs/TabletAssetServer.qml
Normal file
614
interface/resources/qml/hifi/dialogs/TabletAssetServer.qml
Normal file
|
@ -0,0 +1,614 @@
|
||||||
|
//
|
||||||
|
// TabletAssetServer.qml
|
||||||
|
//
|
||||||
|
// Created by Vlad Stelmahovsky on 3/3/17
|
||||||
|
// Copyright 2017 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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
|
import Qt.labs.settings 1.0
|
||||||
|
|
||||||
|
import "../../styles-uit"
|
||||||
|
import "../../controls-uit" as HifiControls
|
||||||
|
import "../../windows"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
objectName: "AssetServer"
|
||||||
|
|
||||||
|
property var eventBridge;
|
||||||
|
signal sendToScript(var message);
|
||||||
|
property bool isHMD: false
|
||||||
|
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
|
||||||
|
property int colorScheme: hifi.colorSchemes.dark
|
||||||
|
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
property var scripts: ScriptDiscoveryService;
|
||||||
|
property var assetProxyModel: Assets.proxyModel;
|
||||||
|
property var assetMappingsModel: Assets.mappingModel;
|
||||||
|
property var currentDirectory;
|
||||||
|
|
||||||
|
Settings {
|
||||||
|
category: "Overlay.AssetServer"
|
||||||
|
property alias directory: root.currentDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
isHMD = HMD.active;
|
||||||
|
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
||||||
|
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function doDeleteFile(path) {
|
||||||
|
console.log("Deleting " + path);
|
||||||
|
|
||||||
|
Assets.deleteMappings(path, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Asset browser - error deleting path: ", path, err);
|
||||||
|
|
||||||
|
box = errorMessageBox("There was an error deleting:\n" + path + "\n" + err);
|
||||||
|
box.selected.connect(reload);
|
||||||
|
} else {
|
||||||
|
console.log("Asset browser - finished deleting path: ", path);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRenameFile(oldPath, newPath) {
|
||||||
|
|
||||||
|
if (newPath[0] !== "/") {
|
||||||
|
newPath = "/" + newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldPath[oldPath.length - 1] === '/' && newPath[newPath.length - 1] != '/') {
|
||||||
|
// this is a folder rename but the user neglected to add a trailing slash when providing a new path
|
||||||
|
newPath = newPath + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Assets.isKnownFolder(newPath)) {
|
||||||
|
box = errorMessageBox("Cannot overwrite existing directory.");
|
||||||
|
box.selected.connect(reload);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Asset browser - renaming " + oldPath + " to " + newPath);
|
||||||
|
|
||||||
|
Assets.renameMapping(oldPath, newPath, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.log("Asset browser - error renaming: ", oldPath, "=>", newPath, " - error ", err);
|
||||||
|
box = errorMessageBox("There was an error renaming:\n" + oldPath + " to " + newPath + "\n" + err);
|
||||||
|
box.selected.connect(reload);
|
||||||
|
} else {
|
||||||
|
console.log("Asset browser - finished rename: ", oldPath, "=>", newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileExists(path) {
|
||||||
|
return Assets.isKnownMapping(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function askForOverwrite(path, callback) {
|
||||||
|
var object = tabletRoot.messageBox({
|
||||||
|
icon: hifi.icons.question,
|
||||||
|
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
|
||||||
|
defaultButton: OriginalDialogs.StandardButton.No,
|
||||||
|
title: "Overwrite File",
|
||||||
|
text: path + "\n" + "This file already exists. Do you want to overwrite it?"
|
||||||
|
});
|
||||||
|
object.selected.connect(function(button) {
|
||||||
|
if (button === OriginalDialogs.StandardButton.Yes) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function canAddToWorld(path) {
|
||||||
|
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
|
||||||
|
|
||||||
|
return supportedExtensions.reduce(function(total, current) {
|
||||||
|
return total | new RegExp(current).test(path);
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
Assets.mappingModel.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
Assets.mappingModel.refresh();
|
||||||
|
treeView.selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGetMappingsError(errorString) {
|
||||||
|
errorMessageBox(
|
||||||
|
"There was a problem retreiving the list of assets from your Asset Server.\n"
|
||||||
|
+ errorString
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToWorld() {
|
||||||
|
var defaultURL = assetProxyModel.data(treeView.selection.currentIndex, 0x103);
|
||||||
|
|
||||||
|
if (!defaultURL || !canAddToWorld(defaultURL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var SHAPE_TYPE_NONE = 0;
|
||||||
|
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||||
|
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||||
|
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||||
|
|
||||||
|
var SHAPE_TYPES = [];
|
||||||
|
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||||
|
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||||
|
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||||
|
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||||
|
|
||||||
|
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||||
|
var DYNAMIC_DEFAULT = false;
|
||||||
|
var prompt = tabletRoot.customInputDialog({
|
||||||
|
textInput: {
|
||||||
|
label: "Model URL",
|
||||||
|
text: defaultURL
|
||||||
|
},
|
||||||
|
comboBox: {
|
||||||
|
label: "Automatic Collisions",
|
||||||
|
index: SHAPE_TYPE_DEFAULT,
|
||||||
|
items: SHAPE_TYPES
|
||||||
|
},
|
||||||
|
checkBox: {
|
||||||
|
label: "Dynamic",
|
||||||
|
checked: DYNAMIC_DEFAULT,
|
||||||
|
disableForItems: [
|
||||||
|
SHAPE_TYPE_STATIC_MESH
|
||||||
|
],
|
||||||
|
checkStateOnDisable: false,
|
||||||
|
warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
prompt.selected.connect(function (jsonResult) {
|
||||||
|
if (jsonResult) {
|
||||||
|
var result = JSON.parse(jsonResult);
|
||||||
|
var url = result.textInput.trim();
|
||||||
|
var shapeType;
|
||||||
|
switch (result.comboBox) {
|
||||||
|
case SHAPE_TYPE_SIMPLE_HULL:
|
||||||
|
shapeType = "simple-hull";
|
||||||
|
break;
|
||||||
|
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
||||||
|
shapeType = "simple-compound";
|
||||||
|
break;
|
||||||
|
case SHAPE_TYPE_STATIC_MESH:
|
||||||
|
shapeType = "static-mesh";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
shapeType = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT;
|
||||||
|
if (shapeType === "static-mesh" && dynamic) {
|
||||||
|
// The prompt should prevent this case
|
||||||
|
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||||
|
} else if (url) {
|
||||||
|
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||||
|
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation)));
|
||||||
|
var gravity;
|
||||||
|
if (dynamic) {
|
||||||
|
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||||
|
// different scripting engine from QTScript.
|
||||||
|
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10);
|
||||||
|
} else {
|
||||||
|
gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||||
|
|
||||||
|
// Entities.addEntity doesn't work from QML, so we use this.
|
||||||
|
Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyURLToClipboard(index) {
|
||||||
|
if (!index) {
|
||||||
|
index = treeView.selection.currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = assetProxyModel.data(treeView.selection.currentIndex, 0x103);
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Window.copyToClipboard(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renameEl(index, data) {
|
||||||
|
if (!index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = assetProxyModel.data(index, 0x100);
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var destinationPath = path.split('/');
|
||||||
|
destinationPath[destinationPath.length - (path[path.length - 1] === '/' ? 2 : 1)] = data;
|
||||||
|
destinationPath = destinationPath.join('/').trim();
|
||||||
|
|
||||||
|
if (path === destinationPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fileExists(destinationPath)) {
|
||||||
|
doRenameFile(path, destinationPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renameFile(index) {
|
||||||
|
if (!index) {
|
||||||
|
index = treeView.selection.currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = assetProxyModel.data(index, 0x100);
|
||||||
|
if (!path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var object = tabletRoot.inputDialog({
|
||||||
|
label: "Enter new path:",
|
||||||
|
current: path,
|
||||||
|
placeholderText: "Enter path here"
|
||||||
|
});
|
||||||
|
object.selected.connect(function(destinationPath) {
|
||||||
|
destinationPath = destinationPath.trim();
|
||||||
|
|
||||||
|
if (path === destinationPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fileExists(destinationPath)) {
|
||||||
|
askForOverwrite(destinationPath, function() {
|
||||||
|
doRenameFile(path, destinationPath);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
doRenameFile(path, destinationPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function deleteFile(index) {
|
||||||
|
if (!index) {
|
||||||
|
index = treeView.selection.currentIndex;
|
||||||
|
}
|
||||||
|
var path = assetProxyModel.data(index, 0x100);
|
||||||
|
if (!path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
||||||
|
var typeString = isFolder ? 'folder' : 'file';
|
||||||
|
|
||||||
|
var object = tabletRoot.messageBox({
|
||||||
|
icon: hifi.icons.question,
|
||||||
|
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
|
||||||
|
defaultButton: OriginalDialogs.StandardButton.Yes,
|
||||||
|
title: "Delete",
|
||||||
|
text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"
|
||||||
|
});
|
||||||
|
object.selected.connect(function(button) {
|
||||||
|
if (button === OriginalDialogs.StandardButton.Yes) {
|
||||||
|
doDeleteFile(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: doUploadTimer
|
||||||
|
property var url
|
||||||
|
property bool isConnected: false
|
||||||
|
interval: 5
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool uploadOpen: false;
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
}
|
||||||
|
function uploadClicked(fileUrl) {
|
||||||
|
if (uploadOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uploadOpen = true;
|
||||||
|
|
||||||
|
function doUpload(url, dropping) {
|
||||||
|
var fileUrl = fileDialogHelper.urlToPath(url);
|
||||||
|
|
||||||
|
var path = assetProxyModel.data(treeView.selection.currentIndex, 0x100);
|
||||||
|
var directory = path ? path.slice(0, path.lastIndexOf('/') + 1) : "/";
|
||||||
|
var filename = fileUrl.slice(fileUrl.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
Assets.uploadFile(fileUrl, directory + filename,
|
||||||
|
function() {
|
||||||
|
// Upload started
|
||||||
|
uploadSpinner.visible = true;
|
||||||
|
uploadButton.enabled = false;
|
||||||
|
uploadProgressLabel.text = "In progress...";
|
||||||
|
},
|
||||||
|
function(err, path) {
|
||||||
|
print(err, path);
|
||||||
|
if (err === "") {
|
||||||
|
uploadProgressLabel.text = "Upload Complete";
|
||||||
|
timer.interval = 1000;
|
||||||
|
timer.repeat = false;
|
||||||
|
timer.triggered.connect(function() {
|
||||||
|
uploadSpinner.visible = false;
|
||||||
|
uploadButton.enabled = true;
|
||||||
|
uploadOpen = false;
|
||||||
|
});
|
||||||
|
timer.start();
|
||||||
|
console.log("Asset Browser - finished uploading: ", fileUrl);
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
uploadSpinner.visible = false;
|
||||||
|
uploadButton.enabled = true;
|
||||||
|
uploadOpen = false;
|
||||||
|
|
||||||
|
if (err !== -1) {
|
||||||
|
console.log("Asset Browser - error uploading: ", fileUrl, " - error ", err);
|
||||||
|
var box = errorMessageBox("There was an error uploading:\n" + fileUrl + "\n" + err);
|
||||||
|
box.selected.connect(reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, dropping);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiateUpload(url) {
|
||||||
|
doUpload(doUploadTimer.url, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileUrl) {
|
||||||
|
doUpload(fileUrl, true);
|
||||||
|
} else {
|
||||||
|
var browser = tabletRoot.fileDialog({
|
||||||
|
selectDirectory: false,
|
||||||
|
dir: currentDirectory
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.canceled.connect(function() {
|
||||||
|
uploadOpen = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.selectedFile.connect(function(url) {
|
||||||
|
currentDirectory = browser.dir;
|
||||||
|
|
||||||
|
// Initiate upload from a timer so that file browser dialog can close beforehand.
|
||||||
|
doUploadTimer.url = url;
|
||||||
|
if (!doUploadTimer.isConnected) {
|
||||||
|
doUploadTimer.triggered.connect(function() { initiateUpload(); });
|
||||||
|
doUploadTimer.isConnected = true;
|
||||||
|
}
|
||||||
|
doUploadTimer.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorMessageBox(message) {
|
||||||
|
return tabletRoot.messageBox({
|
||||||
|
icon: hifi.icons.warning,
|
||||||
|
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||||
|
title: "Error",
|
||||||
|
text: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
y: hifi.dimensions.tabletMenuHeader //-bgNavBar
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
HifiControls.TabletContentSection {
|
||||||
|
id: assetDirectory
|
||||||
|
name: "Asset Directory"
|
||||||
|
isFirst: true
|
||||||
|
|
||||||
|
HifiControls.VerticalSpacer {}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
width: parent.width
|
||||||
|
height: 30
|
||||||
|
spacing: hifi.dimensions.contentSpacing.x
|
||||||
|
|
||||||
|
HifiControls.GlyphButton {
|
||||||
|
glyph: hifi.glyphs.reload
|
||||||
|
color: hifi.buttons.black
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: hifi.dimensions.controlLineHeight
|
||||||
|
|
||||||
|
onClicked: root.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Button {
|
||||||
|
text: "Add To World"
|
||||||
|
color: hifi.buttons.black
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: 120
|
||||||
|
|
||||||
|
enabled: canAddToWorld(assetProxyModel.data(treeView.selection.currentIndex, 0x100))
|
||||||
|
|
||||||
|
onClicked: root.addToWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Button {
|
||||||
|
text: "Rename"
|
||||||
|
color: hifi.buttons.black
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: 80
|
||||||
|
|
||||||
|
onClicked: root.renameFile()
|
||||||
|
enabled: treeView.selection.hasSelection
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Button {
|
||||||
|
id: deleteButton
|
||||||
|
|
||||||
|
text: "Delete"
|
||||||
|
color: hifi.buttons.red
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: 80
|
||||||
|
|
||||||
|
onClicked: root.deleteFile()
|
||||||
|
enabled: treeView.selection.hasSelection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: contextMenu
|
||||||
|
title: "Edit"
|
||||||
|
property var url: ""
|
||||||
|
property var currentIndex: null
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Copy URL"
|
||||||
|
onTriggered: {
|
||||||
|
copyURLToClipboard(contextMenu.currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Rename"
|
||||||
|
onTriggered: {
|
||||||
|
renameFile(contextMenu.currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Delete"
|
||||||
|
onTriggered: {
|
||||||
|
deleteFile(contextMenu.currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
HifiControls.Tree {
|
||||||
|
id: treeView
|
||||||
|
height: 430
|
||||||
|
anchors.leftMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border
|
||||||
|
anchors.rightMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
treeModel: assetProxyModel
|
||||||
|
canEdit: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
|
modifyEl: renameEl
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
propagateComposedEvents: true
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onClicked: {
|
||||||
|
if (!HMD.active) { // Popup only displays properly on desktop
|
||||||
|
var index = treeView.indexAt(mouse.x, mouse.y);
|
||||||
|
treeView.selection.setCurrentIndex(index, 0x0002);
|
||||||
|
contextMenu.currentIndex = index;
|
||||||
|
contextMenu.popup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HifiControls.TabletContentSection {
|
||||||
|
id: uploadSection
|
||||||
|
name: "Upload A File"
|
||||||
|
spacing: hifi.dimensions.contentSpacing.y
|
||||||
|
//anchors.bottom: parent.bottom
|
||||||
|
height: 65
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
HifiControls.Button {
|
||||||
|
id: uploadButton
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
text: "Choose File"
|
||||||
|
color: hifi.buttons.blue
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
height: 30
|
||||||
|
width: 155
|
||||||
|
|
||||||
|
onClicked: uploadClickedTimer.running = true
|
||||||
|
|
||||||
|
// For some reason trigginer an API that enters
|
||||||
|
// an internal event loop directly from the button clicked
|
||||||
|
// trigger below causes the appliction to behave oddly.
|
||||||
|
// Most likely because the button onClicked handling is never
|
||||||
|
// completed until the function returns.
|
||||||
|
// FIXME find a better way of handling the input dialogs that
|
||||||
|
// doesn't trigger this.
|
||||||
|
Timer {
|
||||||
|
id: uploadClickedTimer
|
||||||
|
interval: 5
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
onTriggered: uploadClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: uploadSpinner
|
||||||
|
visible: false
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: 40
|
||||||
|
height: 32
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
source: "../../../images/Loading-Outer-Ring.png"
|
||||||
|
RotationAnimation on rotation {
|
||||||
|
loops: Animation.Infinite
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
duration: 2000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
source: "../../../images/Loading-Inner-H.png"
|
||||||
|
}
|
||||||
|
HifiControls.Label {
|
||||||
|
id: uploadProgressLabel
|
||||||
|
anchors.left: image.right
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.verticalCenter: image.verticalCenter
|
||||||
|
text: "In progress..."
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.0
|
||||||
import Hifi 1.0
|
import Hifi 1.0
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import "../../dialogs"
|
import "../../dialogs"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: tabletRoot
|
id: tabletRoot
|
||||||
objectName: "tabletRoot"
|
objectName: "tabletRoot"
|
||||||
|
@ -29,7 +30,9 @@ Item {
|
||||||
return openMessage;
|
return openMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component { id: customInputDialogBuilder; TabletCustomQueryDialog { } }
|
||||||
function customInputDialog(properties) {
|
function customInputDialog(properties) {
|
||||||
|
return customInputDialogBuilder.createObject(tabletRoot, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
Component { id: fileDialogBuilder; TabletFileDialog { } }
|
Component { id: fileDialogBuilder; TabletFileDialog { } }
|
||||||
|
|
|
@ -159,6 +159,7 @@ Item {
|
||||||
readonly property vector2d menuPadding: Qt.vector2d(14, 102)
|
readonly property vector2d menuPadding: Qt.vector2d(14, 102)
|
||||||
readonly property real scrollbarBackgroundWidth: 18
|
readonly property real scrollbarBackgroundWidth: 18
|
||||||
readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2
|
readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2
|
||||||
|
readonly property real tabletMenuHeader: 90
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -499,6 +499,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
DependencyManager::set<TabletScriptingInterface>();
|
DependencyManager::set<TabletScriptingInterface>();
|
||||||
DependencyManager::set<ToolbarScriptingInterface>();
|
DependencyManager::set<ToolbarScriptingInterface>();
|
||||||
DependencyManager::set<UserActivityLoggerScriptingInterface>();
|
DependencyManager::set<UserActivityLoggerScriptingInterface>();
|
||||||
|
DependencyManager::set<AssetMappingsScriptingInterface>();
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||||
DependencyManager::set<SpeechRecognizer>();
|
DependencyManager::set<SpeechRecognizer>();
|
||||||
|
@ -1620,14 +1621,7 @@ QString Application::getUserAgent() {
|
||||||
return userAgent;
|
return userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t lastTabletUIToggle { 0 };
|
|
||||||
const uint64_t toggleTabletUILockout { 500000 };
|
|
||||||
void Application::toggleTabletUI() const {
|
void Application::toggleTabletUI() const {
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
if (now - lastTabletUIToggle < toggleTabletUILockout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastTabletUIToggle = now;
|
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||||
bool messageOpen = tablet->isMessageDialogOpen();
|
bool messageOpen = tablet->isMessageDialogOpen();
|
||||||
|
@ -1972,7 +1966,7 @@ void Application::initializeUi() {
|
||||||
rootContext->setContextProperty("Quat", new Quat());
|
rootContext->setContextProperty("Quat", new Quat());
|
||||||
rootContext->setContextProperty("Vec3", new Vec3());
|
rootContext->setContextProperty("Vec3", new Vec3());
|
||||||
rootContext->setContextProperty("Uuid", new ScriptUUID());
|
rootContext->setContextProperty("Uuid", new ScriptUUID());
|
||||||
rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface());
|
rootContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||||
|
|
||||||
rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||||
rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||||
|
@ -5839,7 +5833,21 @@ void Application::showAssetServerWidget(QString filePath) {
|
||||||
emit uploadRequest(filePath);
|
emit uploadRequest(filePath);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
|
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||||
|
|
||||||
|
if (tablet->getToolbarMode()) {
|
||||||
|
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||||
|
} else {
|
||||||
|
QQuickItem* tabletRoot = tablet->getTabletRoot();
|
||||||
|
if (!tabletRoot && !isHMDMode()) {
|
||||||
|
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||||
|
} else {
|
||||||
|
static const QUrl url("../../hifi/dialogs/TabletAssetServer.qml");
|
||||||
|
tablet->pushOntoStack(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
startUpload(nullptr, nullptr);
|
startUpload(nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include <AssetClient.h>
|
#include <AssetClient.h>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
|
||||||
|
|
||||||
class AssetMappingModel : public QStandardItemModel {
|
class AssetMappingModel : public QStandardItemModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -39,10 +41,12 @@ private:
|
||||||
QHash<QString, QStandardItem*> _pathToItemMap;
|
QHash<QString, QStandardItem*> _pathToItemMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AssetMappingModel*);
|
Q_DECLARE_METATYPE(AssetMappingModel*)
|
||||||
|
|
||||||
class AssetMappingsScriptingInterface : public QObject {
|
class AssetMappingsScriptingInterface : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
Q_PROPERTY(AssetMappingModel* mappingModel READ getAssetMappingModel CONSTANT)
|
Q_PROPERTY(AssetMappingModel* mappingModel READ getAssetMappingModel CONSTANT)
|
||||||
Q_PROPERTY(QAbstractProxyModel* proxyModel READ getProxyModel CONSTANT)
|
Q_PROPERTY(QAbstractProxyModel* proxyModel READ getProxyModel CONSTANT)
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -258,8 +258,3 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
|
||||||
Line3DOverlay* Line3DOverlay::createClone() const {
|
Line3DOverlay* Line3DOverlay::createClone() const {
|
||||||
return new Line3DOverlay(this);
|
return new Line3DOverlay(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Line3DOverlay::locationChanged(bool tellPhysics) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
|
@ -48,8 +48,6 @@ public:
|
||||||
|
|
||||||
virtual Line3DOverlay* createClone() const override;
|
virtual Line3DOverlay* createClone() const override;
|
||||||
|
|
||||||
virtual void locationChanged(bool tellPhysics = true) override;
|
|
||||||
|
|
||||||
glm::vec3 getDirection() const { return _direction; }
|
glm::vec3 getDirection() const { return _direction; }
|
||||||
float getLength() const { return _length; }
|
float getLength() const { return _length; }
|
||||||
glm::vec3 getLocalStart() const { return getLocalPosition(); }
|
glm::vec3 getLocalStart() const { return getLocalPosition(); }
|
||||||
|
|
|
@ -37,12 +37,13 @@
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
#include "scripting/AccountScriptingInterface.h"
|
#include "scripting/AccountScriptingInterface.h"
|
||||||
#include "scripting/HMDScriptingInterface.h"
|
#include "scripting/HMDScriptingInterface.h"
|
||||||
|
#include "scripting/AssetMappingsScriptingInterface.h"
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <ScriptEngines.h>
|
#include <ScriptEngines.h>
|
||||||
#include "FileDialogHelper.h"
|
#include "FileDialogHelper.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
|
|
||||||
|
|
||||||
static const float DPI = 30.47f;
|
static const float DPI = 30.47f;
|
||||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||||
static const float METERS_TO_INCHES = 39.3701f;
|
static const float METERS_TO_INCHES = 39.3701f;
|
||||||
|
@ -164,6 +165,10 @@ void Web3DOverlay::loadSourceURL() {
|
||||||
_webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
_webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||||
_webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
_webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||||
_webSurface->getRootContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
_webSurface->getRootContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("Vec3", new Vec3());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("Quat", new Quat());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||||
|
|
||||||
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
|
@ -176,16 +181,25 @@ void Web3DOverlay::loadSourceURL() {
|
||||||
_webSurface->getRootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
_webSurface->getRootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||||
_webSurface->getRootContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
_webSurface->getRootContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||||
_webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
_webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||||
_webSurface->getRootContext()->setContextProperty("pathToFonts", "../../");
|
_webSurface->getRootContext()->setContextProperty("pathToFonts", "../../");
|
||||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
|
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
|
||||||
|
|
||||||
// Override min fps for tablet UI, for silky smooth scrolling
|
// Override min fps for tablet UI, for silky smooth scrolling
|
||||||
_webSurface->setMaxFps(90);
|
setMaxFPS(90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
|
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
|
||||||
|
_desiredMaxFPS = maxFPS;
|
||||||
|
if (_webSurface) {
|
||||||
|
_webSurface->setMaxFps(_desiredMaxFPS);
|
||||||
|
_currentMaxFPS = _desiredMaxFPS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Web3DOverlay::render(RenderArgs* args) {
|
void Web3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible || !getParentVisible()) {
|
if (!_visible || !getParentVisible()) {
|
||||||
return;
|
return;
|
||||||
|
@ -195,9 +209,11 @@ void Web3DOverlay::render(RenderArgs* args) {
|
||||||
QSurface * currentSurface = currentContext->surface();
|
QSurface * currentSurface = currentContext->surface();
|
||||||
if (!_webSurface) {
|
if (!_webSurface) {
|
||||||
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(pickURL());
|
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(pickURL());
|
||||||
_webSurface->setMaxFps(10);
|
|
||||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||||
// and the current rendering load)
|
// and the current rendering load)
|
||||||
|
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||||
|
setMaxFPS(_desiredMaxFPS);
|
||||||
|
}
|
||||||
loadSourceURL();
|
loadSourceURL();
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||||
|
@ -247,6 +263,10 @@ void Web3DOverlay::render(RenderArgs* args) {
|
||||||
|
|
||||||
_emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
_emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||||
_webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
_webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||||
|
} else {
|
||||||
|
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||||
|
setMaxFPS(_desiredMaxFPS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 halfSize = getSize() / 2.0f;
|
vec2 halfSize = getSize() / 2.0f;
|
||||||
|
@ -402,6 +422,11 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
_dpi = dpi.toFloat();
|
_dpi = dpi.toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto maxFPS = properties["maxFPS"];
|
||||||
|
if (maxFPS.isValid()) {
|
||||||
|
_desiredMaxFPS = maxFPS.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"];
|
auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"];
|
||||||
if (showKeyboardFocusHighlight.isValid()) {
|
if (showKeyboardFocusHighlight.isValid()) {
|
||||||
_showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool();
|
_showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool();
|
||||||
|
@ -421,6 +446,9 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
|
||||||
if (property == "dpi") {
|
if (property == "dpi") {
|
||||||
return _dpi;
|
return _dpi;
|
||||||
}
|
}
|
||||||
|
if (property == "maxFPS") {
|
||||||
|
return _desiredMaxFPS;
|
||||||
|
}
|
||||||
if (property == "showKeyboardFocusHighlight") {
|
if (property == "showKeyboardFocusHighlight") {
|
||||||
return _showKeyboardFocusHighlight;
|
return _showKeyboardFocusHighlight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
|
|
||||||
QString pickURL();
|
QString pickURL();
|
||||||
void loadSourceURL();
|
void loadSourceURL();
|
||||||
|
void setMaxFPS(uint8_t maxFPS);
|
||||||
virtual void render(RenderArgs* args) override;
|
virtual void render(RenderArgs* args) override;
|
||||||
virtual const render::ShapeKey getShapeKey() override;
|
virtual const render::ShapeKey getShapeKey() override;
|
||||||
|
|
||||||
|
@ -75,6 +76,9 @@ private:
|
||||||
bool _pressed{ false };
|
bool _pressed{ false };
|
||||||
QTouchDevice _touchDevice;
|
QTouchDevice _touchDevice;
|
||||||
|
|
||||||
|
uint8_t _desiredMaxFPS { 10 };
|
||||||
|
uint8_t _currentMaxFPS { 0 };
|
||||||
|
|
||||||
QMetaObject::Connection _mousePressConnection;
|
QMetaObject::Connection _mousePressConnection;
|
||||||
QMetaObject::Connection _mouseReleaseConnection;
|
QMetaObject::Connection _mouseReleaseConnection;
|
||||||
QMetaObject::Connection _mouseMoveConnection;
|
QMetaObject::Connection _mouseMoveConnection;
|
||||||
|
|
|
@ -2000,6 +2000,11 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto currentBasis = getRecordingBasis();
|
||||||
|
if (!currentBasis) {
|
||||||
|
currentBasis = std::make_shared<Transform>(Transform::fromJson(json[JSON_AVATAR_BASIS]));
|
||||||
|
}
|
||||||
|
|
||||||
if (json.contains(JSON_AVATAR_RELATIVE)) {
|
if (json.contains(JSON_AVATAR_RELATIVE)) {
|
||||||
// During playback you can either have the recording basis set to the avatar current state
|
// During playback you can either have the recording basis set to the avatar current state
|
||||||
// meaning that all playback is relative to this avatars starting position, or
|
// meaning that all playback is relative to this avatars starting position, or
|
||||||
|
@ -2008,15 +2013,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
|
||||||
// The first is more useful for playing back recordings on your own avatar, while
|
// The first is more useful for playing back recordings on your own avatar, while
|
||||||
// the latter is more useful for playing back other avatars within your scene.
|
// the latter is more useful for playing back other avatars within your scene.
|
||||||
|
|
||||||
auto currentBasis = getRecordingBasis();
|
|
||||||
if (!currentBasis) {
|
|
||||||
currentBasis = std::make_shared<Transform>(Transform::fromJson(json[JSON_AVATAR_BASIS]));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]);
|
auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]);
|
||||||
auto worldTransform = currentBasis->worldTransform(relativeTransform);
|
auto worldTransform = currentBasis->worldTransform(relativeTransform);
|
||||||
setPosition(worldTransform.getTranslation());
|
setPosition(worldTransform.getTranslation());
|
||||||
setOrientation(worldTransform.getRotation());
|
setOrientation(worldTransform.getRotation());
|
||||||
|
} else {
|
||||||
|
// We still set the position in the case that there is no movement.
|
||||||
|
setPosition(currentBasis->getTranslation());
|
||||||
|
setOrientation(currentBasis->getRotation());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.contains(JSON_AVATAR_SCALE)) {
|
if (json.contains(JSON_AVATAR_SCALE)) {
|
||||||
|
|
|
@ -310,24 +310,24 @@ function getFingerWorldLocation(hand) {
|
||||||
|
|
||||||
// Object assign polyfill
|
// Object assign polyfill
|
||||||
if (typeof Object.assign != 'function') {
|
if (typeof Object.assign != 'function') {
|
||||||
Object.assign = function(target, varArgs) {
|
Object.assign = function(target, varArgs) {
|
||||||
'use strict';
|
'use strict';
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
throw new TypeError('Cannot convert undefined or null to object');
|
throw new TypeError('Cannot convert undefined or null to object');
|
||||||
}
|
|
||||||
var to = Object(target);
|
|
||||||
for (var index = 1; index < arguments.length; index++) {
|
|
||||||
var nextSource = arguments[index];
|
|
||||||
if (nextSource != null) {
|
|
||||||
for (var nextKey in nextSource) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
|
||||||
to[nextKey] = nextSource[nextKey];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
var to = Object(target);
|
||||||
}
|
for (var index = 1; index < arguments.length; index++) {
|
||||||
return to;
|
var nextSource = arguments[index];
|
||||||
};
|
if (nextSource != null) {
|
||||||
|
for (var nextKey in nextSource) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||||
|
to[nextKey] = nextSource[nextKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceBetweenPointAndEntityBoundingBox(point, entityProps) {
|
function distanceBetweenPointAndEntityBoundingBox(point, entityProps) {
|
||||||
|
@ -1609,16 +1609,16 @@ function MyController(hand) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
this.entityIsCloneable = function(entityID) {
|
this.entityIsCloneable = function(entityID) {
|
||||||
var entityProps = entityPropertiesCache.getGrabbableProps(entityID);
|
var entityProps = entityPropertiesCache.getGrabbableProps(entityID);
|
||||||
var props = entityPropertiesCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
if (!props) {
|
if (!props) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityProps.hasOwnProperty("cloneable")) {
|
if (entityProps.hasOwnProperty("cloneable")) {
|
||||||
return entityProps.cloneable;
|
return entityProps.cloneable;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.entityIsGrabbable = function(entityID) {
|
this.entityIsGrabbable = function(entityID) {
|
||||||
var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID);
|
var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID);
|
||||||
|
@ -2330,7 +2330,7 @@ function MyController(hand) {
|
||||||
// Can't set state of other controller to STATE_DISTANCE_HOLDING because then either:
|
// Can't set state of other controller to STATE_DISTANCE_HOLDING because then either:
|
||||||
// (a) The entity would jump to line up with the formerly rotating controller's orientation, or
|
// (a) The entity would jump to line up with the formerly rotating controller's orientation, or
|
||||||
// (b) The grab beam would need an orientation offset to the controller's true orientation.
|
// (b) The grab beam would need an orientation offset to the controller's true orientation.
|
||||||
// Neither of these options is good, so instead set STATE_SEARCHING and subsequently let the formerly distance
|
// Neither of these options is good, so instead set STATE_SEARCHING and subsequently let the formerly distance
|
||||||
// rotating controller start distance holding the entity if it happens to be pointing at the entity.
|
// rotating controller start distance holding the entity if it happens to be pointing at the entity.
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -2702,31 +2702,29 @@ function MyController(hand) {
|
||||||
var userData = JSON.parse(grabbedProperties.userData);
|
var userData = JSON.parse(grabbedProperties.userData);
|
||||||
var grabInfo = userData.grabbableKey;
|
var grabInfo = userData.grabbableKey;
|
||||||
if (grabInfo && grabInfo.cloneable) {
|
if (grabInfo && grabInfo.cloneable) {
|
||||||
// Check if
|
var worldEntities = Entities.findEntities(MyAvatar.position, 50);
|
||||||
var worldEntities = Entities.findEntitiesInBox(Vec3.subtract(MyAvatar.position, {x:25,y:25, z:25}), {x:50, y: 50, z: 50})
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
worldEntities.forEach(function(item) {
|
worldEntities.forEach(function(item) {
|
||||||
var item = Entities.getEntityProperties(item, ["name"]);
|
var item = Entities.getEntityProperties(item, ["name"]);
|
||||||
if (item.name === grabbedProperties.name) {
|
if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0;
|
||||||
|
if (count >= limit && limit !== 0) {
|
||||||
|
delete limit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var cloneableProps = Entities.getEntityProperties(grabbedProperties.id);
|
var cloneableProps = Entities.getEntityProperties(grabbedProperties.id);
|
||||||
|
cloneableProps.name = cloneableProps.name + '-clone-' + grabbedProperties.id;
|
||||||
var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300;
|
var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300;
|
||||||
var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 10;
|
|
||||||
var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false;
|
var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false;
|
||||||
var cUserData = Object.assign({}, userData);
|
var cUserData = Object.assign({}, userData);
|
||||||
var cProperties = Object.assign({}, cloneableProps);
|
var cProperties = Object.assign({}, cloneableProps);
|
||||||
isClone = true;
|
isClone = true;
|
||||||
|
|
||||||
if (count > limit) {
|
|
||||||
delete cloneableProps;
|
|
||||||
delete lifetime;
|
|
||||||
delete cUserData;
|
|
||||||
delete cProperties;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete cUserData.grabbableKey.cloneLifetime;
|
delete cUserData.grabbableKey.cloneLifetime;
|
||||||
delete cUserData.grabbableKey.cloneable;
|
delete cUserData.grabbableKey.cloneable;
|
||||||
delete cUserData.grabbableKey.cloneDynamic;
|
delete cUserData.grabbableKey.cloneDynamic;
|
||||||
|
|
|
@ -45,6 +45,7 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode'];
|
var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode'];
|
||||||
|
|
||||||
function onHmdChanged(isHmd) {
|
function onHmdChanged(isHmd) {
|
||||||
|
HMD.closeTablet();
|
||||||
if (isHmd) {
|
if (isHmd) {
|
||||||
button.editProperties({
|
button.editProperties({
|
||||||
icon: "icons/tablet-icons/switch-desk-i.svg",
|
icon: "icons/tablet-icons/switch-desk-i.svg",
|
||||||
|
|
|
@ -879,7 +879,7 @@ function loaded() {
|
||||||
elCloneable.checked = false;
|
elCloneable.checked = false;
|
||||||
elCloneableDynamic.checked = false;
|
elCloneableDynamic.checked = false;
|
||||||
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
||||||
elCloneableLimit.value = 10;
|
elCloneableLimit.value = 0;
|
||||||
elCloneableLifetime.value = 300;
|
elCloneableLifetime.value = 300;
|
||||||
|
|
||||||
var parsedUserData = {}
|
var parsedUserData = {}
|
||||||
|
@ -899,8 +899,6 @@ function loaded() {
|
||||||
if ("cloneable" in parsedUserData["grabbableKey"]) {
|
if ("cloneable" in parsedUserData["grabbableKey"]) {
|
||||||
elCloneable.checked = parsedUserData["grabbableKey"].cloneable;
|
elCloneable.checked = parsedUserData["grabbableKey"].cloneable;
|
||||||
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
elCloneableGroup.style.display = elCloneable.checked ? "block": "none";
|
||||||
elCloneableLimit.value = elCloneable.checked ? 10: 0;
|
|
||||||
elCloneableLifetime.value = elCloneable.checked ? 300: 0;
|
|
||||||
elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic;
|
elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic;
|
||||||
elDynamic.checked = elCloneable.checked ? false: properties.dynamic;
|
elDynamic.checked = elCloneable.checked ? false: properties.dynamic;
|
||||||
if (elCloneable.checked) {
|
if (elCloneable.checked) {
|
||||||
|
@ -908,7 +906,7 @@ function loaded() {
|
||||||
elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300;
|
elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300;
|
||||||
}
|
}
|
||||||
if ("cloneLimit" in parsedUserData["grabbableKey"]) {
|
if ("cloneLimit" in parsedUserData["grabbableKey"]) {
|
||||||
elCloneableLimit.value = parsedUserData["grabbableKey"].cloneLimit ? parsedUserData["grabbableKey"].cloneLimit : 10;
|
elCloneableLimit.value = parsedUserData["grabbableKey"].cloneLimit ? parsedUserData["grabbableKey"].cloneLimit : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ function calcSpawnInfo(hand, height) {
|
||||||
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||||
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
||||||
|
|
||||||
|
if (!hand) {
|
||||||
|
hand = NO_HANDS;
|
||||||
|
}
|
||||||
|
|
||||||
if (HMD.active && hand !== NO_HANDS) {
|
if (HMD.active && hand !== NO_HANDS) {
|
||||||
var handController = getControllerWorldLocation(hand, true);
|
var handController = getControllerWorldLocation(hand, true);
|
||||||
var controllerPosition = handController.position;
|
var controllerPosition = handController.position;
|
||||||
|
@ -96,7 +100,7 @@ function calcSpawnInfo(hand, height) {
|
||||||
* @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand
|
* @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand
|
||||||
* @param clientOnly [bool] true indicates tablet model is only visible to client.
|
* @param clientOnly [bool] true indicates tablet model is only visible to client.
|
||||||
*/
|
*/
|
||||||
WebTablet = function (url, width, dpi, hand, clientOnly) {
|
WebTablet = function (url, width, dpi, hand, clientOnly, location) {
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
|
@ -134,6 +138,10 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
||||||
|
|
||||||
// compute position, rotation & parentJointIndex of the tablet
|
// compute position, rotation & parentJointIndex of the tablet
|
||||||
this.calculateTabletAttachmentProperties(hand, true, tabletProperties);
|
this.calculateTabletAttachmentProperties(hand, true, tabletProperties);
|
||||||
|
if (location) {
|
||||||
|
tabletProperties.localPosition = location.localPosition;
|
||||||
|
tabletProperties.localRotation = location.localRotation;
|
||||||
|
}
|
||||||
|
|
||||||
this.cleanUpOldTablets();
|
this.cleanUpOldTablets();
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,47 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, MyAvatar */
|
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays,
|
||||||
|
MyAvatar, Menu */
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
var tabletShown = false;
|
var tabletShown = false;
|
||||||
var tabletLocation = null;
|
var tabletRezzed = false;
|
||||||
var activeHand = null;
|
var activeHand = null;
|
||||||
|
var DEFAULT_WIDTH = 0.4375;
|
||||||
|
var DEFAULT_TABLET_SCALE = 100;
|
||||||
|
var preMakeTime = Date.now();
|
||||||
|
var validCheckTime = Date.now();
|
||||||
|
var debugTablet = false;
|
||||||
|
UIWebTablet = null;
|
||||||
|
|
||||||
Script.include("../libraries/WebTablet.js");
|
Script.include("../libraries/WebTablet.js");
|
||||||
|
|
||||||
function showTabletUI() {
|
function tabletIsValid() {
|
||||||
tabletShown = true;
|
if (!UIWebTablet) {
|
||||||
print("show tablet-ui");
|
return false;
|
||||||
|
}
|
||||||
|
if (UIWebTablet.tabletIsOverlay && Overlays.getProperty(HMD.tabletID, "type") != "model") {
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET is invalid due to frame: " + JSON.stringify(Overlays.getProperty(HMD.tabletID, "type")));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Overlays.getProperty(HMD.homeButtonID, "type") != "sphere" ||
|
||||||
|
Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") {
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET is invalid due to other");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var DEFAULT_WIDTH = 0.4375;
|
|
||||||
var DEFAULT_TABLET_SCALE = 100;
|
function rezTablet() {
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET rezzing");
|
||||||
|
}
|
||||||
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
||||||
var TABLET_SCALE = DEFAULT_TABLET_SCALE;
|
var TABLET_SCALE = DEFAULT_TABLET_SCALE;
|
||||||
if (toolbarMode) {
|
if (toolbarMode) {
|
||||||
|
@ -34,37 +60,98 @@
|
||||||
} else {
|
} else {
|
||||||
TABLET_SCALE = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE;
|
TABLET_SCALE = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE;
|
||||||
}
|
}
|
||||||
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (TABLET_SCALE / 100), null, activeHand, true);
|
|
||||||
|
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml",
|
||||||
|
DEFAULT_WIDTH * (TABLET_SCALE / 100),
|
||||||
|
null, activeHand, true);
|
||||||
UIWebTablet.register();
|
UIWebTablet.register();
|
||||||
HMD.tabletID = UIWebTablet.tabletEntityID;
|
HMD.tabletID = UIWebTablet.tabletEntityID;
|
||||||
HMD.homeButtonID = UIWebTablet.homeButtonID;
|
HMD.homeButtonID = UIWebTablet.homeButtonID;
|
||||||
HMD.tabletScreenID = UIWebTablet.webOverlayID;
|
HMD.tabletScreenID = UIWebTablet.webOverlayID;
|
||||||
|
|
||||||
|
tabletRezzed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showTabletUI() {
|
||||||
|
tabletShown = true;
|
||||||
|
|
||||||
|
if (!tabletRezzed) {
|
||||||
|
rezTablet(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UIWebTablet && tabletRezzed) {
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET in showTabletUI, already rezzed");
|
||||||
|
}
|
||||||
|
var tabletProperties = {};
|
||||||
|
UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties);
|
||||||
|
tabletProperties.visible = true;
|
||||||
|
if (UIWebTablet.tabletIsOverlay) {
|
||||||
|
Overlays.editOverlay(HMD.tabletID, tabletProperties);
|
||||||
|
}
|
||||||
|
Overlays.editOverlay(HMD.homeButtonID, { visible: true });
|
||||||
|
Overlays.editOverlay(HMD.tabletScreenID, { visible: true });
|
||||||
|
Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideTabletUI() {
|
function hideTabletUI() {
|
||||||
tabletShown = false;
|
tabletShown = false;
|
||||||
print("hide tablet-ui");
|
if (!UIWebTablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UIWebTablet.tabletIsOverlay) {
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET hide");
|
||||||
|
}
|
||||||
|
if (Settings.getValue("tabletVisibleToOthers")) {
|
||||||
|
closeTabletUI();
|
||||||
|
} else {
|
||||||
|
// Overlays.editOverlay(HMD.tabletID, { localPosition: { x: -1000, y: 0, z:0 } });
|
||||||
|
Overlays.editOverlay(HMD.tabletID, { visible: false });
|
||||||
|
Overlays.editOverlay(HMD.homeButtonID, { visible: false });
|
||||||
|
Overlays.editOverlay(HMD.tabletScreenID, { visible: false });
|
||||||
|
Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 1 });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
closeTabletUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeTabletUI() {
|
||||||
|
tabletShown = false;
|
||||||
if (UIWebTablet) {
|
if (UIWebTablet) {
|
||||||
if (UIWebTablet.onClose) {
|
if (UIWebTablet.onClose) {
|
||||||
UIWebTablet.onClose();
|
UIWebTablet.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
tabletLocation = UIWebTablet.getLocation();
|
if (debugTablet) {
|
||||||
|
print("TABLET close");
|
||||||
|
}
|
||||||
UIWebTablet.unregister();
|
UIWebTablet.unregister();
|
||||||
UIWebTablet.destroy();
|
UIWebTablet.destroy();
|
||||||
UIWebTablet = null;
|
UIWebTablet = null;
|
||||||
HMD.tabletID = null;
|
HMD.tabletID = null;
|
||||||
HMD.homeButtonID = null;
|
HMD.homeButtonID = null;
|
||||||
HMD.tabletScreenID = null;
|
HMD.tabletScreenID = null;
|
||||||
|
} else if (debugTablet) {
|
||||||
|
print("TABLET closeTabletUI, UIWebTablet is null");
|
||||||
}
|
}
|
||||||
|
tabletRezzed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateShowTablet() {
|
function updateShowTablet() {
|
||||||
|
var MSECS_PER_SEC = 1000.0;
|
||||||
|
var now = Date.now();
|
||||||
|
|
||||||
// close the WebTablet if it we go into toolbar mode.
|
// close the WebTablet if it we go into toolbar mode.
|
||||||
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
||||||
|
var visibleToOthers = Settings.getValue("tabletVisibleToOthers");
|
||||||
|
|
||||||
if (tabletShown && toolbarMode) {
|
if (tabletShown && toolbarMode) {
|
||||||
hideTabletUI();
|
closeTabletUI();
|
||||||
HMD.closeTablet();
|
HMD.closeTablet();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -78,19 +165,48 @@
|
||||||
tablet.updateAudioBar(currentMicLevel);
|
tablet.updateAudioBar(currentMicLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabletShown && UIWebTablet && Overlays.getOverlayType(UIWebTablet.webOverlayID) != "web3d") {
|
if (validCheckTime - now > MSECS_PER_SEC) {
|
||||||
// when we switch domains, the tablet entity gets destroyed and recreated. this causes
|
validCheckTime = now;
|
||||||
// the overlay to be deleted, but not recreated. If the overlay is deleted for this or any
|
if (tabletRezzed && UIWebTablet && !tabletIsValid()) {
|
||||||
// other reason, close the tablet.
|
// when we switch domains, the tablet entity gets destroyed and recreated. this causes
|
||||||
hideTabletUI();
|
// the overlay to be deleted, but not recreated. If the overlay is deleted for this or any
|
||||||
HMD.closeTablet();
|
// other reason, close the tablet.
|
||||||
} else if (HMD.showTablet && !tabletShown && !toolbarMode) {
|
closeTabletUI();
|
||||||
UserActivityLogger.openedTablet(Settings.getValue("tabletVisibleToOthers"));
|
HMD.closeTablet();
|
||||||
|
if (debugTablet) {
|
||||||
|
print("TABLET autodestroying");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (HMD.showTablet && !tabletShown && !toolbarMode) {
|
||||||
|
UserActivityLogger.openedTablet(visibleToOthers);
|
||||||
showTabletUI();
|
showTabletUI();
|
||||||
} else if (!HMD.showTablet && tabletShown) {
|
} else if (!HMD.showTablet && tabletShown) {
|
||||||
UserActivityLogger.closedTablet();
|
UserActivityLogger.closedTablet();
|
||||||
hideTabletUI();
|
if (visibleToOthers) {
|
||||||
|
closeTabletUI();
|
||||||
|
} else {
|
||||||
|
hideTabletUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the tablet is an overlay, attempt to pre-create it and then hide it so that when it's
|
||||||
|
// summoned, it will appear quickly.
|
||||||
|
if (!toolbarMode && !visibleToOthers) {
|
||||||
|
if (now - preMakeTime > MSECS_PER_SEC) {
|
||||||
|
preMakeTime = now;
|
||||||
|
if (!tabletIsValid()) {
|
||||||
|
closeTabletUI();
|
||||||
|
rezTablet(false);
|
||||||
|
tabletShown = false;
|
||||||
|
} else if (!tabletShown) {
|
||||||
|
hideTabletUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleHand(channel, hand, senderUUID, localOnly) {
|
function toggleHand(channel, hand, senderUUID, localOnly) {
|
||||||
|
@ -131,7 +247,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
Entities.deleteEntity(HMD.tabletID);
|
var tabletID = HMD.tabletID;
|
||||||
|
Entities.deleteEntity(tabletID);
|
||||||
|
Overlays.deleteOverlay(tabletID)
|
||||||
HMD.tabletID = null;
|
HMD.tabletID = null;
|
||||||
HMD.homeButtonID = null;
|
HMD.homeButtonID = null;
|
||||||
HMD.tabletScreenID = null;
|
HMD.tabletScreenID = null;
|
||||||
|
|
Loading…
Reference in a new issue