fix merge conflict
7
.gitignore
vendored
|
@ -18,9 +18,12 @@ local.properties
|
|||
android/gradle*
|
||||
android/.gradle
|
||||
android/app/src/main/jniLibs
|
||||
android/app/libs
|
||||
android/app/src/main/res/values/libs.xml
|
||||
android/app/src/main/assets/bundled
|
||||
|
||||
# VSCode
|
||||
# List taken from Github Global Ignores master@435c4d92
|
||||
# List taken from Github Global Ignores master@435c4d92
|
||||
# https://github.com/github/gitignore/commits/master/Global/VisualStudioCode.gitignore
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
|
@ -66,7 +69,7 @@ gvr-interface/libs/*
|
|||
# ignore files for various dev environments
|
||||
TAGS
|
||||
*.sw[po]
|
||||
*.qmlc
|
||||
*.jsc
|
||||
|
||||
# ignore QML compilation output
|
||||
*.qmlc
|
||||
|
|
|
@ -2,9 +2,9 @@ Please read the [general build guide](BUILD.md) for information on building othe
|
|||
|
||||
# Dependencies
|
||||
|
||||
*Currently Android building is only supported on 64 bit Linux host environments*
|
||||
Building is currently supported on OSX, Windows and Linux platforms, but developers intending to do work on the library dependencies are strongly urged to use 64 bit Linux as a build platform
|
||||
|
||||
You will need the following tools to build our Android targets.
|
||||
You will need the following tools to build Android targets.
|
||||
|
||||
* [Gradle](https://gradle.org/install/)
|
||||
* [Android Studio](https://developer.android.com/studio/index.html)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# this guide is specific to Ubuntu 16.04.
|
||||
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
## 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
|
||||
|
@ -8,20 +10,27 @@ 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:
|
||||
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:
|
||||
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).
|
||||
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
|
||||
```
|
||||
|
|
|
@ -39,6 +39,19 @@ else()
|
|||
option(BUILD_TESTS "Build tests" ON)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
set(PLATFORM_GL_BACKEND gpu-gles)
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl)
|
||||
endif ()
|
||||
|
||||
foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
|
||||
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
|
||||
endforeach()
|
||||
|
||||
|
||||
option(BUILD_INSTALLER "Build installer" ON)
|
||||
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
set(TARGET_NAME native-lib)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking gl gpu gpu-gles image fbx render-utils physics)
|
||||
setup_hifi_library(Gui Qml Quick)
|
||||
|
||||
# Minimal dependencies for testing UI compositing
|
||||
#link_hifi_libraries(shared networking gl ui)
|
||||
|
||||
link_hifi_libraries(
|
||||
shared networking octree
|
||||
script-engine recording trackers
|
||||
gl ktx image gpu gpu-gles render render-utils
|
||||
physics
|
||||
audio audio-client
|
||||
ui midi controllers pointers
|
||||
graphics model-networking fbx animation
|
||||
entities entities-renderer
|
||||
avatars avatars-renderer
|
||||
ui-plugins input-plugins
|
||||
# display-plugins
|
||||
# auto-updater
|
||||
)
|
||||
|
||||
|
||||
target_link_libraries(native-lib android log m)
|
||||
|
||||
target_opengl()
|
||||
target_googlevr()
|
||||
|
||||
target_bullet()
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
ext.RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
|
||||
ext.RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
|
||||
ext.BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
defaultConfig {
|
||||
applicationId "org.saintandreas.testapp"
|
||||
applicationId "com.highfidelity.iface"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
|
@ -56,8 +53,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: "${project.rootDir}/libraries/jar", include: 'QtAndroid-bundled.jar')
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.google.vr:sdk-audio:1.80.0'
|
||||
compile 'com.google.vr:sdk-base:1.80.0'
|
||||
implementation 'com.google.vr:sdk-audio:1.80.0'
|
||||
implementation 'com.google.vr:sdk-base:1.80.0'
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.saintandreas.testapp">
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.gvrinterface">
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
|
||||
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
|
||||
|
||||
<application
|
||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:allowBackup="true"
|
||||
android:screenOrientation="unspecified"
|
||||
android:theme="@style/NoSystemUI"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:launchMode="singleTop"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".InterfaceActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:resizeableActivity="false">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="native-lib"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
19
android/app/src/main/cpp/+android/simple.qml
Normal file
|
@ -0,0 +1,19 @@
|
|||
import QtQuick 2.7
|
||||
import QtWebView 1.1
|
||||
|
||||
Rectangle {
|
||||
id: window
|
||||
anchors.fill: parent
|
||||
color: "red"
|
||||
ColorAnimation on color { from: "blue"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
|
||||
|
||||
Text {
|
||||
text: "Hello"
|
||||
anchors.top: parent.top
|
||||
}
|
||||
WebView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
url: "http://doc.qt.io/qt-5/qml-qtwebview-webview.html"
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <vr/gvr/capi/include/gvr.h>
|
||||
|
||||
namespace googlevr {
|
||||
|
||||
// Convert a GVR matrix to GLM matrix
|
||||
glm::mat4 toGlm(const gvr::Mat4f &matrix) {
|
||||
glm::mat4 result;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
result[j][i] = matrix.m[i][j];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a field of view in degrees, compute the corresponding projection
|
||||
// matrix.
|
||||
glm::mat4 perspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) {
|
||||
const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near;
|
||||
const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near;
|
||||
const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near;
|
||||
const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near;
|
||||
const float Y = (2 * z_near) / (y_top - y_bottom);
|
||||
const float A = (x_right + x_left) / (x_right - x_left);
|
||||
const float B = (y_top + y_bottom) / (y_top - y_bottom);
|
||||
const float C = (z_near + z_far) / (z_near - z_far);
|
||||
const float D = (2 * z_near * z_far) / (z_near - z_far);
|
||||
|
||||
glm::mat4 result { 0 };
|
||||
result[2][0] = A;
|
||||
result[1][1] = Y;
|
||||
result[2][1] = B;
|
||||
result[2][2] = C;
|
||||
result[3][2] = D;
|
||||
result[2][3] = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::quat toGlm(const gvr::ControllerQuat& q) {
|
||||
glm::quat result;
|
||||
result.w = q.qw;
|
||||
result.x = q.qx;
|
||||
result.y = q.qy;
|
||||
result.z = q.qz;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
153
android/app/src/main/cpp/main.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQml/QQmlFileSelector>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtWebView/QtWebView>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/GLWindow.h>
|
||||
|
||||
#include <ui/OffscreenQmlSurface.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gl")
|
||||
|
||||
bool checkGLError(const char* name) {
|
||||
GLenum error = glGetError();
|
||||
if (!error) {
|
||||
return false;
|
||||
} else {
|
||||
switch (error) {
|
||||
case GL_INVALID_ENUM:
|
||||
qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
|
||||
break;
|
||||
default:
|
||||
qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkGLErrorDebug(const char* name) {
|
||||
return checkGLError(name);
|
||||
}
|
||||
|
||||
|
||||
int QtMsgTypeToAndroidPriority(QtMsgType type) {
|
||||
int priority = ANDROID_LOG_UNKNOWN;
|
||||
switch (type) {
|
||||
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
|
||||
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
|
||||
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
|
||||
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
|
||||
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
|
||||
default: break;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
|
||||
}
|
||||
|
||||
void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
qInstallMessageHandler(messageHandler);
|
||||
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
||||
QGuiApplication app(argc,argv);
|
||||
app.setOrganizationName("QtProject");
|
||||
app.setOrganizationDomain("qt-project.org");
|
||||
app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
|
||||
QtWebView::initialize();
|
||||
qputenv("QSG_RENDERER_DEBUG", (QStringList() << "render" << "build" << "change" << "upload" << "roots" << "dump").join(';').toUtf8());
|
||||
|
||||
OffscreenGLCanvas sharedCanvas;
|
||||
if (!sharedCanvas.create()) {
|
||||
qFatal("Unable to create primary offscreen context");
|
||||
}
|
||||
qt_gl_set_global_share_context(sharedCanvas.getContext());
|
||||
auto globalContext = QOpenGLContext::globalShareContext();
|
||||
|
||||
GLWindow window;
|
||||
window.create();
|
||||
window.setGeometry(qApp->primaryScreen()->availableGeometry());
|
||||
window.createContext(globalContext);
|
||||
if (!window.makeCurrent()) {
|
||||
qFatal("Unable to make primary window GL context current");
|
||||
}
|
||||
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
|
||||
static const ivec2 offscreenSize { 640, 480 };
|
||||
|
||||
OffscreenQmlSurface::setSharedContext(sharedCanvas.getContext());
|
||||
OffscreenQmlSurface* qmlSurface = new OffscreenQmlSurface();
|
||||
qmlSurface->create();
|
||||
qmlSurface->resize(fromGlm(offscreenSize));
|
||||
qmlSurface->load("qrc:///simple.qml");
|
||||
qmlSurface->resume();
|
||||
|
||||
auto discardLambda = qmlSurface->getDiscardLambda();
|
||||
|
||||
window.showFullScreen();
|
||||
QTimer timer;
|
||||
timer.setInterval(10);
|
||||
timer.setSingleShot(false);
|
||||
OffscreenQmlSurface::TextureAndFence currentTextureAndFence;
|
||||
timer.connect(&timer, &QTimer::timeout, &app, [&]{
|
||||
window.makeCurrent();
|
||||
glClearColor(0, 1, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
if (qmlSurface->fetchTexture(newTextureAndFence)) {
|
||||
if (currentTextureAndFence.first) {
|
||||
discardLambda(currentTextureAndFence.first, currentTextureAndFence.second);
|
||||
}
|
||||
currentTextureAndFence = newTextureAndFence;
|
||||
}
|
||||
checkGLErrorDebug(__FUNCTION__);
|
||||
|
||||
if (currentTextureAndFence.second) {
|
||||
glWaitSync((GLsync)currentTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)currentTextureAndFence.second);
|
||||
currentTextureAndFence.second = nullptr;
|
||||
}
|
||||
|
||||
if (currentTextureAndFence.first) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, currentTextureAndFence.first, 0);
|
||||
glBlitFramebuffer(0, 0, offscreenSize.x, offscreenSize.y, 100, 100, offscreenSize.x + 100, offscreenSize.y + 100, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
window.swapBuffers();
|
||||
window.doneCurrent();
|
||||
});
|
||||
timer.start();
|
||||
return app.exec();
|
||||
}
|
6
android/app/src/main/cpp/main.qrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>simple.qml</file>
|
||||
<file>+android/simple.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -1,73 +0,0 @@
|
|||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
int QtMsgTypeToAndroidPriority(QtMsgType type) {
|
||||
int priority = ANDROID_LOG_UNKNOWN;
|
||||
switch (type) {
|
||||
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
|
||||
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
|
||||
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
|
||||
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
|
||||
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
|
||||
default: break;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
|
||||
}
|
||||
|
||||
static jlong toJni(NativeRenderer *renderer) {
|
||||
return reinterpret_cast<intptr_t>(renderer);
|
||||
}
|
||||
|
||||
static NativeRenderer *fromJni(jlong renderer) {
|
||||
return reinterpret_cast<NativeRenderer*>(renderer);
|
||||
}
|
||||
|
||||
#define JNI_METHOD(r, name) JNIEXPORT r JNICALL Java_org_saintandreas_testapp_MainActivity_##name
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNI_METHOD(jlong, nativeCreateRenderer)
|
||||
(JNIEnv *env, jclass clazz, jobject class_loader, jobject android_context, jlong native_gvr_api) {
|
||||
qInstallMessageHandler(messageHandler);
|
||||
return toJni(new NativeRenderer());
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeDestroyRenderer)
|
||||
(JNIEnv *env, jclass clazz, jlong renderer) {
|
||||
delete fromJni(renderer);
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeInitializeGl)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->InitializeGl();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeDrawFrame)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->DrawFrame();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnTriggerEvent)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnTriggerEvent();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnPause)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnPause();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnResume)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnResume();
|
||||
}
|
||||
|
||||
} // extern "C"
|
|
@ -1,120 +0,0 @@
|
|||
#include "renderer.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLShaders.h>
|
||||
|
||||
static const char *kSimepleVertexShader = R"glsl(#version 300 es
|
||||
#extension GL_OVR_multiview2 : enable
|
||||
|
||||
layout(num_views=2) in;
|
||||
|
||||
layout(location = 0) in vec4 a_Position;
|
||||
|
||||
out vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
v_Color = vec4(a_Position.xyz, 1.0);
|
||||
gl_Position = vec4(a_Position.xyz, 1.0);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
static const char *kPassthroughFragmentShader = R"glsl(#version 300 es
|
||||
precision mediump float;
|
||||
in vec4 v_Color;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() { FragColor = v_Color; }
|
||||
)glsl";
|
||||
|
||||
|
||||
int LoadGLShader(int type, const char *shadercode) {
|
||||
GLuint result = 0;
|
||||
std::string shaderError;
|
||||
static const std::string SHADER_DEFINES;
|
||||
if (!gl::compileShader(type, shadercode, SHADER_DEFINES, result, shaderError)) {
|
||||
qWarning() << "QQQ" << __FUNCTION__ << "Shader compile failure" << shaderError.c_str();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void CheckGLError(const char* label) {
|
||||
int gl_error = glGetError();
|
||||
if (gl_error != GL_NO_ERROR) {
|
||||
qWarning("GL error @ %s: %d", label, gl_error);
|
||||
// Crash immediately to make OpenGL errors obvious.
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Contains vertex, normal and other data.
|
||||
namespace triangle {
|
||||
static std::array<float, 9> TRIANGLE_VERTS {{
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f,
|
||||
0.0f, 0.5f, 0.0f
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
void NativeRenderer::InitializeGl() {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kSimepleVertexShader);
|
||||
const uint32_t fragShader = LoadGLShader(GL_FRAGMENT_SHADER, kPassthroughFragmentShader);
|
||||
std::string error;
|
||||
_program = gl::compileProgram({ vertShader, fragShader }, error);
|
||||
CheckGLError("build program");
|
||||
|
||||
glGenBuffers(1, &_geometryBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _geometryBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, triangle::TRIANGLE_VERTS.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
CheckGLError("upload vertices");
|
||||
|
||||
glGenVertexArrays(1, &_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _geometryBuffer);
|
||||
glBindVertexArray(_vao);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
CheckGLError("build vao ");
|
||||
}
|
||||
|
||||
|
||||
void NativeRenderer::DrawFrame() {
|
||||
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start);
|
||||
glm::vec3 v;
|
||||
v.r = (float) (now.count() % 1000) / 1000.0f;
|
||||
v.g = 1.0f - v.r;
|
||||
v.b = 1.0f;
|
||||
|
||||
glClearColor(v.r, v.g, v.b, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(_program);
|
||||
glBindVertexArray(_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void NativeRenderer::OnTriggerEvent() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
}
|
||||
|
||||
void NativeRenderer::OnPause() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
}
|
||||
|
||||
void NativeRenderer::OnResume() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class NativeRenderer {
|
||||
public:
|
||||
void InitializeGl();
|
||||
void DrawFrame();
|
||||
void OnTriggerEvent();
|
||||
void OnPause();
|
||||
void OnResume();
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::system_clock> start { std::chrono::system_clock::now() };
|
||||
|
||||
uint32_t _geometryBuffer { 0 };
|
||||
uint32_t _vao { 0 };
|
||||
uint32_t _program { 0 };
|
||||
};
|
10
android/app/src/main/cpp/simple.qml
Normal file
|
@ -0,0 +1,10 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Rectangle {
|
||||
id: window
|
||||
width: 320
|
||||
height: 480
|
||||
focus: true
|
||||
color: "red"
|
||||
ColorAnimation on color { from: "red"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// InterfaceActivity.java
|
||||
// gvr-interface/java
|
||||
//
|
||||
// Created by Stephen Birarda on 1/26/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
package io.highfidelity.gvrinterface;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.util.Log;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
||||
public class InterfaceActivity extends QtActivity {
|
||||
|
||||
public static native void handleHifiURL(String hifiURLString);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
// Get the intent that started this activity in case we have a hifi:// URL to parse
|
||||
Intent intent = getIntent();
|
||||
if (intent.getAction() == Intent.ACTION_VIEW) {
|
||||
Uri data = intent.getData();
|
||||
|
||||
if (data.getScheme().equals("hifi")) {
|
||||
handleHifiURL(data.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,951 @@
|
|||
/*
|
||||
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
|
||||
Contact: http://www.qt.io/licensing/
|
||||
|
||||
Commercial License Usage
|
||||
Licensees holding valid commercial Qt licenses may use this file in
|
||||
accordance with the commercial license agreement provided with the
|
||||
Software or, alternatively, in accordance with the terms contained in
|
||||
a written agreement between you and The Qt Company. For licensing terms
|
||||
and conditions see http://www.qt.io/terms-conditions. For further
|
||||
information use the contact form at http://www.qt.io/contact-us.
|
||||
|
||||
BSD License Usage
|
||||
Alternatively, this file may be used under the BSD license as follows:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.qtproject.qt5.android.bindings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ActionMode;
|
||||
import android.view.ActionMode.Callback;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class QtActivity extends Activity {
|
||||
public String APPLICATION_PARAMETERS = null;
|
||||
public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
|
||||
public final String[] QT_ANDROID_THEMES = new String[]{"Theme_Holo_Light"};
|
||||
public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme.
|
||||
private QtActivityLoader m_loader = new QtActivityLoader(this);
|
||||
|
||||
public QtActivity() {
|
||||
}
|
||||
|
||||
/////////////////////////// forward all notifications ////////////////////////////
|
||||
/////////////////////////// Super class calls ////////////////////////////////////
|
||||
/////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE //////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event);
|
||||
} else {
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchKeyEvent(KeyEvent event) {
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event);
|
||||
} else {
|
||||
return super.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
return super_dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev);
|
||||
} else {
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchTouchEvent(MotionEvent event) {
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean dispatchTrackballEvent(MotionEvent ev) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev);
|
||||
} else {
|
||||
return super.dispatchTrackballEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchTrackballEvent(MotionEvent event) {
|
||||
return super.dispatchTrackballEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) {
|
||||
QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data);
|
||||
return;
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
public void super_onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
|
||||
if (!QtApplication.invokeDelegate(theme, resid, first).invoked) {
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onApplyThemeResource(Theme theme, int resid, boolean first) {
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@Override
|
||||
protected void onChildTitleChanged(Activity childActivity, CharSequence title) {
|
||||
if (!QtApplication.invokeDelegate(childActivity, title).invoked) {
|
||||
super.onChildTitleChanged(childActivity, title);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onChildTitleChanged(Activity childActivity, CharSequence title) {
|
||||
super.onChildTitleChanged(childActivity, title);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (!QtApplication.invokeDelegate(newConfig).invoked) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onContentChanged() {
|
||||
super.onContentChanged();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onContextItemSelected(MenuItem item) {
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onContextMenuClosed(Menu menu) {
|
||||
if (!QtApplication.invokeDelegate(menu).invoked) {
|
||||
super.onContextMenuClosed(menu);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onContextMenuClosed(Menu menu) {
|
||||
super.onContextMenuClosed(menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
protected void onCreateHook(Bundle savedInstanceState) {
|
||||
m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS;
|
||||
m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES;
|
||||
m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES;
|
||||
m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME;
|
||||
m_loader.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
onCreateHook(savedInstanceState);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public CharSequence onCreateDescription() {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
|
||||
if (res.invoked) {
|
||||
return (CharSequence) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateDescription();
|
||||
}
|
||||
}
|
||||
|
||||
public CharSequence super_onCreateDescription() {
|
||||
return super.onCreateDescription();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(id);
|
||||
if (res.invoked) {
|
||||
return (Dialog) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Dialog super_onCreateDialog(int id) {
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onCreateOptionsMenu(Menu menu) {
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onCreatePanelMenu(int featureId, Menu menu) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreatePanelMenu(featureId, menu);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onCreatePanelMenu(int featureId, Menu menu) {
|
||||
return super.onCreatePanelMenu(featureId, menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreatePanelView(int featureId) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId);
|
||||
if (res.invoked) {
|
||||
return (View) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreatePanelView(featureId);
|
||||
}
|
||||
}
|
||||
|
||||
public View super_onCreatePanelView(int featureId) {
|
||||
return super.onCreatePanelView(featureId);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateThumbnail(outBitmap, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
|
||||
return super.onCreateThumbnail(outBitmap, canvas);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public View onCreateView(String name, Context context, AttributeSet attrs) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs);
|
||||
if (res.invoked) {
|
||||
return (View) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateView(name, context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public View super_onCreateView(String name, Context context, AttributeSet attrs) {
|
||||
return super.onCreateView(name, context, attrs);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event);
|
||||
} else {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onKeyDown(int keyCode, KeyEvent event) {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event);
|
||||
} else {
|
||||
return super.onKeyMultiple(keyCode, repeatCount, event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
|
||||
return super.onKeyMultiple(keyCode, repeatCount, event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onKeyUp != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event);
|
||||
} else {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onKeyUp(int keyCode, KeyEvent event) {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onLowMemory();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onMenuItemSelected(int featureId, MenuItem item) {
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onMenuOpened(int featureId, Menu menu) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onMenuOpened(featureId, menu);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onMenuOpened(int featureId, Menu menu) {
|
||||
return super.onMenuOpened(featureId, menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
if (!QtApplication.invokeDelegate(intent).invoked) {
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(item);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onOptionsMenuClosed(Menu menu) {
|
||||
if (!QtApplication.invokeDelegate(menu).invoked) {
|
||||
super.onOptionsMenuClosed(menu);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onOptionsMenuClosed(Menu menu) {
|
||||
super.onOptionsMenuClosed(menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onPanelClosed(int featureId, Menu menu) {
|
||||
if (!QtApplication.invokeDelegate(featureId, menu).invoked) {
|
||||
super.onPanelClosed(featureId, menu);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onPanelClosed(int featureId, Menu menu) {
|
||||
super.onPanelClosed(featureId, menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
QtApplication.invokeDelegate(savedInstanceState);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
||||
if (!QtApplication.invokeDelegate(id, dialog).invoked) {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onPrepareDialog(int id, Dialog dialog) {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onPrepareOptionsMenu(Menu menu) {
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onPreparePanel(int featureId, View view, Menu menu) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu);
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onPreparePanel(featureId, view, menu);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onPreparePanel(int featureId, View view, Menu menu) {
|
||||
return super.onPreparePanel(featureId, view, menu);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
if (!QtApplication.invokeDelegate(savedInstanceState).invoked) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
|
||||
if (res.invoked) {
|
||||
return res.methodReturns;
|
||||
} else {
|
||||
return super.onRetainNonConfigurationInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public Object super_onRetainNonConfigurationInstance() {
|
||||
return super.onRetainNonConfigurationInstance();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
if (!QtApplication.invokeDelegate(outState).invoked) {
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested() {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate();
|
||||
if (res.invoked) {
|
||||
return (Boolean) res.methodReturns;
|
||||
} else {
|
||||
return super.onSearchRequested();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onSearchRequested() {
|
||||
return super.onSearchRequested();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
if (!QtApplication.invokeDelegate(title, color).invoked) {
|
||||
super.onTitleChanged(title, color);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event);
|
||||
} else {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onTouchEvent(MotionEvent event) {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event);
|
||||
} else {
|
||||
return super.onTrackballEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onTrackballEvent(MotionEvent event) {
|
||||
return super.onTrackballEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onUserInteraction() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onUserInteraction();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onUserInteraction() {
|
||||
super.onUserInteraction();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onUserLeaveHint();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onUserLeaveHint() {
|
||||
super.onUserLeaveHint();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onWindowAttributesChanged(LayoutParams params) {
|
||||
if (!QtApplication.invokeDelegate(params).invoked) {
|
||||
super.onWindowAttributesChanged(params);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onWindowAttributesChanged(LayoutParams params) {
|
||||
super.onWindowAttributesChanged(params);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
if (!QtApplication.invokeDelegate(hasFocus).invoked) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//////////////// Activity API 5 /////////////
|
||||
//@ANDROID-5
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
if (!QtApplication.invokeDelegate().invoked) {
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event);
|
||||
} else {
|
||||
return super.onKeyLongPress(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return super.onKeyLongPress(keyCode, event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
//@ANDROID-5
|
||||
|
||||
//////////////// Activity API 8 /////////////
|
||||
//@ANDROID-8
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id, Bundle args) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args);
|
||||
if (res.invoked) {
|
||||
return (Dialog) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateDialog(id, args);
|
||||
}
|
||||
}
|
||||
|
||||
public Dialog super_onCreateDialog(int id, Bundle args) {
|
||||
return super.onCreateDialog(id, args);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
|
||||
if (!QtApplication.invokeDelegate(id, dialog, args).invoked) {
|
||||
super.onPrepareDialog(id, dialog, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onPrepareDialog(int id, Dialog dialog, Bundle args) {
|
||||
super.onPrepareDialog(id, dialog, args);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
//@ANDROID-8
|
||||
//////////////// Activity API 11 /////////////
|
||||
|
||||
//@ANDROID-11
|
||||
@Override
|
||||
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event);
|
||||
} else {
|
||||
return super.dispatchKeyShortcutEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchKeyShortcutEvent(KeyEvent event) {
|
||||
return super.dispatchKeyShortcutEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onActionModeFinished(ActionMode mode) {
|
||||
if (!QtApplication.invokeDelegate(mode).invoked) {
|
||||
super.onActionModeFinished(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onActionModeFinished(ActionMode mode) {
|
||||
super.onActionModeFinished(mode);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onActionModeStarted(ActionMode mode) {
|
||||
if (!QtApplication.invokeDelegate(mode).invoked) {
|
||||
super.onActionModeStarted(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onActionModeStarted(ActionMode mode) {
|
||||
super.onActionModeStarted(mode);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void onAttachFragment(Fragment fragment) {
|
||||
if (!QtApplication.invokeDelegate(fragment).invoked) {
|
||||
super.onAttachFragment(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
public void super_onAttachFragment(Fragment fragment) {
|
||||
super.onAttachFragment(fragment);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs);
|
||||
if (res.invoked) {
|
||||
return (View) res.methodReturns;
|
||||
} else {
|
||||
return super.onCreateView(parent, name, context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public View super_onCreateView(View parent, String name, Context context,
|
||||
AttributeSet attrs) {
|
||||
return super.onCreateView(parent, name, context, attrs);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode, event);
|
||||
} else {
|
||||
return super.onKeyShortcut(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onKeyShortcut(int keyCode, KeyEvent event) {
|
||||
return super.onKeyShortcut(keyCode, event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ActionMode onWindowStartingActionMode(Callback callback) {
|
||||
QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback);
|
||||
if (res.invoked) {
|
||||
return (ActionMode) res.methodReturns;
|
||||
} else {
|
||||
return super.onWindowStartingActionMode(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public ActionMode super_onWindowStartingActionMode(Callback callback) {
|
||||
return super.onWindowStartingActionMode(callback);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
//@ANDROID-11
|
||||
//////////////// Activity API 12 /////////////
|
||||
|
||||
//@ANDROID-12
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev);
|
||||
} else {
|
||||
return super.dispatchGenericMotionEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_dispatchGenericMotionEvent(MotionEvent event) {
|
||||
return super.dispatchGenericMotionEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null) {
|
||||
return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event);
|
||||
} else {
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean super_onGenericMotionEvent(MotionEvent event) {
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
//@ANDROID-12
|
||||
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onRequestPermissionsResult != null) {
|
||||
QtApplication.invokeDelegateMethod(QtApplication.onRequestPermissionsResult, requestCode, permissions, grantResults);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
|
||||
Contact: http://www.qt-project.org/legal
|
||||
|
||||
Commercial License Usage
|
||||
Licensees holding valid commercial Qt licenses may use this file in
|
||||
accordance with the commercial license agreement provided with the
|
||||
Software or, alternatively, in accordance with the terms contained in
|
||||
a written agreement between you and Digia. For licensing terms and
|
||||
conditions see http://qt.digia.com/licensing. For further information
|
||||
use the contact form at http://qt.digia.com/contact-us.
|
||||
|
||||
BSD License Usage
|
||||
Alternatively, this file may be used under the BSD license as follows:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.qtproject.qt5.android.bindings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Window;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import dalvik.system.DexClassLoader;
|
||||
|
||||
public class QtActivityLoader {
|
||||
private static final String DEX_PATH_KEY = "dex.path";
|
||||
private static final String LIB_PATH_KEY = "lib.path";
|
||||
private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
|
||||
private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
|
||||
private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
|
||||
private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
|
||||
private static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id";
|
||||
private static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id";
|
||||
private static final String MAIN_LIBRARY_KEY = "main.library";
|
||||
private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
|
||||
private static final String EXTRACT_STYLE_KEY = "extract.android.style";
|
||||
private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application,
|
||||
String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
|
||||
String[] QT_ANDROID_THEMES = null;
|
||||
String QT_ANDROID_DEFAULT_THEME = null;
|
||||
|
||||
private String[] m_qtLibs = null; // required qt libs
|
||||
private int m_displayDensity = -1;
|
||||
private ContextWrapper m_context;
|
||||
private ComponentInfo m_contextInfo;
|
||||
private Class<?> m_delegateClass;
|
||||
|
||||
// this function is used to load and start the loader
|
||||
private void loadApplication(Bundle loaderParams) {
|
||||
try {
|
||||
// add all bundled Qt libs to loader params
|
||||
ArrayList<String> libs = new ArrayList<>();
|
||||
|
||||
String libName = m_contextInfo.metaData.getString("android.app.lib_name");
|
||||
loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
|
||||
loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
|
||||
|
||||
// load and start QtLoader class
|
||||
DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
|
||||
m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written.
|
||||
loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists)
|
||||
m_context.getClassLoader()); // parent loader
|
||||
|
||||
Class<?> loaderClass = classLoader.loadClass(loaderClassName()); // load QtLoader class
|
||||
Object qtLoader = loaderClass.newInstance(); // create an instance
|
||||
Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication",
|
||||
contextClassName(),
|
||||
ClassLoader.class,
|
||||
Bundle.class);
|
||||
if (!(Boolean) prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams)) {
|
||||
throw new Exception("");
|
||||
}
|
||||
|
||||
QtApplication.setQtContextDelegate(m_delegateClass, qtLoader);
|
||||
|
||||
// now load the application library so it's accessible from this class loader
|
||||
if (libName != null) {
|
||||
System.loadLibrary(libName);
|
||||
}
|
||||
|
||||
Method startAppMethod = qtLoader.getClass().getMethod("startApplication");
|
||||
if (!(Boolean) startAppMethod.invoke(qtLoader)) {
|
||||
throw new Exception("");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
|
||||
errorDialog.setMessage("Fatal error, your application can't be started.");
|
||||
errorDialog.setButton(DialogInterface.BUTTON_NEUTRAL, m_context.getResources().getString(android.R.string.ok), (dialog, which) -> {
|
||||
finish();
|
||||
});
|
||||
errorDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
static private void copyFile(InputStream inputStream, OutputStream outputStream)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
int count;
|
||||
while ((count = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyAsset(String source, String destination)
|
||||
throws IOException {
|
||||
// Already exists, we don't have to do anything
|
||||
File destinationFile = new File(destination);
|
||||
if (destinationFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File parentDirectory = destinationFile.getParentFile();
|
||||
if (!parentDirectory.exists()) {
|
||||
parentDirectory.mkdirs();
|
||||
}
|
||||
|
||||
destinationFile.createNewFile();
|
||||
|
||||
AssetManager assetsManager = m_context.getAssets();
|
||||
InputStream inputStream = assetsManager.open(source);
|
||||
OutputStream outputStream = new FileOutputStream(destinationFile);
|
||||
copyFile(inputStream, outputStream);
|
||||
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
private static void createBundledBinary(String source, String destination)
|
||||
throws IOException {
|
||||
// Already exists, we don't have to do anything
|
||||
File destinationFile = new File(destination);
|
||||
if (destinationFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File parentDirectory = destinationFile.getParentFile();
|
||||
if (!parentDirectory.exists()) {
|
||||
parentDirectory.mkdirs();
|
||||
}
|
||||
|
||||
destinationFile.createNewFile();
|
||||
|
||||
InputStream inputStream = new FileInputStream(source);
|
||||
OutputStream outputStream = new FileOutputStream(destinationFile);
|
||||
copyFile(inputStream, outputStream);
|
||||
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) {
|
||||
File versionFile = new File(pluginsPrefix + "cache.version");
|
||||
|
||||
long cacheVersion = 0;
|
||||
if (versionFile.exists() && versionFile.canRead()) {
|
||||
try {
|
||||
DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile));
|
||||
cacheVersion = inputStream.readLong();
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheVersion != packageVersion) {
|
||||
deleteRecursively(new File(pluginsPrefix));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void extractBundledPluginsAndImports(String pluginsPrefix) throws IOException {
|
||||
String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/";
|
||||
long packageVersion = -1;
|
||||
try {
|
||||
PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0);
|
||||
packageVersion = packageInfo.lastUpdateTime;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
File versionFile = new File(pluginsPrefix + "cache.version");
|
||||
|
||||
File parentDirectory = versionFile.getParentFile();
|
||||
if (!parentDirectory.exists()) {
|
||||
parentDirectory.mkdirs();
|
||||
}
|
||||
|
||||
versionFile.createNewFile();
|
||||
|
||||
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile));
|
||||
outputStream.writeLong(packageVersion);
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
{
|
||||
String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
|
||||
if (m_contextInfo.metaData.containsKey(key)) {
|
||||
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
|
||||
|
||||
for (String bundledImportBinary : list) {
|
||||
String[] split = bundledImportBinary.split(":");
|
||||
String sourceFileName = libsDir + split[0];
|
||||
String destinationFileName = pluginsPrefix + split[1];
|
||||
createBundledBinary(sourceFileName, destinationFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY;
|
||||
if (m_contextInfo.metaData.containsKey(key)) {
|
||||
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
|
||||
|
||||
for (String fileName : list) {
|
||||
String[] split = fileName.split(":");
|
||||
String sourceFileName = split[0];
|
||||
String destinationFileName = pluginsPrefix + split[1];
|
||||
copyAsset(sourceFileName, destinationFileName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteRecursively(File directory) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteRecursively(file);
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
directory.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) {
|
||||
File newCache = new File(localPrefix);
|
||||
if (!newCache.exists()) {
|
||||
{
|
||||
File oldPluginsCache = new File(oldLocalPrefix + "plugins/");
|
||||
if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) {
|
||||
deleteRecursively(oldPluginsCache);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
File oldImportsCache = new File(oldLocalPrefix + "imports/");
|
||||
if (oldImportsCache.exists() && oldImportsCache.isDirectory()) {
|
||||
deleteRecursively(oldImportsCache);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
File oldQmlCache = new File(oldLocalPrefix + "qml/");
|
||||
if (oldQmlCache.exists() && oldQmlCache.isDirectory()) {
|
||||
deleteRecursively(oldQmlCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startApp() {
|
||||
try {
|
||||
if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) {
|
||||
int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id");
|
||||
m_qtLibs = m_context.getResources().getStringArray(resourceId);
|
||||
}
|
||||
ArrayList<String> libraryList = new ArrayList<>();
|
||||
String localPrefix = m_context.getApplicationInfo().dataDir + "/";
|
||||
String pluginsPrefix = localPrefix + "qt-reserved-files/";
|
||||
cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
|
||||
extractBundledPluginsAndImports(pluginsPrefix);
|
||||
|
||||
for (String lib : m_qtLibs) {
|
||||
libraryList.add(localPrefix + "lib/lib" + lib + ".so");
|
||||
}
|
||||
|
||||
if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) {
|
||||
String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":");
|
||||
for (String lib : extraLibs) {
|
||||
if (lib.length() > 0) {
|
||||
if (lib.startsWith("lib/")) {
|
||||
libraryList.add(localPrefix + lib);
|
||||
} else {
|
||||
libraryList.add(pluginsPrefix + lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bundle loaderParams = new Bundle();
|
||||
loaderParams.putString(DEX_PATH_KEY, new String());
|
||||
if (m_contextInfo.metaData.containsKey("android.app.static_init_classes")) {
|
||||
loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY,
|
||||
m_contextInfo.metaData.getString("android.app.static_init_classes").split(":"));
|
||||
}
|
||||
loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
|
||||
String themePath = m_context.getApplicationInfo().dataDir + "/qt-reserved-files/android-style/";
|
||||
String stylePath = themePath + m_displayDensity + "/";
|
||||
String extractOption = "full";
|
||||
if (m_contextInfo.metaData.containsKey("android.app.extract_android_style")) {
|
||||
extractOption = m_contextInfo.metaData.getString("android.app.extract_android_style");
|
||||
if (!extractOption.equals("full") && !extractOption.equals("minimal") && !extractOption.equals("none")) {
|
||||
Log.e(QtApplication.QtTAG, "Invalid extract_android_style option \"" + extractOption + "\", defaulting to full");
|
||||
extractOption = "full";
|
||||
}
|
||||
}
|
||||
|
||||
if (!(new File(stylePath)).exists() && !extractOption.equals("none")) {
|
||||
loaderParams.putString(EXTRACT_STYLE_KEY, stylePath);
|
||||
loaderParams.putBoolean(EXTRACT_STYLE_MINIMAL_KEY, extractOption.equals("minimal"));
|
||||
}
|
||||
|
||||
if (extractOption.equals("full")) {
|
||||
ENVIRONMENT_VARIABLES += "\tQT_USE_ANDROID_NATIVE_STYLE=1";
|
||||
}
|
||||
|
||||
ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath;
|
||||
|
||||
loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES
|
||||
+ "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml"
|
||||
+ "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports"
|
||||
+ "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins");
|
||||
|
||||
String appParams = null;
|
||||
if (APPLICATION_PARAMETERS != null) {
|
||||
appParams = APPLICATION_PARAMETERS;
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent != null) {
|
||||
String parameters = intent.getStringExtra("applicationArguments");
|
||||
if (parameters != null) {
|
||||
if (appParams == null) {
|
||||
appParams = parameters;
|
||||
} else {
|
||||
appParams += '\t' + parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_contextInfo.metaData.containsKey("android.app.arguments")) {
|
||||
String parameters = m_contextInfo.metaData.getString("android.app.arguments");
|
||||
if (appParams == null) {
|
||||
appParams = parameters;
|
||||
} else {
|
||||
appParams += '\t' + parameters;
|
||||
}
|
||||
}
|
||||
|
||||
if (appParams != null) {
|
||||
loaderParams.putString(APPLICATION_PARAMETERS_KEY, appParams.replace(' ', '\t').trim());
|
||||
}
|
||||
loadApplication(loaderParams);
|
||||
} catch (Exception e) {
|
||||
Log.e(QtApplication.QtTAG, "Can't create main activity", e);
|
||||
}
|
||||
}
|
||||
|
||||
QtActivity m_activity;
|
||||
|
||||
QtActivityLoader(QtActivity activity) {
|
||||
m_context = activity;
|
||||
m_delegateClass = QtActivity.class;
|
||||
m_activity = activity;
|
||||
}
|
||||
|
||||
protected String loaderClassName() {
|
||||
return "org.qtproject.qt5.android.QtActivityDelegate";
|
||||
}
|
||||
|
||||
protected Class<?> contextClassName() {
|
||||
return android.app.Activity.class;
|
||||
}
|
||||
|
||||
protected void finish() {
|
||||
m_activity.finish();
|
||||
}
|
||||
|
||||
protected String getTitle() {
|
||||
return (String) m_activity.getTitle();
|
||||
}
|
||||
|
||||
protected void runOnUiThread(Runnable run) {
|
||||
m_activity.runOnUiThread(run);
|
||||
}
|
||||
|
||||
Intent getIntent() {
|
||||
return m_activity.getIntent();
|
||||
}
|
||||
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
try {
|
||||
m_contextInfo = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
|
||||
int theme = ((ActivityInfo) m_contextInfo).getThemeResource();
|
||||
for (Field f : Class.forName("android.R$style").getDeclaredFields()) {
|
||||
if (f.getInt(null) == theme) {
|
||||
QT_ANDROID_THEMES = new String[]{f.getName()};
|
||||
QT_ANDROID_DEFAULT_THEME = f.getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
m_activity.setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
m_activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
|
||||
|
||||
if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) {
|
||||
QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState);
|
||||
return;
|
||||
}
|
||||
|
||||
m_displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
|
||||
|
||||
ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME
|
||||
+ "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t";
|
||||
|
||||
if (null == m_activity.getLastNonConfigurationInstance()) {
|
||||
if (m_contextInfo.metaData.containsKey("android.app.background_running")
|
||||
&& m_contextInfo.metaData.getBoolean("android.app.background_running")) {
|
||||
ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t";
|
||||
} else {
|
||||
ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t";
|
||||
}
|
||||
|
||||
if (m_contextInfo.metaData.containsKey("android.app.auto_screen_scale_factor")
|
||||
&& m_contextInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) {
|
||||
ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t";
|
||||
}
|
||||
|
||||
startApp();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
Copyright (c) 2016, BogDan Vatra <bogdan@kde.org>
|
||||
Contact: http://www.qt.io/licensing/
|
||||
|
||||
Commercial License Usage
|
||||
Licensees holding valid commercial Qt licenses may use this file in
|
||||
accordance with the commercial license agreement provided with the
|
||||
Software or, alternatively, in accordance with the terms contained in
|
||||
a written agreement between you and The Qt Company. For licensing terms
|
||||
and conditions see http://www.qt.io/terms-conditions. For further
|
||||
information use the contact form at http://www.qt.io/contact-us.
|
||||
|
||||
BSD License Usage
|
||||
Alternatively, this file may be used under the BSD license as follows:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.qtproject.qt5.android.bindings;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class QtApplication extends Application {
|
||||
public final static String QtTAG = "Qt";
|
||||
public static Object m_delegateObject = null;
|
||||
public static Map<String, ArrayList<Method>> m_delegateMethods = new HashMap<>();
|
||||
public static Method dispatchKeyEvent = null;
|
||||
public static Method dispatchPopulateAccessibilityEvent = null;
|
||||
public static Method dispatchTouchEvent = null;
|
||||
public static Method dispatchTrackballEvent = null;
|
||||
public static Method onKeyDown = null;
|
||||
public static Method onKeyMultiple = null;
|
||||
public static Method onKeyUp = null;
|
||||
public static Method onTouchEvent = null;
|
||||
public static Method onTrackballEvent = null;
|
||||
public static Method onActivityResult = null;
|
||||
public static Method onCreate = null;
|
||||
public static Method onKeyLongPress = null;
|
||||
public static Method dispatchKeyShortcutEvent = null;
|
||||
public static Method onKeyShortcut = null;
|
||||
public static Method dispatchGenericMotionEvent = null;
|
||||
public static Method onGenericMotionEvent = null;
|
||||
public static Method onRequestPermissionsResult = null;
|
||||
private static String activityClassName;
|
||||
|
||||
public static void setQtContextDelegate(Class<?> clazz, Object listener) {
|
||||
m_delegateObject = listener;
|
||||
activityClassName = clazz.getCanonicalName();
|
||||
|
||||
ArrayList<Method> delegateMethods = new ArrayList<>();
|
||||
for (Method m : listener.getClass().getMethods()) {
|
||||
if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt5.android")) {
|
||||
delegateMethods.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Field> applicationFields = new ArrayList<>();
|
||||
for (Field f : QtApplication.class.getFields()) {
|
||||
if (f.getDeclaringClass().getName().equals(QtApplication.class.getName())) {
|
||||
applicationFields.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
for (Method delegateMethod : delegateMethods) {
|
||||
try {
|
||||
clazz.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes());
|
||||
if (m_delegateMethods.containsKey(delegateMethod.getName())) {
|
||||
m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod);
|
||||
} else {
|
||||
ArrayList<Method> delegateSet = new ArrayList<>();
|
||||
delegateSet.add(delegateMethod);
|
||||
m_delegateMethods.put(delegateMethod.getName(), delegateSet);
|
||||
}
|
||||
for (Field applicationField : applicationFields) {
|
||||
if (applicationField.getName().equals(delegateMethod.getName())) {
|
||||
try {
|
||||
applicationField.set(null, delegateMethod);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
if (m_delegateObject != null && m_delegateMethods.containsKey("onTerminate")) {
|
||||
invokeDelegateMethod(m_delegateMethods.get("onTerminate").get(0));
|
||||
}
|
||||
super.onTerminate();
|
||||
}
|
||||
|
||||
static class InvokeResult {
|
||||
boolean invoked = false;
|
||||
Object methodReturns = null;
|
||||
}
|
||||
|
||||
private static int stackDeep = -1;
|
||||
|
||||
public static InvokeResult invokeDelegate(Object... args) {
|
||||
InvokeResult result = new InvokeResult();
|
||||
if (m_delegateObject == null) {
|
||||
return result;
|
||||
}
|
||||
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
||||
if (-1 == stackDeep) {
|
||||
for (int it = 0; it < elements.length; it++) {
|
||||
if (elements[it].getClassName().equals(activityClassName)) {
|
||||
stackDeep = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-1 == stackDeep) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final String methodName = elements[stackDeep].getMethodName();
|
||||
if (!m_delegateMethods.containsKey(methodName)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (Method m : m_delegateMethods.get(methodName)) {
|
||||
if (m.getParameterTypes().length == args.length) {
|
||||
result.methodReturns = invokeDelegateMethod(m, args);
|
||||
result.invoked = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Object invokeDelegateMethod(Method m, Object... args) {
|
||||
try {
|
||||
return m.invoke(m_delegateObject, args);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
package org.saintandreas.testapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.vr.ndk.base.AndroidCompat;
|
||||
import com.google.vr.ndk.base.GvrLayout;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
private final static int IMMERSIVE_STICKY_VIEW_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
|
||||
static {
|
||||
System.loadLibrary("gvr");
|
||||
System.loadLibrary("native-lib");
|
||||
}
|
||||
|
||||
private long nativeRenderer;
|
||||
private GLSurfaceView surfaceView;
|
||||
|
||||
private native long nativeCreateRenderer(ClassLoader appClassLoader, Context context);
|
||||
private native void nativeDestroyRenderer(long renderer);
|
||||
private native void nativeInitializeGl(long renderer);
|
||||
private native void nativeDrawFrame(long renderer);
|
||||
private native void nativeOnTriggerEvent(long renderer);
|
||||
private native void nativeOnPause(long renderer);
|
||||
private native void nativeOnResume(long renderer);
|
||||
|
||||
class NativeRenderer implements GLSurfaceView.Renderer {
|
||||
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInitializeGl(nativeRenderer); }
|
||||
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { }
|
||||
@Override public void onDrawFrame(GL10 gl) {
|
||||
nativeDrawFrame(nativeRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setImmersiveSticky();
|
||||
getWindow()
|
||||
.getDecorView()
|
||||
.setOnSystemUiVisibilityChangeListener((int visibility)->{
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { setImmersiveSticky(); }
|
||||
});
|
||||
|
||||
nativeRenderer = nativeCreateRenderer(
|
||||
getClass().getClassLoader(),
|
||||
getApplicationContext());
|
||||
|
||||
surfaceView = new GLSurfaceView(this);
|
||||
surfaceView.setEGLContextClientVersion(3);
|
||||
surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
|
||||
surfaceView.setPreserveEGLContextOnPause(true);
|
||||
surfaceView.setRenderer(new NativeRenderer());
|
||||
setContentView(surfaceView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
nativeDestroyRenderer(nativeRenderer);
|
||||
nativeRenderer = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
surfaceView.queueEvent(()->nativeOnPause(nativeRenderer));
|
||||
surfaceView.onPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
surfaceView.onResume();
|
||||
surfaceView.queueEvent(()->nativeOnResume(nativeRenderer));
|
||||
}
|
||||
|
||||
private void setImmersiveSticky() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_STICKY_VIEW_FLAGS);
|
||||
}
|
||||
}
|
BIN
android/app/src/main/res/drawable/icon.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
|
@ -1,3 +1,13 @@
|
|||
import de.undercouch.gradle.tasks.download.Download
|
||||
import de.undercouch.gradle.tasks.download.Verify
|
||||
import groovy.io.FileType
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.xml.XmlUtil
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
|
@ -10,6 +20,7 @@ buildscript {
|
|||
|
||||
plugins {
|
||||
id 'de.undercouch.download' version '3.3.0'
|
||||
id "cz.malohlava" version "1.0.3"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -19,65 +30,290 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
ext {
|
||||
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
|
||||
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
|
||||
BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
|
||||
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
|
||||
QT5_DEPS = [
|
||||
'Qt5Core',
|
||||
'Qt5Gui',
|
||||
'Qt5Multimedia',
|
||||
'Qt5Network',
|
||||
'Qt5OpenGL',
|
||||
'Qt5Qml',
|
||||
'Qt5Quick',
|
||||
'Qt5Script',
|
||||
'Qt5ScriptTools',
|
||||
'Qt5WebChannel',
|
||||
'Qt5WebSockets',
|
||||
'Qt5Widgets',
|
||||
'Qt5XmlPatterns',
|
||||
// Android specific
|
||||
'Qt5AndroidExtras',
|
||||
'Qt5WebView',
|
||||
]
|
||||
}
|
||||
|
||||
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
|
||||
def jniFolder = new File('app/src/main/jniLibs/arm64-v8a')
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
def appDir = new File(projectDir, 'app')
|
||||
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
|
||||
def baseUrl = 'https://hifi-public.s3.amazonaws.com/austin/android/'
|
||||
def qtFile='qt-5.9.3_linux_armv8-libcpp.tgz'
|
||||
def qtChecksum='547da3547d5690144e23d6504c6d6e91'
|
||||
|
||||
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
|
||||
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
|
||||
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
qtFile = 'qt-5.9.3_osx_armv8-libcpp.tgz'
|
||||
qtChecksum='6fa3e068cfdee863fc909b294a3a0cc6'
|
||||
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
|
||||
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
qtFile = 'qt-5.9.3_win_armv8-libcpp.tgz'
|
||||
qtChecksum='3a757378a7e9dbbfc662177e0eb46408'
|
||||
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='a93d22c0c59aa112fda18c4c6d157d17'
|
||||
qtVersionId='0Bl9NSUWb5CBKLT_NXaxTt75SNBBZ9sB'
|
||||
}
|
||||
|
||||
def packages = [
|
||||
qt: [
|
||||
file: qtFile,
|
||||
versionId: qtVersionId,
|
||||
checksum: qtChecksum,
|
||||
sharedLibFolder: '',
|
||||
includeLibs: ['lib/*.so', 'plugins/*/*.so']
|
||||
],
|
||||
bullet: [
|
||||
file: 'bullet-2.83_armv8-libcpp.tgz',
|
||||
checksum: '2c558d604fce337f5eba3eb7ec1252fd'
|
||||
versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo',
|
||||
checksum: '2c558d604fce337f5eba3eb7ec1252fd',
|
||||
],
|
||||
draco: [
|
||||
file: 'draco_armv8-libcpp.tgz',
|
||||
checksum: '617a80d213a5ec69fbfa21a1f2f738cd'
|
||||
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
|
||||
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
|
||||
],
|
||||
glm: [
|
||||
file: 'glm-0.9.8.tgz',
|
||||
versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2',
|
||||
checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a',
|
||||
],
|
||||
gvr: [
|
||||
file: 'gvrsdk_v1.101.0.tgz',
|
||||
checksum: '57fd02baa069176ba18597a29b6b4fc7'
|
||||
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
|
||||
checksum: '57fd02baa069176ba18597a29b6b4fc7',
|
||||
],
|
||||
openssl: [
|
||||
file: 'openssl-1.1.0g_armv8.tgz',
|
||||
checksum: 'cabb681fbccd79594f65fcc266e02f32'
|
||||
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
|
||||
checksum: 'cabb681fbccd79594f65fcc266e02f32',
|
||||
],
|
||||
polyvox: [
|
||||
file: 'polyvox_armv8-libcpp.tgz',
|
||||
checksum: '5c918288741ee754c16aeb12bb46b9e1',
|
||||
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
|
||||
checksum: '349ad5b72aaf2749ca95d847e60c5314',
|
||||
sharedLibFolder: 'lib',
|
||||
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so']
|
||||
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
|
||||
],
|
||||
tbb: [
|
||||
file: 'tbb-2018_U1_armv8_libcpp.tgz',
|
||||
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
|
||||
checksum: '20768f298f53b195e71b414b0ae240c4',
|
||||
sharedLibFolder: 'lib/release',
|
||||
includeLibs: ['libtbb.so', 'libtbbmalloc.so']
|
||||
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def scribeLocalFile='scribe' + EXEC_SUFFIX
|
||||
|
||||
def scribeFile='scribe_linux_x86_64'
|
||||
def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c'
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
scribeFile = 'scribe_osx_x86_64'
|
||||
scribeChecksum='a137ad62c1bf7cca739da219544a9a16'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
scribeFile = 'scribe_win32_x86_64.exe'
|
||||
scribeChecksum='75c2ce9ed45d17de375e3988bfaba816'
|
||||
}
|
||||
|
||||
def options = [
|
||||
files: new TreeSet<File>(),
|
||||
features: new HashSet<String>(),
|
||||
permissions: new HashSet<String>()
|
||||
]
|
||||
|
||||
def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt')
|
||||
|
||||
def captureOutput = { String command ->
|
||||
def proc = command.execute()
|
||||
def sout = new StringBuilder(), serr = new StringBuilder()
|
||||
proc.consumeProcessOutput(sout, serr)
|
||||
proc.waitForOrKill(10000)
|
||||
def errorOutput = serr.toString()
|
||||
if (!errorOutput.isEmpty()) {
|
||||
throw new GradleException("Command '${command}' failed with error ${errorOutput}")
|
||||
}
|
||||
return sout.toString()
|
||||
}
|
||||
|
||||
def relativize = { File root, File absolute ->
|
||||
def relativeURI = root.toURI().relativize(absolute.toURI())
|
||||
return new File(relativeURI.toString())
|
||||
}
|
||||
|
||||
def scanQmlImports = { File qmlRootPath ->
|
||||
def qmlImportCommandFile = new File(qmlRoot, 'bin/qmlimportscanner' + EXEC_SUFFIX)
|
||||
if (!qmlImportCommandFile.exists()) {
|
||||
throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString())
|
||||
}
|
||||
|
||||
def command = qmlImportCommandFile.absolutePath +
|
||||
" -rootPath ${qmlRootPath.absolutePath}" +
|
||||
" -importPath ${qmlRoot.absolutePath}/qml"
|
||||
|
||||
def commandResult = captureOutput(command)
|
||||
new JsonSlurper().parseText(commandResult).each {
|
||||
if (!it.containsKey('path')) {
|
||||
println "Warning: QML import could not be resolved in any of the import paths: ${it.name}"
|
||||
return
|
||||
}
|
||||
def file = new File(it.path)
|
||||
// Ignore non-existent files
|
||||
if (!file.exists()) {
|
||||
return
|
||||
}
|
||||
// Ignore files in the import path
|
||||
if (file.canonicalPath.startsWith(qmlRootPath.canonicalPath)) {
|
||||
return
|
||||
}
|
||||
if (file.isFile()) {
|
||||
options.files.add(file)
|
||||
} else {
|
||||
file.eachFileRecurse(FileType.FILES, {
|
||||
options.files.add(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def parseQtDependencies = { List qtLibs ->
|
||||
qtLibs.each({
|
||||
def libFile = new File(qmlRoot, "lib/lib${it}.so")
|
||||
options.files.add(libFile)
|
||||
|
||||
def androidDeps = new File(qmlRoot, "lib/${it}-android-dependencies.xml")
|
||||
if (!libFile.exists()) return
|
||||
if (!androidDeps.exists()) return
|
||||
|
||||
new XmlSlurper().parse(androidDeps).dependencies.lib.depends.'*'.each{ node ->
|
||||
switch (node.name()) {
|
||||
case 'lib':
|
||||
case 'bundled':
|
||||
def relativeFilename = node.@file.toString()
|
||||
|
||||
// Special case, since this is handled by qmlimportscanner instead
|
||||
if (relativeFilename.startsWith('qml'))
|
||||
return
|
||||
|
||||
def file = new File(qmlRoot, relativeFilename)
|
||||
|
||||
if (!file.exists())
|
||||
return
|
||||
|
||||
if (file.isFile()) {
|
||||
options.files.add(file)
|
||||
} else {
|
||||
file.eachFileRecurse(FileType.FILES, { options.files.add(it) })
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
case 'jar':
|
||||
if (node.@bundling == "1") {
|
||||
def jar = new File(qmlRoot, node.@file.toString())
|
||||
if (!jar.exists()) {
|
||||
throw new GradleException('Unable to find required JAR ' + jar.path)
|
||||
}
|
||||
options.files.add(jar)
|
||||
}
|
||||
break
|
||||
|
||||
case 'permission':
|
||||
options.permissions.add(node.@name)
|
||||
break
|
||||
|
||||
case 'feature':
|
||||
options.features.add(node.@name)
|
||||
break
|
||||
|
||||
default:
|
||||
throw new GradleException('Unhandled Android Dependency node ' + node.name())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
def generateLibsXml = {
|
||||
def libDestinationDirectory = jniFolder
|
||||
def jarDestinationDirectory = new File(appDir, 'libs')
|
||||
def assetDestinationDirectory = new File(appDir, 'src/main/assets/bundled');
|
||||
def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml')
|
||||
def libPrefix = 'lib' + File.separator
|
||||
def jarPrefix = 'jar' + File.separator
|
||||
|
||||
def xmlParser = new XmlParser()
|
||||
def libsXmlRoot = xmlParser.parseText('<?xml version="1.0" encoding="UTF-8"?><resources/>')
|
||||
def qtLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'qt_libs'])
|
||||
def bundledLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_lib'])
|
||||
def bundledAssetsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_assets'])
|
||||
|
||||
options.files.each {
|
||||
def sourceFile = it
|
||||
if (!sourceFile.exists()) {
|
||||
throw new GradleException("Unable to find dependency file " + sourceFile.toString())
|
||||
}
|
||||
|
||||
def relativePath = relativize( qmlRoot, sourceFile ).toString()
|
||||
def destinationFile
|
||||
if (relativePath.endsWith('.so')) {
|
||||
def garbledFileName
|
||||
if (relativePath.startsWith(libPrefix)) {
|
||||
garbledFileName = relativePath.substring(libPrefix.size())
|
||||
Pattern p = ~/lib(Qt5.*).so/
|
||||
Matcher m = p.matcher(garbledFileName)
|
||||
assert m.matches()
|
||||
def libName = m.group(1)
|
||||
xmlParser.createNode(qtLibsNode, 'item', [:]).setValue(libName)
|
||||
} else {
|
||||
garbledFileName = 'lib' + relativePath.replace(File.separator, '_'[0])
|
||||
xmlParser.createNode(bundledLibsNode, 'item', [:]).setValue("${garbledFileName}:${relativePath}".replace(File.separator, '/'))
|
||||
}
|
||||
destinationFile = new File(libDestinationDirectory, garbledFileName)
|
||||
} else if (relativePath.startsWith('jar')) {
|
||||
destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size()))
|
||||
} else {
|
||||
xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("bundled/${relativePath}:${relativePath}".replace(File.separator, '/'))
|
||||
destinationFile = new File(assetDestinationDirectory, relativePath)
|
||||
}
|
||||
|
||||
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
|
||||
assert destinationFile.exists() && destinationFile.isFile()
|
||||
}
|
||||
def xml = XmlUtil.serialize(libsXmlRoot)
|
||||
new FileWriter(libsXmlFile).withPrintWriter { writer ->
|
||||
writer.write(xml)
|
||||
}
|
||||
}
|
||||
|
||||
task downloadDependencies {
|
||||
doLast {
|
||||
packages.each { entry ->
|
||||
def filename = entry.value['file'];
|
||||
def url = baseUrl + filename;
|
||||
if (entry.value.containsKey('versionId')) {
|
||||
url = url + '?versionId=' + entry.value['versionId']
|
||||
}
|
||||
download {
|
||||
src url
|
||||
dest new File(baseFolder, filename)
|
||||
|
@ -87,8 +323,6 @@ task downloadDependencies {
|
|||
}
|
||||
}
|
||||
|
||||
import de.undercouch.gradle.tasks.download.Verify
|
||||
|
||||
task verifyQt(type: Verify) { def p = packages['qt']; src new File(baseFolder, p['file']); checksum p['checksum']; }
|
||||
task verifyBullet(type: Verify) { def p = packages['bullet']; src new File(baseFolder, p['file']); checksum p['checksum'] }
|
||||
task verifyDraco(type: Verify) { def p = packages['draco']; src new File(baseFolder, p['file']); checksum p['checksum'] }
|
||||
|
@ -109,19 +343,26 @@ verifyDependencyDownloads.dependsOn verifyTBB
|
|||
task extractDependencies(dependsOn: verifyDependencyDownloads) {
|
||||
doLast {
|
||||
packages.each { entry ->
|
||||
def folder = entry.key;
|
||||
def filename = entry.value['file'];
|
||||
def folder = entry.key
|
||||
def filename = entry.value['file']
|
||||
def localFile = new File(HIFI_ANDROID_PRECOMPILED, filename)
|
||||
def localFolder = new File(HIFI_ANDROID_PRECOMPILED, folder)
|
||||
def fileTree;
|
||||
if (filename.endsWith('zip')) {
|
||||
fileTree = zipTree(localFile)
|
||||
} else {
|
||||
fileTree = tarTree(resources.gzip(localFile))
|
||||
}
|
||||
copy {
|
||||
from tarTree(resources.gzip(localFile))
|
||||
from fileTree
|
||||
into localFolder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyDependencies(dependsOn: extractDependencies) {
|
||||
// Copies the non Qt dependencies. Qt dependencies (primary libraries and plugins) are handled by the qtBundle task
|
||||
task copyDependencies(dependsOn: [ extractDependencies ]) {
|
||||
doLast {
|
||||
packages.each { entry ->
|
||||
def packageName = entry.key
|
||||
|
@ -132,31 +373,17 @@ task copyDependencies(dependsOn: extractDependencies) {
|
|||
if (currentPackage.containsKey('includeLibs')) {
|
||||
currentPackage['includeLibs'].each { includeSpec -> tree.include includeSpec }
|
||||
}
|
||||
tree.visit { element ->
|
||||
tree.visit { element ->
|
||||
if (!element.file.isDirectory()) {
|
||||
println "Copying " + element.file + " to " + jniFolder
|
||||
copy { from element.file; into jniFolder }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def scribeFile='scribe_linux_x86_64'
|
||||
def scribeLocalFile='scribe'
|
||||
def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c'
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
scribeFile = 'scribe_osx_x86_64'
|
||||
scribeChecksum='a137ad62c1bf7cca739da219544a9a16'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
scribeFile = 'scribe_win32_x86_64.exe'
|
||||
scribeLocalFile = 'scribe.exe'
|
||||
scribeChecksum='75c2ce9ed45d17de375e3988bfaba816'
|
||||
|
||||
}
|
||||
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
|
||||
task downloadScribe(type: Download) {
|
||||
src baseUrl + scribeFile
|
||||
dest new File(baseFolder, scribeLocalFile)
|
||||
|
@ -203,14 +430,101 @@ task extractGvrBinaries(dependsOn: extractDependencies) {
|
|||
|
||||
}
|
||||
|
||||
task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBinaries]) {
|
||||
// Copy required Qt main libraries and required plugins based on the predefined list here
|
||||
// FIXME eventually we would like to use the readelf functionality to automatically detect dependencies
|
||||
// from our built applications and use that during the full build process. However doing so would mean
|
||||
// hooking existing Android build tasks since the output from the qtBundle logic adds JNI libs, asset
|
||||
// files and resources files and potentially modifies the AndroidManifest.xml
|
||||
task qtBundle {
|
||||
doLast {
|
||||
parseQtDependencies(QT5_DEPS)
|
||||
//def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/")
|
||||
def qmlImportFolder = new File("${projectDir}/app/src/main/cpp")
|
||||
scanQmlImports(qmlImportFolder)
|
||||
generateLibsXml()
|
||||
}
|
||||
}
|
||||
|
||||
task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBinaries, qtBundle]) { }
|
||||
|
||||
task cleanDependencies(type: Delete) {
|
||||
delete HIFI_ANDROID_PRECOMPILED
|
||||
delete 'app/src/main/jniLibs/arm64-v8a'
|
||||
delete 'app/src/main/assets/bundled'
|
||||
delete 'app/src/main/res/values/libs.xml'
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
|
||||
// See the comment on the qtBundle task above
|
||||
/*
|
||||
// FIXME derive the path from the gradle environment
|
||||
def toolchain = [
|
||||
version: '4.9',
|
||||
prefix: 'aarch64-linux-android',
|
||||
// FIXME derive from the host OS
|
||||
ndkHost: 'windows-x86_64',
|
||||
]
|
||||
|
||||
def findDependentLibrary = { String name ->
|
||||
def libFolders = [
|
||||
new File(qmlRoot, 'lib'),
|
||||
new File("${HIFI_ANDROID_PRECOMPILED}/tbb/lib/release"),
|
||||
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/Release"),
|
||||
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/"),
|
||||
new File("${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/libraries"),
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def readElfBinary = new File(android.ndkDirectory, "/toolchains/${toolchain.prefix}-${toolchain.version}/prebuilt/${toolchain.ndkHost}/bin/${toolchain.prefix}-readelf${EXEC_SUFFIX}")
|
||||
|
||||
def getDependencies = { File elfBinary ->
|
||||
Set<File> result = []
|
||||
Queue<File> pending = new LinkedList<>()
|
||||
pending.add(elfBinary)
|
||||
Set<File> scanned = []
|
||||
|
||||
Pattern p = ~/.*\(NEEDED\).*Shared library: \[(.*\.so)\]/
|
||||
while (!pending.isEmpty()) {
|
||||
File current = pending.remove()
|
||||
if (scanned.contains(current)) {
|
||||
continue
|
||||
}
|
||||
scanned.add(current)
|
||||
def command = "${readElfBinary} -d -W ${current.absolutePath}"
|
||||
captureOutput(command).split('[\r\n]').each { line ->
|
||||
Matcher m = p.matcher(line)
|
||||
if (!m.matches()) {
|
||||
return
|
||||
}
|
||||
def libName = m.group(1)
|
||||
def file = new File(qmlRoot, "lib/${libName}")
|
||||
if (file.exists()) {
|
||||
result.add(file)
|
||||
pending.add(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
task testElf (dependsOn: 'externalNativeBuildDebug') {
|
||||
doLast {
|
||||
def appLibraries = new HashSet<File>()
|
||||
def qtDependencies = new HashSet<File>()
|
||||
externalNativeBuildDebug.nativeBuildConfigurationsJsons.each { File file ->
|
||||
def json = new JsonSlurper().parse(file)
|
||||
json.libraries.each { node ->
|
||||
def outputFile = new File(node.value.output)
|
||||
if (outputFile.canonicalPath.startsWith(projectDir.canonicalPath)) {
|
||||
appLibraries.add(outputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appLibraries.each { File file ->
|
||||
println getDependencies(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
87
android/build_recipes.md
Normal file
|
@ -0,0 +1,87 @@
|
|||
Different libraries require different mechanism for building. Some are easiest with the standalone toolchain. Some are easiest with the ndk-build tool. Some can rely on CMake to do the right thing.
|
||||
|
||||
## Setup
|
||||
|
||||
### Android build environment
|
||||
|
||||
You need the Android NDK and SDK. The easiest way to get these is to download Android Studio for your platform and use the SDK manager in Studio to install the items. In particular you will need Android API levels 24 and 26, as well as the NDK, CMake, and LLDB. You should be using NDK version 16 or higher.
|
||||
|
||||
The Studio installation can install the SDK wherver you like. This guide assumes you install it to `$HOME/Android/SDK`. It will install the NDK inside the SDK in a folder named `ndk-bundle` but for convenience we will assume a symlink from `$HOME/Android/NDK` to the actual NDK folder exists
|
||||
|
||||
Additionally, some of the tools require a standalone toolchain in order to build. From the NDK build/tools directory you can execute the following command
|
||||
|
||||
`./make-standalone-toolchain.sh --arch=arm64 --platform=android-24 --install-dir=$HOME/Android/arm64_toolchain`
|
||||
|
||||
This will create the toolchain and install it in `$HOME/Android/arm64_toolchain`
|
||||
|
||||
When doing a build that relies on the toolchain you can execute the following commands to enable it
|
||||
|
||||
```
|
||||
target_host=aarch64-linux-android
|
||||
export PATH=$PATH:$HOME/Android/arm64_toolchain/bin
|
||||
export AR=$target_host-ar
|
||||
export AS=$target_host-as
|
||||
export CC=$target_host-gcc
|
||||
export CXX=$target_host-g++
|
||||
export LD=$target_host-ld
|
||||
export STRIP=$target_host-strip
|
||||
export CFLAGS="-fPIE -fPIC"
|
||||
export LDFLAGS="-pie"
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Qt
|
||||
|
||||
### Windows host
|
||||
|
||||
* Install the Android SDK
|
||||
* Install the Android NDK
|
||||
* Install Git for Windows
|
||||
* Install Strawberry Perl
|
||||
* Install Java 8 (Do NOT use Java 9, it will fail)
|
||||
* Install Python 3.6 for Windows
|
||||
* Open a Git Bash command prompt
|
||||
* Ensure the following commands are visible in the path with `which <command>`
|
||||
* gcc
|
||||
* javac
|
||||
* python
|
||||
* gmake
|
||||
* If any of them fail, fix your path and restart the bash prompt
|
||||
* Fetch the pre-built OpenSSL binaries for Android/iOS from here: https://github.com/leenjewel/openssl_for_ios_and_android/releases
|
||||
* Grab the latest release of the 1.0.2 series
|
||||
* Open the archive and extract the `/android/openssl-arm64-v8a` folder
|
||||
|
||||
* Download the Qt sources
|
||||
* `git clone git://code.qt.io/qt/qt5.git`
|
||||
* `cd qt5`
|
||||
* `perl init-repository`
|
||||
* `git checkout v5.9.3`
|
||||
* `git submodule update --recursive`
|
||||
* `cd ..`
|
||||
* Create a build directory with the command `mkdir qt5build`
|
||||
* Configure the Qt5 build with the command `../qt5/configure -xplatform android-clang -android-ndk-host windows-x86_64 -confirm-license -opensource --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-ndk C:/Android/NDK -android-toolchain-version 4.9 -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -platform win32-g++ -prefix C:/qt5build_debug -android-sdk C:/Android/SDK -c++std c++14 -openssl-linked -L<PATH_TO_SSL>/lib -I<PATH_TO_SSL>/include`
|
||||
|
||||
|
||||
|
||||
## TBB
|
||||
|
||||
Use the ndk-build tool
|
||||
|
||||
ndk-build tbb tbbmalloc target=android arch=ia32 tbb_os=windows ndk_version=16
|
||||
|
||||
## OpenSSL
|
||||
|
||||
Use a standalone toolchain
|
||||
|
||||
* Grab the latest 1.1.0x series source from https://github.com/openssl/openssl/releases
|
||||
* Follow the NDK guidelines for building a standalone toolchain for aarch64
|
||||
* Use the following script to configure and build OpenSSL
|
||||
* Enable the standalone toolchain with the export commands described above
|
||||
* Configure SSL with the command `./Configure android64-aarch64 no-asm no-ssl2 no-ssl3 no-comp no-hw no-engine --prefix=$HOME/Android/openssl_1.1.0g`
|
||||
* Build and install SSL with the command `make depend && make && make install`
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ setup_memory_debugger()
|
|||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree gpu model fbx entities
|
||||
audio avatars octree gpu graphics fbx entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins midi image
|
||||
)
|
||||
|
|
|
@ -94,7 +94,6 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
|
||||
|
||||
|
@ -149,17 +148,6 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
|
|||
}
|
||||
}
|
||||
|
||||
void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
NodeType_t nodeType;
|
||||
message->peekPrimitive(&nodeType);
|
||||
|
||||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
_receivedAudioStream.parseData(*message);
|
||||
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
|
||||
|
@ -355,7 +343,6 @@ void Agent::scriptRequestFinished() {
|
|||
|
||||
void Agent::executeScript() {
|
||||
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
||||
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
||||
|
||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
|
||||
|
||||
|
@ -451,7 +438,7 @@ void Agent::executeScript() {
|
|||
encodedBuffer = audio;
|
||||
}
|
||||
|
||||
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
|
||||
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false,
|
||||
audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0),
|
||||
packetType, _selectedCodecName);
|
||||
});
|
||||
|
@ -483,10 +470,7 @@ void Agent::executeScript() {
|
|||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
_scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
|
||||
|
||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
||||
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||
entityScriptingInterface->init();
|
||||
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
||||
|
||||
_entityViewer.init();
|
||||
|
||||
|
|
|
@ -73,7 +73,6 @@ private slots:
|
|||
|
||||
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void nodeActivated(SharedNodePointer activatedNode);
|
||||
|
|
|
@ -275,17 +275,28 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
|
|||
if (micStreamIt == _audioStreams.end()) {
|
||||
// we don't have a mic stream yet, so add it
|
||||
|
||||
// read the channel flag to see if our stream is stereo or not
|
||||
// hop past the sequence number that leads the packet
|
||||
message.seek(sizeof(quint16));
|
||||
|
||||
quint8 channelFlag;
|
||||
message.readPrimitive(&channelFlag);
|
||||
// pull the codec string from the packet
|
||||
auto codecString = message.readString();
|
||||
|
||||
bool isStereo = channelFlag == 1;
|
||||
// determine if the stream is stereo or not
|
||||
bool isStereo;
|
||||
if (packetType == PacketType::SilentAudioFrame
|
||||
|| packetType == PacketType::ReplicatedSilentAudioFrame) {
|
||||
quint16 numSilentSamples;
|
||||
message.readPrimitive(&numSilentSamples);
|
||||
isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
|
||||
} else {
|
||||
quint8 channelFlag;
|
||||
message.readPrimitive(&channelFlag);
|
||||
isStereo = channelFlag == 1;
|
||||
}
|
||||
|
||||
auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames());
|
||||
avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
|
||||
avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
|
||||
connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec,
|
||||
this, &AudioMixerClientData::handleMismatchAudioFormat);
|
||||
|
@ -324,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
|
|||
|
||||
#if INJECTORS_SUPPORT_CODECS
|
||||
injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName;
|
||||
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
#endif
|
||||
|
||||
auto emplaced = _audioStreams.emplace(
|
||||
|
@ -567,7 +578,8 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c
|
|||
|
||||
auto avatarAudioStream = getAvatarAudioStream();
|
||||
if (avatarAudioStream) {
|
||||
avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO);
|
||||
avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo();
|
||||
}
|
||||
|
||||
#if INJECTORS_SUPPORT_CODECS
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "AudioLogging.h"
|
||||
#include "AvatarAudioStream.h"
|
||||
|
||||
AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) :
|
||||
|
@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
|
|||
_ringBuffer.resizeForFrameSize(isStereo
|
||||
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
||||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
// restart the codec
|
||||
if (_codec) {
|
||||
if (_decoder) {
|
||||
_codec->releaseDecoder(_decoder);
|
||||
}
|
||||
_decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
}
|
||||
qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
|
||||
_isStereo = isStereo;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,9 @@ public:
|
|||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
|
||||
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
|
||||
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
|
||||
return _lastOtherAvatarSentJoints[otherAvatar];
|
||||
auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar];
|
||||
lastOtherAvatarSentJoints.resize(_avatar->getJointCount());
|
||||
return lastOtherAvatarSentJoints;
|
||||
}
|
||||
|
||||
void queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||
|
|
|
@ -214,7 +214,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
uint64_t getTimestamp() const override {
|
||||
return _lastEncodeTime;
|
||||
}
|
||||
const AvatarSharedPointer& getAvatar() const { return _avatar; }
|
||||
AvatarSharedPointer getAvatar() const { return _avatar; }
|
||||
|
||||
private:
|
||||
AvatarSharedPointer _avatar;
|
||||
|
@ -326,7 +326,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
while (!sortedAvatars.empty()) {
|
||||
const auto& avatarData = sortedAvatars.top().getAvatar();
|
||||
const auto avatarData = sortedAvatars.top().getAvatar();
|
||||
sortedAvatars.pop();
|
||||
remainingAvatars--;
|
||||
|
||||
|
|
|
@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
AnimPose& absPose = absPoses[i];
|
||||
if (data.rotation != absPose.rot()) {
|
||||
data.rotation = absPose.rot();
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
AnimPose& relPose = poses[i];
|
||||
if (data.translation != relPose.trans()) {
|
||||
data.translation = relPose.trans();
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,23 +23,6 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
char serverType = getMyNodeType();
|
||||
PacketType packetType = getMyQueryMessageType();
|
||||
|
||||
NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();
|
||||
|
||||
bool wantExtraDebugging = false;
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "OctreeHeadlessViewer::queryOctree() _jurisdictionListener=" << _jurisdictionListener;
|
||||
qCDebug(octree) << "---------------";
|
||||
qCDebug(octree) << "_jurisdictionListener=" << _jurisdictionListener;
|
||||
qCDebug(octree) << "Jurisdictions...";
|
||||
jurisdictions.withReadLock([&] {
|
||||
for (NodeToJurisdictionMapIterator i = jurisdictions.begin(); i != jurisdictions.end(); ++i) {
|
||||
qCDebug(octree) << i.key() << ": " << &i.value();
|
||||
}
|
||||
});
|
||||
qCDebug(octree) << "---------------";
|
||||
}
|
||||
|
||||
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
|
||||
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
|
||||
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
|
||||
|
@ -51,159 +34,22 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
_octreeQuery.setOctreeSizeScale(_voxelSizeScale);
|
||||
_octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
|
||||
|
||||
// Iterate all of the nodes, and get a count of how many voxel servers we have...
|
||||
int totalServers = 0;
|
||||
int inViewServers = 0;
|
||||
int unknownJurisdictionServers = 0;
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
totalServers++;
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
VoxelPositionSize rootDetails;
|
||||
bool foundRootDetails = false;
|
||||
jurisdictions.withReadLock([&] {
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownJurisdictionServers++;
|
||||
return;
|
||||
}
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
if (!rootCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
foundRootDetails = true;
|
||||
});
|
||||
|
||||
if (foundRootDetails) {
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
if ((bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds))) {
|
||||
inViewServers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree, "Servers: total %d, in view %d, unknown jurisdiction %d",
|
||||
totalServers, inViewServers, unknownJurisdictionServers);
|
||||
}
|
||||
|
||||
int perServerPPS = 0;
|
||||
const int SMALL_BUDGET = 10;
|
||||
int perUnknownServer = SMALL_BUDGET;
|
||||
int totalPPS = getMaxPacketsPerSecond();
|
||||
|
||||
// determine PPS based on number of servers
|
||||
if (inViewServers >= 1) {
|
||||
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
|
||||
// for each unknown jurisdiction server
|
||||
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
|
||||
} else {
|
||||
if (unknownJurisdictionServers > 0) {
|
||||
perUnknownServer = (totalPPS / unknownJurisdictionServers);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
auto node = nodeList->soloNodeOfType(serverType);
|
||||
if (node && node->getActiveSocket()) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
|
||||
|
||||
bool inView = false;
|
||||
bool unknownView = false;
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
VoxelPositionSize rootDetails;
|
||||
bool foundRootDetails = false;
|
||||
jurisdictions.withReadLock([&] {
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownView = true; // assume it's in view
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "no known jurisdiction for node " << *node << ", assume it's visible.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
// encode the query data
|
||||
auto packetData = reinterpret_cast<unsigned char*>(queryPacket->getPayload());
|
||||
int packetSize = _octreeQuery.getBroadcastData(packetData);
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (!rootCode) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
|
||||
}
|
||||
return;
|
||||
}
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
foundRootDetails = true;
|
||||
});
|
||||
|
||||
if (foundRootDetails) {
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
inView = (bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds));
|
||||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "inView for node " << *node << ", give it budget of " << perServerPPS;
|
||||
}
|
||||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
}
|
||||
|
||||
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
|
||||
// If there's only one server, then don't do this, and just let the normal voxel query pass through
|
||||
// as expected... this way, we will actually get a valid scene if there is one to be seen
|
||||
if (totalServers > 1) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
|
||||
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
|
||||
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
|
||||
_octreeQuery.setCameraNearClip(0.1f);
|
||||
_octreeQuery.setCameraFarClip(0.1f);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Using 'minimal' camera position for node" << *node;
|
||||
}
|
||||
} else {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// setup the query packet
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// read the data to our packet and set the payload size to fit the query
|
||||
int querySize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
|
||||
queryPacket->setPayloadSize(querySize);
|
||||
|
||||
// ask the NodeList to send it
|
||||
nodeList->sendPacket(std::move(queryPacket), *node);
|
||||
}
|
||||
});
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_OctreeHeadlessViewer_h
|
||||
|
||||
#include <OctreeProcessor.h>
|
||||
#include <JurisdictionListener.h>
|
||||
#include <OctreeQuery.h>
|
||||
|
||||
|
||||
|
@ -23,8 +22,6 @@ class OctreeHeadlessViewer : public OctreeProcessor {
|
|||
public:
|
||||
OctreeHeadlessViewer();
|
||||
virtual ~OctreeHeadlessViewer() {};
|
||||
|
||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||
|
||||
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
|
||||
|
||||
|
@ -57,7 +54,6 @@ public slots:
|
|||
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
|
||||
|
||||
private:
|
||||
JurisdictionListener* _jurisdictionListener = nullptr;
|
||||
OctreeQuery _octreeQuery;
|
||||
|
||||
ViewFrustum _viewFrustum;
|
||||
|
|
|
@ -391,8 +391,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
|
||||
nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE);
|
||||
// start tracking our stats
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
|
||||
_myServer->getOctree()->getRoot(), _myServer->getJurisdiction());
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot());
|
||||
|
||||
preStartNewScene(nodeData, isFullScene);
|
||||
}
|
||||
|
@ -507,7 +506,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
|
||||
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
|
||||
isFullScene, _myServer->getJurisdiction(), nodeData);
|
||||
isFullScene, nodeData);
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
|
||||
|
|
|
@ -237,8 +237,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
|
|||
_debugSending(false),
|
||||
_debugReceiving(false),
|
||||
_verboseDebug(false),
|
||||
_jurisdiction(NULL),
|
||||
_jurisdictionSender(NULL),
|
||||
_octreeInboundPacketProcessor(NULL),
|
||||
_persistThread(NULL),
|
||||
_started(time(0)),
|
||||
|
@ -257,12 +255,6 @@ OctreeServer::~OctreeServer() {
|
|||
delete[] _parsedArgV;
|
||||
}
|
||||
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->terminating();
|
||||
_jurisdictionSender->terminate();
|
||||
_jurisdictionSender->deleteLater();
|
||||
}
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
_octreeInboundPacketProcessor->terminate();
|
||||
|
@ -275,9 +267,6 @@ OctreeServer::~OctreeServer() {
|
|||
_persistThread->deleteLater();
|
||||
}
|
||||
|
||||
delete _jurisdiction;
|
||||
_jurisdiction = NULL;
|
||||
|
||||
// cleanup our tree here...
|
||||
qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]";
|
||||
_tree.reset();
|
||||
|
@ -933,10 +922,6 @@ void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> me
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
_jurisdictionSender->queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_isFinished && !_isShuttingDown) {
|
||||
// these messages are only allowed to come from the domain server, so make sure that is the case
|
||||
|
@ -1111,23 +1096,6 @@ void OctreeServer::readConfiguration() {
|
|||
qDebug() << "statusPort= DISABLED";
|
||||
}
|
||||
|
||||
QString jurisdictionFile;
|
||||
if (readOptionString(QString("jurisdictionFile"), settingsSectionObject, jurisdictionFile)) {
|
||||
qDebug("jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
qDebug("about to readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionFile));
|
||||
qDebug("after readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
} else {
|
||||
QString jurisdictionRoot;
|
||||
bool hasRoot = readOptionString(QString("jurisdictionRoot"), settingsSectionObject, jurisdictionRoot);
|
||||
QString jurisdictionEndNodes;
|
||||
bool hasEndNodes = readOptionString(QString("jurisdictionEndNodes"), settingsSectionObject, jurisdictionEndNodes);
|
||||
|
||||
if (hasRoot || hasEndNodes) {
|
||||
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionRoot), qPrintable(jurisdictionEndNodes));
|
||||
}
|
||||
}
|
||||
|
||||
readOptionBool(QString("verboseDebug"), settingsSectionObject, _verboseDebug);
|
||||
qDebug("verboseDebug=%s", debug::valueOf(_verboseDebug));
|
||||
|
||||
|
@ -1241,7 +1209,6 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
|
||||
|
||||
|
@ -1365,13 +1332,6 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
_persistThread->initialize(true);
|
||||
}
|
||||
|
||||
// set up our jurisdiction broadcaster...
|
||||
if (_jurisdiction) {
|
||||
_jurisdiction->setNodeType(getMyNodeType());
|
||||
}
|
||||
_jurisdictionSender = new JurisdictionSender(_jurisdiction, getMyNodeType());
|
||||
_jurisdictionSender->initialize(true);
|
||||
|
||||
// set up our OctreeServerPacketProcessor
|
||||
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
|
||||
_octreeInboundPacketProcessor->initialize(true);
|
||||
|
@ -1441,10 +1401,6 @@ void OctreeServer::aboutToFinish() {
|
|||
_octreeInboundPacketProcessor->terminating();
|
||||
}
|
||||
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->terminating();
|
||||
}
|
||||
|
||||
// Shut down all the send threads
|
||||
for (auto& it : _sendThreads) {
|
||||
auto& sendThread = *it.second;
|
||||
|
|
|
@ -44,7 +44,6 @@ public:
|
|||
bool wantsVerboseDebug() const { return _verboseDebug; }
|
||||
|
||||
OctreePointer getOctree() { return _tree; }
|
||||
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
|
||||
|
||||
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
|
||||
std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); }
|
||||
|
@ -138,7 +137,6 @@ private slots:
|
|||
void domainSettingsRequestComplete();
|
||||
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
|
||||
void removeSendThread();
|
||||
|
@ -190,8 +188,6 @@ protected:
|
|||
bool _debugReceiving;
|
||||
bool _debugTimestampNow;
|
||||
bool _verboseDebug;
|
||||
JurisdictionMap* _jurisdiction;
|
||||
JurisdictionSender* _jurisdictionSender;
|
||||
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
|
||||
OctreePersistThread* _persistThread;
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
#include <NodeList.h> // for MAX_PACKET_SIZE
|
||||
#include <JurisdictionSender.h>
|
||||
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
|
||||
|
|
|
@ -79,7 +79,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
|
||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
|
@ -283,11 +282,8 @@ void EntityScriptServer::run() {
|
|||
// Setup Script Engine
|
||||
resetEntitiesScriptEngine();
|
||||
|
||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
||||
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->init();
|
||||
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
||||
|
||||
_entityViewer.init();
|
||||
|
||||
|
@ -566,17 +562,6 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> mess
|
|||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
NodeType_t nodeType;
|
||||
message->peekPrimitive(&nodeType);
|
||||
|
||||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::aboutToFinish() {
|
||||
shutdownScriptEngine();
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_GLM)
|
||||
add_dependency_external_projects(glm)
|
||||
find_package(GLM REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
if (ANDROID)
|
||||
set(GLM_INCLUDE_DIRS "${HIFI_ANDROID_PRECOMPILED}/glm/include")
|
||||
else()
|
||||
add_dependency_external_projects(glm)
|
||||
find_package(GLM REQUIRED)
|
||||
endif()
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
endmacro()
|
|
@ -12,10 +12,8 @@ function(JOIN VALUES GLUE OUTPUT)
|
|||
endfunction()
|
||||
|
||||
|
||||
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(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)
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion")
|
||||
|
@ -58,14 +56,6 @@ else ()
|
|||
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||
endif ()
|
||||
|
||||
if (ANDROID)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras)
|
||||
set(PLATFORM_QT_LIBRARIES Qt5::AndroidExtras)
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
|
||||
set(PLATFORM_QT_LIBRARIES Qt5::WebEngine Qt5::WebEngineWidgets)
|
||||
endif ()
|
||||
|
||||
find_package(
|
||||
Qt5 COMPONENTS
|
||||
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
||||
|
@ -82,9 +72,7 @@ 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(
|
||||
|
@ -213,14 +201,14 @@ endif()
|
|||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(
|
||||
shared octree ktx gpu gl gpu-gl procedural model render
|
||||
shared octree ktx gpu gl procedural graphics render
|
||||
pointers
|
||||
recording fbx networking model-networking entities avatars trackers
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer avatars-renderer ui auto-updater midi
|
||||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
${NON_ANDROID_LIBRARIES}
|
||||
${PLATFORM_GL_BACKEND}
|
||||
)
|
||||
|
||||
# include the binary directory of render-utils for shader includes
|
||||
|
|
|
@ -56,29 +56,28 @@
|
|||
|
||||
{
|
||||
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Hips", "to" : "Standard.Hips",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head"},
|
||||
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" },
|
||||
|
||||
|
||||
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
|
||||
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
|
||||
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },
|
||||
|
|
21
interface/resources/icons/tablet-icons/EmoteAppIcon.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
|
||||
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
|
||||
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
|
||||
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
|
||||
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
|
||||
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
|
||||
C19,43.3,19.6,36.4,20.9,29.8z"/>
|
||||
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
|
||||
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
|
||||
C34,59.1,32.8,60,32.3,61.4z"/>
|
||||
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
|
||||
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
|
||||
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
interface/resources/images/preview-privacy.png
Normal file
After Width: | Height: | Size: 52 KiB |
|
@ -110,7 +110,7 @@ Item {
|
|||
}
|
||||
|
||||
function pullFreshValues() {
|
||||
if (Audio.getRecording()) {
|
||||
if (AudioScriptingInterface.getRecording()) {
|
||||
updateRecordingLabel();
|
||||
}
|
||||
|
||||
|
@ -129,14 +129,14 @@ Item {
|
|||
_wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav";
|
||||
// Using controller recording default directory
|
||||
_wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath;
|
||||
if (!Audio.startRecording(_wavFilePath)) {
|
||||
if (!AudioScriptingInterface.startRecording(_wavFilePath)) {
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath}));
|
||||
updateRecordingUI(false);
|
||||
}
|
||||
}
|
||||
|
||||
function stopRecording() {
|
||||
Audio.stopRecording();
|
||||
AudioScriptingInterface.stopRecording();
|
||||
setRecordingLabelOpacity(0.0);
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath}));
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ Item {
|
|||
}
|
||||
|
||||
function toggleRecording() {
|
||||
if (Audio.getRecording()) {
|
||||
if (AudioScriptingInterface.getRecording()) {
|
||||
updateRecordingUI(false);
|
||||
stopRecording();
|
||||
} else {
|
||||
|
|
691
interface/resources/qml/CurrentAPI.qml
Normal file
|
@ -0,0 +1,691 @@
|
|||
//
|
||||
// ScriptAPI.qml
|
||||
//
|
||||
// Created by Luis Cuenca on 12/18/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "styles-uit"
|
||||
import "controls-uit" as HifiControls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
property var hideQtMethods: true
|
||||
property var maxUpdateValues: 20
|
||||
property var maxReloadValues: 200
|
||||
property var apiMembers: []
|
||||
property var membersWithValues: []
|
||||
property var isReloading: false
|
||||
property var evaluatingIdx: -1
|
||||
property Component scrollSlider
|
||||
property Component keyboard
|
||||
|
||||
Rectangle {
|
||||
color: hifi.colors.baseGray
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
FontLoader { id: ralewayRegular; source: pathToFonts + "fonts/Raleway-Regular.ttf"; }
|
||||
|
||||
Timer {
|
||||
id: updateList
|
||||
interval: 200
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
scrollSlider.y = 0;
|
||||
list.contentY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: topBar
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: back;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.backward
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: search.right
|
||||
anchors.leftMargin: 12
|
||||
onClicked: {
|
||||
var text = searchBar.text;
|
||||
var chain = text.split(".");
|
||||
if (chain.length > 2) {
|
||||
var result = chain[0]+".";
|
||||
for (var i = 1; i < chain.length-2; i++) {
|
||||
result += chain[i] + ".";
|
||||
}
|
||||
result += chain[chain.length-2];
|
||||
searchBar.text = result;
|
||||
} else {
|
||||
searchBar.text = (chain.length > 1) ? chain[0] : "";
|
||||
}
|
||||
if (chain.length > 1) {
|
||||
addListElements(searchBar.text);
|
||||
} else {
|
||||
addListElements();
|
||||
}
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: searchBar
|
||||
focus: true
|
||||
isSearchField: true
|
||||
width: parent.width - 112
|
||||
height: 40
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: back.right
|
||||
anchors.leftMargin: 10
|
||||
font.family: firaSansSemiBold.name
|
||||
placeholderText: "Search"
|
||||
onAccepted: {
|
||||
console.log("Enter Pressed");
|
||||
addListElements(searchBar.text);
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus && HMD.mounted) {
|
||||
keyboard.raised = true;
|
||||
} else {
|
||||
keyboard.raised = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: topBar2
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: topBar.bottom
|
||||
anchors.topMargin: 30
|
||||
width: parent.width -60
|
||||
height: 40
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: addMember;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.maximize
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
onClicked: {
|
||||
addNewMember();
|
||||
updateList.start();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: evaluate;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
text: "Eval"
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: addMember.right
|
||||
anchors.leftMargin: 12
|
||||
onClicked: {
|
||||
evaluateMember();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: valueBar
|
||||
isSearchField: false
|
||||
font.pixelSize: 16
|
||||
width: parent.width - 208
|
||||
height: 40
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
font.family: firaSansSemiBold.name
|
||||
placeholderText: "Value"
|
||||
anchors.left: evaluate.right
|
||||
anchors.leftMargin: 12
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus && HMD.mounted) {
|
||||
keyboard.raised = true;
|
||||
} else {
|
||||
keyboard.raised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: reload;
|
||||
enabled: false;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.reload
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.right: update.left
|
||||
anchors.rightMargin: 12
|
||||
onClicked: {
|
||||
reloadListValues();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: update;
|
||||
enabled: false;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.playback_play
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
if (isReloading) {
|
||||
update.glyph = hifi.glyphs.playback_play
|
||||
isReloading = false;
|
||||
stopReload();
|
||||
} else {
|
||||
update.glyph = hifi.glyphs.stop_square
|
||||
isReloading = true;
|
||||
startReload();
|
||||
}
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: membersBackground
|
||||
anchors {
|
||||
left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top;
|
||||
margins: 30
|
||||
}
|
||||
color: hifi.colors.tableBackgroundDark
|
||||
border.color: hifi.colors.lightGray
|
||||
border.width: 2
|
||||
radius: 5
|
||||
|
||||
ListModel {
|
||||
id: memberModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: memberDelegate
|
||||
Item {
|
||||
id: item
|
||||
width: parent.width
|
||||
anchors.left: parent.left
|
||||
height: 26
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Row {
|
||||
id: memberRow
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 10
|
||||
|
||||
FiraSansSemiBold {
|
||||
property var isMainKey: apiType === "class";
|
||||
text: apiMember
|
||||
size: isMainKey ? 17 : 15
|
||||
font.bold: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText
|
||||
MouseArea {
|
||||
width: list.width
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
|
||||
valueBar.text = !apiValue ? "" : apiValue;
|
||||
list.currentIndex = index;
|
||||
evaluatingIdx = index;
|
||||
}
|
||||
onDoubleClicked: {
|
||||
if (apiType === "class") {
|
||||
addListElements(apiMember+".");
|
||||
} else {
|
||||
isolateElement(evaluatingIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FiraSansRegular {
|
||||
text: apiType
|
||||
anchors.left: apiMember.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 13
|
||||
color: hifi.colors.lightGrayText
|
||||
}
|
||||
|
||||
FiraSansRegular {
|
||||
text: !apiValue ? "" : apiValue;
|
||||
anchors.left: apiType.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 14
|
||||
color: hifi.colors.primaryHighlight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: highlight
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: list.left
|
||||
right: scrollBar.left
|
||||
leftMargin: 2
|
||||
rightMargin: 2
|
||||
}
|
||||
color: hifi.colors.primaryHighlight
|
||||
radius: 4
|
||||
z: 10
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: scrollBar.left
|
||||
bottom: parent.bottom
|
||||
topMargin: 2
|
||||
leftMargin: 2
|
||||
bottomMargin: 2
|
||||
}
|
||||
clip: true
|
||||
cacheBuffer: 4000
|
||||
model: memberModel
|
||||
delegate: memberDelegate
|
||||
highlightMoveDuration: 0
|
||||
highlight: highlight
|
||||
onMovementStarted: {
|
||||
scrollSlider.manual = true;
|
||||
}
|
||||
onMovementEnded: {
|
||||
if (list.contentHeight > list.height) {
|
||||
var range = list.contentY/(list.contentHeight-list.height);
|
||||
range = range > 1 ? 1 : range;
|
||||
var idx = Math.round((list.count-1)*range);
|
||||
scrollSlider.positionSlider(idx);
|
||||
}
|
||||
scrollSlider.manual = false;
|
||||
returnToBounds()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: scrollBar
|
||||
|
||||
property bool scrolling: list.contentHeight > list.height
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
margins: 2
|
||||
}
|
||||
width: 22
|
||||
height: parent.height - 4
|
||||
color: hifi.colors.tableScrollBackgroundDark
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
var index = scrollSlider.y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
|
||||
index = Math.round(index);
|
||||
var scrollAmount = Math.round(list.count/10);
|
||||
index = index + (mouse.y <= scrollSlider.y ? -scrollAmount : scrollAmount);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
if (index > list.count - 1) {
|
||||
index = list.count - 1;
|
||||
}
|
||||
scrollSlider.positionSlider(index);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: scrollSlider
|
||||
|
||||
property var manual: false
|
||||
|
||||
function positionSlider(index){
|
||||
y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1);
|
||||
}
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 2
|
||||
width: 18
|
||||
height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height
|
||||
radius: 5
|
||||
color: hifi.colors.tableScrollHandleDark
|
||||
|
||||
visible: scrollBar.scrolling;
|
||||
|
||||
onYChanged: {
|
||||
var index = y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
|
||||
index = Math.round(index);
|
||||
if (!manual) {
|
||||
list.positionViewAtIndex(index, ListView.Visible);
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: scrollSlider
|
||||
drag.axis: Drag.YAxis
|
||||
drag.minimumY: 0
|
||||
drag.maximumY: scrollBar.height - scrollSlider.height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: bottomBar
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: clipboard;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.scriptNew
|
||||
size: 25
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: parent.left
|
||||
onClicked: {
|
||||
var buffer = "";
|
||||
for (var i = 0; i < memberModel.count; i++) {
|
||||
var datarow = memberModel.get(i);
|
||||
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
|
||||
}
|
||||
Window.copyToClipboard(buffer);
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.CheckBox {
|
||||
id: hideQt
|
||||
colorScheme: hifi.checkbox.dark
|
||||
boxSize: 25
|
||||
boxRadius: 3
|
||||
checked: true
|
||||
anchors.left: clipboard.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
onClicked: {
|
||||
hideQtMethods = checked;
|
||||
addListElements();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: hideLabel
|
||||
anchors.left: hideQt.right
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
anchors.margins: 2
|
||||
font.pixelSize: 15
|
||||
text: "Hide Qt Methods"
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: debug;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
text: "Debug Script"
|
||||
width: 120
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 60
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
onClicked: {
|
||||
sendToScript({type: "selectScript"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard;
|
||||
raised: false;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log(event.nativeScanCode);
|
||||
if (event.key == Qt.Key_Left) {
|
||||
keyboard.raised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addNewMember() {
|
||||
apiMembers.push({member: searchBar.text, type: "user", value: valueBar.text, hasValue: true});
|
||||
var data = {'memberIndex': apiMembers.length-1, 'apiMember': searchBar.text, 'apiType':"user", 'apiValue': valueBar.text};
|
||||
memberModel.insert(0, data);
|
||||
computeMembersWithValues();
|
||||
}
|
||||
|
||||
function evaluateMember() {
|
||||
sendToScript({type: "evaluateMember", data:{member: searchBar.text, index: evaluatingIdx}});
|
||||
}
|
||||
|
||||
function getValuesToRefresh() {
|
||||
var valuesToRefresh = [];
|
||||
for (var i = 0; i < membersWithValues.length; i++) {
|
||||
var index = membersWithValues[i];
|
||||
var row = memberModel.get(index);
|
||||
valuesToRefresh.push({index: index, member: (row.apiType == "function()") ? row.apiMember+"()" : row.apiMember, value: row.apiValue});
|
||||
}
|
||||
return valuesToRefresh;
|
||||
}
|
||||
|
||||
|
||||
function reloadListValues(){
|
||||
var valuesToRefresh = getValuesToRefresh();
|
||||
sendToScript({type: "refreshValues", data:valuesToRefresh});
|
||||
}
|
||||
|
||||
function startReload(){
|
||||
var valuesToRefresh = getValuesToRefresh();
|
||||
sendToScript({type: "startRefreshValues", data:valuesToRefresh});
|
||||
}
|
||||
|
||||
function stopReload(){
|
||||
sendToScript({type: "stopRefreshValues"});
|
||||
}
|
||||
|
||||
function refreshValues(data) {
|
||||
var buffer = "";
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var row = memberModel.get(data[i].index);
|
||||
row.apiValue = data[i].value;
|
||||
apiMembers[row.memberIndex].value = data[i].value;
|
||||
memberModel.set(data[i].index, row);
|
||||
buffer += "\n" + apiMembers[row.memberIndex].member + " : " + data[i].value;
|
||||
}
|
||||
print(buffer);
|
||||
}
|
||||
|
||||
|
||||
function fromScript(message) {
|
||||
if (message.type === "methods") {
|
||||
apiMembers = message.data;
|
||||
if (ScriptDiscoveryService.debugScriptUrl != "") {
|
||||
addListElements("GlobalDebugger");
|
||||
if (memberModel.count == 0) {
|
||||
addListElements();
|
||||
}
|
||||
} else {
|
||||
addListElements();
|
||||
}
|
||||
|
||||
} else if (message.type === "debugMethods") {
|
||||
addListElements("GlobalDebugger");
|
||||
} else if (message.type === "refreshValues") {
|
||||
refreshValues(message.data);
|
||||
} else if (message.type === "evaluateMember") {
|
||||
valueBar.text = message.data.value;
|
||||
var selrow = memberModel.get(message.data.index);
|
||||
if (selrow.apiMember === searchBar.text || selrow.apiMember === searchBar.text + "()") {
|
||||
selrow.apiValue = message.data.value;
|
||||
apiMembers[selrow.memberIndex].value = message.data.value;
|
||||
apiMembers[selrow.memberIndex].hasValue = true;
|
||||
memberModel.set(message.data.index, selrow);
|
||||
}
|
||||
} else if (message.type === "selectScript") {
|
||||
if (message.data.length > 0) {
|
||||
ScriptDiscoveryService.debugScriptUrl = message.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getFilterPairs(filter){
|
||||
var filteredArray = [];
|
||||
var filterChain;
|
||||
filterChain = filter.split(" ");
|
||||
for (var i = 0; i < filterChain.length; i++) {
|
||||
filterChain[i] = filterChain[i].toUpperCase();
|
||||
}
|
||||
var matchPairs = [];
|
||||
|
||||
for (var i = 0; i < apiMembers.length; i++) {
|
||||
if (filterChain != undefined) {
|
||||
var found = 0;
|
||||
var memberComp = apiMembers[i].member.toUpperCase();
|
||||
for (var j = 0; j < filterChain.length; j++) {
|
||||
found += memberComp.indexOf(filterChain[j]) >= 0 ? 1 : 0;
|
||||
}
|
||||
if (found === 0) {
|
||||
continue;
|
||||
}
|
||||
matchPairs.push({index: i, found: found, member: apiMembers[i].member});
|
||||
}
|
||||
}
|
||||
|
||||
matchPairs.sort(function(a, b){
|
||||
if(a.found > b.found) return -1;
|
||||
if(a.found < b.found) return 1;
|
||||
if(a.member > b.member) return 1;
|
||||
if(a.member < b.member) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return matchPairs;
|
||||
}
|
||||
|
||||
function isolateElement(index) {
|
||||
var oldElement = memberModel.get(index);
|
||||
var newElement = {memberIndex: oldElement.memberIndex, apiMember: oldElement.apiMember, apiType: oldElement.apiType, apiValue: oldElement.apiValue};
|
||||
membersWithValues = apiMembers[oldElement.memberIndex].hasValue ? [0] : [];
|
||||
memberModel.remove(0, memberModel.count);
|
||||
memberModel.append(newElement);
|
||||
}
|
||||
|
||||
function computeMembersWithValues() {
|
||||
membersWithValues = [];
|
||||
for (var i = 0; i < memberModel.count; i++) {
|
||||
var idx = memberModel.get(i).memberIndex;
|
||||
if (apiMembers[idx].hasValue) {
|
||||
membersWithValues.push(i);
|
||||
}
|
||||
}
|
||||
update.enabled = membersWithValues.length <= maxUpdateValues;
|
||||
reload.enabled = membersWithValues.length <= maxReloadValues;
|
||||
}
|
||||
|
||||
function addListElements(filter) {
|
||||
valueBar.text = "";
|
||||
memberModel.remove(0, memberModel.count);
|
||||
|
||||
var filteredArray = (filter != undefined) ? [] : apiMembers;
|
||||
var matchPairs;
|
||||
if (filter != undefined) {
|
||||
matchPairs = getFilterPairs(filter);
|
||||
for (var i = 0; i < matchPairs.length; i++) {
|
||||
if (matchPairs[i].found < matchPairs[0].found) {
|
||||
break;
|
||||
}
|
||||
var idx = matchPairs[i].index;
|
||||
filteredArray.push(apiMembers[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < filteredArray.length; i++) {
|
||||
var data = {'memberIndex': matchPairs ? matchPairs[i].index : i,
|
||||
'apiMember': filteredArray[i].member,
|
||||
'apiType': filteredArray[i].type,
|
||||
'apiValue': filteredArray[i].value};
|
||||
|
||||
if (hideQtMethods) {
|
||||
var chain = data.apiMember.split(".");
|
||||
var method = chain[chain.length-1];
|
||||
if (method != "destroyed" &&
|
||||
method != "objectName" &&
|
||||
method != "objectNameChanged") {
|
||||
memberModel.append(data);
|
||||
}
|
||||
} else {
|
||||
memberModel.append(data);
|
||||
}
|
||||
}
|
||||
|
||||
computeMembersWithValues();
|
||||
|
||||
if (isReloading) {
|
||||
update.glyph = hifi.glyphs.playback_play
|
||||
isReloading = false;
|
||||
stopReload();
|
||||
}
|
||||
|
||||
if (memberModel.count > 0) {
|
||||
scrollSlider.y = 0;
|
||||
list.contentY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
signal sendToScript(var message);
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
//
|
||||
// ToolWindow.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import "windows"
|
||||
import "controls-uit"
|
||||
import "styles-uit"
|
||||
|
||||
|
||||
ScrollingWindow {
|
||||
id: toolWindow
|
||||
resizable: true
|
||||
objectName: "ToolWindow"
|
||||
destroyOnCloseButton: false
|
||||
destroyOnHidden: false
|
||||
closable: true
|
||||
shown: false
|
||||
title: "Edit"
|
||||
property alias tabView: tabView
|
||||
implicitWidth: 520; implicitHeight: 695
|
||||
minSize: Qt.vector2d(456, 500)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
onParentChanged: {
|
||||
if (parent) {
|
||||
x = 120;
|
||||
y = 120;
|
||||
}
|
||||
}
|
||||
|
||||
onShownChanged: {
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
|
||||
Settings {
|
||||
category: "ToolWindow.Position"
|
||||
property alias x: toolWindow.x
|
||||
property alias y: toolWindow.y
|
||||
}
|
||||
|
||||
Item {
|
||||
id: toolWindowTabViewItem
|
||||
height: pane.scrollHeight
|
||||
width: pane.contentWidth
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
TabView {
|
||||
id: tabView
|
||||
width: pane.contentWidth
|
||||
// Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
|
||||
height: pane.scrollHeight
|
||||
property int tabCount: 0
|
||||
|
||||
Repeater {
|
||||
model: 4
|
||||
Tab {
|
||||
// Force loading of the content even if the tab is not visible
|
||||
// (required for letting the C++ code access the webview)
|
||||
active: true
|
||||
enabled: false
|
||||
property string originalUrl: ""
|
||||
|
||||
WebView {
|
||||
id: webView
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
|
||||
onEnabledChanged: toolWindow.updateVisiblity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
style: TabViewStyle {
|
||||
|
||||
frame: Rectangle { // Background shown before content loads.
|
||||
anchors.fill: parent
|
||||
color: hifi.colors.baseGray
|
||||
}
|
||||
|
||||
frameOverlap: 0
|
||||
|
||||
tab: Rectangle {
|
||||
implicitWidth: text.width
|
||||
implicitHeight: 3 * text.height
|
||||
color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark
|
||||
|
||||
RalewayRegular {
|
||||
id: text
|
||||
text: styleData.title
|
||||
font.capitalization: Font.AllUppercase
|
||||
size: hifi.fontSizes.tabName
|
||||
width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x
|
||||
elide: Text.ElideRight
|
||||
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Rectangle { // Separator.
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: hifi.colors.black
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
visible: styleData.index > 0
|
||||
|
||||
Rectangle {
|
||||
width: 1
|
||||
height: 1
|
||||
color: hifi.colors.baseGray
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Active underline.
|
||||
width: parent.width - (styleData.index > 0 ? 1 : 0)
|
||||
height: 1
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray
|
||||
}
|
||||
}
|
||||
|
||||
tabOverlap: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisiblity() {
|
||||
if (visible) {
|
||||
for (var i = 0; i < tabView.count; ++i) {
|
||||
if (tabView.getTab(i).enabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
shown = false;
|
||||
}
|
||||
}
|
||||
|
||||
function findIndexForUrl(source) {
|
||||
for (var i = 0; i < tabView.count; ++i) {
|
||||
var tab = tabView.getTab(i);
|
||||
if (tab.originalUrl === source) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function findTabForUrl(source) {
|
||||
var index = findIndexForUrl(source);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
return tabView.getTab(index);
|
||||
}
|
||||
|
||||
function showTabForUrl(source, newVisible) {
|
||||
var index = findIndexForUrl(source);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = tabView.getTab(index);
|
||||
if (newVisible) {
|
||||
toolWindow.shown = true
|
||||
tab.enabled = true
|
||||
} else {
|
||||
tab.enabled = false;
|
||||
updateVisiblity();
|
||||
}
|
||||
}
|
||||
|
||||
function findFreeTab() {
|
||||
for (var i = 0; i < tabView.count; ++i) {
|
||||
var tab = tabView.getTab(i);
|
||||
if (tab && (!tab.originalUrl || tab.originalUrl === "")) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function removeTabForUrl(source) {
|
||||
var index = findIndexForUrl(source);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = tabView.getTab(index);
|
||||
tab.title = "";
|
||||
tab.enabled = false;
|
||||
tab.originalUrl = "";
|
||||
tab.item.url = "about:blank";
|
||||
tab.item.enabled = false;
|
||||
tabView.tabCount--;
|
||||
}
|
||||
|
||||
function addWebTab(properties) {
|
||||
if (!properties.source) {
|
||||
console.warn("Attempted to open Web Tool Pane without URL");
|
||||
return;
|
||||
}
|
||||
|
||||
var existingTabIndex = findIndexForUrl(properties.source);
|
||||
if (existingTabIndex >= 0) {
|
||||
var tab = tabView.getTab(existingTabIndex);
|
||||
return tab.item;
|
||||
}
|
||||
|
||||
var freeTabIndex = findFreeTab();
|
||||
if (freeTabIndex === -1) {
|
||||
console.warn("Unable to add new tab");
|
||||
return;
|
||||
}
|
||||
|
||||
if (properties.width) {
|
||||
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||
}
|
||||
|
||||
if (properties.height) {
|
||||
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
|
||||
}
|
||||
|
||||
var tab = tabView.getTab(freeTabIndex);
|
||||
tab.title = properties.title || "Unknown";
|
||||
tab.enabled = true;
|
||||
tab.originalUrl = properties.source;
|
||||
|
||||
var result = tab.item;
|
||||
result.enabled = true;
|
||||
tabView.tabCount++;
|
||||
result.url = properties.source;
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -45,18 +45,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
mouse.accepted = true;
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
||||
webEntity.synthesizeKeyPress(glyph);
|
||||
webEntity.synthesizeKeyPress(glyph, mirrorText);
|
||||
|
||||
if (toggle) {
|
||||
toggled = !toggled;
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: {
|
||||
mouse.accepted = true;
|
||||
}
|
||||
|
@ -94,6 +82,14 @@ Item {
|
|||
|
||||
onReleased: {
|
||||
if (containsMouse) {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
||||
webEntity.synthesizeKeyPress(glyph);
|
||||
webEntity.synthesizeKeyPress(glyph, mirrorText);
|
||||
|
||||
if (toggle) {
|
||||
toggled = !toggled;
|
||||
}
|
||||
keyItem.state = "mouseOver";
|
||||
} else {
|
||||
if (toggled) {
|
||||
|
|
|
@ -24,13 +24,18 @@ TextField {
|
|||
property bool isSearchField: false
|
||||
property string label: ""
|
||||
property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0)
|
||||
property bool hasDefocusedBorder: true;
|
||||
property bool hasRoundedBorder: false
|
||||
property int roundedBorderRadius: 4
|
||||
property bool error: false;
|
||||
property bool hasClearButton: false;
|
||||
property string leftPermanentGlyph: "";
|
||||
property string centerPlaceholderGlyph: "";
|
||||
|
||||
placeholderText: textField.placeholderText
|
||||
|
||||
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
||||
FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; }
|
||||
font.family: firaSansSemiBold.name
|
||||
font.pixelSize: hifi.fontSizes.textFieldInput
|
||||
font.italic: textField.text == ""
|
||||
|
@ -54,6 +59,7 @@ TextField {
|
|||
}
|
||||
|
||||
style: TextFieldStyle {
|
||||
id: style;
|
||||
textColor: {
|
||||
if (isLightColorScheme) {
|
||||
if (textField.activeFocus) {
|
||||
|
@ -98,9 +104,28 @@ TextField {
|
|||
}
|
||||
}
|
||||
border.color: textField.error ? hifi.colors.redHighlight :
|
||||
(textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray))
|
||||
(textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color))
|
||||
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
|
||||
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
|
||||
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0)
|
||||
|
||||
HiFiGlyphs {
|
||||
text: textField.leftPermanentGlyph;
|
||||
color: textColor;
|
||||
size: hifi.fontSizes.textFieldSearchIcon;
|
||||
anchors.left: parent.left;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.leftMargin: hifi.dimensions.textPadding - 2;
|
||||
visible: text;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
text: textField.centerPlaceholderGlyph;
|
||||
color: textColor;
|
||||
size: parent.height;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
visible: text && !textField.focus && textField.text === "";
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.search
|
||||
|
@ -132,7 +157,7 @@ TextField {
|
|||
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
|
||||
selectedTextColor: hifi.colors.black
|
||||
selectionColor: hifi.colors.primaryHighlight
|
||||
padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ Item {
|
|||
onNewViewRequestedCallback: {
|
||||
// desktop is not defined for web-entities or tablet
|
||||
if (typeof desktop !== "undefined") {
|
||||
desktop.openBrowserWindow(request, profile);
|
||||
desktop.openBrowserWindow(request, webViewCoreProfile);
|
||||
} else {
|
||||
tabletRoot.openBrowserWindow(request, profile);
|
||||
tabletRoot.openBrowserWindow(request, webViewCoreProfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,15 @@ ModalWindow {
|
|||
|
||||
signal selectedFile(var file);
|
||||
signal canceled();
|
||||
|
||||
signal selected(int button);
|
||||
function click(button) {
|
||||
clickedButton = button;
|
||||
selected(button);
|
||||
destroy();
|
||||
}
|
||||
|
||||
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Helper " + helper + " drives " + drives);
|
||||
|
||||
|
@ -628,7 +636,10 @@ ModalWindow {
|
|||
case Qt.Key_Backtab:
|
||||
event.accepted = false;
|
||||
break;
|
||||
|
||||
case Qt.Key_Escape:
|
||||
event.accepted = true;
|
||||
root.click(OriginalDialogs.StandardButton.Cancel);
|
||||
break;
|
||||
default:
|
||||
if (addToPrefix(event)) {
|
||||
event.accepted = true
|
||||
|
@ -793,7 +804,11 @@ ModalWindow {
|
|||
case Qt.Key_Home:
|
||||
event.accepted = d.navigateHome();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case Qt.Key_Escape:
|
||||
event.accepted = true;
|
||||
root.click(OriginalDialogs.StandardButton.Cancel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
//
|
||||
// AvatarBrowser.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 30 Aug 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.2
|
||||
|
||||
import "../../windows"
|
||||
import "../../controls-uit"
|
||||
import "../../styles-uit"
|
||||
|
||||
Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
width: 900; height: 700
|
||||
resizable: true
|
||||
modality: Qt.ApplicationModal
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: true
|
||||
property bool punctuationMode: false
|
||||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: Account.metaverseServerURL + "/marketplace?category=avatars"
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: keyboard.top
|
||||
}
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,25 +99,9 @@ Preference {
|
|||
leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0
|
||||
}
|
||||
onClicked: {
|
||||
if (typeof desktop !== "undefined") {
|
||||
// Load dialog via OffscreenUi so that JavaScript EventBridge is available.
|
||||
root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml");
|
||||
root.browser.windowDestroyed.connect(function(){
|
||||
root.browser = null;
|
||||
});
|
||||
} else {
|
||||
root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot);
|
||||
|
||||
// Make dialog modal.
|
||||
tabletRoot.openModal = root.browser;
|
||||
}
|
||||
ApplicationInterface.loadAvatarBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: tabletAvatarBrowserBuilder;
|
||||
TabletAvatarBrowser { }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,6 @@ OriginalDesktop.Desktop {
|
|||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
// The tool window, one instance
|
||||
property alias toolWindow: toolWindow
|
||||
ToolWindow { id: toolWindow }
|
||||
|
||||
Action {
|
||||
text: "Open Browser"
|
||||
shortcut: "Ctrl+B"
|
||||
|
|
|
@ -102,7 +102,7 @@ Column {
|
|||
'include_actions=' + actions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'protocol=' + encodeURIComponent(Window.protocolSignature()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
|
|
|
@ -50,7 +50,7 @@ Item {
|
|||
id: avatarImage
|
||||
visible: profileUrl !== "" && userName !== "";
|
||||
// Size
|
||||
height: isMyCard ? 70 : 42;
|
||||
height: isMyCard ? 84 : 42;
|
||||
width: visible ? height : 0;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: isMyCard ? 0 : 8;
|
||||
|
@ -520,7 +520,7 @@ Item {
|
|||
Slider {
|
||||
id: gainSlider
|
||||
// Size
|
||||
width: thisNameCard.width;
|
||||
width: isMyCard ? thisNameCard.width - 20 : thisNameCard.width;
|
||||
height: 14
|
||||
// Anchors
|
||||
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
|
||||
|
@ -597,18 +597,11 @@ Item {
|
|||
// Function body by Howard Stearns 2017-01-08
|
||||
function goToUserInDomain(avatarUuid) {
|
||||
var avatar = AvatarList.getAvatar(avatarUuid);
|
||||
if (!avatar) {
|
||||
if (!avatar || !avatar.position || !avatar.orientation) {
|
||||
console.log("This avatar is no longer present. goToUserInDomain() failed.");
|
||||
return;
|
||||
}
|
||||
// FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up,
|
||||
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
|
||||
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
|
||||
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
|
||||
// Position avatar 2 metres from the target in the direction that target avatar was facing.
|
||||
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
|
||||
|
||||
// Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch.
|
||||
MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1}));
|
||||
// This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username.
|
||||
MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ Rectangle {
|
|||
// Properties
|
||||
property bool debug: false;
|
||||
property int myCardWidth: width - upperRightInfoContainer.width;
|
||||
property int myCardHeight: 80;
|
||||
property int myCardHeight: 100;
|
||||
property int rowHeight: 60;
|
||||
property int actionButtonWidth: 55;
|
||||
property int locationColumnWidth: 170;
|
||||
|
|
|
@ -26,7 +26,7 @@ Rectangle {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
property var eventBridge;
|
||||
property string title: "Audio Settings - " + Audio.context;
|
||||
property string title: "Audio Settings - " + AudioScriptingInterface.context;
|
||||
signal sendToScript(var message);
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
@ -37,7 +37,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
|
||||
property bool isVR: Audio.context === "VR"
|
||||
property bool isVR: AudioScriptingInterface.context === "VR"
|
||||
property real rightMostInputLevelPos: 0
|
||||
//placeholder for control sizes and paddings
|
||||
//recalculates dynamically in case of UI size is changed
|
||||
|
@ -72,17 +72,17 @@ Rectangle {
|
|||
property bool showPeaks: true;
|
||||
|
||||
function enablePeakValues() {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
if (!enabled && root.showPeaks) {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function disablePeakValues() {
|
||||
root.showPeaks = false;
|
||||
Audio.devices.input.peakValuesEnabled = false;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: enablePeakValues();
|
||||
|
@ -117,10 +117,10 @@ Rectangle {
|
|||
text: qsTr("Mute microphone");
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
isRedCheck: true;
|
||||
checked: Audio.muted;
|
||||
checked: AudioScriptingInterface.muted;
|
||||
onClicked: {
|
||||
Audio.muted = checked;
|
||||
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
|
||||
AudioScriptingInterface.muted = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,10 +130,10 @@ Rectangle {
|
|||
AudioControls.CheckBox {
|
||||
spacing: muteMic.spacing
|
||||
text: qsTr("Enable noise reduction");
|
||||
checked: Audio.noiseReduction;
|
||||
checked: AudioScriptingInterface.noiseReduction;
|
||||
onClicked: {
|
||||
Audio.noiseReduction = checked;
|
||||
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
|
||||
AudioScriptingInterface.noiseReduction = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
|
||||
}
|
||||
}
|
||||
AudioControls.CheckBox {
|
||||
|
@ -184,7 +184,7 @@ Rectangle {
|
|||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.input;
|
||||
model: AudioScriptingInterface.devices.input;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
|
||||
|
@ -204,7 +204,7 @@ Rectangle {
|
|||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setInputDevice(info, bar.currentIndex === 1);
|
||||
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ Rectangle {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
Audio.devices.input.peakValuesAvailable;
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ Rectangle {
|
|||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.output;
|
||||
model: AudioScriptingInterface.devices.output;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ?
|
||||
|
@ -273,7 +273,7 @@ Rectangle {
|
|||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setOutputDevice(info, bar.currentIndex === 1);
|
||||
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ Rectangle {
|
|||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
visible: Audio.muted;
|
||||
visible: AudioScriptingInterface.muted;
|
||||
color: colors.muted;
|
||||
|
||||
text: "MUTED";
|
||||
|
|
|
@ -17,7 +17,7 @@ import QtGraphicalEffects 1.0
|
|||
import TabletScriptingInterface 1.0
|
||||
|
||||
Rectangle {
|
||||
readonly property var level: Audio.inputLevel;
|
||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||
|
||||
property bool standalone: false;
|
||||
property var dragTarget: null;
|
||||
|
@ -60,7 +60,7 @@ Rectangle {
|
|||
hoverEnabled: true;
|
||||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
Audio.muted = !Audio.muted;
|
||||
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
|
@ -82,7 +82,7 @@ Rectangle {
|
|||
readonly property string red: colors.muted;
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
readonly property string icon: Audio.muted ? muted : unmuted;
|
||||
readonly property string icon: AudioScriptingInterface.muted ? muted : unmuted;
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -103,7 +103,7 @@ Rectangle {
|
|||
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
|
||||
|
||||
id: image;
|
||||
source: Audio.muted ? mutedIcon : unmutedIcon;
|
||||
source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
|
||||
|
||||
width: 30;
|
||||
height: 30;
|
||||
|
@ -126,9 +126,9 @@ Rectangle {
|
|||
Item {
|
||||
id: status;
|
||||
|
||||
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
|
||||
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
|
||||
|
||||
visible: Audio.muted;
|
||||
visible: AudioScriptingInterface.muted;
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
|
@ -147,7 +147,7 @@ Rectangle {
|
|||
|
||||
color: parent.color;
|
||||
|
||||
text: Audio.muted ? "MUTED" : "MUTE";
|
||||
text: AudioScriptingInterface.muted ? "MUTED" : "MUTE";
|
||||
font.pointSize: 12;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ RowLayout {
|
|||
}
|
||||
function playSound() {
|
||||
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
|
||||
// FIXME: Audio.playSystemSound should not require position
|
||||
// FIXME: AudioScriptingInterface.playSystemSound should not require position
|
||||
if (sample === null && !isPlaying) {
|
||||
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
|
||||
sample = AudioScriptingInterface.playSystemSound(sound, MyAvatar.qmlPosition);
|
||||
isPlaying = true;
|
||||
sample.finished.connect(reset);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ Rectangle {
|
|||
property string itemName;
|
||||
property string itemId;
|
||||
property string itemHref;
|
||||
property string itemAuthor;
|
||||
property double balanceAfterPurchase;
|
||||
property bool alreadyOwned: false;
|
||||
property int itemPrice: -1;
|
||||
|
@ -81,12 +82,12 @@ Rectangle {
|
|||
if (result.status !== 'success') {
|
||||
failureErrorText.text = result.message;
|
||||
root.activeView = "checkoutFailure";
|
||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
|
||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
|
||||
} else {
|
||||
root.itemHref = result.data.download_url;
|
||||
root.isWearable = result.data.categories.indexOf("Wearables") > -1;
|
||||
root.activeView = "checkoutSuccess";
|
||||
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
|
||||
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -880,6 +881,7 @@ Rectangle {
|
|||
root.itemPrice = message.params.itemPrice;
|
||||
itemHref = message.params.itemHref;
|
||||
referrer = message.params.referrer;
|
||||
itemAuthor = message.params.itemAuthor;
|
||||
setBuyText();
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -145,7 +145,7 @@ Rectangle {
|
|||
// Title text
|
||||
RalewayRegular {
|
||||
id: popText;
|
||||
text: "PROOF OF PURCHASE";
|
||||
text: "Proof of Provenance";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
|
@ -155,7 +155,7 @@ Rectangle {
|
|||
anchors.right: titleBarText.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -182,7 +182,7 @@ Rectangle {
|
|||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
RalewaySemiBold {
|
||||
id: itemName;
|
||||
|
@ -196,7 +196,7 @@ Rectangle {
|
|||
anchors.right: itemNameHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
color: hifi.colors.white;
|
||||
elide: Text.ElideRight;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
@ -205,7 +205,7 @@ Rectangle {
|
|||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
||||
}
|
||||
onEntered: itemName.color = hifi.colors.blueHighlight;
|
||||
onExited: itemName.color = hifi.colors.blueAccent;
|
||||
onExited: itemName.color = hifi.colors.white;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ Rectangle {
|
|||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.lightGray;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: ownedBy;
|
||||
|
@ -236,7 +236,7 @@ Rectangle {
|
|||
anchors.left: ownedByHeader.left;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: hifi.colors.white;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
|
@ -252,7 +252,7 @@ Rectangle {
|
|||
anchors.leftMargin: 6;
|
||||
anchors.right: ownedByHeader.right;
|
||||
// Style
|
||||
color: hifi.colors.lightGray;
|
||||
color: hifi.colors.white;
|
||||
elide: Text.ElideRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ Rectangle {
|
|||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.lightGray;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
id: edition;
|
||||
|
@ -285,7 +285,7 @@ Rectangle {
|
|||
anchors.right: editionHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: hifi.colors.white;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
|
@ -302,7 +302,7 @@ Rectangle {
|
|||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.lightGray;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
AnonymousProRegular {
|
||||
id: dateOfPurchase;
|
||||
|
@ -316,7 +316,7 @@ Rectangle {
|
|||
anchors.right: dateOfPurchaseHeader.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: hifi.colors.white;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
|
@ -349,7 +349,7 @@ Rectangle {
|
|||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.noneBorderless;
|
||||
color: hifi.buttons.noneBorderlessWhite;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
@ -36,6 +36,7 @@ Rectangle {
|
|||
property bool pendingInventoryReply: true;
|
||||
property bool isShowingMyItems: false;
|
||||
property bool isDebuggingFirstUseTutorial: false;
|
||||
property int pendingItemCount: 0;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Connections {
|
||||
|
@ -79,18 +80,22 @@ Rectangle {
|
|||
onInventoryResult: {
|
||||
purchasesReceived = true;
|
||||
|
||||
if (root.pendingInventoryReply) {
|
||||
inventoryTimer.start();
|
||||
}
|
||||
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get purchases", result.message);
|
||||
} else {
|
||||
} else if (!purchasesContentsList.dragging) { // Don't modify the view if the user's scrolling
|
||||
var inventoryResult = processInventoryResult(result.data.assets);
|
||||
|
||||
var currentIndex = purchasesContentsList.currentIndex === -1 ? 0 : purchasesContentsList.currentIndex;
|
||||
purchasesModel.clear();
|
||||
purchasesModel.append(inventoryResult);
|
||||
|
||||
root.pendingItemCount = 0;
|
||||
for (var i = 0; i < purchasesModel.count; i++) {
|
||||
if (purchasesModel.get(i).status === "pending") {
|
||||
root.pendingItemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (previousPurchasesModel.count !== 0) {
|
||||
checkIfAnyItemStatusChanged();
|
||||
} else {
|
||||
|
@ -103,6 +108,12 @@ Rectangle {
|
|||
previousPurchasesModel.append(inventoryResult);
|
||||
|
||||
buildFilteredPurchasesModel();
|
||||
|
||||
purchasesContentsList.positionViewAtIndex(currentIndex, ListView.Beginning);
|
||||
}
|
||||
|
||||
if (root.pendingInventoryReply && root.pendingItemCount > 0) {
|
||||
inventoryTimer.start();
|
||||
}
|
||||
|
||||
root.pendingInventoryReply = false;
|
||||
|
@ -419,6 +430,8 @@ Rectangle {
|
|||
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
|
||||
clip: true;
|
||||
model: filteredPurchasesModel;
|
||||
snapMode: ListView.SnapToItem;
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange;
|
||||
// Anchors
|
||||
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
|
||||
anchors.topMargin: 12;
|
||||
|
|
|
@ -25,7 +25,6 @@ Item {
|
|||
|
||||
id: root;
|
||||
property string keyFilePath;
|
||||
property bool showDebugButtons: true;
|
||||
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
@ -55,37 +54,6 @@ 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;
|
||||
|
|
|
@ -53,7 +53,7 @@ Item {
|
|||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "HIFI COMMERCE - LOGIN";
|
||||
text: "Log in to continue";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
|
|
|
@ -66,7 +66,7 @@ Item {
|
|||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
Commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
|
@ -194,7 +194,7 @@ Item {
|
|||
securityImageSubmitButton.text = "Submitting...";
|
||||
securityImageSubmitButton.enabled = false;
|
||||
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
|
||||
commerce.chooseSecurityImage(securityImagePath);
|
||||
Commerce.chooseSecurityImage(securityImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// SendMoney.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// SendMoney
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Connections {
|
||||
target: Commerce;
|
||||
}
|
||||
|
||||
// "Unavailable"
|
||||
RalewayRegular {
|
||||
text: "You currently cannot send money to other High Fidelity users.";
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -19,8 +19,7 @@ import "../../../styles-uit"
|
|||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
import "../common" as HifiCommerceCommon
|
||||
|
||||
// references XXX from root context
|
||||
import "./sendMoney"
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
@ -316,18 +315,29 @@ Rectangle {
|
|||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
sendToScript(msg);
|
||||
if (msg.method === 'transactionHistory_usernameLinkClicked') {
|
||||
userInfoViewer.url = msg.usernameLink;
|
||||
userInfoViewer.visible = true;
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SendMoney {
|
||||
id: sendMoney;
|
||||
z: 997;
|
||||
visible: root.activeView === "sendMoney";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.fill: parent;
|
||||
parentAppTitleBarHeight: titleBarContainer.height;
|
||||
parentAppNavBarHeight: tabButtonsContainer.height;
|
||||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Security {
|
||||
|
@ -379,7 +389,7 @@ Rectangle {
|
|||
//
|
||||
Item {
|
||||
id: tabButtonsContainer;
|
||||
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange";
|
||||
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep";
|
||||
property int numTabs: 5;
|
||||
// Size
|
||||
width: root.width;
|
||||
|
@ -497,7 +507,7 @@ Rectangle {
|
|||
Rectangle {
|
||||
id: sendMoneyButtonContainer;
|
||||
visible: !walletSetup.visible;
|
||||
color: hifi.colors.black;
|
||||
color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: exchangeMoneyButtonContainer.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
@ -513,7 +523,7 @@ Rectangle {
|
|||
anchors.top: parent.top;
|
||||
anchors.topMargin: -2;
|
||||
// Style
|
||||
color: hifi.colors.lightGray50;
|
||||
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
|
@ -528,12 +538,24 @@ Rectangle {
|
|||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.lightGray50;
|
||||
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: sendMoneyTabMouseArea;
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
root.activeView = "sendMoney";
|
||||
tabButtonsContainer.resetTabButtonColors();
|
||||
}
|
||||
onEntered: parent.color = hifi.colors.blueHighlight;
|
||||
onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
}
|
||||
}
|
||||
|
||||
// "SECURITY" tab button
|
||||
|
@ -665,9 +687,16 @@ Rectangle {
|
|||
// TAB BUTTONS END
|
||||
//
|
||||
|
||||
HifiControls.TabletWebView {
|
||||
id: userInfoViewer;
|
||||
z: 998;
|
||||
anchors.fill: parent;
|
||||
visible: false;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: keyboardContainer;
|
||||
z: 998;
|
||||
z: 999;
|
||||
visible: keyboard.raised;
|
||||
property bool punctuationMode: false;
|
||||
anchors {
|
||||
|
@ -713,6 +742,13 @@ Rectangle {
|
|||
case 'inspectionCertificate_resetCert':
|
||||
// NOP
|
||||
break;
|
||||
case 'updateConnections':
|
||||
sendMoney.updateConnections(message.connections);
|
||||
break;
|
||||
case 'selectRecipient':
|
||||
case 'updateSelectedRecipientUsername':
|
||||
sendMoney.fromScript(message);
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
|
|
|
@ -19,14 +19,30 @@ import "../../../styles-uit"
|
|||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property bool historyReceived: false;
|
||||
property bool initialHistoryReceived: false;
|
||||
property bool historyRequestPending: true;
|
||||
property bool noMoreHistoryData: false;
|
||||
property int pendingCount: 0;
|
||||
property int currentHistoryPage: 1;
|
||||
property var pagesAlreadyAdded: new Array();
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
transactionHistoryModel.clear();
|
||||
Commerce.balance();
|
||||
initialHistoryReceived = false;
|
||||
root.currentHistoryPage = 1;
|
||||
root.noMoreHistoryData = false;
|
||||
root.historyRequestPending = true;
|
||||
Commerce.history(root.currentHistoryPage);
|
||||
} else {
|
||||
refreshTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
@ -36,32 +52,86 @@ Item {
|
|||
}
|
||||
|
||||
onHistoryResult : {
|
||||
historyReceived = true;
|
||||
if (result.status === 'success') {
|
||||
var sameItemCount = 0;
|
||||
tempTransactionHistoryModel.clear();
|
||||
|
||||
tempTransactionHistoryModel.append(result.data.history);
|
||||
|
||||
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
|
||||
if (!transactionHistoryModel.get(i)) {
|
||||
sameItemCount = -1;
|
||||
break;
|
||||
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
|
||||
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
|
||||
sameItemCount++;
|
||||
}
|
||||
}
|
||||
root.initialHistoryReceived = true;
|
||||
root.historyRequestPending = false;
|
||||
|
||||
if (sameItemCount !== tempTransactionHistoryModel.count) {
|
||||
transactionHistoryModel.clear();
|
||||
if (result.status === 'success') {
|
||||
var currentPage = parseInt(result.current_page);
|
||||
|
||||
if (result.data.history.length === 0) {
|
||||
root.noMoreHistoryData = true;
|
||||
console.log("No more data to retrieve from Commerce.history() endpoint.")
|
||||
} else if (root.currentHistoryPage === 1) {
|
||||
var sameItemCount = 0;
|
||||
tempTransactionHistoryModel.clear();
|
||||
|
||||
tempTransactionHistoryModel.append(result.data.history);
|
||||
|
||||
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
|
||||
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
|
||||
if (!transactionHistoryModel.get(i)) {
|
||||
sameItemCount = -1;
|
||||
break;
|
||||
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
|
||||
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
|
||||
sameItemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sameItemCount !== tempTransactionHistoryModel.count) {
|
||||
transactionHistoryModel.clear();
|
||||
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
|
||||
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
|
||||
}
|
||||
calculatePendingAndInvalidated();
|
||||
}
|
||||
} else {
|
||||
if (root.pagesAlreadyAdded.indexOf(currentPage) !== -1) {
|
||||
console.log("Page " + currentPage + " of history has already been added to the list.");
|
||||
} else {
|
||||
// First, add the history result to a temporary model
|
||||
tempTransactionHistoryModel.clear();
|
||||
tempTransactionHistoryModel.append(result.data.history);
|
||||
|
||||
// Make a note that we've already added this page to the model...
|
||||
root.pagesAlreadyAdded.push(currentPage);
|
||||
|
||||
var insertionIndex = 0;
|
||||
// If there's nothing in the model right now, we don't need to modify insertionIndex.
|
||||
if (transactionHistoryModel.count !== 0) {
|
||||
var currentIteratorPage;
|
||||
// Search through the whole transactionHistoryModel and look for the insertion point.
|
||||
// The insertion point is found when the result page from the server is less than
|
||||
// the page that the current item came from, OR when we've reached the end of the whole model.
|
||||
for (var i = 0; i < transactionHistoryModel.count; i++) {
|
||||
currentIteratorPage = transactionHistoryModel.get(i).resultIsFromPage;
|
||||
|
||||
if (currentPage < currentIteratorPage) {
|
||||
insertionIndex = i;
|
||||
break;
|
||||
} else if (i === transactionHistoryModel.count - 1) {
|
||||
insertionIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the results we just got back from the server, setting the "resultIsFromPage"
|
||||
// property of those results and adding them to the main model.
|
||||
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
|
||||
tempTransactionHistoryModel.setProperty(i, "resultIsFromPage", currentPage);
|
||||
transactionHistoryModel.insert(i + insertionIndex, tempTransactionHistoryModel.get(i))
|
||||
}
|
||||
|
||||
calculatePendingAndInvalidated();
|
||||
}
|
||||
calculatePendingAndInvalidated();
|
||||
}
|
||||
}
|
||||
refreshTimer.start();
|
||||
|
||||
// Only auto-refresh if the user hasn't scrolled
|
||||
// and there is more data to grab
|
||||
if (transactionHistory.atYBeginning && !root.noMoreHistoryData) {
|
||||
refreshTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,16 +201,6 @@ Item {
|
|||
color: hifi.colors.white;
|
||||
// Alignment
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
historyReceived = false;
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
} else {
|
||||
refreshTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "balance" text below field
|
||||
|
@ -164,9 +224,12 @@ Item {
|
|||
id: refreshTimer;
|
||||
interval: 4000;
|
||||
onTriggered: {
|
||||
console.log("Refreshing Wallet Home...");
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
if (transactionHistory.atYBeginning) {
|
||||
console.log("Refreshing 1st Page of Recent Activity...");
|
||||
root.historyRequestPending = true;
|
||||
Commerce.balance();
|
||||
Commerce.history(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,14 +260,36 @@ Item {
|
|||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
width: paintedWidth;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Style
|
||||
color: hifi.colors.baseGrayHighlight;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: myPurchasesLink;
|
||||
text: '<font color="#0093C5"><a href="#myPurchases">My Purchases</a></font>';
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: paintedWidth;
|
||||
height: 30;
|
||||
y: 4;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGrayHighlight;
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
|
||||
onLinkActivated: {
|
||||
sendSignalToWallet({method: 'goToPurchases_fromWalletHome'});
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: tempTransactionHistoryModel;
|
||||
}
|
||||
|
@ -219,7 +304,7 @@ Item {
|
|||
anchors.right: parent.right;
|
||||
|
||||
Item {
|
||||
visible: transactionHistoryModel.count === 0 && root.historyReceived;
|
||||
visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived;
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 12;
|
||||
height: parent.height;
|
||||
|
@ -297,8 +382,8 @@ Item {
|
|||
height: visible ? parent.height : 0;
|
||||
|
||||
AnonymousProRegular {
|
||||
id: dateText;
|
||||
text: model.created_at ? getFormattedDate(model.created_at * 1000) : "";
|
||||
id: hfcText;
|
||||
text: model.hfc_text || '';
|
||||
// Style
|
||||
size: 18;
|
||||
anchors.left: parent.left;
|
||||
|
@ -306,7 +391,6 @@ Item {
|
|||
anchors.topMargin: 15;
|
||||
width: 118;
|
||||
height: paintedHeight;
|
||||
color: hifi.colors.blueAccent;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
|
@ -314,20 +398,25 @@ Item {
|
|||
|
||||
AnonymousProRegular {
|
||||
id: transactionText;
|
||||
text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : "";
|
||||
text: model.transaction_text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.transaction_text) : model.transaction_text) : "";
|
||||
size: 18;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 15;
|
||||
anchors.left: dateText.right;
|
||||
anchors.left: hfcText.right;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
|
||||
linkColor: hifi.colors.blueAccent;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.strikeout: model.status === "invalidated";
|
||||
|
||||
onLinkActivated: {
|
||||
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
|
||||
if (link.indexOf("users/") !== -1) {
|
||||
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
|
||||
} else {
|
||||
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,7 +431,12 @@ Item {
|
|||
onAtYEndChanged: {
|
||||
if (transactionHistory.atYEnd) {
|
||||
console.log("User scrolled to the bottom of 'Recent Activity'.");
|
||||
// Grab next page of results and append to model
|
||||
if (!root.historyRequestPending && !root.noMoreHistoryData) {
|
||||
// Grab next page of results and append to model
|
||||
root.historyRequestPending = true;
|
||||
Commerce.history(++root.currentHistoryPage);
|
||||
console.log("Fetching Page " + root.currentHistoryPage + " of Recent Activity...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// ConnectionItem.qml
|
||||
// qml/hifi/commerce/wallet/sendMoney
|
||||
//
|
||||
// ConnectionItem
|
||||
//
|
||||
// Created by Zach Fox on 2018-01-09
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import "../../../../styles-uit"
|
||||
import "../../../../controls-uit" as HifiControlsUit
|
||||
import "../../../../controls" as HifiControls
|
||||
import "../../wallet" as HifiWallet
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property bool isSelected: false;
|
||||
property string userName;
|
||||
property string profilePicUrl;
|
||||
|
||||
height: 75;
|
||||
width: parent.width;
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
// Style
|
||||
color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white;
|
||||
// Size
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
height: root.height;
|
||||
|
||||
Item {
|
||||
id: avatarImage;
|
||||
visible: profileUrl !== "" && userName !== "";
|
||||
// Size
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 36;
|
||||
height: 50;
|
||||
width: visible ? height : 0;
|
||||
clip: true;
|
||||
Image {
|
||||
id: userImage;
|
||||
source: root.profilePicUrl !== "" ? ((0 === root.profilePicUrl.indexOf("http")) ?
|
||||
root.profilePicUrl : (Account.metaverseServerURL + root.profilePicUrl)) : "";
|
||||
mipmap: true;
|
||||
// Anchors
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Item {
|
||||
width: userImage.width;
|
||||
height: userImage.height;
|
||||
Rectangle {
|
||||
anchors.centerIn: parent;
|
||||
width: userImage.width; // This works because userImage is square
|
||||
height: width;
|
||||
radius: width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedImage {
|
||||
source: "../../../../../icons/profilePicLoading.gif"
|
||||
anchors.fill: parent;
|
||||
visible: userImage.status != Image.Ready;
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: userName;
|
||||
anchors.left: avatarImage.right;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: chooseButton.visible ? chooseButton.left : parent.right;
|
||||
anchors.rightMargin: chooseButton.visible ? 10 : 0;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
text: root.userName;
|
||||
elide: Text.ElideRight;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "Choose" button
|
||||
HifiControlsUit.Button {
|
||||
id: chooseButton;
|
||||
visible: root.isSelected;
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 28;
|
||||
height: 35;
|
||||
width: 100;
|
||||
text: "CHOOSE";
|
||||
onClicked: {
|
||||
var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl };
|
||||
sendToSendMoney(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
signal sendToSendMoney(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// RecipientDisplay.qml
|
||||
// qml/hifi/commerce/wallet/sendMoney
|
||||
//
|
||||
// RecipientDisplay
|
||||
//
|
||||
// Created by Zach Fox on 2018-01-11
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../../../styles-uit"
|
||||
import "../../../../controls-uit" as HifiControlsUit
|
||||
import "../../../../controls" as HifiControls
|
||||
import "../../common" as HifiCommerceCommon
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
property bool isDisplayingNearby; // as opposed to 'connections'
|
||||
property string displayName;
|
||||
property string userName;
|
||||
property string profilePic;
|
||||
property string textColor: hifi.colors.white;
|
||||
|
||||
Item {
|
||||
visible: root.isDisplayingNearby;
|
||||
anchors.fill: parent;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: recipientDisplayName;
|
||||
text: root.displayName;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 12;
|
||||
height: parent.height/2;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignBottom;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
text: root.userName;
|
||||
// Anchors
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: recipientDisplayName.anchors.left;
|
||||
anchors.leftMargin: recipientDisplayName.anchors.leftMargin;
|
||||
anchors.right: recipientDisplayName.anchors.right;
|
||||
anchors.rightMargin: recipientDisplayName.anchors.rightMargin;
|
||||
height: parent.height/2;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: !root.isDisplayingNearby;
|
||||
anchors.fill: parent;
|
||||
|
||||
Image {
|
||||
id: userImage;
|
||||
source: root.profilePic;
|
||||
mipmap: true;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: parent.height - 36;
|
||||
width: height;
|
||||
layer.enabled: true;
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Item {
|
||||
width: userImage.width;
|
||||
height: userImage.height;
|
||||
Rectangle {
|
||||
anchors.centerIn: parent;
|
||||
width: userImage.width; // This works because userImage is square
|
||||
height: width;
|
||||
radius: width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
text: root.userName;
|
||||
// Anchors
|
||||
anchors.left: userImage.right;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.right: parent.right;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: parent.height - 4;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
}
|
||||
}
|
1575
interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1398BB;}
|
||||
</style>
|
||||
<path class="st0" d="M256.8,42C138.5,42,42.2,138.3,42.2,256.6s96.3,214.6,214.6,214.6c118.3,0,214.6-96.3,214.6-214.6
|
||||
S375.1,42,256.8,42z M256.8,444.4C153.2,444.4,69,360.1,69,256.6C69,153,153.2,68.7,256.8,68.7c103.6,0,187.8,84.3,187.8,187.8
|
||||
C444.6,360.1,360.4,444.4,256.8,444.4z"/>
|
||||
<circle class="st0" cx="260.6" cy="189.4" r="60.6"/>
|
||||
<path class="st0" d="M306.4,282.6h-87.6c-36.5,0-66.4,30.2-66.4,66.7v33.9c29.3,22.6,65.4,36.1,105,36.1c44.4,0,84.7-17,115.2-44.7
|
||||
v-25.3C372.7,312.7,342.9,282.6,306.4,282.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 58 KiB |
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1398BB;}
|
||||
</style>
|
||||
<path class="st0" d="M144.3,155c-8.7-8.8-16.7-17.1-25.2-25.7c-22.4,25.4-36.6,53.9-43.6,86.2c-26.9,125.7,81.2,241.5,208.6,223.4
|
||||
C385.9,424.4,458,329.2,443.5,228.2c-4.4-30.6-15.4-58.9-34.1-83.9c-1.6-2.2-3.3-4.3-4.7-6.6c-4.4-7.4-3.1-14.9,3-19.5
|
||||
c6-4.5,14.2-3.6,19.2,3.5c8.2,11.7,16.6,23.4,22.9,36.1c41.9,84.9,25.7,181.7-41,248.7c-67,67.4-175.1,81.3-257.1,33.3
|
||||
c-35.3-20.7-63.1-48.6-82.7-84.5c-41-75-31.5-172.5,23.4-237.9c2.2-2.7,4.4-5.4,7.1-8.7c-8.1-8.5-16.1-16.9-24-25.3
|
||||
c-7.2-7.7-7.9-16.1-1.9-22.1c5.9-5.9,15-5.1,22.1,2.4c56.8,59.5,113.6,119,170.4,178.6c7.2,7.6,7.8,15.4,1.9,21.4
|
||||
c-6.1,6.2-14.7,5.5-22-2c-10.2-10.4-20.2-21-30.3-31.5c-17.4,24.7-3.3,62,27.2,72.5c23,7.9,48.8-2,60.7-23.1
|
||||
c1.2-2.1,2.3-4.3,3.7-6.3c4.5-6.6,11.7-8.6,18.3-5c6.6,3.5,8.9,10.5,5.9,18.1c-12.4,31.6-47.9,52.2-82.3,47.7
|
||||
c-36.1-4.8-64.3-32.6-68.4-67.7c-2.4-20.7,2.4-39.7,14.9-57.5c-10.4-10.9-20.8-21.6-31.7-33c-9.7,10.8-16.2,22.7-20.9,35.7
|
||||
c-31.3,86.4,38.9,175.1,130.1,164.3c74-8.7,122.3-80.9,103.3-154.3c-3.4-13.1-1-20.2,7.8-22.7c9.5-2.8,15.2,1.8,19.2,15.4
|
||||
c20.1,67.4-16,144.8-79.8,175.3c-67.5,32.2-147.5,9.7-188.8-49c-37.8-53.8-36.1-127.7,4.7-179.1C141.2,159.4,142.5,157.4,144.3,155z
|
||||
"/>
|
||||
<path class="st0" d="M236.2,262.2c-4.2-13.6,3.4-28.2,16.8-32.4c13.5-4.2,28.1,3.3,32.5,16.8c4.5,13.9-3.1,28.4-17.2,32.7
|
||||
C254.6,283.5,240.4,275.9,236.2,262.2z"/>
|
||||
<path class="st0" d="M319.6,101.9c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
|
||||
C334.5,118.5,323.3,112.9,319.6,101.9z"/>
|
||||
<path class="st0" d="M214.9,66.2c3.2,10.2-2.2,20.5-12.5,23.6c-9.8,3-20-2.3-23.3-12c-3.3-9.9,2.2-20.8,12-24
|
||||
C201.1,50.5,211.8,56.2,214.9,66.2z"/>
|
||||
<path class="st0" d="M230.8,164.6c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
|
||||
C245.7,181.2,234.5,175.6,230.8,164.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#B9B9B9;}
|
||||
.st1{fill:#EE982D;}
|
||||
.st2{fill:#FF630A;}
|
||||
</style>
|
||||
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
|
||||
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
|
||||
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
|
||||
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
|
||||
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
|
||||
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
|
||||
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
|
||||
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
|
||||
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
|
||||
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
|
||||
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
|
||||
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
|
||||
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
|
||||
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
|
||||
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
|
||||
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
|
||||
<circle class="st2" cx="70.9" cy="109.9" r="19"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#B9B9B9;}
|
||||
.st1{fill:#009175;}
|
||||
.st2{fill:#1FC6A6;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
|
||||
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
|
||||
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
|
||||
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
|
||||
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
|
||||
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
|
||||
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
|
||||
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
|
||||
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
|
||||
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
|
||||
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
|
||||
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
|
||||
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
|
||||
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
|
||||
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
|
||||
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
|
||||
<circle class="st2" cx="70.9" cy="109.9" r="23"/>
|
||||
<path class="st3" d="M70.9,127.8c-9.9,0-17.9-8-17.9-17.9s8-17.9,17.9-17.9s17.9,8,17.9,17.9S80.7,127.8,70.9,127.8z M70.9,94.8
|
||||
c-8.3,0-15.1,6.8-15.1,15.1s6.8,15.1,15.1,15.1S86,118.3,86,109.9S79.2,94.8,70.9,94.8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -7,7 +7,7 @@ import "."
|
|||
Overlay {
|
||||
id: root
|
||||
|
||||
Image {
|
||||
AnimatedImage {
|
||||
id: image
|
||||
property bool scaleFix: true
|
||||
property real xStart: 0
|
||||
|
|
|
@ -62,7 +62,8 @@ StackView {
|
|||
|
||||
var callback = rpcCalls[message.id];
|
||||
if (!callback) {
|
||||
console.log('No callback for message fromScript', JSON.stringify(message));
|
||||
// FIXME: We often recieve very long messages here, the logging of which is drastically slowing down the main thread
|
||||
//console.log('No callback for message fromScript', JSON.stringify(message));
|
||||
return;
|
||||
}
|
||||
delete rpcCalls[message.id];
|
||||
|
@ -72,10 +73,14 @@ StackView {
|
|||
Component { id: tabletWebView; TabletWebView {} }
|
||||
Component.onCompleted: {
|
||||
updateLocationText(false);
|
||||
addressLine.focus = !HMD.active;
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
Qt.callLater(function() {
|
||||
addressBarDialog.keyboardEnabled = HMD.active;
|
||||
addressLine.forceActiveFocus();
|
||||
})
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.parentChanged.disconnect(center);
|
||||
|
|
|
@ -23,11 +23,26 @@ Item {
|
|||
property double sortOrder: 100
|
||||
property int stableOrder: 0
|
||||
property var tabletRoot;
|
||||
property var flickable: null
|
||||
property var gridView: null
|
||||
|
||||
property int buttonIndex: -1
|
||||
|
||||
width: 129
|
||||
height: 129
|
||||
|
||||
signal clicked()
|
||||
|
||||
Connections {
|
||||
target: flickable
|
||||
onMovingChanged: {
|
||||
//when flick/move started, and hover is on, clean hove state
|
||||
if (flickable.moving && tabletButton.state.indexOf("hover") !== -1) {
|
||||
tabletButton.state = (tabletButton.isActive) ? "active state" : "base state";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeProperty(key, value) {
|
||||
tabletButton[key] = value;
|
||||
}
|
||||
|
@ -121,8 +136,10 @@ Item {
|
|||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
preventStealing: true
|
||||
preventStealing: false
|
||||
onClicked: {
|
||||
gridView.currentIndex = buttonIndex
|
||||
|
||||
if (tabletButton.inDebugMode) {
|
||||
if (tabletButton.isActive) {
|
||||
tabletButton.isActive = false;
|
||||
|
@ -130,12 +147,15 @@ Item {
|
|||
tabletButton.isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
tabletButton.clicked();
|
||||
if (tabletRoot) {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
gridView.currentIndex = buttonIndex
|
||||
tabletButton.isEntered = true;
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
import "."
|
||||
import "../../styles-uit"
|
||||
import "../audio" as HifiAudio
|
||||
|
@ -10,7 +13,11 @@ Item {
|
|||
id: tablet
|
||||
objectName: "tablet"
|
||||
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
|
||||
property var currentGridItems: null
|
||||
|
||||
focus: true
|
||||
|
||||
Rectangle {
|
||||
id: bgTopBar
|
||||
height: 90
|
||||
|
@ -85,7 +92,6 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
clip: true
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
|
@ -102,55 +108,209 @@ Item {
|
|||
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"
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
clip: false
|
||||
currentIndex: -1
|
||||
property int previousIndex: -1
|
||||
Repeater {
|
||||
id: pageRepeater
|
||||
model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage)
|
||||
onItemAdded: {
|
||||
item.proxyModel.sourceModel = tabletProxy.buttons;
|
||||
item.proxyModel.pageIndex = index;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelData;
|
||||
onPropertiesChanged: {
|
||||
updateProperties();
|
||||
delegate: Item {
|
||||
id: page
|
||||
property TabletButtonsProxyModel proxyModel: TabletButtonsProxyModel {}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
keyNavigationEnabled: false
|
||||
highlightFollowsCurrentItem: false
|
||||
|
||||
property int previousGridIndex: -1
|
||||
|
||||
// true if any of the buttons contains mouse
|
||||
property bool containsMouse: false
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 20
|
||||
leftMargin: 30
|
||||
rightMargin: 30
|
||||
bottomMargin: 0
|
||||
}
|
||||
|
||||
function setButtonState(buttonIndex, buttonstate) {
|
||||
if (buttonIndex < 0 || gridView.contentItem === undefined
|
||||
|| gridView.contentItem.children.length - 1 < buttonIndex) {
|
||||
return;
|
||||
}
|
||||
var itemat = gridView.contentItem.children[buttonIndex].children[0];
|
||||
if (itemat.isActive) {
|
||||
itemat.state = "active state";
|
||||
} else {
|
||||
itemat.state = buttonstate;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
setButtonState(previousGridIndex, "base state");
|
||||
setButtonState(currentIndex, "hover state");
|
||||
previousGridIndex = currentIndex
|
||||
}
|
||||
|
||||
cellWidth: width/3
|
||||
cellHeight: cellWidth
|
||||
flow: GridView.LeftToRight
|
||||
model: page.proxyModel
|
||||
|
||||
delegate: Control {
|
||||
id: wrapper
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
property bool containsMouse: gridView.containsMouse
|
||||
onHoveredChanged: {
|
||||
if (hovered && !gridView.containsMouse) {
|
||||
gridView.containsMouse = true
|
||||
} else {
|
||||
gridView.containsMouse = false
|
||||
}
|
||||
}
|
||||
|
||||
property var proxy: modelData
|
||||
|
||||
TabletButton {
|
||||
id: tabletButton
|
||||
|
||||
// Temporarily disable magnification
|
||||
// scale: wrapper.hovered ? 1.25 : wrapper.containsMouse ? 0.75 : 1.0
|
||||
// Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.Linear } }
|
||||
|
||||
anchors.centerIn: parent
|
||||
gridView: wrapper.GridView.view
|
||||
buttonIndex: page.proxyModel.buttonIndex(uuid);
|
||||
flickable: swipeView.contentItem;
|
||||
onClicked: modelData.clicked()
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
});
|
||||
onCurrentIndexChanged: {
|
||||
if (swipeView.currentIndex < 0
|
||||
|| swipeView.itemAt(swipeView.currentIndex) === null
|
||||
|| swipeView.itemAt(swipeView.currentIndex).children[0] === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentGridItems = swipeView.itemAt(swipeView.currentIndex).children[0];
|
||||
|
||||
currentGridItems.currentIndex = (previousIndex > swipeView.currentIndex ? currentGridItems.count - 1 : 0);
|
||||
previousIndex = currentIndex;
|
||||
}
|
||||
|
||||
hoverEnabled: true
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: pageIndicator.top
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
currentIndex: swipeView.currentIndex
|
||||
visible: swipeView.count > 1
|
||||
|
||||
delegate: Item {
|
||||
width: 15
|
||||
height: 15
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
opacity: index === pageIndicator.currentIndex ? 0.95 : 0.45
|
||||
implicitWidth: index === pageIndicator.currentIndex ? 15 : 10
|
||||
implicitHeight: implicitWidth
|
||||
radius: width/2
|
||||
color: "white"
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interactive: false
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
count: swipeView.count
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
focus = true;
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
if (!currentGridItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
var index = currentGridItems.currentIndex;
|
||||
currentGridItems.moveCurrentIndexRight();
|
||||
if (index === currentGridItems.count - 1 && index === currentGridItems.currentIndex) {
|
||||
if (swipeView.currentIndex < swipeView.count - 1) {
|
||||
swipeView.incrementCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onRightPressed: flickable.moveCurrentIndexRight();
|
||||
Keys.onLeftPressed: flickable.moveCurrentIndexLeft();
|
||||
Keys.onDownPressed: flickable.moveCurrentIndexDown();
|
||||
Keys.onUpPressed: flickable.moveCurrentIndexUp();
|
||||
Keys.onLeftPressed: {
|
||||
if (!currentGridItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
var index = currentGridItems.currentIndex;
|
||||
currentGridItems.moveCurrentIndexLeft();
|
||||
if (index === 0 && index === currentGridItems.currentIndex) {
|
||||
if (swipeView.currentIndex > 0) {
|
||||
swipeView.decrementCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onDownPressed: currentGridItems.moveCurrentIndexDown();
|
||||
Keys.onUpPressed: currentGridItems.moveCurrentIndexUp();
|
||||
Keys.onReturnPressed: {
|
||||
if (flickable.currentItem) {
|
||||
flickable.currentItem.proxy.clicked();
|
||||
if (currentGridItems.currentItem) {
|
||||
currentGridItems.currentItem.proxy.clicked();
|
||||
if (tabletRoot) {
|
||||
tabletRoot.playButtonClickSound();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ Item {
|
|||
anchors.fill: parent
|
||||
id: d
|
||||
objectName: "stack"
|
||||
initialItem: topMenu
|
||||
|
||||
property var menuStack: []
|
||||
property var topMenu: null;
|
||||
|
@ -48,11 +47,15 @@ Item {
|
|||
}
|
||||
|
||||
function pushSource(path) {
|
||||
d.push(Qt.resolvedUrl(path));
|
||||
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
|
||||
d.push(Qt.resolvedUrl("../../" + path));
|
||||
if (d.currentItem.sendToScript !== undefined) {
|
||||
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
|
||||
}
|
||||
d.currentItem.focus = true;
|
||||
d.currentItem.forceActiveFocus();
|
||||
breadcrumbText.text = d.currentItem.title;
|
||||
if (d.currentItem.title !== undefined) {
|
||||
breadcrumbText.text = d.currentItem.title;
|
||||
}
|
||||
if (typeof bgNavBar !== "undefined") {
|
||||
d.currentItem.y = bgNavBar.height;
|
||||
d.currentItem.height -= bgNavBar.height;
|
||||
|
|
|
@ -106,7 +106,7 @@ Item {
|
|||
if (isWebPage) {
|
||||
var webUrl = tabletApps.get(currentApp).appWebUrl;
|
||||
var scriptUrl = tabletApps.get(currentApp).scriptUrl;
|
||||
loadSource("TabletWebView.qml");
|
||||
loadSource("hifi/tablet/TabletWebView.qml");
|
||||
loadWebUrl(webUrl, scriptUrl);
|
||||
} else {
|
||||
loader.load(tabletApps.get(currentApp).appUrl);
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
//
|
||||
// TabletAvatarBrowser.qml
|
||||
//
|
||||
// Created by David Rowe on 14 Mar 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.2
|
||||
|
||||
import "../../../../windows"
|
||||
import "../../../../controls-uit"
|
||||
import "../../../../styles-uit"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "ModelBrowserDialog"
|
||||
|
||||
property string title: "Attachment Model"
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: (Account.metaverseServerURL + "/marketplace?category=avatars")
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: footer.top
|
||||
}
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: footer
|
||||
height: 40
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: keyboard.top
|
||||
}
|
||||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
rightMargin: hifi.dimensions.contentMargin.x
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Cancel"
|
||||
color: hifi.buttons.white
|
||||
onClicked: root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
}
|
BIN
interface/resources/styles/filter.png
Normal file
After Width: | Height: | Size: 244 B |
|
@ -67,8 +67,22 @@ QPushButton#revealLogButton {
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
QPushButton#showAllButton {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background-color: #333333;
|
||||
color: #BBBBBB;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QCheckBox {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #3d3d3d;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
|
@ -77,4 +91,25 @@ QCheckBox::indicator:unchecked {
|
|||
|
||||
QCheckBox::indicator:checked {
|
||||
image: url(styles/checked.svg);
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
text-align: center;
|
||||
background-color: #CCCCCC;
|
||||
color: #3d3d3d;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
QComboBox::drop-down {
|
||||
border-width: 0px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
QComboBox::down-arrow {
|
||||
image: url(styles/filter.png);
|
||||
border-width: 0px;
|
||||
}
|
|
@ -154,11 +154,10 @@
|
|||
#include "scripting/Audio.h"
|
||||
#include "networking/CloseEventSender.h"
|
||||
#include "scripting/TestScriptingInterface.h"
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/AssetMappingsScriptingInterface.h"
|
||||
#include "scripting/ClipboardScriptingInterface.h"
|
||||
#include "scripting/DesktopScriptingInterface.h"
|
||||
#include "scripting/GlobalServicesScriptingInterface.h"
|
||||
#include "scripting/AccountServicesScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "scripting/SettingsScriptingInterface.h"
|
||||
|
@ -191,6 +190,7 @@
|
|||
#include <GPUIdent.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
|
||||
#include <src/scripting/GooglePolyScriptingInterface.h>
|
||||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
|
@ -209,6 +209,7 @@
|
|||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||
#include <DesktopPreviewProvider.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -503,7 +504,13 @@ public:
|
|||
}
|
||||
|
||||
if (message->message == WM_DEVICECHANGE) {
|
||||
Midi::USBchanged(); // re-scan the MIDI bus
|
||||
const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal
|
||||
static float lastTriggerTime = 0.0f;
|
||||
const float deltaSeconds = secTimestampNow() - lastTriggerTime;
|
||||
lastTriggerTime = secTimestampNow();
|
||||
if (deltaSeconds > MIN_DELTA_SECONDS) {
|
||||
Midi::USBchanged(); // re-scan the MIDI bus
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -573,8 +580,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
}
|
||||
};
|
||||
reportAndQuit("--protocolVersion", [&](FILE* fp) {
|
||||
DependencyManager::set<AddressManager>();
|
||||
auto version = DependencyManager::get<AddressManager>()->protocolVersion();
|
||||
auto version = protocolVersionsSignatureBase64();
|
||||
fputs(version.toLatin1().data(), fp);
|
||||
});
|
||||
reportAndQuit("--version", [&](FILE* fp) {
|
||||
|
@ -632,6 +638,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<PointerScriptingInterface>();
|
||||
DependencyManager::set<PickScriptingInterface>();
|
||||
DependencyManager::set<Cursor::Manager>();
|
||||
DependencyManager::set<DesktopPreviewProvider>();
|
||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||
|
@ -698,6 +705,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<EntityScriptClient>();
|
||||
DependencyManager::set<EntityScriptServerLogClient>();
|
||||
DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>();
|
||||
DependencyManager::set<GooglePolyScriptingInterface>();
|
||||
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
|
||||
DependencyManager::set<AvatarBookmarks>();
|
||||
DependencyManager::set<LocationBookmarks>();
|
||||
|
@ -737,6 +745,7 @@ const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 100.0f;
|
|||
const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
||||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false;
|
||||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
|
||||
const QString DEFAULT_CURSOR_NAME = "DEFAULT";
|
||||
|
||||
|
@ -756,6 +765,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT),
|
||||
_desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR),
|
||||
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
|
||||
_preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER),
|
||||
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
|
||||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),
|
||||
|
@ -1189,8 +1199,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
userActivityLogger.logAction("launch", properties);
|
||||
}
|
||||
|
||||
// Tell our entity edit sender about our known jurisdictions
|
||||
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
|
||||
_entityEditSender.setMyAvatar(myAvatar.get());
|
||||
|
||||
// The entity octree will have to know about MyAvatar for the parentJointName import
|
||||
|
@ -1443,7 +1451,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
|
||||
connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived);
|
||||
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
||||
#ifdef HAVE_DDE
|
||||
|
@ -1467,8 +1474,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
applicationUpdater->checkForUpdate();
|
||||
}
|
||||
|
||||
// Now that menu is initialized we can sync myAvatar with it's state.
|
||||
myAvatar->updateMotionBehaviorFromMenu();
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ActionMotorControl, true);
|
||||
|
||||
// FIXME spacemouse code still needs cleanup
|
||||
#if 0
|
||||
|
@ -1852,6 +1858,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
EntityTreeRenderer::setRenderDebugHullsOperator([] {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls);
|
||||
});
|
||||
|
||||
// Preload Tablet sounds
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
|
@ -2091,6 +2100,11 @@ void Application::cleanupBeforeQuit() {
|
|||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
// The PointerManager must be destroyed before the PickManager because when a Pointer is deleted,
|
||||
// it accesses the PickManager to delete its associated Pick
|
||||
DependencyManager::destroy<PointerManager>();
|
||||
DependencyManager::destroy<PickManager>();
|
||||
|
||||
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
|
||||
}
|
||||
|
||||
|
@ -2285,7 +2299,7 @@ void Application::initializeUi() {
|
|||
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/sendMoney/SendMoney.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
|
||||
QUrl{ "hifi/commerce/wallet/WalletSetup.qml" },
|
||||
|
@ -2320,9 +2334,10 @@ void Application::initializeUi() {
|
|||
|
||||
setupPreferences();
|
||||
|
||||
// For some reason there is already an "Application" object in the QML context,
|
||||
// though I can't find it. Hence, "ApplicationInterface"
|
||||
surfaceContext->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
surfaceContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
|
||||
|
||||
|
@ -2373,9 +2388,11 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
|
||||
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
|
||||
|
@ -2441,7 +2458,7 @@ void Application::initializeUi() {
|
|||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
}
|
||||
|
||||
void Application::updateCamera(RenderArgs& renderArgs) {
|
||||
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
PerformanceTimer perfTimer("updateCamera");
|
||||
|
||||
|
@ -2456,6 +2473,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
|
|||
// Using the latter will cause the camera to wobble with idle animations,
|
||||
// or with changes from the face tracker
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
if (isHMDMode()) {
|
||||
mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||
_myCamera.setPosition(extractTranslation(camMat));
|
||||
|
@ -2468,12 +2486,25 @@ void Application::updateCamera(RenderArgs& renderArgs) {
|
|||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
if (isHMDMode()) {
|
||||
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||
_myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat)));
|
||||
_myCamera.setPosition(extractTranslation(hmdWorldMat) +
|
||||
myAvatar->getWorldOrientation() * boomOffset);
|
||||
|
||||
if (!_thirdPersonHMDCameraBoomValid) {
|
||||
const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f);
|
||||
_thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET;
|
||||
_thirdPersonHMDCameraBoomValid = true;
|
||||
}
|
||||
|
||||
glm::mat4 thirdPersonCameraSensorToWorldMatrix = myAvatar->getSensorToWorldMatrix();
|
||||
|
||||
const glm::vec3 cameraPos = myAvatar->getHMDSensorPosition() + _thirdPersonHMDCameraBoom * myAvatar->getBoomLength();
|
||||
glm::mat4 sensorCameraMat = createMatFromQuatAndPos(myAvatar->getHMDSensorOrientation(), cameraPos);
|
||||
glm::mat4 worldCameraMat = thirdPersonCameraSensorToWorldMatrix * sensorCameraMat;
|
||||
|
||||
_myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat)));
|
||||
_myCamera.setPosition(extractTranslation(worldCameraMat));
|
||||
}
|
||||
else {
|
||||
_thirdPersonHMDCameraBoomValid = false;
|
||||
|
||||
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
|
@ -2486,6 +2517,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
|
|||
}
|
||||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
if (isHMDMode()) {
|
||||
auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
|
||||
|
||||
|
@ -2520,6 +2552,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
|
|||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer();
|
||||
if (cameraEntity != nullptr) {
|
||||
if (isHMDMode()) {
|
||||
|
@ -2582,6 +2615,10 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) {
|
|||
updateSystemTabletMode();
|
||||
}
|
||||
|
||||
void Application::setPreferStylusOverLaser(bool value) {
|
||||
_preferStylusOverLaserSetting.set(value);
|
||||
}
|
||||
|
||||
void Application::setPreferAvatarFingerOverStylus(bool value) {
|
||||
_preferAvatarFingerOverStylusSetting.set(value);
|
||||
}
|
||||
|
@ -4279,7 +4316,7 @@ void Application::init() {
|
|||
|
||||
getEntities()->init();
|
||||
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
|
||||
auto dims = item.getDimensions();
|
||||
auto dims = item.getScaledDimensions();
|
||||
auto maxSize = glm::compMax(dims);
|
||||
|
||||
if (maxSize <= 0.0f) {
|
||||
|
@ -4326,8 +4363,9 @@ void Application::updateLOD(float deltaTime) const {
|
|||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
|
||||
lodManager->autoAdjustLOD(deltaTime);
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||
}
|
||||
|
@ -4557,7 +4595,7 @@ void Application::reloadResourceCaches() {
|
|||
_lastQueriedTime = 0;
|
||||
_octreeQuery.incrementConnectionID();
|
||||
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
|
||||
|
@ -4633,7 +4671,7 @@ void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) {
|
|||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
|
||||
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
|
||||
entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
|
||||
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5000,8 +5038,6 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("misc");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
|
@ -5010,11 +5046,13 @@ void Application::update(float deltaTime) {
|
|||
// TODO: break these out into distinct perfTimers when they prove interesting
|
||||
{
|
||||
PROFILE_RANGE(app, "PickManager");
|
||||
PerformanceTimer perfTimer("pickManager");
|
||||
DependencyManager::get<PickManager>()->update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "PointerManager");
|
||||
PerformanceTimer perfTimer("pointerManager");
|
||||
DependencyManager::get<PointerManager>()->update();
|
||||
}
|
||||
|
||||
|
@ -5040,8 +5078,8 @@ void Application::update(float deltaTime) {
|
|||
// Update my voxel servers with my current voxel query...
|
||||
{
|
||||
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
PerformanceTimer perfTimer("queryOctree");
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
|
||||
|
@ -5050,7 +5088,7 @@ void Application::update(float deltaTime) {
|
|||
if (queryIsDue || viewIsDifferentEnough) {
|
||||
_lastQueriedTime = now;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||
}
|
||||
sendAvatarViewFrustum();
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
|
@ -5077,12 +5115,14 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
avatarManager->postUpdate(deltaTime, getMain3DScene());
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("avatarManager/postUpdate");
|
||||
avatarManager->postUpdate(deltaTime, getMain3DScene());
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0);
|
||||
|
||||
PROFILE_RANGE_EX(app, "PostUpdateLambdas", 0xffff0000, (uint64_t)0);
|
||||
PerformanceTimer perfTimer("postUpdateLambdas");
|
||||
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
|
||||
for (auto& iter : _postUpdateLambdas) {
|
||||
iter.second();
|
||||
|
@ -5090,7 +5130,9 @@ void Application::update(float deltaTime) {
|
|||
_postUpdateLambdas.clear();
|
||||
}
|
||||
|
||||
editRenderArgs([this](AppRenderArgs& appRenderArgs) {
|
||||
|
||||
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
|
||||
PerformanceTimer perfTimer("editRenderArgs");
|
||||
appRenderArgs._headPose= getHMDSensorPose();
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
|
@ -5136,7 +5178,7 @@ void Application::update(float deltaTime) {
|
|||
resizeGL();
|
||||
}
|
||||
|
||||
this->updateCamera(appRenderArgs._renderArgs);
|
||||
this->updateCamera(appRenderArgs._renderArgs, deltaTime);
|
||||
appRenderArgs._eyeToWorld = _myCamera.getTransform();
|
||||
appRenderArgs._isStereo = false;
|
||||
|
||||
|
@ -5204,12 +5246,20 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
});
|
||||
|
||||
AnimDebugDraw::getInstance().update();
|
||||
{
|
||||
PerformanceTimer perfTimer("limitless");
|
||||
AnimDebugDraw::getInstance().update();
|
||||
}
|
||||
|
||||
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
|
||||
{
|
||||
PerformanceTimer perfTimer("limitless");
|
||||
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
|
||||
}
|
||||
|
||||
// Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
|
||||
getMain3DScene()->enqueueFrame();
|
||||
{ // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
|
||||
PerformanceTimer perfTimer("enqueueFrame");
|
||||
getMain3DScene()->enqueueFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::sendAvatarViewFrustum() {
|
||||
|
@ -5271,15 +5321,12 @@ int Application::sendNackPackets() {
|
|||
return packetsSent;
|
||||
}
|
||||
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
|
||||
|
||||
if (!_settingsLoaded) {
|
||||
return; // bail early if settings are not loaded
|
||||
}
|
||||
|
||||
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
|
||||
bool wantExtraDebugging = getLogger()->extraDebugging();
|
||||
|
||||
ViewFrustum viewFrustum;
|
||||
copyViewFrustum(viewFrustum);
|
||||
_octreeQuery.setCameraPosition(viewFrustum.getPosition());
|
||||
|
@ -5294,147 +5341,22 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
|
||||
|
||||
// Iterate all of the nodes, and get a count of how many octree servers we have...
|
||||
int totalServers = 0;
|
||||
int inViewServers = 0;
|
||||
int unknownJurisdictionServers = 0;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
totalServers++;
|
||||
auto node = nodeList->soloNodeOfType(serverType);
|
||||
if (node && node->getActiveSocket()) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(getMaxOctreePacketsPerSecond());
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownJurisdictionServers++;
|
||||
} else {
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
// encode the query data
|
||||
auto packetData = reinterpret_cast<unsigned char*>(queryPacket->getPayload());
|
||||
int packetSize = _octreeQuery.getBroadcastData(packetData);
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
if (viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
|
||||
inViewServers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp, "Servers: total %d, in view %d, unknown jurisdiction %d",
|
||||
totalServers, inViewServers, unknownJurisdictionServers);
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
|
||||
int perServerPPS = 0;
|
||||
const int SMALL_BUDGET = 10;
|
||||
int perUnknownServer = SMALL_BUDGET;
|
||||
int totalPPS = getMaxOctreePacketsPerSecond();
|
||||
|
||||
// determine PPS based on number of servers
|
||||
if (inViewServers >= 1) {
|
||||
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
|
||||
// for each unknown jurisdiction server
|
||||
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
|
||||
} else {
|
||||
if (unknownJurisdictionServers > 0) {
|
||||
perUnknownServer = (totalPPS / unknownJurisdictionServers);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
|
||||
}
|
||||
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
bool inView = false;
|
||||
bool unknownView = false;
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownView = true; // assume it's in view
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", assume it's visible.";
|
||||
}
|
||||
} else {
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
|
||||
|
||||
inView = viewFrustum.cubeIntersectsKeyhole(serverBounds);
|
||||
} else if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
|
||||
}
|
||||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
}
|
||||
|
||||
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
|
||||
// If there's only one server, then don't do this, and just let the normal voxel query pass through
|
||||
// as expected... this way, we will actually get a valid scene if there is one to be seen
|
||||
if (totalServers > 1) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
|
||||
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
|
||||
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
|
||||
_octreeQuery.setCameraNearClip(0.1f);
|
||||
_octreeQuery.setCameraFarClip(0.1f);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Using 'minimal' camera position for node" << *node;
|
||||
}
|
||||
} else {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// encode the query data
|
||||
int packetSize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -5549,11 +5471,6 @@ void Application::clearDomainOctreeDetails() {
|
|||
|
||||
resetPhysicsReadyInformation();
|
||||
|
||||
// reset our node to stats and node to jurisdiction maps... since these must be changing...
|
||||
_entityServerJurisdictions.withWriteLock([&] {
|
||||
_entityServerJurisdictions.clear();
|
||||
});
|
||||
|
||||
_octreeServerSceneStats.withWriteLock([&] {
|
||||
_octreeServerSceneStats.clear();
|
||||
});
|
||||
|
@ -5563,7 +5480,7 @@ void Application::clearDomainOctreeDetails() {
|
|||
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
|
||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
|
||||
skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT);
|
||||
|
||||
DependencyManager::get<AnimationCache>()->clearUnusedResources();
|
||||
DependencyManager::get<ModelCache>()->clearUnusedResources();
|
||||
|
@ -5755,8 +5672,6 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
}
|
||||
|
||||
int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
|
||||
// parse the incoming stats datas stick it in a temporary object for now, while we
|
||||
// determine which server it belongs to
|
||||
int statsMessageLength = 0;
|
||||
|
@ -5771,42 +5686,6 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
if (octreeStats.isFullScene()) {
|
||||
_fullSceneReceivedCounter++;
|
||||
}
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
NodeToJurisdictionMap* jurisdiction = nullptr;
|
||||
QString serverType;
|
||||
if (sendingNode->getType() == NodeType::EntityServer) {
|
||||
jurisdiction = &_entityServerJurisdictions;
|
||||
serverType = "Entity";
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
jurisdiction->withReadLock([&] {
|
||||
if (jurisdiction->find(nodeUUID) != jurisdiction->end()) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(octreeStats.getJurisdictionRoot().get(), rootDetails);
|
||||
|
||||
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
|
||||
qPrintable(serverType),
|
||||
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
// store jurisdiction details for later use
|
||||
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
|
||||
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
|
||||
// details from the OctreeSceneStats to construct the JurisdictionMap
|
||||
JurisdictionMap jurisdictionMap;
|
||||
jurisdictionMap.copyContents(octreeStats.getJurisdictionRoot(), octreeStats.getJurisdictionEndNodes());
|
||||
jurisdiction->withWriteLock([&] {
|
||||
(*jurisdiction)[nodeUUID] = jurisdictionMap;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return statsMessageLength;
|
||||
|
@ -5827,7 +5706,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
return !entityServerNode || isPhysicsEnabled();
|
||||
});
|
||||
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// setup the packet sender of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
||||
|
@ -5892,6 +5771,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
@ -5911,10 +5791,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
scriptEngine->registerGlobalObject("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
|
@ -5941,6 +5822,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get<GooglePolyScriptingInterface>().data());
|
||||
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine.data(), steamClient.get()));
|
||||
|
@ -6313,7 +6195,7 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||
} else {
|
||||
static const QUrl url("../../hifi/dialogs/TabletAssetServer.qml");
|
||||
static const QUrl url("../dialogs/TabletAssetServer.qml");
|
||||
tablet->pushOntoStack(url);
|
||||
}
|
||||
}
|
||||
|
@ -6886,7 +6768,7 @@ void Application::loadLODToolsDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->lodTools();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletLODTools.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6898,7 +6780,7 @@ void Application::loadEntityStatisticsDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->octreeStatsDetails();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletEntityStatistics.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletEntityStatistics.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6909,7 +6791,7 @@ void Application::loadDomainConnectionDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->showDomainConnectionDialog();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletDCDialog.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletDCDialog.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6942,6 +6824,15 @@ void Application::loadAddAvatarBookmarkDialog() const {
|
|||
avatarBookmarks->addBookmark();
|
||||
}
|
||||
|
||||
void Application::loadAvatarBrowser() const {
|
||||
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
// construct the url to the marketplace item
|
||||
QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars";
|
||||
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
|
||||
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
|
||||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
// Get a screenshot and save it
|
||||
|
@ -6959,7 +6850,8 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
|
||||
void Application::takeSecondaryCameraSnapshot() {
|
||||
postLambdaEvent([this] {
|
||||
Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
|
||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7523,11 +7415,13 @@ void Application::updateThreadPoolCount() const {
|
|||
}
|
||||
|
||||
void Application::updateSystemTabletMode() {
|
||||
qApp->setProperty(hifi::properties::HMD, isHMDMode());
|
||||
if (isHMDMode()) {
|
||||
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getHmdTabletBecomesToolbarSetting());
|
||||
} else {
|
||||
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getDesktopTabletBecomesToolbarSetting());
|
||||
if (_settingsLoaded) {
|
||||
qApp->setProperty(hifi::properties::HMD, isHMDMode());
|
||||
if (isHMDMode()) {
|
||||
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getHmdTabletBecomesToolbarSetting());
|
||||
} else {
|
||||
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getDesktopTabletBecomesToolbarSetting());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <model/Skybox.h>
|
||||
#include <graphics/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
|
||||
|
@ -146,7 +146,7 @@ public:
|
|||
void initializeGL();
|
||||
void initializeUi();
|
||||
|
||||
void updateCamera(RenderArgs& renderArgs);
|
||||
void updateCamera(RenderArgs& renderArgs, float deltaTime);
|
||||
void paintGL();
|
||||
void resizeGL();
|
||||
|
||||
|
@ -207,7 +207,11 @@ public:
|
|||
void setDesktopTabletBecomesToolbarSetting(bool value);
|
||||
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
|
||||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
|
||||
bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); }
|
||||
void setPreferStylusOverLaser(bool value);
|
||||
// FIXME: Remove setting completely or make available through JavaScript API?
|
||||
//bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
|
||||
bool getPreferAvatarFingerOverStylus() { return false; }
|
||||
void setPreferAvatarFingerOverStylus(bool value);
|
||||
|
||||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
|
@ -228,8 +232,6 @@ public:
|
|||
|
||||
FileLogger* getLogger() const { return _logger; }
|
||||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
||||
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
||||
|
@ -268,7 +270,7 @@ public:
|
|||
void takeSecondaryCameraSnapshot();
|
||||
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
|
||||
|
||||
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
|
||||
gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
|
||||
|
||||
|
@ -307,6 +309,7 @@ public slots:
|
|||
void toggleEntityScriptServerLogDialog();
|
||||
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
|
||||
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
|
||||
Q_INVOKABLE void loadAvatarBrowser() const;
|
||||
Q_INVOKABLE SharedSoundPointer getSampleSound() const;
|
||||
|
||||
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
|
||||
|
@ -450,7 +453,7 @@ private:
|
|||
void updateThreads(float deltaTime);
|
||||
void updateDialogs(float deltaTime) const;
|
||||
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType);
|
||||
|
||||
int sendNackPackets();
|
||||
void sendAvatarViewFrustum();
|
||||
|
@ -553,6 +556,7 @@ private:
|
|||
Setting::Handle<float> _desktopTabletScale;
|
||||
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _preferStylusOverLaserSetting;
|
||||
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
|
||||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
Setting::Handle<QString> _preferredCursor;
|
||||
|
@ -571,7 +575,6 @@ private:
|
|||
StDev _idleLoopStdev;
|
||||
float _idleLoopMeasuredJitter;
|
||||
|
||||
NodeToJurisdictionMap _entityServerJurisdictions;
|
||||
NodeToOctreeSceneStats _octreeServerSceneStats;
|
||||
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
|
||||
QPointer<LogDialog> _logDialog;
|
||||
|
@ -664,7 +667,7 @@ private:
|
|||
|
||||
ConnectionMonitor _connectionMonitor;
|
||||
|
||||
model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
|
||||
graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
|
||||
gpu::TexturePointer _defaultSkyboxTexture;
|
||||
gpu::TexturePointer _defaultSkyboxAmbientTexture;
|
||||
|
||||
|
@ -693,6 +696,9 @@ private:
|
|||
void startHMDStandBySession();
|
||||
void endHMDSession();
|
||||
|
||||
glm::vec3 _thirdPersonHMDCameraBoom { 0.0f, 0.0f, -1.0f };
|
||||
bool _thirdPersonHMDCameraBoomValid { true };
|
||||
|
||||
QUrl _avatarOverrideUrl;
|
||||
bool _saveAvatarOverrideUrl { false };
|
||||
QObject* _renderEventHandler{ nullptr };
|
||||
|
|
|
@ -26,43 +26,50 @@ Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO
|
|||
LODManager::LODManager() {
|
||||
}
|
||||
|
||||
float LODManager::getLODDecreaseFPS() {
|
||||
float LODManager::getLODDecreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODDecreaseFPS();
|
||||
}
|
||||
return getDesktopLODDecreaseFPS();
|
||||
}
|
||||
|
||||
float LODManager::getLODIncreaseFPS() {
|
||||
float LODManager::getLODIncreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODIncreaseFPS();
|
||||
}
|
||||
return getDesktopLODIncreaseFPS();
|
||||
}
|
||||
|
||||
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
|
||||
// We use a "time-weighted running average" of the maxRenderTime 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
|
||||
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // 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
|
||||
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to 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
|
||||
const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
||||
|
||||
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
|
||||
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
|
||||
_presentTime = presentTime;
|
||||
_engineRunTime = engineRunTime;
|
||||
_gpuTime = gpuTime;
|
||||
}
|
||||
|
||||
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||
float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime);
|
||||
// compute time-weighted running average maxRenderTime
|
||||
// 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
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
|
||||
if (!_automaticLODAdjust) {
|
||||
// early exit
|
||||
return;
|
||||
|
@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODDecreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just above the decrease threshold. It will drift close to its
|
||||
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f);
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODIncreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just below the increase threshold. It will drift close to its
|
||||
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f);
|
||||
}
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() {
|
|||
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
float LODManager::getLODLevel() const {
|
||||
// simpleLOD is a linearized and normalized number that represents how much LOD is being applied.
|
||||
// It ranges from:
|
||||
// 1.0 = normal (max) level of detail
|
||||
// 0.0 = min level of detail
|
||||
// In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0.
|
||||
const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||
float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||
float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO;
|
||||
return simpleLOD;
|
||||
}
|
||||
|
||||
const float MIN_DECREASE_FPS = 0.5f;
|
||||
|
||||
void LODManager::setDesktopLODDecreaseFPS(float fps) {
|
||||
|
|
|
@ -37,7 +37,7 @@ class AABox;
|
|||
class LODManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||
|
@ -49,34 +49,56 @@ public:
|
|||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||
|
||||
|
||||
// User Tweakable LOD Items
|
||||
Q_INVOKABLE QString getLODFeedbackText();
|
||||
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
||||
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
||||
|
||||
|
||||
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
Q_INVOKABLE float getLODDecreaseFPS();
|
||||
Q_INVOKABLE float getLODIncreaseFPS();
|
||||
|
||||
|
||||
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||
|
||||
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
||||
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
||||
Q_PROPERTY(float lodLevel READ getLODLevel)
|
||||
|
||||
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
||||
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
||||
|
||||
float getPresentTime() const { return _presentTime; }
|
||||
float getEngineRunTime() const { return _engineRunTime; }
|
||||
float getGPUTime() const { return _gpuTime; }
|
||||
|
||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||
void autoAdjustLOD(float renderTime, float realTimeDelta);
|
||||
|
||||
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
|
||||
void autoAdjustLOD(float realTimeDelta);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void resetLODAdjust();
|
||||
|
||||
|
||||
float getAverageRenderTime() const { return _avgRenderTime; };
|
||||
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
|
||||
float getLODLevel() const;
|
||||
|
||||
signals:
|
||||
void LODIncreased();
|
||||
void LODDecreased();
|
||||
|
||||
|
||||
private:
|
||||
LODManager();
|
||||
|
||||
|
||||
bool _automaticLODAdjust = true;
|
||||
float _avgRenderTime { 0.0f };
|
||||
float _presentTime { 0.0f }; // msec
|
||||
float _engineRunTime { 0.0f }; // msec
|
||||
float _gpuTime { 0.0f }; // msec
|
||||
float _avgRenderTime { 0.0f }; // msec
|
||||
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
||||
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
});
|
||||
|
||||
// Avatar > Size
|
||||
|
@ -309,13 +309,13 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/GeneralPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
|
||||
QString("hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
|
||||
});
|
||||
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
static const QUrl widgetUrl("hifi/dialogs/Audio.qml");
|
||||
static const QUrl tabletUrl("../../hifi/audio/Audio.qml");
|
||||
static const QUrl tabletUrl("hifi/audio/Audio.qml");
|
||||
static const QString name("AudioDialog");
|
||||
qApp->showDialog(widgetUrl, tabletUrl, name);
|
||||
});
|
||||
|
@ -324,14 +324,14 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
QString("hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > LOD...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/LodPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
||||
QString("hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
||||
});
|
||||
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings...");
|
||||
|
@ -364,7 +364,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(developerMenu, "Graphics...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
});
|
||||
|
||||
// Developer > UI >>>
|
||||
|
@ -574,8 +574,6 @@ Menu::Menu() {
|
|||
avatar.get(), SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
|
||||
avatar.get(), SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||
avatar.get(), SLOT(setEnableInverseKinematics(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false,
|
||||
|
@ -589,8 +587,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderDetailedCollision, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawDetailedCollision(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl, 0, true,
|
||||
avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ScriptedMotorControl, 0, true,
|
||||
|
@ -615,7 +613,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(networkMenu, MenuOption::Networking);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/NetworkingPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletNetworkingPreferences.qml"), "NetworkingPreferencesDialog");
|
||||
QString("hifi/tablet/TabletNetworkingPreferences.qml"), "NetworkingPreferencesDialog");
|
||||
});
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0,
|
||||
|
@ -676,7 +674,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AudioBuffers.qml"),
|
||||
QString("../../hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
|
||||
QString("hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
|
||||
});
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
@ -698,7 +696,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned,
|
||||
0, false, drawStatusConfig, SLOT(setShowNetwork(bool)));
|
||||
}
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls, 0, false, qApp->getEntities().data(), SIGNAL(setRenderDebugHulls()));
|
||||
|
||||
// Developer > Ask to Reset Settings
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);
|
||||
|
@ -758,6 +756,14 @@ Menu::Menu() {
|
|||
// Developer > Stats
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
|
||||
|
||||
// Developer > API Debugger
|
||||
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
});
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
|
|
|
@ -194,7 +194,6 @@ namespace MenuOption {
|
|||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
|
|
|
@ -407,7 +407,7 @@ void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &s
|
|||
ShapeInfo::PointList points;
|
||||
pointCollection.push_back(points);
|
||||
|
||||
GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getDimensions(), pointCollection.back());
|
||||
GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getScaledDimensions(), pointCollection.back());
|
||||
shapeInfo.setPointCollection(pointCollection);
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
||||
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
|
||||
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
|
||||
const AvatarSharedPointer& getAvatar() const { return _avatar; }
|
||||
AvatarSharedPointer getAvatar() const { return _avatar; }
|
||||
private:
|
||||
AvatarSharedPointer _avatar;
|
||||
};
|
||||
|
@ -185,7 +185,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
render::Transaction transaction;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const SortableAvatar& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
|
||||
const auto avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
|
||||
|
||||
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
|
||||
if (ignoring) {
|
||||
|
@ -239,7 +239,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
sortedAvatars.pop();
|
||||
while (inView && !sortedAvatars.empty()) {
|
||||
const SortableAvatar& newSortData = sortedAvatars.top();
|
||||
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && newAvatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
|
|
|
@ -424,6 +424,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
emit positionGoneTo();
|
||||
// Run safety tests as soon as we can after goToLocation, or clear if we're not colliding.
|
||||
_physicsSafetyPending = getCollisionsEnabled();
|
||||
_characterController.recomputeFlying(); // In case we've gone to into the sky.
|
||||
}
|
||||
if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) {
|
||||
// When needed and ready, arrange to check and fix.
|
||||
|
@ -536,6 +537,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// we've achived our final adjusted position and rotation for the avatar
|
||||
// and all of its joints, now update our attachements.
|
||||
Avatar::simulateAttachments(deltaTime);
|
||||
relayJointDataToChildren();
|
||||
|
||||
if (!_skeletonModel->hasSkeleton()) {
|
||||
// All the simulation that can be done has been done
|
||||
|
@ -1060,11 +1062,6 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
|||
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
|
||||
}
|
||||
|
||||
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
|
||||
AnimClip::usePreAndPostPoseFromAnim = isEnabled;
|
||||
reset(true);
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||
_skeletonModel->getRig().setEnableInverseKinematics(isEnabled);
|
||||
}
|
||||
|
@ -1928,7 +1925,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
|||
_prevShouldDrawHead = shouldDrawHead;
|
||||
}
|
||||
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
||||
|
||||
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
||||
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
|
||||
|
@ -2022,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
_smoothOrientationTimer = 0.0f;
|
||||
}
|
||||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
|
||||
Head* head = getHead();
|
||||
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
|
||||
|
@ -2035,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
head->setBaseYaw(YAW(euler));
|
||||
head->setBasePitch(PITCH(euler));
|
||||
head->setBaseRoll(ROLL(euler));
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
head->setBaseRoll(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2099,7 +2099,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
_actionMotorVelocity = motorSpeed * direction;
|
||||
} else {
|
||||
// we're interacting with a floor --> simple horizontal speed and exponential decay
|
||||
_actionMotorVelocity = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_WALKING_SPEED * direction;
|
||||
_actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction;
|
||||
}
|
||||
|
||||
float boomChange = getDriveKey(ZOOM);
|
||||
|
@ -2315,6 +2315,19 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
|||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
||||
// Most cases of going to a place or user go through this now. Some possible improvements to think about in the future:
|
||||
// - It would be nice if this used the same teleport steps and smoothing as in the teleport.js script, as long as it
|
||||
// still worked if the target is in the air.
|
||||
// - Sometimes (such as the response from /api/v1/users/:username/location), the location can be stale, but there is a
|
||||
// node_id supplied by which we could update the information after going to the stale location first and "looking around".
|
||||
// This could be passed through AddressManager::goToAddressFromObject => AddressManager::handleViewpoint => here.
|
||||
// The trick is that you have to yield enough time to resolve the node_id.
|
||||
// - Instead of always doing the same thing for shouldFaceLocation -- which places users uncomfortabley on top of each other --
|
||||
// it would be nice to see how many users are already "at" a person or place, and place ourself in semicircle or other shape
|
||||
// around the target. Avatars and entities (specified by the node_id) could define an adjustable "face me" method that would
|
||||
// compute the position (e.g., so that if I'm on stage, going to me would compute an available seat in the audience rather than
|
||||
// being in my face on-stage). Note that this could work for going to an entity as well as to a person.
|
||||
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
|
@ -2678,6 +2691,14 @@ float MyAvatar::getUserEyeHeight() const {
|
|||
return userHeight - userHeight * ratio;
|
||||
}
|
||||
|
||||
float MyAvatar::getWalkSpeed() const {
|
||||
return _walkSpeed.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setWalkSpeed(float value) {
|
||||
_walkSpeed.set(value);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getPositionForAudio() {
|
||||
switch (_audioListenerMode) {
|
||||
case AudioListenerMode::FROM_HEAD:
|
||||
|
@ -2785,14 +2806,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
|||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
auto cameraMode = qApp->getCamera().getMode();
|
||||
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
|
||||
return false;
|
||||
} else {
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
||||
}
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
@ -3152,6 +3168,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const {
|
|||
}
|
||||
|
||||
bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& orientation) {
|
||||
std::lock_guard<std::mutex> guard(_pinnedJointsMutex);
|
||||
auto hipsIndex = getJointIndex("Hips");
|
||||
if (index != hipsIndex) {
|
||||
qWarning() << "Pinning is only supported for the hips joint at the moment.";
|
||||
|
@ -3171,7 +3188,14 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MyAvatar::isJointPinned(int index) {
|
||||
std::lock_guard<std::mutex> guard(_pinnedJointsMutex);
|
||||
auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index);
|
||||
return it != _pinnedJoints.end();
|
||||
}
|
||||
|
||||
bool MyAvatar::clearPinOnJoint(int index) {
|
||||
std::lock_guard<std::mutex> guard(_pinnedJointsMutex);
|
||||
auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index);
|
||||
if (it != _pinnedJoints.end()) {
|
||||
_pinnedJoints.erase(it);
|
||||
|
|
|
@ -163,6 +163,8 @@ class MyAvatar : public Avatar {
|
|||
|
||||
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
|
||||
|
||||
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
|
||||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
const QString DOMINANT_RIGHT_HAND = "right";
|
||||
|
||||
|
@ -448,9 +450,8 @@ public:
|
|||
virtual void clearJointData(const QString& name) override;
|
||||
virtual void clearJointsData() override;
|
||||
|
||||
|
||||
|
||||
Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation);
|
||||
bool isJointPinned(int index);
|
||||
Q_INVOKABLE bool clearPinOnJoint(int index);
|
||||
|
||||
Q_INVOKABLE float getIKErrorOnLastSolve() const;
|
||||
|
@ -558,6 +559,9 @@ public:
|
|||
|
||||
const QUuid& getSelfID() const { return AVATAR_SELF_ID; }
|
||||
|
||||
void setWalkSpeed(float value);
|
||||
float getWalkSpeed() const;
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
|
@ -595,7 +599,6 @@ public slots:
|
|||
|
||||
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||
void setEnableInverseKinematics(bool isEnabled);
|
||||
|
||||
QUrl getAnimGraphOverrideUrl() const; // thread-safe
|
||||
|
@ -837,10 +840,14 @@ private:
|
|||
bool getIsAway() const { return _isAway; }
|
||||
void setAway(bool value);
|
||||
|
||||
std::mutex _pinnedJointsMutex;
|
||||
std::vector<int> _pinnedJoints;
|
||||
|
||||
// height of user in sensor space, when standing erect.
|
||||
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
|
||||
|
||||
// max unscaled forward movement speed
|
||||
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|