diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6956fd22c3..c8710eed05 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,8 +80,28 @@ endif()
if (ANDROID)
set(GLES_OPTION ON)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
+ add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
+ if (
+ (${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
+ (${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
+ (${HIFI_ANDROID_APP} STREQUAL "framePlayer")
+ )
+ # We know the quest hardware has this extension, so we can force the use of instanced stereo
+ add_definitions(-DHAVE_EXT_clip_cull_distance)
+ # We can also use multiview stereo techniques
+ add_definitions(-DHAVE_OVR_multiview2)
+ add_definitions(-DHAVE_OVR_multiview)
+ # We can also use our own foveated textures
+ add_definitions(-DHAVE_QCOM_texture_foveated)
+
+ # if set, the application itself or some library it depends on MUST implement
+ # `DisplayPluginList getDisplayPlugins()` and `InputPluginList getInputPlugins()`
+ add_definitions(-DCUSTOM_INPUT_PLUGINS)
+ add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
+ set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
+ endif()
else ()
- set(PLATFORM_QT_COMPONENTS WebEngine)
+ set(PLATFORM_QT_COMPONENTS WebEngine Xml)
endif ()
if (USE_GLES AND (NOT ANDROID))
diff --git a/android/apps/framePlayer/build.gradle b/android/apps/framePlayer/build.gradle
index fc8651fce1..5bf8176863 100644
--- a/android/apps/framePlayer/build.gradle
+++ b/android/apps/framePlayer/build.gradle
@@ -1,15 +1,8 @@
+import com.android.builder.core.BuilderConstants
+
apply plugin: 'com.android.application'
android {
- signingConfigs {
- release {
- keyAlias 'key0'
- keyPassword 'password'
- storeFile file('C:/android/keystore.jks')
- storePassword 'password'
- }
- }
-
compileSdkVersion 28
defaultConfig {
applicationId "io.highfidelity.frameplayer"
@@ -32,19 +25,17 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- signingConfig signingConfigs.release
+ externalNativeBuild.cmake.path '../../../CMakeLists.txt'
+
+ variantFilter { variant ->
+ def build = variant.buildType.name
+ if (build == BuilderConstants.RELEASE) {
+ variant.setIgnore(true)
}
}
-
- externalNativeBuild.cmake.path '../../../CMakeLists.txt'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
- //implementation project(':oculus')
implementation project(':qt')
}
diff --git a/android/apps/interface/src/main/java/io/highfidelity/gvrinterface/InterfaceActivity.java b/android/apps/interface/src/main/java/io/highfidelity/gvrinterface/InterfaceActivity.java
deleted file mode 100644
index aad769de70..0000000000
--- a/android/apps/interface/src/main/java/io/highfidelity/gvrinterface/InterfaceActivity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// 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());
- }
- }
-
- }
-}
\ No newline at end of file
diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
index b7d2157737..a7bda3c29b 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,6 +32,7 @@ import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.SlidingDrawer;
+import org.qtproject.qt5.android.QtNative;
import org.qtproject.qt5.android.QtLayout;
import org.qtproject.qt5.android.QtSurface;
import org.qtproject.qt5.android.bindings.QtActivity;
@@ -53,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
public static final String DOMAIN_URL = "url";
public static final String EXTRA_GOTO_USERNAME = "gotousername";
private static final String TAG = "Interface";
+ public static final String EXTRA_ARGS = "args";
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
private static final int NORMAL_DPI = 160;
@@ -77,6 +80,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
private boolean nativeEnterBackgroundCallEnqueued = false;
private SlidingDrawer mWebSlidingDrawer;
+ private boolean mStartInDomain;
// private GvrApi gvrApi;
// Opaque native pointer to the Application C++ object.
// This object is owned by the InterfaceActivity instance and passed to the native methods.
@@ -92,8 +96,14 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
public void onCreate(Bundle savedInstanceState) {
super.isLoading = true;
Intent intent = getIntent();
- if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
+ if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
+ } else if (intent.hasExtra(EXTRA_ARGS)) {
+ String args = intent.getStringExtra(EXTRA_ARGS);
+ if (!TextUtils.isEmpty(args)) {
+ mStartInDomain = true;
+ intent.putExtra("applicationArguments", args);
+ }
}
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -124,7 +134,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
getActionBar().hide();
}
});
- startActivity(new Intent(this, SplashActivity.class));
+ Intent splashIntent = new Intent(this, SplashActivity.class);
+ splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain);
+ startActivity(splashIntent);
+
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
headsetStateReceiver = new HeadsetStateReceiver();
}
@@ -166,8 +179,27 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
@Override
protected void onDestroy() {
- super.onDestroy();
nativeOnDestroy();
+ /*
+ cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
+ After Qt upgrade to 5.11 we had a black screen crash after closing the application with
+ the hardware button "Back" and trying to start the app again. It could only be fixed after
+ totally closing the app swiping it in the list of running apps.
+ This problem did not happen with the previous Qt version.
+ After analysing changes we came up with this case and change:
+ https://codereview.qt-project.org/#/c/218882/
+ In summary they've moved libs loading to the same thread as main() and as a matter of correctness
+ in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
+ That exit call is the main reason of this problem.
+
+ In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
+ entire onDestroy method including that thread exit line for other three lines that purposely
+ terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
+ */
+ QtNative.terminateQt();
+ QtNative.setActivity(null, null);
+ System.exit(0);
+ super.onDestroy();
}
@Override
diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
index 78a6421746..ef9876c71a 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java
@@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
@@ -27,9 +28,14 @@ public class PermissionChecker extends Activity {
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
private static final String TAG = "Interface";
+ private static final String EXTRA_ARGS = "args";
+ private String mArgs;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
+
Intent myIntent = new Intent(this, BreakpadUploaderService.class);
startService(myIntent);
if (CHOOSE_AVATAR_ON_STARTUP) {
@@ -76,6 +82,11 @@ public class PermissionChecker extends Activity {
private void launchActivityWithPermissions(){
Intent i = new Intent(this, InterfaceActivity.class);
+
+ if (!TextUtils.isEmpty(mArgs)) {
+ i.putExtra(EXTRA_ARGS, mArgs);
+ }
+
startActivity(i);
finish();
}
diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
index bb42467ace..536bf23603 100644
--- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
+++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java
@@ -7,6 +7,9 @@ import android.view.View;
public class SplashActivity extends Activity {
+ public static final String EXTRA_START_IN_DOMAIN = "start-in-domain";
+ private boolean mStartInDomain;
+
private native void registerLoadCompleteListener();
@Override
@@ -36,13 +39,27 @@ public class SplashActivity extends Activity {
}
public void onAppLoadedComplete() {
- if (HifiUtils.getInstance().isUserLoggedIn()) {
- startActivity(new Intent(this, MainActivity.class));
- } else {
- Intent menuIntent = new Intent(this, LoginMenuActivity.class);
- menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
- startActivity(menuIntent);
+ if (!mStartInDomain) {
+ if (HifiUtils.getInstance().isUserLoggedIn()) {
+ startActivity(new Intent(this, MainActivity.class));
+ } else {
+ Intent menuIntent = new Intent(this, LoginMenuActivity.class);
+ menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
+ startActivity(menuIntent);
+ }
}
SplashActivity.this.finish();
}
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false);
+ }
}
diff --git a/android/apps/questFramePlayer/CMakeLists.txt b/android/apps/questFramePlayer/CMakeLists.txt
new file mode 100644
index 0000000000..5889585a6c
--- /dev/null
+++ b/android/apps/questFramePlayer/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(TARGET_NAME questFramePlayer)
+setup_hifi_library(AndroidExtras)
+link_hifi_libraries(shared ktx shaders gpu gl oculusMobile ${PLATFORM_GL_BACKEND})
+target_include_directories(${TARGET_NAME} PRIVATE ${HIFI_ANDROID_PRECOMPILED}/ovr/VrApi/Include)
+target_link_libraries(${TARGET_NAME} android log m)
+target_opengl()
+target_oculus_mobile()
+
+
diff --git a/android/apps/questFramePlayer/build.gradle b/android/apps/questFramePlayer/build.gradle
new file mode 100644
index 0000000000..0b153af0a9
--- /dev/null
+++ b/android/apps/questFramePlayer/build.gradle
@@ -0,0 +1,43 @@
+import com.android.builder.core.BuilderConstants
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "io.highfidelity.frameplayer"
+ minSdkVersion 25
+ targetSdkVersion 28
+ ndk { abiFilters 'arm64-v8a' }
+ externalNativeBuild {
+ cmake {
+ arguments '-DHIFI_ANDROID=1',
+ '-DHIFI_ANDROID_APP=questFramePlayer',
+ '-DANDROID_TOOLCHAIN=clang',
+ '-DANDROID_STL=c++_shared',
+
+ '-DCMAKE_VERBOSE_MAKEFILE=ON'
+ targets = ['questFramePlayer']
+ }
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ externalNativeBuild.cmake.path '../../../CMakeLists.txt'
+
+ variantFilter { variant ->
+ def build = variant.buildType.name
+ if (build == BuilderConstants.RELEASE) {
+ variant.setIgnore(true)
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
+ implementation project(':oculus')
+ implementation project(':qt')
+}
diff --git a/android/apps/questFramePlayer/proguard-rules.pro b/android/apps/questFramePlayer/proguard-rules.pro
new file mode 100644
index 0000000000..b3c0078513
--- /dev/null
+++ b/android/apps/questFramePlayer/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Android\SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/android/apps/questFramePlayer/src/main/AndroidManifest.xml b/android/apps/questFramePlayer/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..721e8cee89
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/AndroidManifest.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp
new file mode 100644
index 0000000000..ec2986298e
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp
@@ -0,0 +1,25 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 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
+//
+
+#include "PlayerWindow.h"
+
+#include
+
+PlayerWindow::PlayerWindow() {
+ installEventFilter(this);
+ setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
+ setSurfaceType(QSurface::OpenGLSurface);
+ create();
+ showFullScreen();
+ // Ensure the window is visible and the GL context is valid
+ QCoreApplication::processEvents();
+ _renderThread.initialize(this);
+}
+
+PlayerWindow::~PlayerWindow() {
+}
diff --git a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h
new file mode 100644
index 0000000000..e4dd6cef43
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h
@@ -0,0 +1,29 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 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
+//
+
+#pragma once
+#include
+#include
+
+#include
+#include "RenderThread.h"
+
+// Create a simple OpenGL window that renders text in various ways
+class PlayerWindow : public QWindow {
+public:
+ PlayerWindow();
+ virtual ~PlayerWindow();
+
+protected:
+ //bool eventFilter(QObject* obj, QEvent* event) override;
+ //void keyPressEvent(QKeyEvent* event) override;
+
+private:
+ QSettings _settings;
+ RenderThread _renderThread;
+};
diff --git a/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp b/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp
new file mode 100644
index 0000000000..5eabe6b9b1
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp
@@ -0,0 +1,240 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 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
+//
+
+#include "RenderThread.h"
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+static JNIEnv* _env { nullptr };
+static JavaVM* _vm { nullptr };
+static jobject _activity { nullptr };
+
+struct HandController{
+ ovrInputTrackedRemoteCapabilities caps {};
+ ovrInputStateTrackedRemote state {};
+ ovrResult stateResult{ ovrSuccess };
+ ovrTracking tracking {};
+ ovrResult trackingResult{ ovrSuccess };
+
+ void update(ovrMobile* session, double time = 0.0) {
+ const auto& deviceId = caps.Header.DeviceID;
+ stateResult = vrapi_GetCurrentInputState(session, deviceId, &state.Header);
+ trackingResult = vrapi_GetInputTrackingState(session, deviceId, 0.0, &tracking);
+ }
+};
+
+std::vector devices;
+
+extern "C" {
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) {
+ __android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__);
+ return JNI_VERSION_1_6;
+}
+
+
+JNIEXPORT void JNICALL Java_io_highfidelity_frameplayer_QuestQtActivity_nativeOnCreate(JNIEnv* env, jobject obj) {
+ env->GetJavaVM(&_vm);
+ _activity = env->NewGlobalRef(obj);
+}
+}
+
+static const char* FRAME_FILE = "assets:/frames/20190121_1220.json";
+
+static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
+ QImage image;
+ QImageReader(filename.c_str()).read(&image);
+ if (layer > 0) {
+ return;
+ }
+ texture->assignStoredMip(0, image.byteCount(), image.constBits());
+}
+
+void RenderThread::submitFrame(const gpu::FramePointer& frame) {
+ std::unique_lock lock(_frameLock);
+ _pendingFrames.push(frame);
+}
+
+void RenderThread::move(const glm::vec3& v) {
+ std::unique_lock lock(_frameLock);
+ _correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
+}
+
+void RenderThread::initialize(QWindow* window) {
+ std::unique_lock lock(_frameLock);
+ setObjectName("RenderThread");
+ Parent::initialize();
+ _window = window;
+ _thread->setObjectName("RenderThread");
+}
+
+void RenderThread::setup() {
+ // Wait until the context has been moved to this thread
+ { std::unique_lock lock(_frameLock); }
+
+
+ ovr::VrHandler::initVr();
+ __android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity");
+ _vm->AttachCurrentThread(&_env, nullptr);
+ jclass cls = _env->GetObjectClass(_activity);
+ jmethodID mid = _env->GetMethodID(cls, "launchOculusActivity", "()V");
+ _env->CallVoidMethod(_activity, mid);
+ __android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity done");
+ ovr::VrHandler::setHandler(this);
+
+ makeCurrent();
+
+ // GPU library init
+ gpu::Context::init();
+ _gpuContext = std::make_shared();
+ _backend = _gpuContext->getBackend();
+ _gpuContext->beginFrame();
+ _gpuContext->endFrame();
+
+ makeCurrent();
+ glGenTextures(1, &_externalTexture);
+ glBindTexture(GL_TEXTURE_2D, _externalTexture);
+ static const glm::u8vec4 color{ 0,1,0,0 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
+
+ if (QFileInfo(FRAME_FILE).exists()) {
+ auto frame = gpu::readFrame(FRAME_FILE, _externalTexture, &textureLoader);
+ submitFrame(frame);
+ }
+}
+
+void RenderThread::shutdown() {
+ _activeFrame.reset();
+ while (!_pendingFrames.empty()) {
+ _gpuContext->consumeFrameUpdates(_pendingFrames.front());
+ _pendingFrames.pop();
+ }
+ _gpuContext->shutdown();
+ _gpuContext.reset();
+}
+
+void RenderThread::handleInput() {
+ static std::once_flag once;
+ std::call_once(once, [&]{
+ withOvrMobile([&](ovrMobile* session){
+ int deviceIndex = 0;
+ ovrInputCapabilityHeader capsHeader;
+ while (vrapi_EnumerateInputDevices(session, deviceIndex, &capsHeader) >= 0) {
+ if (capsHeader.Type == ovrControllerType_TrackedRemote) {
+ HandController controller = {};
+ controller.caps.Header = capsHeader;
+ controller.state.Header.ControllerType = ovrControllerType_TrackedRemote;
+ vrapi_GetInputDeviceCapabilities( session, &controller.caps.Header);
+ devices.push_back(controller);
+ }
+ ++deviceIndex;
+ }
+ });
+ });
+
+ auto readResult = ovr::VrHandler::withOvrMobile([&](ovrMobile *session) {
+ for (auto &controller : devices) {
+ controller.update(session);
+ }
+ });
+
+ if (readResult) {
+ for (auto &controller : devices) {
+ const auto &caps = controller.caps;
+ if (controller.stateResult >= 0) {
+ const auto &remote = controller.state;
+ if (remote.Joystick.x != 0.0f || remote.Joystick.y != 0.0f) {
+ glm::vec3 translation;
+ float rotation = 0.0f;
+ if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) {
+ translation = glm::vec3{0.0f, -remote.Joystick.y, 0.0f};
+ } else {
+ translation = glm::vec3{remote.Joystick.x, 0.0f, -remote.Joystick.y};
+ }
+ float scale = 0.1f + (1.9f * remote.GripTrigger);
+ _correction = glm::translate(glm::mat4(), translation * scale) * _correction;
+ }
+ }
+ }
+ }
+}
+
+void RenderThread::renderFrame() {
+ GLuint finalTexture = 0;
+ glm::uvec2 finalTextureSize;
+ const auto& tracking = beginFrame();
+ if (_activeFrame) {
+ const auto& frame = _activeFrame;
+ auto& eyeProjections = frame->stereoState._eyeProjections;
+ auto& eyeOffsets = frame->stereoState._eyeViews;
+ // Quest
+ auto frameCorrection = _correction * ovr::toGlm(tracking.HeadPose.Pose);
+ _backend->setCameraCorrection(glm::inverse(frameCorrection), frame->view);
+ ovr::for_each_eye([&](ovrEye eye){
+ const auto& eyeInfo = tracking.Eye[eye];
+ eyeProjections[eye] = ovr::toGlm(eyeInfo.ProjectionMatrix);
+ eyeOffsets[eye] = ovr::toGlm(eyeInfo.ViewMatrix);
+ });
+ _backend->recycle();
+ _backend->syncCache();
+ _gpuContext->enableStereo(true);
+ if (frame && !frame->batches.empty()) {
+ _gpuContext->executeFrame(frame);
+ }
+ auto& glbackend = (gpu::gl::GLBackend&)(*_backend);
+ finalTextureSize = { frame->framebuffer->getWidth(), frame->framebuffer->getHeight() };
+ finalTexture = glbackend.getTextureID(frame->framebuffer->getRenderBuffer(0));
+ }
+ presentFrame(finalTexture, finalTextureSize, tracking);
+}
+
+bool RenderThread::process() {
+ pollTask();
+
+ if (!vrActive()) {
+ QThread::msleep(1);
+ return true;
+ }
+
+ std::queue pendingFrames;
+ {
+ std::unique_lock lock(_frameLock);
+ pendingFrames.swap(_pendingFrames);
+ }
+
+ makeCurrent();
+ while (!pendingFrames.empty()) {
+ _activeFrame = pendingFrames.front();
+ pendingFrames.pop();
+ _gpuContext->consumeFrameUpdates(_activeFrame);
+ _activeFrame->stereoState._enable = true;
+ }
+
+ handleInput();
+ renderFrame();
+ return true;
+}
diff --git a/android/apps/questFramePlayer/src/main/cpp/RenderThread.h b/android/apps/questFramePlayer/src/main/cpp/RenderThread.h
new file mode 100644
index 0000000000..701cd25f5b
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/cpp/RenderThread.h
@@ -0,0 +1,44 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 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
+//
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class RenderThread : public GenericThread, ovr::VrHandler {
+ using Parent = GenericThread;
+public:
+ QWindow* _window{ nullptr };
+ std::mutex _mutex;
+ gpu::ContextPointer _gpuContext; // initialized during window creation
+ std::shared_ptr _backend;
+ std::atomic _presentCount{ 0 };
+ std::mutex _frameLock;
+ std::queue _pendingFrames;
+ gpu::FramePointer _activeFrame;
+ uint32_t _externalTexture{ 0 };
+ glm::mat4 _correction;
+
+ void move(const glm::vec3& v);
+ void setup() override;
+ bool process() override;
+ void shutdown() override;
+
+ void handleInput();
+
+ void submitFrame(const gpu::FramePointer& frame);
+ void initialize(QWindow* window);
+ void renderFrame();
+};
diff --git a/android/apps/questFramePlayer/src/main/cpp/main.cpp b/android/apps/questFramePlayer/src/main/cpp/main.cpp
new file mode 100644
index 0000000000..4730d3fa15
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/cpp/main.cpp
@@ -0,0 +1,56 @@
+//
+// Created by Bradley Austin Davis on 2018/11/22
+// Copyright 2014 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
+//
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include "PlayerWindow.h"
+
+void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
+ if (!message.isEmpty()) {
+ const char * local=message.toStdString().c_str();
+ switch (type) {
+ case QtDebugMsg:
+ __android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
+ break;
+ case QtInfoMsg:
+ __android_log_write(ANDROID_LOG_INFO,"Interface",local);
+ break;
+ case QtWarningMsg:
+ __android_log_write(ANDROID_LOG_WARN,"Interface",local);
+ break;
+ case QtCriticalMsg:
+ __android_log_write(ANDROID_LOG_ERROR,"Interface",local);
+ break;
+ case QtFatalMsg:
+ default:
+ __android_log_write(ANDROID_LOG_FATAL,"Interface",local);
+ abort();
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ setupHifiApplication("gpuFramePlayer");
+ QGuiApplication app(argc, argv);
+ auto oldMessageHandler = qInstallMessageHandler(messageHandler);
+ DependencyManager::set();
+ PlayerWindow window;
+ __android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec");
+ app.exec();
+ __android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done");
+ qInstallMessageHandler(oldMessageHandler);
+ return 0;
+}
+
+
diff --git a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java
new file mode 100644
index 0000000000..d498e27547
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java
@@ -0,0 +1,53 @@
+//
+// Created by Bradley Austin Davis on 2018/11/20
+// Copyright 2013-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
+//
+package io.highfidelity.frameplayer;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.qtproject.qt5.android.bindings.QtActivity;
+
+import io.highfidelity.oculus.OculusMobileActivity;
+
+
+public class QuestQtActivity extends QtActivity {
+ private native void nativeOnCreate();
+ private boolean launchedQuestMode = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.w("QQQ_Qt", "QuestQtActivity::onCreate");
+ super.onCreate(savedInstanceState);
+ nativeOnCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.w("QQQ_Qt", "QuestQtActivity::onDestroy");
+ super.onDestroy();
+ }
+
+ public void launchOculusActivity() {
+ Log.w("QQQ_Qt", "QuestQtActivity::launchOculusActivity");
+ runOnUiThread(()->{
+ keepInterfaceRunning = true;
+ launchedQuestMode = true;
+ moveTaskToBack(true);
+ startActivity(new Intent(this, QuestRenderActivity.class));
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (launchedQuestMode) {
+ moveTaskToBack(true);
+ }
+ }
+}
diff --git a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java
new file mode 100644
index 0000000000..a395a32b68
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java
@@ -0,0 +1,14 @@
+package io.highfidelity.frameplayer;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import io.highfidelity.oculus.OculusMobileActivity;
+
+public class QuestRenderActivity extends OculusMobileActivity {
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ startActivity(new Intent(this, QuestQtActivity.class));
+ }
+}
diff --git a/android/apps/questFramePlayer/src/main/res/drawable/ic_launcher.xml b/android/apps/questFramePlayer/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000000..03b1edc4e9
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/apps/questFramePlayer/src/main/res/values/strings.xml b/android/apps/questFramePlayer/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..8bf550f74e
--- /dev/null
+++ b/android/apps/questFramePlayer/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ GPU Frame Player
+
diff --git a/android/docker/Dockerfile b/android/docker/Dockerfile
index c37f73cb2a..fe3a83950a 100644
--- a/android/docker/Dockerfile
+++ b/android/docker/Dockerfile
@@ -73,13 +73,10 @@ RUN mkdir "$HIFI_BASE" && \
RUN git clone https://github.com/jherico/hifi.git && \
cd ~/hifi && \
- git checkout feature/quest_move_interface
+ git checkout feature/quest_frame_player
WORKDIR /home/jenkins/hifi
-RUN touch .test6 && \
- git fetch && git reset origin/feature/quest_move_interface --hard
-
RUN mkdir build
# Pre-cache the vcpkg managed dependencies
diff --git a/android/libraries/oculus/build.gradle b/android/libraries/oculus/build.gradle
new file mode 100644
index 0000000000..b072f99eb7
--- /dev/null
+++ b/android/libraries/oculus/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 24
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
diff --git a/android/libraries/oculus/src/main/AndroidManifest.xml b/android/libraries/oculus/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..57df1a4226
--- /dev/null
+++ b/android/libraries/oculus/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java
new file mode 100644
index 0000000000..01d74ea94d
--- /dev/null
+++ b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java
@@ -0,0 +1,103 @@
+//
+// Created by Bradley Austin Davis on 2018/11/20
+// Copyright 2013-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
+//
+package io.highfidelity.oculus;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+
+/**
+ * Contains a native surface and forwards the activity lifecycle and surface lifecycle
+ * events to the OculusMobileDisplayPlugin
+ */
+public class OculusMobileActivity extends Activity implements SurfaceHolder.Callback {
+ private static final String TAG = OculusMobileActivity.class.getSimpleName();
+ static { System.loadLibrary("oculusMobile"); }
+ private native void nativeOnCreate();
+ private native static void nativeOnResume();
+ private native static void nativeOnPause();
+ private native static void nativeOnDestroy();
+ private native static void nativeOnSurfaceChanged(Surface s);
+
+ private SurfaceView mView;
+ private SurfaceHolder mSurfaceHolder;
+
+
+ public static void launch(Activity activity) {
+ if (activity != null) {
+ activity.runOnUiThread(()->{
+ activity.startActivity(new Intent(activity, OculusMobileActivity.class));
+ });
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.w(TAG, "QQQ onCreate");
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ // Create a native surface for VR rendering (Qt GL surfaces are not suitable
+ // because of the lack of fine control over the surface callbacks)
+ mView = new SurfaceView(this);
+ setContentView(mView);
+ mView.getHolder().addCallback(this);
+
+ // Forward the create message to the JNI code
+ nativeOnCreate();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.w(TAG, "QQQ onDestroy");
+ if (mSurfaceHolder != null) {
+ nativeOnSurfaceChanged(null);
+ }
+ nativeOnDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onResume() {
+ Log.w(TAG, "QQQ onResume");
+ super.onResume();
+ nativeOnResume();
+ }
+
+ @Override
+ protected void onPause() {
+ Log.w(TAG, "QQQ onPause");
+ nativeOnPause();
+ super.onPause();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.w(TAG, "QQQ surfaceCreated");
+ nativeOnSurfaceChanged(holder.getSurface());
+ mSurfaceHolder = holder;
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.w(TAG, "QQQ surfaceChanged");
+ nativeOnSurfaceChanged(holder.getSurface());
+ mSurfaceHolder = holder;
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.w(TAG, "QQQ surfaceDestroyed");
+ nativeOnSurfaceChanged(null);
+ mSurfaceHolder = null;
+ }
+}
\ No newline at end of file
diff --git a/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java b/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java
index 93ae2bc227..6a6688ac41 100644
--- a/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java
+++ b/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java
@@ -364,25 +364,7 @@ public class QtActivity extends Activity {
@Override
protected void onDestroy() {
super.onDestroy();
- /*
- cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
- After Qt upgrade to 5.11 we had a black screen crash after closing the application with
- the hardware button "Back" and trying to start the app again. It could only be fixed after
- totally closing the app swiping it in the list of running apps.
- This problem did not happen with the previous Qt version.
- After analysing changes we came up with this case and change:
- https://codereview.qt-project.org/#/c/218882/
- In summary they've moved libs loading to the same thread as main() and as a matter of correctness
- in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
- That exit call is the main reason of this problem.
-
- In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
- entire onDestroy method including that thread exit line for other three lines that purposely
- terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
- */
- QtNative.terminateQt();
- QtNative.setActivity(null, null);
- System.exit(0);
+ QtApplication.invokeDelegate();
}
//---------------------------------------------------------------------------
@@ -668,6 +650,8 @@ public class QtActivity extends Activity {
if (!keepInterfaceRunning) {
QtApplication.invokeDelegate();
}
+ QtNative.terminateQt();
+ QtNative.setActivity(null,null);
}
//---------------------------------------------------------------------------
diff --git a/android/settings.gradle b/android/settings.gradle
index 1e7b3c768a..699f617cce 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1,8 +1,26 @@
+//
+// Libraries
+//
+
+include ':oculus'
+project(':oculus').projectDir = new File(settingsDir, 'libraries/oculus')
+
include ':qt'
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
+//
+// Applications
+//
+
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
-//include ':framePlayer'
-//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
+//
+// Test projects
+//
+
+include ':framePlayer'
+project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
+
+include ':questFramePlayer'
+project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 500772c1b5..801f28c6f5 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -38,6 +38,19 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
+const QRegularExpression AvatarMixer::suffixedNamePattern { R"(^\s*(.+)\s*_(\d)+\s*$)" };
+
+// Lexicographic comparison:
+bool AvatarMixer::SessionDisplayName::operator<(const SessionDisplayName& rhs) const {
+ if (_baseName < rhs._baseName) {
+ return true;
+ } else if (rhs._baseName < _baseName) {
+ return false;
+ } else {
+ return _suffix < rhs._suffix;
+ }
+}
+
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
ThreadedAssignment(message),
_slavePool(&_slaveSharedData)
@@ -313,27 +326,40 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
bool sendIdentity = false;
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
AvatarData& avatar = nodeData->getAvatar();
- const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
- if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
- _sessionDisplayNames.remove(existingBaseDisplayName);
+ const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName();
+ if (!existingBaseDisplayName.isEmpty()) {
+ SessionDisplayName existingDisplayName { existingBaseDisplayName };
+
+ auto suffixMatch = suffixedNamePattern.match(existingBaseDisplayName);
+ if (suffixMatch.hasMatch()) {
+ existingDisplayName._baseName = suffixMatch.captured(1);
+ existingDisplayName._suffix = suffixMatch.captured(2).toInt();
+ }
+ _sessionDisplayNames.erase(existingDisplayName);
}
QString baseName = avatar.getDisplayName().trimmed();
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
- const QRegularExpression trailingDigits { "\\s*(_\\d+\\s*)?(\\s*\\n[^$]*)?$" }; // trailing whitespace "_123" and any subsequent lines
+ static const QRegularExpression trailingDigits { R"(\s*(_\d+\s*)?(\s*\n[^$]*)?$)" }; // trailing whitespace "_123" and any subsequent lines
baseName = baseName.remove(trailingDigits);
if (baseName.isEmpty()) {
baseName = "anonymous";
}
- QPair& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
- int& highWater = soFar.first;
- nodeData->setBaseDisplayName(baseName);
- QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
+ SessionDisplayName newDisplayName { baseName };
+ auto nameIter = _sessionDisplayNames.lower_bound(newDisplayName);
+ if (nameIter != _sessionDisplayNames.end() && nameIter->_baseName == baseName) {
+ // Existing instance(s) of name; find first free suffix
+ while (*nameIter == newDisplayName && ++newDisplayName._suffix && ++nameIter != _sessionDisplayNames.end())
+ ;
+ }
+
+ _sessionDisplayNames.insert(newDisplayName);
+ QString sessionDisplayName = (newDisplayName._suffix > 0) ? baseName + "_" + QString::number(newDisplayName._suffix) : baseName;
avatar.setSessionDisplayName(sessionDisplayName);
- highWater++;
- soFar.second++; // refcount
+ nodeData->setBaseDisplayName(baseName);
+
nodeData->flagIdentityChange();
nodeData->setAvatarSessionDisplayNameMustChange(false);
sendIdentity = true;
@@ -409,10 +435,19 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
{ // decrement sessionDisplayNames table and possibly remove
QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex());
AvatarMixerClientData* nodeData = dynamic_cast(avatarNode->getLinkedData());
- const QString& baseDisplayName = nodeData->getBaseDisplayName();
- // No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case.
- if (--_sessionDisplayNames[baseDisplayName].second <= 0) {
- _sessionDisplayNames.remove(baseDisplayName);
+ const QString& displayName = nodeData->getAvatar().getSessionDisplayName();
+ SessionDisplayName exitingDisplayName { displayName };
+
+ auto suffixMatch = suffixedNamePattern.match(displayName);
+ if (suffixMatch.hasMatch()) {
+ exitingDisplayName._baseName = suffixMatch.captured(1);
+ exitingDisplayName._suffix = suffixMatch.captured(2).toInt();
+ }
+ auto displayNameIter = _sessionDisplayNames.find(exitingDisplayName);
+ if (displayNameIter == _sessionDisplayNames.end()) {
+ qCDebug(avatars) << "Exiting avatar displayname" << displayName << "not found";
+ } else {
+ _sessionDisplayNames.erase(displayNameIter);
}
}
diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h
index 764656a2d5..2992e19b8f 100644
--- a/assignment-client/src/avatars/AvatarMixer.h
+++ b/assignment-client/src/avatars/AvatarMixer.h
@@ -15,6 +15,7 @@
#ifndef hifi_AvatarMixer_h
#define hifi_AvatarMixer_h
+#include
#include
#include
@@ -88,7 +89,24 @@ private:
RateCounter<> _broadcastRate;
p_high_resolution_clock::time_point _lastDebugMessage;
- QHash> _sessionDisplayNames;
+
+ // Pair of basename + uniquifying integer suffix.
+ struct SessionDisplayName {
+ explicit SessionDisplayName(QString baseName = QString(), int suffix = 0) :
+ _baseName(baseName),
+ _suffix(suffix) { }
+ // Does lexicographic ordering:
+ bool operator<(const SessionDisplayName& rhs) const;
+ bool operator==(const SessionDisplayName& rhs) const {
+ return _baseName == rhs._baseName && _suffix == rhs._suffix;
+ }
+
+ QString _baseName;
+ int _suffix;
+ };
+ static const QRegularExpression suffixedNamePattern;
+
+ std::set _sessionDisplayNames;
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
quint64 _ignoreCalculationElapsedTime { 0 };
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 8b7c8771e8..a6542689e0 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -111,7 +111,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
- startNewTraversal(newView, root);
+ startNewTraversal(newView, root, isFullScene);
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
// and also use the opportunity to cull anything no longer in view
@@ -220,9 +220,10 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
-void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
+void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root,
+ bool forceFirstPass) {
- DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
+ DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, forceFirstPass);
// there are three types of traversal:
//
// (1) FirstTime = at login --> find everything in view
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 199769ca09..7eedc2f1ba 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -42,7 +42,7 @@ private:
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
- void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
+ void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root, bool forceFirstPass = false);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;
diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp
index ef0c807bc4..f1a6c97831 100644
--- a/assignment-client/src/scripts/EntityScriptServer.cpp
+++ b/assignment-client/src/scripts/EntityScriptServer.cpp
@@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointerunloadEntityScript(entityID);
checkAndCallPreload(entityID, true);
}
}
@@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() {
pps = std::min(_maxEntityPPS, pps);
}
_entityEditSender.setPacketsPerSecond(pps);
- qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps);
}
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer message, SharedNodePointer senderNode) {
@@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) {
if (_entityViewer.getTree() && !_shuttingDown) {
- _entitiesScriptEngine->unloadEntityScript(entityID, true);
checkAndCallPreload(entityID, reload);
}
}
-void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) {
+void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) {
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
EntityScriptDetails details;
- bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details);
- if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
+ bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details);
+ if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
+ if (isRunning) {
+ _entitiesScriptEngine->unloadEntityScript(entityID, true);
+ }
+
QString scriptUrl = entity->getServerScripts();
if (!scriptUrl.isEmpty()) {
scriptUrl = DependencyManager::get()->normalizeURL(scriptUrl);
- qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
- _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
+ _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload);
}
}
}
diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h
index 9c6c4c752e..944fee36a3 100644
--- a/assignment-client/src/scripts/EntityScriptServer.h
+++ b/assignment-client/src/scripts/EntityScriptServer.h
@@ -67,7 +67,7 @@ private:
void addingEntity(const EntityItemID& entityID);
void deletingEntity(const EntityItemID& entityID);
void entityServerScriptChanging(const EntityItemID& entityID, bool reload);
- void checkAndCallPreload(const EntityItemID& entityID, bool reload = false);
+ void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false);
void cleanupOldKilledListeners();
diff --git a/cmake/macros/FixupNitpick.cmake b/cmake/macros/FixupNitpick.cmake
new file mode 100644
index 0000000000..8477b17823
--- /dev/null
+++ b/cmake/macros/FixupNitpick.cmake
@@ -0,0 +1,36 @@
+#
+# FixupNitpick.cmake
+# cmake/macros
+#
+# Copyright 2019 High Fidelity, Inc.
+# Created by Nissim Hadar on January 14th, 2016
+#
+# Distributed under the Apache License, Version 2.0.
+# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+#
+
+macro(fixup_nitpick)
+ if (APPLE)
+ string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
+ string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
+ set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
+
+ find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
+
+ if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
+ message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
+ It is required to produce a relocatable nitpick application.\
+ Check that the environment variable QT_DIR points to your Qt installation.\
+ ")
+ endif ()
+
+ install(CODE "
+ execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
+ \${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
+ -verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
+ )"
+ COMPONENT ${CLIENT_COMPONENT}
+ )
+
+ endif ()
+endmacro()
diff --git a/cmake/macros/IncludeHifiLibraryHeaders.cmake b/cmake/macros/IncludeHifiLibraryHeaders.cmake
index 913d1e1181..008d76a8dc 100644
--- a/cmake/macros/IncludeHifiLibraryHeaders.cmake
+++ b/cmake/macros/IncludeHifiLibraryHeaders.cmake
@@ -10,5 +10,5 @@
#
macro(include_hifi_library_headers LIBRARY)
- include_directories("${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
+ target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
endmacro(include_hifi_library_headers _library _root_dir)
\ No newline at end of file
diff --git a/cmake/macros/LinkHifiLibraries.cmake b/cmake/macros/LinkHifiLibraries.cmake
index 7a6a136799..6a430f5b13 100644
--- a/cmake/macros/LinkHifiLibraries.cmake
+++ b/cmake/macros/LinkHifiLibraries.cmake
@@ -19,8 +19,8 @@ function(LINK_HIFI_LIBRARIES)
endforeach()
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
- include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
- include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
+ target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
+ target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
# link the actual library - it is static so don't bubble it up
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
endforeach()
diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake
index 1b7243d4f2..3ebc44e931 100644
--- a/cmake/macros/SetPackagingParameters.cmake
+++ b/cmake/macros/SetPackagingParameters.cmake
@@ -77,6 +77,9 @@ macro(SET_PACKAGING_PARAMETERS)
add_definitions(-DDEV_BUILD)
endif ()
+ set(NITPICK_BUNDLE_NAME "nitpick")
+ set(NITPICK_ICON_PREFIX "nitpick")
+
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
@@ -140,8 +143,9 @@ macro(SET_PACKAGING_PARAMETERS)
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
- set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
+ set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
+ set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
if (CLIENT_ONLY)
set(CONSOLE_EXEC_NAME "Console.app")
@@ -159,11 +163,14 @@ macro(SET_PACKAGING_PARAMETERS)
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
+ set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.icns")
else ()
if (WIN32)
set(CONSOLE_INSTALL_DIR "server-console")
+ set(NITPICK_INSTALL_DIR "nitpick")
else ()
set(CONSOLE_INSTALL_DIR ".")
+ set(NITPICK_INSTALL_DIR ".")
endif ()
set(COMPONENT_INSTALL_DIR ".")
@@ -173,6 +180,7 @@ macro(SET_PACKAGING_PARAMETERS)
if (WIN32)
set(INTERFACE_EXEC_PREFIX "interface")
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
+ set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
set(CONSOLE_EXEC_NAME "server-console.exe")
diff --git a/cmake/macros/TargetEGL.cmake b/cmake/macros/TargetEGL.cmake
new file mode 100644
index 0000000000..1d8ce26d83
--- /dev/null
+++ b/cmake/macros/TargetEGL.cmake
@@ -0,0 +1,4 @@
+macro(target_egl)
+ find_library(EGL EGL)
+ target_link_libraries(${TARGET_NAME} ${EGL})
+endmacro()
diff --git a/cmake/macros/TargetOculusMobile.cmake b/cmake/macros/TargetOculusMobile.cmake
new file mode 100644
index 0000000000..3eaa008b14
--- /dev/null
+++ b/cmake/macros/TargetOculusMobile.cmake
@@ -0,0 +1,20 @@
+
+macro(target_oculus_mobile)
+ set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus/VrApi)
+
+ # Mobile SDK
+ set(OVR_MOBILE_INCLUDE_DIRS ${INSTALL_DIR}/Include)
+ target_include_directories(${TARGET_NAME} PRIVATE ${OVR_MOBILE_INCLUDE_DIRS})
+ set(OVR_MOBILE_LIBRARY_DIR ${INSTALL_DIR}/Libs/Android/arm64-v8a)
+ set(OVR_MOBILE_LIBRARY_RELEASE ${OVR_MOBILE_LIBRARY_DIR}/Release/libvrapi.so)
+ set(OVR_MOBILE_LIBRARY_DEBUG ${OVR_MOBILE_LIBRARY_DIR}/Debug/libvrapi.so)
+ select_library_configurations(OVR_MOBILE)
+ target_link_libraries(${TARGET_NAME} ${OVR_MOBILE_LIBRARIES})
+
+ # Platform SDK
+ set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculusPlatform)
+ set(OVR_PLATFORM_INCLUDE_DIRS ${INSTALL_DIR}/Include)
+ target_include_directories(${TARGET_NAME} PRIVATE ${OVR_PLATFORM_INCLUDE_DIRS})
+ set(OVR_PLATFORM_LIBRARIES ${INSTALL_DIR}/Android/libs/arm64-v8a/libovrplatformloader.so)
+ target_link_libraries(${TARGET_NAME} ${OVR_PLATFORM_LIBRARIES})
+endmacro()
diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in
index 9ce11ca032..cc7a6929a2 100644
--- a/cmake/templates/NSIS.template.in
+++ b/cmake/templates/NSIS.template.in
@@ -300,6 +300,8 @@ Var substringResult
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
;Checking lowest bit:
IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
+ !insertmacro LoadVar ${SecName}_was_installed
+ IntOp $AR_SecFlags $AR_SecFlags | $R0
IntCmp $AR_SecFlags 1 "leave_${SecName}"
;Section is not selected:
@@ -478,18 +480,6 @@ Var GAClientID
;--------------------------------
; Installation types
-Section "-Previous Install Cleanup"
- ; Remove the resources folder so we don't end up including removed QML files
- RMDir /r "$INSTDIR\resources"
-
- ; delete old assignment-client and domain-server so they're no longer present
- ; in client only installs.
- Delete "$INSTDIR\@DS_EXEC_NAME@"
- Delete "$INSTDIR\@AC_EXEC_NAME@"
-
- ; delete interface so it's not there for server-only installs
- Delete "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
-SectionEnd
@CPACK_NSIS_INSTALLATION_TYPES@
diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt
deleted file mode 100644
index 72f1096881..0000000000
--- a/gvr-interface/CMakeLists.txt
+++ /dev/null
@@ -1,85 +0,0 @@
-set(TARGET_NAME gvr-interface)
-
-if (ANDROID)
- set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build")
- set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk")
-
- set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME})
- set(ANDROID_APP_DISPLAY_NAME Interface)
- set(ANDROID_API_LEVEL 19)
- set(ANDROID_APK_PACKAGE io.highfidelity.gvrinterface)
- set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceActivity)
- set(ANDROID_APK_VERSION_NAME "0.1")
- set(ANDROID_APK_VERSION_CODE 1)
- set(ANDROID_APK_FULLSCREEN TRUE)
- set(ANDROID_DEPLOY_QT_INSTALL "--install")
-
- set(BUILD_SHARED_LIBS ON)
- set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}")
-
- setup_hifi_library(Gui AndroidExtras)
-else ()
- setup_hifi_project(Gui)
-endif ()
-
-include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
-
-link_hifi_libraries(shared networking audio-client avatars)
-
-if (ANDROID)
- find_package(LibOVR)
-
- if (LIBOVR_FOUND)
- add_definitions(-DHAVE_LIBOVR)
- target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES} ${LIBOVR_ANDROID_LIBRARIES} ${TURBOJPEG_LIBRARY})
- include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS})
-
- # we need VRLib, so add a project.properties to our apk build folder that says that
- file(RELATIVE_PATH RELATIVE_VRLIB_PATH ${ANDROID_APK_OUTPUT_DIR} "${LIBOVR_VRLIB_DIR}")
- file(WRITE "${ANDROID_APK_BUILD_DIR}/project.properties" "android.library.reference.1=${RELATIVE_VRLIB_PATH}")
-
- list(APPEND IGNORE_COPY_LIBS ${LIBOVR_ANDROID_LIBRARIES})
- endif ()
-
-endif ()
-
-# the presence of a HOCKEY_APP_ID means we are making a beta build
-if (ANDROID AND HOCKEY_APP_ID)
- set(HOCKEY_APP_ENABLED true)
- set(HOCKEY_APP_ACTIVITY "\n")
- set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceBetaActivity)
- set(ANDROID_DEPLOY_QT_INSTALL "")
- set(ANDROID_APK_CUSTOM_NAME "Interface-beta.apk")
-
- # set the ANDROID_APK_VERSION_CODE to the number of git commits
- execute_process(
- COMMAND git rev-list --first-parent --count HEAD
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
- OUTPUT_VARIABLE GIT_COMMIT_COUNT
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
-
- set(ANDROID_APK_VERSION_CODE ${GIT_COMMIT_COUNT})
-
- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/InterfaceBetaActivity.java.in" "${ANDROID_APK_BUILD_DIR}/src/io/highfidelity/gvrinterface/InterfaceBetaActivity.java")
-elseif (ANDROID)
- set(HOCKEY_APP_ENABLED false)
-endif ()
-
-if (ANDROID)
-
- set(HIFI_URL_INTENT "\
- \n \
- \n \
- \n \
- \n \
- \n "
- )
-
- set(ANDROID_EXTRA_APPLICATION_XML "${HOCKEY_APP_ACTIVITY}")
- set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}")
-
- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/hockeyapp.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/hockeyapp.xml")
- qt_create_apk()
-
-endif (ANDROID)
diff --git a/gvr-interface/res/drawable/icon.png b/gvr-interface/res/drawable/icon.png
deleted file mode 100644
index 70aaf9b4ed..0000000000
Binary files a/gvr-interface/res/drawable/icon.png and /dev/null differ
diff --git a/gvr-interface/src/Client.cpp b/gvr-interface/src/Client.cpp
deleted file mode 100644
index 8f064c7fd5..0000000000
--- a/gvr-interface/src/Client.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Client.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/15.
-// Copyright 2013 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
-//
-
-#include "Client.h"
-
-#include
-#include
-#include
-#include
-#include
-
-Client::Client(QObject* parent) :
- QObject(parent)
-{
- // we need to make sure that required dependencies are created
- DependencyManager::set();
-
- setupNetworking();
-}
-
-void Client::setupNetworking() {
- // once Application order of instantiation is fixed this should be done from AccountManager
- AccountManager::getInstance().setAuthURL(DEFAULT_NODE_AUTH_URL);
-
- // setup the NodeList for this client
- DependencyManager::registerInheritance();
- auto nodeList = DependencyManager::set(NodeType::Agent, 0);
-
- // while datagram processing remains simple for targets using Client, we'll handle datagrams
- connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &Client::processDatagrams);
-
- // every second, ask the NodeList to check in with the domain server
- QTimer* domainCheckInTimer = new QTimer(this);
- domainCheckInTimer->setInterval(DOMAIN_SERVER_CHECK_IN_MSECS);
- connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
-
- // TODO: once the Client knows its Address on start-up we should be able to immediately send a check in here
- domainCheckInTimer->start();
-
- // handle the case where the domain stops talking to us
- // TODO: can we just have the nodelist do this when it sets up? Is there a user of the NodeList that wouldn't want this?
- connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
-}
-
-void Client::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
- DependencyManager::get()->processNodeData(senderSockAddr, incomingPacket);
-}
-
-void Client::processDatagrams() {
- HifiSockAddr senderSockAddr;
-
- static QByteArray incomingPacket;
-
- auto nodeList = DependencyManager::get();
-
- while (DependencyManager::get()->getNodeSocket().hasPendingDatagrams()) {
- incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
- nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(),
- senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
-
- if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
- processVerifiedPacket(senderSockAddr, incomingPacket);
- }
- }
-}
diff --git a/gvr-interface/src/Client.h b/gvr-interface/src/Client.h
deleted file mode 100644
index 6fbe40f165..0000000000
--- a/gvr-interface/src/Client.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// Client.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/15.
-// Copyright 2013 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
-//
-
-#ifndef hifi_Client_h
-#define hifi_Client_h
-
-#include
-
-#include
-
-class Client : public QObject {
- Q_OBJECT
-public:
- Client(QObject* parent = 0);
-
- virtual void cleanupBeforeQuit() = 0;
-protected:
-
- void setupNetworking();
- virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
-private slots:
- void processDatagrams();
-};
-
-#endif // hifi_Client_h
diff --git a/gvr-interface/src/GVRInterface.cpp b/gvr-interface/src/GVRInterface.cpp
deleted file mode 100644
index f9a29d4ac4..0000000000
--- a/gvr-interface/src/GVRInterface.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-//
-// GVRInterface.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 11/18/14.
-// Copyright 2013 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
-//
-
-#include "GVRInterface.h"
-
-#ifdef ANDROID
-
-#include
-
-#include
-#include
-#include
-
-#ifdef HAVE_LIBOVR
-
-#include
-#include
-
-#endif
-#endif
-
-#include
-#include
-#include
-
-#include "GVRMainWindow.h"
-#include "RenderingClient.h"
-
-static QString launchURLString = QString();
-
-#ifdef ANDROID
-
-extern "C" {
-
-JNIEXPORT void Java_io_highfidelity_gvrinterface_InterfaceActivity_handleHifiURL(JNIEnv *jni, jclass clazz, jstring hifiURLString) {
- launchURLString = QAndroidJniObject(hifiURLString).toString();
-}
-
-}
-
-#endif
-
-GVRInterface::GVRInterface(int argc, char* argv[]) :
- QApplication(argc, argv),
- _mainWindow(NULL),
- _inVRMode(false)
-{
- setApplicationName("gvr-interface");
- setOrganizationName("highfidelity");
- setOrganizationDomain("io");
-
- if (!launchURLString.isEmpty()) {
- // did we get launched with a lookup URL? If so it is time to give that to the AddressManager
- qDebug() << "We were opened via a hifi URL -" << launchURLString;
- }
-
- _client = new RenderingClient(this, launchURLString);
-
- launchURLString = QString();
-
- connect(this, &QGuiApplication::applicationStateChanged, this, &GVRInterface::handleApplicationStateChange);
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- QAndroidJniEnvironment jniEnv;
-
- QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
- jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
-
- ovr_RegisterHmtReceivers(&*jniEnv, activity);
-
- // PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
- // stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
- // but that increases the size of the plugin by ~1MiB
- OVR::linkerPlatformActivity++;
-#endif
-
- // call our idle function whenever we can
- QTimer* idleTimer = new QTimer(this);
- connect(idleTimer, &QTimer::timeout, this, &GVRInterface::idle);
- idleTimer->start(0);
-
- // call our quit handler before we go down
- connect(this, &QCoreApplication::aboutToQuit, this, &GVRInterface::handleApplicationQuit);
-}
-
-void GVRInterface::handleApplicationQuit() {
- _client->cleanupBeforeQuit();
-}
-
-void GVRInterface::idle() {
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- if (!_inVRMode && ovr_IsHeadsetDocked()) {
- qDebug() << "The headset just got docked - enter VR mode.";
- enterVRMode();
- } else if (_inVRMode) {
-
- if (ovr_IsHeadsetDocked()) {
- static int counter = 0;
-
- // Get the latest head tracking state, predicted ahead to the midpoint of the time
- // it will be displayed. It will always be corrected to the real values by
- // time warp, but the closer we get, the less black will be pulled in at the edges.
- const double now = ovr_GetTimeInSeconds();
- static double prev;
- const double rawDelta = now - prev;
- prev = now;
- const double clampedPrediction = std::min( 0.1, rawDelta * 2);
- ovrSensorState sensor = ovrHmd_GetSensorState(OvrHmd, now + clampedPrediction, true );
-
- auto ovrOrientation = sensor.Predicted.Pose.Orientation;
- glm::quat newOrientation(ovrOrientation.w, ovrOrientation.x, ovrOrientation.y, ovrOrientation.z);
- _client->setOrientation(newOrientation);
-
- if (counter++ % 100000 == 0) {
- qDebug() << "GetSensorState in frame" << counter << "-"
- << ovrOrientation.x << ovrOrientation.y << ovrOrientation.z << ovrOrientation.w;
- }
- } else {
- qDebug() << "The headset was undocked - leaving VR mode.";
-
- leaveVRMode();
- }
- }
-
- OVR::KeyState& backKeyState = _mainWindow->getBackKeyState();
- auto backEvent = backKeyState.Update(ovr_GetTimeInSeconds());
-
- if (backEvent == OVR::KeyState::KEY_EVENT_LONG_PRESS) {
- qDebug() << "Attemping to start the Platform UI Activity.";
- ovr_StartPackageActivity(_ovr, PUI_CLASS_NAME, PUI_GLOBAL_MENU);
- } else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP || backEvent == OVR::KeyState::KEY_EVENT_SHORT_PRESS) {
- qDebug() << "Got an event we should cancel for!";
- } else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP) {
- qDebug() << "The button is down!";
- }
-#endif
-}
-
-void GVRInterface::handleApplicationStateChange(Qt::ApplicationState state) {
- switch(state) {
- case Qt::ApplicationActive:
- qDebug() << "The application is active.";
- break;
- case Qt::ApplicationSuspended:
- qDebug() << "The application is being suspended.";
- break;
- default:
- break;
- }
-}
-
-void GVRInterface::enterVRMode() {
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- // Default vrModeParms
- ovrModeParms vrModeParms;
- vrModeParms.AsynchronousTimeWarp = true;
- vrModeParms.AllowPowerSave = true;
- vrModeParms.DistortionFileName = NULL;
- vrModeParms.EnableImageServer = false;
- vrModeParms.CpuLevel = 2;
- vrModeParms.GpuLevel = 2;
- vrModeParms.GameThreadTid = 0;
-
- QAndroidJniEnvironment jniEnv;
-
- QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
- jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
-
- vrModeParms.ActivityObject = activity;
-
- ovrHmdInfo hmdInfo;
- _ovr = ovr_EnterVrMode(vrModeParms, &hmdInfo);
-
- _inVRMode = true;
-#endif
-}
-
-void GVRInterface::leaveVRMode() {
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- ovr_LeaveVrMode(_ovr);
- _inVRMode = false;
-#endif
-}
diff --git a/gvr-interface/src/GVRInterface.h b/gvr-interface/src/GVRInterface.h
deleted file mode 100644
index 9ffbd52909..0000000000
--- a/gvr-interface/src/GVRInterface.h
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// GVRInterface.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 11/18/14.
-// Copyright 2013 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
-//
-
-#ifndef hifi_GVRInterface_h
-#define hifi_GVRInterface_h
-
-#include
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
-class ovrMobile;
-class ovrHmdInfo;
-
-// This is set by JNI_OnLoad() when the .so is initially loaded.
-// Must use to attach each thread that will use JNI:
-namespace OVR {
- // PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
- // stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
- // but that increases the size of the plugin by ~1MiB
- extern int linkerPlatformActivity;
-}
-
-#endif
-
-class GVRMainWindow;
-class RenderingClient;
-class QKeyEvent;
-
-#if defined(qApp)
-#undef qApp
-#endif
-#define qApp (static_cast(QApplication::instance()))
-
-class GVRInterface : public QApplication {
- Q_OBJECT
-public:
- GVRInterface(int argc, char* argv[]);
- RenderingClient* getClient() { return _client; }
-
- void setMainWindow(GVRMainWindow* mainWindow) { _mainWindow = mainWindow; }
-
-protected:
- void keyPressEvent(QKeyEvent* event);
-
-private slots:
- void handleApplicationStateChange(Qt::ApplicationState state);
- void idle();
-private:
- void handleApplicationQuit();
-
- void enterVRMode();
- void leaveVRMode();
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- ovrMobile* _ovr;
- ovrHmdInfo* _hmdInfo;
-#endif
-
- GVRMainWindow* _mainWindow;
-
- RenderingClient* _client;
- bool _inVRMode;
-};
-
-#endif // hifi_GVRInterface_h
diff --git a/gvr-interface/src/GVRMainWindow.cpp b/gvr-interface/src/GVRMainWindow.cpp
deleted file mode 100644
index 5495354233..0000000000
--- a/gvr-interface/src/GVRMainWindow.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-//
-// GVRMainWindow.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/14.
-// Copyright 2013 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
-//
-
-#include "GVRMainWindow.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifndef ANDROID
-
-#include
-
-#elif defined(HAVE_LIBOVR)
-
-#include
-
-const float LIBOVR_DOUBLE_TAP_DURATION = 0.25f;
-const float LIBOVR_LONG_PRESS_DURATION = 0.75f;
-
-#endif
-
-#include
-
-#include "InterfaceView.h"
-#include "LoginDialog.h"
-#include "RenderingClient.h"
-
-
-
-GVRMainWindow::GVRMainWindow(QWidget* parent) :
- QMainWindow(parent),
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- _backKeyState(LIBOVR_DOUBLE_TAP_DURATION, LIBOVR_LONG_PRESS_DURATION),
- _wasBackKeyDown(false),
-#endif
- _mainLayout(NULL),
- _menuBar(NULL),
- _loginAction(NULL)
-{
-
-#ifndef ANDROID
- const int NOTE_4_WIDTH = 2560;
- const int NOTE_4_HEIGHT = 1440;
- setFixedSize(NOTE_4_WIDTH / 2, NOTE_4_HEIGHT / 2);
-#endif
-
- setupMenuBar();
-
- QWidget* baseWidget = new QWidget(this);
-
- // setup a layout so we can vertically align to top
- _mainLayout = new QVBoxLayout(baseWidget);
- _mainLayout->setAlignment(Qt::AlignTop);
-
- // set the layout on the base widget
- baseWidget->setLayout(_mainLayout);
-
- setCentralWidget(baseWidget);
-
- // add the interface view
- new InterfaceView(baseWidget);
-}
-
-GVRMainWindow::~GVRMainWindow() {
- delete _menuBar;
-}
-
-void GVRMainWindow::keyPressEvent(QKeyEvent* event) {
-#ifdef ANDROID
- if (event->key() == Qt::Key_Back) {
- // got the Android back key, hand off to OVR KeyState
- _backKeyState.HandleEvent(ovr_GetTimeInSeconds(), true, (_wasBackKeyDown ? 1 : 0));
- _wasBackKeyDown = true;
- return;
- }
-#endif
- QWidget::keyPressEvent(event);
-}
-
-void GVRMainWindow::keyReleaseEvent(QKeyEvent* event) {
-#ifdef ANDROID
- if (event->key() == Qt::Key_Back) {
- // release on the Android back key, hand off to OVR KeyState
- _backKeyState.HandleEvent(ovr_GetTimeInSeconds(), false, 0);
- _wasBackKeyDown = false;
- }
-#endif
- QWidget::keyReleaseEvent(event);
-}
-
-void GVRMainWindow::setupMenuBar() {
- QMenu* fileMenu = new QMenu("File");
- QMenu* helpMenu = new QMenu("Help");
-
- _menuBar = new QMenuBar(0);
-
- _menuBar->addMenu(fileMenu);
- _menuBar->addMenu(helpMenu);
-
- QAction* goToAddress = new QAction("Go to Address", fileMenu);
- connect(goToAddress, &QAction::triggered, this, &GVRMainWindow::showAddressBar);
- fileMenu->addAction(goToAddress);
-
- _loginAction = new QAction("Login", fileMenu);
- fileMenu->addAction(_loginAction);
-
- // change the login action depending on our logged in/out state
- AccountManager& accountManager = AccountManager::getInstance();
- connect(&accountManager, &AccountManager::loginComplete, this, &GVRMainWindow::refreshLoginAction);
- connect(&accountManager, &AccountManager::logoutComplete, this, &GVRMainWindow::refreshLoginAction);
-
- // refresh the state now
- refreshLoginAction();
-
- QAction* aboutQt = new QAction("About Qt", helpMenu);
- connect(aboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
- helpMenu->addAction(aboutQt);
-
- setMenuBar(_menuBar);
-}
-
-void GVRMainWindow::showAddressBar() {
- // setup the address QInputDialog
- QInputDialog* addressDialog = new QInputDialog(this);
- addressDialog->setLabelText("Address");
-
- // add the address dialog to the main layout
- _mainLayout->addWidget(addressDialog);
-
- connect(addressDialog, &QInputDialog::textValueSelected,
- DependencyManager::get().data(), &AddressManager::handleLookupString);
-}
-
-void GVRMainWindow::showLoginDialog() {
- LoginDialog* loginDialog = new LoginDialog(this);
-
- // have the acccount manager handle credentials from LoginDialog
- AccountManager& accountManager = AccountManager::getInstance();
- connect(loginDialog, &LoginDialog::credentialsEntered, &accountManager, &AccountManager::requestAccessToken);
- connect(&accountManager, &AccountManager::loginFailed, this, &GVRMainWindow::showLoginFailure);
-
- _mainLayout->addWidget(loginDialog);
-}
-
-void GVRMainWindow::showLoginFailure() {
- QMessageBox::warning(this, "Login Failed",
- "Could not log in with that username and password. Please try again!");
-}
-
-void GVRMainWindow::refreshLoginAction() {
- AccountManager& accountManager = AccountManager::getInstance();
- disconnect(_loginAction, &QAction::triggered, &accountManager, 0);
-
- if (accountManager.isLoggedIn()) {
- _loginAction->setText("Logout");
- connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
- } else {
- _loginAction->setText("Login");
- connect(_loginAction, &QAction::triggered, this, &GVRMainWindow::showLoginDialog);
- }
-
-}
diff --git a/gvr-interface/src/GVRMainWindow.h b/gvr-interface/src/GVRMainWindow.h
deleted file mode 100644
index c28c19a6c1..0000000000
--- a/gvr-interface/src/GVRMainWindow.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// GVRMainWindow.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/14.
-// Copyright 2013 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
-//
-
-#ifndef hifi_GVRMainWindow_h
-#define hifi_GVRMainWindow_h
-
-#include
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
-#include
-#endif
-
-class QKeyEvent;
-class QMenuBar;
-class QVBoxLayout;
-
-class GVRMainWindow : public QMainWindow {
- Q_OBJECT
-public:
- GVRMainWindow(QWidget* parent = 0);
- ~GVRMainWindow();
-public slots:
- void showAddressBar();
- void showLoginDialog();
-
- void showLoginFailure();
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- OVR::KeyState& getBackKeyState() { return _backKeyState; }
-#endif
-
-protected:
- void keyPressEvent(QKeyEvent* event);
- void keyReleaseEvent(QKeyEvent* event);
-private slots:
- void refreshLoginAction();
-private:
- void setupMenuBar();
-
-#if defined(ANDROID) && defined(HAVE_LIBOVR)
- OVR::KeyState _backKeyState;
- bool _wasBackKeyDown;
-#endif
-
- QVBoxLayout* _mainLayout;
- QMenuBar* _menuBar;
- QAction* _loginAction;
-};
-
-#endif // hifi_GVRMainWindow_h
diff --git a/gvr-interface/src/InterfaceView.cpp b/gvr-interface/src/InterfaceView.cpp
deleted file mode 100644
index e7992d3921..0000000000
--- a/gvr-interface/src/InterfaceView.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// InterfaceView.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/28/14.
-// Copyright 2013 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
-//
-
-#include "InterfaceView.h"
-
-InterfaceView::InterfaceView(QWidget* parent, Qt::WindowFlags flags) :
- QOpenGLWidget(parent, flags)
-{
-
-}
\ No newline at end of file
diff --git a/gvr-interface/src/InterfaceView.h b/gvr-interface/src/InterfaceView.h
deleted file mode 100644
index 3d358a3e64..0000000000
--- a/gvr-interface/src/InterfaceView.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// InterfaceView.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/28/14.
-// Copyright 2013 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
-//
-
-#ifndef hifi_InterfaceView_h
-#define hifi_InterfaceView_h
-
-#include
-
-class InterfaceView : public QOpenGLWidget {
- Q_OBJECT
-public:
- InterfaceView(QWidget* parent = 0, Qt::WindowFlags flags = 0);
-};
-
-#endif // hifi_InterfaceView_h
diff --git a/gvr-interface/src/LoginDialog.cpp b/gvr-interface/src/LoginDialog.cpp
deleted file mode 100644
index d4efd425bd..0000000000
--- a/gvr-interface/src/LoginDialog.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// LoginDialog.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 2015-02-03.
-// 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
-//
-
-#include "LoginDialog.h"
-
-#include
-#include
-#include
-#include
-#include
-
-LoginDialog::LoginDialog(QWidget* parent) :
- QDialog(parent)
-{
- setupGUI();
- setWindowTitle("Login");
- setModal(true);
-}
-
-void LoginDialog::setupGUI() {
- // setup a grid layout
- QGridLayout* formGridLayout = new QGridLayout(this);
-
- _usernameLineEdit = new QLineEdit(this);
-
- QLabel* usernameLabel = new QLabel(this);
- usernameLabel->setText("Username");
- usernameLabel->setBuddy(_usernameLineEdit);
-
- formGridLayout->addWidget(usernameLabel, 0, 0);
- formGridLayout->addWidget(_usernameLineEdit, 1, 0);
-
- _passwordLineEdit = new QLineEdit(this);
- _passwordLineEdit->setEchoMode(QLineEdit::Password);
-
- QLabel* passwordLabel = new QLabel(this);
- passwordLabel->setText("Password");
- passwordLabel->setBuddy(_passwordLineEdit);
-
- formGridLayout->addWidget(passwordLabel, 2, 0);
- formGridLayout->addWidget(_passwordLineEdit, 3, 0);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(this);
-
- QPushButton* okButton = buttons->addButton(QDialogButtonBox::Ok);
- QPushButton* cancelButton = buttons->addButton(QDialogButtonBox::Cancel);
-
- okButton->setText("Login");
-
- connect(cancelButton, &QPushButton::clicked, this, &QDialog::close);
- connect(okButton, &QPushButton::clicked, this, &LoginDialog::loginButtonClicked);
-
- formGridLayout->addWidget(buttons, 4, 0, 1, 2);
-
- setLayout(formGridLayout);
-}
-
-void LoginDialog::loginButtonClicked() {
- emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
- close();
-}
diff --git a/gvr-interface/src/LoginDialog.h b/gvr-interface/src/LoginDialog.h
deleted file mode 100644
index 13f630818d..0000000000
--- a/gvr-interface/src/LoginDialog.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// LoginDialog.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 2015-02-03.
-// 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
-//
-
-#ifndef hifi_LoginDialog_h
-#define hifi_LoginDialog_h
-
-#include
-
-class QLineEdit;
-
-class LoginDialog : public QDialog {
- Q_OBJECT
-public:
- LoginDialog(QWidget* parent = 0);
-signals:
- void credentialsEntered(const QString& username, const QString& password);
-private slots:
- void loginButtonClicked();
-private:
- void setupGUI();
-
- QLineEdit* _usernameLineEdit;
- QLineEdit* _passwordLineEdit;
-};
-
-#endif // hifi_LoginDialog_h
\ No newline at end of file
diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp
deleted file mode 100644
index 4c691a48e6..0000000000
--- a/gvr-interface/src/RenderingClient.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-// RenderingClient.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/15.
-// Copyright 2013 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
-//
-
-#include "RenderingClient.h"
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-RenderingClient* RenderingClient::_instance = NULL;
-
-RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString) :
- Client(parent)
-{
- _instance = this;
-
- // connect to AddressManager and pass it the launch URL, if we have one
- auto addressManager = DependencyManager::get();
- connect(addressManager.data(), &AddressManager::locationChangeRequired, this, &RenderingClient::goToLocation);
- addressManager->loadSettings(launchURLString);
-
- // tell the NodeList which node types all rendering clients will want to know about
- DependencyManager::get()->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
-
- DependencyManager::set();
-
- // get our audio client setup on its own thread
- auto audioClient = DependencyManager::set();
- audioClient->setPositionGetter(getPositionForAudio);
- audioClient->setOrientationGetter(getOrientationForAudio);
- audioClient->startThread();
-
-
- connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);
- _avatarTimer.setInterval(16); // 60 FPS
- _avatarTimer.start();
- _fakeAvatar.setDisplayName("GearVR");
- _fakeAvatar.setFaceModelURL(QUrl(DEFAULT_HEAD_MODEL_URL));
- _fakeAvatar.setSkeletonModelURL(QUrl(DEFAULT_BODY_MODEL_URL));
- _fakeAvatar.toByteArray(); // Creates HeadData
-}
-
-void RenderingClient::sendAvatarPacket() {
- _fakeAvatar.setPosition(_position);
- _fakeAvatar.setHeadOrientation(_orientation);
-
- QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
- packet.append(_fakeAvatar.toByteArray());
- DependencyManager::get()->broadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
- _fakeAvatar.sendIdentityPacket();
-}
-
-void RenderingClient::cleanupBeforeQuit() {
- DependencyManager::get()->cleanupBeforeQuit();
- // destroy the AudioClient so it and its thread will safely go down
- DependencyManager::destroy();
-}
-
-void RenderingClient::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
- auto nodeList = DependencyManager::get();
- PacketType incomingType = packetTypeForPacket(incomingPacket);
-
- switch (incomingType) {
- case PacketTypeAudioEnvironment:
- case PacketTypeAudioStreamStats:
- case PacketTypeMixedAudio:
- case PacketTypeSilentAudioFrame: {
-
- if (incomingType == PacketTypeAudioStreamStats) {
- QMetaObject::invokeMethod(DependencyManager::get().data(), "parseAudioStreamStatsPacket",
- Qt::QueuedConnection,
- Q_ARG(QByteArray, incomingPacket));
- } else if (incomingType == PacketTypeAudioEnvironment) {
- QMetaObject::invokeMethod(DependencyManager::get().data(), "parseAudioEnvironmentData",
- Qt::QueuedConnection,
- Q_ARG(QByteArray, incomingPacket));
- } else {
- QMetaObject::invokeMethod(DependencyManager::get().data(), "addReceivedAudioToStream",
- Qt::QueuedConnection,
- Q_ARG(QByteArray, incomingPacket));
- }
-
- // update having heard from the audio-mixer and record the bytes received
- SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
-
- if (audioMixer) {
- audioMixer->setLastHeardMicrostamp(usecTimestampNow());
- }
-
- break;
- }
- case PacketTypeBulkAvatarData:
- case PacketTypeKillAvatar:
- case PacketTypeAvatarIdentity:
- case PacketTypeAvatarBillboard: {
- // update having heard from the avatar-mixer and record the bytes received
- SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
-
- if (avatarMixer) {
- avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
-
- QMetaObject::invokeMethod(DependencyManager::get().data(),
- "processAvatarMixerDatagram",
- Q_ARG(const QByteArray&, incomingPacket),
- Q_ARG(const QWeakPointer&, avatarMixer));
- }
- break;
- }
- default:
- Client::processVerifiedPacket(senderSockAddr, incomingPacket);
- break;
- }
-}
-
-void RenderingClient::goToLocation(const glm::vec3& newPosition,
- bool hasOrientationChange, const glm::quat& newOrientation,
- bool shouldFaceLocation) {
- qDebug().nospace() << "RenderingClient goToLocation - moving to " << newPosition.x << ", "
- << newPosition.y << ", " << newPosition.z;
-
- glm::vec3 shiftedPosition = newPosition;
-
- if (hasOrientationChange) {
- qDebug().nospace() << "RenderingClient goToLocation - new orientation is "
- << newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
-
- // orient the user to face the target
- glm::quat quatOrientation = newOrientation;
-
- if (shouldFaceLocation) {
-
- quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
-
- // move the user a couple units away
- const float DISTANCE_TO_USER = 2.0f;
- shiftedPosition = newPosition - quatOrientation * glm::vec3( 0.0f, 0.0f,-1.0f) * DISTANCE_TO_USER;
- }
-
- _orientation = quatOrientation;
- }
-
- _position = shiftedPosition;
-
-}
diff --git a/gvr-interface/src/RenderingClient.h b/gvr-interface/src/RenderingClient.h
deleted file mode 100644
index c4724bc086..0000000000
--- a/gvr-interface/src/RenderingClient.h
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-// RenderingClient.h
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 1/20/15.
-// Copyright 2013 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
-//
-
-
-#ifndef hifi_RenderingClient_h
-#define hifi_RenderingClient_h
-
-#include
-#include
-
-#include
-
-#include
-
-#include "Client.h"
-
-class RenderingClient : public Client {
- Q_OBJECT
-public:
- RenderingClient(QObject* parent = 0, const QString& launchURLString = QString());
-
- const glm::vec3& getPosition() const { return _position; }
- const glm::quat& getOrientation() const { return _orientation; }
- void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
-
- static glm::vec3 getPositionForAudio() { return _instance->getPosition(); }
- static glm::quat getOrientationForAudio() { return _instance->getOrientation(); }
-
- virtual void cleanupBeforeQuit();
-
-private slots:
- void goToLocation(const glm::vec3& newPosition,
- bool hasOrientationChange, const glm::quat& newOrientation,
- bool shouldFaceLocation);
- void sendAvatarPacket();
-
-private:
- virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
-
- static RenderingClient* _instance;
-
- glm::vec3 _position;
- glm::quat _orientation;
-
- QTimer _avatarTimer;
- AvatarData _fakeAvatar;
-};
-
-#endif // hifi_RenderingClient_h
diff --git a/gvr-interface/src/java/io/highfidelity/gvrinterface/InterfaceActivity.java b/gvr-interface/src/java/io/highfidelity/gvrinterface/InterfaceActivity.java
deleted file mode 100644
index c7cbdd3dff..0000000000
--- a/gvr-interface/src/java/io/highfidelity/gvrinterface/InterfaceActivity.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// 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.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());
- }
- }
-
- }
-}
\ No newline at end of file
diff --git a/gvr-interface/src/main.cpp b/gvr-interface/src/main.cpp
deleted file mode 100644
index 26576393fb..0000000000
--- a/gvr-interface/src/main.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// main.cpp
-// gvr-interface/src
-//
-// Created by Stephen Birarda on 11/17/14.
-// Copyright 2014 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
-//
-
-#include "GVRMainWindow.h"
-#include "GVRInterface.h"
-
-int main(int argc, char* argv[]) {
- GVRInterface app(argc, argv);
-
- GVRMainWindow mainWindow;
-#ifdef ANDROID
- mainWindow.showFullScreen();
-#else
- mainWindow.showMaximized();
-#endif
-
- app.setMainWindow(&mainWindow);
-
- return app.exec();
-}
\ No newline at end of file
diff --git a/gvr-interface/templates/InterfaceBetaActivity.java.in b/gvr-interface/templates/InterfaceBetaActivity.java.in
deleted file mode 100644
index 6698cfa409..0000000000
--- a/gvr-interface/templates/InterfaceBetaActivity.java.in
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// InterfaceBetaActivity.java
-// gvr-interface/java
-//
-// Created by Stephen Birarda on 1/27/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.os.Bundle;
-import net.hockeyapp.android.CrashManager;
-import net.hockeyapp.android.UpdateManager;
-
-public class InterfaceBetaActivity extends InterfaceActivity {
-
- public String _hockeyAppID;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- _hockeyAppID = getString(R.string.HockeyAppID);
-
- checkForUpdates();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- UpdateManager.unregister();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- checkForCrashes();
- }
-
- private void checkForCrashes() {
- CrashManager.register(this, _hockeyAppID);
- }
-
- private void checkForUpdates() {
- // Remove this for store / production builds!
- UpdateManager.register(this, _hockeyAppID);
- }
-}
\ No newline at end of file
diff --git a/gvr-interface/templates/hockeyapp.xml.in b/gvr-interface/templates/hockeyapp.xml.in
deleted file mode 100644
index edf2d0a8aa..0000000000
--- a/gvr-interface/templates/hockeyapp.xml.in
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- ${HOCKEY_APP_ID}
- ${HOCKEY_APP_ENABLED}
-
diff --git a/hifi_android.py b/hifi_android.py
index 13c9cdccf2..2e6a42d127 100644
--- a/hifi_android.py
+++ b/hifi_android.py
@@ -52,6 +52,13 @@ ANDROID_PACKAGES = {
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
'includeLibs': ['libvrapi.so']
},
+ 'oculusPlatform': {
+ 'file': 'OVRPlatformSDK_v1.32.0.zip',
+ 'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL',
+ 'checksum': 'ab5b203b3a39a56ab148d68fff769e05',
+ 'sharedLibFolder': 'Android/libs/arm64-v8a',
+ 'includeLibs': ['libovrplatformloader.so']
+ },
'openssl': {
'file': 'openssl-1.1.0g_armv8.tgz',
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt
index c013cfacd3..74e25662c7 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -211,7 +211,10 @@ link_hifi_libraries(
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
+ # Platform specific GL libraries
${PLATFORM_GL_BACKEND}
+ # Plaform specific input & display plugin libraries
+ ${PLATFORM_PLUGIN_LIBRARIES}
shaders
)
@@ -265,7 +268,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
endforeach()
# include headers for interface and InterfaceConfig.
-include_directories("${PROJECT_SOURCE_DIR}/src")
+target_include_directories(${TARGET_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src")
if (ANDROID)
find_library(ANDROID_LOG_LIB log)
@@ -330,7 +333,11 @@ if (APPLE)
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/resources/fonts"
"${RESOURCES_DEV_DIR}/fonts"
- # add redirect json to macOS builds.
+ #copy serverless for android
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory
+ "${PROJECT_SOURCE_DIR}/resources/serverless"
+ "${RESOURCES_DEV_DIR}/serverless"
+ # add redirect json to macOS builds.
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
diff --git a/interface/resources/icons/+android/backward.svg b/interface/resources/icons/+android_interface/backward.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/backward.svg
rename to interface/resources/icons/+android_interface/backward.svg
diff --git a/interface/resources/icons/+android/bubble-a.svg b/interface/resources/icons/+android_interface/bubble-a.svg
similarity index 100%
rename from interface/resources/icons/+android/bubble-a.svg
rename to interface/resources/icons/+android_interface/bubble-a.svg
diff --git a/interface/resources/icons/+android/bubble-i.svg b/interface/resources/icons/+android_interface/bubble-i.svg
similarity index 100%
rename from interface/resources/icons/+android/bubble-i.svg
rename to interface/resources/icons/+android_interface/bubble-i.svg
diff --git a/interface/resources/icons/+android/button-a.svg b/interface/resources/icons/+android_interface/button-a.svg
similarity index 100%
rename from interface/resources/icons/+android/button-a.svg
rename to interface/resources/icons/+android_interface/button-a.svg
diff --git a/interface/resources/icons/+android/button.svg b/interface/resources/icons/+android_interface/button.svg
similarity index 100%
rename from interface/resources/icons/+android/button.svg
rename to interface/resources/icons/+android_interface/button.svg
diff --git a/interface/resources/icons/+android/forward.svg b/interface/resources/icons/+android_interface/forward.svg
similarity index 100%
rename from interface/resources/icons/+android/forward.svg
rename to interface/resources/icons/+android_interface/forward.svg
diff --git a/interface/resources/icons/+android/go-a.svg b/interface/resources/icons/+android_interface/go-a.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/go-a.svg
rename to interface/resources/icons/+android_interface/go-a.svg
diff --git a/interface/resources/icons/+android/go-i.svg b/interface/resources/icons/+android_interface/go-i.svg
similarity index 100%
rename from interface/resources/icons/+android/go-i.svg
rename to interface/resources/icons/+android_interface/go-i.svg
diff --git a/interface/resources/icons/+android/hand.svg b/interface/resources/icons/+android_interface/hand.svg
similarity index 100%
rename from interface/resources/icons/+android/hand.svg
rename to interface/resources/icons/+android_interface/hand.svg
diff --git a/interface/resources/icons/+android/hide.svg b/interface/resources/icons/+android_interface/hide.svg
similarity index 100%
rename from interface/resources/icons/+android/hide.svg
rename to interface/resources/icons/+android_interface/hide.svg
diff --git a/interface/resources/icons/+android/mic-mute-a.svg b/interface/resources/icons/+android_interface/mic-mute-a.svg
similarity index 100%
rename from interface/resources/icons/+android/mic-mute-a.svg
rename to interface/resources/icons/+android_interface/mic-mute-a.svg
diff --git a/interface/resources/icons/+android/mic-mute-i.svg b/interface/resources/icons/+android_interface/mic-mute-i.svg
similarity index 100%
rename from interface/resources/icons/+android/mic-mute-i.svg
rename to interface/resources/icons/+android_interface/mic-mute-i.svg
diff --git a/interface/resources/icons/+android/mic-unmute-a.svg b/interface/resources/icons/+android_interface/mic-unmute-a.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/mic-unmute-a.svg
rename to interface/resources/icons/+android_interface/mic-unmute-a.svg
diff --git a/interface/resources/icons/+android/mic-unmute-i.svg b/interface/resources/icons/+android_interface/mic-unmute-i.svg
similarity index 100%
rename from interface/resources/icons/+android/mic-unmute-i.svg
rename to interface/resources/icons/+android_interface/mic-unmute-i.svg
diff --git a/interface/resources/icons/+android/myview-a.svg b/interface/resources/icons/+android_interface/myview-a.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/myview-a.svg
rename to interface/resources/icons/+android_interface/myview-a.svg
diff --git a/interface/resources/icons/+android/myview-hover.svg b/interface/resources/icons/+android_interface/myview-hover.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/myview-hover.svg
rename to interface/resources/icons/+android_interface/myview-hover.svg
diff --git a/interface/resources/icons/+android/myview-i.svg b/interface/resources/icons/+android_interface/myview-i.svg
old mode 100755
new mode 100644
similarity index 100%
rename from interface/resources/icons/+android/myview-i.svg
rename to interface/resources/icons/+android_interface/myview-i.svg
diff --git a/interface/resources/icons/+android/show-up.svg b/interface/resources/icons/+android_interface/show-up.svg
similarity index 100%
rename from interface/resources/icons/+android/show-up.svg
rename to interface/resources/icons/+android_interface/show-up.svg
diff --git a/interface/resources/icons/+android/stats.svg b/interface/resources/icons/+android_interface/stats.svg
similarity index 100%
rename from interface/resources/icons/+android/stats.svg
rename to interface/resources/icons/+android_interface/stats.svg
diff --git a/interface/resources/icons/+android/tick.svg b/interface/resources/icons/+android_interface/tick.svg
similarity index 100%
rename from interface/resources/icons/+android/tick.svg
rename to interface/resources/icons/+android_interface/tick.svg
diff --git a/interface/resources/images/unsupportedImage.png b/interface/resources/images/unsupportedImage.png
new file mode 100644
index 0000000000..87d238b67c
Binary files /dev/null and b/interface/resources/images/unsupportedImage.png differ
diff --git a/interface/resources/images/whitePixel.png b/interface/resources/images/whitePixel.png
new file mode 100644
index 0000000000..fe2ec44cb7
Binary files /dev/null and b/interface/resources/images/whitePixel.png differ
diff --git a/interface/resources/meshes/defaultAvatar_full.fst b/interface/resources/meshes/defaultAvatar_full.fst
index b47faeb8a8..aa679e319a 100644
--- a/interface/resources/meshes/defaultAvatar_full.fst
+++ b/interface/resources/meshes/defaultAvatar_full.fst
@@ -10,10 +10,6 @@ joint = jointRoot = Hips
joint = jointLeftHand = LeftHand
joint = jointRightHand = RightHand
joint = jointHead = Head
-freeJoint = LeftArm
-freeJoint = LeftForeArm
-freeJoint = RightArm
-freeJoint = RightForeArm
bs = JawOpen = mouth_Open = 1
bs = LipsFunnel = Oo = 1
bs = BrowsU_L = brow_Up = 1
diff --git a/interface/resources/qml/+android/StatText.qml b/interface/resources/qml/+android_interface/StatText.qml
similarity index 100%
rename from interface/resources/qml/+android/StatText.qml
rename to interface/resources/qml/+android_interface/StatText.qml
diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml
similarity index 100%
rename from interface/resources/qml/+android/Stats.qml
rename to interface/resources/qml/+android_interface/Stats.qml
diff --git a/interface/resources/qml/+android/Web3DSurface.qml b/interface/resources/qml/+android_interface/Web3DSurface.qml
similarity index 98%
rename from interface/resources/qml/+android/Web3DSurface.qml
rename to interface/resources/qml/+android_interface/Web3DSurface.qml
index d7b8306d6c..c4a613222e 100644
--- a/interface/resources/qml/+android/Web3DSurface.qml
+++ b/interface/resources/qml/+android_interface/Web3DSurface.qml
@@ -1,5 +1,5 @@
//
-// Web3DOverlay.qml
+// Web3DSurface.qml
//
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
// Copyright 2016 High Fidelity, Inc.
diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml
new file mode 100644
index 0000000000..5c5cf2cfb9
--- /dev/null
+++ b/interface/resources/qml/+webengine/BrowserWebView.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.5
+import QtWebChannel 1.0
+import QtWebEngine 1.5
+
+import controlsUit 1.0
+
+WebView {
+ id: webview
+ url: "https://highfidelity.com/"
+ profile: FileTypeProfile;
+
+ property var parentRoot: null
+
+ // Create a global EventBridge object for raiseAndLowerKeyboard.
+ WebEngineScript {
+ id: createGlobalEventBridge
+ sourceCode: eventBridgeJavaScriptToInject
+ injectionPoint: WebEngineScript.Deferred
+ 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 ]
+
+ onFeaturePermissionRequested: {
+ if (feature == 2) { // QWebEnginePage::MediaAudioCapture
+ grantFeaturePermission(securityOrigin, feature, true);
+ } else {
+ permissionsBar.securityOrigin = securityOrigin;
+ permissionsBar.feature = feature;
+ parentRoot.showPermissionsBar();
+ }
+ }
+
+ onLoadingChanged: {
+ if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
+ addressBar.text = loadRequest.url
+ }
+ parentRoot.loadingChanged(loadRequest.status);
+ }
+
+ onWindowCloseRequested: {
+ parentRoot.destroy();
+ }
+
+ Component.onCompleted: {
+ webChannel.registerObject("eventBridge", eventBridge);
+ webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+ desktop.initWebviewProfileHandlers(webview.profile);
+ }
+}
diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml
new file mode 100644
index 0000000000..d2f1820e9a
--- /dev/null
+++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.5
+import QtWebEngine 1.1
+import QtWebChannel 1.0
+
+import controlsUit 1.0 as Controls
+import stylesUit 1.0
+Controls.WebView {
+ id: webview
+ url: "about:blank"
+ anchors.fill: parent
+ focus: true
+ profile: HFWebEngineProfile;
+
+ property string userScriptUrl: ""
+
+ // 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
+ }
+
+ // User script.
+ WebEngineScript {
+ id: userScript
+ sourceUrl: webview.userScriptUrl
+ injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
+ worldId: WebEngineScript.MainWorld
+ }
+
+ userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+ function onWebEventReceived(event) {
+ if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
+ ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
+ }
+ }
+
+ Component.onCompleted: {
+ webChannel.registerObject("eventBridge", eventBridge);
+ webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+ eventBridge.webEventReceived.connect(onWebEventReceived);
+ }
+}
diff --git a/interface/resources/qml/+webengine/TabletBrowser.qml b/interface/resources/qml/+webengine/TabletBrowser.qml
new file mode 100644
index 0000000000..720a904231
--- /dev/null
+++ b/interface/resources/qml/+webengine/TabletBrowser.qml
@@ -0,0 +1,125 @@
+import QtQuick 2.5
+import QtWebChannel 1.0
+import QtWebEngine 1.5
+
+import "controls"
+import controlsUit 1.0 as HifiControls
+import "styles" as HifiStyles
+import stylesUit 1.0
+import "windows"
+
+Item {
+ id: root
+ HifiConstants { id: hifi }
+ HifiStyles.HifiConstants { id: hifistyles }
+
+ height: 600
+ property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
+ property alias url: webview.url
+
+ property bool canGoBack: webview.canGoBack
+ property bool canGoForward: webview.canGoForward
+
+
+ signal loadingChanged(int status)
+
+ x: 0
+ y: 0
+
+ function setProfile(profile) {
+ webview.profile = profile;
+ }
+
+ WebEngineView {
+ id: webview
+ objectName: "webEngineView"
+ x: 0
+ y: 0
+ width: parent.width
+ height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
+
+ profile: HFWebEngineProfile;
+
+ property string userScriptUrl: ""
+
+ // creates a global EventBridge object.
+ WebEngineScript {
+ id: createGlobalEventBridge
+ sourceCode: eventBridgeJavaScriptToInject
+ injectionPoint: WebEngineScript.DocumentCreation
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // detects when to raise and lower virtual keyboard
+ WebEngineScript {
+ id: raiseAndLowerKeyboard
+ injectionPoint: WebEngineScript.Deferred
+ sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // User script.
+ WebEngineScript {
+ id: userScript
+ sourceUrl: webview.userScriptUrl
+ injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
+ worldId: WebEngineScript.MainWorld
+ }
+
+ userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+ property string newUrl: ""
+
+ Component.onCompleted: {
+ webChannel.registerObject("eventBridge", eventBridge);
+ webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+
+ // Ensure the JS from the web-engine makes it to our logging
+ webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+ console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
+ });
+
+ webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
+ web.address = url;
+ }
+
+ onFeaturePermissionRequested: {
+ grantFeaturePermission(securityOrigin, feature, true);
+ }
+
+ onLoadingChanged: {
+ keyboardRaised = false;
+ punctuationMode = false;
+ keyboard.resetShiftMode(false);
+
+ // Required to support clicking on "hifi://" links
+ if (WebEngineView.LoadStartedStatus == loadRequest.status) {
+ urlAppend(loadRequest.url.toString())
+ var url = loadRequest.url.toString();
+ if (urlHandler.canHandleUrl(url)) {
+ if (urlHandler.handleUrl(url)) {
+ root.stop();
+ }
+ }
+ }
+ }
+
+ onNewViewRequested: {
+ request.openIn(webView);
+ }
+
+ HifiControls.WebSpinner { }
+ }
+
+ Keys.onPressed: {
+ switch(event.key) {
+ case Qt.Key_L:
+ if (event.modifiers == Qt.ControlModifier) {
+ event.accepted = true
+ addressBar.selectAll()
+ addressBar.forceActiveFocus()
+ }
+ break;
+ }
+ }
+}
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 4ddee15a10..496209a2a8 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -1,16 +1,13 @@
import QtQuick 2.5
-import QtWebChannel 1.0
-import QtWebEngine 1.5
-
import controlsUit 1.0
-import "styles" as HifiStyles
import stylesUit 1.0
import "windows"
+import "."
ScrollingWindow {
id: root
HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifistyles }
+
title: "Browser"
resizable: true
destroyOnHidden: true
@@ -68,7 +65,7 @@ ScrollingWindow {
id: back;
enabled: webview.canGoBack;
text: hifi.glyphs.backward
- color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+ color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goBack() }
}
@@ -77,7 +74,7 @@ ScrollingWindow {
id: forward;
enabled: webview.canGoForward;
text: hifi.glyphs.forward
- color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+ color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
}
@@ -86,7 +83,7 @@ ScrollingWindow {
id: reload;
enabled: webview.canGoForward;
text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
- color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
+ color: enabled ? hifi.colors.text : hifi.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
}
@@ -202,61 +199,15 @@ ScrollingWindow {
}
}
- WebView {
+ BrowserWebView {
id: webview
- url: "https://highfidelity.com/"
- profile: FileTypeProfile;
-
- // Create a global EventBridge object for raiseAndLowerKeyboard.
- WebEngineScript {
- id: createGlobalEventBridge
- sourceCode: eventBridgeJavaScriptToInject
- injectionPoint: WebEngineScript.Deferred
- 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 ]
+ parentRoot: root
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
-
- onFeaturePermissionRequested: {
- if (feature == 2) { // QWebEnginePage::MediaAudioCapture
- grantFeaturePermission(securityOrigin, feature, true);
- } else {
- permissionsBar.securityOrigin = securityOrigin;
- permissionsBar.feature = feature;
- root.showPermissionsBar();
- }
- }
-
- onLoadingChanged: {
- if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
- addressBar.text = loadRequest.url
- }
- root.loadingChanged(loadRequest.status);
- }
-
- onWindowCloseRequested: {
- root.destroy();
- }
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
- desktop.initWebviewProfileHandlers(webview.profile);
- }
}
} // item
diff --git a/interface/resources/qml/BrowserWebView.qml b/interface/resources/qml/BrowserWebView.qml
new file mode 100644
index 0000000000..6db016c284
--- /dev/null
+++ b/interface/resources/qml/BrowserWebView.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.5
+import controlsUit 1.0
+
+ProxyWebView {
+ property var parentRoot: null
+
+ function grantFeaturePermission(origin, feature) {}
+}
diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml
index 8c5900b4c3..2fd0ddf925 100644
--- a/interface/resources/qml/InfoView.qml
+++ b/interface/resources/qml/InfoView.qml
@@ -19,13 +19,12 @@ Windows.ScrollingWindow {
width: 800
height: 800
resizable: true
-
Hifi.InfoView {
id: infoView
width: pane.contentWidth
implicitHeight: pane.scrollHeight
- WebView {
+ BaseWebView {
id: webview
objectName: "WebView"
anchors.fill: parent
diff --git a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml
similarity index 100%
rename from interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml
rename to interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml
diff --git a/interface/resources/qml/LoginDialog/+android/SignUpBody.qml b/interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml
similarity index 100%
rename from interface/resources/qml/LoginDialog/+android/SignUpBody.qml
rename to interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml
diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml
index 322535641d..7ee4b1c50c 100644
--- a/interface/resources/qml/QmlWebWindow.qml
+++ b/interface/resources/qml/QmlWebWindow.qml
@@ -9,10 +9,9 @@
//
import QtQuick 2.5
-import QtWebEngine 1.1
-import QtWebChannel 1.0
import "windows" as Windows
+import "."
import controlsUit 1.0 as Controls
import stylesUit 1.0
@@ -57,52 +56,8 @@ Windows.ScrollingWindow {
width: pane.contentWidth
implicitHeight: pane.scrollHeight
- Controls.WebView {
+ QmlWebWindowView {
id: webview
- url: "about:blank"
- anchors.fill: parent
- focus: true
- profile: HFWebEngineProfile;
-
- property string userScriptUrl: ""
-
- // 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
- }
-
- // User script.
- WebEngineScript {
- id: userScript
- sourceUrl: webview.userScriptUrl
- injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
- worldId: WebEngineScript.MainWorld
- }
-
- userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
- function onWebEventReceived(event) {
- if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
- ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
- }
- }
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
- eventBridge.webEventReceived.connect(onWebEventReceived);
- }
}
}
}
diff --git a/interface/resources/qml/QmlWebWindowView.qml b/interface/resources/qml/QmlWebWindowView.qml
new file mode 100644
index 0000000000..9210468ae2
--- /dev/null
+++ b/interface/resources/qml/QmlWebWindowView.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.5
+import controlsUit 1.0
+
+BaseWebView {
+}
diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml
index a65170ee3b..3b703d72e6 100644
--- a/interface/resources/qml/Stats.qml
+++ b/interface/resources/qml/Stats.qml
@@ -129,11 +129,11 @@ Item {
}
StatText {
visible: root.expanded
- text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " +
- "Styluses:\t" + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " +
- "Rays:\t" + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " +
- "Parabolas:\t" + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " +
- "Colliders:\t" + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w
+ text: "Intersection calls: Entities/Avatars/HUD\n " +
+ "Styluses:\t" + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "\n " +
+ "Rays:\t" + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "\n " +
+ "Parabolas:\t" + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "\n " +
+ "Colliders:\t" + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z
}
}
}
diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml
index 720a904231..f83a9c81a5 100644
--- a/interface/resources/qml/TabletBrowser.qml
+++ b/interface/resources/qml/TabletBrowser.qml
@@ -1,17 +1,11 @@
import QtQuick 2.5
-import QtWebChannel 1.0
-import QtWebEngine 1.5
-import "controls"
import controlsUit 1.0 as HifiControls
-import "styles" as HifiStyles
import stylesUit 1.0
-import "windows"
Item {
id: root
HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifistyles }
height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
@@ -30,96 +24,9 @@ Item {
webview.profile = profile;
}
- WebEngineView {
+ HifiControls.ProxyWebView {
id: webview
- objectName: "webEngineView"
- x: 0
- y: 0
width: parent.width
- height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
-
- profile: HFWebEngineProfile;
-
- property string userScriptUrl: ""
-
- // creates a global EventBridge object.
- WebEngineScript {
- id: createGlobalEventBridge
- sourceCode: eventBridgeJavaScriptToInject
- injectionPoint: WebEngineScript.DocumentCreation
- worldId: WebEngineScript.MainWorld
- }
-
- // detects when to raise and lower virtual keyboard
- WebEngineScript {
- id: raiseAndLowerKeyboard
- injectionPoint: WebEngineScript.Deferred
- sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
- worldId: WebEngineScript.MainWorld
- }
-
- // User script.
- WebEngineScript {
- id: userScript
- sourceUrl: webview.userScriptUrl
- injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
- worldId: WebEngineScript.MainWorld
- }
-
- userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
- property string newUrl: ""
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-
- // Ensure the JS from the web-engine makes it to our logging
- webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
- console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
- });
-
- webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
- web.address = url;
- }
-
- onFeaturePermissionRequested: {
- grantFeaturePermission(securityOrigin, feature, true);
- }
-
- onLoadingChanged: {
- keyboardRaised = false;
- punctuationMode = false;
- keyboard.resetShiftMode(false);
-
- // Required to support clicking on "hifi://" links
- if (WebEngineView.LoadStartedStatus == loadRequest.status) {
- urlAppend(loadRequest.url.toString())
- var url = loadRequest.url.toString();
- if (urlHandler.canHandleUrl(url)) {
- if (urlHandler.handleUrl(url)) {
- root.stop();
- }
- }
- }
- }
-
- onNewViewRequested: {
- request.openIn(webView);
- }
-
- HifiControls.WebSpinner { }
- }
-
- Keys.onPressed: {
- switch(event.key) {
- case Qt.Key_L:
- if (event.modifiers == Qt.ControlModifier) {
- event.accepted = true
- addressBar.selectAll()
- addressBar.forceActiveFocus()
- }
- break;
- }
+ height: parent.height
}
}
diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml
index fdd5d8a7c6..32c19daf14 100644
--- a/interface/resources/qml/Web3DSurface.qml
+++ b/interface/resources/qml/Web3DSurface.qml
@@ -1,5 +1,5 @@
//
-// Web3DOverlay.qml
+// Web3DSurface.qml
//
// Created by David Rowe on 16 Dec 2016.
// Copyright 2016 High Fidelity, Inc.
diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml
new file mode 100644
index 0000000000..823d0107a2
--- /dev/null
+++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml
@@ -0,0 +1,189 @@
+import QtQuick 2.7
+import QtWebEngine 1.5
+import QtWebChannel 1.0
+
+import QtQuick.Controls 2.2
+
+import stylesUit 1.0 as StylesUIt
+
+Item {
+ id: flick
+
+ property alias url: webViewCore.url
+ property alias canGoBack: webViewCore.canGoBack
+ property alias webViewCore: webViewCore
+ property alias webViewCoreProfile: webViewCore.profile
+ property string webViewCoreUserAgent
+
+ property string userScriptUrl: ""
+ property string urlTag: "noDownload=false";
+
+ signal newViewRequestedCallback(var request)
+ signal loadingChangedCallback(var loadRequest)
+
+
+ width: parent.width
+
+ property bool interactive: false
+
+ property bool blurOnCtrlShift: true
+
+ StylesUIt.HifiConstants {
+ id: hifi
+ }
+
+ function stop() {
+ webViewCore.stop();
+ }
+
+ Timer {
+ id: delayedUnfocuser
+ repeat: false
+ interval: 200
+ onTriggered: {
+
+ // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
+ // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
+ //
+ // if (raised) {
+ // item->setProperty("keyboardRaised", QVariant(!raised));
+ // }
+ //
+ // item->setProperty("keyboardRaised", QVariant(raised));
+ //
+
+ webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
+ console.log('unfocus completed: ', result);
+ });
+ }
+ }
+
+ function unfocus() {
+ delayedUnfocuser.start();
+ }
+
+ function stopUnfocus() {
+ delayedUnfocuser.stop();
+ }
+
+ function onLoadingChanged(loadRequest) {
+ if (WebEngineView.LoadStartedStatus === loadRequest.status) {
+
+ // Required to support clicking on "hifi://" links
+ var url = loadRequest.url.toString();
+ url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
+ if (urlHandler.canHandleUrl(url)) {
+ if (urlHandler.handleUrl(url)) {
+ webViewCore.stop();
+ }
+ }
+ }
+
+ if (WebEngineView.LoadFailedStatus === loadRequest.status) {
+ console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
+ }
+
+ if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
+ //disable Chromium's scroll bars
+ }
+ }
+
+ WebEngineView {
+ id: webViewCore
+
+ width: parent.width
+ height: parent.height
+
+ profile: HFWebEngineProfile;
+ settings.pluginsEnabled: true
+ settings.touchIconsEnabled: true
+ settings.allowRunningInsecureContent: true
+
+ // creates a global EventBridge object.
+ WebEngineScript {
+ id: createGlobalEventBridge
+ sourceCode: eventBridgeJavaScriptToInject
+ injectionPoint: WebEngineScript.DocumentCreation
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // detects when to raise and lower virtual keyboard
+ WebEngineScript {
+ id: raiseAndLowerKeyboard
+ injectionPoint: WebEngineScript.Deferred
+ sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
+ worldId: WebEngineScript.MainWorld
+ }
+
+ // User script.
+ WebEngineScript {
+ id: userScript
+ sourceUrl: flick.userScriptUrl
+ injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
+ worldId: WebEngineScript.MainWorld
+ }
+
+ userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
+
+ Component.onCompleted: {
+ webChannel.registerObject("eventBridge", eventBridge);
+ webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
+
+ if (webViewCoreUserAgent !== undefined) {
+ webViewCore.profile.httpUserAgent = webViewCoreUserAgent
+ } else {
+ webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
+ }
+ // Ensure the JS from the web-engine makes it to our logging
+ webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+ console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
+ });
+ }
+
+ onFeaturePermissionRequested: {
+ grantFeaturePermission(securityOrigin, feature, true);
+ }
+
+ //disable popup
+ onContextMenuRequested: {
+ request.accepted = true;
+ }
+
+ onNewViewRequested: {
+ newViewRequestedCallback(request)
+ }
+
+ // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
+ // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
+ // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
+ // when QT fixes this.
+ property bool safeLoading: false
+ property bool loadingLatched: false
+ property var loadingRequest: null
+ onLoadingChanged: {
+ webViewCore.loadingRequest = loadRequest;
+ webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
+ webViewCore.loadingLatched |= webViewCore.loading;
+ }
+ onSafeLoadingChanged: {
+ flick.onLoadingChanged(webViewCore.loadingRequest)
+ loadingChangedCallback(webViewCore.loadingRequest)
+ }
+ }
+
+ AnimatedImage {
+ //anchoring doesnt works here when changing content size
+ x: flick.width/2 - width/2
+ y: flick.height/2 - height/2
+ source: "../../icons/loader-snake-64-w.gif"
+ visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
+ playing: visible
+ z: 10000
+ }
+
+ Keys.onPressed: {
+ if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
+ webViewCore.focus = false;
+ }
+ }
+}
diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index 823d0107a2..a844c8b624 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -1,10 +1,9 @@
import QtQuick 2.7
-import QtWebEngine 1.5
-import QtWebChannel 1.0
import QtQuick.Controls 2.2
import stylesUit 1.0 as StylesUIt
+import controlsUit 1.0 as ControlsUit
Item {
id: flick
@@ -33,157 +32,21 @@ Item {
}
function stop() {
- webViewCore.stop();
- }
- Timer {
- id: delayedUnfocuser
- repeat: false
- interval: 200
- onTriggered: {
-
- // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
- // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
- //
- // if (raised) {
- // item->setProperty("keyboardRaised", QVariant(!raised));
- // }
- //
- // item->setProperty("keyboardRaised", QVariant(raised));
- //
-
- webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
- console.log('unfocus completed: ', result);
- });
- }
}
function unfocus() {
- delayedUnfocuser.start();
}
function stopUnfocus() {
- delayedUnfocuser.stop();
}
function onLoadingChanged(loadRequest) {
- if (WebEngineView.LoadStartedStatus === loadRequest.status) {
-
- // Required to support clicking on "hifi://" links
- var url = loadRequest.url.toString();
- url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
- if (urlHandler.canHandleUrl(url)) {
- if (urlHandler.handleUrl(url)) {
- webViewCore.stop();
- }
- }
- }
-
- if (WebEngineView.LoadFailedStatus === loadRequest.status) {
- console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
- }
-
- if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
- //disable Chromium's scroll bars
- }
}
- WebEngineView {
+ ControlsUit.ProxyWebView {
id: webViewCore
-
width: parent.width
height: parent.height
-
- profile: HFWebEngineProfile;
- settings.pluginsEnabled: true
- settings.touchIconsEnabled: true
- settings.allowRunningInsecureContent: true
-
- // creates a global EventBridge object.
- WebEngineScript {
- id: createGlobalEventBridge
- sourceCode: eventBridgeJavaScriptToInject
- injectionPoint: WebEngineScript.DocumentCreation
- worldId: WebEngineScript.MainWorld
- }
-
- // detects when to raise and lower virtual keyboard
- WebEngineScript {
- id: raiseAndLowerKeyboard
- injectionPoint: WebEngineScript.Deferred
- sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
- worldId: WebEngineScript.MainWorld
- }
-
- // User script.
- WebEngineScript {
- id: userScript
- sourceUrl: flick.userScriptUrl
- injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
- worldId: WebEngineScript.MainWorld
- }
-
- userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
-
- if (webViewCoreUserAgent !== undefined) {
- webViewCore.profile.httpUserAgent = webViewCoreUserAgent
- } else {
- webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
- }
- // Ensure the JS from the web-engine makes it to our logging
- webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
- console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
- });
- }
-
- onFeaturePermissionRequested: {
- grantFeaturePermission(securityOrigin, feature, true);
- }
-
- //disable popup
- onContextMenuRequested: {
- request.accepted = true;
- }
-
- onNewViewRequested: {
- newViewRequestedCallback(request)
- }
-
- // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
- // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
- // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
- // when QT fixes this.
- property bool safeLoading: false
- property bool loadingLatched: false
- property var loadingRequest: null
- onLoadingChanged: {
- webViewCore.loadingRequest = loadRequest;
- webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
- webViewCore.loadingLatched |= webViewCore.loading;
- }
- onSafeLoadingChanged: {
- flick.onLoadingChanged(webViewCore.loadingRequest)
- loadingChangedCallback(webViewCore.loadingRequest)
- }
- }
-
- AnimatedImage {
- //anchoring doesnt works here when changing content size
- x: flick.width/2 - width/2
- y: flick.height/2 - height/2
- source: "../../icons/loader-snake-64-w.gif"
- visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
- playing: visible
- z: 10000
- }
-
- Keys.onPressed: {
- if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
- webViewCore.focus = false;
- }
}
}
diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml
index 3959dbf01b..9cbbd48a22 100644
--- a/interface/resources/qml/controls/TabletWebView.qml
+++ b/interface/resources/qml/controls/TabletWebView.qml
@@ -1,5 +1,4 @@
import QtQuick 2.7
-import QtWebEngine 1.5
import controlsUit 1.0 as HiFiControls
import "../styles" as HifiStyles
import stylesUit 1.0
diff --git a/interface/resources/qml/controlsUit/+android/ImageButton.qml b/interface/resources/qml/controlsUit/+android_interface/ImageButton.qml
similarity index 100%
rename from interface/resources/qml/controlsUit/+android/ImageButton.qml
rename to interface/resources/qml/controlsUit/+android_interface/ImageButton.qml
diff --git a/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml
new file mode 100644
index 0000000000..fdd9c12220
--- /dev/null
+++ b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml
@@ -0,0 +1,38 @@
+//
+// WebView.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.7
+import QtWebEngine 1.5
+
+WebEngineView {
+ id: root
+
+ Component.onCompleted: {
+ console.log("Connecting JS messaging to Hifi Logging")
+ // Ensure the JS from the web-engine makes it to our logging
+ root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
+ console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
+ });
+ }
+
+ onLoadingChanged: {
+ // Required to support clicking on "hifi://" links
+ if (WebEngineView.LoadStartedStatus == loadRequest.status) {
+ var url = loadRequest.url.toString();
+ if (urlHandler.canHandleUrl(url)) {
+ if (urlHandler.handleUrl(url)) {
+ root.stop();
+ }
+ }
+ }
+ }
+
+ WebSpinner { }
+}
diff --git a/interface/resources/qml/controlsUit/+webengine/WebSpinner.qml b/interface/resources/qml/controlsUit/+webengine/WebSpinner.qml
new file mode 100644
index 0000000000..c2e2ca4b40
--- /dev/null
+++ b/interface/resources/qml/controlsUit/+webengine/WebSpinner.qml
@@ -0,0 +1,24 @@
+//
+// WebSpinner.qml
+//
+// Created by David Rowe on 23 May 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 QtWebEngine 1.5
+
+AnimatedImage {
+ property WebEngineView webview: parent
+ source: "qrc:////icons//loader-snake-64-w.gif"
+ visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
+ playing: visible
+ z: 10000
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+}
diff --git a/interface/resources/qml/controlsUit/BaseWebView.qml b/interface/resources/qml/controlsUit/BaseWebView.qml
index fdd9c12220..52b2d1f3db 100644
--- a/interface/resources/qml/controlsUit/BaseWebView.qml
+++ b/interface/resources/qml/controlsUit/BaseWebView.qml
@@ -8,31 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import QtQuick 2.7
-import QtWebEngine 1.5
+import "."
-WebEngineView {
+ProxyWebView {
id: root
-
- Component.onCompleted: {
- console.log("Connecting JS messaging to Hifi Logging")
- // Ensure the JS from the web-engine makes it to our logging
- root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
- console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
- });
- }
-
- onLoadingChanged: {
- // Required to support clicking on "hifi://" links
- if (WebEngineView.LoadStartedStatus == loadRequest.status) {
- var url = loadRequest.url.toString();
- if (urlHandler.canHandleUrl(url)) {
- if (urlHandler.handleUrl(url)) {
- root.stop();
- }
- }
- }
- }
-
- WebSpinner { }
}
diff --git a/interface/resources/qml/controlsUit/ProxyWebView.qml b/interface/resources/qml/controlsUit/ProxyWebView.qml
new file mode 100644
index 0000000000..2b13760962
--- /dev/null
+++ b/interface/resources/qml/controlsUit/ProxyWebView.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.7
+import stylesUit 1.0
+
+Rectangle {
+ HifiConstants {
+ id: hifi
+ }
+
+ color: hifi.colors.darkGray
+
+ signal onNewViewRequested();
+
+ property string url: "";
+ property bool canGoBack: false
+ property bool canGoForward: false
+ property string icon: ""
+ property var profile: {}
+
+ property bool safeLoading: false
+ property bool loadingLatched: false
+ property bool loading: false
+ property var loadingRequest: null
+
+
+ Text {
+ anchors.centerIn: parent
+ text: "This feature is not supported"
+ font.pixelSize: 32
+ color: hifi.colors.white
+ }
+}
diff --git a/interface/resources/qml/controlsUit/WebSpinner.qml b/interface/resources/qml/controlsUit/WebSpinner.qml
index e8e01c4865..bcf415e0c0 100644
--- a/interface/resources/qml/controlsUit/WebSpinner.qml
+++ b/interface/resources/qml/controlsUit/WebSpinner.qml
@@ -9,11 +9,15 @@
//
import QtQuick 2.5
-import QtWebEngine 1.5
-AnimatedImage {
- property WebEngineView webview: parent
- source: "../../icons/loader-snake-64-w.gif"
+Image {
+ Item {
+ id: webView
+ property bool loading: false
+ property string url: ""
+ }
+
+ source: "qrc:////images//unsupportedImage.png"
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
playing: visible
z: 10000
diff --git a/interface/resources/qml/controlsUit/qmldir b/interface/resources/qml/controlsUit/qmldir
index d0577f5575..e1665df40e 100644
--- a/interface/resources/qml/controlsUit/qmldir
+++ b/interface/resources/qml/controlsUit/qmldir
@@ -29,6 +29,7 @@ TextEdit 1.0 TextEdit.qml
TextField 1.0 TextField.qml
ToolTip 1.0 ToolTip.qml
Tree 1.0 Tree.qml
+ProxyWebView 1.0 ProxyWebView.qml
VerticalSpacer 1.0 VerticalSpacer.qml
WebGlyphButton 1.0 WebGlyphButton.qml
WebSpinner 1.0 WebSpinner.qml
diff --git a/interface/resources/qml/desktop/+android/FocusHack.qml b/interface/resources/qml/desktop/+android_interface/FocusHack.qml
similarity index 100%
rename from interface/resources/qml/desktop/+android/FocusHack.qml
rename to interface/resources/qml/desktop/+android_interface/FocusHack.qml
diff --git a/interface/resources/qml/hifi/+android/ActionBar.qml b/interface/resources/qml/hifi/+android_interface/ActionBar.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/ActionBar.qml
rename to interface/resources/qml/hifi/+android_interface/ActionBar.qml
diff --git a/interface/resources/qml/hifi/+android/AudioBar.qml b/interface/resources/qml/hifi/+android_interface/AudioBar.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/AudioBar.qml
rename to interface/resources/qml/hifi/+android_interface/AudioBar.qml
diff --git a/interface/resources/qml/hifi/+android/AvatarOption.qml b/interface/resources/qml/hifi/+android_interface/AvatarOption.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/AvatarOption.qml
rename to interface/resources/qml/hifi/+android_interface/AvatarOption.qml
diff --git a/interface/resources/qml/hifi/+android/Desktop.qml b/interface/resources/qml/hifi/+android_interface/Desktop.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/Desktop.qml
rename to interface/resources/qml/hifi/+android_interface/Desktop.qml
diff --git a/interface/resources/qml/hifi/+android/HifiConstants.qml b/interface/resources/qml/hifi/+android_interface/HifiConstants.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/HifiConstants.qml
rename to interface/resources/qml/hifi/+android_interface/HifiConstants.qml
diff --git a/interface/resources/qml/hifi/+android/StatsBar.qml b/interface/resources/qml/hifi/+android_interface/StatsBar.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/StatsBar.qml
rename to interface/resources/qml/hifi/+android_interface/StatsBar.qml
diff --git a/interface/resources/qml/hifi/+android/WindowHeader.qml b/interface/resources/qml/hifi/+android_interface/WindowHeader.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/WindowHeader.qml
rename to interface/resources/qml/hifi/+android_interface/WindowHeader.qml
diff --git a/interface/resources/qml/hifi/+android/bottomHudOptions.qml b/interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/bottomHudOptions.qml
rename to interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml
diff --git a/interface/resources/qml/hifi/+android/button.qml b/interface/resources/qml/hifi/+android_interface/button.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/button.qml
rename to interface/resources/qml/hifi/+android_interface/button.qml
diff --git a/interface/resources/qml/hifi/+android/modesbar.qml b/interface/resources/qml/hifi/+android_interface/modesbar.qml
similarity index 100%
rename from interface/resources/qml/hifi/+android/modesbar.qml
rename to interface/resources/qml/hifi/+android_interface/modesbar.qml
diff --git a/interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml b/interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
new file mode 100644
index 0000000000..56cc38254f
--- /dev/null
+++ b/interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.7
+import QtWebEngine 1.5
+
+Item {
+ id: root
+
+ property bool webViewProfileSetup: false
+ property string currentUrl: ""
+ property string downloadUrl: ""
+ property string adaptedPath: ""
+ property string tempDir: ""
+ function setupWebEngineSettings() {
+ WebEngine.settings.javascriptCanOpenWindows = true;
+ WebEngine.settings.javascriptCanAccessClipboard = false;
+ WebEngine.settings.spatialNavigationEnabled = false;
+ WebEngine.settings.localContentCanAccessRemoteUrls = true;
+ }
+
+
+ function initWebviewProfileHandlers(profile) {
+ downloadUrl = currentUrl;
+ if (webViewProfileSetup) return;
+ webViewProfileSetup = true;
+
+ profile.downloadRequested.connect(function(download){
+ adaptedPath = File.convertUrlToPath(downloadUrl);
+ tempDir = File.getTempDir();
+ download.path = tempDir + "/" + adaptedPath;
+ download.accept();
+ if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
+ console.log("download failed to complete");
+ }
+ })
+
+ profile.downloadFinished.connect(function(download){
+ if (download.state === WebEngineDownloadItem.DownloadCompleted) {
+ File.runUnzip(download.path, downloadUrl, autoAdd);
+ } else {
+ console.log("The download was corrupted, state: " + download.state);
+ }
+ autoAdd = false;
+ })
+ }
+}
diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml
index 731477e2ae..c44ebdbab1 100644
--- a/interface/resources/qml/hifi/Desktop.qml
+++ b/interface/resources/qml/hifi/Desktop.qml
@@ -1,5 +1,4 @@
import QtQuick 2.7
-import QtWebEngine 1.5;
import Qt.labs.settings 1.0 as QtSettings
import QtQuick.Controls 2.3
@@ -88,43 +87,20 @@ OriginalDesktop.Desktop {
})({});
Component.onCompleted: {
- WebEngine.settings.javascriptCanOpenWindows = true;
- WebEngine.settings.javascriptCanAccessClipboard = false;
- WebEngine.settings.spatialNavigationEnabled = false;
- WebEngine.settings.localContentCanAccessRemoteUrls = true;
+ webEngineConfig.setupWebEngineSettings();
}
// Accept a download through the webview
- property bool webViewProfileSetup: false
- property string currentUrl: ""
- property string downloadUrl: ""
- property string adaptedPath: ""
- property string tempDir: ""
+ property alias webViewProfileSetup: webEngineConfig.webViewProfileSetup
+ property alias currentUrl: webEngineConfig.currentUrl
+ property alias downloadUrl: webEngineConfig.downloadUrl
+ property alias adaptedPath: webEngineConfig.adaptedPath
+ property alias tempDir: webEngineConfig.tempDir
+ property var initWebviewProfileHandlers: webEngineConfig.initWebviewProfileHandlers
property bool autoAdd: false
- function initWebviewProfileHandlers(profile) {
- downloadUrl = currentUrl;
- if (webViewProfileSetup) return;
- webViewProfileSetup = true;
-
- profile.downloadRequested.connect(function(download){
- adaptedPath = File.convertUrlToPath(downloadUrl);
- tempDir = File.getTempDir();
- download.path = tempDir + "/" + adaptedPath;
- download.accept();
- if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
- console.log("download failed to complete");
- }
- })
-
- profile.downloadFinished.connect(function(download){
- if (download.state === WebEngineDownloadItem.DownloadCompleted) {
- File.runUnzip(download.path, downloadUrl, autoAdd);
- } else {
- console.log("The download was corrupted, state: " + download.state);
- }
- autoAdd = false;
- })
+ DesktopWebEngine {
+ id: webEngineConfig
}
function setAutoAdd(auto) {
diff --git a/interface/resources/qml/hifi/DesktopWebEngine.qml b/interface/resources/qml/hifi/DesktopWebEngine.qml
new file mode 100644
index 0000000000..58c6244e7e
--- /dev/null
+++ b/interface/resources/qml/hifi/DesktopWebEngine.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.7
+
+Item {
+ id: root
+
+ property bool webViewProfileSetup: false
+ property string currentUrl: ""
+ property string downloadUrl: ""
+ property string adaptedPath: ""
+ property string tempDir: ""
+ function setupWebEngineSettings() {
+ }
+
+
+ function initWebviewProfileHandlers(profile) {
+ }
+}
diff --git a/interface/resources/qml/hifi/WebBrowser.qml b/interface/resources/qml/hifi/WebBrowser.qml
deleted file mode 100644
index c05de26471..0000000000
--- a/interface/resources/qml/hifi/WebBrowser.qml
+++ /dev/null
@@ -1,443 +0,0 @@
-
-//
-// WebBrowser.qml
-//
-//
-// Created by Vlad Stelmahovsky on 06/22/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.7
-import QtQuick.Controls 2.2 as QQControls
-import QtQuick.Layouts 1.3
-import QtGraphicalEffects 1.0
-
-import QtWebEngine 1.5
-import QtWebChannel 1.0
-
-import stylesUit 1.0
-import controlsUit 1.0 as HifiControls
-import "../windows"
-import "../controls"
-
-import HifiWeb 1.0
-
-Rectangle {
- id: root;
-
- HifiConstants { id: hifi; }
-
- property string title: "";
- signal sendToScript(var message);
- property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
- property bool keyboardRaised: false
- property bool punctuationMode: false
- property var suggestionsList: []
- readonly property string searchUrlTemplate: "https://www.google.com/search?client=hifibrowser&q=";
-
-
- WebBrowserSuggestionsEngine {
- id: searchEngine
-
- onSuggestions: {
- if (suggestions.length > 0) {
- suggestionsList = []
- suggestionsList.push(addressBarInput.text); //do not overwrite edit text
- for(var i = 0; i < suggestions.length; i++) {
- suggestionsList.push(suggestions[i]);
- }
- addressBar.model = suggestionsList
- if (!addressBar.popup.visible) {
- addressBar.popup.open();
- }
- }
- }
- }
-
- Timer {
- id: suggestionRequestTimer
- interval: 200
- repeat: false
- onTriggered: {
- if (addressBar.editText !== "") {
- searchEngine.querySuggestions(addressBarInput.text);
- }
- }
- }
-
- color: hifi.colors.baseGray;
-
- function goTo(url) {
- //must be valid attempt to open an site with dot
- var urlNew = url
- if (url.indexOf(".") > 0) {
- if (url.indexOf("http") < 0) {
- urlNew = "http://" + url;
- }
- } else {
- urlNew = searchUrlTemplate + url
- }
-
- addressBar.model = []
- //need to rebind if binfing was broken by selecting from suggestions
- addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
- webStack.currentItem.webEngineView.url = urlNew
- suggestionRequestTimer.stop();
- addressBar.popup.close();
- }
-
- Column {
- spacing: 2
- width: parent.width;
-
- RowLayout {
- id: addressBarRow
- width: parent.width;
- height: 48
-
- HifiControls.WebGlyphButton {
- enabled: webStack.currentItem.webEngineView.canGoBack || webStack.depth > 1
- glyph: hifi.glyphs.backward;
- anchors.verticalCenter: parent.verticalCenter;
- size: 38;
- onClicked: {
- if (webStack.currentItem.webEngineView.canGoBack) {
- webStack.currentItem.webEngineView.goBack();
- } else if (webStack.depth > 1) {
- webStack.pop();
- }
- }
- }
-
- HifiControls.WebGlyphButton {
- enabled: webStack.currentItem.webEngineView.canGoForward
- glyph: hifi.glyphs.forward;
- anchors.verticalCenter: parent.verticalCenter;
- size: 38;
- onClicked: {
- webStack.currentItem.webEngineView.goForward();
- }
- }
-
- QQControls.ComboBox {
- id: addressBar
-
- //selectByMouse: true
- focus: true
-
- editable: true
- //flat: true
- indicator: Item {}
- background: Item {}
- onActivated: {
- goTo(textAt(index));
- }
-
- onHighlightedIndexChanged: {
- if (highlightedIndex >= 0) {
- addressBar.editText = textAt(highlightedIndex)
- }
- }
-
- popup.height: webStack.height
-
- onFocusChanged: {
- if (focus) {
- addressBarInput.selectAll();
- }
- }
-
- contentItem: QQControls.TextField {
- id: addressBarInput
- leftPadding: 26
- rightPadding: hifi.dimensions.controlLineHeight + 5
- text: addressBar.editText
- placeholderText: qsTr("Enter URL")
- font: addressBar.font
- selectByMouse: true
- horizontalAlignment: Text.AlignLeft
- verticalAlignment: Text.AlignVCenter
- onFocusChanged: {
- if (focus) {
- selectAll();
- }
- }
-
- Keys.onDeletePressed: {
- addressBarInput.text = ""
- }
-
- Keys.onPressed: {
- if (event.key === Qt.Key_Return) {
- goTo(addressBarInput.text);
- event.accepted = true;
- }
- }
-
- Image {
- anchors.verticalCenter: parent.verticalCenter;
- x: 5
- z: 2
- id: faviconImage
- width: 16; height: 16
- sourceSize: Qt.size(width, height)
- source: webStack.currentItem.webEngineView.icon
- }
-
- HifiControls.WebGlyphButton {
- glyph: webStack.currentItem.webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
- anchors.verticalCenter: parent.verticalCenter;
- width: hifi.dimensions.controlLineHeight
- z: 2
- x: addressBarInput.width - implicitWidth
- onClicked: {
- if (webStack.currentItem.webEngineView.loading) {
- webStack.currentItem.webEngineView.stop();
- } else {
- webStack.currentItem.reloadTimer.start();
- }
- }
- }
- }
-
- Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i");
-
- Keys.onPressed: {
- if (event.key === Qt.Key_Return) {
- goTo(addressBarInput.text);
- event.accepted = true;
- }
- }
-
- onEditTextChanged: {
- if (addressBar.editText !== "" && addressBar.editText !== webStack.currentItem.webEngineView.url.toString()) {
- suggestionRequestTimer.restart();
- } else {
- addressBar.model = []
- addressBar.popup.close();
- }
-
- }
-
- Layout.fillWidth: true
- editText: webStack.currentItem.webEngineView.url
- onAccepted: goTo(addressBarInput.text);
- }
-
- HifiControls.WebGlyphButton {
- checkable: true
- checked: webStack.currentItem.webEngineView.audioMuted
- glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
- anchors.verticalCenter: parent.verticalCenter;
- width: hifi.dimensions.controlLineHeight
- onClicked: {
- webStack.currentItem.webEngineView.audioMuted = !webStack.currentItem.webEngineView.audioMuted
- }
- }
- }
-
- QQControls.ProgressBar {
- id: loadProgressBar
- background: Rectangle {
- implicitHeight: 2
- color: "#6A6A6A"
- }
-
- contentItem: Item {
- implicitHeight: 2
-
- Rectangle {
- width: loadProgressBar.visualPosition * parent.width
- height: parent.height
- color: "#00B4EF"
- }
- }
-
- width: parent.width;
- from: 0
- to: 100
- value: webStack.currentItem.webEngineView.loadProgress
- height: 2
- }
-
- Component {
- id: webViewComponent
- Rectangle {
- property alias webEngineView: webEngineView
- property alias reloadTimer: reloadTimer
-
- property WebEngineNewViewRequest request: null
-
- property bool isDialog: QQControls.StackView.index > 0
- property real margins: isDialog ? 10 : 0
-
- color: "#d1d1d1"
-
- QQControls.StackView.onActivated: {
- addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
- }
-
- onRequestChanged: {
- if (isDialog && request !== null && request !== undefined) {//is Dialog ?
- request.openIn(webEngineView);
- }
- }
-
- HifiControls.BaseWebView {
- id: webEngineView
- anchors.fill: parent
- anchors.margins: parent.margins
-
- layer.enabled: parent.isDialog
- layer.effect: DropShadow {
- verticalOffset: 8
- horizontalOffset: 8
- color: "#330066ff"
- samples: 10
- spread: 0.5
- }
-
- focus: true
- objectName: "tabletWebEngineView"
-
- //profile: HFWebEngineProfile;
- profile.httpUserAgent: "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"
-
- property string userScriptUrl: ""
-
- onLoadingChanged: {
- if (!loading) {
- addressBarInput.cursorPosition = 0 //set input field cursot to beginning
- suggestionRequestTimer.stop();
- addressBar.popup.close();
- }
- }
-
- onLinkHovered: {
- //TODO: change cursor shape?
- }
-
- // creates a global EventBridge object.
- WebEngineScript {
- id: createGlobalEventBridge
- sourceCode: eventBridgeJavaScriptToInject
- injectionPoint: WebEngineScript.Deferred
- worldId: WebEngineScript.MainWorld
- }
-
- // detects when to raise and lower virtual keyboard
- WebEngineScript {
- id: raiseAndLowerKeyboard
- injectionPoint: WebEngineScript.Deferred
- sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
- worldId: WebEngineScript.MainWorld
- }
-
- // User script.
- WebEngineScript {
- id: userScript
- sourceUrl: webEngineView.userScriptUrl
- injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
- worldId: WebEngineScript.MainWorld
- }
-
- userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
-
- settings.autoLoadImages: true
- settings.javascriptEnabled: true
- settings.errorPageEnabled: true
- settings.pluginsEnabled: true
- settings.fullScreenSupportEnabled: true
- settings.autoLoadIconsForPage: true
- settings.touchIconsEnabled: true
-
- onCertificateError: {
- error.defer();
- }
-
- Component.onCompleted: {
- webChannel.registerObject("eventBridge", eventBridge);
- webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
- }
-
- onFeaturePermissionRequested: {
- grantFeaturePermission(securityOrigin, feature, true);
- }
-
- onNewViewRequested: {
- if (request.destination == WebEngineView.NewViewInDialog) {
- webStack.push(webViewComponent, {"request": request});
- } else {
- request.openIn(webEngineView);
- }
- }
-
- onRenderProcessTerminated: {
- var status = "";
- switch (terminationStatus) {
- case WebEngineView.NormalTerminationStatus:
- status = "(normal exit)";
- break;
- case WebEngineView.AbnormalTerminationStatus:
- status = "(abnormal exit)";
- break;
- case WebEngineView.CrashedTerminationStatus:
- status = "(crashed)";
- break;
- case WebEngineView.KilledTerminationStatus:
- status = "(killed)";
- break;
- }
-
- console.error("Render process exited with code " + exitCode + " " + status);
- reloadTimer.running = true;
- }
-
- onFullScreenRequested: {
- if (request.toggleOn) {
- webEngineView.state = "FullScreen";
- } else {
- webEngineView.state = "";
- }
- request.accept();
- }
-
- onWindowCloseRequested: {
- webStack.pop();
- }
- }
- Timer {
- id: reloadTimer
- interval: 0
- running: false
- repeat: false
- onTriggered: webEngineView.reload()
- }
- }
- }
-
- QQControls.StackView {
- id: webStack
- width: parent.width;
- property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
- height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
-
- Component.onCompleted: webStack.push(webViewComponent, {"webEngineView.url": "https://www.highfidelity.com"});
- }
- }
-
-
- HifiControls.Keyboard {
- id: keyboard
- raised: parent.keyboardEnabled && parent.keyboardRaised
- numeric: parent.punctuationMode
- anchors {
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- }
- }
-}
diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml
index fee37ca1c1..39f75a9182 100644
--- a/interface/resources/qml/hifi/audio/MicBar.qml
+++ b/interface/resources/qml/hifi/audio/MicBar.qml
@@ -16,7 +16,13 @@ import TabletScriptingInterface 1.0
Rectangle {
readonly property var level: AudioScriptingInterface.inputLevel;
-
+
+ property bool gated: false;
+ Component.onCompleted: {
+ AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
+ AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
+ }
+
property bool standalone: false;
property var dragTarget: null;
@@ -77,6 +83,7 @@ Rectangle {
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
+ readonly property string yellow: "#C0C000";
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
@@ -189,7 +196,7 @@ Rectangle {
Rectangle { // mask
id: mask;
- width: parent.width * level;
+ width: gated ? 0 : parent.width * level;
radius: 5;
anchors {
bottom: parent.bottom;
@@ -212,18 +219,42 @@ Rectangle {
color: colors.greenStart;
}
GradientStop {
- position: 0.8;
+ position: 0.5;
color: colors.greenEnd;
}
- GradientStop {
- position: 0.81;
- color: colors.red;
- }
GradientStop {
position: 1;
- color: colors.red;
+ color: colors.yellow;
}
}
}
+
+ Rectangle {
+ id: gatedIndicator;
+ visible: gated && !AudioScriptingInterface.clipping
+
+ radius: 4;
+ width: 2 * radius;
+ height: 2 * radius;
+ color: "#0080FF";
+ anchors {
+ right: parent.left;
+ verticalCenter: parent.verticalCenter;
+ }
+ }
+
+ Rectangle {
+ id: clippingIndicator;
+ visible: AudioScriptingInterface.clipping
+
+ radius: 4;
+ width: 2 * radius;
+ height: 2 * radius;
+ color: colors.red;
+ anchors {
+ left: parent.right;
+ verticalCenter: parent.verticalCenter;
+ }
+ }
}
}
diff --git a/interface/resources/qml/hifi/avatarapp/+android/TransparencyMask.qml b/interface/resources/qml/hifi/avatarapp/+android_interface/TransparencyMask.qml
similarity index 100%
rename from interface/resources/qml/hifi/avatarapp/+android/TransparencyMask.qml
rename to interface/resources/qml/hifi/avatarapp/+android_interface/TransparencyMask.qml
diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml
index 39c48646d3..c59fe42608 100644
--- a/interface/resources/qml/hifi/avatarapp/Settings.qml
+++ b/interface/resources/qml/hifi/avatarapp/Settings.qml
@@ -21,7 +21,7 @@ Rectangle {
HifiControlsUit.Keyboard {
id: keyboard
z: 1000
- raised: parent.keyboardEnabled && parent.keyboardRaised
+ raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active
numeric: parent.punctuationMode
anchors {
left: parent.left
diff --git a/interface/resources/qml/hifi/avatarapp/Spinner.qml b/interface/resources/qml/hifi/avatarapp/Spinner.qml
index 3fc331346d..14f8e922d7 100644
--- a/interface/resources/qml/hifi/avatarapp/Spinner.qml
+++ b/interface/resources/qml/hifi/avatarapp/Spinner.qml
@@ -1,5 +1,4 @@
import QtQuick 2.5
-import QtWebEngine 1.5
AnimatedImage {
source: "../../../icons/loader-snake-64-w.gif"
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index c76f5a428a..2d5f77f006 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -662,7 +662,7 @@ Rectangle {
anchors.right: parent.right;
text: "Cancel"
onClicked: {
- sendToScript({method: 'checkout_cancelClicked', params: itemId});
+ sendToScript({method: 'checkout_cancelClicked', itemId: itemId});
}
}
}
diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
index 0d0af875d1..759d61b924 100644
--- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
+++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml
@@ -24,11 +24,8 @@ Item {
HifiConstants { id: hifi; }
id: root;
- property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
- readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
- property alias usernameDropdownVisible: usernameDropdown.visible;
- height: mainContainer.height + additionalDropdownHeight;
+ height: mainContainer.height;
Connections {
target: Commerce;
@@ -93,77 +90,7 @@ Item {
MouseArea {
anchors.fill: parent;
onClicked: {
- sendToParent({method: "header_marketplaceImageClicked", referrerURL: root.referrerURL});
- }
- }
- }
-
- Item {
- id: buttonAndUsernameContainer;
- anchors.left: marketplaceHeaderImage.right;
- anchors.leftMargin: 8;
- anchors.top: parent.top;
- anchors.bottom: parent.bottom;
- anchors.bottomMargin: 10;
- anchors.right: securityImage.left;
- anchors.rightMargin: 6;
-
- TextMetrics {
- id: textMetrics;
- font.family: "Raleway"
- text: usernameText.text;
- }
-
- Rectangle {
- id: myUsernameButton;
- anchors.right: parent.right;
- anchors.verticalCenter: parent.verticalCenter;
- height: 40;
- width: usernameText.width + 25;
- color: "white";
- radius: 4;
- border.width: 1;
- border.color: hifi.colors.lightGray;
-
- // Username Text
- RalewayRegular {
- id: usernameText;
- text: Account.username;
- // Text size
- size: 18;
- // Style
- color: hifi.colors.baseGray;
- elide: Text.ElideRight;
- horizontalAlignment: Text.AlignHCenter;
- verticalAlignment: Text.AlignVCenter;
- width: Math.min(textMetrics.width + 25, 110);
- // Anchors
- anchors.centerIn: parent;
- rightPadding: 10;
- }
-
- HiFiGlyphs {
- id: dropdownIcon;
- text: hifi.glyphs.caratDn;
- // Size
- size: 50;
- // Anchors
- anchors.right: parent.right;
- anchors.rightMargin: -14;
- anchors.verticalCenter: parent.verticalCenter;
- horizontalAlignment: Text.AlignHCenter;
- // Style
- color: hifi.colors.baseGray;
- }
-
- MouseArea {
- anchors.fill: parent;
- hoverEnabled: enabled;
- onClicked: {
- usernameDropdown.visible = !usernameDropdown.visible;
- }
- onEntered: usernameText.color = hifi.colors.baseGrayShadow;
- onExited: usernameText.color = hifi.colors.baseGray;
+ sendToParent({method: "header_marketplaceImageClicked"});
}
}
}
@@ -205,92 +132,6 @@ Item {
}
}
- Item {
- id: usernameDropdown;
- z: 998;
- visible: false;
- anchors.top: buttonAndUsernameContainer.bottom;
- anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
- anchors.right: buttonAndUsernameContainer.right;
- height: childrenRect.height;
- width: 150;
-
- Rectangle {
- id: myItemsButton;
- color: hifi.colors.white;
- anchors.top: parent.top;
- anchors.left: parent.left;
- anchors.right: parent.right;
- height: 50;
-
- RalewaySemiBold {
- anchors.fill: parent;
- text: "My Submissions"
- color: hifi.colors.baseGray;
- horizontalAlignment: Text.AlignHCenter;
- verticalAlignment: Text.AlignVCenter;
- size: 18;
- }
-
- MouseArea {
- anchors.fill: parent;
- hoverEnabled: true;
- onEntered: {
- myItemsButton.color = hifi.colors.blueHighlight;
- }
- onExited: {
- myItemsButton.color = hifi.colors.white;
- }
- onClicked: {
- sendToParent({method: "header_myItemsClicked"});
- }
- }
- }
-
- Rectangle {
- id: logOutButton;
- color: hifi.colors.white;
- anchors.top: myItemsButton.bottom;
- anchors.left: parent.left;
- anchors.right: parent.right;
- height: 50;
-
- RalewaySemiBold {
- anchors.fill: parent;
- text: "Log Out"
- color: hifi.colors.baseGray;
- horizontalAlignment: Text.AlignHCenter;
- verticalAlignment: Text.AlignVCenter;
- size: 18;
- }
-
- MouseArea {
- anchors.fill: parent;
- hoverEnabled: true;
- onEntered: {
- logOutButton.color = hifi.colors.blueHighlight;
- }
- onExited: {
- logOutButton.color = hifi.colors.white;
- }
- onClicked: {
- Account.logOut();
- }
- }
- }
- }
-
- DropShadow {
- z: 997;
- visible: usernameDropdown.visible;
- anchors.fill: usernameDropdown;
- horizontalOffset: 3;
- verticalOffset: 3;
- radius: 8.0;
- samples: 17;
- color: "#80000000";
- source: usernameDropdown;
- }
}
diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
index bc8816e0ea..68d437a346 100644
--- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
+++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
@@ -21,11 +21,10 @@ import "../../../../controls" as HifiControls
import "../" as HifiCommerceCommon
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
-Rectangle {
+Item {
HifiConstants { id: hifi; }
id: root;
- color: hifi.colors.baseGray
property int parentAppTitleBarHeight;
property int parentAppNavBarHeight;
property string currentActiveView: "sendAssetHome";
diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
index 232e17d851..8ca34af28a 100644
--- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
+++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
@@ -22,7 +22,6 @@ Rectangle {
HifiConstants { id: hifi; }
id: root;
- property string marketplaceUrl: "";
property string entityId: "";
property string certificateId: "";
property string itemName: "--";
@@ -30,6 +29,7 @@ Rectangle {
property string itemEdition: "--";
property string dateAcquired: "--";
property string itemCost: "--";
+ property string marketplace_item_id: "";
property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white;
property string infoTextColor: hifi.colors.blueAccent;
@@ -69,7 +69,7 @@ Rectangle {
errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
}
} else {
- root.marketplaceUrl = result.data.marketplace_item_url;
+ root.marketplace_item_id = result.data.marketplace_item_id;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
if (root.certInfoReplaceMode > 3) {
@@ -352,7 +352,7 @@ Rectangle {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
- sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
+ sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = root.certTextColor;
@@ -391,7 +391,7 @@ Rectangle {
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
- enabled: root.marketplaceUrl;
+ enabled: root.marketplace_item_id && marketplace_item_id !== "";
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom;
@@ -401,7 +401,7 @@ Rectangle {
height: 40;
text: "View In Market"
onClicked: {
- sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
+ sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
}
}
}
@@ -620,7 +620,7 @@ Rectangle {
root.itemOwner = "--";
root.itemEdition = "--";
root.dateAcquired = "--";
- root.marketplaceUrl = "";
+ root.marketplace_item_id = "";
root.itemCost = "--";
root.isMyCert = false;
errorText.text = "";
diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
new file mode 100644
index 0000000000..0d42cb599e
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
@@ -0,0 +1,1216 @@
+//
+// Marketplace.qml
+// qml/hifi/commerce/marketplace
+//
+// Marketplace
+//
+// Created by Roxanne Skelly on 2019-01-18
+// Copyright 2019 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.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+ HifiConstants {
+ id: hifi
+ }
+
+ id: root
+
+ property string activeView: "initialize"
+ property int currentSortIndex: 0
+ property string sortString: "recent"
+ property string categoryString: ""
+ property string searchString: ""
+ property bool keyboardEnabled: HMD.active
+ property bool keyboardRaised: false
+ property string searchScopeString: "Featured"
+ property bool isLoggedIn: false
+ property bool supports3DHTML: true
+
+ anchors.fill: (typeof parent === undefined) ? undefined : parent
+
+ function getMarketplaceItems() {
+ marketplaceItemView.visible = false;
+ itemsList.visible = true;
+ licenseInfo.visible = false;
+ marketBrowseModel.getFirstPage();
+ {
+ if(root.searchString !== undefined && root.searchString !== "") {
+ root.searchScopeString = "Search Results: \"" + root.searchString + "\"";
+ } else if (root.categoryString !== "") {
+ root.searchScopeString = root.categoryString;
+ } else {
+ root.searchScopeString = "Featured";
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ Commerce.getLoginStatus();
+
+ supports3DHTML = PlatformInfo.has3DHTML();
+ }
+
+ Component.onDestruction: {
+ keyboard.raised = false;
+ }
+
+ Connections {
+ target: GlobalServices
+
+ onMyUsernameChanged: {
+ Commerce.getLoginStatus();
+ }
+ }
+
+ Connections {
+ target: MarketplaceScriptingInterface
+
+ onGetMarketplaceCategoriesResult: {
+ if (result.status !== 'success') {
+ console.log("Failed to get Marketplace Categories", result.data.message);
+ } else {
+ categoriesModel.clear();
+ categoriesModel.append({
+ id: -1,
+ name: "Everything"
+ });
+ result.data.items.forEach(function(category) {
+ categoriesModel.append({
+ id: category.id,
+ name: category.name
+ });
+ });
+ }
+ getMarketplaceItems();
+ }
+ onGetMarketplaceItemsResult: {
+ marketBrowseModel.handlePage(result.status !== "success" && result.message, result);
+ }
+
+ onGetMarketplaceItemResult: {
+ if (result.status !== 'success') {
+ console.log("Failed to get Marketplace Item", result.data.message);
+ } else {
+ marketplaceItem.supports3DHTML = root.supports3DHTML;
+ marketplaceItem.item_id = result.data.id;
+ marketplaceItem.image_url = result.data.thumbnail_url;
+ marketplaceItem.name = result.data.title;
+ marketplaceItem.likes = result.data.likes;
+ if(result.data.has_liked !== undefined) {
+ marketplaceItem.liked = result.data.has_liked;
+ }
+ marketplaceItem.creator = result.data.creator;
+ marketplaceItem.categories = result.data.categories;
+ marketplaceItem.price = result.data.cost;
+ marketplaceItem.description = result.data.description;
+ marketplaceItem.attributions = result.data.attributions;
+ marketplaceItem.license = result.data.license;
+ marketplaceItem.available = result.data.availability === "available";
+ marketplaceItem.created_at = result.data.created_at;
+ marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
+ itemsList.visible = false;
+ marketplaceItemView.visible = true;
+ }
+ }
+ }
+
+ Connections {
+ target: Commerce
+
+ onLoginStatusResult: {
+ root.isLoggedIn = isLoggedIn;
+ }
+ }
+
+ HifiCommerceCommon.CommerceLightbox {
+ id: lightboxPopup
+ visible: false
+ anchors.fill: parent
+ }
+
+ //
+ // HEADER BAR START
+ //
+
+ Rectangle {
+ id: headerShadowBase
+ anchors.fill: header
+ color: "white"
+ }
+ DropShadow {
+ anchors.fill: headerShadowBase
+ source: headerShadowBase
+ verticalOffset: 4
+ horizontalOffset: 4
+ radius: 6
+ samples: 9
+ color: hifi.colors.baseGrayShadow
+ z:5
+ }
+ Rectangle {
+ id: header;
+
+ visible: true
+ anchors {
+ left: parent.left
+ top: parent.top
+ right: parent.right
+ }
+
+ height: childrenRect.height+5
+ z: 5
+
+ Rectangle {
+ id: titleBarContainer
+
+ anchors.left: parent.left
+ anchors.top: parent.top
+ width: parent.width
+ height: 60
+ visible: true
+
+ Image {
+ id: marketplaceHeaderImage;
+ source: "../common/images/marketplaceHeaderImage.png";
+ anchors.top: parent.top;
+ anchors.topMargin: 2;
+ anchors.bottom: parent.bottom;
+ anchors.bottomMargin: 0;
+ anchors.left: parent.left;
+ anchors.leftMargin: 8;
+ width: 140;
+ fillMode: Image.PreserveAspectFit;
+
+ MouseArea {
+ anchors.fill: parent;
+ onClicked: {
+ sendToParent({method: "header_marketplaceImageClicked"});
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: searchBarContainer
+
+ anchors.top: titleBarContainer.bottom
+ width: parent.width
+ height: 50
+
+ visible: true
+ clip: false
+
+ //
+ // TODO: Possibly change this to be a combo box
+ //
+ Rectangle {
+ id: categoriesButton
+
+ anchors {
+ left: parent.left
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ height: 34
+ width: categoriesText.width + 25
+
+ color: hifi.colors.white
+ radius: 4
+ border.width: 1
+ border.color: hifi.colors.lightGrayText
+
+ RalewayRegular {
+ id: categoriesText
+
+ anchors.centerIn: parent
+
+ text: "Categories"
+ size: 14
+ color: hifi.colors.lightGrayText
+ elide: Text.ElideRight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ rightPadding: 10
+ }
+
+ HiFiGlyphs {
+ id: categoriesDropdownIcon
+
+ anchors {
+ right: parent.right
+ rightMargin: -8
+ verticalCenter: parent.verticalCenter
+ }
+
+ text: hifi.glyphs.caratDn
+ size: 34
+ horizontalAlignment: Text.AlignHCenter
+ color: hifi.colors.baseGray
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ hoverEnabled: enabled
+ onClicked: {
+ categoriesDropdown.visible = !categoriesDropdown.visible;
+ categoriesButton.color = categoriesDropdown.visible ? hifi.colors.lightGray : hifi.colors.white;
+ categoriesDropdown.forceActiveFocus();
+ }
+ onEntered: categoriesText.color = hifi.colors.baseGrayShadow
+ onExited: categoriesText.color = hifi.colors.baseGray
+ }
+
+ Component.onCompleted: {
+ MarketplaceScriptingInterface.getMarketplaceCategories();
+ }
+ }
+
+ // or
+ RalewayRegular {
+ id: orText
+
+ anchors.left: categoriesButton.right
+ anchors.verticalCenter: parent.verticalCenter
+ width: 25
+
+ text: "or"
+ size: 18;
+ color: hifi.colors.baseGray
+ elide: Text.ElideRight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ rightPadding: 10
+ leftPadding: 10
+ }
+
+ HifiControlsUit.TextField {
+ id: searchField
+
+ anchors {
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ left: orText.right
+ rightMargin: 10
+ }
+ height: 34
+
+ isSearchField: true
+ colorScheme: hifi.colorSchemes.faintGray
+ font.family: "Fira Sans"
+ font.pixelSize: hifi.fontSizes.textFieldInput
+ placeholderText: "Search Marketplace"
+
+ // workaround for https://bugreports.qt.io/browse/QTBUG-49297
+ Keys.onPressed: {
+ switch (event.key) {
+ case Qt.Key_Return:
+ case Qt.Key_Enter:
+ event.accepted = true;
+
+ // emit accepted signal manually
+ if (acceptableInput) {
+ searchField.accepted();
+ searchField.forceActiveFocus();
+ }
+ break;
+ case Qt.Key_Backspace:
+ if (searchField.text === "") {
+ primaryFilter_index = -1;
+ }
+ break;
+ }
+ }
+
+ onAccepted: {
+ root.searchString = searchField.text;
+ getMarketplaceItems();
+ searchField.forceActiveFocus();
+ }
+
+ onActiveFocusChanged: {
+ root.keyboardRaised = activeFocus;
+ }
+
+ }
+ }
+ }
+ //
+ // HEADER BAR END
+ //
+
+ //
+ // CATEGORIES LIST START
+ //
+ Item {
+ id: categoriesDropdown
+
+ anchors.fill: parent;
+
+ visible: false
+ z: 10
+ MouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+ onClicked: {
+ categoriesDropdown.visible = false;
+ categoriesButton.color = hifi.colors.white;
+ }
+ }
+
+ Rectangle {
+ anchors {
+ left: parent.left;
+ bottom: parent.bottom;
+ top: parent.top;
+ topMargin: 100;
+ }
+ width: parent.width/3
+
+ color: hifi.colors.white
+
+ ListModel {
+ id: categoriesModel
+ }
+
+ ListView {
+ id: categoriesListView;
+
+ anchors.fill: parent
+ anchors.rightMargin: 10
+ width: parent.width
+ currentIndex: -1;
+ clip: true
+
+ model: categoriesModel
+ delegate: ItemDelegate {
+ height: 34
+ width: parent.width
+
+ clip: true
+ contentItem: Rectangle {
+ id: categoriesItem
+
+ anchors.fill: parent
+
+ color: hifi.colors.white
+ visible: true
+
+ RalewayRegular {
+ id: categoriesItemText
+
+ anchors.leftMargin: 15
+ anchors.fill:parent
+
+ text: model.name
+ color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 14
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ z: 10
+
+ hoverEnabled: true
+ propagateComposedEvents: false
+
+ onEntered: {
+ categoriesItem.color = ListView.isCurrentItem ? hifi.colors.white : hifi.colors.lightBlueHighlight;
+ }
+
+ onExited: {
+ categoriesItem.color = ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.white;
+ }
+
+ onClicked: {
+ categoriesListView.currentIndex = index;
+ categoriesText.text = categoriesItemText.text;
+ categoriesDropdown.visible = false;
+ categoriesButton.color = hifi.colors.white;
+ root.categoryString = categoriesItemText.text;
+ getMarketplaceItems();
+ }
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar {
+ parent: categoriesListView.parent
+
+ anchors {
+ top: categoriesListView.top;
+ bottom: categoriesListView.bottom;
+ left: categoriesListView.right;
+ }
+
+ contentItem.opacity: 1
+ }
+ }
+ }
+ }
+ //
+ // CATEGORIES LIST END
+ //
+
+ //
+ // ITEMS LIST START
+ //
+ Item {
+ id: itemsList
+
+ anchors {
+ fill: parent
+ topMargin: 115
+ bottomMargin: 50
+ }
+
+ visible: true
+
+ HifiModels.PSFListModel {
+ id: marketBrowseModel
+
+ itemsPerPage: 7
+ listModelName: 'marketBrowse'
+ listView: marketBrowseList
+
+ getPage: function () {
+ MarketplaceScriptingInterface.getMarketplaceItems(
+ root.searchString === "" ? undefined : root.searchString,
+ "",
+ root.categoryString.toLowerCase(),
+ "",
+ "",
+ root.sortString,
+ WalletScriptingInterface.limitedCommerce,
+ marketBrowseModel.currentPageToRetrieve,
+ marketBrowseModel.itemsPerPage
+ );
+ }
+
+ processPage: function(data) {
+ return data.items;
+ }
+ }
+ ListView {
+ id: marketBrowseList
+
+ anchors.fill: parent
+ anchors.rightMargin: 10
+
+ model: marketBrowseModel
+
+ orientation: ListView.Vertical
+ focus: true
+ clip: true
+
+ delegate: MarketplaceListItem {
+ item_id: model.id
+
+ anchors.topMargin: 10
+
+ image_url:model.thumbnail_url
+ name: model.title
+ likes: model.likes
+ liked: model.has_liked ? model.has_liked : false
+ creator: model.creator
+ category: model.primary_category
+ price: model.cost
+ available: model.availability === "available"
+ isLoggedIn: root.isLoggedIn;
+
+ onShowItem: {
+ MarketplaceScriptingInterface.getMarketplaceItem(item_id);
+ }
+
+ onBuy: {
+ sendToScript({method: 'marketplace_checkout', itemId: item_id});
+ }
+
+ onCategoryClicked: {
+ root.categoryString = category;
+ categoriesText.text = category;
+ getMarketplaceItems();
+ }
+
+ onRequestReload: getMarketplaceItems();
+ }
+
+ ScrollBar.vertical: ScrollBar {
+ parent: marketBrowseList.parent
+
+ anchors {
+ top: marketBrowseList.top
+ bottom: marketBrowseList.bottom
+ left: marketBrowseList.right
+ }
+
+ contentItem.opacity: 1
+ }
+
+ headerPositioning: ListView.InlineHeader
+
+ header: Item {
+ id: itemsHeading
+
+ height: childrenRect.height
+ width: parent.width
+
+ Rectangle {
+ id: itemsSpacer;
+ height: 20
+ }
+
+ Rectangle {
+ id: itemsLoginStatus;
+ anchors {
+ top: itemsSpacer.bottom
+ left: parent.left
+ right: parent.right
+ leftMargin: 15
+ }
+ height: root.isLoggedIn ? 0 : 80
+
+ visible: !root.isLoggedIn
+ color: hifi.colors.greenHighlight
+ border.color: hifi.colors.greenShadow
+ border.width: 1
+ radius: 4
+ z: 10000
+
+ HifiControlsUit.Button {
+ id: loginButton;
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: 15
+ topMargin:10
+ bottomMargin: 10
+ }
+ width: 80;
+
+ text: "LOG IN"
+
+ onClicked: {
+ sendToScript({method: 'needsLogIn_loginClicked'});
+ }
+ }
+
+ RalewayRegular {
+ id: itemsLoginText
+
+ anchors {
+ leftMargin: 15
+ top: parent.top;
+ bottom: parent.bottom;
+ right: parent.right;
+ left: loginButton.right
+ }
+
+ text: "to get items from the Marketplace."
+ color: hifi.colors.baseGray
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 18
+ }
+ }
+
+ Item {
+ id: breadcrumbs
+
+ anchors.top: itemsLoginStatus.bottom;
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 34
+ visible: categoriesListView.currentIndex >= 0
+
+ RalewayRegular {
+ id: categoriesItemText
+
+ anchors.leftMargin: 15
+ anchors.fill:parent
+
+ text: "Home /"
+ color: hifi.colors.blueHighlight
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 18
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ onClicked: {
+ categoriesListView.currentIndex = -1;
+ categoriesText.text = "Categories";
+ root.categoryString = "";
+ searchField.text = "";
+ getMarketplaceItems();
+ }
+ }
+ }
+
+ Item {
+ id: searchScope
+
+ anchors {
+ top: breadcrumbs.bottom
+ left: parent.left
+ right: parent.right
+ }
+ height: 50
+
+ RalewaySemiBold {
+ id: searchScopeText;
+
+ anchors {
+ leftMargin: 15
+ fill:parent
+ topMargin: 10
+ }
+
+ text: searchScopeString
+ color: hifi.colors.baseGray
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 22
+ }
+ }
+ Item {
+ id: sort
+ visible: searchString === undefined || searchString === ""
+
+ anchors {
+ top: searchScope.bottom;
+ left: parent.left;
+ right: parent.right;
+ topMargin: 10;
+ leftMargin: 15;
+ }
+ height: visible ? childrenRect.height : 0
+
+ RalewayRegular {
+ id: sortText
+
+ anchors.leftMargin: 15
+ anchors.rightMargin: 20
+ height: 34
+
+ text: "Sort:"
+ color: hifi.colors.lightGrayText
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 14
+ }
+
+ Rectangle {
+ anchors {
+ left: sortText.right
+ top: parent.top
+ leftMargin: 20
+ }
+ width: root.isLoggedIn ? 322 : 242
+ height: 36
+
+ radius: 4
+ border.width: 1
+ border.color: hifi.colors.faintGray
+
+ ListModel {
+ id: sortModel
+
+ ListElement {
+ name: "Name";
+ glyph: ";"
+ sortString: "alpha"
+ }
+
+ ListElement {
+ name: "Date";
+ glyph: ";";
+ sortString: "recent";
+ }
+
+ ListElement {
+ name: "Popular";
+ glyph: ";";
+ sortString: "likes";
+ }
+
+ ListElement {
+ name: "My Likes";
+ glyph: ";";
+ sortString: "my_likes";
+ }
+ }
+
+ ListView {
+ id: sortListView
+
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ left: parent.left
+ topMargin:1
+ bottomMargin:1
+ leftMargin:1
+ rightMargin:1
+ }
+ width: childrenRect.width
+ height: 34
+
+ orientation: ListView.Horizontal
+ focus: true
+ clip: true
+ highlightFollowsCurrentItem: false
+ currentIndex: 1;
+
+ delegate: SortButton {
+ width: 80
+ height: parent.height
+
+ glyph: model.glyph
+ text: model.name
+
+ visible: root.isLoggedIn || model.sortString != "my_likes"
+
+ checked: ListView.isCurrentItem
+
+ onClicked: {
+ root.currentSortIndex = index;
+ sortListView.positionViewAtIndex(index, ListView.Beginning);
+ sortListView.currentIndex = index;
+ root.sortString = model.sortString;
+ getMarketplaceItems();
+ }
+ }
+ highlight: Rectangle {
+ width: 80
+ height: parent.height
+
+ color: hifi.colors.faintGray
+ x: sortListView.currentItem.x
+ }
+
+ model: sortModel
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // ITEMS LIST END
+ //
+
+ //
+ // ITEM START
+ //
+ Item {
+ id: marketplaceItemView
+
+ anchors.fill: parent
+ anchors.topMargin: 115
+ anchors.bottomMargin: 50
+ width: parent.width
+
+ visible: false
+
+ ScrollView {
+ id: marketplaceItemScrollView
+
+ anchors.fill: parent
+
+ clip: true
+ ScrollBar.vertical.policy: ScrollBar.AlwaysOn
+ contentWidth: parent.width
+ contentHeight: childrenRect.height
+
+ function resize() {
+ contentHeight = (marketplaceItemContent.y - itemSpacer.y + marketplaceItemContent.height);
+ }
+
+ Item {
+ id: itemSpacer
+ anchors.top: parent.top
+ height: 15
+ }
+
+ Rectangle {
+ id: itemLoginStatus;
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: itemSpacer.bottom
+ topMargin: 10
+ leftMargin: 15
+ rightMargin: 15
+ }
+ height: root.isLoggedIn ? 0 : 80
+
+ visible: !root.isLoggedIn
+ color: hifi.colors.greenHighlight
+ border.color: hifi.colors.greenShadow
+ border.width: 1
+ radius: 4
+ z: 10000
+
+ HifiControlsUit.Button {
+ id: loginButton;
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: 15
+ topMargin:10
+ bottomMargin: 10
+ }
+ width: 80;
+
+ text: "LOG IN"
+
+ onClicked: {
+ sendToScript({method: 'needsLogIn_loginClicked'});
+ }
+ }
+
+ RalewayRegular {
+ id: itemsLoginText
+
+ anchors {
+ leftMargin: 15
+ top: parent.top;
+ bottom: parent.bottom;
+ right: parent.right;
+ left: loginButton.right
+ }
+
+ text: "to get items from the Marketplace."
+ color: hifi.colors.baseGray
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 18
+ }
+ }
+
+
+ Rectangle {
+ id: marketplaceItemContent
+ anchors.top: itemLoginStatus.bottom
+ width: parent.width
+ height: childrenRect.height;
+
+ RalewaySemiBold {
+ id: backText
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ topMargin: 10
+ leftMargin: 15
+ bottomMargin: 10
+ }
+ width: paintedWidth
+ height: paintedHeight
+
+ text: "Back"
+ size: hifi.fontSizes.overlayTitle
+ color: hifi.colors.blueHighlight
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ anchors.fill: backText
+
+ onClicked: {
+ getMarketplaceItems();
+ }
+ }
+
+ MarketplaceItem {
+ id: marketplaceItem
+
+
+ anchors.topMargin: 15
+ anchors.top: backText.bottom
+ width: parent.width
+ height: childrenRect.height
+
+ isLoggedIn: root.isLoggedIn;
+
+ onBuy: {
+ sendToScript({method: 'marketplace_checkout', itemId: item_id, itemEdition: edition});
+ }
+
+ onShowLicense: {
+ var xhr = new XMLHttpRequest;
+ xhr.open("GET", url);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == XMLHttpRequest.DONE) {
+ console.log(xhr.responseText);
+ licenseText.text = xhr.responseText;
+ licenseInfo.visible = true;
+ }
+ };
+ xhr.send();
+ }
+ onCategoryClicked: {
+ root.categoryString = category;
+ categoriesText.text = category;
+ getMarketplaceItems();
+ }
+
+ onResized: {
+ marketplaceItemScrollView.resize();
+ }
+ }
+ }
+ }
+ }
+ //
+ // ITEM END
+ //
+
+ //
+ // FOOTER START
+ //
+
+ Rectangle {
+ id: footer
+
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+ height: 50
+
+ color: hifi.colors.blueHighlight
+
+ Item {
+ anchors {
+ fill: parent
+ rightMargin: 15
+ leftMargin: 15
+ }
+
+
+
+ Item {
+ id: footerText
+
+ anchors.fill: parent
+ visible: root.supports3DHTML && itemsList.visible
+
+ HiFiGlyphs {
+ id: footerGlyph
+
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ rightMargin: 10
+ }
+
+ text: hifi.glyphs.info
+ size: 34
+ color: hifi.colors.white
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: footerInfo
+
+ anchors {
+ left: footerGlyph.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+
+ text: "Get items from Clara.io!"
+ color: hifi.colors.white
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ size: 18
+ }
+ }
+
+ HifiControlsUit.Button {
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ topMargin: 10
+ bottomMargin: 10
+ leftMargin: 10
+ rightMargin: 10
+ }
+
+ visible: marketplaceItemView.visible
+ text: "< BACK"
+ width: 100
+
+ onClicked: {
+ marketplaceItemView.visible = false;
+ itemsList.visible = true;
+ licenseInfo.visible = false;
+ }
+ }
+
+ HifiControlsUit.Button {
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ topMargin: 10
+ bottomMargin: 10
+ leftMargin: 10
+ rightMargin: 10
+ }
+
+ visible: root.supports3DHTML
+
+ text: "SEE ALL MARKETS"
+ width: 180
+
+ onClicked: {
+ sendToScript({method: 'marketplace_marketplaces', itemId: marketplaceItemView.visible ? marketplaceItem.item_id : undefined});
+ }
+ }
+ }
+ }
+ //
+ // FOOTER END
+ //
+
+
+ //
+ // LICENSE START
+ //
+ Rectangle {
+ id: licenseInfo
+
+ anchors {
+ fill: root
+ topMargin: 120
+ bottomMargin: 0
+ }
+
+ visible: false;
+
+ ScrollView {
+ anchors {
+ bottomMargin: 1
+ topMargin: 60
+ leftMargin: 15
+ fill: parent
+ }
+
+ RalewayRegular {
+ id: licenseText
+
+ width:440
+ wrapMode: Text.Wrap
+
+ text: ""
+ size: 18;
+ color: hifi.colors.baseGray
+ }
+ }
+
+ Item {
+ id: licenseClose
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ topMargin: 10
+ rightMargin: 10
+ }
+ width: 30
+ height: 30
+
+ HiFiGlyphs {
+ anchors {
+ fill: parent
+ rightMargin: 0
+ verticalCenter: parent.verticalCenter
+ }
+ height: 30
+
+ text: hifi.glyphs.close
+ size: 34
+ horizontalAlignment: Text.AlignHCenter
+ color: hifi.colors.baseGray
+ }
+
+ MouseArea {
+ anchors.fill: licenseClose
+
+ onClicked: licenseInfo.visible = false;
+ }
+ }
+ }
+ //
+ // LICENSE END
+ //
+
+ HifiControlsUit.Keyboard {
+ id: keyboard
+
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+
+ raised: parent.keyboardEnabled && parent.keyboardRaised
+ numeric: false
+ }
+
+ //
+ // Function Name: fromScript()
+ //
+ // Relevant Variables:
+ // None
+ //
+ // Arguments:
+ // message: The message sent from the JavaScript, in this case the Marketplaces 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) {
+ case 'updateMarketplaceQMLItem':
+ if (!message.params.itemId) {
+ console.log("A message with method 'updateMarketplaceQMLItem' was sent without an itemId!");
+ return;
+ }
+ marketplaceItem.edition = message.params.edition ? message.params.edition : -1;
+ MarketplaceScriptingInterface.getMarketplaceItem(message.params.itemId);
+ break;
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
new file mode 100644
index 0000000000..0a57e56099
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceItem.qml
@@ -0,0 +1,648 @@
+//
+// MarketplaceListItem.qml
+// qml/hifi/commerce/marketplace
+//
+// MarketplaceListItem
+//
+// Created by Roxanne Skelly on 2019-01-22
+// Copyright 2019 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.9
+import QtQuick.Controls 2.2
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+ id: root;
+
+ property string item_id: ""
+ property string image_url: ""
+ property string name: ""
+ property int likes: 0
+ property bool liked: false
+ property string creator: ""
+ property var categories: []
+ property int price: 0
+ property var attributions: []
+ property string description: ""
+ property string license: ""
+ property string posted: ""
+ property bool available: false
+ property string created_at: ""
+ property bool isLoggedIn: false;
+ property int edition: -1;
+ property bool supports3DHTML: false;
+
+
+ onCategoriesChanged: {
+ categoriesListModel.clear();
+ categories.forEach(function(category) {
+ categoriesListModel.append({"category":category});
+ });
+ }
+
+ onDescriptionChanged: {
+
+ if(root.supports3DHTML) {
+ descriptionTextModel.clear();
+ descriptionTextModel.append({text: description});
+ } else {
+ descriptionText.text = description;
+ }
+ }
+
+ onAttributionsChanged: {
+ attributionsModel.clear();
+ if(root.attributions) {
+ root.attributions.forEach(function(attribution) {
+ attributionsModel.append(attribution);
+ });
+ }
+ footer.evalHeight();
+ }
+
+ signal buy()
+ signal categoryClicked(string category)
+ signal showLicense(string url)
+
+ HifiConstants {
+ id: hifi
+ }
+
+ Connections {
+ target: MarketplaceScriptingInterface
+
+ onMarketplaceItemLikeResult: {
+ if (result.status !== 'success') {
+ console.log("Like/Unlike item", result.data.message);
+ } else {
+ root.liked = !root.liked;
+ root.likes = root.liked ? root.likes + 1 : root.likes - 1;
+ }
+ }
+ }
+ function getFormattedDate(timestamp) {
+ function addLeadingZero(n) {
+ return n < 10 ? '0' + n : '' + n;
+ }
+
+ var a = new Date(timestamp);
+
+ var year = a.getFullYear();
+ var month = addLeadingZero(a.getMonth() + 1);
+ var day = addLeadingZero(a.getDate());
+ var hour = a.getHours();
+ var drawnHour = hour;
+ if (hour === 0) {
+ drawnHour = 12;
+ } else if (hour > 12) {
+ drawnHour -= 12;
+ }
+ drawnHour = addLeadingZero(drawnHour);
+
+ var amOrPm = "AM";
+ if (hour >= 12) {
+ amOrPm = "PM";
+ }
+
+ var min = addLeadingZero(a.getMinutes());
+ var sec = addLeadingZero(a.getSeconds());
+ return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
+ }
+ function evalHeight() {
+ height = footer.y - header.y + footer.height;
+ }
+
+ signal resized()
+
+ onHeightChanged: {
+ resized();
+ }
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 15
+ rightMargin: 15
+ }
+ height: footer.y - header.y + footer.height
+
+ Rectangle {
+ id: header
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ height: 50
+
+ RalewaySemiBold {
+ id: nameText
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ bottom: parent.bottom
+ }
+ width: paintedWidth
+
+ text: root.name
+ size: 24
+ color: hifi.colors.baseGray
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Item {
+ id: likes
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ bottom: parent.bottom
+ rightMargin: 5
+ }
+
+ RalewaySemiBold {
+ id: heart
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ rightMargin: 0
+ verticalCenter: parent.verticalCenter
+ }
+
+ text: "\u2665"
+ size: 20
+ horizontalAlignment: Text.AlignHCenter
+ color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
+ }
+
+ RalewaySemiBold {
+ id: likesText
+
+ anchors {
+ top: parent.top
+ right: heart.left
+ rightMargin: 5
+ leftMargin: 15
+ bottom: parent.bottom
+ }
+ width: paintedWidth
+
+ text: root.likes
+ size: hifi.fontSizes.overlayTitle
+ color: hifi.colors.baseGray
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ anchors {
+ left: likesText.left
+ right: heart.right
+ top: likesText.top
+ bottom: likesText.bottom
+ }
+
+ onClicked: {
+ if (isLoggedIn) {
+ MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
+ }
+ }
+ }
+ }
+ }
+
+ Image {
+ id: itemImage
+
+ anchors {
+ top: header.bottom
+ left: parent.left
+ right: parent.right
+ }
+ height: width*0.5625
+
+ source: root.image_url
+ fillMode: Image.PreserveAspectCrop;
+ }
+
+ Item {
+ id: footer
+
+ anchors {
+ left: parent.left;
+ right: parent.right;
+ top: itemImage.bottom;
+ }
+ height: categoriesList.y - buyButton.y + categoriesList.height
+
+ function evalHeight() {
+ height = categoriesList.y - buyButton.y + categoriesList.height;
+ console.log("HEIGHT: " + height);
+ }
+
+ HifiControlsUit.Button {
+ id: buyButton
+
+ anchors {
+ right: parent.right
+ top: parent.top
+ left: parent.left
+ topMargin: 15
+ }
+ height: 50
+
+ text: root.edition >= 0 ? "UPGRADE FOR FREE" : (root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)")
+ enabled: root.edition >= 0 || root.available
+ buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
+ color: hifi.buttons.blue
+
+ onClicked: root.buy();
+ }
+
+ Item {
+ id: creatorItem
+
+ anchors {
+ top: buyButton.bottom
+ leftMargin: 15
+ topMargin: 15
+ }
+ width: parent.width
+ height: childrenRect.height
+
+ RalewaySemiBold {
+ id: creatorLabel
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: paintedWidth
+
+ text: "CREATOR:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: creatorText
+
+ anchors {
+ top: creatorLabel.bottom
+ left: parent.left
+ topMargin: 10
+ }
+ width: paintedWidth
+ text: root.creator
+
+ size: 18
+ color: hifi.colors.blueHighlight
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ Item {
+ id: posted
+
+ anchors {
+ top: creatorItem.bottom
+ leftMargin: 15
+ topMargin: 15
+ }
+ width: parent.width
+ height: childrenRect.height
+
+ RalewaySemiBold {
+ id: postedLabel
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: paintedWidth
+
+ text: "POSTED:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: created_at
+
+ anchors {
+ top: postedLabel.bottom
+ left: parent.left
+ right: parent.right
+ topMargin: 5
+ }
+
+ text: { getFormattedDate(root.created_at); }
+ size: 14
+ color: hifi.colors.lightGray
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ Rectangle {
+
+ anchors {
+ top: posted.bottom
+ leftMargin: 15
+ topMargin: 15
+ }
+ width: parent.width
+ height: 1
+
+ color: hifi.colors.lightGrayText
+ }
+
+ Item {
+ id: attributions
+
+ anchors {
+ top: posted.bottom
+ topMargin: 30
+ left: parent.left
+ right: parent.right
+ }
+ width: parent.width
+ height: attributionsModel.count > 0 ? childrenRect.height : 0
+ visible: attributionsModel.count > 0
+
+ RalewaySemiBold {
+ id: attributionsLabel
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: paintedWidth
+ height: paintedHeight
+
+ text: "ATTRIBUTIONS:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+ ListModel {
+ id: attributionsModel
+ }
+
+ ListView {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: attributionsLabel.bottom
+ bottomMargin: 15
+ }
+
+ height: 24*model.count+10
+
+ model: attributionsModel
+ delegate: Item {
+ height: 24
+ width: parent.width
+ RalewaySemiBold {
+ id: attributionName
+
+ anchors.leftMargin: 15
+ height: 24
+ width: parent.width
+
+ text: model.name
+ elide: Text.ElideRight
+ size: 14
+
+ color: (model.link && root.supports3DHTML)? hifi.colors.blueHighlight : hifi.colors.baseGray
+ verticalAlignment: Text.AlignVCenter
+ MouseArea {
+ anchors.fill: parent
+
+ onClicked: {
+ if (model.link && root.supports3DHTML) {
+ sendToScript({method: 'marketplace_open_link', link: model.link});
+ }
+ }
+ }
+ }
+ }
+ }
+ Rectangle {
+
+ anchors {
+ bottom: attributions.bottom
+ leftMargin: 15
+ }
+ width: parent.width
+ height: 1
+
+ color: hifi.colors.lightGrayText
+ }
+ }
+ Item {
+ id: licenseItem;
+
+ anchors {
+ top: attributions.bottom
+ left: parent.left
+ topMargin: 15
+ }
+ width: parent.width
+ height: childrenRect.height
+
+ RalewaySemiBold {
+ id: licenseLabel
+
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ width: paintedWidth;
+
+ text: "SHARED UNDER:";
+ size: 14;
+ color: hifi.colors.lightGrayText;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ RalewaySemiBold {
+ id: licenseText
+
+ anchors.top: licenseLabel.bottom
+ anchors.left: parent.left
+ anchors.topMargin: 5
+ width: paintedWidth
+
+ text: root.license
+ size: 14
+ color: hifi.colors.lightGray
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: licenseHelp
+
+ anchors.top: licenseText.bottom
+ anchors.left: parent.left
+ anchors.topMargin: 5
+ width: paintedWidth
+
+ text: "More about this license"
+ size: 14
+ color: hifi.colors.blueHighlight
+ verticalAlignment: Text.AlignVCenter
+
+ MouseArea {
+ anchors.fill: parent
+
+ onClicked: {
+ licenseInfo.visible = true;
+ var url;
+ if (root.license === "No Rights Reserved (CC0)") {
+ url = "https://creativecommons.org/publicdomain/zero/1.0/"
+ } else if (root.license === "Attribution (CC BY)") {
+ url = "https://creativecommons.org/licenses/by/4.0/legalcode.txt"
+ } else if (root.license === "Attribution-ShareAlike (CC BY-SA)") {
+ url = "https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt"
+ } else if (root.license === "Attribution-NoDerivs (CC BY-ND)") {
+ url = "https://creativecommons.org/licenses/by-nd/4.0/legalcode.txt"
+ } else if (root.license === "Attribution-NonCommercial (CC BY-NC)") {
+ url = "https://creativecommons.org/licenses/by-nc/4.0/legalcode.txt"
+ } else if (root.license === "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
+ url = "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt"
+ } else if (root.license === "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
+ url = "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode.txt"
+ } else if (root.license === "Proof of Provenance License (PoP License)") {
+ url = "https://digitalassetregistry.com/PoP-License/v1/"
+ }
+ if(url) {
+ showLicense(url)
+ }
+ }
+ }
+ }
+ }
+
+ Item {
+ id: descriptionItem
+ property string text: ""
+
+ anchors {
+ top: licenseItem.bottom
+ topMargin: 15
+ left: parent.left
+ right: parent.right
+ }
+ height: childrenRect.height
+ onHeightChanged: {
+ footer.evalHeight();
+ }
+ RalewaySemiBold {
+ id: descriptionLabel
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: paintedWidth
+ height: 20
+
+ text: "DESCRIPTION:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: descriptionText
+
+ anchors.top: descriptionLabel.bottom
+ anchors.left: parent.left
+ anchors.topMargin: 5
+ width: parent.width
+
+ text: root.description
+ size: 14
+ color: hifi.colors.lightGray
+ linkColor: root.supports3DHTML ? hifi.colors.blueHighlight : hifi.colors.lightGray
+ verticalAlignment: Text.AlignVCenter
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ onLinkActivated: {
+ if (root.supports3DHTML) {
+ sendToScript({method: 'marketplace_open_link', link: link});
+ }
+ }
+
+ onHeightChanged: { footer.evalHeight(); }
+ }
+ }
+
+ Item {
+ id: categoriesList
+
+ anchors {
+ top: descriptionItem.bottom
+ topMargin: 15
+ left: parent.left
+ right: parent.right
+ }
+ width: parent.width
+ height: categoriesListModel.count*24 + categoryLabel.height + (isLoggedIn ? 50 : 150)
+
+ onHeightChanged: { footer.evalHeight(); }
+
+ RalewaySemiBold {
+ id: categoryLabel
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: paintedWidth
+
+ text: "IN:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+ ListModel {
+ id: categoriesListModel
+ }
+
+ ListView {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: categoryLabel.bottom
+ bottomMargin: 15
+ }
+
+ height: 24*model.count+10
+
+ model: categoriesListModel
+ delegate: RalewaySemiBold {
+ id: categoryText
+
+ anchors.leftMargin: 15
+ width: paintedWidth
+
+ text: model.category
+ size: 14
+ height: 24
+ color: hifi.colors.blueHighlight
+ verticalAlignment: Text.AlignVCenter
+
+ MouseArea {
+ anchors.fill: categoryText
+
+ onClicked: root.categoryClicked(model.category);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
new file mode 100644
index 0000000000..2f37637e40
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/MarketplaceListItem.qml
@@ -0,0 +1,321 @@
+//
+// MarketplaceListItem.qml
+// qml/hifi/commerce/marketplace
+//
+// MarketplaceListItem
+//
+// Created by Roxanne Skelly on 2019-01-22
+// Copyright 2019 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.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Rectangle {
+ id: root
+
+ property string item_id: ""
+ property string image_url: ""
+ property string name: ""
+ property int likes: 0
+ property bool liked: false
+ property string creator: ""
+ property string category: ""
+ property int price: 0
+ property bool available: false
+ property bool isLoggedIn: false;
+
+ signal buy()
+ signal showItem()
+ signal categoryClicked(string category)
+ signal requestReload()
+
+ HifiConstants { id: hifi }
+
+ width: parent.width
+ height: childrenRect.height+50
+
+ DropShadow {
+ anchors.fill: shadowBase
+
+ source: shadowBase
+ verticalOffset: 4
+ horizontalOffset: 4
+ radius: 6
+ samples: 9
+ color: hifi.colors.baseGrayShadow
+ }
+
+ Rectangle {
+ id: shadowBase
+
+ anchors.fill: itemRect
+
+ color: "white"
+ }
+
+ Rectangle {
+ id: itemRect
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ topMargin: 20
+ bottomMargin: 10
+ leftMargin: 15
+ rightMargin: 15
+ }
+ height: childrenRect.height
+
+ MouseArea {
+ anchors.fill: parent
+
+ hoverEnabled: true
+
+ onClicked: root.showItem();
+ onEntered: hoverRect.visible = true;
+ onExited: hoverRect.visible = false;
+
+ }
+
+ Rectangle {
+ id: header
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ height: 50
+
+ RalewaySemiBold {
+ id: nameText
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ rightMargin: 50
+ leftMargin: 15
+ bottom: parent.bottom
+ }
+ width: paintedWidth
+
+ text: root.name
+ size: hifi.fontSizes.overlayTitle
+ elide: Text.ElideRight
+ color: hifi.colors.blueHighlight
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Item {
+ id: likes
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ rightMargin: 15
+ bottom: parent.bottom
+ }
+ width: heart.width + likesText.width
+
+ Connections {
+ target: MarketplaceScriptingInterface
+
+ onMarketplaceItemLikeResult: {
+ if (result.status !== 'success') {
+ console.log("Failed to get Marketplace Categories", result.data.message);
+ root.requestReload();
+ }
+ }
+ }
+
+ RalewaySemiBold {
+ id: heart
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ rightMargin: 0
+ verticalCenter: parent.verticalCenter
+ }
+
+ text: "\u2665"
+ size: 20
+ horizontalAlignment: Text.AlignHCenter;
+ color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
+ }
+
+ RalewaySemiBold {
+ id: likesText
+
+ anchors {
+ top: parent.top
+ right: heart.left
+ rightMargin: 5
+ leftMargin: 15
+ bottom: parent.bottom
+ }
+ width: paintedWidth
+
+ text: root.likes
+ size: hifi.fontSizes.overlayTitle
+ color: hifi.colors.baseGray
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ anchors {
+ left: likesText.left
+ right: heart.right
+ top: parent.top
+ bottom: parent.bottom
+ }
+ onClicked: {
+ if (isLoggedIn) {
+ root.liked = !root.liked;
+ root.likes = root.liked ? root.likes + 1 : root.likes - 1;
+ MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
+ }
+ }
+ }
+ }
+ }
+
+ Image {
+ id: itemImage
+
+ anchors {
+ top: header.bottom
+ left: parent.left
+ right: parent.right
+ }
+ height: width * 0.5625
+
+ source: root.image_url
+ fillMode: Image.PreserveAspectCrop
+ }
+
+ Item {
+ id: footer
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: itemImage.bottom
+ }
+ height: 60
+
+ RalewaySemiBold {
+ id: creatorLabel
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ leftMargin: 15
+ }
+ width: paintedWidth
+
+ text: "CREATOR:"
+ size: 14
+ color: hifi.colors.lightGrayText
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ RalewaySemiBold {
+ id: creatorText
+
+ anchors {
+ top: creatorLabel.top;
+ left: creatorLabel.right;
+ leftMargin: 15;
+ }
+ width: paintedWidth;
+
+ text: root.creator;
+ size: 14;
+ color: hifi.colors.lightGray;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ RalewaySemiBold {
+ id: categoryLabel
+
+ anchors {
+ top: creatorLabel.bottom
+ left: parent.left
+ leftMargin: 15
+ }
+ width: paintedWidth;
+
+ text: "IN:";
+ size: 14;
+ color: hifi.colors.lightGrayText;
+ verticalAlignment: Text.AlignVCenter;
+ }
+
+ RalewaySemiBold {
+ id: categoryText
+
+ anchors {
+ top: categoryLabel.top
+ left: categoryLabel.right
+ leftMargin: 15
+ }
+ width: paintedWidth
+
+ text: root.category
+ size: 14
+ color: hifi.colors.blueHighlight;
+ verticalAlignment: Text.AlignVCenter;
+
+ MouseArea {
+ anchors.fill: parent
+
+ onClicked: root.categoryClicked(root.category);
+ }
+ }
+
+ HifiControlsUit.Button {
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ rightMargin: 15
+ topMargin:10
+ bottomMargin: 10
+ }
+
+ text: root.price ? root.price : "FREE"
+ buttonGlyph: root.price ? hifi.glyphs.hfc : ""
+ color: hifi.buttons.blue;
+
+ onClicked: root.buy();
+ }
+ }
+
+ Rectangle {
+ id: hoverRect
+
+ anchors.fill: parent
+
+ border.color: hifi.colors.blueHighlight
+ border.width: 2
+ color: "#00000000"
+ visible: false
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml
new file mode 100644
index 0000000000..37ad2735ce
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplace/SortButton.qml
@@ -0,0 +1,87 @@
+//
+// SortButton.qml
+// qml/hifi/commerce/marketplace
+//
+// SortButton
+//
+// Created by Roxanne Skelly on 2019-01-18
+// Copyright 2019 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.9
+import QtQuick.Controls 2.2
+import QtGraphicalEffects 1.0
+import stylesUit 1.0
+import controlsUit 1.0 as HifiControlsUit
+import "../../../controls" as HifiControls
+import "../common" as HifiCommerceCommon
+import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
+import "../common/sendAsset"
+import "../.." as HifiCommon
+
+Item {
+ HifiConstants { id: hifi; }
+
+ id: root;
+
+
+ property string glyph: "";
+ property string text: "";
+ property bool checked: false;
+ signal clicked();
+
+ width: childrenRect.width;
+ height: parent.height;
+
+ Rectangle {
+ anchors.top: parent.top;
+ anchors.left: parent.left;
+ height: parent.height;
+ width: 2;
+ color: hifi.colors.faintGray;
+ visible: index > 0;
+ }
+
+ HiFiGlyphs {
+ id: buttonGlyph;
+ text: root.glyph;
+ // Size
+ size: 14;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.leftMargin: 0;
+ anchors.top: parent.top;
+ anchors.verticalCenter: parent.verticalCenter;
+ height: parent.height;
+ horizontalAlignment: Text.AlignHCenter;
+ // Style
+ color: hifi.colors.lightGray;
+ }
+ RalewayRegular {
+ id: buttonText;
+ text: root.text;
+ // Text size
+ size: 14;
+ // Style
+ color: hifi.colors.lightGray;
+ elide: Text.ElideRight;
+ horizontalAlignment: Text.AlignHCenter;
+ verticalAlignment: Text.AlignVCenter;
+ // Anchors
+ anchors.left: parent.left;
+ anchors.leftMargin: 20;
+ anchors.top: parent.top;
+ height: parent.height;
+ }
+ MouseArea {
+ anchors.fill: parent;
+ hoverEnabled: enabled;
+ onClicked: {
+ root.clicked();
+ }
+ }
+}
\ No newline at end of file
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index f7dc26df5f..df6e216b32 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -28,6 +28,7 @@ Item {
property string purchaseStatus;
property string itemName;
property string itemId;
+ property string updateItemId;
property string itemPreviewImageUrl;
property string itemHref;
property string certificateId;
@@ -45,9 +46,9 @@ Item {
property bool cardBackVisible;
property bool isInstalled;
property string wornEntityID;
- property string upgradeUrl;
+ property string updatedItemId;
property string upgradeTitle;
- property bool updateAvailable: root.upgradeUrl !== "";
+ property bool updateAvailable: root.updateItemId && root.updateItemId !== "";
property bool valid;
property string originalStatusText;
@@ -175,7 +176,7 @@ Item {
Item {
property alias buttonGlyphText: buttonGlyph.text;
- property alias buttonText: buttonText.text;
+ property alias itemButtonText: buttonText.text;
property alias glyphSize: buttonGlyph.size;
property string buttonColor: hifi.colors.black;
property string buttonColor_hover: hifi.colors.blueHighlight;
@@ -243,7 +244,7 @@ Item {
onLoaded: {
item.enabled = root.valid;
item.buttonGlyphText = hifi.glyphs.gift;
- item.buttonText = "Gift";
+ item.itemButtonText = "Gift";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({
@@ -270,7 +271,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.market;
- item.buttonText = "View in Marketplace";
+ item.itemButtonText = "View in Marketplace";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
@@ -288,7 +289,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.certificate;
- item.buttonText = "View Certificate";
+ item.itemButtonText = "View Certificate";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
@@ -307,7 +308,7 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.uninstall;
- item.buttonText = "Uninstall";
+ item.itemButtonText = "Uninstall";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
Commerce.uninstallApp(root.itemHref);
@@ -330,15 +331,14 @@ Item {
onLoaded: {
item.buttonGlyphText = hifi.glyphs.update;
- item.buttonText = "Update";
+ item.itemButtonText = "Update";
item.buttonColor = "#E2334D";
item.buttonClicked = function() {
sendToPurchases({ method: 'flipCard', closeAll: true });
sendToPurchases({
method: 'updateItemClicked',
- itemId: root.itemId,
+ itemId: root.updateAvailable ? root.updateItemId : root.itemId,
itemEdition: root.itemEdition,
- upgradeUrl: root.upgradeUrl,
itemHref: root.itemHref,
itemType: root.itemType,
isInstalled: root.isInstalled,
@@ -378,10 +378,10 @@ Item {
function updateProperties() {
if (updateButton.visible && uninstallButton.visible) {
- item.buttonText = "";
+ item.itemButtonText = "";
item.glyphSize = 20;
} else {
- item.buttonText = "Send to Trash";
+ item.itemButtonText = "Send to Trash";
item.glyphSize = 30;
}
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 9433618b6b..dc892e6640 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -29,7 +29,6 @@ Rectangle {
id: root;
property string activeView: "initialize";
- property string referrerURL: "";
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
@@ -154,55 +153,10 @@ Rectangle {
}
}
- //
- // TITLE BAR START
- //
- HifiCommerceCommon.EmulatedMarketplaceHeader {
- id: titleBarContainer;
- z: 997;
- visible: false;
- height: 100;
- // Size
- width: parent.width;
- // Anchors
- anchors.left: parent.left;
- anchors.top: parent.top;
-
- Connections {
- onSendToParent: {
- if (msg.method === 'needsLogIn' && root.activeView !== "needsLogIn") {
- root.activeView = "needsLogIn";
- } else if (msg.method === 'showSecurityPicLightbox') {
- lightboxPopup.titleText = "Your Security Pic";
- lightboxPopup.bodyImageSource = msg.securityImageSource;
- lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
- lightboxPopup.button1text = "CLOSE";
- lightboxPopup.button1method = function() {
- lightboxPopup.visible = false;
- }
- lightboxPopup.visible = true;
- } else {
- sendToScript(msg);
- }
- }
- }
- }
- MouseArea {
- enabled: titleBarContainer.usernameDropdownVisible;
- anchors.fill: parent;
- onClicked: {
- titleBarContainer.usernameDropdownVisible = false;
- }
- }
- //
- // TITLE BAR END
- //
-
Rectangle {
id: initialize;
visible: root.activeView === "initialize";
- anchors.top: titleBarContainer.bottom;
- anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
+ anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
@@ -219,8 +173,7 @@ Rectangle {
id: installedAppsContainer;
z: 998;
visible: false;
- anchors.top: titleBarContainer.bottom;
- anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
+ anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
@@ -422,8 +375,8 @@ Rectangle {
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
- anchors.top: titleBarContainer.bottom;
- anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
+ anchors.top: parent.top;
+ anchors.topMargin: 8;
anchors.bottom: parent.bottom;
//
@@ -585,6 +538,7 @@ Rectangle {
delegate: PurchasedItem {
itemName: title;
itemId: id;
+ updateItemId: model.upgrade_id ? model.upgrade_id : "";
itemPreviewImageUrl: preview;
itemHref: download_url;
certificateId: certificate_id;
@@ -596,7 +550,6 @@ Rectangle {
cardBackVisible: model.cardBackVisible || false;
isInstalled: model.isInstalled || false;
wornEntityID: model.wornEntityID;
- upgradeUrl: model.upgrade_url;
upgradeTitle: model.upgrade_title;
itemType: model.item_type;
valid: model.valid;
@@ -654,7 +607,7 @@ Rectangle {
} else if (msg.method === "showTrashLightbox") {
lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash";
lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " +
- "and it will be inaccessible to you from Purchases.\n\nThis action cannot be undone.";
+ "and it will be inaccessible to you from Inventory.\n\nThis action cannot be undone.";
lightboxPopup.button1text = "CANCEL";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
@@ -1083,8 +1036,6 @@ Rectangle {
function fromScript(message) {
switch (message.method) {
case 'updatePurchases':
- referrerURL = message.referrerURL || "";
- titleBarContainer.referrerURL = message.referrerURL || "";
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'purchases_showMyItems':
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index cf293a06df..eb8aa0f809 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -335,7 +335,7 @@ Item {
if (link.indexOf("users/") !== -1) {
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
} else {
- sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
+ sendSignalToWallet({method: 'transactionHistory_linkClicked', itemId: model.marketplace_item});
}
}
}
diff --git a/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml
new file mode 100644
index 0000000000..050515da37
--- /dev/null
+++ b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import QtWebEngine 1.5
+import "../../controls" as Controls
+
+Controls.TabletWebView {
+ profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"}
+}
diff --git a/interface/resources/qml/hifi/tablet/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/BlocksWebView.qml
index 1e9eb3beb4..eaed88ba01 100644
--- a/interface/resources/qml/hifi/tablet/BlocksWebView.qml
+++ b/interface/resources/qml/hifi/tablet/BlocksWebView.qml
@@ -1,10 +1,7 @@
import QtQuick 2.0
-import QtWebEngine 1.2
-
import "../../controls" as Controls
Controls.TabletWebView {
- profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"}
}
diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml
index 267fb9f0cf..5f06e4fbab 100644
--- a/interface/resources/qml/hifi/tablet/TabletMenu.qml
+++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml
@@ -2,8 +2,6 @@ import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQml 2.2
-import QtWebChannel 1.0
-import QtWebEngine 1.1
import "."
diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml
index b19dcbb919..93a23f1b9d 100644
--- a/interface/resources/qml/hifi/tablet/TabletRoot.qml
+++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml
@@ -3,10 +3,13 @@ import Hifi 1.0
import "../../dialogs"
import "../../controls"
+import stylesUit 1.0
-Item {
+Rectangle {
+ HifiConstants { id: hifi; }
id: tabletRoot
objectName: "tabletRoot"
+ color: hifi.colors.baseGray
property string username: "Unknown user"
property string usernameShort: "Unknown user"
property var rootMenu;
diff --git a/interface/resources/qml/hifi/tablet/TabletWebView.qml b/interface/resources/qml/hifi/tablet/TabletWebView.qml
index ff6be0480f..9eba7824e0 100644
--- a/interface/resources/qml/hifi/tablet/TabletWebView.qml
+++ b/interface/resources/qml/hifi/tablet/TabletWebView.qml
@@ -1,6 +1,4 @@
import QtQuick 2.0
-import QtWebEngine 1.2
-
import "../../controls" as Controls
Controls.TabletWebScreen {
diff --git a/interface/resources/qml/hifi/tablet/WindowWebView.qml b/interface/resources/qml/hifi/tablet/WindowWebView.qml
index 0f697d634e..632ab712cb 100644
--- a/interface/resources/qml/hifi/tablet/WindowWebView.qml
+++ b/interface/resources/qml/hifi/tablet/WindowWebView.qml
@@ -1,6 +1,4 @@
import QtQuick 2.0
-import QtWebEngine 1.2
-
import "../../controls" as Controls
Controls.WebView {
diff --git a/interface/resources/qml/stylesUit/+android/HifiConstants.qml b/interface/resources/qml/stylesUit/+android_interface/HifiConstants.qml
similarity index 100%
rename from interface/resources/qml/stylesUit/+android/HifiConstants.qml
rename to interface/resources/qml/stylesUit/+android_interface/HifiConstants.qml
diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h
index 767e69842d..1495045b85 100644
--- a/interface/src/AboutUtil.h
+++ b/interface/src/AboutUtil.h
@@ -20,7 +20,8 @@
*
* @hifi-interface
* @hifi-client-entity
- *
+ * @hifi-avatar
+ *
* @property {string} buildDate
* @property {string} buildVersion
* @property {string} qtVersion
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index daf2dd6363..bbe4d70ab6 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -152,6 +152,7 @@
#include
#include
#include
+#include
#include
#include
#include "recording/ClipCache.h"
@@ -198,15 +199,12 @@
#include "ui/AvatarInputs.h"
#include "ui/DialogsManager.h"
#include "ui/LoginDialog.h"
-#include "ui/overlays/Cube3DOverlay.h"
-#include "ui/overlays/Web3DOverlay.h"
#include "ui/Snapshot.h"
#include "ui/SnapshotAnimated.h"
#include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h"
#include "ui/AnimStats.h"
#include "ui/UpdateDialog.h"
-#include "ui/overlays/Overlays.h"
#include "ui/DomainConnectionModel.h"
#include "ui/Keyboard.h"
#include "Util.h"
@@ -235,6 +233,7 @@
#include "commerce/Ledger.h"
#include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h"
+#include "commerce/QmlMarketplace.h"
#include "ResourceRequestObserver.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
@@ -350,6 +349,8 @@ static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin";
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn";
+static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f;
+
#if defined(Q_OS_ANDROID)
static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt";
#endif
@@ -602,8 +603,9 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
abort();
}
-#endif
+#else
qApp->getLogger()->addMessage(qPrintable(logMessage));
+#endif
}
}
@@ -621,7 +623,7 @@ public:
case NestableType::Entity:
return getEntityModelProvider(static_cast(uuid));
case NestableType::Overlay:
- return getOverlayModelProvider(static_cast(uuid));
+ return nullptr;
case NestableType::Avatar:
return getAvatarModelProvider(uuid);
}
@@ -645,22 +647,6 @@ private:
return provider;
}
- scriptable::ModelProviderPointer getOverlayModelProvider(OverlayID overlayID) {
- scriptable::ModelProviderPointer provider;
- auto &overlays = qApp->getOverlays();
- if (auto overlay = overlays.getOverlay(overlayID)) {
- if (auto base3d = std::dynamic_pointer_cast(overlay)) {
- provider = std::dynamic_pointer_cast(base3d);
- provider->modelProviderType = NestableType::Overlay;
- } else {
- qCWarning(interfaceapp) << "no renderer for overlay ID" << overlayID.toString();
- }
- } else {
- qCWarning(interfaceapp) << "overlay not found" << overlayID.toString();
- }
- return provider;
- }
-
scriptable::ModelProviderPointer getAvatarModelProvider(QUuid sessionUUID) {
scriptable::ModelProviderPointer provider;
auto avatarManager = DependencyManager::get();
@@ -761,6 +747,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET);
+ // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
+ static const auto OCULUS_STORE_ARG = "--oculus-store";
+ bool isStore = cmdOptionExists(argc, const_cast(argv), OCULUS_STORE_ARG);
+ qApp->setProperty(hifi::properties::OCULUS_STORE, isStore);
+
// Ignore any previous crashes if running from command line with a test script.
bool inTestMode { false };
for (int i = 0; i < argc; ++i) {
@@ -940,9 +931,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
// FIXME move to header, or better yet, design some kind of UI manager
// to take care of highlighting keyboard focused items, rather than
// continuing to overburden Application.cpp
-std::shared_ptr _keyboardFocusHighlight{ nullptr };
-OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID };
-
+QUuid _keyboardFocusHighlightID;
OffscreenGLCanvas* _qmlShareContext { nullptr };
@@ -983,7 +972,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QApplication(argc, argv),
_window(new MainWindow(desktop())),
_sessionRunTimer(startupTimer),
+#ifndef Q_OS_ANDROID
_logger(new FileLogger(this)),
+#endif
_previousSessionCrashed(setupEssentials(argc, argv, runningMarkerExisted)),
_entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
@@ -1117,7 +1108,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
auto accountManager = DependencyManager::get();
+#ifndef Q_OS_ANDROID
_logger->setSessionID(accountManager->getSessionID());
+#endif
setCrashAnnotation("metaverse_session_id", accountManager->getSessionID().toString().toStdString());
setCrashAnnotation("main_thread_id", std::to_string((size_t)QThread::currentThreadId()));
@@ -1138,10 +1131,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
#endif
- // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
- static const QString OCULUS_STORE_ARG = "--oculus-store";
- bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
- setProperty(hifi::properties::OCULUS_STORE, isStore);
+ bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
+
DependencyManager::get()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
updateHeartbeat();
@@ -1211,9 +1202,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
if (tabletScriptingInterface) {
tabletScriptingInterface->setQmlTabletRoot(SYSTEM_TABLET, nullptr);
}
- getOverlays().deleteOverlay(getTabletScreenID());
- getOverlays().deleteOverlay(getTabletHomeButtonID());
- getOverlays().deleteOverlay(getTabletFrameID());
+ auto entityScriptingInterface = DependencyManager::get();
+ entityScriptingInterface->deleteEntity(getTabletScreenID());
+ entityScriptingInterface->deleteEntity(getTabletHomeButtonID());
+ entityScriptingInterface->deleteEntity(getTabletFrameID());
_failedToConnectToEntityServer = false;
});
@@ -1303,10 +1295,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
if (isHMDMode()) {
emit loginDialogFocusDisabled();
dialogsManager->hideLoginDialog();
- createLoginDialogOverlay();
+ createLoginDialog();
} else {
- getOverlays().deleteOverlay(_loginDialogOverlayID);
- _loginDialogOverlayID = OverlayID();
+ DependencyManager::get()->deleteEntity(_loginDialogID);
+ _loginDialogID = QUuid();
_loginStateManager.tearDown();
dialogsManager->showLoginDialog();
emit loginDialogFocusEnabled();
@@ -1897,25 +1889,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
SpacemouseManager::getInstance().init();
#endif
- // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it
- auto entityScriptingInterface = DependencyManager::get();
- connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity,
- [this](const EntityItemID& entityItemID, const PointerEvent& event) {
+ // If the user clicks on an object, we will check that it's a web surface, and if so, set the focus to it
+ auto pointerManager = DependencyManager::get();
+ auto keyboardFocusOperator = [this](const QUuid& id, const PointerEvent& event) {
if (event.shouldFocus()) {
- if (getEntities()->wantsKeyboardFocus(entityItemID)) {
- setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
- setKeyboardFocusEntity(entityItemID);
- } else {
+ auto keyboard = DependencyManager::get();
+ if (getEntities()->wantsKeyboardFocus(id)) {
+ setKeyboardFocusEntity(id);
+ } else if (!keyboard->containsID(id)) { // FIXME: this is a hack to make the keyboard work for now, since the keys would otherwise steal focus
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
}
}
- });
+ };
+ connect(pointerManager.data(), &PointerManager::triggerBeginEntity, keyboardFocusOperator);
+ connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, keyboardFocusOperator);
- connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [this](const EntityItemID& entityItemID) {
+ auto entityScriptingInterface = DependencyManager::get();
+ connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityItemID) {
if (entityItemID == _keyboardFocusedEntity.get()) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
}
- });
+ }, Qt::QueuedConnection);
EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_aboutToQuit) {
@@ -1975,33 +1969,25 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return false;
});
- EntityTree::setAddMaterialToOverlayOperator([this](const QUuid& overlayID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
- auto overlay = _overlays.getOverlay(overlayID);
- if (overlay) {
- overlay->addMaterial(material, parentMaterialName);
- return true;
+ EntityTree::setGetEntityObjectOperator([this](const QUuid& id) -> QObject* {
+ auto entities = getEntities();
+ if (auto entity = entities->renderableForEntityId(id)) {
+ return qobject_cast(entity.get());
}
- return false;
- });
- EntityTree::setRemoveMaterialFromOverlayOperator([this](const QUuid& overlayID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
- auto overlay = _overlays.getOverlay(overlayID);
- if (overlay) {
- overlay->removeMaterial(material, parentMaterialName);
- return true;
- }
- return false;
+ return nullptr;
});
- // Keyboard focus handling for Web overlays.
- auto overlays = &(qApp->getOverlays());
- connect(overlays, &Overlays::overlayDeleted, [this](const OverlayID& overlayID) {
- if (overlayID == _keyboardFocusedOverlay.get()) {
- setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
+ EntityTree::setTextSizeOperator([this](const QUuid& id, const QString& text) {
+ auto entities = getEntities();
+ if (auto entity = entities->renderableForEntityId(id)) {
+ if (auto renderable = std::dynamic_pointer_cast(entity)) {
+ return renderable->textSize(text);
+ }
}
+ return QSizeF(0.0f, 0.0f);
});
connect(this, &Application::aboutToQuit, [this]() {
- setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
});
@@ -2322,8 +2308,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get()->setPrecisionPicking(rayPickID, value);
});
- EntityTreeRenderer::setGetAvatarUpOperator([] {
- return DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP;
+ EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) {
+ if (billboardMode == BillboardMode::YAW) {
+ //rotate about vertical to face the camera
+ ViewFrustum frustum;
+ copyViewFrustum(frustum);
+ glm::vec3 dPosition = frustum.getPosition() - position;
+ // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
+ float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
+ return glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
+ } else if (billboardMode == BillboardMode::FULL) {
+ ViewFrustum frustum;
+ copyViewFrustum(frustum);
+ glm::vec3 cameraPos = frustum.getPosition();
+ // use the referencial from the avatar, y isn't always up
+ glm::vec3 avatarUP = DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP;
+ // check to see if glm::lookAt will work / using glm::lookAt variable name
+ glm::highp_vec3 s(glm::cross(position - cameraPos, avatarUP));
+
+ // make sure s is not NaN for any component
+ if (glm::length2(s) > 0.0f) {
+ return glm::conjugate(glm::toQuat(glm::lookAt(cameraPos, position, avatarUP)));
+ }
+ }
+ return rotation;
});
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) {
@@ -2349,7 +2357,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
});
auto rootItemLoadedFunctor = [webSurface, url, isTablet] {
- Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == OVERLAY_LOGIN_DIALOG.toString());
+ Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == LOGIN_DIALOG.toString());
};
if (webSurface->getRootItem()) {
rootItemLoadedFunctor();
@@ -2368,7 +2376,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Fix for crash in QtWebEngineCore when rapidly switching domains
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
- if (rootItem) {
+ if (rootItem && !cachedWebSurface) {
// stop loading
QMetaObject::invokeMethod(rootItem, "stop");
}
@@ -2397,6 +2405,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
// Preload Tablet sounds
+ DependencyManager::get()->setEntityTree(qApp->getEntities()->getTree());
DependencyManager::get()->preloadSounds();
DependencyManager::get()->createKeyboard();
@@ -2604,11 +2613,10 @@ void Application::cleanupBeforeQuit() {
_applicationStateDevice.reset();
{
- if (_keyboardFocusHighlightID != UNKNOWN_OVERLAY_ID) {
- getOverlays().deleteOverlay(_keyboardFocusHighlightID);
- _keyboardFocusHighlightID = UNKNOWN_OVERLAY_ID;
+ if (_keyboardFocusHighlightID != UNKNOWN_ENTITY_ID) {
+ DependencyManager::get()->deleteEntity(_keyboardFocusHighlightID);
+ _keyboardFocusHighlightID = UNKNOWN_ENTITY_ID;
}
- _keyboardFocusHighlight = nullptr;
}
{
@@ -3005,7 +3013,23 @@ void Application::initializeUi() {
QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
QUrl{ "hifi/tablet/TabletMenu.qml" },
+ QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
}, commerceCallback);
+
+ QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
+ context->setContextProperty("MarketplaceScriptingInterface", new QmlMarketplace());
+ };
+ OffscreenQmlSurface::addWhitelistContextHandler({
+ QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
+ }, marketplaceCallback);
+
+ QmlContextCallback platformInfoCallback = [](QQmlContext* context) {
+ context->setContextProperty("PlatformInfo", new PlatformInfoScriptingInterface());
+ };
+ OffscreenQmlSurface::addWhitelistContextHandler({
+ QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
+ }, platformInfoCallback);
+
QmlContextCallback ttsCallback = [](QQmlContext* context) {
context->setContextProperty("TextToSpeech", DependencyManager::get().data());
};
@@ -3096,7 +3120,7 @@ void Application::initializeUi() {
});
#if !defined(DISABLE_QML)
- // Pre-create a couple of Web3D overlays to speed up tablet UI
+ // Pre-create a couple of offscreen surfaces to speed up tablet UI
auto offscreenSurfaceCache = DependencyManager::get();
offscreenSurfaceCache->setOnRootContextCreated([&](const QString& rootObject, QQmlContext* surfaceContext) {
if (rootObject == TabletScriptingInterface::QML) {
@@ -3817,8 +3841,8 @@ static inline bool isKeyEvent(QEvent::Type type) {
return type == QEvent::KeyPress || type == QEvent::KeyRelease;
}
-bool Application::handleKeyEventForFocusedEntityOrOverlay(QEvent* event) {
- if (!_keyboardFocusedEntity.get().isInvalidID()) {
+bool Application::handleKeyEventForFocusedEntity(QEvent* event) {
+ if (_keyboardFocusedEntity.get() != UNKNOWN_ENTITY_ID) {
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease:
@@ -3839,28 +3863,6 @@ bool Application::handleKeyEventForFocusedEntityOrOverlay(QEvent* event) {
}
}
- if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) {
- switch (event->type()) {
- case QEvent::KeyPress:
- case QEvent::KeyRelease: {
- // Only Web overlays can have focus.
- auto overlay = std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get()));
- if (overlay && overlay->getEventHandler()) {
- event->setAccepted(false);
- QCoreApplication::sendEvent(overlay->getEventHandler(), event);
- if (event->isAccepted()) {
- _lastAcceptedKeyPress = usecTimestampNow();
- return true;
- }
- }
- }
- break;
-
- default:
- break;
- }
- }
-
return false;
}
@@ -3903,8 +3905,8 @@ bool Application::event(QEvent* event) {
return false;
}
- // Allow focused Entities and Overlays to handle keyboard input
- if (isKeyEvent(event->type()) && handleKeyEventForFocusedEntityOrOverlay(event)) {
+ // Allow focused Entities to handle keyboard input
+ if (isKeyEvent(event->type()) && handleKeyEventForFocusedEntity(event)) {
return true;
}
@@ -4385,9 +4387,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
buttons, event->modifiers());
if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() ||
- getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_OVERLAY_ID) {
- getOverlays().mouseMoveEvent(&mappedEvent);
+ getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) {
getEntities()->mouseMoveEvent(&mappedEvent);
+ getOverlays().mouseMoveEvent(&mappedEvent);
}
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
@@ -4419,15 +4421,16 @@ void Application::mousePressEvent(QMouseEvent* event) {
#else
QPointF transformedPos;
#endif
- QMouseEvent mappedEvent(event->type(),
- transformedPos,
- event->screenPos(), event->button(),
- event->buttons(), event->modifiers());
- getOverlays().mousePressEvent(&mappedEvent);
+ QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers());
+ std::pair entityResult;
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
- getEntities()->mousePressEvent(&mappedEvent);
+ entityResult = getEntities()->mousePressEvent(&mappedEvent);
}
+ std::pair overlayResult = getOverlays().mousePressEvent(&mappedEvent);
+
+ QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second;
+ setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID);
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@@ -4467,10 +4470,10 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) {
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
- getOverlays().mouseDoublePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
+ getOverlays().mouseDoublePressEvent(&mappedEvent);
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isMouseCaptured()) {
@@ -4494,8 +4497,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
- getOverlays().mouseReleaseEvent(&mappedEvent);
getEntities()->mouseReleaseEvent(&mappedEvent);
+ getOverlays().mouseReleaseEvent(&mappedEvent);
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@@ -4943,7 +4946,11 @@ void Application::idle() {
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
// details normally.
+#ifdef Q_OS_ANDROID
+ bool showWarnings = false;
+#else
bool showWarnings = getLogger()->extraDebugging();
+#endif
PerformanceWarning warn(showWarnings, "idle()");
{
@@ -4957,31 +4964,19 @@ void Application::idle() {
update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS));
}
-
- // Update focus highlight for entity or overlay.
- {
- if (!_keyboardFocusedEntity.get().isInvalidID() || _keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) {
+ { // Update keyboard focus highlight
+ if (!_keyboardFocusedEntity.get().isInvalidID()) {
const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress;
if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
- setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
} else {
- // update position of highlight overlay
- if (!_keyboardFocusedEntity.get().isInvalidID()) {
- auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get());
- if (entity && _keyboardFocusHighlight) {
- _keyboardFocusHighlight->setWorldOrientation(entity->getWorldOrientation());
- _keyboardFocusHighlight->setWorldPosition(entity->getWorldPosition());
- }
- } else {
- // Only Web overlays can have focus.
- auto overlay =
- std::dynamic_pointer_cast(getOverlays().getOverlay(_keyboardFocusedOverlay.get()));
- if (overlay && _keyboardFocusHighlight) {
- _keyboardFocusHighlight->setWorldOrientation(overlay->getWorldOrientation());
- _keyboardFocusHighlight->setWorldPosition(overlay->getWorldPosition());
- }
+ if (auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get())) {
+ EntityItemProperties properties;
+ properties.setPosition(entity->getWorldPosition());
+ properties.setRotation(entity->getWorldOrientation());
+ properties.setDimensions(entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
+ DependencyManager::get()->editEntity(_keyboardFocusHighlightID, properties);
}
}
}
@@ -5779,118 +5774,80 @@ void Application::rotationModeChanged() const {
}
void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions) {
- // Create focus
if (qApp->getLoginDialogPoppedUp()) {
return;
}
- if (_keyboardFocusHighlightID == UNKNOWN_OVERLAY_ID || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
- _keyboardFocusHighlight = std::make_shared();
- _keyboardFocusHighlight->setAlpha(1.0f);
- _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
- _keyboardFocusHighlight->setIsSolid(false);
- _keyboardFocusHighlight->setPulseMin(0.5);
- _keyboardFocusHighlight->setPulseMax(1.0);
- _keyboardFocusHighlight->setColorPulse(1.0);
- _keyboardFocusHighlight->setIgnorePickIntersection(true);
- _keyboardFocusHighlight->setDrawInFront(false);
- _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
+
+ auto entityScriptingInterface = DependencyManager::get();
+ if (_keyboardFocusHighlightID == UNKNOWN_ENTITY_ID || !entityScriptingInterface->isAddedEntity(_keyboardFocusHighlightID)) {
+ EntityItemProperties properties;
+ properties.setType(EntityTypes::Box);
+ properties.setAlpha(1.0f);
+ properties.setColor({ 0xFF, 0xEF, 0x00 });
+ properties.setPrimitiveMode(PrimitiveMode::LINES);
+ properties.getPulse().setMin(0.5);
+ properties.getPulse().setMax(1.0f);
+ properties.getPulse().setColorMode(PulseMode::IN_PHASE);
+ properties.setIgnorePickIntersection(true);
+ _keyboardFocusHighlightID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);
}
// Position focus
- _keyboardFocusHighlight->setWorldOrientation(rotation);
- _keyboardFocusHighlight->setWorldPosition(position);
- _keyboardFocusHighlight->setDimensions(dimensions);
- _keyboardFocusHighlight->setVisible(true);
+ EntityItemProperties properties;
+ properties.setPosition(position);
+ properties.setRotation(rotation);
+ properties.setDimensions(dimensions);
+ properties.setVisible(true);
+ entityScriptingInterface->editEntity(_keyboardFocusHighlightID, properties);
}
QUuid Application::getKeyboardFocusEntity() const {
return _keyboardFocusedEntity.get();
}
-static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f;
-
-void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) {
- if (qApp->getLoginDialogPoppedUp()) {
- return;
- }
- if (_keyboardFocusedEntity.get() != entityItemID) {
- _keyboardFocusedEntity.set(entityItemID);
-
- if (_keyboardFocusHighlight && _keyboardFocusedOverlay.get() == UNKNOWN_OVERLAY_ID) {
- _keyboardFocusHighlight->setVisible(false);
- }
-
- if (entityItemID == UNKNOWN_ENTITY_ID) {
- return;
- }
-
- auto entityScriptingInterface = DependencyManager::get();
- auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
- if (!properties.getLocked() && properties.getVisible()) {
-
- auto entities = getEntities();
- auto entityId = _keyboardFocusedEntity.get();
- if (entities->wantsKeyboardFocus(entityId)) {
- entities->setProxyWindow(entityId, _window->windowHandle());
- if (_keyboardMouseDevice->isActive()) {
- _keyboardMouseDevice->pluginFocusOutEvent();
- }
- _lastAcceptedKeyPress = usecTimestampNow();
-
- auto entity = getEntities()->getEntity(entityId);
- if (entity) {
- setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
- entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
- }
- }
- }
- }
-}
-
-OverlayID Application::getKeyboardFocusOverlay() {
- return _keyboardFocusedOverlay.get();
-}
-
-void Application::setKeyboardFocusOverlay(const OverlayID& overlayID) {
- if (overlayID != _keyboardFocusedOverlay.get()) {
- if (qApp->getLoginDialogPoppedUp() && !_loginDialogOverlayID.isNull()) {
- if (overlayID == _loginDialogOverlayID) {
+void Application::setKeyboardFocusEntity(const QUuid& id) {
+ if (_keyboardFocusedEntity.get() != id) {
+ if (qApp->getLoginDialogPoppedUp() && !_loginDialogID.isNull()) {
+ if (id == _loginDialogID) {
emit loginDialogFocusEnabled();
} else {
- // that's the only overlay we want in focus;
+ // that's the only entity we want in focus;
return;
}
}
- _keyboardFocusedOverlay.set(overlayID);
+ _keyboardFocusedEntity.set(id);
- if (_keyboardFocusHighlight && _keyboardFocusedEntity.get() == UNKNOWN_ENTITY_ID) {
- _keyboardFocusHighlight->setVisible(false);
- }
+ auto entityScriptingInterface = DependencyManager::get();
+ if (id != UNKNOWN_ENTITY_ID) {
+ EntityPropertyFlags desiredProperties;
+ desiredProperties += PROP_VISIBLE;
+ desiredProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT;
+ auto properties = entityScriptingInterface->getEntityProperties(id);
+ if (properties.getVisible()) {
+ auto entities = getEntities();
+ auto entityId = _keyboardFocusedEntity.get();
+ if (entities->wantsKeyboardFocus(entityId)) {
+ entities->setProxyWindow(entityId, _window->windowHandle());
+ if (_keyboardMouseDevice->isActive()) {
+ _keyboardMouseDevice->pluginFocusOutEvent();
+ }
+ _lastAcceptedKeyPress = usecTimestampNow();
- if (overlayID == UNKNOWN_OVERLAY_ID) {
- return;
- }
-
- auto overlayType = getOverlays().getOverlayType(overlayID);
- auto isVisible = getOverlays().getProperty(overlayID, "visible").value.toBool();
- if (overlayType == Web3DOverlay::TYPE && isVisible) {
- auto overlay = std::dynamic_pointer_cast(getOverlays().getOverlay(overlayID));
- overlay->setProxyWindow(_window->windowHandle());
-
- if (_keyboardMouseDevice->isActive()) {
- _keyboardMouseDevice->pluginFocusOutEvent();
- }
- _lastAcceptedKeyPress = usecTimestampNow();
-
- if (overlay->getProperty("showKeyboardFocusHighlight").toBool()) {
- auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR;
- const float OVERLAY_DEPTH = 0.0105f;
- setKeyboardFocusHighlight(overlay->getWorldPosition(), overlay->getWorldOrientation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH));
- } else if (_keyboardFocusHighlight) {
- _keyboardFocusHighlight->setVisible(false);
+ if (properties.getShowKeyboardFocusHighlight()) {
+ if (auto entity = entities->getEntity(entityId)) {
+ setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
+ entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
+ return;
+ }
+ }
+ }
}
}
+
+ EntityItemProperties properties;
+ properties.setVisible(false);
+ entityScriptingInterface->editEntity(_keyboardFocusHighlightID, properties);
}
}
@@ -6267,6 +6224,19 @@ void Application::update(float deltaTime) {
auto grabManager = DependencyManager::get();
grabManager->simulateGrabs();
+ // TODO: break these out into distinct perfTimers when they prove interesting
+ {
+ PROFILE_RANGE(app, "PickManager");
+ PerformanceTimer perfTimer("pickManager");
+ DependencyManager::get()->update();
+ }
+
+ {
+ PROFILE_RANGE(app, "PointerManager");
+ PerformanceTimer perfTimer("pointerManager");
+ DependencyManager::get()->update();
+ }
+
QSharedPointer avatarManager = DependencyManager::get();
{
@@ -6280,38 +6250,57 @@ void Application::update(float deltaTime) {
PROFILE_RANGE(simulation_physics, "PrePhysics");
PerformanceTimer perfTimer("prePhysics)");
{
+ PROFILE_RANGE(simulation_physics, "RemoveEntities");
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
- _physicsEngine->removeObjects(motionStates);
+ {
+ PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
+ _physicsEngine->removeObjects(motionStates);
+ }
_entitySimulation->deleteObjectsRemovedFromPhysics();
}
- VectorOfMotionStates motionStates;
- getEntities()->getTree()->withReadLock([&] {
- _entitySimulation->getObjectsToAddToPhysics(motionStates);
- _physicsEngine->addObjects(motionStates);
-
- });
- getEntities()->getTree()->withReadLock([&] {
- _entitySimulation->getObjectsToChange(motionStates);
- VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
- _entitySimulation->setObjectsToChange(stillNeedChange);
- });
+ {
+ PROFILE_RANGE(simulation_physics, "AddEntities");
+ VectorOfMotionStates motionStates;
+ getEntities()->getTree()->withReadLock([&] {
+ _entitySimulation->getObjectsToAddToPhysics(motionStates);
+ PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
+ _physicsEngine->addObjects(motionStates);
+ });
+ }
+ {
+ VectorOfMotionStates motionStates;
+ PROFILE_RANGE(simulation_physics, "ChangeEntities");
+ getEntities()->getTree()->withReadLock([&] {
+ _entitySimulation->getObjectsToChange(motionStates);
+ VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
+ _entitySimulation->setObjectsToChange(stillNeedChange);
+ });
+ }
_entitySimulation->applyDynamicChanges();
t1 = std::chrono::high_resolution_clock::now();
- PhysicsEngine::Transaction transaction;
- avatarManager->buildPhysicsTransaction(transaction);
- _physicsEngine->processTransaction(transaction);
- avatarManager->handleProcessedPhysicsTransaction(transaction);
- myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
- _physicsEngine->processTransaction(transaction);
- myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
- myAvatar->prepareForPhysicsSimulation();
- _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
- dynamic->prepareForPhysicsSimulation();
- });
+ {
+ PROFILE_RANGE(simulation_physics, "Avatars");
+ PhysicsEngine::Transaction transaction;
+ avatarManager->buildPhysicsTransaction(transaction);
+ _physicsEngine->processTransaction(transaction);
+ avatarManager->handleProcessedPhysicsTransaction(transaction);
+ myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
+ _physicsEngine->processTransaction(transaction);
+ myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
+ myAvatar->prepareForPhysicsSimulation();
+ _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
+ }
+
+ {
+ PROFILE_RANGE(simulation_physics, "PrepareActions");
+ _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
+ dynamic->prepareForPhysicsSimulation();
+ });
+ }
}
auto t2 = std::chrono::high_resolution_clock::now();
{
@@ -6414,22 +6403,9 @@ void Application::update(float deltaTime) {
updateLOD(deltaTime);
- if (!_loginDialogOverlayID.isNull()) {
- _loginStateManager.update(getMyAvatar()->getDominantHand(), _loginDialogOverlayID);
- updateLoginDialogOverlayPosition();
- }
-
- // TODO: break these out into distinct perfTimers when they prove interesting
- {
- PROFILE_RANGE(app, "PickManager");
- PerformanceTimer perfTimer("pickManager");
- DependencyManager::get()->update();
- }
-
- {
- PROFILE_RANGE(app, "PointerManager");
- PerformanceTimer perfTimer("pointerManager");
- DependencyManager::get()->update();
+ if (!_loginDialogID.isNull()) {
+ _loginStateManager.update(getMyAvatar()->getDominantHand(), _loginDialogID);
+ updateLoginDialogPosition();
}
{
@@ -7195,7 +7171,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater);
scriptEngine->registerGlobalObject("Overlays", &_overlays);
- qScriptRegisterMetaType(scriptEngine.data(), OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);
qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue,
RayToOverlayIntersectionResultFromScriptValue);
@@ -7312,8 +7287,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get().data());
- qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
-
registerInteractiveWindowMetaType(scriptEngine.data());
auto pickScriptingInterface = DependencyManager::get();
@@ -8284,7 +8257,7 @@ void Application::toggleLogDialog() {
bool keepOnTop =_keepLogWindowOnTop.get();
#ifdef Q_OS_WIN
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
-#else
+#elif !defined(Q_OS_ANDROID)
_logDialog = new LogDialog(nullptr, getLogger());
if (keepOnTop) {
@@ -8835,45 +8808,47 @@ void Application::setShowBulletConstraintLimits(bool value) {
_physicsEngine->setShowBulletConstraintLimits(value);
}
-void Application::createLoginDialogOverlay() {
- const glm::vec2 LOGIN_OVERLAY_DIMENSIONS{ 0.89f, 0.5f };
- const auto OVERLAY_OFFSET = glm::vec2(0.7f, -0.1f);
+void Application::createLoginDialog() {
+ const glm::vec3 LOGIN_DIMENSIONS { 0.89f, 0.5f, 0.01f };
+ const auto OFFSET = glm::vec2(0.7f, -0.1f);
auto cameraPosition = _myCamera.getPosition();
auto cameraOrientation = _myCamera.getOrientation();
auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y;
auto headLookVec = (cameraOrientation * Vectors::FRONT);
// DEFAULT_DPI / tablet scale percentage
- const float OVERLAY_DPI = 31.0f / (75.0f / 100.0f);
- auto offset = headLookVec * OVERLAY_OFFSET.x;
- auto overlayPosition = (cameraPosition + offset) + (upVec * OVERLAY_OFFSET.y);
- QVariantMap overlayProperties = {
- { "name", "LoginDialogOverlay" },
- { "url", OVERLAY_LOGIN_DIALOG },
- { "position", vec3toVariant(overlayPosition) },
- { "orientation", quatToVariant(cameraOrientation) },
- { "isSolid", true },
- { "grabbable", false },
- { "ignorePickIntersection", false },
- { "alpha", 1.0 },
- { "dimensions", vec2ToVariant(LOGIN_OVERLAY_DIMENSIONS)},
- { "dpi", OVERLAY_DPI },
- { "visible", true }
- };
- auto& overlays = getOverlays();
- _loginDialogOverlayID = overlays.addOverlay("web3d", overlayProperties);
- auto loginOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_loginDialogOverlayID));
+ const float DPI = 31.0f / (75.0f / 100.0f);
+ auto offset = headLookVec * OFFSET.x;
+ auto position = (cameraPosition + offset) + (upVec * OFFSET.y);
+
+ EntityItemProperties properties;
+ properties.setType(EntityTypes::Web);
+ properties.setName("LoginDialogEntity");
+ properties.setSourceUrl(LOGIN_DIALOG.toString());
+ properties.setPosition(position);
+ properties.setRotation(cameraOrientation);
+ properties.setDimensions(LOGIN_DIMENSIONS);
+ properties.setPrimitiveMode(PrimitiveMode::SOLID);
+ properties.getGrab().setGrabbable(false);
+ properties.setIgnorePickIntersection(false);
+ properties.setAlpha(1.0f);
+ properties.setDPI(DPI);
+ properties.setVisible(true);
+
+ auto entityScriptingInterface = DependencyManager::get();
+ _loginDialogID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);
+
auto keyboard = DependencyManager::get().data();
- if (!keyboard->getAnchorID().isNull() && !_loginDialogOverlayID.isNull()) {
- const auto KEYBOARD_LOCAL_ORIENTATION = glm::quat(0.0f, 0.0, 1.0f, 0.25f);
+ if (!keyboard->getAnchorID().isNull() && !_loginDialogID.isNull()) {
auto keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f);
- QVariantMap properties {
- { "position", vec3toVariant(overlayPosition + keyboardLocalOffset) },
- { "orientation", quatToVariant(cameraOrientation * KEYBOARD_LOCAL_ORIENTATION) },
- };
- overlays.editOverlay(keyboard->getAnchorID(), properties);
+
+ EntityItemProperties properties;
+ properties.setPosition(position + keyboardLocalOffset);
+ properties.setRotation(cameraOrientation * Quaternions::Y_180);
+
+ entityScriptingInterface->editEntity(keyboard->getAnchorID(), properties);
keyboard->setResetKeyboardPositionOnRaise(false);
}
- setKeyboardFocusOverlay(_loginDialogOverlayID);
+ setKeyboardFocusEntity(_loginDialogID);
emit loginDialogFocusEnabled();
getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(false);
getApplicationCompositor().getReticleInterface()->setVisible(false);
@@ -8882,38 +8857,43 @@ void Application::createLoginDialogOverlay() {
}
}
-void Application::updateLoginDialogOverlayPosition() {
+void Application::updateLoginDialogPosition() {
const float LOOK_AWAY_THRESHOLD_ANGLE = 70.0f;
- const auto OVERLAY_OFFSET = glm::vec2(0.7f, -0.1f);
- auto& overlays = getOverlays();
- auto loginOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_loginDialogOverlayID));
- auto overlayPositionVec = loginOverlay->getWorldPosition();
+ const auto OFFSET = glm::vec2(0.7f, -0.1f);
+
+ auto entityScriptingInterface = DependencyManager::get();
+ EntityPropertyFlags desiredProperties;
+ desiredProperties += PROP_POSITION;
+ auto properties = entityScriptingInterface->getEntityProperties(_loginDialogID, desiredProperties);
+ auto positionVec = properties.getPosition();
auto cameraPositionVec = _myCamera.getPosition();
auto cameraOrientation = cancelOutRollAndPitch(_myCamera.getOrientation());
auto headLookVec = (cameraOrientation * Vectors::FRONT);
- auto overlayToHeadVec = overlayPositionVec - cameraPositionVec;
- auto pointAngle = (glm::acos(glm::dot(glm::normalize(overlayToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI);
+ auto entityToHeadVec = positionVec - cameraPositionVec;
+ auto pointAngle = (glm::acos(glm::dot(glm::normalize(entityToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI);
auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y;
- auto offset = headLookVec * OVERLAY_OFFSET.x;
- auto newOverlayPositionVec = (cameraPositionVec + offset) + (upVec * OVERLAY_OFFSET.y);
- auto newOverlayOrientation = glm::inverse(glm::quat_cast(glm::lookAt(newOverlayPositionVec, cameraPositionVec, upVec))) * Quaternions::Y_180;
+ auto offset = headLookVec * OFFSET.x;
+ auto newPositionVec = (cameraPositionVec + offset) + (upVec * OFFSET.y);
- bool overlayOutOfBounds = glm::distance(overlayPositionVec, cameraPositionVec) > 1.0f;
+ bool outOfBounds = glm::distance(positionVec, cameraPositionVec) > 1.0f;
- if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || overlayOutOfBounds) {
- QVariantMap properties {
- {"position", vec3toVariant(newOverlayPositionVec)},
- {"orientation", quatToVariant(newOverlayOrientation)}
- };
- overlays.editOverlay(_loginDialogOverlayID, properties);
- const auto KEYBOARD_LOCAL_ORIENTATION = glm::quat(0.0f, 0.0, 1.0f, 0.25f);
- auto keyboardLocalOffset = newOverlayOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f);
- QVariantMap keyboardProperties {
- { "position", vec3toVariant(newOverlayPositionVec + keyboardLocalOffset) },
- { "orientation", quatToVariant(newOverlayOrientation * KEYBOARD_LOCAL_ORIENTATION) },
- };
- auto keyboard = DependencyManager::get().data();
- overlays.editOverlay(keyboard->getAnchorID(), keyboardProperties);
+ if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || outOfBounds) {
+ {
+ EntityItemProperties properties;
+ properties.setPosition(newPositionVec);
+ properties.setRotation(cameraOrientation);
+ entityScriptingInterface->editEntity(_loginDialogID, properties);
+ }
+
+ {
+ glm::vec3 keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f);
+ glm::quat keyboardOrientation = cameraOrientation * glm::quat(glm::radians(glm::vec3(-30.0f, 180.0f, 0.0f)));
+
+ EntityItemProperties properties;
+ properties.setPosition(newPositionVec + keyboardLocalOffset);
+ properties.setRotation(keyboardOrientation);
+ entityScriptingInterface->editEntity(DependencyManager::get()->getAnchorID(), properties);
+ }
}
}
@@ -8930,10 +8910,9 @@ void Application::onDismissedLoginDialog() {
loginDialogPoppedUp.set(false);
auto keyboard = DependencyManager::get().data();
keyboard->setResetKeyboardPositionOnRaise(true);
- if (!_loginDialogOverlayID.isNull()) {
- // deleting overlay.
- getOverlays().deleteOverlay(_loginDialogOverlayID);
- _loginDialogOverlayID = OverlayID();
+ if (!_loginDialogID.isNull()) {
+ DependencyManager::get()->deleteEntity(_loginDialogID);
+ _loginDialogID = QUuid();
_loginStateManager.tearDown();
}
resumeAfterLoginDialogActionTaken();
@@ -9099,12 +9078,12 @@ void Application::updateSystemTabletMode() {
}
}
-OverlayID Application::getTabletScreenID() const {
+QUuid Application::getTabletScreenID() const {
auto HMD = DependencyManager::get();
return HMD->getCurrentTabletScreenID();
}
-OverlayID Application::getTabletHomeButtonID() const {
+QUuid Application::getTabletHomeButtonID() const {
auto HMD = DependencyManager::get();
return HMD->getCurrentHomeButtonID();
}
@@ -9115,7 +9094,7 @@ QUuid Application::getTabletFrameID() const {
}
QVector Application::getTabletIDs() const {
- // Most important overlays first.
+ // Most important first.
QVector result;
auto HMD = DependencyManager::get();
result << HMD->getCurrentTabletScreenID();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index de79e91cb2..afd9f5f12f 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -251,7 +251,9 @@ public:
void setActiveDisplayPlugin(const QString& pluginName);
+#ifndef Q_OS_ANDROID
FileLogger* getLogger() const { return _logger; }
+#endif
float getRenderResolutionScale() const;
@@ -299,10 +301,10 @@ public:
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
- OverlayID getTabletScreenID() const;
- OverlayID getTabletHomeButtonID() const;
- QUuid getTabletFrameID() const; // may be an entity or an overlay
- QVector getTabletIDs() const; // In order of most important IDs first.
+ QUuid getTabletScreenID() const;
+ QUuid getTabletHomeButtonID() const;
+ QUuid getTabletFrameID() const;
+ QVector getTabletIDs() const;
void setAvatarOverrideUrl(const QUrl& url, bool save);
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
@@ -325,8 +327,8 @@ public:
void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); }
bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; }
- void createLoginDialogOverlay();
- void updateLoginDialogOverlayPosition();
+ void createLoginDialog();
+ void updateLoginDialogPosition();
// Check if a headset is connected
bool hasRiftControllers();
@@ -440,10 +442,7 @@ public slots:
void setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions);
QUuid getKeyboardFocusEntity() const; // thread-safe
- void setKeyboardFocusEntity(const EntityItemID& entityItemID);
-
- OverlayID getKeyboardFocusOverlay();
- void setKeyboardFocusOverlay(const OverlayID& overlayID);
+ void setKeyboardFocusEntity(const QUuid& id);
void addAssetToWorldMessageClose();
@@ -534,7 +533,7 @@ private:
void init();
void pauseUntilLoginDetermined();
void resumeAfterLoginDialogActionTaken();
- bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event);
+ bool handleKeyEventForFocusedEntity(QEvent* event);
bool handleFileOpenEvent(QFileOpenEvent* event);
void cleanupBeforeQuit();
@@ -599,7 +598,9 @@ private:
bool _aboutToQuit { false };
+#ifndef Q_OS_ANDROID
FileLogger* _logger { nullptr };
+#endif
bool _previousSessionCrashed;
@@ -706,7 +707,7 @@ private:
QString _previousAvatarSkeletonModel;
float _previousAvatarTargetScale;
CameraMode _previousCameraMode;
- OverlayID _loginDialogOverlayID;
+ QUuid _loginDialogID;
LoginStateManager _loginStateManager;
quint64 _lastFaceTrackerUpdate;
@@ -724,7 +725,6 @@ private:
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
ThreadSafeValueCache _keyboardFocusedEntity;
- ThreadSafeValueCache _keyboardFocusedOverlay;
quint64 _lastAcceptedKeyPress = 0;
bool _isForeground = true; // starts out assumed to be in foreground
bool _isGLInitialized { false };
diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h
index f1bc6820eb..4623e7d929 100644
--- a/interface/src/AvatarBookmarks.h
+++ b/interface/src/AvatarBookmarks.h
@@ -21,6 +21,7 @@
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*
*/
diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp
index b9be58f04b..33328f54cc 100644
--- a/interface/src/InterfaceParentFinder.cpp
+++ b/interface/src/InterfaceParentFinder.cpp
@@ -50,15 +50,6 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s
return avatarManager->getMyAvatar();
}
- // search overlays
- auto& overlays = qApp->getOverlays();
- auto overlay = overlays.getOverlay(parentID);
- parent = std::dynamic_pointer_cast(overlay); // this will return nullptr for non-3d overlays
- if (!parent.expired()) {
- success = true;
- return parent;
- }
-
success = false;
return parent;
}
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index 6206fd3539..559bae1779 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -36,6 +36,7 @@ class AABox;
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*
* @property {number} presentTime Read-only.
* @property {number} engineRunTime Read-only.
diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h
index 70ea50e2e7..8cd8e40634 100644
--- a/interface/src/LocationBookmarks.h
+++ b/interface/src/LocationBookmarks.h
@@ -21,6 +21,7 @@
*
* @hifi-client-entity
* @hifi-interface
+ * @hifi-avatar
*/
class LocationBookmarks : public Bookmarks, public Dependency {
diff --git a/interface/src/LoginStateManager.cpp b/interface/src/LoginStateManager.cpp
index 8811303f7d..0a09d33775 100644
--- a/interface/src/LoginStateManager.cpp
+++ b/interface/src/LoginStateManager.cpp
@@ -170,7 +170,7 @@ void LoginStateManager::setUp() {
const unsigned int leftHand = 0;
QVariantMap leftPointerProperties {
{ "joint", "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND" },
- { "filter", PickScriptingInterface::PICK_OVERLAYS() },
+ { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() },
{ "triggers", leftPointerTriggerProperties },
{ "posOffset", vec3toVariant(grabPointSphereOffsetLeft + malletOffset) },
{ "hover", true },
@@ -197,7 +197,7 @@ void LoginStateManager::setUp() {
rightPointerTriggerProperties = QList({rtClick1, rtClick2});
QVariantMap rightPointerProperties{
{ "joint", "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" },
- { "filter", PickScriptingInterface::PICK_OVERLAYS() },
+ { "filter", PickScriptingInterface::PICK_LOCAL_ENTITIES() },
{ "triggers", rightPointerTriggerProperties },
{ "posOffset", vec3toVariant(grabPointSphereOffsetRight + malletOffset) },
{ "hover", true },
@@ -212,7 +212,7 @@ void LoginStateManager::setUp() {
pointers->enablePointer(_rightLoginPointerID);
}
-void LoginStateManager::update(const QString dominantHand, const QUuid loginOverlayID) {
+void LoginStateManager::update(const QString& dominantHand, const QUuid& loginEntityID) {
if (!isSetUp()) {
return;
}
@@ -224,8 +224,8 @@ void LoginStateManager::update(const QString dominantHand, const QUuid loginOver
if (pointers && raypicks) {
const auto rightObjectID = raypicks->getPrevRayPickResult(_rightLoginPointerID)["objectID"].toUuid();
const auto leftObjectID = raypicks->getPrevRayPickResult(_leftLoginPointerID)["objectID"].toUuid();
- const QString leftMode = (leftObjectID.isNull() || leftObjectID != loginOverlayID) ? "" : "full";
- const QString rightMode = (rightObjectID.isNull() || rightObjectID != loginOverlayID) ? "" : "full";
+ const QString leftMode = (leftObjectID.isNull() || leftObjectID != loginEntityID) ? "" : "full";
+ const QString rightMode = (rightObjectID.isNull() || rightObjectID != loginEntityID) ? "" : "full";
pointers->setRenderState(_leftLoginPointerID, leftMode);
pointers->setRenderState(_rightLoginPointerID, rightMode);
if (_dominantHand == "left" && !leftObjectID.isNull()) {
diff --git a/interface/src/LoginStateManager.h b/interface/src/LoginStateManager.h
index ad25e87ee6..b898303ba6 100644
--- a/interface/src/LoginStateManager.h
+++ b/interface/src/LoginStateManager.h
@@ -26,7 +26,7 @@ public:
void setUp();
void tearDown();
- void update(const QString dominantHand, const QUuid loginOverlayID);
+ void update(const QString& dominantHand, const QUuid& loginObjectID);
bool isSetUp() const { return (_leftLoginPointerID > PointerEvent::INVALID_POINTER_ID) && (_rightLoginPointerID > PointerEvent::INVALID_POINTER_ID); }
diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp
index 84325da473..db74b34d91 100644
--- a/interface/src/ModelPackager.cpp
+++ b/interface/src/ModelPackager.cpp
@@ -294,13 +294,6 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
}
mapping.insert(JOINT_FIELD, joints);
-
- if (!mapping.contains(FREE_JOINT_FIELD)) {
- mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm");
- mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm");
- mapping.insertMulti(FREE_JOINT_FIELD, "RightArm");
- mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
- }
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
// then we can add the default mixamo to "faceshift" mappings
diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp
index 1bdb170b60..d67341990d 100644
--- a/interface/src/ModelPropertiesDialog.cpp
+++ b/interface/src/ModelPropertiesDialog.cpp
@@ -58,11 +58,6 @@ _hfmModel(hfmModel)
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
- form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
- QPushButton* newFreeJoint = new QPushButton("New Free Joint");
- _freeJoints->addWidget(newFreeJoint);
- connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
-
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
@@ -102,11 +97,6 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
- mapping.remove(FREE_JOINT_FIELD);
- for (int i = 0; i < _freeJoints->count() - 1; i++) {
- QComboBox* box = static_cast(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
- mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
- }
mapping.insert(JOINT_FIELD, joints);
return mapping;
@@ -133,16 +123,6 @@ void ModelPropertiesDialog::reset() {
setJointText(_headJoint, jointHash.value("jointHead").toString());
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
-
- while (_freeJoints->count() > 1) {
- delete _freeJoints->itemAt(0)->widget();
- }
- foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
- QString jointName = joint.toString();
- if (_hfmModel.jointIndices.contains(jointName)) {
- createNewFreeJoint(jointName);
- }
- }
}
void ModelPropertiesDialog::chooseTextureDirectory() {
@@ -176,20 +156,6 @@ void ModelPropertiesDialog::updatePivotJoint() {
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
}
-void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
- QWidget* freeJoint = new QWidget();
- QHBoxLayout* freeJointLayout = new QHBoxLayout();
- freeJointLayout->setContentsMargins(QMargins());
- freeJoint->setLayout(freeJointLayout);
- QComboBox* jointBox = createJointBox(false);
- jointBox->setCurrentText(joint);
- freeJointLayout->addWidget(jointBox, 1);
- QPushButton* deleteJoint = new QPushButton("Delete");
- freeJointLayout->addWidget(deleteJoint);
- freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater()));
- _freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint);
-}
-
QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
QComboBox* box = new QComboBox();
if (withNone) {
diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h
index 7019d239ff..8cf9bd5248 100644
--- a/interface/src/ModelPropertiesDialog.h
+++ b/interface/src/ModelPropertiesDialog.h
@@ -39,7 +39,6 @@ private slots:
void chooseTextureDirectory();
void chooseScriptDirectory();
void updatePivotJoint();
- void createNewFreeJoint(const QString& joint = QString());
private:
QComboBox* createJointBox(bool withNone = true) const;
@@ -66,7 +65,6 @@ private:
QComboBox* _headJoint = nullptr;
QComboBox* _leftHandJoint = nullptr;
QComboBox* _rightHandJoint = nullptr;
- QVBoxLayout* _freeJoints = nullptr;
};
#endif // hifi_ModelPropertiesDialog_h
diff --git a/interface/src/SpeechRecognizer.h b/interface/src/SpeechRecognizer.h
index b22ab73837..7e2acdb8ac 100644
--- a/interface/src/SpeechRecognizer.h
+++ b/interface/src/SpeechRecognizer.h
@@ -27,6 +27,7 @@
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*/
class SpeechRecognizer : public QObject, public Dependency {
Q_OBJECT
diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h
index 41cee8d17d..912e337670 100644
--- a/interface/src/audio/AudioScope.h
+++ b/interface/src/audio/AudioScope.h
@@ -31,6 +31,7 @@ class AudioScope : public QObject, public Dependency {
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*
* @property {number} scopeInput Read-only.
* @property {number} scopeOutputLeft Read-only.
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index 5fb9c9a0ee..a1826076fa 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -569,7 +569,7 @@ void AvatarActionHold::lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, cons
}
btTransform worldTrans = rigidBody->getWorldTransform();
- AnimPose worldBodyPose(glm::vec3(1), bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
+ AnimPose worldBodyPose(1.0f, bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
// transform the body transform into sensor space with the prePhysics sensor-to-world matrix.
// then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix.
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 1eb87c16f0..0b33220c01 100755
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -652,28 +652,25 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
PROFILE_RANGE(simulation_physics, __FUNCTION__);
- float distance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
+ float bulletDistance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
glm::vec3 rayDirection = glm::normalize(ray.direction);
- std::vector physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), distance, QVector());
+ std::vector physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), bulletDistance, QVector());
if (physicsResults.size() > 0) {
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
- MyCharacterController::RayAvatarResult rayAvatarResult;
- AvatarPointer avatar = nullptr;
-
- BoxFace face = BoxFace::UNKNOWN_FACE;
- glm::vec3 surfaceNormal;
- QVariantMap extraInfo;
-
for (auto &hit : physicsResults) {
auto avatarID = hit._intersectWithAvatar;
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatarID)) ||
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatarID))) {
continue;
}
-
+
+ MyCharacterController::RayAvatarResult rayAvatarResult;
+ BoxFace face = BoxFace::UNKNOWN_FACE;
+ QVariantMap extraInfo;
+ AvatarPointer avatar = nullptr;
if (_myAvatar->getSessionUUID() != avatarID) {
auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.find(avatarID);
@@ -683,46 +680,45 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
} else {
avatar = _myAvatar;
}
+
if (!hit._isBound) {
rayAvatarResult = hit;
} else if (avatar) {
auto &multiSpheres = avatar->getMultiSphereShapes();
if (multiSpheres.size() > 0) {
- std::vector boxHits;
+ MyCharacterController::RayAvatarResult boxHit;
+ boxHit._distance = FLT_MAX;
+
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
assert(hit._boundJoints[i] < multiSpheres.size());
auto &mSphere = multiSpheres[hit._boundJoints[i]];
if (mSphere.isValid()) {
float boundDistance = FLT_MAX;
- BoxFace face;
- glm::vec3 surfaceNormal;
+ BoxFace boundFace = BoxFace::UNKNOWN_FACE;
+ glm::vec3 boundSurfaceNormal;
auto &bbox = mSphere.getBoundingBox();
- if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, face, surfaceNormal)) {
- MyCharacterController::RayAvatarResult boxHit;
- boxHit._distance = boundDistance;
- boxHit._intersect = true;
- boxHit._intersectionNormal = surfaceNormal;
- boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
- boxHit._intersectWithAvatar = avatarID;
- boxHit._intersectWithJoint = mSphere.getJointIndex();
- boxHits.push_back(boxHit);
+ if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, boundFace, boundSurfaceNormal)) {
+ if (boundDistance < boxHit._distance) {
+ boxHit._intersect = true;
+ boxHit._intersectWithAvatar = avatarID;
+ boxHit._intersectWithJoint = mSphere.getJointIndex();
+ boxHit._distance = boundDistance;
+ boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
+ boxHit._intersectionNormal = boundSurfaceNormal;
+ face = boundFace;
+ }
}
}
}
- if (boxHits.size() > 0) {
- if (boxHits.size() > 1) {
- std::sort(boxHits.begin(), boxHits.end(), [](const MyCharacterController::RayAvatarResult& hitA,
- const MyCharacterController::RayAvatarResult& hitB) {
- return hitA._distance < hitB._distance;
- });
- }
- rayAvatarResult = boxHits[0];
+ if (boxHit._distance < FLT_MAX) {
+ rayAvatarResult = boxHit;
}
}
}
- if (pickAgainstMesh) {
+
+ if (rayAvatarResult._intersect && pickAgainstMesh) {
glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint);
- glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayDirection, rayAvatarResult._intersectWithJoint);
+ glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayAvatarResult._distance * rayDirection, rayAvatarResult._intersectWithJoint);
auto avatarOrientation = avatar->getWorldOrientation();
auto avatarPosition = avatar->getWorldPosition();
@@ -732,31 +728,37 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
auto defaultFrameRayOrigin = jointPosition + jointOrientation * localRayOrigin;
auto defaultFrameRayPoint = jointPosition + jointOrientation * localRayPoint;
- auto defaultFrameRayDirection = defaultFrameRayPoint - defaultFrameRayOrigin;
+ auto defaultFrameRayDirection = glm::normalize(defaultFrameRayPoint - defaultFrameRayOrigin);
- if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, distance, face, surfaceNormal, extraInfo, true, false)) {
- auto newDistance = glm::length(vec3FromVariant(extraInfo["worldIntersectionPoint"]) - defaultFrameRayOrigin);
- rayAvatarResult._distance = newDistance;
- rayAvatarResult._intersectionPoint = ray.origin + newDistance * rayDirection;
- rayAvatarResult._intersectionNormal = surfaceNormal;
- extraInfo["worldIntersectionPoint"] = vec3toVariant(rayAvatarResult._intersectionPoint);
- break;
+ float subMeshDistance = FLT_MAX;
+ BoxFace subMeshFace = BoxFace::UNKNOWN_FACE;
+ glm::vec3 subMeshSurfaceNormal;
+ QVariantMap subMeshExtraInfo;
+ if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, subMeshDistance, subMeshFace, subMeshSurfaceNormal, subMeshExtraInfo, true, false)) {
+ rayAvatarResult._distance = subMeshDistance;
+ rayAvatarResult._intersectionPoint = ray.origin + subMeshDistance * rayDirection;
+ rayAvatarResult._intersectionNormal = subMeshSurfaceNormal;
+ face = subMeshFace;
+ extraInfo = subMeshExtraInfo;
+ } else {
+ rayAvatarResult._intersect = false;
}
- } else if (rayAvatarResult._intersect){
+ }
+
+ if (rayAvatarResult._intersect) {
+ result.intersects = true;
+ result.avatarID = rayAvatarResult._intersectWithAvatar;
+ result.distance = rayAvatarResult._distance;
+ result.face = face;
+ result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
+ result.surfaceNormal = rayAvatarResult._intersectionNormal;
+ result.jointIndex = rayAvatarResult._intersectWithJoint;
+ result.extraInfo = extraInfo;
break;
}
}
- if (rayAvatarResult._intersect) {
- result.intersects = true;
- result.avatarID = rayAvatarResult._intersectWithAvatar;
- result.distance = rayAvatarResult._distance;
- result.surfaceNormal = rayAvatarResult._intersectionNormal;
- result.jointIndex = rayAvatarResult._intersectWithJoint;
- result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
- result.extraInfo = extraInfo;
- result.face = face;
- }
}
+
return result;
}
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 50d9e80e8b..51352ec861 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -46,6 +46,7 @@ using SortedAvatar = std::pair>;
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*
* @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers
* @borrows AvatarList.getAvatarsInRange as getAvatarsInRange
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 92d9270d20..b5a938faba 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -75,7 +75,6 @@ const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
const float MIN_AVATAR_SPEED = 0.05f;
-const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
@@ -847,6 +846,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
updateOrientation(deltaTime);
updatePosition(deltaTime);
+ updateViewBoom();
}
// update sensorToWorldMatrix for camera and hand controllers
@@ -915,14 +915,9 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
auto entityTreeRenderer = qApp->getEntities();
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
if (entityTree) {
- bool zoneAllowsFlying = true;
- bool collisionlessAllowed = true;
+ std::pair zoneInteractionProperties;
entityTree->withWriteLock([&] {
- std::shared_ptr zone = entityTreeRenderer->myAvatarZone();
- if (zone) {
- zoneAllowsFlying = zone->getFlyingAllowed();
- collisionlessAllowed = zone->getGhostingAllowed();
- }
+ zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
forEachDescendant([&](SpatiallyNestablePointer object) {
// we need to update attached queryAACubes in our own local tree so point-select always works
@@ -935,6 +930,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
});
});
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
+ bool zoneAllowsFlying = zoneInteractionProperties.first;
+ bool collisionlessAllowed = zoneInteractionProperties.second;
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
_characterController.setCollisionlessAllowed(collisionlessAllowed);
}
@@ -2983,7 +2980,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
// the rig is in the skeletonModel frame
- AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation());
+ AnimPose xform(1.0f, _skeletonModel->getRotation(), _skeletonModel->getTranslation());
if (_enableDebugDrawDefaultPose && animSkeleton) {
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
@@ -3028,7 +3025,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
if (_enableDebugDrawDetailedCollision) {
- AnimPose rigToWorldPose(glm::vec3(1.0f), getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
+ AnimPose rigToWorldPose(1.0f, getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
const int NUM_DEBUG_COLORS = 8;
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
@@ -3323,21 +3320,22 @@ void MyAvatar::updateActionMotor(float deltaTime) {
direction = Vectors::ZERO;
}
+ float sensorToWorldScale = getSensorToWorldScale();
if (state == CharacterController::State::Hover) {
// we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
float motorSpeed = glm::length(_actionMotorVelocity);
- float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
+ float finalMaxMotorSpeed = sensorToWorldScale * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
float speedGrowthTimescale = 2.0f;
float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
- const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED;
+ const float maxBoostSpeed = sensorToWorldScale * MAX_BOOST_SPEED;
if (_isPushing) {
if (motorSpeed < maxBoostSpeed) {
// an active action motor should never be slower than this
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
- motorSpeed += getSensorToWorldScale() * MIN_AVATAR_SPEED * boostCoefficient;
+ motorSpeed += sensorToWorldScale * MIN_AVATAR_SPEED * boostCoefficient;
} else if (motorSpeed > finalMaxMotorSpeed) {
motorSpeed = finalMaxMotorSpeed;
}
@@ -3348,45 +3346,21 @@ void MyAvatar::updateActionMotor(float deltaTime) {
const glm::vec2 currentVel = { direction.x, direction.z };
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
- _actionMotorVelocity = getSensorToWorldScale() * (scaledSpeed * _walkSpeedScalar) * direction;
- }
-
- float previousBoomLength = _boomLength;
- float boomChange = getDriveKey(ZOOM);
- _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
- _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX);
-
- // May need to change view if boom length has changed
- if (previousBoomLength != _boomLength) {
- qApp->changeViewAsNeeded(_boomLength);
+ _actionMotorVelocity = sensorToWorldScale * (scaledSpeed * _walkSpeedScalar) * direction;
}
}
void MyAvatar::updatePosition(float deltaTime) {
- if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
- updateActionMotor(deltaTime);
- }
-
- vec3 velocity = getWorldVelocity();
- float sensorToWorldScale = getSensorToWorldScale();
- float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
- const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
- if (!_characterController.isEnabledAndReady()) {
- // _characterController is not in physics simulation but it can still compute its target velocity
- updateMotors();
- _characterController.computeNewVelocity(deltaTime, velocity);
-
- float speed2 = glm::length(velocity);
- if (speed2 > sensorToWorldScale2 * MIN_AVATAR_SPEED_SQUARED) {
- // update position ourselves
- applyPositionDelta(deltaTime * velocity);
+ if (_characterController.isEnabledAndReady()) {
+ if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
+ updateActionMotor(deltaTime);
}
- measureMotionDerivatives(deltaTime);
- _moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
- } else {
+ float sensorToWorldScale = getSensorToWorldScale();
+ float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
+ vec3 velocity = getWorldVelocity();
float speed2 = glm::length2(velocity);
+ const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
-
if (_moving) {
// scan for walkability
glm::vec3 position = getWorldPosition();
@@ -3398,6 +3372,18 @@ void MyAvatar::updatePosition(float deltaTime) {
}
}
+void MyAvatar::updateViewBoom() {
+ float previousBoomLength = _boomLength;
+ float boomChange = getDriveKey(ZOOM);
+ _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
+ _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX);
+
+ // May need to change view if boom length has changed
+ if (previousBoomLength != _boomLength) {
+ qApp->changeViewAsNeeded(_boomLength);
+ }
+}
+
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
// COLLISION SOUND API in Audio has been removed
}
@@ -4835,7 +4821,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld);
// remove scale present from sensorToWorldMatrix
- followWorldPose.scale() = glm::vec3(1.0f);
+ followWorldPose.scale() = 1.0f;
if (isActive(Rotation)) {
//use the hmd reading for the hips follow
@@ -5300,6 +5286,21 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
bool tellHandler { false };
_avatarGrabsLock.withWriteLock([&] {
+
+ std::map::iterator itr;
+ itr = _avatarGrabs.find(grabID);
+ if (itr != _avatarGrabs.end()) {
+ GrabPointer grab = itr->second;
+ if (grab) {
+ grab->setReleased(true);
+ bool success;
+ SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
+ if (target && success) {
+ target->disableGrab(grab);
+ }
+ }
+ }
+
if (_avatarGrabData.remove(grabID)) {
_grabsToDelete.push_back(grabID);
tellHandler = true;
@@ -5312,3 +5313,15 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
}
}
+void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const {
+ auto treeRenderer = DependencyManager::get();
+ EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
+ if (entityTree) {
+ entityTree->withWriteLock([&] {
+ // force an update packet
+ EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
+ packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityID, properties);
+ });
+ }
+}
+
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 0d27988543..984d7b297b 100755
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -66,6 +66,7 @@ class MyAvatar : public Avatar {
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*
* @property {Vec3} qmlPosition - A synonym for position
for use by QML.
* @property {boolean} shouldRenderLocally=true - If true
then your avatar is rendered for you in Interface,
@@ -137,7 +138,7 @@ class MyAvatar : public Avatar {
* your avatar when rolling your HMD in degrees per second.
* @property {number} userHeight=1.75 - The height of the user in sensor space.
* @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. Read-only.
- * @property {Uuid} SELF_ID - UUID representing "my avatar". Only use for local-only entities and overlays in situations
+ * @property {Uuid} SELF_ID - UUID representing "my avatar". Only use for local-only entities in situations
* where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated.
* Read-only.
* @property {number} walkSpeed
@@ -1732,6 +1733,7 @@ private:
void updateOrientation(float deltaTime);
void updateActionMotor(float deltaTime);
void updatePosition(float deltaTime);
+ void updateViewBoom();
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
void initHeadBones();
void initAnimGraph();
@@ -1882,6 +1884,7 @@ private:
bool didTeleport();
bool getIsAway() const { return _isAway; }
void setAway(bool value);
+ void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const override;
std::mutex _pinnedJointsMutex;
std::vector _pinnedJoints;
diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp
index 51a2c3767b..9b0e2fb7f8 100755
--- a/interface/src/avatar/MySkeletonModel.cpp
+++ b/interface/src/avatar/MySkeletonModel.cpp
@@ -55,7 +55,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
if (myAvatar->isJointPinned(hipsIndex)) {
Transform avatarTransform = myAvatar->getTransform();
AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180);
- result.scale() = glm::vec3(1.0f, 1.0f, 1.0f);
+ result.scale() = 1.0f;
return result;
}
@@ -122,7 +122,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
Rig::ControllerParameters params;
- AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f));
+ AnimPose avatarToRigPose(1.0f, Quaternions::Y_180, glm::vec3(0.0f));
glm::mat4 rigToAvatarMatrix = Matrices::Y_180;
glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition());
@@ -141,7 +141,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// preMult 180 is necessary to convert from avatar to rig coordinates.
// postMult 180 is necessary to convert head from -z forward to z forward.
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
- params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
+ params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(1.0f, headRot, glm::vec3(0.0f));
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
}
diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp
index a3950c8e96..40c7c01b30 100755
--- a/interface/src/avatar/OtherAvatar.cpp
+++ b/interface/src/avatar/OtherAvatar.cpp
@@ -55,44 +55,47 @@ OtherAvatar::~OtherAvatar() {
void OtherAvatar::removeOrb() {
if (!_otherAvatarOrbMeshPlaceholderID.isNull()) {
- qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID);
- _otherAvatarOrbMeshPlaceholderID = UNKNOWN_OVERLAY_ID;
+ DependencyManager::get()->deleteEntity(_otherAvatarOrbMeshPlaceholderID);
+ _otherAvatarOrbMeshPlaceholderID = UNKNOWN_ENTITY_ID;
}
}
void OtherAvatar::updateOrbPosition() {
- if (_otherAvatarOrbMeshPlaceholder != nullptr) {
- _otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
- if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
- _otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder);
- }
+ if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
+ EntityItemProperties properties;
+ properties.setPosition(getHead()->getPosition());
+ DependencyManager::get()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties);
}
}
void OtherAvatar::createOrb() {
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
- _otherAvatarOrbMeshPlaceholder = std::make_shared();
- _otherAvatarOrbMeshPlaceholder->setAlpha(1.0f);
- _otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus));
- _otherAvatarOrbMeshPlaceholder->setIsSolid(false);
- _otherAvatarOrbMeshPlaceholder->setPulseMin(0.5);
- _otherAvatarOrbMeshPlaceholder->setPulseMax(1.0);
- _otherAvatarOrbMeshPlaceholder->setColorPulse(1.0);
- _otherAvatarOrbMeshPlaceholder->setIgnorePickIntersection(true);
- _otherAvatarOrbMeshPlaceholder->setDrawInFront(false);
- _otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder);
- // Position focus
- _otherAvatarOrbMeshPlaceholder->setWorldOrientation(glm::quat(0.0f, 0.0f, 0.0f, 1.0));
- _otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
- _otherAvatarOrbMeshPlaceholder->setDimensions(glm::vec3(0.5f, 0.5f, 0.5f));
- _otherAvatarOrbMeshPlaceholder->setVisible(true);
+ EntityItemProperties properties;
+ properties.setType(EntityTypes::Sphere);
+ properties.setAlpha(1.0f);
+ properties.setColor(getLoadingOrbColor(_loadingStatus));
+ properties.setPrimitiveMode(PrimitiveMode::LINES);
+ properties.getPulse().setMin(0.5f);
+ properties.getPulse().setMax(1.0f);
+ properties.getPulse().setColorMode(PulseMode::IN_PHASE);
+ properties.setIgnorePickIntersection(true);
+
+ properties.setPosition(getHead()->getPosition());
+ properties.setRotation(glm::quat(0.0f, 0.0f, 0.0f, 1.0));
+ properties.setDimensions(glm::vec3(0.5f, 0.5f, 0.5f));
+ properties.setVisible(true);
+
+ _otherAvatarOrbMeshPlaceholderID = DependencyManager::get()->addEntityInternal(properties, entity::HostType::LOCAL);
}
}
void OtherAvatar::indicateLoadingStatus(LoadingStatus loadingStatus) {
Avatar::indicateLoadingStatus(loadingStatus);
- if (_otherAvatarOrbMeshPlaceholder) {
- _otherAvatarOrbMeshPlaceholder->setColor(getLoadingOrbColor(_loadingStatus));
+
+ if (_otherAvatarOrbMeshPlaceholderID != UNKNOWN_ENTITY_ID) {
+ EntityItemProperties properties;
+ properties.setColor(getLoadingOrbColor(_loadingStatus));
+ DependencyManager::get()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties);
}
}
@@ -447,6 +450,10 @@ void OtherAvatar::handleChangedAvatarEntityData() {
EntityItemProperties properties;
int32_t bytesLeftToRead = data.size();
unsigned char* dataAt = (unsigned char*)(data.data());
+ // FIXME: This function will cause unintented changes in SpaillyNestable
+ // E.g overriding the ID index of an exisiting entity to temporary entity
+ // in the following map QHash _children;
+ // Andrew Meadows will address this issue
if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) {
// properties are corrupt
continue;
@@ -489,6 +496,17 @@ void OtherAvatar::handleChangedAvatarEntityData() {
bool success = true;
if (entity) {
QUuid oldParentID = entity->getParentID();
+
+ // Since has overwrtiiten the back pointer
+ // from the parent children map (see comment for function call above),
+ // we need to for reset the back pointer in the map correctly by setting the parentID, but
+ // since the parentID of the entity has not changed we first need to set it some ither ID,
+ // then set the the original ID for the changes to take effect
+ // TODO: This is a horrible hack and once properties.constructFromBuffer no longer causes
+ // side effects...remove the following three lines
+ const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}");
+ entity->setParentID(NULL_ID);
+ entity->setParentID(oldParentID);
if (entityTree->updateEntity(entityID, properties)) {
entity->updateLastEditedFromRemote();
} else {
@@ -510,6 +528,11 @@ void OtherAvatar::handleChangedAvatarEntityData() {
}
}
stateItr.value().success = success;
+ if (success) {
+ stateItr.value().hash = newHash;
+ } else {
+ stateItr.value().hash = 0;
+ }
}
AvatarEntityIDs recentlyRemovedAvatarEntities = getAndClearRecentlyRemovedIDs();
diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h
index 3ecd35413f..696e122b30 100644
--- a/interface/src/avatar/OtherAvatar.h
+++ b/interface/src/avatar/OtherAvatar.h
@@ -16,8 +16,6 @@
#include
#include "InterfaceLogging.h"
-#include "ui/overlays/Overlays.h"
-#include "ui/overlays/Sphere3DOverlay.h"
class AvatarManager;
class AvatarMotionState;
@@ -76,9 +74,18 @@ protected:
void onAddAttachedAvatarEntity(const QUuid& id);
void onRemoveAttachedAvatarEntity(const QUuid& id);
+ class AvatarEntityDataHash {
+ public:
+ AvatarEntityDataHash(uint32_t h) : hash(h) {};
+ uint32_t hash { 0 };
+ bool success { false };
+ };
+
+ using MapOfAvatarEntityDataHashes = QMap;
+ MapOfAvatarEntityDataHashes _avatarEntityDataHashes;
+
std::vector _attachedAvatarEntities;
- std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr };
- OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID };
+ QUuid _otherAvatarOrbMeshPlaceholderID;
AvatarMotionState* _motionState { nullptr };
std::vector _detailedMotionStates;
int32_t _spaceIndex { -1 };
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 5236c5a7fb..046e697b6d 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -54,11 +54,10 @@ void QmlCommerce::openSystemApp(const QString& appName) {
{"GOTO", "hifi/tablet/TabletAddressDialog.qml"},
{"PEOPLE", "hifi/Pal.qml"},
{"WALLET", "hifi/commerce/wallet/Wallet.qml"},
- {"MARKET", "/marketplace.html"}
+ {"MARKET", "hifi/commerce/marketplace/Marketplace.qml"}
};
static const QMap systemInject{
- {"MARKET", "/scripts/system/html/js/marketplacesInject.js"}
};
diff --git a/interface/src/commerce/QmlMarketplace.cpp b/interface/src/commerce/QmlMarketplace.cpp
new file mode 100644
index 0000000000..23ba418a2d
--- /dev/null
+++ b/interface/src/commerce/QmlMarketplace.cpp
@@ -0,0 +1,131 @@
+//
+// QmlMarketplace.cpp
+// interface/src/commerce
+//
+// Guard for safe use of Marketplace by authorized QML.
+//
+// Created by Roxanne Skelly on 1/18/19.
+// Copyright 2019 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
+//
+
+
+#include "QmlMarketplace.h"
+#include "CommerceLogging.h"
+#include "Application.h"
+#include "DependencyManager.h"
+#include
+#include
+#include
+#include
+#include "scripting/HMDScriptingInterface.h"
+
+#define ApiHandler(NAME) void QmlMarketplace::NAME##Success(QNetworkReply* reply) { emit NAME##Result(apiResponse(#NAME, reply)); }
+#define FailHandler(NAME) void QmlMarketplace::NAME##Failure(QNetworkReply* reply) { emit NAME##Result(failResponse(#NAME, reply)); }
+#define Handler(NAME) ApiHandler(NAME) FailHandler(NAME)
+Handler(getMarketplaceItems)
+Handler(getMarketplaceItem)
+Handler(marketplaceItemLike)
+Handler(getMarketplaceCategories)
+
+QmlMarketplace::QmlMarketplace() {
+}
+
+void QmlMarketplace::openMarketplace(const QString& marketplaceItemId) {
+ auto tablet = dynamic_cast(
+ DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"));
+ tablet->loadQMLSource("hifi/commerce/marketplace/Marketplace.qml");
+ DependencyManager::get()->openTablet();
+ if (!marketplaceItemId.isEmpty()) {
+ tablet->sendToQml(QVariantMap({ { "method", "marketplace_openItem" }, { "itemId", marketplaceItemId } }));
+ }
+}
+
+void QmlMarketplace::getMarketplaceItems(
+ const QString& q,
+ const QString& view,
+ const QString& category,
+ const QString& adminFilter,
+ const QString& adminFilterCost,
+ const QString& sort,
+ const bool isFree,
+ const int& page,
+ const int& perPage) {
+
+ QString endpoint = "items";
+ QUrlQuery request;
+ request.addQueryItem("q", q);
+ request.addQueryItem("view", view);
+ request.addQueryItem("category", category);
+ request.addQueryItem("adminFilter", adminFilter);
+ request.addQueryItem("adminFilterCost", adminFilterCost);
+ request.addQueryItem("sort", sort);
+ if (isFree) {
+ request.addQueryItem("isFree", "true");
+ }
+ request.addQueryItem("page", QString::number(page));
+ request.addQueryItem("perPage", QString::number(perPage));
+ send(endpoint, "getMarketplaceItemsSuccess", "getMarketplaceItemsFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
+}
+
+void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
+ QString endpoint = QString("items/") + marketplaceItemId;
+ send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional);
+}
+
+void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
+ QString endpoint = QString("items/") + marketplaceItemId + "/like";
+ send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required);
+}
+
+void QmlMarketplace::getMarketplaceCategories() {
+ QString endpoint = "categories";
+ send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None);
+}
+
+
+void QmlMarketplace::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request) {
+ auto accountManager = DependencyManager::get();
+ const QString URL = "/api/v1/marketplace/";
+ JSONCallbackParameters callbackParams(this, success, fail);
+
+ accountManager->sendRequest(URL + endpoint + "?" + request.toString(),
+ authType,
+ method,
+ callbackParams,
+ QByteArray(),
+ NULL,
+ QVariantMap());
+
+}
+
+QJsonObject QmlMarketplace::apiResponse(const QString& label, QNetworkReply* reply) {
+ QByteArray response = reply->readAll();
+ QJsonObject data = QJsonDocument::fromJson(response).object();
+#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy.
+ qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact);
+#endif
+ return data;
+}
+
+// Non-200 responses are not json:
+QJsonObject QmlMarketplace::failResponse(const QString& label, QNetworkReply* reply) {
+ QString response = reply->readAll();
+ qWarning(commerce) << "FAILED" << label << response;
+
+ // tempResult will be NULL if the response isn't valid JSON.
+ QJsonDocument tempResult = QJsonDocument::fromJson(response.toLocal8Bit());
+ if (tempResult.isNull()) {
+ QJsonObject result
+ {
+ { "status", "fail" },
+ { "message", response }
+ };
+ return result;
+ }
+ else {
+ return tempResult.object();
+ }
+}
\ No newline at end of file
diff --git a/interface/src/commerce/QmlMarketplace.h b/interface/src/commerce/QmlMarketplace.h
new file mode 100644
index 0000000000..5794d4f53c
--- /dev/null
+++ b/interface/src/commerce/QmlMarketplace.h
@@ -0,0 +1,73 @@
+//
+// QmlMarketplace.h
+// interface/src/commerce
+//
+// Guard for safe use of Marketplace by authorized QML.
+//
+// Created by Roxanne Skelly on 1/18/19.
+// Copyright 2019 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
+//
+
+#pragma once
+#ifndef hifi_QmlMarketplace_h
+#define hifi_QmlMarketplace_h
+
+#include
+
+#include
+#include
+#include "AccountManager.h"
+
+class QmlMarketplace : public QObject {
+ Q_OBJECT
+
+public:
+ QmlMarketplace();
+
+public slots:
+ void getMarketplaceItemsSuccess(QNetworkReply* reply);
+ void getMarketplaceItemsFailure(QNetworkReply* reply);
+ void getMarketplaceItemSuccess(QNetworkReply* reply);
+ void getMarketplaceItemFailure(QNetworkReply* reply);
+ void getMarketplaceCategoriesSuccess(QNetworkReply* reply);
+ void getMarketplaceCategoriesFailure(QNetworkReply* reply);
+ void marketplaceItemLikeSuccess(QNetworkReply* reply);
+ void marketplaceItemLikeFailure(QNetworkReply* reply);
+
+protected:
+ Q_INVOKABLE void openMarketplace(const QString& marketplaceItemId = QString());
+ Q_INVOKABLE void getMarketplaceItems(
+ const QString& q = QString(),
+ const QString& view = QString(),
+ const QString& category = QString(),
+ const QString& adminFilter = QString("published"),
+ const QString& adminFilterCost = QString(),
+ const QString& sort = QString(),
+ const bool isFree = false,
+ const int& page = 1,
+ const int& perPage = 20);
+ Q_INVOKABLE void getMarketplaceItem(const QString& marketplaceItemId);
+ Q_INVOKABLE void marketplaceItemLike(const QString& marketplaceItemId, const bool like = true);
+ Q_INVOKABLE void getMarketplaceCategories();
+
+signals:
+ void getMarketplaceItemsResult(QJsonObject result);
+ void getMarketplaceItemResult(QJsonObject result);
+ void getMarketplaceCategoriesResult(QJsonObject result);
+ void marketplaceItemLikeResult(QJsonObject result);
+
+private:
+ void send(const QString& endpoint,
+ const QString& success,
+ const QString& fail,
+ QNetworkAccessManager::Operation method,
+ AccountManagerAuth::Type authType,
+ const QUrlQuery& request = QUrlQuery());
+ QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
+ QJsonObject failResponse(const QString& label, QNetworkReply* reply);
+};
+
+#endif // hifi_QmlMarketplace_h
diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp
index ed52083d77..b9dc8326e8 100644
--- a/interface/src/devices/DdeFaceTracker.cpp
+++ b/interface/src/devices/DdeFaceTracker.cpp
@@ -194,7 +194,6 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_calibrationCount(0),
_calibrationValues(),
_calibrationBillboard(NULL),
- _calibrationBillboardID(UNKNOWN_OVERLAY_ID),
_calibrationMessage(QString()),
_isCalibrated(false)
{
@@ -616,10 +615,6 @@ void DdeFaceTracker::setEyeClosingThreshold(float eyeClosingThreshold) {
static const int CALIBRATION_BILLBOARD_WIDTH = 300;
static const int CALIBRATION_BILLBOARD_HEIGHT = 120;
-static const int CALIBRATION_BILLBOARD_TOP_MARGIN = 30;
-static const int CALIBRATION_BILLBOARD_LEFT_MARGIN = 30;
-static const int CALIBRATION_BILLBOARD_FONT_SIZE = 16;
-static const float CALIBRATION_BILLBOARD_ALPHA = 0.5f;
static QString CALIBRATION_INSTRUCTION_MESSAGE = "Hold still to calibrate camera";
void DdeFaceTracker::calibrate() {
@@ -634,12 +629,8 @@ void DdeFaceTracker::calibrate() {
_calibrationCount = 0;
_calibrationMessage = CALIBRATION_INSTRUCTION_MESSAGE + "\n\n";
+ // FIXME: this overlay probably doesn't work anymore
_calibrationBillboard = new TextOverlay();
- _calibrationBillboard->setTopMargin(CALIBRATION_BILLBOARD_TOP_MARGIN);
- _calibrationBillboard->setLeftMargin(CALIBRATION_BILLBOARD_LEFT_MARGIN);
- _calibrationBillboard->setFontSize(CALIBRATION_BILLBOARD_FONT_SIZE);
- _calibrationBillboard->setText(CALIBRATION_INSTRUCTION_MESSAGE);
- _calibrationBillboard->setAlpha(CALIBRATION_BILLBOARD_ALPHA);
glm::vec2 viewport = qApp->getCanvasSize();
_calibrationBillboard->setX((viewport.x - CALIBRATION_BILLBOARD_WIDTH) / 2);
_calibrationBillboard->setY((viewport.y - CALIBRATION_BILLBOARD_HEIGHT) / 2);
@@ -659,10 +650,10 @@ void DdeFaceTracker::addCalibrationDatum() {
int samplesLeft = CALIBRATION_SAMPLES - _calibrationCount;
if (samplesLeft % LARGE_TICK_INTERVAL == 0) {
_calibrationMessage += QString::number(samplesLeft / LARGE_TICK_INTERVAL);
- _calibrationBillboard->setText(_calibrationMessage);
+ // FIXME: set overlay text
} else if (samplesLeft % SMALL_TICK_INTERVAL == 0) {
_calibrationMessage += ".";
- _calibrationBillboard->setText(_calibrationMessage);
+ // FIXME: set overlay text
}
for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) {
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index 4fe36b582e..0ad8d85c62 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -32,6 +32,7 @@
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*/
class DdeFaceTracker : public FaceTracker, public Dependency {
@@ -168,7 +169,7 @@ private:
int _calibrationCount;
QVector _calibrationValues;
TextOverlay* _calibrationBillboard;
- OverlayID _calibrationBillboardID;
+ QUuid _calibrationBillboardID;
QString _calibrationMessage;
bool _isCalibrated;
void addCalibrationDatum();
diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp
index 5c8868abdb..6b07e6717c 100644
--- a/interface/src/octree/OctreePacketProcessor.cpp
+++ b/interface/src/octree/OctreePacketProcessor.cpp
@@ -38,11 +38,13 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"OctreePacketProcessor::processPacket()");
+#ifndef Q_OS_ANDROID
const int WAY_BEHIND = 300;
if (packetsToProcessCount() > WAY_BEHIND && qApp->getLogger()->extraDebugging()) {
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
}
+#endif
bool wasStatsPacket = false;
diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp
index d30f98051e..82d75257df 100644
--- a/interface/src/raypick/CollisionPick.cpp
+++ b/interface/src/raypick/CollisionPick.cpp
@@ -409,10 +409,6 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi
return std::make_shared(pick, entityIntersections, std::vector());
}
-PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) {
- return std::make_shared(pick, std::vector(), std::vector());
-}
-
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
if (!pick.loaded) {
// Cannot compute result
diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h
index 92aa415f9e..c742c089b4 100644
--- a/interface/src/raypick/CollisionPick.h
+++ b/interface/src/raypick/CollisionPick.h
@@ -54,7 +54,6 @@ public:
return std::make_shared(pickVariant, std::vector(), std::vector());
}
PickResultPointer getEntityIntersection(const CollisionRegion& pick) override;
- PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override;
PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override;
PickResultPointer getHUDIntersection(const CollisionRegion& pick) override;
Transform getResultTransform() const override;
diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp
index 236512f2fe..aeed65fbad 100644
--- a/interface/src/raypick/LaserPointer.cpp
+++ b/interface/src/raypick/LaserPointer.cpp
@@ -17,6 +17,8 @@
#include "PickManager.h"
#include "RayPick.h"
+#include "PolyLineEntityItem.h"
+
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalTime, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithParent, bool enabled) :
@@ -28,7 +30,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende
void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) {
auto renderState = std::static_pointer_cast(_renderStates[state]);
if (renderState) {
- updateRenderStateOverlay(renderState->getPathID(), pathProps);
+ updateRenderState(renderState->getPathID(), pathProps);
QVariant lineWidth = pathProps.toMap()["lineWidth"];
if (lineWidth.isValid()) {
renderState->setLineWidth(lineWidth.toFloat());
@@ -121,48 +123,62 @@ void LaserPointer::setVisualPickResultInternal(PickResultPointer pickResult, Int
}
}
-LaserPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) :
+LaserPointer::RenderState::RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID) :
StartEndRenderState(startID, endID), _pathID(pathID)
{
- if (!_pathID.isNull()) {
- _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignorePickIntersection").value.toBool();
- _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
+ if (!getPathID().isNull()) {
+ auto entityScriptingInterface = DependencyManager::get();
+ {
+ EntityPropertyFlags desiredProperties;
+ desiredProperties += PROP_IGNORE_PICK_INTERSECTION;
+ _pathIgnorePicks = entityScriptingInterface->getEntityProperties(getPathID(), desiredProperties).getIgnorePickIntersection();
+ }
+ {
+ EntityPropertyFlags desiredProperties;
+ desiredProperties += PROP_STROKE_WIDTHS;
+ auto widths = entityScriptingInterface->getEntityProperties(getPathID(), desiredProperties).getStrokeWidths();
+ _lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0];
+ }
}
}
void LaserPointer::RenderState::cleanup() {
StartEndRenderState::cleanup();
- if (!_pathID.isNull()) {
- qApp->getOverlays().deleteOverlay(_pathID);
+ if (!getPathID().isNull()) {
+ DependencyManager::get()->deleteEntity(getPathID());
}
}
void LaserPointer::RenderState::disable() {
StartEndRenderState::disable();
if (!getPathID().isNull()) {
- QVariantMap pathProps;
- pathProps.insert("visible", false);
- pathProps.insert("ignorePickIntersection", true);
- qApp->getOverlays().editOverlay(getPathID(), pathProps);
+ EntityItemProperties properties;
+ properties.setVisible(false);
+ properties.setIgnorePickIntersection(true);
+ DependencyManager::get()->editEntity(getPathID(), properties);
}
}
void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
- QVariant endVariant = vec3toVariant(end);
if (!getPathID().isNull()) {
- QVariantMap pathProps;
- pathProps.insert("start", vec3toVariant(origin));
- pathProps.insert("end", endVariant);
- pathProps.insert("visible", true);
- pathProps.insert("ignorePickIntersection", doesPathIgnoreRays());
- pathProps.insert("lineWidth", getLineWidth() * parentScale);
- qApp->getOverlays().editOverlay(getPathID(), pathProps);
+ EntityItemProperties properties;
+ QVector points;
+ points.append(glm::vec3(0.0f));
+ points.append(end - origin);
+ properties.setPosition(origin);
+ properties.setLinePoints(points);
+ properties.setVisible(true);
+ properties.setIgnorePickIntersection(doesPathIgnorePicks());
+ QVector widths;
+ widths.append(getLineWidth() * parentScale);
+ DependencyManager::get()->editEntity(getPathID(), properties);
}
}
std::shared_ptr LaserPointer::buildRenderState(const QVariantMap& propMap) {
+ // FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers
QUuid startID;
if (propMap["start"].isValid()) {
QVariantMap startMap = propMap["start"].toMap();
@@ -232,9 +248,8 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P
glm::vec3 LaserPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {
switch (pickedObject.type) {
case ENTITY:
+ case LOCAL_ENTITY:
return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction);
- case OVERLAY:
- return RayPick::intersectRayWithOverlayXYPlane(pickedObject.objectID, origin, direction);
default:
return glm::vec3(NAN);
}
diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h
index bcd4bb607d..13d108baee 100644
--- a/interface/src/raypick/LaserPointer.h
+++ b/interface/src/raypick/LaserPointer.h
@@ -19,10 +19,10 @@ public:
class RenderState : public StartEndRenderState {
public:
RenderState() {}
- RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID);
+ RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID);
- const OverlayID& getPathID() const { return _pathID; }
- const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; }
+ const QUuid& getPathID() const { return _pathID; }
+ const bool& doesPathIgnorePicks() const { return _pathIgnorePicks; }
void setLineWidth(float width) { _lineWidth = width; }
float getLineWidth() const { return _lineWidth; }
@@ -33,9 +33,9 @@ public:
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
private:
- OverlayID _pathID;
- bool _pathIgnoreRays;
+ QUuid _pathID;
+ bool _pathIgnorePicks;
float _lineWidth;
};
diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h
index d85e329e9a..425ffd7de4 100644
--- a/interface/src/raypick/LaserPointerScriptingInterface.h
+++ b/interface/src/raypick/LaserPointerScriptingInterface.h
@@ -27,6 +27,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
*
* @hifi-interface
* @hifi-client-entity
+ * @hifi-avatar
*/
public:
@@ -111,10 +112,10 @@ public:
* @function LaserPointers.setLockEndUUID
* @param {number} id
* @param {Uuid} itemID
- * @param {boolean} isOverlay
+ * @param {boolean} isAvatar
* @param {Mat4} [offsetMat]
*/
- Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
+ Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); }
/**jsdoc
diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp
index b93ced17c6..7a0eed96a8 100644
--- a/interface/src/raypick/ParabolaPick.cpp
+++ b/interface/src/raypick/ParabolaPick.cpp
@@ -10,7 +10,6 @@
#include "Application.h"
#include "EntityScriptingInterface.h"
#include "PickScriptingInterface.h"
-#include "ui/overlays/Overlays.h"
#include "avatar/AvatarManager.h"
#include "scripting/HMDScriptingInterface.h"
#include "DependencyManager.h"
@@ -68,20 +67,15 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick)
DependencyManager::get()->evalParabolaIntersectionVector(pick, searchFilter,
getIncludeItemsAs