Merge branch 'master' of github.com:highfidelity/hifi into scale-wearables-with-avatar-3
27
BUILD_LINUX_CHEATSHEET.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# this guide is specific to Ubuntu 16.04.
|
||||
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
sudo su -
|
||||
apt-get -y update
|
||||
apt-get install -y software-properties-common
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 15FF1AAE
|
||||
add-apt-repository "deb http://debian.highfidelity.com stable main"
|
||||
apt-get -y update
|
||||
apt-get install -y hifi-domain-server
|
||||
apt-get install -y hifi-assignment-client
|
||||
|
||||
# When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
|
||||
apt-get install -y hifi-dev-domain-server
|
||||
apt-get install -y hifi-dev-assignment-client
|
||||
|
||||
# domain server and assignment clients should already be running. The processes are controlled via:
|
||||
systemctl start hifi-domain-server
|
||||
systemctl stop hifi-domain-server
|
||||
|
||||
# Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100).
|
||||
|
||||
# The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
|
||||
# As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
|
||||
To do this you can modify /etc/crontab by adding the following lines
|
||||
0 */1 * * * root apt-get update
|
||||
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
|
||||
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
|
|
@ -46,8 +46,14 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION,
|
||||
"run single assignment client of given type", "type");
|
||||
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||
for (Assignment::Type type = Assignment::FirstType;
|
||||
type != Assignment::AllTypes;
|
||||
type = static_cast<Assignment::Type>(static_cast<int>(type) + 1)) {
|
||||
typeDescription.append(QStringLiteral("\n%1 | %2").arg(QString::number(type), Assignment::typeToString(type)));
|
||||
}
|
||||
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, typeDescription, "type");
|
||||
|
||||
parser.addOption(clientTypeOption);
|
||||
|
||||
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
||||
|
|
|
@ -477,7 +477,7 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = i.key();
|
||||
|
|
|
@ -94,7 +94,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
root.insert(requestSubobjectKey, subobject);
|
||||
QJsonDocument doc { root };
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -420,7 +420,7 @@ bool DomainServer::optionallySetupOAuth() {
|
|||
|
||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||
if (_oauthProviderURL.isEmpty()) {
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -945,7 +945,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q
|
|||
|
||||
void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes) {
|
||||
// enumerate over all assignment types and see if we've already excluded it
|
||||
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
|
||||
for (Assignment::Type defaultedType = Assignment::FirstType;
|
||||
defaultedType != Assignment::AllTypes;
|
||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
||||
|
@ -2159,7 +2159,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QJsonDocument doc(root);
|
||||
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id };
|
||||
|
||||
url.setQuery("access_token=" + accessTokenVariant->toString());
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
|||
// send a request to the metaverse API for the public key for this domain
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL() };
|
||||
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
publicKeyURL.setPath(publicKeyPath);
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ function(JOIN VALUES GLUE OUTPUT)
|
|||
endfunction()
|
||||
|
||||
|
||||
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
|
||||
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
|
||||
if (NOT DEV_BUILD)
|
||||
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
|
||||
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
|
||||
endif()
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion")
|
||||
|
@ -80,7 +82,9 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}")
|
|||
# add them to the interface source files
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
|
||||
|
||||
if (NOT DEV_BUILD)
|
||||
list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC})
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
install(
|
||||
|
|
|
@ -34,7 +34,7 @@ var EventBridge;
|
|||
var tempEventBridge = EventBridge;
|
||||
EventBridge = channel.objects.eventBridge;
|
||||
EventBridge.audioOutputDeviceChanged.connect(function(deviceName) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.enumerateDevices().then(function(devices) {
|
||||
devices.forEach(function(device) {
|
||||
if (device.kind == "audiooutput") {
|
||||
|
|
|
@ -52,7 +52,11 @@ Item {
|
|||
targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height
|
||||
}
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
if(!isNaN(newWidth)) {
|
||||
parent.width = root.width = newWidth;
|
||||
}
|
||||
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
|
||||
}
|
||||
|
|
18
interface/resources/qml/OverlayWindowTest.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
color: "white"
|
||||
Rectangle {
|
||||
width: 10
|
||||
height: 10
|
||||
color: "red"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: OverlayWindowTestString
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ Windows.Window {
|
|||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
property var source;
|
||||
property var component;
|
||||
property var dynamicContent;
|
||||
|
||||
// Keyboard control properties in case needed by QML content.
|
||||
|
@ -35,28 +34,9 @@ Windows.Window {
|
|||
dynamicContent.destroy();
|
||||
dynamicContent = null;
|
||||
}
|
||||
component = Qt.createComponent(source);
|
||||
console.log("Created component " + component + " from source " + source);
|
||||
}
|
||||
|
||||
onComponentChanged: {
|
||||
console.log("Component changed to " + component)
|
||||
populate();
|
||||
}
|
||||
|
||||
function populate() {
|
||||
console.log("Populate called: dynamicContent " + dynamicContent + " component " + component);
|
||||
if (!dynamicContent && component) {
|
||||
if (component.status == Component.Error) {
|
||||
console.log("Error loading component:", component.errorString());
|
||||
} else if (component.status == Component.Ready) {
|
||||
console.log("Building dynamic content");
|
||||
dynamicContent = component.createObject(contentHolder);
|
||||
} else {
|
||||
console.log("Component not yet ready, connecting to status change");
|
||||
component.statusChanged.connect(populate);
|
||||
}
|
||||
}
|
||||
QmlSurface.load(source, contentHolder, function(newObject) {
|
||||
dynamicContent = newObject;
|
||||
});
|
||||
}
|
||||
|
||||
// Handle message traffic from the script that launched us to the loaded QML
|
||||
|
|
|
@ -29,12 +29,12 @@ Original.Button {
|
|||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
|
|
@ -31,12 +31,12 @@ Original.CheckBox {
|
|||
activeFocusOnPress: true
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
// TODO: doesnt works for QQC1. check with QQC2
|
||||
// onHovered: {
|
||||
// tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
// Tablet.playSound(TabletEnums.ButtonHover);
|
||||
// }
|
||||
|
||||
style: CheckBoxStyle {
|
||||
|
|
|
@ -36,12 +36,12 @@ CheckBox {
|
|||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@ Original.Button {
|
|||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
|
|
@ -41,13 +41,13 @@ Item {
|
|||
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
mouse.accepted = true;
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
||||
webEntity.synthesizeKeyPress(glyph);
|
||||
webEntity.synthesizeKeyPress(glyph, mirrorText);
|
||||
|
|
|
@ -30,12 +30,12 @@ Original.RadioButton {
|
|||
readonly property int checkRadius: 2
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
// TODO: doesnt works for QQC1. check with QQC2
|
||||
// onHovered: {
|
||||
// tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
// Tablet.playSound(TabletEnums.ButtonHover);
|
||||
// }
|
||||
|
||||
style: RadioButtonStyle {
|
||||
|
|
|
@ -49,7 +49,7 @@ Item {
|
|||
}
|
||||
|
||||
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
|
||||
console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
|
||||
console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
|
||||
|
|
|
@ -25,13 +25,13 @@ Preference {
|
|||
id: button
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
preference.trigger();
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
width: 180
|
||||
anchors.bottom: parent.bottom
|
||||
|
|
|
@ -41,12 +41,12 @@ Preference {
|
|||
id: checkBox
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
anchors {
|
||||
|
|
|
@ -246,12 +246,12 @@ Item {
|
|||
anchors.fill: parent;
|
||||
acceptedButtons: Qt.LeftButton;
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
goFunction("hifi://" + hifiUrl);
|
||||
}
|
||||
hoverEnabled: true;
|
||||
onEntered: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
hoverThunk();
|
||||
}
|
||||
onExited: unhoverThunk();
|
||||
|
@ -269,7 +269,7 @@ Item {
|
|||
}
|
||||
}
|
||||
function go() {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
|
||||
}
|
||||
MouseArea {
|
||||
|
|
|
@ -45,11 +45,13 @@ OriginalDesktop.Desktop {
|
|||
Toolbar {
|
||||
id: sysToolbar;
|
||||
objectName: "com.highfidelity.interface.toolbar.system";
|
||||
property var tablet: Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
|
||||
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
|
||||
x: sysToolbar.x
|
||||
y: 50
|
||||
shown: true
|
||||
buttonModel: tablet.buttons;
|
||||
shown: tablet.toolbarMode;
|
||||
}
|
||||
|
||||
Settings {
|
||||
|
|
|
@ -61,12 +61,12 @@ Rectangle {
|
|||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
Audio.muted = !Audio.muted;
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,10 +41,11 @@ Rectangle {
|
|||
property bool debugCheckoutSuccess: false;
|
||||
property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified();
|
||||
property bool isWearable;
|
||||
property string referrer;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
|
@ -59,6 +60,7 @@ Rectangle {
|
|||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
UserActivityLogger.commercePassphraseEntry("marketplace checkout");
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
authSuccessStep();
|
||||
|
@ -71,7 +73,7 @@ Rectangle {
|
|||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +116,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
onItemIdChanged: {
|
||||
commerce.inventory();
|
||||
Commerce.inventory();
|
||||
itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
|
||||
}
|
||||
|
||||
|
@ -123,14 +125,14 @@ Rectangle {
|
|||
}
|
||||
|
||||
onItemPriceChanged: {
|
||||
commerce.balance();
|
||||
Commerce.balance();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: notSetUpTimer;
|
||||
interval: 200;
|
||||
onTriggered: {
|
||||
sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId});
|
||||
sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId, referrer: referrer});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,7 +204,7 @@ Rectangle {
|
|||
Component.onCompleted: {
|
||||
purchasesReceived = false;
|
||||
balanceReceived = false;
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +225,7 @@ Rectangle {
|
|||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
commerce.getLoginStatus();
|
||||
Commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,9 +475,9 @@ Rectangle {
|
|||
if (itemIsJson) {
|
||||
buyButton.enabled = false;
|
||||
if (!root.shouldBuyWithControlledFailure) {
|
||||
commerce.buy(itemId, itemPrice);
|
||||
Commerce.buy(itemId, itemPrice);
|
||||
} else {
|
||||
commerce.buy(itemId, itemPrice, true);
|
||||
Commerce.buy(itemId, itemPrice, true);
|
||||
}
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
|
@ -876,6 +878,7 @@ Rectangle {
|
|||
itemName = message.params.itemName;
|
||||
root.itemPrice = message.params.itemPrice;
|
||||
itemHref = message.params.itemHref;
|
||||
referrer = message.params.referrer;
|
||||
setBuyText();
|
||||
break;
|
||||
default:
|
||||
|
@ -939,8 +942,8 @@ Rectangle {
|
|||
}
|
||||
root.balanceReceived = false;
|
||||
root.purchasesReceived = false;
|
||||
commerce.inventory();
|
||||
commerce.balance();
|
||||
Commerce.inventory();
|
||||
Commerce.balance();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -31,14 +31,14 @@ Item {
|
|||
|
||||
height: mainContainer.height + additionalDropdownHeight;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
sendToParent({method: "needsLogIn"});
|
||||
} else if (walletStatus === 3) {
|
||||
commerce.getSecurityImage();
|
||||
Commerce.getSecurityImage();
|
||||
} else if (walletStatus > 3) {
|
||||
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ Item {
|
|||
if (!isLoggedIn) {
|
||||
sendToParent({method: "needsLogIn"});
|
||||
} else {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,13 +61,13 @@ Item {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
commerce.getLoginStatus();
|
||||
Commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ Rectangle {
|
|||
property bool isCertificateInvalid: false;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onCertificateInfoResult: {
|
||||
if (result.status !== 'success') {
|
||||
|
@ -109,7 +109,7 @@ Rectangle {
|
|||
|
||||
onCertificateIdChanged: {
|
||||
if (certificateId !== "") {
|
||||
commerce.certificateInfo(certificateId);
|
||||
Commerce.certificateInfo(certificateId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ Rectangle {
|
|||
property bool isDebuggingFirstUseTutorial: false;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
|
@ -54,13 +54,14 @@ Rectangle {
|
|||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
UserActivityLogger.commercePassphraseEntry("marketplace purchases");
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
|
||||
root.activeView = "firstUseTutorial";
|
||||
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
Commerce.inventory();
|
||||
}
|
||||
} else {
|
||||
console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus);
|
||||
|
@ -71,7 +72,7 @@ Rectangle {
|
|||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,7 @@ Rectangle {
|
|||
Component.onCompleted: {
|
||||
securityImageResultReceived = false;
|
||||
purchasesReceived = false;
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +219,7 @@ Rectangle {
|
|||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
commerce.getLoginStatus();
|
||||
Commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,7 +234,7 @@ Rectangle {
|
|||
onSendSignalToParent: {
|
||||
if (msg.method === "authSuccess") {
|
||||
root.activeView = "initialize";
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
|
@ -254,7 +255,7 @@ Rectangle {
|
|||
case 'tutorial_finished':
|
||||
Settings.setValue("isFirstUseOfPurchases", false);
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
Commerce.inventory();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -594,7 +595,7 @@ Rectangle {
|
|||
if (root.activeView === "purchasesMain" && !root.pendingInventoryReply) {
|
||||
console.log("Refreshing Purchases...");
|
||||
root.pendingInventoryReply = true;
|
||||
commerce.inventory();
|
||||
Commerce.inventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ Item {
|
|||
property string keyFilePath;
|
||||
property bool showDebugButtons: true;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
root.keyFilePath = path;
|
||||
|
@ -37,7 +37,7 @@ Item {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
Commerce.getKeyFilePathIfExists();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,37 @@ Item {
|
|||
// Style
|
||||
color: hifi.colors.blueHighlight;
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: clearCachedPassphraseButton;
|
||||
visible: root.showDebugButtons;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: helpTitleText.right;
|
||||
anchors.leftMargin: 20;
|
||||
height: 40;
|
||||
width: 150;
|
||||
text: "DBG: Clear Pass";
|
||||
onClicked: {
|
||||
Commerce.setPassphrase("");
|
||||
sendSignalToWallet({method: 'passphraseReset'});
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: resetButton;
|
||||
visible: root.showDebugButtons;
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: clearCachedPassphraseButton.top;
|
||||
anchors.left: clearCachedPassphraseButton.right;
|
||||
height: 40;
|
||||
width: 150;
|
||||
text: "DBG: RST Wallet";
|
||||
onClicked: {
|
||||
Commerce.reset();
|
||||
sendSignalToWallet({method: 'walletReset'});
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: helpModel;
|
||||
|
|
|
@ -30,8 +30,8 @@ Item {
|
|||
source: "images/wallet-bg.jpg";
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -25,10 +25,6 @@ Item {
|
|||
|
||||
id: root;
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
// Username Text
|
||||
RalewayRegular {
|
||||
id: usernameText;
|
||||
|
|
|
@ -36,8 +36,8 @@ Item {
|
|||
source: "images/wallet-bg.jpg";
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
titleBarSecurityImage.source = "";
|
||||
|
@ -50,8 +50,12 @@ Item {
|
|||
submitPassphraseInputButton.enabled = true;
|
||||
if (!isAuthenticated) {
|
||||
errorText.text = "Authentication failed - please try again.";
|
||||
passphraseField.error = true;
|
||||
UserActivityLogger.commercePassphraseAuthenticationStatus("auth failure");
|
||||
} else {
|
||||
sendSignalToParent({method: 'authSuccess'});;
|
||||
sendSignalToParent({method: 'authSuccess'});
|
||||
passphraseField.error = false;
|
||||
UserActivityLogger.commercePassphraseAuthenticationStatus("auth success");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +74,7 @@ Item {
|
|||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
passphraseField.error = false;
|
||||
passphraseField.focus = true;
|
||||
sendSignalToParent({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
|
@ -208,7 +213,7 @@ Item {
|
|||
|
||||
onAccepted: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
Commerce.setPassphrase(passphraseField.text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +253,7 @@ Item {
|
|||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
Commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
|
@ -316,7 +321,7 @@ Item {
|
|||
text: "Submit"
|
||||
onClicked: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
Commerce.setPassphrase(passphraseField.text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,6 +341,7 @@ Item {
|
|||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
|
||||
UserActivityLogger.commercePassphraseAuthenticationStatus("passphrase modal cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ Item {
|
|||
propagateComposedEvents: false;
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
onSecurityImageResult: {
|
||||
passphrasePageSecurityImage.source = "";
|
||||
passphrasePageSecurityImage.source = "image://security/securityImage";
|
||||
|
@ -54,6 +54,9 @@ Item {
|
|||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
passphraseField.error = false;
|
||||
passphraseFieldAgain.error = false;
|
||||
currentPassphraseField.error = false;
|
||||
if (root.shouldImmediatelyFocus) {
|
||||
focusFirstTextField();
|
||||
}
|
||||
|
@ -160,7 +163,7 @@ Item {
|
|||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
Commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
|
@ -283,7 +286,7 @@ Item {
|
|||
passphraseFieldAgain.error = false;
|
||||
currentPassphraseField.error = false;
|
||||
setErrorText("");
|
||||
commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
|
||||
Commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ Item {
|
|||
id: root;
|
||||
property string keyFilePath;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
root.keyFilePath = path;
|
||||
|
@ -234,7 +234,7 @@ Item {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
Commerce.getKeyFilePathIfExists();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,8 @@ Item {
|
|||
id: root;
|
||||
property bool justSubmitted: false;
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
securityImageChangePageSecurityImage.source = "";
|
||||
|
@ -213,4 +209,8 @@ Item {
|
|||
securityImageSubmitButton.enabled = true;
|
||||
securityImageSubmitButton.text = "Submit";
|
||||
}
|
||||
|
||||
function initModel() {
|
||||
securityImageSelection.initModel();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,29 +15,28 @@ import QtQuick 2.5
|
|||
|
||||
ListModel {
|
||||
id: root;
|
||||
ListElement{
|
||||
sourcePath: "images/01.jpg"
|
||||
securityImageEnumValue: 1;
|
||||
|
||||
function initModel() {
|
||||
var array = [];
|
||||
while (array.length < 6) {
|
||||
// We currently have 34 security images to choose from
|
||||
var randomNumber = Math.floor(Math.random() * 34) + 1;
|
||||
if (array.indexOf(randomNumber) > -1) {
|
||||
continue;
|
||||
}
|
||||
array[array.length] = randomNumber;
|
||||
}
|
||||
|
||||
var modelElement;
|
||||
|
||||
for (var i = 0; i < 6; i++) {
|
||||
modelElement = { "sourcePath":"images/" + addLeadingZero(array[i]) + ".jpg", "securityImageEnumValue": (i + 1) }
|
||||
root.insert(i, modelElement);
|
||||
}
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/02.jpg"
|
||||
securityImageEnumValue: 2;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/03.jpg"
|
||||
securityImageEnumValue: 3;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/04.jpg"
|
||||
securityImageEnumValue: 4;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/05.jpg"
|
||||
securityImageEnumValue: 5;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/06.jpg"
|
||||
securityImageEnumValue: 6;
|
||||
|
||||
function addLeadingZero(n) {
|
||||
return n < 10 ? '0' + n : '' + n;
|
||||
}
|
||||
|
||||
function getImagePathFromImageID(imageID) {
|
||||
|
|
|
@ -95,6 +95,10 @@ Item {
|
|||
function getSelectedImageIndex() {
|
||||
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
|
||||
}
|
||||
|
||||
function initModel() {
|
||||
gridModel.initModel();
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
|
|
|
@ -25,8 +25,8 @@ Item {
|
|||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
}
|
||||
|
||||
// "Unavailable"
|
||||
|
|
|
@ -31,13 +31,15 @@ Rectangle {
|
|||
property bool keyboardRaised: false;
|
||||
property bool isPassword: false;
|
||||
|
||||
anchors.fill: (typeof parent === undefined) ? undefined : parent;
|
||||
|
||||
Image {
|
||||
anchors.fill: parent;
|
||||
source: "images/wallet-bg.jpg";
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onWalletStatusResult: {
|
||||
if (walletStatus === 0) {
|
||||
|
@ -47,7 +49,7 @@ Rectangle {
|
|||
} else if (walletStatus === 1) {
|
||||
if (root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletSetup";
|
||||
commerce.resetLocalWalletOnly();
|
||||
Commerce.resetLocalWalletOnly();
|
||||
var timestamp = new Date();
|
||||
walletSetup.startingTimestamp = timestamp;
|
||||
walletSetup.setupAttemptID = generateUUID();
|
||||
|
@ -57,10 +59,13 @@ Rectangle {
|
|||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
root.activeView = "passphraseModal";
|
||||
UserActivityLogger.commercePassphraseEntry("wallet app");
|
||||
}
|
||||
} else if (walletStatus === 3) {
|
||||
root.activeView = "walletHome";
|
||||
commerce.getSecurityImage();
|
||||
if (root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletHome";
|
||||
Commerce.getSecurityImage();
|
||||
}
|
||||
} else {
|
||||
console.log("ERROR in Wallet.qml: Unknown wallet status: " + walletStatus);
|
||||
}
|
||||
|
@ -70,7 +75,7 @@ Rectangle {
|
|||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,10 +87,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
HifiCommerceCommon.CommerceLightbox {
|
||||
id: lightboxPopup;
|
||||
visible: false;
|
||||
|
@ -180,7 +181,7 @@ Rectangle {
|
|||
if (msg.method === 'walletSetup_finished') {
|
||||
if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
|
||||
root.activeView = "initialize";
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
} else if (msg.referrer === 'purchases') {
|
||||
sendToScript({method: 'goToPurchases'});
|
||||
} else {
|
||||
|
@ -209,17 +210,19 @@ Rectangle {
|
|||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletSetup_raiseKeyboard') {
|
||||
root.keyboardRaised = true;
|
||||
root.isPassword = msg.isPasswordField;
|
||||
} else if (msg.method === 'walletSetup_lowerKeyboard') {
|
||||
root.keyboardRaised = false;
|
||||
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
|
||||
root.activeView = "security";
|
||||
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
|
||||
root.activeView = "security";
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
if (passphraseChange.visible) {
|
||||
if (msg.method === 'walletSetup_raiseKeyboard') {
|
||||
root.keyboardRaised = true;
|
||||
root.isPassword = msg.isPasswordField;
|
||||
} else if (msg.method === 'walletSetup_lowerKeyboard') {
|
||||
root.keyboardRaised = false;
|
||||
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
|
||||
root.activeView = "security";
|
||||
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
|
||||
root.activeView = "security";
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,7 +263,7 @@ Rectangle {
|
|||
color: hifi.colors.baseGray;
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +284,7 @@ Rectangle {
|
|||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
commerce.getLoginStatus();
|
||||
Commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +298,7 @@ Rectangle {
|
|||
Connections {
|
||||
onSendSignalToParent: {
|
||||
if (msg.method === "authSuccess") {
|
||||
commerce.getWalletStatus();
|
||||
Commerce.getWalletStatus();
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
|
@ -342,6 +345,7 @@ Rectangle {
|
|||
passphraseChange.clearPassphraseFields();
|
||||
passphraseChange.resetSubmitButton();
|
||||
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
|
||||
securityImageChange.initModel();
|
||||
root.activeView = "securityImageChange";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ Item {
|
|||
property bool historyReceived: false;
|
||||
property int pendingCount: 0;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onBalanceResult : {
|
||||
balanceText.text = result.data.balance;
|
||||
|
@ -135,8 +135,8 @@ Item {
|
|||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
historyReceived = false;
|
||||
commerce.balance();
|
||||
commerce.history();
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
} else {
|
||||
refreshTimer.stop();
|
||||
}
|
||||
|
@ -165,8 +165,8 @@ Item {
|
|||
interval: 4000;
|
||||
onTriggered: {
|
||||
console.log("Refreshing Wallet Home...");
|
||||
commerce.balance();
|
||||
commerce.history();
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ Item {
|
|||
source: "images/wallet-bg.jpg";
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (!exists && root.lastPage === "step_2") {
|
||||
|
@ -236,6 +236,7 @@ Item {
|
|||
height: 50;
|
||||
text: "Set Up Wallet";
|
||||
onClicked: {
|
||||
securityImageSelection.initModel();
|
||||
root.activeView = "step_2";
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ Item {
|
|||
height: 50;
|
||||
text: "Cancel";
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
|
||||
sendSignalToWallet({method: 'walletSetup_cancelClicked', referrer: root.referrer });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +366,7 @@ Item {
|
|||
onClicked: {
|
||||
root.lastPage = "step_2";
|
||||
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
|
||||
commerce.chooseSecurityImage(securityImagePath);
|
||||
Commerce.chooseSecurityImage(securityImagePath);
|
||||
root.activeView = "step_3";
|
||||
passphraseSelection.clearPassphraseFields();
|
||||
}
|
||||
|
@ -448,7 +449,7 @@ Item {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
Commerce.getWalletAuthenticatedStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,7 +535,7 @@ Item {
|
|||
onClicked: {
|
||||
if (passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
root.lastPage = "step_3";
|
||||
commerce.generateKeyPair();
|
||||
Commerce.generateKeyPair();
|
||||
root.activeView = "step_4";
|
||||
}
|
||||
}
|
||||
|
@ -667,7 +668,7 @@ Item {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
Commerce.getKeyFilePathIfExists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 35 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/07.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/08.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/09.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/10.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/11.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/12.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/13.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/14.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/15.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/16.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/17.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/18.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/19.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/20.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/21.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/22.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/23.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/24.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/25.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/26.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/27.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/28.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/29.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/30.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/31.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/32.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/33.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/34.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
|
@ -123,11 +123,11 @@ Item {
|
|||
hoverEnabled: true
|
||||
enabled: true
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
newEntityButton.clicked();
|
||||
}
|
||||
onEntered: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
newEntityButton.state = "hover state";
|
||||
}
|
||||
onExited: {
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "../../styles-uit"
|
||||
import "../audio" as HifiAudio
|
||||
|
||||
Item {
|
||||
id: tablet
|
||||
objectName: "tablet"
|
||||
property int rowIndex: 6 // by default
|
||||
property int columnIndex: 1 // point to 'go to location'
|
||||
property int count: (flowMain.children.length - 1)
|
||||
|
||||
// used to look up a button by its uuid
|
||||
function findButtonIndex(uuid) {
|
||||
if (!uuid) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var i in flowMain.children) {
|
||||
var child = flowMain.children[i];
|
||||
if (child.uuid === uuid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function sortButtons() {
|
||||
var children = [];
|
||||
for (var i = 0; i < flowMain.children.length; i++) {
|
||||
children[i] = flowMain.children[i];
|
||||
}
|
||||
|
||||
children.sort(function (a, b) {
|
||||
if (a.sortOrder === b.sortOrder) {
|
||||
// subsort by stableOrder, because JS sort is not stable in qml.
|
||||
return a.stableOrder - b.stableOrder;
|
||||
} else {
|
||||
return a.sortOrder - b.sortOrder;
|
||||
}
|
||||
});
|
||||
|
||||
flowMain.children = children;
|
||||
}
|
||||
|
||||
// called by C++ code when a button should be added to the tablet
|
||||
function addButtonProxy(properties) {
|
||||
var component = Qt.createComponent("TabletButton.qml");
|
||||
var button = component.createObject(flowMain);
|
||||
|
||||
// copy all properites to button
|
||||
var keys = Object.keys(properties).forEach(function (key) {
|
||||
button[key] = properties[key];
|
||||
});
|
||||
|
||||
// pass a reference to the tabletRoot object to the button.
|
||||
if (tabletRoot) {
|
||||
button.tabletRoot = tabletRoot;
|
||||
} else {
|
||||
button.tabletRoot = parent.parent;
|
||||
}
|
||||
|
||||
sortButtons();
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
// called by C++ code when a button should be removed from the tablet
|
||||
function removeButtonProxy(properties) {
|
||||
var index = findButtonIndex(properties.uuid);
|
||||
if (index < 0) {
|
||||
console.log("Warning: Tablet.qml could not find button with uuid = " + properties.uuid);
|
||||
} else {
|
||||
flowMain.children[index].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgTopBar
|
||||
height: 90
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 0
|
||||
left: parent.left
|
||||
leftMargin: 0
|
||||
right: parent.right
|
||||
rightMargin: 0
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#1e1e1e"
|
||||
}
|
||||
}
|
||||
|
||||
HifiAudio.MicBar {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 30
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 150
|
||||
height: 50
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RalewaySemiBold {
|
||||
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
visible: Account.loggedIn
|
||||
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
|
||||
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (!Account.loggedIn) {
|
||||
DialogsManager.showLoginDialog()
|
||||
} else {
|
||||
Account.logOut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#0f212e"
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.top: bgTopBar.bottom
|
||||
anchors.topMargin: 0
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
contentWidth: parent.width
|
||||
contentHeight: flowMain.childrenRect.height + flowMain.anchors.topMargin + flowMain.anchors.bottomMargin + flowMain.spacing
|
||||
clip: true
|
||||
Flow {
|
||||
id: flowMain
|
||||
spacing: 16
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentItemState(state) {
|
||||
var index = rowIndex + columnIndex;
|
||||
|
||||
if (index >= 0 && index <= count ) {
|
||||
flowMain.children[index].state = state;
|
||||
}
|
||||
}
|
||||
|
||||
function nextItem() {
|
||||
setCurrentItemState("base state");
|
||||
var nextColumnIndex = (columnIndex + 3 + 1) % 3;
|
||||
var nextIndex = rowIndex + nextColumnIndex;
|
||||
if(nextIndex <= count) {
|
||||
columnIndex = nextColumnIndex;
|
||||
};
|
||||
setCurrentItemState("hover state");
|
||||
}
|
||||
|
||||
function previousItem() {
|
||||
setCurrentItemState("base state");
|
||||
var prevIndex = (columnIndex + 3 - 1) % 3;
|
||||
if((rowIndex + prevIndex) <= count){
|
||||
columnIndex = prevIndex;
|
||||
}
|
||||
setCurrentItemState("hover state");
|
||||
}
|
||||
|
||||
function upItem() {
|
||||
setCurrentItemState("base state");
|
||||
rowIndex = rowIndex - 3;
|
||||
if (rowIndex < 0 ) {
|
||||
rowIndex = (count - (count % 3));
|
||||
var index = rowIndex + columnIndex;
|
||||
if(index > count) {
|
||||
rowIndex = rowIndex - 3;
|
||||
}
|
||||
}
|
||||
setCurrentItemState("hover state");
|
||||
}
|
||||
|
||||
function downItem() {
|
||||
setCurrentItemState("base state");
|
||||
rowIndex = rowIndex + 3;
|
||||
var index = rowIndex + columnIndex;
|
||||
if (index > count ) {
|
||||
rowIndex = 0;
|
||||
}
|
||||
setCurrentItemState("hover state");
|
||||
}
|
||||
|
||||
function selectItem() {
|
||||
flowMain.children[rowIndex + columnIndex].clicked();
|
||||
if (tabletRoot) {
|
||||
tabletRoot.playButtonClickSound();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onRightPressed: nextItem();
|
||||
Keys.onLeftPressed: previousItem();
|
||||
Keys.onDownPressed: downItem();
|
||||
Keys.onUpPressed: upItem();
|
||||
Keys.onReturnPressed: selectItem();
|
||||
}
|
|
@ -20,6 +20,8 @@ import "../"
|
|||
import "../toolbars"
|
||||
import "../../styles-uit" as HifiStyles
|
||||
import "../../controls-uit" as HifiControls
|
||||
import QtQuick.Controls 2.2 as QQC2
|
||||
import QtQuick.Templates 2.2 as T
|
||||
|
||||
// references HMD, AddressManager, AddressBarDialog from root context
|
||||
|
||||
|
@ -223,7 +225,7 @@ StackView {
|
|||
visible: addressLine.text.length === 0
|
||||
}
|
||||
|
||||
TextField {
|
||||
QQC2.TextField {
|
||||
id: addressLine
|
||||
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
|
||||
anchors {
|
||||
|
@ -238,16 +240,36 @@ StackView {
|
|||
addressBarDialog.keyboardEnabled = false;
|
||||
toggleOrGo();
|
||||
}
|
||||
placeholderText: "Type domain address here"
|
||||
|
||||
// unfortunately TextField from Quick Controls 2 disallow customization of placeHolderText color without creation of new style
|
||||
property string placeholderText2: "Type domain address here"
|
||||
verticalAlignment: TextInput.AlignBottom
|
||||
style: TextFieldStyle {
|
||||
textColor: hifi.colors.text
|
||||
placeholderTextColor: "gray"
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
}
|
||||
|
||||
color: hifi.colors.text
|
||||
background: Item {}
|
||||
|
||||
QQC2.Label {
|
||||
T.TextField {
|
||||
id: control
|
||||
|
||||
padding: 6 // numbers taken from Qt\5.9.2\Src\qtquickcontrols2\src\imports\controls\TextField.qml
|
||||
leftPadding: padding + 4
|
||||
}
|
||||
background: Item {}
|
||||
|
||||
font: parent.font
|
||||
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
|
||||
text: parent.placeholderText2
|
||||
verticalAlignment: "AlignVCenter"
|
||||
color: 'gray'
|
||||
visible: parent.text === ''
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,6 @@ Item {
|
|||
enabled: true
|
||||
preventStealing: true
|
||||
onClicked: {
|
||||
console.log("Tablet Button Clicked!");
|
||||
if (tabletButton.inDebugMode) {
|
||||
if (tabletButton.isActive) {
|
||||
tabletButton.isActive = false;
|
||||
|
@ -133,12 +132,12 @@ Item {
|
|||
}
|
||||
tabletButton.clicked();
|
||||
if (tabletRoot) {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
}
|
||||
onEntered: {
|
||||
tabletButton.isEntered = true;
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
|
||||
if (tabletButton.isActive) {
|
||||
tabletButton.state = "hover active state";
|
||||
|
|
159
interface/resources/qml/hifi/tablet/TabletHome.qml
Normal file
|
@ -0,0 +1,159 @@
|
|||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "."
|
||||
import "../../styles-uit"
|
||||
import "../audio" as HifiAudio
|
||||
|
||||
Item {
|
||||
id: tablet
|
||||
objectName: "tablet"
|
||||
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
Rectangle {
|
||||
id: bgTopBar
|
||||
height: 90
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#1e1e1e"
|
||||
}
|
||||
}
|
||||
|
||||
HifiAudio.MicBar {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 30
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 150
|
||||
height: 50
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RalewaySemiBold {
|
||||
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
visible: Account.loggedIn
|
||||
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
|
||||
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (!Account.loggedIn) {
|
||||
DialogsManager.showLoginDialog()
|
||||
} else {
|
||||
Account.logOut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
clip: true
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#0f212e"
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: bgTopBar.bottom
|
||||
|
||||
GridView {
|
||||
id: flickable
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: cellWidth * 3
|
||||
cellHeight: 145
|
||||
cellWidth: 145
|
||||
model: tabletProxy.buttons
|
||||
delegate: Item {
|
||||
width: flickable.cellWidth
|
||||
height: flickable.cellHeight
|
||||
property var proxy: modelData
|
||||
|
||||
TabletButton {
|
||||
id: tabletButton
|
||||
anchors.centerIn: parent
|
||||
onClicked: modelData.clicked()
|
||||
state: wrapper.GridView.isCurrentItem ? "hover state" : "base state"
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelData;
|
||||
onPropertiesChanged: {
|
||||
updateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: updateProperties()
|
||||
|
||||
function updateProperties() {
|
||||
var keys = Object.keys(modelData.properties).forEach(function (key) {
|
||||
if (tabletButton[key] !== modelData.properties[key]) {
|
||||
tabletButton[key] = modelData.properties[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onRightPressed: flickable.moveCurrentIndexRight();
|
||||
Keys.onLeftPressed: flickable.moveCurrentIndexLeft();
|
||||
Keys.onDownPressed: flickable.moveCurrentIndexDown();
|
||||
Keys.onUpPressed: flickable.moveCurrentIndexUp();
|
||||
Keys.onReturnPressed: {
|
||||
if (flickable.currentItem) {
|
||||
flickable.currentItem.proxy.clicked();
|
||||
if (tabletRoot) {
|
||||
tabletRoot.playButtonClickSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,12 +77,12 @@ FocusScope {
|
|||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonHover);
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
listView.currentIndex = index
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
tabletInterface.playSound(TabletEnums.ButtonClick);
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
root.selected(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,37 +68,36 @@ Item {
|
|||
|
||||
function loadSource(url) {
|
||||
tabletApps.clear();
|
||||
loader.source = ""; // make sure we load the qml fresh each time.
|
||||
loader.source = url;
|
||||
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
|
||||
loader.load(url)
|
||||
}
|
||||
|
||||
function loadQMLOnTop(url) {
|
||||
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
|
||||
loader.source = "";
|
||||
loader.source = tabletApps.get(currentApp).appUrl;
|
||||
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
|
||||
loader.item.gotoPreviousApp = true;
|
||||
}
|
||||
loader.load(tabletApps.get(currentApp).appUrl, function(){
|
||||
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
|
||||
loader.item.gotoPreviousApp = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function loadWebOnTop(url, injectJavaScriptUrl) {
|
||||
tabletApps.append({"appUrl": loader.source, "isWebUrl": true, "scriptUrl": injectJavaScriptUrl, "appWebUrl": url});
|
||||
loader.item.url = tabletApps.get(currentApp).appWebUrl;
|
||||
loader.item.scriptUrl = tabletApps.get(currentApp).scriptUrl;
|
||||
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
|
||||
loader.item.gotoPreviousApp = true;
|
||||
}
|
||||
function loadWebContent(source, url, injectJavaScriptUrl) {
|
||||
tabletApps.append({"appUrl": source, "isWebUrl": true, "scriptUrl": injectJavaScriptUrl, "appWebUrl": url});
|
||||
loader.load(source, function() {
|
||||
loader.item.scriptURL = injectJavaScriptUrl;
|
||||
loader.item.url = url;
|
||||
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
|
||||
loader.item.gotoPreviousApp = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "TabletWebView.qml";
|
||||
function loadWebBase(url, injectJavaScriptUrl) {
|
||||
loadWebContent("hifi/tablet/TabletWebView.qml", url, injectJavaScriptUrl);
|
||||
}
|
||||
|
||||
function loadTabletWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "./BlocksWebView.qml";
|
||||
function loadTabletWebBase(url, injectJavaScriptUrl) {
|
||||
loadWebContent("hifi/tablet/BlocksWebView.qml", url, injectJavaScriptUrl);
|
||||
}
|
||||
|
||||
function returnToPreviousApp() {
|
||||
|
@ -110,7 +109,7 @@ Item {
|
|||
loadSource("TabletWebView.qml");
|
||||
loadWebUrl(webUrl, scriptUrl);
|
||||
} else {
|
||||
loader.source = tabletApps.get(currentApp).appUrl;
|
||||
loader.load(tabletApps.get(currentApp).appUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,47 +172,79 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
objectName: "loader"
|
||||
asynchronous: false
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
if (loader.item.hasOwnProperty("setRootMenu")) {
|
||||
loader.item.setRootMenu(tabletRoot.rootMenu, tabletRoot.subMenu);
|
||||
}
|
||||
loader.item.forceActiveFocus();
|
||||
|
||||
if (openModal) {
|
||||
openModal.canceled();
|
||||
openModal.destroy();
|
||||
openModal = null;
|
||||
}
|
||||
|
||||
if (openBrowser) {
|
||||
openBrowser.destroy();
|
||||
openBrowser = null;
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loader
|
||||
objectName: "loader";
|
||||
anchors.fill: parent;
|
||||
property string source: "";
|
||||
property var item: null;
|
||||
signal loaded;
|
||||
|
||||
onWidthChanged: {
|
||||
if (loader.item) {
|
||||
loader.item.width = loader.width;
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: {
|
||||
if (loader.item) {
|
||||
loader.item.height = loader.height;
|
||||
}
|
||||
}
|
||||
|
||||
function load(newSource, callback) {
|
||||
if (loader.source == newSource) {
|
||||
loader.loaded();
|
||||
return;
|
||||
}
|
||||
|
||||
if (loader.item) {
|
||||
loader.item.destroy();
|
||||
loader.item = null;
|
||||
}
|
||||
|
||||
QmlSurface.load(newSource, loader, function(newItem) {
|
||||
loader.item = newItem;
|
||||
loader.item.width = loader.width;
|
||||
loader.item.height = loader.height;
|
||||
loader.loaded();
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
if (loader.item.hasOwnProperty("setRootMenu")) {
|
||||
loader.item.setRootMenu(tabletRoot.rootMenu, tabletRoot.subMenu);
|
||||
}
|
||||
loader.item.forceActiveFocus();
|
||||
|
||||
if (openModal) {
|
||||
openModal.canceled();
|
||||
openModal.destroy();
|
||||
openModal = null;
|
||||
}
|
||||
|
||||
if (openBrowser) {
|
||||
openBrowser.destroy();
|
||||
openBrowser = null;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
width: 480
|
||||
height: 706
|
||||
|
||||
|
|
|
@ -59,26 +59,25 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
function loadSource(url) {
|
||||
loader.source = ""; // make sure we load the qml fresh each time.
|
||||
loader.source = url;
|
||||
loader.load(url)
|
||||
}
|
||||
|
||||
function loadWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "WindowWebView.qml";
|
||||
function loadWebContent(source, url, injectJavaScriptUrl) {
|
||||
loader.load(source, function() {
|
||||
loader.item.scriptURL = injectJavaScriptUrl;
|
||||
loader.item.url = url;
|
||||
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||
loader.item.closeButtonVisible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadTabletWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "./BlocksWebView.qml";
|
||||
function loadWebBase(url, injectJavaScriptUrl) {
|
||||
loadWebContent("hifi/tablet/TabletWebView.qml", url, injectJavaScriptUrl);
|
||||
}
|
||||
|
||||
function loadWebUrl(url, injectedJavaScriptUrl) {
|
||||
loader.item.url = url;
|
||||
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||
loader.item.closeButtonVisible = false;
|
||||
}
|
||||
function loadTabletWebBase(url, injectJavaScriptUrl) {
|
||||
loadWebContent("hifi/tablet/BlocksWebView.qml", url, injectJavaScriptUrl);
|
||||
}
|
||||
|
||||
// used to send a message from qml to interface script.
|
||||
|
@ -111,38 +110,68 @@ Windows.ScrollingWindow {
|
|||
username = newUsername;
|
||||
}
|
||||
|
||||
Loader {
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loader
|
||||
objectName: "loader"
|
||||
asynchronous: false
|
||||
objectName: "loader";
|
||||
property string source: "";
|
||||
property var item: null;
|
||||
|
||||
height: pane.scrollHeight
|
||||
width: pane.contentWidth
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: eventBridge
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
signal loaded;
|
||||
|
||||
onWidthChanged: {
|
||||
if (loader.item) {
|
||||
loader.item.width = loader.width;
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
|
||||
onHeightChanged: {
|
||||
if (loader.item) {
|
||||
loader.item.height = loader.height;
|
||||
}
|
||||
if (loader.item.hasOwnProperty("setRootMenu")) {
|
||||
loader.item.setRootMenu(tabletRoot.rootMenu, tabletRoot.subMenu);
|
||||
}
|
||||
|
||||
function load(newSource, callback) {
|
||||
if (loader.item) {
|
||||
loader.item.destroy();
|
||||
loader.item = null;
|
||||
}
|
||||
loader.item.forceActiveFocus();
|
||||
|
||||
QmlSurface.load(newSource, loader, function(newItem) {
|
||||
loader.item = newItem;
|
||||
loader.item.width = loader.width;
|
||||
loader.item.height = loader.height;
|
||||
loader.loaded();
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
if (loader.item.hasOwnProperty("setRootMenu")) {
|
||||
loader.item.setRootMenu(tabletRoot.rootMenu, tabletRoot.subMenu);
|
||||
}
|
||||
loader.item.forceActiveFocus();
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
implicitWidth: 480
|
||||
implicitHeight: 706
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ Window {
|
|||
horizontalSpacers: horizontal
|
||||
verticalSpacers: !horizontal
|
||||
}
|
||||
property var tabletProxy;
|
||||
property var buttonModel: ListModel {}
|
||||
hideBackground: true
|
||||
resizable: false
|
||||
destroyOnCloseButton: false
|
||||
|
@ -23,24 +25,32 @@ Window {
|
|||
activator: Item {}
|
||||
property bool horizontal: true
|
||||
property real buttonSize: 50;
|
||||
property var buttons: []
|
||||
property var container: horizontal ? row : column
|
||||
|
||||
Settings {
|
||||
category: "toolbar/" + window.objectName
|
||||
property alias x: window.x
|
||||
property alias y: window.y
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonComponent
|
||||
ToolbarButton {
|
||||
id: toolbarButton
|
||||
property var proxy: modelData;
|
||||
onClicked: proxy.clicked()
|
||||
Component.onCompleted: updateProperties()
|
||||
|
||||
onHorizontalChanged: {
|
||||
var newParent = horizontal ? row : column;
|
||||
for (var i in buttons) {
|
||||
var child = buttons[i];
|
||||
child.parent = newParent;
|
||||
if (horizontal) {
|
||||
child.y = 0
|
||||
} else {
|
||||
child.x = 0
|
||||
Connections {
|
||||
target: proxy;
|
||||
onPropertiesChanged: updateProperties();
|
||||
}
|
||||
|
||||
function updateProperties() {
|
||||
Object.keys(proxy.properties).forEach(function (key) {
|
||||
if (toolbarButton[key] !== proxy.properties[key]) {
|
||||
toolbarButton[key] = proxy.properties[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,97 +62,22 @@ Window {
|
|||
|
||||
Row {
|
||||
id: row
|
||||
visible: window.horizontal
|
||||
spacing: 6
|
||||
Repeater {
|
||||
model: buttonModel
|
||||
delegate: buttonComponent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
id: column
|
||||
visible: !window.horizontal
|
||||
spacing: 6
|
||||
}
|
||||
|
||||
Component { id: toolbarButtonBuilder; ToolbarButton { } }
|
||||
}
|
||||
|
||||
|
||||
function findButtonIndex(name) {
|
||||
if (!name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var i in buttons) {
|
||||
var child = buttons[i];
|
||||
if (child.objectName === name) {
|
||||
return i;
|
||||
Repeater {
|
||||
model: buttonModel
|
||||
delegate: buttonComponent
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function findButton(name) {
|
||||
var index = findButtonIndex(name);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
return buttons[index];
|
||||
}
|
||||
|
||||
function sortButtons() {
|
||||
var children = [];
|
||||
for (var i = 0; i < container.children.length; i++) {
|
||||
children[i] = container.children[i];
|
||||
}
|
||||
|
||||
children.sort(function (a, b) {
|
||||
if (a.sortOrder === b.sortOrder) {
|
||||
// subsort by stableOrder, because JS sort is not stable in qml.
|
||||
return a.stableOrder - b.stableOrder;
|
||||
} else {
|
||||
return a.sortOrder - b.sortOrder;
|
||||
}
|
||||
});
|
||||
|
||||
container.children = children;
|
||||
}
|
||||
|
||||
function addButton(properties) {
|
||||
properties = properties || {}
|
||||
|
||||
// If a name is specified, then check if there's an existing button with that name
|
||||
// and return it if so. This will allow multiple clients to listen to a single button,
|
||||
// and allow scripts to be idempotent so they don't duplicate buttons if they're reloaded
|
||||
var result = findButton(properties.objectName);
|
||||
if (result) {
|
||||
for (var property in properties) {
|
||||
result[property] = properties[property];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
properties.toolbar = this;
|
||||
properties.opacity = 0;
|
||||
result = toolbarButtonBuilder.createObject(container, properties);
|
||||
buttons.push(result);
|
||||
|
||||
result.opacity = 1;
|
||||
|
||||
sortButtons();
|
||||
|
||||
fadeIn(null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeButton(name) {
|
||||
var index = findButtonIndex(name);
|
||||
if (index < -1) {
|
||||
console.warn("Tried to remove non-existent button " + name);
|
||||
return;
|
||||
}
|
||||
|
||||
buttons[index].destroy();
|
||||
buttons.splice(index, 1);
|
||||
|
||||
if (buttons.length === 0) {
|
||||
fadeOut(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ StateImage {
|
|||
property string activeHoverIcon: button.activeIcon
|
||||
|
||||
property int sortOrder: 100
|
||||
property int stableSortOrder: 0
|
||||
property int stableOrder: 0
|
||||
property var uuid;
|
||||
|
||||
signal clicked()
|
||||
|
||||
|
|
BIN
interface/resources/qml/js/Utils.jsc
Normal file
|
@ -971,7 +971,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// set the account manager's root URL and trigger a login request if we don't have the access token
|
||||
accountManager->setIsAgent(true);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
|
@ -1496,6 +1496,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
}
|
||||
});
|
||||
|
||||
connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [=](const EntityItemID& entityItemID) {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
|
||||
if (myAvatar) {
|
||||
myAvatar->clearAvatarEntity(entityItemID);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard focus handling for Web overlays.
|
||||
auto overlays = &(qApp->getOverlays());
|
||||
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
|
||||
|
@ -2240,27 +2248,65 @@ extern void setupPreferences();
|
|||
void Application::initializeUi() {
|
||||
// Make sure all QML surfaces share the main thread GL context
|
||||
OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext());
|
||||
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "OverlayWindowTest.qml" },
|
||||
[](QQmlContext* context) {
|
||||
qDebug() << "Whitelist OverlayWindow worked";
|
||||
context->setContextProperty("OverlayWindowTestString", "TestWorked");
|
||||
});
|
||||
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "hifi/audio/Audio.qml" },
|
||||
[](QQmlContext* context) {
|
||||
qDebug() << "QQQ" << __FUNCTION__ << "Whitelist Audio worked";
|
||||
});
|
||||
|
||||
|
||||
AddressBarDialog::registerType();
|
||||
ErrorDialog::registerType();
|
||||
LoginDialog::registerType();
|
||||
Tooltip::registerType();
|
||||
UpdateDialog::registerType();
|
||||
QmlCommerce::registerType();
|
||||
QmlContextCallback callback = [](QQmlContext* context) {
|
||||
context->setContextProperty("Commerce", new QmlCommerce());
|
||||
};
|
||||
OffscreenQmlSurface::addWhitelistContextHandler({
|
||||
QUrl{ "hifi/commerce/checkout/Checkout.qml" },
|
||||
QUrl{ "hifi/commerce/common/CommerceLightbox.qml" },
|
||||
QUrl{ "hifi/commerce/common/EmulatedMarketplaceHeader.qml" },
|
||||
QUrl{ "hifi/commerce/common/FirstUseTutorial.qml" },
|
||||
QUrl{ "hifi/commerce/common/SortableListModel.qml" },
|
||||
QUrl{ "hifi/commerce/inspectionCertificate/InspectionCertificate.qml" },
|
||||
QUrl{ "hifi/commerce/purchases/PurchasedItem.qml" },
|
||||
QUrl{ "hifi/commerce/purchases/Purchases.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/Help.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/NeedsLogIn.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/PassphraseChange.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/PassphraseModal.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/PassphraseSelection.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/Security.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/SendMoney.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/WalletSetup.qml" },
|
||||
}, callback);
|
||||
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
|
||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||
qmlRegisterType<WebBrowserSuggestionsEngine>("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine");
|
||||
|
||||
{
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->getTablet(SYSTEM_TABLET);
|
||||
}
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create();
|
||||
|
||||
auto surfaceContext = offscreenUi->getSurfaceContext();
|
||||
|
||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
||||
// support the window management and scripting proxies for VR use
|
||||
offscreenUi->createDesktop(QString("qrc:///qml/hifi/Desktop.qml"));
|
||||
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
|
||||
|
||||
// FIXME either expose so that dialogs can set this themselves or
|
||||
// do better detection in the offscreen UI of what has focus
|
||||
|
@ -2328,9 +2374,6 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
// Tablet inteference with Tablet.qml. Need to avoid this in QML space
|
||||
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
|
@ -3222,8 +3265,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
_keysPressed.remove(event->key());
|
||||
|
||||
|
@ -4282,9 +4323,11 @@ void Application::updateLOD(float deltaTime) const {
|
|||
PerformanceTimer perfTimer("LOD");
|
||||
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
|
||||
if (!isThrottleRendering()) {
|
||||
float batchTime = (float)_gpuContext->getFrameTimerBatchAverage();
|
||||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(batchTime, engineRunTime, deltaTime);
|
||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||
}
|
||||
|
@ -4848,8 +4891,7 @@ void Application::update(float deltaTime) {
|
|||
if (_physicsEnabled) {
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PreStep");
|
||||
|
||||
PerformanceTimer perfTimer("updateStates)");
|
||||
PerformanceTimer perfTimer("preStep)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
@ -4882,22 +4924,22 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Step");
|
||||
PerformanceTimer perfTimer("stepSimulation");
|
||||
PerformanceTimer perfTimer("step");
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PostStep");
|
||||
PerformanceTimer perfTimer("harvestChanges");
|
||||
PerformanceTimer perfTimer("postStep");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||
// we have a better idea of which objects we own or should own.
|
||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PROFILE_RANGE(simulation_physics, "Harvest");
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
PROFILE_RANGE(simulation_physics, "HandleChanges");
|
||||
PerformanceTimer perfTimer("handleChanges");
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
|
@ -4908,17 +4950,15 @@ void Application::update(float deltaTime) {
|
|||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
||||
// handleCollisionEvents() AFTER handleOutgoingChanges()
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
||||
PerformanceTimer perfTimer("entities");
|
||||
avatarManager->handleCollisionEvents(collisionEvents);
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||
// deadlock.)
|
||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||
}
|
||||
|
||||
PROFILE_RANGE(simulation_physics, "UpdateEntities");
|
||||
// NOTE: the getEntities()->update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
getEntities()->update(true); // update the models...
|
||||
|
@ -4929,7 +4969,8 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
if (PerformanceTimer::isActive() &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||
_physicsEngine->harvestPerformanceStats();
|
||||
}
|
||||
|
@ -5832,9 +5873,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<TabletProxy>, wrapperFromScriptValue<TabletProxy>);
|
||||
qScriptRegisterMetaType(scriptEngine.data(),
|
||||
wrapperToScriptValue<TabletButtonProxy>, wrapperFromScriptValue<TabletButtonProxy>);
|
||||
// Tablet inteference with Tablet.qml. Need to avoid this in QML space
|
||||
scriptEngine->registerGlobalObject("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
// FIXME remove these deprecated names for the tablet scripting interface
|
||||
scriptEngine->registerGlobalObject("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
|
||||
auto toolbarScriptingInterface = DependencyManager::get<ToolbarScriptingInterface>().data();
|
||||
DependencyManager::get<TabletScriptingInterface>().data()->setToolbarScriptingInterface(toolbarScriptingInterface);
|
||||
|
@ -7241,13 +7282,17 @@ void Application::updateDisplayMode() {
|
|||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
|
||||
// Make the switch atomic from the perspective of other threads
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_displayPluginLock);
|
||||
// Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below.
|
||||
bool wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool();
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", true);
|
||||
bool wasRepositionLocked = false;
|
||||
if (desktop) {
|
||||
// Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below.
|
||||
wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool();
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", true);
|
||||
}
|
||||
|
||||
if (_displayPlugin) {
|
||||
disconnect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent);
|
||||
|
@ -7293,7 +7338,6 @@ void Application::updateDisplayMode() {
|
|||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection);
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
|
@ -7506,4 +7550,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) {
|
|||
_avatarOverrideUrl = url;
|
||||
_saveAvatarOverrideUrl = save;
|
||||
}
|
||||
|
||||
void Application::saveNextPhysicsStats(QString filename) {
|
||||
_physicsEngine->saveNextPhysicsStats(filename);
|
||||
}
|
||||
|
||||
#include "Application.moc"
|
||||
|
|
|
@ -280,6 +280,7 @@ public:
|
|||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||
void saveNextPhysicsStats(QString filename);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
@ -432,6 +433,7 @@ private slots:
|
|||
|
||||
void handleSandboxStatus(QNetworkReply* reply);
|
||||
void switchDisplayMode();
|
||||
|
||||
private:
|
||||
static void initDisplay();
|
||||
void init();
|
||||
|
|
|
@ -19,10 +19,14 @@ class FancyCamera : public Camera {
|
|||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Camera
|
||||
* @property cameraEntity {EntityID} The position and rotation properties of
|
||||
* the entity specified by this ID are then used as the camera's position and
|
||||
* orientation. Only works when <code>mode</code> is "entity".
|
||||
* @namespace
|
||||
* @augments Camera
|
||||
*/
|
||||
|
||||
// FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
|
||||
/**jsdoc
|
||||
* @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in
|
||||
* entity mode.
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
|
@ -34,7 +38,25 @@ public:
|
|||
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can
|
||||
* also get the entity ID using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.getCameraEntity
|
||||
* @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; <code>null</code> if no camera
|
||||
* entity has been set.
|
||||
*/
|
||||
QUuid getCameraEntity() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the
|
||||
* entity using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.setCameraEntity
|
||||
* @param {Uuid} entityID The entity that the camera should follow when it's in entity mode.
|
||||
* @example <caption>Move your camera to the position and orientation of the closest entity.</caption>
|
||||
* Camera.setModeString("entity");
|
||||
* var entity = Entities.findClosestEntity(MyAvatar.position, 100.0);
|
||||
* Camera.setCameraEntity(entity);
|
||||
*/
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
private:
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "LODManager.h"
|
||||
|
||||
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
||||
|
||||
|
@ -39,156 +40,95 @@ float LODManager::getLODIncreaseFPS() {
|
|||
return getDesktopLODIncreaseFPS();
|
||||
}
|
||||
|
||||
void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec) {
|
||||
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
|
||||
// to determine if we should adjust the level of detail (LOD).
|
||||
//
|
||||
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
|
||||
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
|
||||
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
|
||||
// reduced by 1/e every timescale of real-time that passes.
|
||||
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
|
||||
//
|
||||
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
|
||||
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage settle
|
||||
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
|
||||
// multiples of the running average timescale:
|
||||
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
||||
|
||||
// NOTE: our first ~100 samples at app startup are completely all over the place, and we don't
|
||||
// really want to count them in our average, so we will ignore the real frame rates and stuff
|
||||
// our moving average with simulated good data
|
||||
const int IGNORE_THESE_SAMPLES = 100;
|
||||
if (_fpsAverageUpWindow.getSampleCount() < IGNORE_THESE_SAMPLES) {
|
||||
_lastStable = _lastUpShift = _lastDownShift = usecTimestampNow();
|
||||
}
|
||||
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
|
||||
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
|
||||
|
||||
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||
// compute time-weighted running average renderTime
|
||||
const float OVERLAY_AND_SWAP_TIME_BUDGET = 2.0f; // msec
|
||||
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
|
||||
float maxTime = glm::max(renderTime, engineRunTime);
|
||||
const float BLEND_TIMESCALE = 0.3f; // sec
|
||||
const float MIN_DELTA_TIME = 0.001f;
|
||||
const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
|
||||
float blend = BLEND_TIMESCALE / safeDeltaTime;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
// Note: we MUST clamp the blend to 1.0 for stability
|
||||
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec
|
||||
if (!_automaticLODAdjust) {
|
||||
// early exit
|
||||
return;
|
||||
}
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxTime; // msec
|
||||
|
||||
// translate into fps for legacy implementation
|
||||
float oldOctreeSizeScale = _octreeSizeScale;
|
||||
float currentFPS = (float)MSECS_PER_SECOND / _avgRenderTime;
|
||||
|
||||
_fpsAverageStartWindow.updateAverage(currentFPS);
|
||||
_fpsAverageDownWindow.updateAverage(currentFPS);
|
||||
_fpsAverageUpWindow.updateAverage(currentFPS);
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
quint64 elapsedSinceDownShift = now - _lastDownShift;
|
||||
quint64 elapsedSinceUpShift = now - _lastUpShift;
|
||||
|
||||
quint64 lastStableOrUpshift = glm::max(_lastUpShift, _lastStable);
|
||||
quint64 elapsedSinceStableOrUpShift = now - lastStableOrUpshift;
|
||||
|
||||
if (_automaticLODAdjust) {
|
||||
bool changed = false;
|
||||
|
||||
// LOD Downward adjustment
|
||||
// If we've been downshifting, we watch a shorter downshift window so that we will quickly move toward our
|
||||
// target frame rate. But if we haven't just done a downshift (either because our last shift was an upshift,
|
||||
// or because we've just started out) then we look at a much longer window to consider whether or not to start
|
||||
// downshifting.
|
||||
bool doDownShift = false;
|
||||
|
||||
if (_isDownshifting) {
|
||||
// only consider things if our DOWN_SHIFT time has elapsed...
|
||||
if (elapsedSinceDownShift > DOWN_SHIFT_ELPASED) {
|
||||
doDownShift = _fpsAverageDownWindow.getAverage() < getLODDecreaseFPS();
|
||||
|
||||
if (!doDownShift) {
|
||||
qCDebug(interfaceapp) << "---- WE APPEAR TO BE DONE DOWN SHIFTING -----";
|
||||
_isDownshifting = false;
|
||||
_lastStable = now;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doDownShift = (elapsedSinceStableOrUpShift > START_SHIFT_ELPASED
|
||||
&& _fpsAverageStartWindow.getAverage() < getLODDecreaseFPS());
|
||||
}
|
||||
|
||||
if (doDownShift) {
|
||||
|
||||
// Octree items... stepwise adjustment
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (currentFPS < getLODDecreaseFPS()) {
|
||||
if (now > _decreaseFPSExpiry) {
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale *= ADJUST_LOD_DOWN_BY;
|
||||
_octreeSizeScale *= LOD_AUTO_ADJUST_DECREMENT_FACTOR;
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (_isDownshifting) {
|
||||
// subsequent downshift
|
||||
qCDebug(interfaceapp) << "adjusting LOD DOWN..."
|
||||
<< "average fps for last "<< DOWN_SHIFT_WINDOW_IN_SECS <<"seconds was "
|
||||
<< _fpsAverageDownWindow.getAverage()
|
||||
<< "minimum is:" << getLODDecreaseFPS()
|
||||
<< "elapsedSinceDownShift:" << elapsedSinceDownShift
|
||||
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
|
||||
} else {
|
||||
// first downshift
|
||||
qCDebug(interfaceapp) << "adjusting LOD DOWN after initial delay..."
|
||||
<< "average fps for last "<< START_DELAY_WINDOW_IN_SECS <<"seconds was "
|
||||
<< _fpsAverageStartWindow.getAverage()
|
||||
<< "minimum is:" << getLODDecreaseFPS()
|
||||
<< "elapsedSinceUpShift:" << elapsedSinceUpShift
|
||||
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
|
||||
}
|
||||
|
||||
_lastDownShift = now;
|
||||
_isDownshifting = true;
|
||||
|
||||
qCDebug(interfaceapp) << "adjusting LOD DOWN"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODDecreased();
|
||||
}
|
||||
} else {
|
||||
|
||||
// LOD Upward adjustment
|
||||
if (elapsedSinceUpShift > UP_SHIFT_ELPASED) {
|
||||
|
||||
if (_fpsAverageUpWindow.getAverage() > getLODIncreaseFPS()) {
|
||||
|
||||
// Octee items... stepwise adjustment
|
||||
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
} else {
|
||||
_octreeSizeScale *= ADJUST_LOD_UP_BY;
|
||||
}
|
||||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
qCDebug(interfaceapp) << "adjusting LOD UP... average fps for last "<< UP_SHIFT_WINDOW_IN_SECS <<"seconds was "
|
||||
<< _fpsAverageUpWindow.getAverage()
|
||||
<< "upshift point is:" << getLODIncreaseFPS()
|
||||
<< "elapsedSinceUpShift:" << elapsedSinceUpShift
|
||||
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
|
||||
|
||||
_lastUpShift = now;
|
||||
_isDownshifting = false;
|
||||
|
||||
emit LODIncreased();
|
||||
}
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
||||
if (lodToolsDialog) {
|
||||
lodToolsDialog->reloadSliders();
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else if (currentFPS > getLODIncreaseFPS()) {
|
||||
if (now > _increaseFPSExpiry) {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
} else {
|
||||
_octreeSizeScale *= LOD_AUTO_ADJUST_INCREMENT_FACTOR;
|
||||
}
|
||||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||
}
|
||||
qCDebug(interfaceapp) << "adjusting LOD UP"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODIncreased();
|
||||
}
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry;
|
||||
}
|
||||
|
||||
if (oldOctreeSizeScale != _octreeSizeScale) {
|
||||
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
||||
if (lodToolsDialog) {
|
||||
lodToolsDialog->reloadSliders();
|
||||
}
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to be at middle of target zone. It will drift close to its true value within
|
||||
// about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||
float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS());
|
||||
_avgRenderTime = MSECS_PER_SECOND / expectedFPS;
|
||||
}
|
||||
}
|
||||
|
||||
void LODManager::resetLODAdjust() {
|
||||
_fpsAverageStartWindow.reset();
|
||||
_fpsAverageDownWindow.reset();
|
||||
_fpsAverageUpWindow.reset();
|
||||
_lastUpShift = _lastDownShift = usecTimestampNow();
|
||||
_isDownshifting = false;
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
const float MIN_DECREASE_FPS = 0.5f;
|
||||
|
@ -206,7 +146,7 @@ float LODManager::getDesktopLODDecreaseFPS() const {
|
|||
}
|
||||
|
||||
float LODManager::getDesktopLODIncreaseFPS() const {
|
||||
return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_DESKTOP_FPS);
|
||||
return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS);
|
||||
}
|
||||
|
||||
void LODManager::setHMDLODDecreaseFPS(float fps) {
|
||||
|
@ -222,7 +162,7 @@ float LODManager::getHMDLODDecreaseFPS() const {
|
|||
}
|
||||
|
||||
float LODManager::getHMDLODIncreaseFPS() const {
|
||||
return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS);
|
||||
return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS);
|
||||
}
|
||||
|
||||
QString LODManager::getLODFeedbackText() {
|
||||
|
|
|
@ -19,29 +19,13 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <render/Args.h>
|
||||
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0;
|
||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0;
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0f;
|
||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 45.0f;
|
||||
const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_DESKTOP_LOD_DOWN_FPS; // msec
|
||||
const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec
|
||||
const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps
|
||||
const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
|
||||
const float INCREASE_LOD_GAP = 15.0f;
|
||||
|
||||
const float START_DELAY_WINDOW_IN_SECS = 3.0f; // wait at least this long after steady state/last upshift to consider downshifts
|
||||
const float DOWN_SHIFT_WINDOW_IN_SECS = 0.5f;
|
||||
const float UP_SHIFT_WINDOW_IN_SECS = 2.5f;
|
||||
|
||||
const int ASSUMED_FPS = 60;
|
||||
const quint64 START_SHIFT_ELPASED = USECS_PER_SECOND * START_DELAY_WINDOW_IN_SECS;
|
||||
const quint64 DOWN_SHIFT_ELPASED = USECS_PER_SECOND * DOWN_SHIFT_WINDOW_IN_SECS; // Consider adjusting LOD down after half a second
|
||||
const quint64 UP_SHIFT_ELPASED = USECS_PER_SECOND * UP_SHIFT_WINDOW_IN_SECS;
|
||||
|
||||
const int START_DELAY_SAMPLES_OF_FRAMES = ASSUMED_FPS * START_DELAY_WINDOW_IN_SECS;
|
||||
const int DOWN_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * DOWN_SHIFT_WINDOW_IN_SECS;
|
||||
const int UP_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * UP_SHIFT_WINDOW_IN_SECS;
|
||||
|
||||
const float ADJUST_LOD_DOWN_BY = 0.9f;
|
||||
const float ADJUST_LOD_UP_BY = 1.1f;
|
||||
const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // this is essentially, V-synch - 1 fps
|
||||
const float MAX_LIKELY_HMD_FPS = 74.0f; // this is essentially, V-synch - 1 fps
|
||||
const float INCREASE_LOD_GAP_FPS = 15.0f; // fps
|
||||
|
||||
// The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
|
||||
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
|
||||
|
@ -78,7 +62,7 @@ public:
|
|||
Q_INVOKABLE float getLODIncreaseFPS();
|
||||
|
||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||
void autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec);
|
||||
void autoAdjustLOD(float renderTime, float realTimeDelta);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
@ -92,21 +76,15 @@ private:
|
|||
LODManager();
|
||||
|
||||
bool _automaticLODAdjust = true;
|
||||
float _avgRenderTime { 0.0 };
|
||||
float _avgRenderTime { 0.0f };
|
||||
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
||||
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||
|
||||
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
|
||||
int _boundaryLevelAdjust = 0;
|
||||
|
||||
quint64 _lastDownShift = 0;
|
||||
quint64 _lastUpShift = 0;
|
||||
quint64 _lastStable = 0;
|
||||
bool _isDownshifting = false; // start out as if we're not downshifting
|
||||
|
||||
SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES;
|
||||
SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES;
|
||||
SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES;
|
||||
|
||||
uint64_t _decreaseFPSExpiry { 0 };
|
||||
uint64_t _increaseFPSExpiry { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_LODManager_h
|
||||
|
|
|
@ -101,7 +101,7 @@ Menu::Menu() {
|
|||
auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
static const QUrl widgetUrl("hifi/dialogs/RunningScripts.qml");
|
||||
static const QUrl tabletUrl("../../hifi/dialogs/TabletRunningScripts.qml");
|
||||
static const QUrl tabletUrl("hifi/dialogs/TabletRunningScripts.qml");
|
||||
static const QString name("RunningScripts");
|
||||
qApp->showDialog(widgetUrl, tabletUrl, name);
|
||||
});
|
||||
|
@ -338,7 +338,7 @@ Menu::Menu() {
|
|||
connect(action, &QAction::triggered, [] {
|
||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
tablet->loadQMLSource("ControllerSettings.qml");
|
||||
tablet->loadQMLSource("hifi/tablet/ControllerSettings.qml");
|
||||
|
||||
if (!hmd->getShouldShowTablet()) {
|
||||
hmd->toggleShouldShowTablet();
|
||||
|
@ -645,7 +645,8 @@ Menu::Menu() {
|
|||
// Developer > Timing >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
|
||||
qApp, SLOT(enablePerfStats(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
||||
|
|
|
@ -81,6 +81,7 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz
|
|||
const float MyAvatar::ZOOM_MIN = 0.5f;
|
||||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
const float MIN_SCALE_CHANGED_DELTA = 0.001f;
|
||||
|
||||
MyAvatar::MyAvatar(QThread* thread) :
|
||||
Avatar(thread),
|
||||
|
@ -670,6 +671,11 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition());
|
||||
_sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix);
|
||||
|
||||
bool hasSensorToWorldScaleChanged = false;
|
||||
if (fabsf(getSensorToWorldScale() - sensorToWorldScale) > MIN_SCALE_CHANGED_DELTA) {
|
||||
hasSensorToWorldScaleChanged = true;
|
||||
}
|
||||
|
||||
lateUpdatePalms();
|
||||
|
||||
if (_enableDebugDrawSensorToWorldMatrix) {
|
||||
|
@ -678,9 +684,13 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
}
|
||||
|
||||
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
|
||||
|
||||
updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache);
|
||||
updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
|
||||
|
||||
if (hasSensorToWorldScaleChanged) {
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
|
@ -1405,6 +1415,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene());
|
||||
_headBoneSet.clear();
|
||||
emit skeletonChanged();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1440,6 +1451,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
|
|||
UserActivityLogger::getInstance().changedModel("skeleton", urlString);
|
||||
}
|
||||
markIdentityDataChanged();
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
#include "MySkeletonModel.h"
|
||||
|
||||
#include <avatars-renderer/Avatar.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
|
||||
MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) {
|
||||
}
|
||||
|
@ -30,6 +33,39 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle
|
|||
};
|
||||
}
|
||||
|
||||
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
||||
glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor();
|
||||
glm::vec3 hipsPos = extractTranslation(hipsMat);
|
||||
glm::quat hipsRot = glmExtractRotation(hipsMat);
|
||||
|
||||
glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix();
|
||||
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat;
|
||||
|
||||
// dampen hips rotation, by mixing it with the avatar orientation in sensor space
|
||||
const float MIX_RATIO = 0.5f;
|
||||
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
|
||||
|
||||
if (isFlying) {
|
||||
// rotate the hips back to match the flying animation.
|
||||
|
||||
const float TILT_ANGLE = 0.523f;
|
||||
const glm::quat tiltRot = glm::angleAxis(TILT_ANGLE, transformVectorFast(avatarToSensorMat, -Vectors::UNIT_X));
|
||||
|
||||
glm::vec3 headPos;
|
||||
int headIndex = myAvatar->getJointIndex("Head");
|
||||
if (headIndex != -1) {
|
||||
headPos = transformPoint(avatarToSensorMat, myAvatar->getAbsoluteJointTranslationInObjectFrame(headIndex));
|
||||
} else {
|
||||
headPos = transformPoint(myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition());
|
||||
}
|
||||
hipsRot = tiltRot * hipsRot;
|
||||
hipsPos = headPos + tiltRot * (hipsPos - headPos);
|
||||
}
|
||||
|
||||
return AnimPose(hipsRot * Quaternions::Y_180, hipsPos);
|
||||
}
|
||||
|
||||
// Called within Model::simulate call, below.
|
||||
void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
|
@ -124,6 +160,39 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
// if hips are not under direct control, estimate the hips position.
|
||||
if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) {
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
|
||||
if (!_prevHipsValid) {
|
||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
||||
_prevHips = hips;
|
||||
}
|
||||
|
||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
||||
|
||||
// smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation.
|
||||
const float ROT_ALPHA = 0.9f;
|
||||
const float TRANS_HORIZ_ALPHA = 0.9f;
|
||||
const float TRANS_VERT_ALPHA = 0.1f;
|
||||
float hipsY = hips.trans().y;
|
||||
hips.trans() = lerp(hips.trans(), _prevHips.trans(), TRANS_HORIZ_ALPHA);
|
||||
hips.trans().y = lerp(hipsY, _prevHips.trans().y, TRANS_VERT_ALPHA);
|
||||
hips.rot() = safeLerp(hips.rot(), _prevHips.rot(), ROT_ALPHA);
|
||||
|
||||
_prevHips = hips;
|
||||
_prevHipsValid = true;
|
||||
|
||||
glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180);
|
||||
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
|
||||
|
||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips;
|
||||
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true;
|
||||
|
||||
} else {
|
||||
_prevHipsValid = false;
|
||||
}
|
||||
|
||||
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
||||
|
||||
// pass detailed torso k-dops to rig.
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
|
||||
private:
|
||||
void updateFingers();
|
||||
|
||||
AnimPose _prevHips; // sensor frame
|
||||
bool _prevHipsValid { false };
|
||||
};
|
||||
|
||||
#endif // hifi_MySkeletonModel_h
|
||||
|
|
|
@ -130,7 +130,7 @@ QString amountString(const QString& label, const QString&color, const QJsonValue
|
|||
return result + QString("</font>");
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
void Ledger::historySuccess(QNetworkReply& reply) {
|
||||
// here we send a historyResult with some extra stuff in it
|
||||
// Namely, the styled text we'd like to show. The issue is the
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
#include "Wallet.h"
|
||||
#include <AccountManager.h>
|
||||
|
||||
HIFI_QML_DEF(QmlCommerce)
|
||||
|
||||
QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
||||
QmlCommerce::QmlCommerce() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
connect(ledger.data(), &Ledger::buyResult, this, &QmlCommerce::buyResult);
|
||||
|
|
|
@ -16,16 +16,14 @@
|
|||
#define hifi_QmlCommerce_h
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
class QmlCommerce : public OffscreenQmlDialog {
|
||||
class QmlCommerce : public QObject {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
public:
|
||||
QmlCommerce(QQuickItem* parent = nullptr);
|
||||
QmlCommerce();
|
||||
|
||||
signals:
|
||||
void walletStatusResult(uint walletStatus);
|
||||
|
|
|
@ -315,7 +315,6 @@ Wallet::Wallet() {
|
|||
}
|
||||
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
emit walletStatusResult(status);
|
||||
});
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -491,6 +490,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
}
|
||||
if (_publicKeys.count() > 0) {
|
||||
// we _must_ be authenticated if the publicKeys are there
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -503,6 +503,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -801,15 +802,12 @@ void Wallet::account() {
|
|||
|
||||
void Wallet::getWalletStatus() {
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint status;
|
||||
|
||||
if (DependencyManager::get<AccountManager>()->isLoggedIn()) {
|
||||
// This will set account info for the wallet, allowing us to decrypt and display the security image.
|
||||
account();
|
||||
} else {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN;
|
||||
emit walletStatusResult(status);
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
walletScriptingInterface->setWalletStatus((uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ QNetworkRequest createNetworkRequest() {
|
|||
|
||||
QNetworkRequest request;
|
||||
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath(USER_ACTIVITY_URL);
|
||||
|
||||
request.setUrl(requestURL);
|
||||
|
|
|
@ -23,10 +23,10 @@ static const float WEB_STYLUS_LENGTH = 0.2f;
|
|||
static const float TABLET_MIN_HOVER_DISTANCE = -0.1f;
|
||||
static const float TABLET_MAX_HOVER_DISTANCE = 0.1f;
|
||||
static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f;
|
||||
static const float TABLET_MAX_TOUCH_DISTANCE = 0.01f;
|
||||
static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f;
|
||||
|
||||
static const float HOVER_HYSTERESIS = 0.01f;
|
||||
static const float TOUCH_HYSTERESIS = 0.02f;
|
||||
static const float TOUCH_HYSTERESIS = 0.001f;
|
||||
|
||||
static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND;
|
||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f;
|
||||
|
|
|
@ -34,7 +34,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const
|
|||
return retVal;
|
||||
}
|
||||
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||
bool retVal;
|
||||
BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
|
@ -42,7 +42,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
|
|||
Q_ARG(float, x),
|
||||
Q_ARG(float, y),
|
||||
Q_ARG(float, z),
|
||||
Q_ARG(float, s));
|
||||
Q_ARG(float, scale));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <EntityItemID.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Clipboard API enables you to export and import entities to and from JSON files.
|
||||
*
|
||||
* @namespace Clipboard
|
||||
*/
|
||||
class ClipboardScriptingInterface : public QObject {
|
||||
|
@ -25,48 +27,56 @@ class ClipboardScriptingInterface : public QObject {
|
|||
public:
|
||||
ClipboardScriptingInterface();
|
||||
|
||||
signals:
|
||||
void readyToImport();
|
||||
|
||||
public:
|
||||
/**jsdoc
|
||||
* Compute the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getContentsDimensions
|
||||
* @return {Vec3} The extents of the contents held in the clipboard.
|
||||
* @returns {Vec3} The extents of the contents held in the clipboard.
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getContentsDimensions();
|
||||
|
||||
/**jsdoc
|
||||
* Compute largest dimension of the extents of the contents held in the clipboard
|
||||
* Compute the largest dimension of the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getClipboardContentsLargestDimension
|
||||
* @return {float} The largest dimension computed.
|
||||
* @returns {number} The largest dimension computed.
|
||||
*/
|
||||
Q_INVOKABLE float getClipboardContentsLargestDimension();
|
||||
|
||||
/**jsdoc
|
||||
* Import entities from a .json file containing entity data into the clipboard.
|
||||
* You can generate * a .json file using {Clipboard.exportEntities}.
|
||||
* Import entities from a JSON file containing entity data into the clipboard.
|
||||
* You can generate a JSON file using {@link Clipboard.exportEntities}.
|
||||
* @function Clipboard.importEntities
|
||||
* @param {string} filename Filename of file to import.
|
||||
* @return {bool} True if the import was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of file to import.
|
||||
* @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool importEntities(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities listed in `entityIDs` to the file `filename`
|
||||
* Export the entities specified to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path to the file to export entities to.
|
||||
* @param {EntityID[]} entityIDs IDs of entities to export.
|
||||
* @return {bool} True if the export was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {Uuid[]} entityIDs Array of IDs of the entities to export.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities with centers within a cube to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {number} x X-coordinate of the cube center.
|
||||
* @param {number} y Y-coordinate of the cube center.
|
||||
* @param {number} z Z-coordinate of the cube center.
|
||||
* @param {number} scale Half dimension of the cube.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
|
||||
/**jsdoc
|
||||
* Paste the contents of the clipboard into the world.
|
||||
* @function Clipboard.pasteEntities
|
||||
* @param {Vec3} position Position to paste clipboard at.
|
||||
* @return {EntityID[]} Array of entity IDs for the new entities that were
|
||||
* created as a result of the paste operation.
|
||||
* @param {Vec3} position Position to paste the clipboard contents at.
|
||||
* @returns {Uuid[]} Array of entity IDs for the new entities that were created as a result of the paste operation.
|
||||
*/
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
};
|
||||
|
|