Merge branch 'master' into tablet_extended_sounds

This commit is contained in:
vladest 2017-10-02 18:03:49 +02:00
commit e360c1363e
413 changed files with 18702 additions and 7518 deletions

1
.gitattributes vendored
View file

@ -10,6 +10,7 @@
*.json text
*.js text
*.qml text
*.qrc text
*.slf text
*.slh text
*.slv text

5
.gitignore vendored
View file

@ -12,6 +12,11 @@ ext/
Makefile
*.user
# Android Studio
*.iml
local.properties
android/libraries
# Xcode
*.xcodeproj
*.xcworkspace

View file

@ -1,19 +1,56 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file.
### Android Dependencies
# Android Dependencies
You will need the following tools to build our Android targets.
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1
* Install the latest Platform-tools
* Install the latest Build-tools
* Install the SDK Platform for API Level 19
* Install Sources for Android SDK for API Level 19
* Install the ARM EABI v7a System Image if you want to run an emulator.
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.9.1
* [Android Studio](https://developer.android.com/studio/index.html)
* [Google VR SDK](https://github.com/googlevr/gvr-android-sdk/releases)
* [Gradle](https://gradle.org/releases/)
### Qt
Download the Qt online installer. Run the installer and select the android_armv7 binaries. Installing to the default path is recommended
### Android Studio
Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager
From the SDK Platforms tab, select API level 26.
* Install the ARM EABI v7a System Image if you want to run an emulator.
From the SDK Tools tab select the following
* Android SDK Build-Tools
* GPU Debugging Tools
* CMake (even if you have a separate CMake installation)
* LLDB
* Android SDK Platform-Tools
* Android SDK Tools
* Android SDK Tools
* NDK (even if you have the NDK installed separately)
### Google VR SDK
Download the 1.8 Google VR SDK [release](https://github.com/googlevr/gvr-android-sdk/archive/v1.80.0.zip). Unzip the archive to a location on your drive.
### Gradle
Download [Gradle 4.1](https://services.gradle.org/distributions/gradle-4.1-all.zip) and unzip it on your local drive. You may wish to add the location of the bin directory within the archive to your path
#### Set up machine specific Gradle properties
Create a `gradle.properties` file in ~/.gradle. Edit the file to contain the following
QT5_ROOT=C\:\\Qt\\5.9.1\\android_armv7
GVR_ROOT=C\:\\Android\\gvr-android-sdk
Replace the paths with your local installations of Qt5 and the Google VR SDK
# TODO fix the rest
You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.

View file

@ -1,13 +1,16 @@
if (WIN32)
# If we're running under the gradle build, HIFI_ANDROID will be set here, but
# ANDROID will not be set until after the `project` statement. This is the *ONLY*
# place you need to use `HIFI_ANDROID` instead of `ANDROID`
if (WIN32 AND NOT HIFI_ANDROID)
cmake_minimum_required(VERSION 3.7)
else()
cmake_minimum_required(VERSION 3.2)
endif()
include("cmake/init.cmake")
project(hifi)
include("cmake/init.cmake")
include("cmake/compiler.cmake")
if (NOT DEFINED SERVER_ONLY)
@ -54,11 +57,13 @@ endif()
file(GLOB_RECURSE CMAKE_SRC cmake/*.cmake cmake/CMakeLists.txt)
add_custom_target(cmake SOURCES ${CMAKE_SRC})
GroupSources("cmake")
unset(CMAKE_SRC)
file(GLOB_RECURSE JS_SRC scripts/*.js unpublishedScripts/*.js)
add_custom_target(js SOURCES ${JS_SRC})
GroupSources("scripts")
GroupSources("unpublishedScripts")
unset(JS_SRC)
# Locate the required Qt build on the filesystem
setup_qt()
@ -77,6 +82,12 @@ option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
set_packaging_parameters()
# FIXME hack to work on the proper Android toolchain
if (ANDROID)
add_subdirectory(android/app)
return()
endif()
# add subdirectories for all targets
if (BUILD_SERVER)
add_subdirectory(assignment-client)

View file

@ -0,0 +1,8 @@
set(TARGET_NAME native-lib)
setup_hifi_library()
link_hifi_libraries(shared networking gl gpu gpu-gles render-utils)
autoscribe_shader_lib(gpu model render render-utils)
target_opengl()
target_link_libraries(native-lib android log m)
target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers")
target_link_libraries(native-lib "C:/Users/bdavis/Git/hifi/android/libraries/jni/armeabi-v7a/libgvr.so")

57
android/app/build.gradle Normal file
View file

@ -0,0 +1,57 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "org.saintandreas.testapp"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
versionName "1.0"
ndk { abiFilters 'armeabi-v7a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DANDROID_PLATFORM=android-24',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=gnustl_shared',
'-DGVR_ROOT=' + GVR_ROOT,
'-DNATIVE_SCRIBE=c:/bin/scribe.exe',
"-DHIFI_ANDROID_PRECOMPILED=${project.rootDir}/libraries/jni/armeabi-v7a"
}
}
jackOptions { enabled true }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs += '../libraries/jni';
}
}
externalNativeBuild {
cmake {
path '../../CMakeLists.txt'
}
}
}
dependencies {
compile fileTree(dir: "${project.rootDir}/libraries/jar", include: 'QtAndroid-bundled.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.vr:sdk-audio:1.80.0'
compile 'com.google.vr:sdk-base:1.80.0'
}
build.dependsOn(':extractQt5')

25
android/app/proguard-rules.pro vendored Normal file
View file

@ -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

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.saintandreas.testapp">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<uses-feature android:name="android.software.vr.mode" android:required="false"/>
<uses-feature android:name="android.hardware.vr.high_performance" android:required="false"/>
<application
android:allowBackup="true"
android:theme="@style/VrActivityTheme"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize"
android:enableVrMode="@string/gvr_vr_mode_component"
android:resizeableActivity="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.google.intent.category.DAYDREAM"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.google.intent.category.CARDBOARD" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,50 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <vr/gvr/capi/include/gvr.h>
namespace googlevr {
// Convert a GVR matrix to GLM matrix
glm::mat4 toGlm(const gvr::Mat4f &matrix) {
glm::mat4 result;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
result[j][i] = matrix.m[i][j];
}
}
return result;
}
// Given a field of view in degrees, compute the corresponding projection
// matrix.
glm::mat4 perspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) {
const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near;
const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near;
const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near;
const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near;
const float Y = (2 * z_near) / (y_top - y_bottom);
const float A = (x_right + x_left) / (x_right - x_left);
const float B = (y_top + y_bottom) / (y_top - y_bottom);
const float C = (z_near + z_far) / (z_near - z_far);
const float D = (2 * z_near * z_far) / (z_near - z_far);
glm::mat4 result { 0 };
result[2][0] = A;
result[1][1] = Y;
result[2][1] = B;
result[2][2] = C;
result[3][2] = D;
result[2][3] = -1;
return result;
}
glm::quat toGlm(const gvr::ControllerQuat& q) {
glm::quat result;
result.w = q.qw;
result.x = q.qx;
result.y = q.qy;
result.z = q.qz;
return result;
}
}

View file

@ -0,0 +1,78 @@
#include <jni.h>
#include <android/log.h>
#include <QtCore/QDebug>
#include "renderer.h"
int QtMsgTypeToAndroidPriority(QtMsgType type) {
int priority = ANDROID_LOG_UNKNOWN;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
default: break;
}
return priority;
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
}
static jlong toJni(NativeRenderer *renderer) {
return reinterpret_cast<intptr_t>(renderer);
}
static NativeRenderer *fromJni(jlong renderer) {
return reinterpret_cast<NativeRenderer*>(renderer);
}
#define JNI_METHOD(r, name) JNIEXPORT r JNICALL Java_org_saintandreas_testapp_MainActivity_##name
extern "C" {
JNI_METHOD(jlong, nativeCreateRenderer)
(JNIEnv *env, jclass clazz, jobject class_loader, jobject android_context, jlong native_gvr_api) {
qInstallMessageHandler(messageHandler);
#if defined(GVR)
auto gvrContext = reinterpret_cast<gvr_context *>(native_gvr_api);
return toJni(new NativeRenderer(gvrContext));
#else
return toJni(new NativeRenderer(nullptr));
#endif
}
JNI_METHOD(void, nativeDestroyRenderer)
(JNIEnv *env, jclass clazz, jlong renderer) {
delete fromJni(renderer);
}
JNI_METHOD(void, nativeInitializeGl)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->InitializeGl();
}
JNI_METHOD(void, nativeDrawFrame)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->DrawFrame();
}
JNI_METHOD(void, nativeOnTriggerEvent)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnTriggerEvent();
}
JNI_METHOD(void, nativeOnPause)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnPause();
}
JNI_METHOD(void, nativeOnResume)
(JNIEnv *env, jobject obj, jlong renderer) {
fromJni(renderer)->OnResume();
}
} // extern "C"

View file

@ -0,0 +1,636 @@
#include "renderer.h"
#include <mutex>
#include <QtCore/QDebug>
#include <gl/Config.h>
#include "GoogleVRHelpers.h"
#include <gl/GLShaders.h>
#include <shared/Bilateral.h>
#include <gpu/DrawTransformUnitQuad_vert.h>
#include <gpu/DrawTexcoordRectTransformUnitQuad_vert.h>
#include <gpu/DrawViewportQuadTransformTexcoord_vert.h>
#include <gpu/DrawTexture_frag.h>
#include <gpu/DrawTextureOpaque_frag.h>
#include <gpu/DrawColoredTexture_frag.h>
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
#include <render-utils/simple_textured_frag.h>
#include <render-utils/simple_textured_unlit_frag.h>
#include <render-utils/deferred_light_vert.h>
#include <render-utils/deferred_light_point_vert.h>
#include <render-utils/deferred_light_spot_vert.h>
#include <render-utils/directional_ambient_light_frag.h>
#include <render-utils/directional_skybox_light_frag.h>
#include <render-utils/standardTransformPNTC_vert.h>
#include <render-utils/standardDrawTexture_frag.h>
#include <render-utils/model_vert.h>
#include <render-utils/model_shadow_vert.h>
#include <render-utils/model_normal_map_vert.h>
#include <render-utils/model_lightmap_vert.h>
#include <render-utils/model_lightmap_normal_map_vert.h>
#include <render-utils/skin_model_vert.h>
#include <render-utils/skin_model_shadow_vert.h>
#include <render-utils/skin_model_normal_map_vert.h>
#include <render-utils/model_frag.h>
#include <render-utils/model_shadow_frag.h>
#include <render-utils/model_normal_map_frag.h>
#include <render-utils/model_normal_specular_map_frag.h>
#include <render-utils/model_specular_map_frag.h>
#include <render-utils/model_lightmap_frag.h>
#include <render-utils/model_lightmap_normal_map_frag.h>
#include <render-utils/model_lightmap_normal_specular_map_frag.h>
#include <render-utils/model_lightmap_specular_map_frag.h>
#include <render-utils/model_translucent_frag.h>
#include <render-utils/overlay3D_vert.h>
#include <render-utils/overlay3D_frag.h>
#include <render-utils/sdf_text3D_vert.h>
#include <render-utils/sdf_text3D_frag.h>
#if 0
#include <model/skybox_vert.h>
#include <model/skybox_frag.h>
#include <entities-renderer/textured_particle_frag.h>
#include <entities-renderer/textured_particle_vert.h>
#include <entities-renderer/paintStroke_vert.h>
#include <entities-renderer/paintStroke_frag.h>
#include <entities-renderer/polyvox_vert.h>
#include <entities-renderer/polyvox_frag.h>
#endif
template <typename F>
void withFrameBuffer(gvr::Frame& frame, int32_t index, F f) {
frame.BindBuffer(index);
f();
frame.Unbind();
}
static const uint64_t kPredictionTimeWithoutVsyncNanos = 50000000;
// Each shader has two variants: a single-eye ES 2.0 variant, and a multiview
// ES 3.0 variant. The multiview vertex shaders use transforms defined by
// arrays of mat4 uniforms, using gl_ViewID_OVR to determine the array index.
#define UNIFORM_LIGHT_POS 20
#define UNIFORM_M 16
#define UNIFORM_MV 8
#define UNIFORM_MVP 0
#if 0
uniform Transform { // API uses “Transform[2]” to refer to instance 2
mat4 u_MVP[2];
mat4 u_MVMatrix[2];
mat4 u_Model;
vec3 u_LightPos[2];
};
static const char *kDiffuseLightingVertexShader = R"glsl(
#version 300 es
#extension GL_OVR_multiview2 : enable
layout(num_views=2) in;
layout(location = 0) uniform mat4 u_MVP[2];
layout(location = 8) uniform mat4 u_MVMatrix[2];
layout(location = 16) uniform mat4 u_Model;
layout(location = 20) uniform vec3 u_LightPos[2];
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec4 a_Color;
layout(location = 2) in vec3 a_Normal;
out vec4 v_Color;
out vec3 v_Grid;
void main() {
mat4 mvp = u_MVP[gl_ViewID_OVR];
mat4 modelview = u_MVMatrix[gl_ViewID_OVR];
vec3 lightpos = u_LightPos[gl_ViewID_OVR];
v_Grid = vec3(u_Model * a_Position);
vec3 modelViewVertex = vec3(modelview * a_Position);
vec3 modelViewNormal = vec3(modelview * vec4(a_Normal, 0.0));
float distance = length(lightpos - modelViewVertex);
vec3 lightVector = normalize(lightpos - modelViewVertex);
float diffuse = max(dot(modelViewNormal, lightVector), 0.5);
diffuse = diffuse * (1.0 / (1.0 + (0.00001 * distance * distance)));
v_Color = vec4(a_Color.rgb * diffuse, a_Color.a);
gl_Position = mvp * a_Position;
}
)glsl";
#endif
static const char *kSimepleVertexShader = R"glsl(
#version 300 es
#extension GL_OVR_multiview2 : enable
layout(num_views=2) in;
layout(location = 0) in vec4 a_Position;
out vec4 v_Color;
void main() {
v_Color = vec4(a_Position.xyz, 1.0);
gl_Position = vec4(a_Position.xyz, 1.0);
}
)glsl";
static const char *kPassthroughFragmentShader = R"glsl(
#version 300 es
precision mediump float;
in vec4 v_Color;
out vec4 FragColor;
void main() { FragColor = v_Color; }
)glsl";
static void CheckGLError(const char* label) {
int gl_error = glGetError();
if (gl_error != GL_NO_ERROR) {
qWarning("GL error @ %s: %d", label, gl_error);
// Crash immediately to make OpenGL errors obvious.
abort();
}
}
// Contains vertex, normal and other data.
namespace cube {
const std::array<float, 108> CUBE_COORDS{{
// Front face
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
// Right face
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
// Back face
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
// Left face
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
// Top face
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
// Bottom face
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f
}};
const std::array<float, 108> CUBE_COLORS{{
// front, green
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
// right, blue
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
// back, also green
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
0.0f, 0.5273f, 0.2656f,
// left, also blue
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
0.0f, 0.3398f, 0.9023f,
// top, red
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
// bottom, also red
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f,
0.8359375f, 0.17578125f, 0.125f
}};
const std::array<float, 108> CUBE_NORMALS{{
// Front face
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
// Right face
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
// Back face
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
// Left face
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
// Top face
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
// Bottom face
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f
}};
}
namespace triangle {
static std::array<float, 9> TRIANGLE_VERTS {{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
}};
}
std::array<gvr::BufferViewport, 2> buildViewports(const std::unique_ptr<gvr::GvrApi> &gvrapi) {
return { {gvrapi->CreateBufferViewport(), gvrapi->CreateBufferViewport()} };
};
const std::string VERTEX_SHADER_DEFINES{ R"GLSL(
#version 300 es
#extension GL_EXT_clip_cull_distance : enable
#define GPU_VERTEX_SHADER
#define GPU_SSBO_TRANSFORM_OBJECT 1
#define GPU_TRANSFORM_IS_STEREO
#define GPU_TRANSFORM_STEREO_CAMERA
#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED
#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN
)GLSL" };
const std::string PIXEL_SHADER_DEFINES{ R"GLSL(
#version 300 es
precision mediump float;
#define GPU_PIXEL_SHADER
#define GPU_TRANSFORM_IS_STEREO
#define GPU_TRANSFORM_STEREO_CAMERA
#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED
#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN
)GLSL" };
#if defined(GVR)
NativeRenderer::NativeRenderer(gvr_context *vrContext) :
_gvrapi(new gvr::GvrApi(vrContext, false)),
_viewports(buildViewports(_gvrapi)),
_gvr_viewer_type(_gvrapi->GetViewerType())
#else
NativeRenderer::NativeRenderer(void *vrContext)
#endif
{
start = std::chrono::system_clock::now();
qDebug() << "QQQ" << __FUNCTION__;
}
/**
* Converts a raw text file, saved as a resource, into an OpenGL ES shader.
*
* @param type The type of shader we will be creating.
* @param resId The resource ID of the raw text file.
* @return The shader object handler.
*/
int LoadGLShader(int type, const char *shadercode) {
GLuint result = 0;
std::string shaderError;
static const std::string SHADER_DEFINES;
if (!gl::compileShader(type, shadercode, SHADER_DEFINES, result, shaderError)) {
qWarning() << "QQQ" << __FUNCTION__ << "Shader compile failure" << shaderError.c_str();
}
return result;
}
// Computes a texture size that has approximately half as many pixels. This is
// equivalent to scaling each dimension by approximately sqrt(2)/2.
static gvr::Sizei HalfPixelCount(const gvr::Sizei &in) {
// Scale each dimension by sqrt(2)/2 ~= 7/10ths.
gvr::Sizei out;
out.width = (7 * in.width) / 10;
out.height = (7 * in.height) / 10;
return out;
}
#if defined(GVR)
void NativeRenderer::InitializeVR() {
_gvrapi->InitializeGl();
bool multiviewEnabled = _gvrapi->IsFeatureSupported(GVR_FEATURE_MULTIVIEW);
qWarning() << "QQQ" << __FUNCTION__ << "Multiview enabled " << multiviewEnabled;
// Because we are using 2X MSAA, we can render to half as many pixels and
// achieve similar quality.
_renderSize = HalfPixelCount(_gvrapi->GetMaximumEffectiveRenderTargetSize());
std::vector<gvr::BufferSpec> specs;
specs.push_back(_gvrapi->CreateBufferSpec());
specs[0].SetColorFormat(GVR_COLOR_FORMAT_RGBA_8888);
specs[0].SetDepthStencilFormat(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16);
specs[0].SetSamples(2);
gvr::Sizei half_size = {_renderSize.width / 2, _renderSize.height};
specs[0].SetMultiviewLayers(2);
specs[0].SetSize(half_size);
_swapchain.reset(new gvr::SwapChain(_gvrapi->CreateSwapChain(specs)));
_viewportlist.reset(new gvr::BufferViewportList(_gvrapi->CreateEmptyBufferViewportList()));
}
void NativeRenderer::PrepareFramebuffer() {
const gvr::Sizei recommended_size = HalfPixelCount(
_gvrapi->GetMaximumEffectiveRenderTargetSize());
if (_renderSize.width != recommended_size.width ||
_renderSize.height != recommended_size.height) {
// We need to resize the framebuffer. Note that multiview uses two texture
// layers, each with half the render width.
gvr::Sizei framebuffer_size = recommended_size;
framebuffer_size.width /= 2;
_swapchain->ResizeBuffer(0, framebuffer_size);
_renderSize = recommended_size;
}
}
#endif
void testShaderBuild(const char* vs_src, const char * fs_src) {
std::string error;
GLuint vs, fs;
if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) ||
!gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) {
throw std::runtime_error("Failed to compile shader");
}
auto pr = gl::compileProgram({ vs, fs }, error);
if (!pr) {
throw std::runtime_error("Failed to link shader");
}
}
void NativeRenderer::InitializeGl() {
qDebug() << "QQQ" << __FUNCTION__;
//gl::initModuleGl();
#if defined(GVR)
InitializeVR();
#endif
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kSimepleVertexShader);
//const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kDiffuseLightingVertexShader);
const uint32_t fragShader = LoadGLShader(GL_FRAGMENT_SHADER, kPassthroughFragmentShader);
std::string error;
_cubeProgram = gl::compileProgram({ vertShader, fragShader }, error);
CheckGLError("build program");
glGenBuffers(1, &_cubeBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, triangle::TRIANGLE_VERTS.data(), GL_STATIC_DRAW);
/*
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 3, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 0, sizeof(float) * 108, cube::CUBE_COORDS.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 1, sizeof(float) * 108, cube::CUBE_COLORS.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 2, sizeof(float) * 108, cube::CUBE_NORMALS.data());
*/
glBindBuffer(GL_ARRAY_BUFFER, 0);
CheckGLError("upload vertices");
glGenVertexArrays(1, &_cubeVao);
glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer);
glBindVertexArray(_cubeVao);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
/*
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 1) );
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 2));
glEnableVertexAttribArray(2);
*/
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CheckGLError("build vao ");
static std::once_flag once;
std::call_once(once, [&]{
testShaderBuild(sdf_text3D_vert, sdf_text3D_frag);
testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag);
testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag);
testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag);
testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag);
testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag);
testShaderBuild(simple_vert, simple_frag);
testShaderBuild(simple_vert, simple_textured_frag);
testShaderBuild(simple_vert, simple_textured_unlit_frag);
testShaderBuild(deferred_light_vert, directional_ambient_light_frag);
testShaderBuild(deferred_light_vert, directional_skybox_light_frag);
testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);
testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);
testShaderBuild(model_vert, model_frag);
testShaderBuild(model_normal_map_vert, model_normal_map_frag);
testShaderBuild(model_vert, model_specular_map_frag);
testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag);
testShaderBuild(model_vert, model_translucent_frag);
testShaderBuild(model_normal_map_vert, model_translucent_frag);
testShaderBuild(model_lightmap_vert, model_lightmap_frag);
testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag);
testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag);
testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag);
testShaderBuild(skin_model_vert, model_frag);
testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag);
testShaderBuild(skin_model_vert, model_specular_map_frag);
testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag);
testShaderBuild(skin_model_vert, model_translucent_frag);
testShaderBuild(skin_model_normal_map_vert, model_translucent_frag);
testShaderBuild(model_shadow_vert, model_shadow_frag);
testShaderBuild(overlay3D_vert, overlay3D_frag);
#if 0
testShaderBuild(textured_particle_vert, textured_particle_frag);
testShaderBuild(skybox_vert, skybox_frag);
testShaderBuild(paintStroke_vert,paintStroke_frag);
testShaderBuild(polyvox_vert, polyvox_frag);
#endif
});
qDebug() << "done";
}
static const float kZNear = 1.0f;
static const float kZFar = 100.0f;
static const gvr_rectf fullscreen = {0, 1, 0, 1};
void NativeRenderer::DrawFrame() {
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - start);
glm::vec3 v;
v.r = (float) (now.count() % 1000) / 1000.0f;
v.g = 1.0f - v.r;
v.b = 1.0f;
PrepareFramebuffer();
// A client app does its rendering here.
gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
using namespace googlevr;
using namespace bilateral;
const auto gvrHeadPose = _gvrapi->GetHeadSpaceFromStartSpaceRotation(target_time);
_head_view = toGlm(gvrHeadPose);
_viewportlist->SetToRecommendedBufferViewports();
glm::mat4 eye_views[2];
for_each_side([&](bilateral::Side side) {
int eye = index(side);
const gvr::Eye gvr_eye = eye == 0 ? GVR_LEFT_EYE : GVR_RIGHT_EYE;
const auto& eyeView = eye_views[eye] = toGlm(_gvrapi->GetEyeFromHeadMatrix(gvr_eye)) * _head_view;
auto& viewport = _viewports[eye];
_viewportlist->GetBufferViewport(eye, &viewport);
viewport.SetSourceUv(fullscreen);
viewport.SetSourceLayer(eye);
_viewportlist->SetBufferViewport(eye, viewport);
const auto &mvc = _modelview_cube[eye] = eyeView * _model_cube;
const auto &mvf = _modelview_floor[eye] = eyeView * _model_floor;
const gvr_rectf fov = viewport.GetSourceFov();
const glm::mat4 perspective = perspectiveMatrixFromView(fov, kZNear, kZFar);
_modelview_projection_cube[eye] = perspective * mvc;
_modelview_projection_floor[eye] = perspective * mvf;
_light_pos_eye_space[eye] = glm::vec3(eyeView * _light_pos_world_space);
});
gvr::Frame frame = _swapchain->AcquireFrame();
withFrameBuffer(frame, 0, [&]{
glClearColor(v.r, v.g, v.b, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, _renderSize.width / 2, _renderSize.height);
glUseProgram(_cubeProgram);
glBindVertexArray(_cubeVao);
glDrawArrays(GL_TRIANGLES, 0, 3);
/*
float* fp;
fp = (float*)&_light_pos_eye_space[0];
glUniform3fv(UNIFORM_LIGHT_POS, 2, fp);
fp = (float*)&_modelview_cube[0];
glUniformMatrix4fv(UNIFORM_MV, 2, GL_FALSE, fp);
fp = (float*)&_modelview_projection_cube[0];
glUniformMatrix4fv(UNIFORM_MVP, 2, GL_FALSE, fp);
fp = (float*)&_model_cube;
glUniformMatrix4fv(UNIFORM_M, 1, GL_FALSE, fp);
glDrawArrays(GL_TRIANGLES, 0, 36);
*/
glBindVertexArray(0);
});
frame.Submit(*_viewportlist, gvrHeadPose);
CheckGLError("onDrawFrame");
}
void NativeRenderer::OnTriggerEvent() {
qDebug() << "QQQ" << __FUNCTION__;
}
void NativeRenderer::OnPause() {
qDebug() << "QQQ" << __FUNCTION__;
_gvrapi->PauseTracking();
}
void NativeRenderer::OnResume() {
qDebug() << "QQQ" << __FUNCTION__;
_gvrapi->ResumeTracking();
_gvrapi->RefreshViewerProfile();
}

View file

@ -0,0 +1,60 @@
#pragma once
#include <chrono>
#include <array>
#include <glm/glm.hpp>
#define GVR
#if defined(GVR)
#include <vr/gvr/capi/include/gvr.h>
#endif
class NativeRenderer {
public:
#if defined(GVR)
NativeRenderer(gvr_context* vrContext);
#else
NativeRenderer(void* vrContext);
#endif
void InitializeGl();
void DrawFrame();
void OnTriggerEvent();
void OnPause();
void OnResume();
private:
std::chrono::time_point<std::chrono::system_clock> start;
#if defined(GVR)
void InitializeVR();
void PrepareFramebuffer();
std::unique_ptr<gvr::GvrApi> _gvrapi;
gvr::ViewerType _gvr_viewer_type;
std::unique_ptr<gvr::BufferViewportList> _viewportlist;
std::unique_ptr<gvr::SwapChain> _swapchain;
std::array<gvr::BufferViewport, 2> _viewports;
gvr::Sizei _renderSize;
#endif
uint32_t _cubeBuffer { 0 };
uint32_t _cubeVao { 0 };
uint32_t _cubeProgram { 0 };
glm::mat4 _head_view;
glm::mat4 _model_cube;
glm::mat4 _camera;
glm::mat4 _view;
glm::mat4 _model_floor;
std::array<glm::mat4, 2> _modelview_cube;
std::array<glm::mat4, 2> _modelview_floor;
std::array<glm::mat4, 2> _modelview_projection_cube;
std::array<glm::mat4, 2> _modelview_projection_floor;
std::array<glm::vec3, 2> _light_pos_eye_space;
const glm::vec4 _light_pos_world_space{ 0, 2, 0, 1};
};

View file

@ -0,0 +1,105 @@
package org.saintandreas.testapp;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.View;
import com.google.vr.ndk.base.AndroidCompat;
import com.google.vr.ndk.base.GvrLayout;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MainActivity extends Activity {
private final static int IMMERSIVE_STICKY_VIEW_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
static {
System.loadLibrary("gvr");
System.loadLibrary("native-lib");
}
private long nativeRenderer;
private GvrLayout gvrLayout;
private GLSurfaceView surfaceView;
private native long nativeCreateRenderer(ClassLoader appClassLoader, Context context, long nativeGvrContext);
private native void nativeDestroyRenderer(long renderer);
private native void nativeInitializeGl(long renderer);
private native void nativeDrawFrame(long renderer);
private native void nativeOnTriggerEvent(long renderer);
private native void nativeOnPause(long renderer);
private native void nativeOnResume(long renderer);
class NativeRenderer implements GLSurfaceView.Renderer {
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInitializeGl(nativeRenderer); }
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { }
@Override public void onDrawFrame(GL10 gl) {
nativeDrawFrame(nativeRenderer);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setImmersiveSticky();
getWindow()
.getDecorView()
.setOnSystemUiVisibilityChangeListener((int visibility)->{
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { setImmersiveSticky(); }
});
gvrLayout = new GvrLayout(this);
nativeRenderer = nativeCreateRenderer(
getClass().getClassLoader(),
getApplicationContext(),
gvrLayout.getGvrApi().getNativeGvrContext());
surfaceView = new GLSurfaceView(this);
surfaceView.setEGLContextClientVersion(3);
surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
surfaceView.setPreserveEGLContextOnPause(true);
surfaceView.setRenderer(new NativeRenderer());
gvrLayout.setPresentationView(surfaceView);
setContentView(gvrLayout);
if (gvrLayout.setAsyncReprojectionEnabled(true)) {
AndroidCompat.setSustainedPerformanceMode(this, true);
}
AndroidCompat.setVrModeEnabled(this, true);
}
@Override
protected void onDestroy() {
super.onDestroy();
gvrLayout.shutdown();
nativeDestroyRenderer(nativeRenderer);
nativeRenderer = 0;
}
@Override
protected void onPause() {
surfaceView.queueEvent(()->nativeOnPause(nativeRenderer));
surfaceView.onPause();
gvrLayout.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
gvrLayout.onResume();
surfaceView.onResume();
surfaceView.queueEvent(()->nativeOnResume(nativeRenderer));
}
private void setImmersiveSticky() {
getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_STICKY_VIEW_FLAGS);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white_opaque">#ffffff</color>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">TestApp</string>
</resources>

View file

@ -0,0 +1,15 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:style/Theme.NoTitleBar.Fullscreen">
<!-- Customize your theme here. -->
<!--item name="android:windowFullscreen">true</item-->
<!--item name="android:windowNoTitle">true</item-->
<!--item name="android:windowActionBar">false</item-->
<!--item name="android:windowContentOverlay">@null</item-->
<!--item name="android:background">@color/white_opaque</item-->
</style>
</resources>

91
android/build.gradle Normal file
View file

@ -0,0 +1,91 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
task extractQt5jars(type: Copy) {
from fileTree(QT5_ROOT + "/jar")
into("${project.rootDir}/libraries/jar")
include("*.jar")
}
task extractQt5so(type: Copy) {
from fileTree(QT5_ROOT + "/lib")
into("${project.rootDir}/libraries/jni/armeabi-v7a/")
include("libQt5AndroidExtras.so")
include("libQt5Concurrent.so")
include("libQt5Core.so")
include("libQt5Gamepad.so")
include("libQt5Gui.so")
include("libQt5Location.so")
include("libQt5Multimedia.so")
include("libQt5MultimediaQuick_p.so")
include("libQt5Network.so")
include("libQt5NetworkAuth.so")
include("libQt5OpenGL.so")
include("libQt5Positioning.so")
include("libQt5Qml.so")
include("libQt5Quick.so")
include("libQt5QuickControls2.so")
include("libQt5QuickParticles.so")
include("libQt5QuickTemplates2.so")
include("libQt5QuickWidgets.so")
include("libQt5Script.so")
include("libQt5ScriptTools.so")
include("libQt5Sensors.so")
include("libQt5Svg.so")
include("libQt5WebChannel.so")
include("libQt5WebSockets.so")
include("libQt5WebView.so")
include("libQt5Widgets.so")
include("libQt5Xml.so")
include("libQt5XmlPatterns.so")
}
task extractAudioSo(type: Copy) {
from zipTree(GVR_ROOT + "/libraries/sdk-audio-1.80.0.aar")
into "${project.rootDir}/libraries/"
include "jni/armeabi-v7a/libgvr_audio.so"
}
task extractGvrSo(type: Copy) {
from zipTree(GVR_ROOT + "/libraries/sdk-base-1.80.0.aar")
into "${project.rootDir}/libraries/"
include "jni/armeabi-v7a/libgvr.so"
}
task extractNdk { }
extractNdk.dependsOn extractAudioSo
extractNdk.dependsOn extractGvrSo
task extractQt5 { }
extractQt5.dependsOn extractQt5so
extractQt5.dependsOn extractQt5jars
task extractBinaries { }
extractBinaries.dependsOn extractQt5
extractBinaries.dependsOn extractNdk
task deleteBinaries(type: Delete) {
delete "${project.rootDir}/libraries/jni"
}
//clean.dependsOn(deleteBinaries)

17
android/gradle.properties Normal file
View file

@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

1
android/settings.gradle Normal file
View file

@ -0,0 +1 @@
include ':app'

View file

@ -0,0 +1,53 @@
//
// EntityPriorityQueue.cpp
// assignment-client/src/entities
//
// Created by Andrew Meadows 2017.08.08
// 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
//
#include "EntityPriorityQueue.h"
const float PrioritizedEntity::DO_NOT_SEND = -1.0e-6f;
const float PrioritizedEntity::FORCE_REMOVE = -1.0e-5f;
const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f;
void ConicalView::set(const ViewFrustum& viewFrustum) {
// The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
// Why? Because approximate intersection tests are much faster to compute for a cone than for a frustum.
_position = viewFrustum.getPosition();
_direction = viewFrustum.getDirection();
// We cache the sin and cos of the half angle of the cone that bounds the frustum.
// (the math here is left as an exercise for the reader)
float A = viewFrustum.getAspectRatio();
float t = tanf(0.5f * viewFrustum.getFieldOfView());
_cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t));
_sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
_radius = viewFrustum.getCenterRadius();
}
float ConicalView::computePriority(const AACube& cube) const {
glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame
float d = glm::length(p); // distance to center of bounding sphere
float r = 0.5f * cube.getScale(); // radius of bounding sphere
if (d < _radius + r) {
return r;
}
// We check the angle between the center of the cube and the _direction of the view.
// If it is less than the sum of the half-angle from center of cone to outer edge plus
// the half apparent angle of the bounding sphere then it is in view.
//
// The math here is left as an exercise for the reader with the following hints:
// (1) We actually check the dot product of the cube's local position rather than the angle and
// (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) {
const float AVOID_DIVIDE_BY_ZERO = 0.001f;
return r / (d + AVOID_DIVIDE_BY_ZERO);
}
return PrioritizedEntity::DO_NOT_SEND;
}

View file

@ -0,0 +1,66 @@
//
// EntityPriorityQueue.h
// assignment-client/src/entities
//
// Created by Andrew Meadows 2017.08.08
// 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
//
#ifndef hifi_EntityPriorityQueue_h
#define hifi_EntityPriorityQueue_h
#include <queue>
#include <AACube.h>
#include <EntityTreeElement.h>
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
const float DEFAULT_VIEW_RADIUS = 10.0f;
// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
class ConicalView {
public:
ConicalView() {}
ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); }
void set(const ViewFrustum& viewFrustum);
float computePriority(const AACube& cube) const;
private:
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
float _sinAngle { SQRT_TWO_OVER_TWO };
float _cosAngle { SQRT_TWO_OVER_TWO };
float _radius { DEFAULT_VIEW_RADIUS };
};
// PrioritizedEntity is a placeholder in a sorted queue.
class PrioritizedEntity {
public:
static const float DO_NOT_SEND;
static const float FORCE_REMOVE;
static const float WHEN_IN_DOUBT_PRIORITY;
PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
EntityItem* getRawEntityPointer() const { return _rawEntityPointer; }
float getPriority() const { return _priority; }
bool shouldForceRemove() const { return _forceRemove; }
class Compare {
public:
bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; }
};
friend class Compare;
private:
EntityItemWeakPointer _weakEntity;
EntityItem* _rawEntityPointer;
float _priority;
bool _forceRemove;
};
using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector<PrioritizedEntity>, PrioritizedEntity::Compare >;
#endif // hifi_EntityPriorityQueue_h

View file

@ -13,9 +13,18 @@
#include <EntityNodeData.h>
#include <EntityTypes.h>
#include <OctreeUtils.h>
#include "EntityServer.h"
EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
OctreeSendThread(myServer, node)
{
connect(std::static_pointer_cast<EntityTree>(myServer->getOctree()).get(), &EntityTree::editingEntityPointer, this, &EntityTreeSendThread::editingEntityPointer, Qt::QueuedConnection);
connect(std::static_pointer_cast<EntityTree>(myServer->getOctree()).get(), &EntityTree::deletingEntityPointer, this, &EntityTreeSendThread::deletingEntityPointer, Qt::QueuedConnection);
}
void EntityTreeSendThread::preDistributionProcessing() {
auto node = _node.toStrongRef();
auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
@ -80,6 +89,72 @@ void EntityTreeSendThread::preDistributionProcessing() {
}
}
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) {
if (viewFrustumChanged || _traversal.finished()) {
ViewFrustum viewFrustum;
nodeData->copyCurrentViewFrustum(viewFrustum);
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum());
// 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
if (viewFrustumChanged && !_sendQueue.empty()) {
EntityPriorityQueue prevSendQueue;
_sendQueue.swap(prevSendQueue);
_entitiesInQueue.clear();
// Re-add elements from previous traversal if they still need to be sent
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
while (!prevSendQueue.empty()) {
EntityItemPointer entity = prevSendQueue.top().getEntity();
bool forceRemove = prevSendQueue.top().shouldForceRemove();
prevSendQueue.pop();
if (entity) {
if (!forceRemove) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
float priority = _conicalView.computePriority(cube);
if (priority != PrioritizedEntity::DO_NOT_SEND) {
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
}
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
_entitiesInQueue.insert(entity.get());
}
}
}
}
}
if (!_traversal.finished()) {
quint64 startTime = usecTimestampNow();
#ifdef DEBUG
const uint64_t TIME_BUDGET = 400; // usec
#else
const uint64_t TIME_BUDGET = 200; // usec
#endif
_traversal.traverse(TIME_BUDGET);
OctreeServer::trackTreeTraverseTime((float)(usecTimestampNow() - startTime));
}
OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
}
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
EntityItem& entityItem, EntityNodeData& nodeData) {
// check if this entity has a parent that is also an entity
@ -129,4 +204,288 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) {
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
// there are three types of traversal:
//
// (1) FirstTime = at login --> find everything in view
// (2) Repeat = view hasn't changed --> find what has changed since last complete traversal
// (3) Differential = view has changed --> find what has changed or in new view but not old
//
// The "scanCallback" we provide to the traversal depends on the type:
//
// The _conicalView is updated here as a cached view approximation used by the lambdas for efficient
// computation of entity sorting priorities.
//
_conicalView.set(_traversal.getCurrentView());
switch (type) {
case DiffTraversal::First:
// When we get to a First traversal, clear the _knownState
_knownState.clear();
if (usesViewFrustum) {
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
_traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([=](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
// Check the size of the entity, it's possible that a "too small to see" entity is included in a
// larger octree cell because of its position (for example if it crosses the boundary of a cell it
// pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
// before we consider including it.
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
});
});
} else {
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([this](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
});
});
}
break;
case DiffTraversal::Repeat:
if (usesViewFrustum) {
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
_traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([=](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end()) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
} else if (entity->getLastEdited() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
});
}
});
} else {
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([this](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end() || entity->getLastEdited() > knownTimestamp->second) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
});
}
});
}
break;
case DiffTraversal::Differential:
assert(usesViewFrustum);
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
float completedLODScaleFactor = _traversal.getCompletedLODScaleFactor();
glm::vec3 completedViewPosition = _traversal.getCompletedView().getPosition();
_traversal.setScanCallback([=] (DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([=](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end()) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
if (!_traversal.getCompletedView().cubeIntersectsKeyhole(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
} else {
// If this entity was skipped last time because it was too small, we still need to send it
distance = glm::distance(cube.calcCenter(), completedViewPosition) + MIN_VISIBLE_DISTANCE;
angularDiameter = cube.getScale() / distance;
if (angularDiameter <= MIN_ENTITY_ANGULAR_DIAMETER * completedLODScaleFactor) {
// this object was skipped in last completed traversal
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
}
}
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
} else if (entity->getLastEdited() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
});
});
break;
}
}
bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
if (_sendQueue.empty()) {
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
return false;
}
quint64 encodeStart = usecTimestampNow();
if (!_packetData.hasContent()) {
// This is the beginning of a new packet.
// We pack minimal data for this to be accepted as an OctreeElement payload for the root element.
// The Octree header bytes look like this:
//
// 0x00 octalcode for root
// 0x00 colors (1 bit where recipient should call: child->readElementDataFromBuffer())
// 0xXX childrenInTreeMask (when params.includeExistsBits is true: 1 bit where child is existant)
// 0x00 childrenInBufferMask (1 bit where recipient should call: child->readElementData() recursively)
const uint8_t zeroByte = 0;
_packetData.appendValue(zeroByte); // octalcode
_packetData.appendValue(zeroByte); // colors
if (params.includeExistsBits) {
uint8_t childrenExistBits = 0;
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
for (int32_t i = 0; i < NUMBER_OF_CHILDREN; ++i) {
if (root->getChildAtIndex(i)) {
childrenExistBits += (1 << i);
}
}
_packetData.appendValue(childrenExistBits); // childrenInTreeMask
}
_packetData.appendValue(zeroByte); // childrenInBufferMask
// Pack zero for numEntities.
// But before we do: grab current byteOffset so we can come back later
// and update this with the real number.
_numEntities = 0;
_numEntitiesOffset = _packetData.getUncompressedByteOffset();
_packetData.appendValue(_numEntities);
}
LevelDetails entitiesLevel = _packetData.startLevel();
uint64_t sendTime = usecTimestampNow();
auto nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
nodeData->stats.encodeStarted();
while(!_sendQueue.empty()) {
PrioritizedEntity queuedItem = _sendQueue.top();
EntityItemPointer entity = queuedItem.getEntity();
if (entity) {
// Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again
if (entity->matchesJSONFilters(jsonFilters)) {
OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData);
if (appendEntityState != OctreeElement::COMPLETED) {
if (appendEntityState == OctreeElement::PARTIAL) {
++_numEntities;
}
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
break;
}
++_numEntities;
}
if (queuedItem.shouldForceRemove()) {
_knownState.erase(entity.get());
} else {
_knownState[entity.get()] = sendTime;
}
}
_sendQueue.pop();
_entitiesInQueue.erase(entity.get());
}
nodeData->stats.encodeStopped();
if (_sendQueue.empty()) {
assert(_entitiesInQueue.empty());
params.stopReason = EncodeBitstreamParams::FINISHED;
_extraEncodeData->entities.clear();
}
if (_numEntities == 0) {
_packetData.discardLevel(entitiesLevel);
OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart));
return false;
}
_packetData.endLevel(entitiesLevel);
_packetData.updatePriorBytes(_numEntitiesOffset, (const unsigned char*)&_numEntities, sizeof(_numEntities));
OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart));
return true;
}
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
if (entity) {
if (_entitiesInQueue.find(entity.get()) == _entitiesInQueue.end() && _knownState.find(entity.get()) != _knownState.end()) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
// We can force a removal from _knownState if the current view is used and entity is out of view
if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
_entitiesInQueue.insert(entity.get());
}
} else {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true));
_entitiesInQueue.insert(entity.get());
}
}
}
}
void EntityTreeSendThread::deletingEntityPointer(EntityItem* entity) {
_knownState.erase(entity);
}

View file

@ -12,24 +12,55 @@
#ifndef hifi_EntityTreeSendThread_h
#define hifi_EntityTreeSendThread_h
#include <unordered_set>
#include "../octree/OctreeSendThread.h"
#include <DiffTraversal.h>
#include "EntityPriorityQueue.h"
class EntityNodeData;
class EntityItem;
class EntityTreeSendThread : public OctreeSendThread {
Q_OBJECT
public:
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) {};
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
protected:
virtual void preDistributionProcessing() override;
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) override;
private:
// the following two methods return booleans to indicate if any extra flagged entities were new additions to set
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;
bool hasSomethingToSend(OctreeQueryNode* nodeData) override { return !_sendQueue.empty(); }
bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) override { return viewFrustumChanged || _traversal.finished(); }
void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) override {};
bool shouldTraverseAndSend(OctreeQueryNode* nodeData) override { return true; }
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;
std::unordered_set<EntityItem*> _entitiesInQueue;
std::unordered_map<EntityItem*, uint64_t> _knownState;
ConicalView _conicalView; // cached optimized view for fast priority calculations
// packet construction stuff
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
int32_t _numEntitiesOffset { 0 };
uint16_t _numEntities { 0 };
private slots:
void editingEntityPointer(const EntityItemPointer& entity);
void deletingEntityPointer(EntityItem* entity);
};
#endif // hifi_EntityTreeSendThread_h

View file

@ -17,7 +17,6 @@
#include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include "OctreeQueryNode.h"
#include "OctreeSendThread.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
@ -27,8 +26,8 @@ quint64 startSceneSleepTime = 0;
quint64 endSceneSleepTime = 0;
OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
_myServer(myServer),
_node(node),
_myServer(myServer),
_nodeUuid(node->getUUID())
{
QString safeServerName("Octree");
@ -48,7 +47,7 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePoint
OctreeSendThread::~OctreeSendThread() {
setIsShuttingDown();
QString safeServerName("Octree");
if (_myServer) {
safeServerName = _myServer->getMyServerName();
@ -301,9 +300,25 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
return numPackets;
}
void OctreeSendThread::preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) {
// If we're starting a full scene, then definitely we want to empty the elementBag
if (isFullScene) {
nodeData->elementBag.deleteAll();
}
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->elementBag.isEmpty()) {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
} else {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
}
/// Version of octree element distributor that sends the deepest LOD level at once
int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
OctreeServer::didPacketDistributor(this);
// if shutting down, exit early
@ -311,7 +326,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
return 0;
}
if (nodeData->elementBag.isEmpty()) {
if (shouldStartNewTraversal(nodeData, viewFrustumChanged)) {
// if we're about to do a fresh pass,
// give our pre-distribution processing a chance to do what it needs
preDistributionProcessing();
@ -345,7 +360,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// If the current view frustum has changed OR we have nothing to send, then search against
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->elementBag.isEmpty()) {
if (shouldStartNewTraversal(nodeData, viewFrustumChanged)) {
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
@ -367,11 +382,6 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
_packetsSentThisInterval += handlePacketSend(node, nodeData, isFullScene);
// If we're starting a full scene, then definitely we want to empty the elementBag
if (isFullScene) {
nodeData->elementBag.deleteAll();
}
// TODO: add these to stats page
//::startSceneSleepTime = _usleepTime;
@ -380,19 +390,11 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
_myServer->getOctree()->getRoot(), _myServer->getJurisdiction());
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->elementBag.isEmpty()) {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
} else {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
preStartNewScene(nodeData, isFullScene);
}
// If we have something in our elementBag, then turn them into packets and send them out...
if (!nodeData->elementBag.isEmpty()) {
if (shouldTraverseAndSend(nodeData)) {
quint64 start = usecTimestampNow();
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
@ -441,7 +443,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the octree elements from the current view frustum
if (nodeData->elementBag.isEmpty()) {
if (!hasSomethingToSend(nodeData)) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
@ -458,7 +460,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
return _truePacketsSent;
}
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params) {
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
bool somethingToSend = false;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
if (!nodeData->elementBag.isEmpty()) {
@ -502,8 +504,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
isFullScene, _myServer->getJurisdiction(), nodeData);
// Our trackSend() function is implemented by the server subclass, and will be called back
// during the encodeTreeBitstream() as new entities/data elements are sent
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
@ -513,7 +514,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
}
bool somethingToSend = true; // assume we have something
bool bagHadSomething = !nodeData->elementBag.isEmpty();
bool hadSomething = hasSomethingToSend(nodeData);
while (somethingToSend && _packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME;
float packetSendingElapsedUsec = OctreeServer::SKIP_TIME;
@ -523,7 +524,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
bool lastNodeDidntFit = false; // assume each node fits
params.stopReason = EncodeBitstreamParams::UNKNOWN; // reset params.stopReason before traversal
somethingToSend = traverseTreeAndBuildNextPacketPayload(params);
somethingToSend = traverseTreeAndBuildNextPacketPayload(params, nodeData->getJSONParameters());
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
@ -531,7 +532,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
}
// If the bag had contents but is now empty then we know we've sent the entire scene.
bool completedScene = bagHadSomething && nodeData->elementBag.isEmpty();
bool completedScene = hadSomething && nodeData->elementBag.isEmpty();
if (completedScene || lastNodeDidntFit) {
// we probably want to flush what has accumulated in nodeData but:
// do we have more data to send? and is there room?

View file

@ -19,6 +19,7 @@
#include <GenericThread.h>
#include <Node.h>
#include <OctreePacketData.h>
#include "OctreeQueryNode.h"
class OctreeQueryNode;
class OctreeServer;
@ -51,24 +52,27 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process() override;
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters);
OctreeServer* _myServer { nullptr };
OctreePacketData _packetData;
QWeakPointer<Node> _node;
OctreeServer* _myServer { nullptr };
private:
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, bool dontSuppressDuplicate = false);
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) { return !nodeData->elementBag.isEmpty(); }
virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) { return viewFrustumChanged || !hasSomethingToSend(nodeData); }
virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene);
virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); }
QUuid _nodeUuid;
OctreePacketData _packetData;
int _truePacketsSent { 0 }; // available for debug stats
int _trueBytesSent { 0 }; // available for debug stats
int _packetsSentThisInterval { 0 }; // used for bandwidth throttle condition

View file

@ -60,6 +60,8 @@ int OctreeServer::_longTreeWait = 0;
int OctreeServer::_shortTreeWait = 0;
int OctreeServer::_noTreeWait = 0;
SimpleMovingAverage OctreeServer::_averageTreeTraverseTime(MOVING_AVERAGE_SAMPLE_COUNTS);
SimpleMovingAverage OctreeServer::_averageNodeWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS);
SimpleMovingAverage OctreeServer::_averageCompressAndWriteTime(MOVING_AVERAGE_SAMPLE_COUNTS);
@ -106,6 +108,8 @@ void OctreeServer::resetSendingStats() {
_shortTreeWait = 0;
_noTreeWait = 0;
_averageTreeTraverseTime.reset();
_averageNodeWaitTime.reset();
_averageCompressAndWriteTime.reset();
@ -522,6 +526,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
(double)_averageTreeExtraLongWaitTime.getAverage(),
(double)(extraLongVsTotal * AS_PERCENT), _extraLongTreeWait);
// traverse
float averageTreeTraverseTime = getAverageTreeTraverseTime();
statsString += QString().sprintf(" Average tree traverse time: %9.2f usecs\r\n\r\n", (double)averageTreeTraverseTime);
// encode
float averageEncodeTime = getAverageEncodeTime();
statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", (double)averageEncodeTime);
@ -883,7 +891,7 @@ OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePoint
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
auto sendThread = newSendThread(node);
// we want to be notified when the thread finishes
connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread);
sendThread->initialize(true);
@ -905,13 +913,13 @@ void OctreeServer::handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> messa
// need to make sure we have it in our nodeList.
auto nodeList = DependencyManager::get<NodeList>();
nodeList->updateNodeWithDataFromPacket(message, senderNode);
auto it = _sendThreads.find(senderNode->getUUID());
if (it == _sendThreads.end()) {
_sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode));
} else if (it->second->isShuttingDown()) {
_sendThreads.erase(it); // Remove right away and wait on thread to be
_sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode));
}
}
@ -1085,7 +1093,7 @@ void OctreeServer::readConfiguration() {
if (getPayload().size() > 0) {
parsePayload();
}
const QJsonObject& settingsObject = DependencyManager::get<NodeList>()->getDomainHandler().getSettingsObject();
QString settingsKey = getMyDomainSettingsKey();
@ -1212,9 +1220,9 @@ void OctreeServer::run() {
OctreeElement::resetPopulationStatistics();
_tree = createTree();
_tree->setIsServer(true);
qDebug() << "Waiting for connection to domain to request settings from domain-server.";
// wait until we have the domain-server settings, otherwise we bail
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::settingsReceived, this, &OctreeServer::domainSettingsRequestComplete);
@ -1225,9 +1233,9 @@ void OctreeServer::run() {
}
void OctreeServer::domainSettingsRequestComplete() {
auto nodeList = DependencyManager::get<NodeList>();
// we need to ask the DS about agents so we can ping/reply with them
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
@ -1237,26 +1245,26 @@ void OctreeServer::domainSettingsRequestComplete() {
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
readConfiguration();
beforeRun(); // after payload has been processed
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
nodeList->linkedDataCreateCallback = [this](Node* node) {
auto queryNodeData = createOctreeQueryNode();
queryNodeData->init();
node->setLinkedData(std::move(queryNodeData));
};
srand((unsigned)time(0));
// if we want Persistence, set up the local file and persist thread
if (_wantPersist) {
// If persist filename does not exist, let's see if there is one beside the application binary
@ -1351,24 +1359,24 @@ void OctreeServer::domainSettingsRequestComplete() {
}
}
qDebug() << "Backups will be stored in: " << _backupDirectoryPath;
// now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval,
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
_persistThread->initialize(true);
}
// set up our jurisdiction broadcaster...
if (_jurisdiction) {
_jurisdiction->setNodeType(getMyNodeType());
}
_jurisdictionSender = new JurisdictionSender(_jurisdiction, getMyNodeType());
_jurisdictionSender->initialize(true);
// set up our OctreeServerPacketProcessor
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
_octreeInboundPacketProcessor->initialize(true);
// Convert now to tm struct for local timezone
tm* localtm = localtime(&_started);
const int MAX_TIME_LENGTH = 128;
@ -1380,7 +1388,7 @@ void OctreeServer::domainSettingsRequestComplete() {
if (gmtm) {
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
}
qDebug() << "Now running... started at: " << localBuffer << utcBuffer;
}
@ -1391,7 +1399,7 @@ void OctreeServer::nodeAdded(SharedNodePointer node) {
void OctreeServer::nodeKilled(SharedNodePointer node) {
quint64 start = usecTimestampNow();
// Shutdown send thread
auto it = _sendThreads.find(node->getUUID());
if (it != _sendThreads.end()) {
@ -1437,13 +1445,13 @@ void OctreeServer::aboutToFinish() {
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
}
// Shut down all the send threads
for (auto& it : _sendThreads) {
auto& sendThread = *it.second;
sendThread.setIsShuttingDown();
}
// Clear will destruct all the unique_ptr to OctreeSendThreads which will call the GenericThread's dtor
// which waits on the thread to be done before returning
_sendThreads.clear(); // Cleans up all the send threads.
@ -1563,7 +1571,7 @@ void OctreeServer::sendStatsPacket() {
threadsStats["2. packetDistributor"] = (double)howManyThreadsDidPacketDistributor(oneSecondAgo);
threadsStats["3. handlePacektSend"] = (double)howManyThreadsDidHandlePacketSend(oneSecondAgo);
threadsStats["4. writeDatagram"] = (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo);
QJsonObject statsArray1;
statsArray1["1. configuration"] = getConfiguration();
statsArray1["2. detailed_stats_url"] = getStatusLink();
@ -1571,13 +1579,13 @@ void OctreeServer::sendStatsPacket() {
statsArray1["4. persistFileLoadTime"] = getFileLoadTime();
statsArray1["5. clients"] = getCurrentClientCount();
statsArray1["6. threads"] = threadsStats;
// Octree Stats
QJsonObject octreeStats;
octreeStats["1. elementCount"] = (double)OctreeElement::getNodeCount();
octreeStats["2. internalElementCount"] = (double)OctreeElement::getInternalNodeCount();
octreeStats["3. leafElementCount"] = (double)OctreeElement::getLeafNodeCount();
// Stats Object 2
QJsonObject dataObject1;
dataObject1["1. totalPackets"] = (double)OctreeSendThread::_totalPackets;
@ -1590,12 +1598,12 @@ void OctreeServer::sendStatsPacket() {
QJsonObject timingArray1;
timingArray1["1. avgLoopTime"] = getAverageLoopTime();
timingArray1["2. avgInsideTime"] = getAverageInsideTime();
timingArray1["3. avgTreeLockTime"] = getAverageTreeWaitTime();
timingArray1["3. avgTreeTraverseTime"] = getAverageTreeTraverseTime();
timingArray1["4. avgEncodeTime"] = getAverageEncodeTime();
timingArray1["5. avgCompressAndWriteTime"] = getAverageCompressAndWriteTime();
timingArray1["6. avgSendTime"] = getAveragePacketSendingTime();
timingArray1["7. nodeWaitTime"] = getAverageNodeWaitTime();
QJsonObject statsObject2;
statsObject2["data"] = dataObject1;
statsObject2["timing"] = timingArray1;
@ -1615,18 +1623,18 @@ void OctreeServer::sendStatsPacket() {
timingArray2["4. avgProcessTimePerElement"] = (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
timingArray2["5. avgLockWaitTimePerElement"] = (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
}
QJsonObject statsObject3;
statsObject3["data"] = dataArray2;
statsObject3["timing"] = timingArray2;
// Merge everything
QJsonObject jsonArray;
jsonArray["1. misc"] = statsArray1;
jsonArray["2. octree"] = octreeStats;
jsonArray["3. outbound"] = statsObject2;
jsonArray["4. inbound"] = statsObject3;
QJsonObject statsObject;
statsObject[QString(getMyServerName()) + "Server"] = jsonArray;
addPacketStatsAndSendStatsPacket(statsObject);

View file

@ -96,6 +96,9 @@ public:
static void trackTreeWaitTime(float time);
static float getAverageTreeWaitTime() { return _averageTreeWaitTime.getAverage(); }
static void trackTreeTraverseTime(float time) { _averageTreeTraverseTime.updateAverage(time); }
static float getAverageTreeTraverseTime() { return _averageTreeTraverseTime.getAverage(); }
static void trackNodeWaitTime(float time) { _averageNodeWaitTime.updateAverage(time); }
static float getAverageNodeWaitTime() { return _averageNodeWaitTime.getAverage(); }
@ -228,6 +231,8 @@ protected:
static int _shortTreeWait;
static int _noTreeWait;
static SimpleMovingAverage _averageTreeTraverseTime;
static SimpleMovingAverage _averageNodeWaitTime;
static SimpleMovingAverage _averageCompressAndWriteTime;

View file

@ -102,7 +102,7 @@ static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
// about each other.
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
if (_entityViewer.getTree() && !_shuttingDown) {
@ -116,7 +116,7 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
// about each other.
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
MessageID messageID;
message->readPrimitive(&messageID);
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));

View file

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- IMPORTANT: Do not manually manipulate this automatically generated file, changes will be gone after the next build! -->
<manifest package="${ANDROID_APK_PACKAGE}" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="${ANDROID_APK_VERSION_NAME}" android:versionCode="${ANDROID_APK_VERSION_CODE}" android:installLocation="auto">
<application
android:hardwareAccelerated="true"
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:label="@string/AppDisplayName"
android:icon="@drawable/icon"
android:debuggable="${ANDROID_APK_DEBUGGABLE}">
<!-- VR MODE -->
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name="${ANDROID_ACTIVITY_NAME}"
android:label="@string/AppDisplayName"
android:screenOrientation="landscape"
android:launchMode="singleTop"
${ANDROID_APK_THEME}>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/> -->
${ANDROID_EXTRA_ACTIVITY_XML}
</activity>
<activity
android:name="com.oculusvr.vrlib.PlatformActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:configChanges="screenSize|orientation|keyboardHidden|keyboard">
</activity>
${ANDROID_EXTRA_APPLICATION_XML}
</application>
<uses-sdk android:minSdkVersion="${ANDROID_API_LEVEL}" android:targetSdkVersion="${ANDROID_API_LEVEL}"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- camera permission required for GEAR VR passthrough camera -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
</manifest>

View file

@ -1,159 +0,0 @@
#
# QtCreateAPK.cmake
#
# 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
#
#
# OPTIONS
# These options will modify how QtCreateAPK behaves. May be useful if somebody wants to fork.
# For High Fidelity purposes these should not need to be changed.
#
set(ANDROID_THIS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) # Directory this CMake file is in
if (POLICY CMP0026)
cmake_policy(SET CMP0026 OLD)
endif ()
macro(qt_create_apk)
if(ANDROID_APK_FULLSCREEN)
set(ANDROID_APK_THEME "android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\"")
else()
set(ANDROID_APK_THEME "")
endif()
if (UPPER_CMAKE_BUILD_TYPE MATCHES RELEASE)
set(ANDROID_APK_DEBUGGABLE "false")
set(ANDROID_APK_RELEASE_LOCAL ${ANDROID_APK_RELEASE})
else ()
set(ANDROID_APK_DEBUGGABLE "true")
set(ANDROID_APK_RELEASE_LOCAL "0")
endif ()
# Create "AndroidManifest.xml"
configure_file("${ANDROID_THIS_DIRECTORY}/AndroidManifest.xml.in" "${ANDROID_APK_BUILD_DIR}/AndroidManifest.xml")
# create "strings.xml"
configure_file("${ANDROID_THIS_DIRECTORY}/strings.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/strings.xml")
# find androiddeployqt
find_program(ANDROID_DEPLOY_QT androiddeployqt HINTS "${QT_DIR}/bin")
# set the path to our app shared library
set(EXECUTABLE_DESTINATION_PATH "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}/lib${TARGET_NAME}.so")
# add our dependencies to the deployment file
get_property(_DEPENDENCIES TARGET ${TARGET_NAME} PROPERTY INTERFACE_LINK_LIBRARIES)
foreach(_IGNORE_COPY IN LISTS IGNORE_COPY_LIBS)
list(REMOVE_ITEM _DEPENDENCIES ${_IGNORE_COPY})
endforeach()
foreach(_DEP IN LISTS _DEPENDENCIES)
if (NOT TARGET ${_DEP})
list(APPEND _DEPS_LIST ${_DEP})
else ()
if(NOT _DEP MATCHES "Qt5::.*")
get_property(_DEP_LOCATION TARGET ${_DEP} PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}")
# recurisvely add libraries which are dependencies of this target
get_property(_DEP_DEPENDENCIES TARGET ${_DEP} PROPERTY INTERFACE_LINK_LIBRARIES)
foreach(_SUB_DEP IN LISTS _DEP_DEPENDENCIES)
if (NOT TARGET ${_SUB_DEP} AND NOT _SUB_DEP MATCHES "Qt5::.*")
list(APPEND _DEPS_LIST ${_SUB_DEP})
endif()
endforeach()
list(APPEND _DEPS_LIST ${_DEP_LOCATION})
endif()
endif ()
endforeach()
list(REMOVE_DUPLICATES _DEPS_LIST)
# just copy static libs to apk libs folder - don't add to deps list
foreach(_LOCATED_DEP IN LISTS _DEPS_LIST)
if (_LOCATED_DEP MATCHES "\\.a$")
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${_LOCATED_DEP} "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}"
)
list(REMOVE_ITEM _DEPS_LIST ${_LOCATED_DEP})
endif ()
endforeach()
string(REPLACE ";" "," _DEPS "${_DEPS_LIST}")
configure_file("${ANDROID_THIS_DIRECTORY}/deployment-file.json.in" "${TARGET_NAME}-deployment.json")
# copy the res folder from the target to the apk build dir
add_custom_target(
${TARGET_NAME}-copy-res
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/res" "${ANDROID_APK_BUILD_DIR}/res"
)
# copy the assets folder from the target to the apk build dir
add_custom_target(
${TARGET_NAME}-copy-assets
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${ANDROID_APK_BUILD_DIR}/assets"
)
# copy the java folder from src to the apk build dir
add_custom_target(
${TARGET_NAME}-copy-java
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/java" "${ANDROID_APK_BUILD_DIR}/src"
)
# copy the libs folder from src to the apk build dir
add_custom_target(
${TARGET_NAME}-copy-libs
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/libs" "${ANDROID_APK_BUILD_DIR}/libs"
)
# handle setup for ndk-gdb
add_custom_target(${TARGET_NAME}-gdb DEPENDS ${TARGET_NAME})
if (ANDROID_APK_DEBUGGABLE)
get_property(TARGET_LOCATION TARGET ${TARGET_NAME} PROPERTY LOCATION)
set(GDB_SOLIB_PATH ${ANDROID_APK_BUILD_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/)
# generate essential Android Makefiles
file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Android.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Application.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
# create gdb.setup
get_directory_property(PROJECT_INCLUDES DIRECTORY ${PROJECT_SOURCE_DIR} INCLUDE_DIRECTORIES)
string(REGEX REPLACE ";" " " PROJECT_INCLUDES "${PROJECT_INCLUDES}")
file(WRITE ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "set solib-search-path ${GDB_SOLIB_PATH}\n")
file(APPEND ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "directory ${PROJECT_INCLUDES}\n")
# copy lib to obj
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${GDB_SOLIB_PATH})
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND cp ${TARGET_LOCATION} ${GDB_SOLIB_PATH})
# strip symbols
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_STRIP} ${TARGET_LOCATION})
endif ()
# use androiddeployqt to create the apk
add_custom_target(${TARGET_NAME}-apk
COMMAND ${ANDROID_DEPLOY_QT} --input "${TARGET_NAME}-deployment.json" --output "${ANDROID_APK_OUTPUT_DIR}" --android-platform android-${ANDROID_API_LEVEL} ${ANDROID_DEPLOY_QT_INSTALL} --verbose --deployment bundled "\\$(ARGS)"
DEPENDS ${TARGET_NAME} ${TARGET_NAME}-copy-res ${TARGET_NAME}-copy-assets ${TARGET_NAME}-copy-java ${TARGET_NAME}-copy-libs ${TARGET_NAME}-gdb
)
# rename the APK if the caller asked us to
if (ANDROID_APK_CUSTOM_NAME)
add_custom_command(
TARGET ${TARGET_NAME}-apk
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E rename "${ANDROID_APK_OUTPUT_DIR}/bin/QtApp-debug.apk" "${ANDROID_APK_OUTPUT_DIR}/bin/${ANDROID_APK_CUSTOM_NAME}"
)
endif ()
endmacro()

File diff suppressed because it is too large Load diff

View file

@ -1,13 +0,0 @@
{
"qt": "@QT_DIR@",
"sdk": "@ANDROID_SDK_ROOT@",
"ndk": "@ANDROID_NDK@",
"toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"toolchain-version": "@ANDROID_COMPILER_VERSION@",
"ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@",
"target-architecture": "@ANDROID_ABI@",
"application-binary": "@EXECUTABLE_DESTINATION_PATH@",
"android-extra-libs": "@_DEPS@",
"android-package-source-directory": "@ANDROID_APK_BUILD_DIR@"
}

View file

@ -1,11 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- IMPORTANT: Do not manually manipulate this automatically generated file, changes will be gone after the next build! -->
<resources>
<string name="AppDisplayName">${ANDROID_APP_DISPLAY_NAME}</string>
<string name="ministro_not_found_msg">Can\'t find Ministro service.\nThe application can\'t start.</string>
<string name="ministro_needed_msg">This application requires Ministro service. Would you like to install it?</string>
<string name="fatal_error_msg">Your application encountered a fatal error and cannot continue.</string>
</resources>

View file

@ -6,7 +6,7 @@ ExternalProject_Add(
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip
URL_MD5 579ac77a3110befa3244d68c0ceb7281
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1

View file

@ -8,9 +8,6 @@ if (WIN32)
elseif (APPLE)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
elseif (ANDROID)
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_and_slim.tar.gz)
set(DOWNLOAD_MD5 04d50b64e1d81245a1be5f75f34d64c7)
else ()
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
@ -107,3 +104,4 @@ endif ()
if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories")
endif ()

View file

@ -34,10 +34,23 @@ file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
include(${CUSTOM_MACRO})
endforeach()
unset(HIFI_CUSTOM_MACROS)
if (ANDROID)
file(GLOB ANDROID_CUSTOM_MACROS "cmake/android/*.cmake")
foreach(CUSTOM_MACRO ${ANDROID_CUSTOM_MACROS})
include(${CUSTOM_MACRO})
endforeach()
set(BUILD_SHARED_LIBS ON)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
string(REGEX REPLACE "\\\\" "/" ANDROID_NDK ${ANDROID_NDK})
string(REGEX REPLACE "\\\\" "/" CMAKE_TOOLCHAIN_FILE ${CMAKE_TOOLCHAIN_FILE})
string(REGEX REPLACE "\\\\" "/" ANDROID_TOOLCHAIN ${ANDROID_TOOLCHAIN})
string(REGEX REPLACE "\\\\" "/" CMAKE_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM})
list(APPEND EXTERNAL_ARGS -DANDROID_ABI=${ANDROID_ABI})
list(APPEND EXTERNAL_ARGS -DANDROID_NDK=${ANDROID_NDK})
list(APPEND EXTERNAL_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
list(APPEND EXTERNAL_ARGS -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM})
list(APPEND EXTERNAL_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE})
list(APPEND EXTERNAL_ARGS -DHIFI_ANDROID=${HIFI_ANDROID})
list(APPEND EXTERNAL_ARGS -DANDROID_PLATFORM=${ANDROID_PLATFORM})
list(APPEND EXTERNAL_ARGS -DANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN})
list(APPEND EXTERNAL_ARGS -DANDROID_STL=${ANDROID_STL})
endif ()

View file

@ -62,7 +62,9 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
# since it's unrunnable by the cross-compiling build machine
# so, we require the compiling user to point us at a compiled executable version for their native toolchain
find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH)
if (NOT NATIVE_SCRIBE)
find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH)
endif()
if (NOT NATIVE_SCRIBE)
message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \

View file

@ -162,5 +162,6 @@ macro(SET_PACKAGING_PARAMETERS)
# create a header file our targets can use to find out the application version
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/includes")
configure_file("${HF_CMAKE_DIR}/templates/BuildInfo.h.in" "${CMAKE_BINARY_DIR}/includes/BuildInfo.h")
include_directories("${CMAKE_BINARY_DIR}/includes")
endmacro(SET_PACKAGING_PARAMETERS)

View file

@ -12,7 +12,7 @@ macro(SETUP_HIFI_LIBRARY)
project(${TARGET_NAME})
# grab the implementation and header files
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c" "src/*.qrc")
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
# add compiler flags to AVX source files
@ -65,7 +65,7 @@ macro(SETUP_HIFI_LIBRARY)
list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core)
# find these Qt modules and link them to our own target
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED CMAKE_FIND_ROOT_PATH_BOTH)
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})

View file

@ -28,7 +28,7 @@ function(calculate_default_qt_dir _RESULT_NAME)
set(QT_DEFAULT_ARCH "gcc_64")
endif()
if (WIN32)
if (WIN32 OR (ANDROID AND ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")))
set(QT_DEFAULT_ROOT "c:/Qt")
else()
set(QT_DEFAULT_ROOT "$ENV{HOME}/Qt")

View file

@ -6,9 +6,11 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_GLEW)
add_dependency_external_projects(glew)
find_package(GLEW REQUIRED)
add_definitions(-DGLEW_STATIC)
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY})
if (NOT ANDROID)
add_definitions(-DGLEW_STATIC)
add_dependency_external_projects(glew)
find_package(GLEW REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY})
endif()
endmacro()

View file

@ -6,15 +6,13 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_OPENGL)
add_definitions(-DGLEW_STATIC)
if (APPLE)
# link in required OS X frameworks and include the right GL headers
find_library(OpenGL OpenGL)
target_link_libraries(${TARGET_NAME} ${OpenGL})
elseif(ANDROID)
target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL")
target_link_libraries(${TARGET_NAME} GLESv3 EGL)
else()
target_nsight()
find_package(OpenGL REQUIRED)
if (${OPENGL_INCLUDE_DIR})
include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}")
@ -22,4 +20,6 @@ macro(TARGET_OPENGL)
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}")
target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR})
endif()
target_nsight()
target_glew()
endmacro()

View file

@ -0,0 +1,32 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_OPENSSL)
if (ANDROID)
# FIXME use a distributable binary
set(OPENSSL_INSTALL_DIR C:/Android/openssl)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE TYPE INTERNAL)
else()
find_package(OpenSSL REQUIRED)
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings."
"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.")
endif()
endif()
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
endmacro()

View file

@ -0,0 +1,24 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_TBB)
if (ANDROID)
set(TBB_INSTALL_DIR C:/tbb-2018/built)
set(TBB_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbb.so CACHE FILEPATH "TBB library location")
set(TBB_MALLOC_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbbmalloc.so CACHE FILEPATH "TBB malloc library location")
set(TBB_INCLUDE_DIRS ${TBB_INSTALL_DIR}/include CACHE TYPE "List of tbb include directories" CACHE FILEPATH "TBB includes location")
set(TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY})
else()
add_dependency_external_projects(tbb)
find_package(TBB REQUIRED)
endif()
target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES})
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS})
endmacro()

View file

@ -269,6 +269,8 @@ void DomainGatekeeper::updateNodePermissions() {
userPerms.permissions |= NodePermissions::Permission::canAdjustLocks;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
} else {
@ -358,6 +360,8 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
userPerms.permissions |= NodePermissions::Permission::canAdjustLocks;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
newNode->setPermissions(userPerms);

View file

@ -959,7 +959,8 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
bool isAgentWithoutRights = nodeA->getType() == NodeType::Agent
&& nodeB->getType() == NodeType::EntityScriptServer
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp();
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp()
&& !nodeA->getCanRezCertified() && !nodeA->getCanRezTmpCertified();
if (isAgentWithoutRights) {
return false;
@ -968,7 +969,7 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
bool isScriptServerForIneffectiveAgent =
(nodeA->getType() == NodeType::EntityScriptServer && nodeB->getType() == NodeType::Agent)
&& ((nodeBData && !nodeBData->getNodeInterestSet().contains(NodeType::EntityScriptServer))
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp()));
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp() && !nodeB->getCanRezCertified() && !nodeB->getCanRezTmpCertified()));
return !isScriptServerForIneffectiveAgent;
} else {

View file

@ -302,6 +302,14 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canReplaceDomainContent);
packPermissions();
}
if (oldVersion < 1.9) {
unpackPermissions();
// This was prior to addition of canRez(Tmp)Certified; add those to localhost permissions by default
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canRezPermanentCertifiedEntities);
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canRezTemporaryCertifiedEntities);
packPermissions();
}
}
unpackPermissions();

View file

@ -171,8 +171,6 @@ else ()
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
endif ()
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
if (WIN32)
# These are external plugins, but we need to do the 'add dependency' here so that their
# binary directories get added to the fixup path
@ -214,10 +212,6 @@ target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries
target_bullet()
target_opengl()
if (NOT ANDROID)
target_glew()
endif ()
# perform standard include and linking for found externals
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})

View file

@ -14,7 +14,6 @@
var isWindowFocused = true;
var isKeyboardRaised = false;
var isNumericKeyboard = false;
var KEYBOARD_HEIGHT = 200;
function shouldRaiseKeyboard() {
var nodeName = document.activeElement.nodeName;
@ -38,6 +37,19 @@
return document.activeElement.type === "number";
};
function scheduleBringToView(timeout) {
var timer = setTimeout(function () {
clearTimeout(timer);
var elementRect = document.activeElement.getBoundingClientRect();
var absoluteElementTop = elementRect.top + window.scrollY;
var middle = absoluteElementTop - (window.innerHeight / 2);
window.scrollTo(0, middle);
}, timeout);
}
setInterval(function () {
var keyboardRaised = shouldRaiseKeyboard();
var numericKeyboard = shouldSetNumeric();
@ -56,13 +68,8 @@
}
if (!isKeyboardRaised) {
var delta = document.activeElement.getBoundingClientRect().bottom + 10
- (document.body.clientHeight - KEYBOARD_HEIGHT);
if (delta > 0) {
setTimeout(function () {
document.body.scrollTop += delta;
}, 500); // Allow time for keyboard to be raised in QML.
}
scheduleBringToView(250); // Allow time for keyboard to be raised in QML.
// 2DO: should it be rather done from 'client area height changed' event?
}
isKeyboardRaised = keyboardRaised;
@ -70,6 +77,13 @@
}
}, POLL_FREQUENCY);
window.addEventListener("click", function () {
var keyboardRaised = shouldRaiseKeyboard();
if(keyboardRaised && isKeyboardRaised) {
scheduleBringToView(150);
}
});
window.addEventListener("focus", function () {
isWindowFocused = true;
});

View file

@ -206,6 +206,10 @@ Item {
text: "Audio Codec: " + root.audioCodec + " Noise Gate: " +
root.audioNoiseGate;
}
StatText {
visible: root.expanded;
text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps";
}
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +

View file

@ -16,8 +16,11 @@ import TabletScriptingInterface 1.0
import "../styles-uit"
Original.Button {
id: root;
property int color: 0
property int colorScheme: hifi.colorSchemes.light
property string buttonGlyph: "";
width: 120
height: hifi.dimensions.controlLineHeight
@ -39,6 +42,13 @@ Original.Button {
background: Rectangle {
radius: hifi.buttons.radius
border.width: (control.color === hifi.buttons.none ||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
gradient: Gradient {
GradientStop {
position: 0.2
@ -71,14 +81,35 @@ Original.Button {
}
}
label: RalewayBold {
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
label: Item {
HiFiGlyphs {
id: buttonGlyph;
visible: root.buttonGlyph !== "";
text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph;
// Size
size: 34;
// Anchors
anchors.right: buttonText.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme];
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayBold {
id: buttonText;
anchors.centerIn: parent;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
}
}
}
}

View file

@ -11,9 +11,14 @@
import QtQuick 2.0
import "."
Item {
Rectangle {
id: keyboardBase
anchors.left: parent.left
anchors.right: parent.right
color: "#252525"
property bool raised: false
property bool numeric: false
@ -105,6 +110,7 @@ Item {
height: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
color: "#252525"
anchors.horizontalCenter: parent.horizontalCenter
TextEdit {
id: mirrorText
@ -116,6 +122,13 @@ Item {
wrapMode: Text.WordWrap
readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent
selectByMouse: false
Keys.onPressed: {
if (event.key == Qt.Key_Return) {
mirrorText.text = "";
event.accepted = true;
}
}
}
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus

View file

@ -17,6 +17,19 @@ RalewaySemiBold {
property int colorScheme: hifi.colorSchemes.light
size: hifi.fontSizes.inputLabel
color: enabled ? (colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGray : hifi.colors.lightGrayText)
: (colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
color: {
if (colorScheme === hifi.colorSchemes.dark) {
if (enabled) {
hifi.colors.lightGrayText
} else {
hifi.colors.baseGrayHighlight
}
} else {
if (enabled) {
hifi.colors.lightGray
} else {
hifi.colors.lightGrayText
}
}
}
}

View file

@ -12,8 +12,13 @@ import QtQuick 2.5
import "../styles-uit"
Item {
property int colorScheme: 0;
readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray ];
readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray ];
// Size
height: 2;
height: colorScheme === 0 ? 2 : 1;
Rectangle {
// Size
width: parent.width;
@ -21,18 +26,19 @@ Item {
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: height;
// Style
color: hifi.colors.baseGrayShadow;
color: topColor[colorScheme];
}
Rectangle {
visible: colorScheme === 0;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: -height;
// Style
color: hifi.colors.baseGrayHighlight;
color: bottomColor[colorScheme];
}
}

View file

@ -20,9 +20,13 @@ TextField {
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray
property bool isSearchField: false
property string label: ""
property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0)
property bool hasRoundedBorder: false
property bool error: false;
property bool hasClearButton: false;
placeholderText: textField.placeholderText
@ -35,16 +39,53 @@ TextField {
y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0
style: TextFieldStyle {
textColor: isLightColorScheme
? (textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (textField.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
textColor: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.lightGrayText
}
}
}
background: Rectangle {
color: isLightColorScheme
? (textField.activeFocus ? hifi.colors.white : hifi.colors.textFieldLightBackground)
: (textField.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
border.color: hifi.colors.primaryHighlight
border.width: textField.activeFocus ? 1 : 0
radius: isSearchField ? textField.height / 2 : 0
color: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.textFieldLightBackground
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.faintGray50
}
} else {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.baseGrayShadow
}
}
}
border.color: textField.error ? hifi.colors.redHighlight :
(textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray))
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
HiFiGlyphs {
text: hifi.glyphs.search
@ -55,12 +96,29 @@ TextField {
anchors.leftMargin: hifi.dimensions.textPadding - 2
visible: isSearchField
}
HiFiGlyphs {
text: hifi.glyphs.error
color: textColor
size: 40
anchors.right: parent.right
anchors.rightMargin: hifi.dimensions.textPadding - 2
anchors.verticalCenter: parent.verticalCenter
visible: hasClearButton && textField.text !== "";
MouseArea {
anchors.fill: parent;
onClicked: {
textField.text = "";
}
}
}
}
placeholderTextColor: hifi.colors.lightGray
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.right: hifi.dimensions.textPadding
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
}
HifiControls.Label {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
//
// CommerceLightbox.qml
// qml/hifi/commerce/common
//
// CommerceLightbox
//
// Created by Zach Fox on 2017-09-19
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
property string titleText;
property string bodyImageSource;
property string bodyText;
property string button1text;
property string button1method;
property string button2text;
property string button2method;
readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " +
"Wallet's private keys.<br><br>You can change your Security Pic in your Wallet.";
id: root;
visible: false;
anchors.fill: parent;
color: Qt.rgba(0, 0, 0, 0.5);
z: 999;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Rectangle {
anchors.centerIn: parent;
width: parent.width - 100;
height: childrenRect.height + 30;
color: "white";
RalewaySemiBold {
id: titleText;
text: root.titleText;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.baseGray;
size: 24;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
Image {
id: bodyImage;
visible: root.bodyImageSource;
source: root.bodyImageSource ? root.bodyImageSource : "";
anchors.top: root.titleText ? titleText.bottom : parent.top;
anchors.topMargin: root.titleText ? 20 : 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 140;
fillMode: Image.PreserveAspectFit;
mipmap: true;
}
RalewayRegular {
id: bodyText;
text: root.bodyText;
anchors.top: root.bodyImageSource ? bodyImage.bottom : (root.titleText ? titleText.bottom : parent.top);
anchors.topMargin: root.bodyImageSource ? 20 : (root.titleText ? 20 : 30);
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.baseGray;
size: 20;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
Item {
id: buttons;
anchors.top: bodyText.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
height: 70;
// Button 1
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 10;
width: root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2;
text: root.button1text;
onClicked: {
eval(button1method);
}
}
// Button 2
HifiControlsUit.Button {
visible: root.button2text;
color: hifi.buttons.noneBorderless;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 10;
width: parent.width/2 - anchors.rightMargin*2;
text: root.button2text;
onClicked: {
eval(button2method);
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToParent(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,333 @@
//
// EmulatedMarketplaceHeader.qml
// qml/hifi/commerce/common
//
// EmulatedMarketplaceHeader
//
// Created by Zach Fox on 2017-09-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.7
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
property string referrerURL: "https://metaverse.highfidelity.com/marketplace?";
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
height: mainContainer.height + additionalDropdownHeight;
Hifi.QmlCommerce {
id: commerce;
onLoginStatusResult: {
if (!isLoggedIn) {
sendToParent({method: "needsLogIn"});
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onSecurityImageResult: {
if (exists) {
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
}
Component.onCompleted: {
commerce.getLoginStatus();
commerce.getSecurityImage();
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
}
}
Rectangle {
id: mainContainer;
color: hifi.colors.white;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
height: 70;
Image {
id: marketplaceHeaderImage;
source: "images/marketplaceHeaderImage.png";
anchors.top: parent.top;
anchors.topMargin: 2;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 8;
width: 140;
fillMode: Image.PreserveAspectFit;
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;
Rectangle {
id: myPurchasesLink;
anchors.right: myUsernameButton.left;
anchors.rightMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: myPurchasesText.paintedWidth + 10;
RalewaySemiBold {
id: myPurchasesText;
text: "My Purchases";
// Text size
size: 18;
// Style
color: hifi.colors.blueAccent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
// Anchors
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToParent({method: 'header_goToPurchases'});
}
onEntered: myPurchasesText.color = hifi.colors.blueHighlight;
onExited: myPurchasesText.color = hifi.colors.blueAccent;
}
}
FontLoader { id: ralewayRegular; source: "../../../../fonts/Raleway-Regular.ttf"; }
TextMetrics {
id: textMetrics;
font.family: ralewayRegular.name
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;
}
}
}
Image {
id: securityImage;
source: "";
visible: securityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 16;
width: height;
mipmap: true;
MouseArea {
enabled: securityImage.visible;
anchors.fill: parent;
onClicked: {
sendToParent({method: "showSecurityPicLightbox", securityImageSource: securityImage.source});
}
}
}
LinearGradient {
z: 996;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 10;
start: Qt.point(0, 0);
end: Qt.point(0, height);
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.lightGrayText }
GradientStop { position: 1.0; color: hifi.colors.white }
}
}
Item {
id: usernameDropdown;
z: 998;
visible: false;
anchors.top: buttonAndUsernameContainer.bottom;
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
anchors.right: buttonAndUsernameContainer.right;
height: childrenRect.height;
width: 100;
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 Items"
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;
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToParent(var msg);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,321 @@
//
// InspectionCertificate.qml
// qml/hifi/commerce/inspectionCertificate
//
// InspectionCertificate
//
// Created by Zach Fox on 2017-09-14
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string marketplaceId: "";
property string itemName: "--";
property string itemOwner: "--";
property string itemEdition: "--";
property string dateOfPurchase: "";
property bool closeGoesToPurchases: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
id: commerce;
}
Image {
anchors.fill: parent;
source: "images/cert-bg.jpg";
}
// Title text
RalewayLight {
id: titleBarText;
text: "Certificate";
// Text size
size: 40;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
// Title text
RalewayRegular {
id: popText;
text: "PROOF OF PURCHASE";
// Text size
size: 16;
// Anchors
anchors.top: titleBarText.bottom;
anchors.topMargin: 4;
anchors.left: titleBarText.left;
anchors.right: titleBarText.right;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
//
// "CERTIFICATE" START
//
Item {
id: certificateContainer;
anchors.top: popText.bottom;
anchors.topMargin: 30;
anchors.bottom: buttonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
RalewayRegular {
id: itemNameHeader;
text: "ITEM NAME";
// Text size
size: 16;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
RalewaySemiBold {
id: itemName;
text: root.itemName;
// Text size
size: 28;
// Anchors
anchors.top: itemNameHeader.bottom;
anchors.topMargin: 4;
anchors.left: itemNameHeader.left;
anchors.right: itemNameHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.blueAccent;
elide: Text.ElideRight;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = hifi.colors.blueAccent;
}
}
RalewayRegular {
id: ownedByHeader;
text: "OWNER";
// Text size
size: 16;
// Anchors
anchors.top: itemName.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
RalewayRegular {
id: ownedBy;
text: root.itemOwner;
// Text size
size: 22;
// Anchors
anchors.top: ownedByHeader.bottom;
anchors.topMargin: 4;
anchors.left: ownedByHeader.left;
anchors.right: ownedByHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
elide: Text.ElideRight;
}
RalewayRegular {
id: editionHeader;
text: "EDITION";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
AnonymousProRegular {
id: edition;
text: root.itemEdition;
// Text size
size: 22;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: dateOfPurchaseHeader;
text: "DATE OF PURCHASE";
visible: root.dateOfPurchase !== "";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
AnonymousProRegular {
id: dateOfPurchase;
text: root.dateOfPurchase;
visible: root.dateOfPurchase !== "";
// Text size
size: 22;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: errorText;
text: "Here we will display some text if there's an <b>error</b> with the certificate " +
"(DMCA takedown, invalid cert, location of item updated)";
// Text size
size: 20;
// Anchors
anchors.top: root.dateOfPurchase !== "" ? dateOfPurchase.bottom : edition.bottom;
anchors.topMargin: 40;
anchors.left: root.dateOfPurchase !== "" ? dateOfPurchase.left : edition.left;
anchors.right: root.dateOfPurchase !== "" ? dateOfPurchase.right : edition.right;
anchors.bottom: parent.bottom;
// Style
wrapMode: Text.WordWrap;
color: hifi.colors.redHighlight;
verticalAlignment: Text.AlignTop;
}
}
//
// "CERTIFICATE" END
//
Item {
id: buttonsContainer;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderless;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "close";
onClicked: {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
}
}
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.right: parent.right;
anchors.rightMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// 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 'inspectionCertificate_setMarketplaceId':
root.marketplaceId = message.marketplaceId;
root.closeGoesToPurchases = message.closeGoesToPurchases;
break;
case 'inspectionCertificate_setItemInfo':
root.itemName = message.itemName;
root.itemOwner = message.itemOwner;
root.itemEdition = message.itemEdition;
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -0,0 +1,196 @@
//
// FirstUseTutorial.qml
// qml/hifi/commerce/purchases
//
// FirstUseTutorial
//
// Created by Zach Fox on 2017-09-13
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string activeView: "step_1";
// Style
color: hifi.colors.baseGray;
//
// "STEP 1" START
//
Item {
id: step_1;
visible: root.activeView === "step_1";
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
RalewayRegular {
id: step1text;
text: "<b>This is the first-time Purchases tutorial.</b><br><br>Here is some <b>bold text</b> " +
"inside Step 1.";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
//
// "STEP 1" END
//
//
// "STEP 2" START
//
Item {
id: step_2;
visible: root.activeView === "step_2";
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
RalewayRegular {
id: step2text;
text: "<b>STEP TWOOO!!!</b>";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
//
// "STEP 2" END
//
Item {
id: tutorialActionButtonsContainer;
// Size
width: root.width;
height: 70;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
// "Skip" or "Back" button
HifiControlsUit.Button {
id: skipOrBackButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: root.activeView === "step_1" ? "Skip" : "Back";
onClicked: {
if (root.activeView === "step_1") {
sendSignalToParent({method: 'tutorial_skipClicked'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) - 1);
}
}
}
// "Next" or "Finish" button
HifiControlsUit.Button {
id: nextButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: root.activeView === "step_2" ? "Finish" : "Next";
onClicked: {
// If this is the final step...
if (root.activeView === "step_2") {
sendSignalToParent({method: 'tutorial_finished'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) + 1);
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// 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 'updatePurchases':
referrerURL = message.referrerURL;
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendSignalToParent(var message);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -13,7 +13,9 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -21,116 +23,395 @@ import "../wallet" as HifiWallet
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
property string itemName: "";
property string itemId: "";
property string itemPreviewImageUrl: "";
property string itemHref: "";
// Style
color: hifi.colors.white;
// Size
width: parent.width;
height: 120;
property string purchaseStatus;
property bool purchaseStatusChanged;
property bool canRezCertifiedItems: false;
property string itemName;
property string itemId;
property string itemPreviewImageUrl;
property string itemHref;
property int ownedItemCount;
property int itemEdition;
Image {
id: itemPreviewImage;
source: root.itemPreviewImageUrl;
height: 110;
width: parent.width;
onPurchaseStatusChangedChanged: {
if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") {
statusText.text = "CONFIRMED!";
statusText.color = hifi.colors.blueAccent;
confirmedTimer.start();
root.purchaseStatusChanged = false;
}
}
Timer {
id: confirmedTimer;
interval: 3000;
onTriggered: {
root.purchaseStatus = "";
}
}
Rectangle {
id: mainContainer;
// Style
color: hifi.colors.white;
// Size
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.top: parent.top;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
width: 180;
fillMode: Image.PreserveAspectFit;
height: root.height - 10;
MouseArea {
anchors.fill: parent;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
}
}
RalewayRegular {
id: itemName;
anchors.top: itemPreviewImage.top;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 30;
// Text size
size: 20;
// Style
color: hifi.colors.blueAccent;
text: root.itemName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
onEntered: {
itemName.color = hifi.colors.blueHighlight;
}
onExited: {
itemName.color = hifi.colors.blueAccent;
}
}
}
Item {
id: buttonContainer;
anchors.top: itemName.bottom;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
// "Rez" button
HifiControlsUit.Button {
id: rezButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
Image {
id: itemPreviewImage;
source: root.itemPreviewImageUrl;
anchors.left: parent.left;
anchors.right: parent.right;
height: parent.height/2 - 4;
text: "Rez Item"
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
anchors.top: parent.top;
anchors.bottom: parent.bottom;
width: height;
fillMode: Image.PreserveAspectCrop;
MouseArea {
anchors.fill: parent;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
}
}
// "More Info" button
HifiControlsUit.Button {
id: moreInfoButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: parent.height/2 - 4;
text: "More Info"
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
RalewaySemiBold {
id: itemName;
anchors.top: itemPreviewImage.top;
anchors.topMargin: 4;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: buttonContainer.left;
anchors.rightMargin: 8;
height: paintedHeight;
// Text size
size: 24;
// Style
color: hifi.colors.blueAccent;
text: root.itemName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
onEntered: {
itemName.color = hifi.colors.blueHighlight;
}
onExited: {
itemName.color = hifi.colors.blueAccent;
}
}
}
Item {
id: certificateContainer;
anchors.top: itemName.bottom;
anchors.topMargin: 4;
anchors.left: itemName.left;
anchors.right: buttonContainer.left;
anchors.rightMargin: 2;
height: 24;
HiFiGlyphs {
id: certificateIcon;
text: hifi.glyphs.scriptNew;
// Size
size: 30;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: 32;
// Style
color: hifi.colors.lightGray;
}
RalewayRegular {
id: viewCertificateText;
text: "VIEW CERTIFICATE";
size: 14;
anchors.left: certificateIcon.right;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
color: hifi.colors.lightGray;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemCertificateClicked', itemMarketplaceId: root.itemId});
}
onEntered: {
certificateIcon.color = hifi.colors.black;
viewCertificateText.color = hifi.colors.black;
}
onExited: {
certificateIcon.color = hifi.colors.lightGray;
viewCertificateText.color = hifi.colors.lightGray;
}
}
}
Item {
id: statusContainer;
visible: root.purchaseStatus || root.ownedItemCount > 1;
anchors.left: itemName.left;
anchors.top: certificateContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.right: buttonContainer.left;
anchors.rightMargin: 2;
RalewaySemiBold {
id: statusText;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
width: paintedWidth;
text: {
if (root.purchaseStatus === "pending") {
"PENDING..."
} else if (root.purchaseStatus === "invalidated") {
"INVALIDATED"
} else if (root.ownedItemCount > 1) {
"<font color='#6a6a6a'>(#" + root.itemEdition + ")</font> <u>You own " + root.ownedItemCount + " others</u>"
} else {
""
}
}
size: 18;
color: {
if (root.purchaseStatus === "pending") {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
}
verticalAlignment: Text.AlignTop;
}
HiFiGlyphs {
id: statusIcon;
text: {
if (root.purchaseStatus === "pending") {
hifi.glyphs.question
} else if (root.purchaseStatus === "invalidated") {
hifi.glyphs.question
} else {
""
}
}
// Size
size: 36;
// Anchors
anchors.top: parent.top;
anchors.topMargin: -8;
anchors.left: statusText.right;
anchors.bottom: parent.bottom;
// Style
color: {
if (root.purchaseStatus === "pending") {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
}
verticalAlignment: Text.AlignTop;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
if (root.purchaseStatus === "pending") {
sendToPurchases({method: 'showPendingLightbox'});
} else if (root.purchaseStatus === "invalidated") {
sendToPurchases({method: 'showInvalidatedLightbox'});
} else if (root.ownedItemCount > 1) {
sendToPurchases({method: 'setFilterText', filterText: root.itemName});
}
}
onEntered: {
if (root.purchaseStatus === "pending") {
statusText.color = hifi.colors.blueHighlight;
statusIcon.color = hifi.colors.blueHighlight;
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redAccent;
statusIcon.color = hifi.colors.redAccent;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueHighlight;
statusIcon.color = hifi.colors.blueHighlight;
}
}
onExited: {
if (root.purchaseStatus === "pending") {
statusText.color = hifi.colors.blueAccent;
statusIcon.color = hifi.colors.blueAccent;
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redHighlight;
statusIcon.color = hifi.colors.redHighlight;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueAccent;
statusIcon.color = hifi.colors.blueAccent;
}
}
}
}
Rectangle {
id: rezzedNotifContainer;
z: 998;
visible: false;
color: hifi.colors.blueHighlight;
anchors.fill: buttonContainer;
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
RalewayBold {
anchors.fill: parent;
text: "REZZED";
size: 18;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignHCenter;
}
Timer {
id: rezzedNotifContainerTimer;
interval: 2000;
onTriggered: rezzedNotifContainer.visible = false
}
}
Button {
id: buttonContainer;
property int color: hifi.buttons.red;
property int colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
width: height;
enabled: root.canRezCertifiedItems && root.purchaseStatus !== "invalidated";
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
}
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
style: ButtonStyle {
background: Rectangle {
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
}
}
}
}
label: Item {
HiFiGlyphs {
id: lightningIcon;
text: hifi.glyphs.lightning;
// Size
size: 32;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.right: parent.right;
horizontalAlignment: Text.AlignHCenter;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
}
RalewayBold {
anchors.top: lightningIcon.bottom;
anchors.topMargin: -20;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: 16;
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: "Rez It"
}
}
}
}
}
DropShadow {
anchors.fill: mainContainer;
horizontalOffset: 3;
verticalOffset: 3;
radius: 8.0;
samples: 17;
color: "#80000000";
source: mainContainer;
}
//

View file

@ -18,6 +18,7 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -30,19 +31,13 @@ Rectangle {
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
property bool canRezCertifiedItems: false;
property bool pendingInventoryReply: true;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
@ -52,9 +47,18 @@ Rectangle {
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
@ -64,103 +68,101 @@ Rectangle {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
} else if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && !passphraseModal.visible) {
passphraseModal.visible = true;
if (!isAuthenticated && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
root.activeView = "purchasesMain";
commerce.inventory();
sendToScript({method: 'purchases_getIsFirstUse'});
}
}
onInventoryResult: {
purchasesReceived = true;
if (result.status !== 'success') {
console.log("Failed to get purchases", result.message);
} else {
purchasesModel.clear();
purchasesModel.append(result.data.assets);
filteredPurchasesModel.clear();
filteredPurchasesModel.append(result.data.assets);
if (previousPurchasesModel.count !== 0) {
checkIfAnyItemStatusChanged();
} else {
// Fill statusChanged default value
// Not doing this results in the default being true...
for (var i = 0; i < purchasesModel.count; i++) {
purchasesModel.setProperty(i, "statusChanged", false);
}
}
previousPurchasesModel.append(result.data.assets);
buildFilteredPurchasesModel();
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
}
root.pendingInventoryReply = false;
}
}
HifiWallet.SecurityImageModel {
id: securityImageModel;
Timer {
id: notSetUpTimer;
interval: 200;
onTriggered: {
sendToScript({method: 'checkout_walletNotSetUp'});
}
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
Connections {
onSendToParent: {
sendToScript(msg);
}
}
}
//
// TITLE BAR START
//
Item {
HifiCommerceCommon.EmulatedMarketplaceHeader {
id: titleBarContainer;
z: 998;
visible: !needsLogIn.visible;
// Size
height: 50;
width: parent.width;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "PURCHASES";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Security Image (TEMPORARY!)
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
Image {
id: securityImageOverlay;
source: "../wallet/images/lockOverlay.png";
width: securityImage.width * 0.45;
height: securityImage.height * 0.45;
anchors.bottom: securityImage.bottom;
anchors.right: securityImage.right;
mipmap: true;
opacity: 0.9;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
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 = "root.visible = false;"
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = "sendToParent({method: 'purchases_openWallet'});";
lightboxPopup.visible = true;
} else {
sendToScript(msg);
}
}
}
}
//
@ -171,10 +173,11 @@ Rectangle {
id: initialize;
visible: root.activeView === "initialize";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
color: hifi.colors.baseGray;
color: hifi.colors.white;
Component.onCompleted: {
securityImageResultReceived = false;
@ -206,101 +209,45 @@ Rectangle {
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
visible: root.activeView === "passphraseModal";
anchors.fill: parent;
titleBarText: "Purchases";
titleBarIcon: hifi.glyphs.wallet;
Connections {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
root.activeView = "initialize";
sendToScript({method: 'purchases_getIsFirstUse'});
} else {
sendToScript(msg);
}
}
}
}
FirstUseTutorial {
id: firstUseTutorial;
visible: root.activeView === "firstUseTutorial";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
//
// "WALLET NOT SET UP" START
//
Item {
id: notSetUp;
visible: root.activeView === "notSetUp";
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
RalewayRegular {
id: notSetUpText;
text: "<b>Your wallet isn't set up.</b><br><br>Set up your Wallet (no credit card necessary) to claim your <b>free HFC</b> " +
"and get items from the Marketplace.";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: notSetUpActionButtonsContainer.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
Item {
id: notSetUpActionButtonsContainer;
// Size
width: root.width;
height: 70;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL});
}
}
// "Set Up" button
HifiControlsUit.Button {
id: setUpButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: "Set Up Wallet"
onClicked: {
sendToScript({method: 'checkout_setUpClicked'});
switch (message.method) {
case 'tutorial_skipClicked':
case 'tutorial_finished':
sendToScript({method: 'purchases_setIsFirstUse'});
root.activeView = "purchasesMain";
commerce.inventory();
break;
}
}
}
}
//
// "WALLET NOT SET UP" END
//
//
// PURCHASES CONTENTS START
@ -310,13 +257,10 @@ Rectangle {
visible: root.activeView === "purchasesMain";
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
//
// FILTER BAR START
@ -329,32 +273,38 @@ Rectangle {
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.rightMargin: 12;
anchors.top: parent.top;
anchors.topMargin: 4;
RalewayRegular {
id: myPurchasesText;
anchors.top: parent.top;
anchors.topMargin: 10;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 4;
width: paintedWidth;
text: "My Purchases";
color: hifi.colors.baseGray;
size: 28;
}
HifiControlsUit.TextField {
id: filterBar;
property int previousLength: 0;
anchors.fill: parent;
placeholderText: "Filter";
colorScheme: hifi.colorSchemes.faintGray;
hasClearButton: true;
hasRoundedBorder: true;
anchors.left: myPurchasesText.right;
anchors.leftMargin: 16;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
placeholderText: "filter items";
onTextChanged: {
if (filterBar.text.length < previousLength) {
filteredPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
for (var i = 0; i < filteredPurchasesModel.count; i++) {
if (filteredPurchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) === -1) {
filteredPurchasesModel.remove(i);
i--;
}
}
previousLength = filterBar.text.length;
buildFilteredPurchasesModel();
}
onAccepted: {
@ -366,29 +316,107 @@ Rectangle {
// FILTER BAR END
//
HifiControlsUit.Separator {
id: separator;
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: filterBarContainer.bottom;
anchors.topMargin: 16;
}
ListModel {
id: purchasesModel;
}
ListModel {
id: previousPurchasesModel;
}
ListModel {
id: filteredPurchasesModel;
}
Rectangle {
id: cantRezCertified;
visible: !root.canRezCertifiedItems;
color: "#FFC3CD";
radius: 4;
border.color: hifi.colors.redAccent;
border.width: 1;
anchors.top: separator.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 80;
HiFiGlyphs {
id: lightningIcon;
text: hifi.glyphs.lightning;
// Size
size: 36;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 18;
anchors.left: parent.left;
anchors.leftMargin: 12;
horizontalAlignment: Text.AlignHCenter;
// Style
color: hifi.colors.lightGray;
}
RalewayRegular {
text: "You don't have permission to rez certified items in this domain. " +
'<b><font color="' + hifi.colors.blueAccent + '"><a href="#">Learn More</a></font></b>';
// Text size
size: 18;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 4;
anchors.left: lightningIcon.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 4;
// Style
color: hifi.colors.baseGray;
wrapMode: Text.WordWrap;
// Alignment
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
lightboxPopup.titleText = "Rez Permission Required";
lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.<br><br>" +
"Use the <b>GOTO app</b> to visit another domain or <b>go to your own sandbox.</b>";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.button2text = "OPEN GOTO";
lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});";
lightboxPopup.visible = true;
}
}
}
ListView {
id: purchasesContentsList;
visible: purchasesModel.count !== 0;
clip: true;
model: filteredPurchasesModel;
// Anchors
anchors.top: filterBarContainer.bottom;
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
delegate: PurchasedItem {
canRezCertifiedItems: root.canRezCertifiedItems;
itemName: title;
itemId: id;
itemPreviewImageUrl: preview;
itemHref: root_file_url;
purchaseStatus: status;
purchaseStatusChanged: statusChanged;
anchors.topMargin: 12;
anchors.bottomMargin: 12;
@ -396,6 +424,24 @@ Rectangle {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
} else if (msg.method === 'purchases_itemCertificateClicked') {
sendToScript(msg);
} else if (msg.method === "showInvalidatedLightbox") {
lightboxPopup.titleText = "Item Invalidated";
lightboxPopup.bodyText = 'Your item is marked "invalidated" because this item has been suspended ' +
"from the Marketplace due to a claim against its author.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
} else if (msg.method === "showPendingLightbox") {
lightboxPopup.titleText = "Item Pending";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' +
"Usually, purchases take about 90 seconds to confirm.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
} else if (msg.method === "setFilterText") {
filterBar.text = msg.filterText;
}
}
}
@ -414,7 +460,7 @@ Rectangle {
// Explanitory text
RalewayRegular {
id: haventPurchasedYet;
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to your <b>Purchases</b>.";
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to My Purchases.";
// Text size
size: 22;
// Anchors
@ -426,7 +472,7 @@ Rectangle {
anchors.rightMargin: 24;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.baseGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
@ -452,42 +498,6 @@ Rectangle {
// PURCHASES CONTENTS END
//
//
// ACTION BUTTONS START
//
Item {
id: actionButtonsContainer;
visible: purchasesContentsContainer.visible;
// Size
width: parent.width;
height: 40;
// Anchors
anchors.left: parent.left;
anchors.bottom: keyboard.top;
anchors.bottomMargin: 8;
// "Back" button
HifiControlsUit.Button {
id: backButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Back"
onClicked: {
sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL});
}
}
}
//
// ACTION BUTTONS END
//
HifiControlsUit.Keyboard {
id: keyboard;
raised: HMD.mounted && filterBar.focus;
@ -499,9 +509,60 @@ Rectangle {
}
}
onVisibleChanged: {
if (!visible) {
inventoryTimer.stop();
}
}
Timer {
id: inventoryTimer;
interval: 90000;
onTriggered: {
if (root.activeView === "purchasesMain" && !root.pendingInventoryReply) {
root.pendingInventoryReply = true;
commerce.inventory();
}
}
}
//
// FUNCTION DEFINITIONS START
//
function buildFilteredPurchasesModel() {
filteredPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed") {
filteredPurchasesModel.insert(0, purchasesModel.get(i));
} else {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
}
}
function checkIfAnyItemStatusChanged() {
var currentPurchasesModelId, currentPurchasesModelEdition, currentPurchasesModelStatus;
var previousPurchasesModelStatus;
for (var i = 0; i < purchasesModel.count; i++) {
currentPurchasesModelId = purchasesModel.get(i).id;
currentPurchasesModelEdition = purchasesModel.get(i).edition_number;
currentPurchasesModelStatus = purchasesModel.get(i).status;
for (var j = 0; j < previousPurchasesModel.count; j++) {
previousPurchasesModelStatus = previousPurchasesModel.get(j).status;
if (currentPurchasesModelId === previousPurchasesModel.get(j).id &&
currentPurchasesModelEdition === previousPurchasesModel.get(j).edition_number &&
currentPurchasesModelStatus !== previousPurchasesModelStatus) {
purchasesModel.setProperty(i, "statusChanged", true);
}
}
}
}
//
// Function Name: fromScript()
//
@ -519,6 +580,17 @@ Rectangle {
switch (message.method) {
case 'updatePurchases':
referrerURL = message.referrerURL;
titleBarContainer.referrerURL = message.referrerURL;
root.canRezCertifiedItems = message.canRezCertifiedItems;
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));

View file

@ -1,8 +1,8 @@
//
// SendMoney.qml
// Help.qml
// qml/hifi/commerce/wallet
//
// SendMoney
// Help
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -12,8 +12,8 @@
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -24,35 +24,44 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath;
Hifi.QmlCommerce {
id: commerce;
onKeyFilePathIfExistsResult: {
keyFilePath = path;
}
}
// "Unavailable"
RalewayRegular {
id: helpText;
text: "Help me!";
Component.onCompleted: {
commerce.getKeyFilePathIfExists();
}
RalewaySemiBold {
id: helpTitleText;
text: "Help Topics";
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: paintedWidth;
height: 30;
// Text size
size: 24;
size: 18;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
HifiControlsUit.Button {
id: clearCachedPassphraseButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: resetButton.top;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
text: "DEBUG: Clear Cached Passphrase";
anchors.top: parent.top;
anchors.left: helpTitleText.right;
anchors.leftMargin: 20;
height: 40;
width: 150;
text: "DBG: Clear Pass";
onClicked: {
commerce.setPassphrase("");
sendSignalToWallet({method: 'passphraseReset'});
@ -62,18 +71,184 @@ Item {
id: resetButton;
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: helpText.bottom;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
text: "DEBUG: Reset Wallet!";
anchors.top: clearCachedPassphraseButton.top;
anchors.left: clearCachedPassphraseButton.right;
height: 40;
width: 150;
text: "DBG: RST Wallet";
onClicked: {
commerce.reset();
sendSignalToWallet({method: 'walletReset'});
}
}
ListModel {
id: helpModel;
ListElement {
isExpanded: false;
question: "What are private keys?"
answer: qsTr("A private key is a secret piece of text that is used to decrypt code.<br><br>In High Fidelity, <b>your private keys are used to decrypt the contents of your Wallet and Purchases.</b>");
}
ListElement {
isExpanded: false;
question: "Where are my private keys stored?"
answer: qsTr('Your private keys are <b>only stored on your hard drive</b> in High Fidelity Interface\'s AppData directory.<br><br><b><font color="#0093C5"><a href="#privateKeyPath">Tap here to open the file path of your hifikey in your file explorer.</a></font></b><br><br> You may backup this file by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. Restore your backup by replacing the file in Interface\'s AppData directory with your backed-up copy.');
}
ListElement {
isExpanded: false;
question: "What happens if I lose my passphrase?"
answer: qsTr("If you lose your passphrase, you will no longer have access to the contents of your Wallet or My Purchases.<br><br><b>Nobody can help you recover your passphrase, including High Fidelity.</b> Please write it down and store it securely.");
}
ListElement {
isExpanded: false;
question: "What is a 'Security Pic'?"
answer: qsTr("Your Security Pic is an encrypted image that you selected during Wallet Setup. <b>It acts as an extra layer of Wallet security.</b><br><br>When you see your Security Pic, you know that your actions and data are securely making use of your private keys.<br><br><b>If you don't see your Security Pic on a page that is asking you for your Wallet passphrase, someone untrustworthy may be trying to gain access to your Wallet.</b><br><br>The Pic is stored on your hard drive inside the same file as your private keys.");
}
ListElement {
isExpanded: false;
question: "My HFC balance isn't what I expect it to be. Why?"
answer: qsTr('High Fidelity Coin (HFC) transactions are backed by a <b>blockchain</b>, which takes time to update. The status of a transaction usually updates within 90 seconds.<br><br><b><font color="#0093C5"><a href="#blockchain">Tap here to learn more about the blockchain.</a></font></b>');
}
ListElement {
isExpanded: false;
question: "My friend purchased my item from the Marketplace, but I still haven't received the money from the sale. Why not?"
answer: qsTr('High Fidelity Coin (HFC) transactions are backed by a <b>blockchain</b>, which takes time to update. The status of a transaction usually updates within 90 seconds, at which point you will receive your money.<br><br><b><font color="#0093C5"><a href="#blockchain">Tap here to learn more about the blockchain.</a></font></b>');
}
ListElement {
isExpanded: false;
question: "Do I get charged money if a transaction fails?"
answer: qsTr("<b>No.</b> Your HFC balance only changes after a transaction is confirmed.");
}
ListElement {
isExpanded: false;
question: "How do I convert HFC to other currencies?"
answer: qsTr("We are still building the tools needed to support a vibrant economy in High Fidelity. <b>There is currently no way to convert HFC to other currencies.</b>");
}
}
ListView {
id: helpListView;
ScrollBar.vertical: ScrollBar {
policy: helpListView.contentHeight > helpListView.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
parent: helpListView.parent;
anchors.top: helpListView.top;
anchors.right: helpListView.right;
anchors.bottom: helpListView.bottom;
width: 20;
}
anchors.top: helpTitleText.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right
clip: true;
model: helpModel;
delegate: Item {
width: parent.width;
height: model.isExpanded ? questionContainer.height + answerContainer.height : questionContainer.height;
HifiControlsUit.Separator {
colorScheme: 1;
visible: index === 0;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
Item {
id: questionContainer;
anchors.top: parent.top;
anchors.left: parent.left;
width: parent.width;
height: questionText.paintedHeight + 50;
RalewaySemiBold {
id: plusMinusButton;
text: model.isExpanded ? "-" : "+";
// Anchors
anchors.top: parent.top;
anchors.topMargin: model.isExpanded ? -9 : 0;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
width: 60;
// Text size
size: 60;
// Style
color: hifi.colors.white;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: questionText;
text: model.question;
size: 18;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: plusMinusButton.right;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 10;
wrapMode: Text.WordWrap;
height: paintedHeight;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
}
MouseArea {
id: securityTabMouseArea;
anchors.fill: parent;
onClicked: {
model.isExpanded = !model.isExpanded;
if (model.isExpanded) {
collapseAllOtherHelpItems(index);
}
}
}
}
Rectangle {
id: answerContainer;
visible: model.isExpanded;
color: Qt.rgba(0, 0, 0, 0.5);
anchors.top: questionContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: answerText.paintedHeight + 50;
RalewayRegular {
id: answerText;
text: model.answer;
size: 18;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 32;
anchors.right: parent.right;
anchors.rightMargin: 32;
wrapMode: Text.WordWrap;
height: paintedHeight;
color: hifi.colors.white;
onLinkActivated: {
if (link === "#privateKeyPath") {
Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')));
} else if (link === "#blockchain") {
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
}
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
}
//
// FUNCTION DEFINITIONS START
//
@ -97,6 +272,14 @@ Item {
}
}
signal sendSignalToWallet(var msg);
function collapseAllOtherHelpItems(thisIndex) {
for (var i = 0; i < helpModel.count; i++) {
if (i !== thisIndex) {
helpModel.setProperty(i, "isExpanded", false);
}
}
}
//
// FUNCTION DEFINITIONS END
//

View file

@ -24,6 +24,12 @@ Item {
HifiConstants { id: hifi; }
id: root;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
}

View file

@ -1,127 +0,0 @@
//
// NotSetUp.qml
// qml/hifi/commerce/wallet
//
// NotSetUp
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
}
//
// TAB CONTENTS START
//
// Text below title bar
RalewaySemiBold {
id: notSetUpText;
text: "Your Wallet Account Has Not Been Set Up";
// Text size
size: 22;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 100;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// Explanitory text
RalewayRegular {
text: "To buy and sell items in High Fidelity Coin (HFC), you first need " +
"to set up your wallet.<br><b>You do not need to submit a credit card or personal information to set up your wallet.</b>";
// Text size
size: 18;
// Anchors
anchors.top: notSetUpText.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 100;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "Set Up" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 150;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Set Up My Wallet";
onClicked: {
sendSignalToWallet({method: 'setUpClicked'});
}
}
//
// TAB CONTENTS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -1,8 +1,8 @@
//
// PassphraseSelectionLightbox.qml
// PassphraseChange.qml
// qml/hifi/commerce/wallet
//
// PassphraseSelectionLightbox
// PassphraseChange
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -20,12 +20,31 @@ import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
// Style
color: hifi.colors.baseGray;
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
//
// SECURE PASSPHRASE SELECTION START
@ -33,60 +52,31 @@ Rectangle {
Item {
id: choosePassphraseContainer;
// Anchors
anchors.fill: parent;
anchors.top: usernameText.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE PASSPHRASE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
// "Change Passphrase" text
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
id: passphraseTitle;
text: "Change Passphrase:";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
size: 18;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
isChangingPassphrase: true;
anchors.top: passphraseTitle.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
@ -96,11 +86,10 @@ Rectangle {
if (msg.method === 'statusResult') {
if (msg.status) {
// Success submitting new passphrase
root.resetSubmitButton();
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changePassphraseSuccess"});
} else {
// Error submitting new passphrase
root.resetSubmitButton();
resetSubmitButton();
passphraseSelection.setErrorText("Backend error");
}
} else {
@ -115,40 +104,37 @@ Rectangle {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
height: 40;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 30;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
width: 150;
text: "Cancel"
onClicked: {
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changePassphraseCancelled"});
}
}
// "Submit" button
HifiControlsUit.Button {
id: passphraseSubmitButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
width: 150;
text: "Submit";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {

View file

@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -26,11 +27,20 @@ Item {
id: root;
z: 998;
property bool keyboardRaised: false;
property string titleBarIcon: "";
property string titleBarText: "";
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
passphraseModalSecurityImage.source = "";
passphraseModalSecurityImage.source = "image://security/securityImage";
}
@ -40,7 +50,7 @@ Item {
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
} else {
root.visible = false;
sendSignalToParent({method: 'authSuccess'});;
}
}
}
@ -66,24 +76,88 @@ Item {
}
}
// Background rectangle
Rectangle {
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
color: "black";
opacity: 0.9;
}
Rectangle {
Item {
id: titleBar;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
// Wallet icon
HiFiGlyphs {
id: titleBarIcon;
text: root.titleBarIcon;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
RalewaySemiBold {
id: titleBarText;
text: root.titleBarText;
anchors.top: parent.top;
anchors.left: titleBarIcon.right;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
size: 20;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
}
Image {
id: titleBarSecurityImage;
source: "";
visible: titleBarSecurityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
Item {
id: passphraseContainer;
anchors.top: titleBar.bottom;
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 250;
color: hifi.colors.baseGray;
RalewaySemiBold {
id: instructionsText;
text: "Enter Wallet Passphrase";
size: 16;
text: "Please Enter Your Passphrase";
size: 24;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
@ -91,17 +165,34 @@ Item {
width: passphraseField.width;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignLeft;
}
// Error text above buttons
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 15;
// Anchors
anchors.bottom: passphraseField.top;
anchors.bottomMargin: 4;
anchors.left: passphraseField.left;
anchors.right: parent.right;
height: 20;
// Style
color: hifi.colors.redHighlight;
}
HifiControlsUit.TextField {
id: passphraseField;
colorScheme: hifi.colorSchemes.dark;
anchors.top: instructionsText.bottom;
anchors.topMargin: 4;
anchors.topMargin: 40;
anchors.left: instructionsText.left;
width: 280;
width: 260;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
@ -133,7 +224,7 @@ Item {
anchors.top: passphraseField.bottom;
anchors.topMargin: 8;
height: 30;
text: "Show passphrase as plain text";
text: "Show passphrase";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
@ -144,16 +235,18 @@ Item {
Item {
id: securityImageContainer;
// Anchors
anchors.top: instructionsText.top;
anchors.top: passphraseField.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 145;
Image {
id: passphraseModalSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
@ -162,54 +255,43 @@ Item {
commerce.getSecurityImage();
}
}
Image {
id: passphraseModalSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphraseModalSecurityImage.width * 0.45;
height: passphraseModalSecurityImage.height * 0.45;
anchors.bottom: passphraseModalSecurityImage.bottom;
Item {
id: iconAndTextContainer;
anchors.left: passphraseModalSecurityImage.left;
anchors.right: passphraseModalSecurityImage.right;
mipmap: true;
opacity: 0.9;
anchors.bottom: parent.bottom;
height: 24;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 30;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: passphraseModalSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// Error text above buttons
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
// Anchors
anchors.bottom: passphrasePopupActionButtonsContainer.top;
anchors.bottomMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
@ -217,29 +299,10 @@ Item {
//
Item {
id: passphrasePopupActionButtonsContainer;
// Size
width: root.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
// "Cancel" button
HifiControlsUit.Button {
id: cancelPassphraseInputButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
}
}
// "Submit" button
HifiControlsUit.Button {
@ -247,16 +310,38 @@ Item {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 20;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
anchors.rightMargin: 16;
width: parent.width/2 -4;
text: "Submit"
onClicked: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
// "Cancel" button
HifiControlsUit.Button {
id: cancelPassphraseInputButton;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: submitPassphraseInputButton.bottom;
anchors.topMargin: 20;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
width: parent.width/2 - 4;
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
}
}
}
}

View file

@ -24,6 +24,8 @@ Item {
HifiConstants { id: hifi; }
id: root;
property bool isChangingPassphrase: false;
property bool isShowingTip: false;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
@ -51,23 +53,62 @@ Item {
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.focus = true;
if (root.isChangingPassphrase) {
currentPassphraseField.focus = true;
} else {
passphraseField.focus = true;
}
sendMessageToLightbox({method: 'disableHmdPreview'});
} else {
sendMessageToLightbox({method: 'maybeEnableHmdPreview'});
}
}
HifiControlsUit.TextField {
id: currentPassphraseField;
colorScheme: hifi.colorSchemes.dark;
visible: root.isChangingPassphrase;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: passphraseField.right;
height: 50;
echoMode: TextInput.Password;
placeholderText: "enter current passphrase";
onFocusChanged: {
if (focus) {
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
} else if (!passphraseFieldAgain.focus) {
sendSignalToWallet({method: 'walletSetup_lowerKeyboard'});
}
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
}
}
onAccepted: {
passphraseField.focus = true;
}
}
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: parent.top;
anchors.topMargin: 30;
colorScheme: hifi.colorSchemes.dark;
anchors.top: root.isChangingPassphrase ? currentPassphraseField.bottom : parent.top;
anchors.topMargin: root.isChangingPassphrase ? 40 : 0;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: 280;
anchors.leftMargin: 20;
width: 285;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
placeholderText: root.isShowingTip ? "" : "enter new passphrase";
onFocusChanged: {
if (focus) {
@ -91,13 +132,14 @@ Item {
}
HifiControlsUit.TextField {
id: passphraseFieldAgain;
colorScheme: hifi.colorSchemes.dark;
anchors.top: passphraseField.bottom;
anchors.topMargin: 10;
anchors.topMargin: root.isChangingPassphrase ? 20 : 40;
anchors.left: passphraseField.left;
anchors.right: passphraseField.right;
height: 50;
echoMode: TextInput.Password;
placeholderText: "re-enter passphrase";
placeholderText: root.isShowingTip ? "" : "re-enter new passphrase";
onFocusChanged: {
if (focus) {
@ -124,16 +166,16 @@ Item {
Item {
id: securityImageContainer;
// Anchors
anchors.top: passphraseField.top;
anchors.top: root.isChangingPassphrase ? currentPassphraseField.top : passphraseField.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.right: parent.right;
anchors.bottom: root.isChangingPassphrase ? passphraseField.bottom : passphraseFieldAgain.bottom;
Image {
id: passphrasePageSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
@ -142,123 +184,125 @@ Item {
commerce.getSecurityImage();
}
}
Image {
id: topSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphrasePageSecurityImage.width * 0.45;
height: passphrasePageSecurityImage.height * 0.45;
anchors.bottom: passphrasePageSecurityImage.bottom;
Item {
id: iconAndTextContainer;
anchors.left: passphrasePageSecurityImage.left;
anchors.right: passphrasePageSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: passphrasePageSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
anchors.bottom: parent.bottom;
height: 22;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 35;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
}
// Error text below TextFields
// Error text above TextFields
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
size: 15;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 0;
anchors.bottom: passphraseField.top;
anchors.bottomMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.leftMargin: 20;
anchors.right: securityImageContainer.left;
anchors.rightMargin: 4;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below TextFields
RalewaySemiBold {
id: passwordReqs;
text: "Passphrase must be at least 3 characters";
// Text size
size: 16;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Show passphrase text
HifiControlsUit.CheckBox {
id: showPassphrase;
visible: !root.isShowingTip;
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.top: passwordReqs.bottom;
anchors.leftMargin: 20;
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 16;
height: 30;
text: "Show passphrase as plain text";
text: "Show passphrase";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
passphraseFieldAgain.echoMode = checked ? TextInput.Normal : TextInput.Password;
if (root.isChangingPassphrase) {
currentPassphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
}
}
}
// Text below checkbox
RalewayRegular {
text: "Your passphrase is used to encrypt your private keys. <b>Please write it down.</b> If it is lost, you will not be able to recover it.";
visible: !root.isShowingTip;
text: "Your passphrase is used to encrypt your private keys. Only you have it.<br><br>Please write it down.<br><br><b>If it is lost, you will not be able to recover it.</b>";
// Text size
size: 16;
size: 18;
// Anchors
anchors.top: showPassphrase.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.leftMargin: 24;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.rightMargin: 24;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
function validateAndSubmitPassphrase() {
if (passphraseField.text.length < 3) {
setErrorText("Passphrase too short.");
setErrorText("Passphrase must be at least 3 characters.");
passphraseField.error = true;
passphraseFieldAgain.error = true;
currentPassphraseField.error = true;
return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) {
setErrorText("Passphrases don't match.");
passphraseField.error = true;
passphraseFieldAgain.error = true;
currentPassphraseField.error = true;
return false;
} else {
passphraseField.error = false;
passphraseFieldAgain.error = false;
currentPassphraseField.error = false;
setErrorText("");
commerce.setPassphrase(passphraseField.text);
return true;
@ -270,6 +314,7 @@ Item {
}
function clearPassphraseFields() {
currentPassphraseField.text = "";
passphraseField.text = "";
passphraseFieldAgain.text = "";
setErrorText("");

View file

@ -25,29 +25,16 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath: "";
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) { // "If security image is set up"
var path = "image://security/securityImage";
topSecurityImage.source = "";
topSecurityImage.source = path;
changeSecurityImageImage.source = "";
changeSecurityImageImage.source = path;
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
keyFilePath = path;
}
}
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;
@ -55,253 +42,258 @@ Item {
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.left: parent.left;
anchors.right: securityImageContainer.left;
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
Image {
id: topSecurityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
}
Image {
id: topSecurityImageMask;
source: "images/lockOverlay.png";
width: topSecurityImage.width * 0.45;
height: topSecurityImage.height * 0.45;
anchors.bottom: topSecurityImage.bottom;
anchors.right: topSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: topSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
Item {
id: securityContainer;
anchors.top: securityImageContainer.bottom;
anchors.top: usernameText.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
anchors.bottom: parent.bottom;
RalewayRegular {
RalewaySemiBold {
id: securityText;
text: "Security";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
size: 18;
// Style
color: hifi.colors.blueHighlight;
}
Rectangle {
id: securityTextSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: securityText.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changePassphraseContainer;
anchors.top: securityText.bottom;
anchors.topMargin: 16;
anchors.top: securityTextSeparator.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
Image {
HiFiGlyphs {
id: changePassphraseImage;
text: hifi.glyphs.passphrase;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
height: parent.height;
width: height;
source: "images/lockOverlay.png";
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
visible: false;
// Style
color: hifi.colors.white;
}
ColorOverlay {
anchors.fill: changePassphraseImage;
source: changePassphraseImage;
color: "white"
RalewaySemiBold {
text: "Passphrase";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: changePassphraseImage.right;
anchors.leftMargin: 30;
width: 50;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Passphrase" button
HifiControlsUit.Button {
id: changePassphraseButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changePassphraseImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Passphrase";
width: 140;
height: 40;
text: "Change";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changePassphrase'});
}
}
}
Item {
id: changeSecurityImageContainer;
anchors.top: changePassphraseContainer.bottom;
anchors.topMargin: 8;
Rectangle {
id: changePassphraseSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: changePassphraseContainer.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changeSecurityImageContainer;
anchors.top: changePassphraseSeparator.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
Image {
HiFiGlyphs {
id: changeSecurityImageImage;
text: hifi.glyphs.securityImage;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
height: parent.height;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
// Style
color: hifi.colors.white;
}
// "Change Security Image" button
RalewaySemiBold {
text: "Security Pic";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 30;
width: 50;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Passphrase" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Security Image";
width: 140;
height: 40;
text: "Change";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
}
}
}
}
Item {
id: yourPrivateKeysContainer;
anchors.top: securityContainer.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
RalewaySemiBold {
id: yourPrivateKeysText;
text: "Your Private Keys";
Rectangle {
id: privateKeysSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
anchors.top: changeSecurityImageContainer.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
// Text below "your private keys"
RalewayRegular {
id: explanitoryText;
text: "Your money and purchases are secured with private keys that only you " +
"have access to. <b>If they are lost, you will not be able to access your money or purchases. " +
"To safeguard your private keys, back up this file regularly:</b>";
// Text size
size: 18;
// Anchors
anchors.top: yourPrivateKeysText.bottom;
anchors.topMargin: 10;
Item {
id: yourPrivateKeysContainer;
anchors.top: privateKeysSeparator.bottom;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanitoryText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
anchors.rightMargin: 55;
anchors.bottom: parent.bottom;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
id: yourPrivateKeysImage;
text: hifi.glyphs.walletKey;
// Size
size: parent.height*1.3;
size: 80;
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.topMargin: 20;
anchors.left: parent.left;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
color: hifi.colors.white;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
RalewaySemiBold {
id: yourPrivateKeysText;
text: "Private Keys";
size: 18;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 32;
anchors.left: yourPrivateKeysImage.right;
anchors.leftMargin: 30;
anchors.right: parent.right;
height: 30;
// Style
color: hifi.colors.white;
}
// Text below "private keys"
RalewayRegular {
id: explanitoryText;
text: "Your money and purchases are secured with private keys that only you have access to.";
// Text size
size: 18;
// Anchors
anchors.top: yourPrivateKeysText.bottom;
anchors.topMargin: 10;
anchors.left: yourPrivateKeysText.left;
anchors.right: yourPrivateKeysText.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.Button {
id: backupInstructionsButton;
text: "View Backup Instructions";
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.left: explanitoryText.left;
anchors.right: explanitoryText.right;
anchors.top: explanitoryText.bottom;
anchors.topMargin: 16;
height: 40;
onClicked: {
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
}

View file

@ -1,8 +1,8 @@
//
// SecurityImageSelectionLightbox.qml
// SecurityImageChange.qml
// qml/hifi/commerce/wallet
//
// SecurityImageSelectionLightbox
// SecurityImageChange
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -20,22 +20,26 @@ import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
property bool justSubmitted: false;
// Style
color: hifi.colors.baseGray;
SecurityImageModel {
id: securityImageModel;
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
securityImageChangePageSecurityImage.source = "";
securityImageChangePageSecurityImage.source = "image://security/securityImage";
if (exists) { // Success submitting new security image
if (root.justSubmitted) {
root.resetSubmitButton();
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"});
root.justSubmitted = false;
}
} else if (root.justSubmitted) {
@ -45,71 +49,104 @@ Rectangle {
}
}
}
// Security Image
Item {
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.rightMargin: 25;
width: 130;
height: 150;
Image {
id: securityImageChangePageSecurityImage;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
}
}
Item {
id: iconAndTextContainer;
anchors.left: securityImageChangePageSecurityImage.left;
anchors.right: securityImageChangePageSecurityImage.right;
anchors.bottom: parent.bottom;
height: 22;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 10;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
}
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.topMargin: 135;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE SECURITY IMAGE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
// "Change Security Image" text
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
id: securityImageTitle;
text: "Change Security Pic:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
size: 18;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
height: 300;
Connections {
onSendSignalToWallet: {
@ -118,66 +155,43 @@ Rectangle {
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
id: securityImageNavBar;
// Size
width: parent.width;
height: 100;
height: 40;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 30;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
width: 150;
text: "Cancel"
onClicked: {
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changeSecurityImageCancelled"});
}
}
// "Submit" button
HifiControlsUit.Button {
id: securityImageSubmitButton;
color: hifi.buttons.black;
enabled: securityImageSelection.currentIndex !== -1;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
width: 150;
text: "Submit";
onClicked: {
root.justSubmitted = true;

View file

@ -24,6 +24,7 @@ Item {
HifiConstants { id: hifi; }
id: root;
property int currentIndex: securityImageGrid.currentIndex;
// This will cause a bug -- if you bring up security image selection in HUD mode while
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
@ -43,6 +44,7 @@ Item {
GridView {
id: securityImageGrid;
interactive: false;
clip: true;
// Anchors
anchors.fill: parent;
@ -56,8 +58,8 @@ Item {
Item {
anchors.fill: parent;
Image {
width: parent.width - 8;
height: parent.height - 8;
width: parent.width - 12;
height: parent.height - 12;
source: sourcePath;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;

View file

@ -13,10 +13,12 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -28,18 +30,14 @@ Rectangle {
property string activeView: "initialize";
property bool keyboardRaised: false;
// Style
color: hifi.colors.baseGray;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
@ -49,25 +47,35 @@ Rectangle {
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
if (path === "" && root.activeView !== "walletSetup") {
root.activeView = "walletSetup";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
}
onSecurityImageResult: {
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
if (!exists && root.activeView !== "walletSetup") { // "If security image is not set up"
root.activeView = "walletSetup";
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && passphraseModal && !passphraseModal.visible) {
passphraseModal.visible = true;
if (!isAuthenticated && passphraseModal && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
root.activeView = "walletHome";
}
@ -78,74 +86,11 @@ Rectangle {
id: securityImageModel;
}
Rectangle {
id: walletSetupLightboxContainer;
visible: walletSetupLightbox.visible || passphraseSelectionLightbox.visible || securityImageSelectionLightbox.visible;
z: 998;
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
color: "black";
opacity: 0.5;
}
WalletSetupLightbox {
id: walletSetupLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') {
root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
PassphraseSelectionLightbox {
id: passphraseSelectionLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
SecurityImageSelectionLightbox {
id: securityImageSelectionLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
//
// TITLE BAR START
@ -160,6 +105,20 @@ Rectangle {
anchors.left: parent.left;
anchors.top: parent.top;
// Wallet icon
HiFiGlyphs {
id: walletIcon;
text: hifi.glyphs.wallet;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
// Title Bar text
RalewaySemiBold {
id: titleBarText;
@ -168,28 +127,119 @@ Rectangle {
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.left: walletIcon.right;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
Image {
id: titleBarSecurityImage;
source: "";
visible: titleBarSecurityImage.source !== "" && !securityImageChange.visible;
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
//
// TITLE BAR END
//
WalletSetup {
id: walletSetup;
visible: root.activeView === "walletSetup";
z: 998;
anchors.fill: parent;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') {
root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'});
}
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
PassphraseChange {
id: passphraseChange;
visible: root.activeView === "passphraseChange";
z: 998;
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
SecurityImageChange {
id: securityImageChange;
visible: root.activeView === "securityImageChange";
z: 998;
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changeSecurityImageCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
//
// TAB CONTENTS START
//
@ -231,31 +281,17 @@ Rectangle {
PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
visible: root.activeView === "passphraseModal";
anchors.fill: parent;
titleBarText: "Wallet";
titleBarIcon: hifi.glyphs.wallet;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
NotSetUp {
id: notSetUp;
visible: root.activeView === "notSetUp";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToWallet: {
if (msg.method === 'setUpClicked') {
walletSetupLightbox.visible = true;
if (msg.method === "authSuccess") {
root.activeView = "walletHome";
} else {
sendToScript(msg);
}
}
}
@ -265,47 +301,42 @@ Rectangle {
id: walletHome;
visible: root.activeView === "walletHome";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
SendMoney {
id: sendMoney;
visible: root.activeView === "sendMoney";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
Security {
id: security;
visible: root.activeView === "security";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changePassphrase') {
passphraseSelectionLightbox.visible = true;
passphraseSelectionLightbox.clearPassphraseFields();
root.activeView = "passphraseChange";
passphraseChange.clearPassphraseFields();
passphraseChange.resetSubmitButton();
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageSelectionLightbox.visible = true;
root.activeView = "securityImageChange";
}
}
}
@ -315,13 +346,9 @@ Rectangle {
id: help;
visible: root.activeView === "help";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
@ -342,11 +369,11 @@ Rectangle {
//
Item {
id: tabButtonsContainer;
visible: !needsLogIn.visible;
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange";
property int numTabs: 5;
// Size
width: root.width;
height: 80;
height: 90;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
@ -361,30 +388,46 @@ Rectangle {
// "WALLET HOME" tab button
Rectangle {
id: walletHomeButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: homeTabIcon;
text: hifi.glyphs.home2;
// Size
size: 50;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "WALLET HOME";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: walletHomeTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -396,87 +439,136 @@ Rectangle {
}
}
// "SEND MONEY" tab button
// "EXCHANGE MONEY" tab button
Rectangle {
id: sendMoneyButtonContainer;
visible: !notSetUp.visible;
id: exchangeMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: walletHomeButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "SEND MONEY";
// Text size
size: 14;
HiFiGlyphs {
id: exchangeMoneyTabIcon;
text: hifi.glyphs.leftRightArrows;
// Size
size: 50;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// "EXCHANGE MONEY" tab button
Rectangle {
id: exchangeMoneyButtonContainer;
visible: !notSetUp.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "EXCHANGE MONEY";
// Text size
size: 14;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
}
// "SEND MONEY" tab button
Rectangle {
id: sendMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: sendMoneyTabIcon;
text: hifi.glyphs.paperPlane;
// Size
size: 46;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
}
RalewaySemiBold {
text: "SEND MONEY";
// Text size
size: 16;
// Anchors
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
}
// "SECURITY" tab button
Rectangle {
id: securityButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: securityTabIcon;
text: hifi.glyphs.lock;
// Size
size: 38;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: 2;
// Style
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "SECURITY";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: securityTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -487,34 +579,50 @@ Rectangle {
onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
// "HELP" tab button
Rectangle {
id: helpButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: securityButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: helpTabIcon;
text: hifi.glyphs.question;
// Size
size: 55;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -6;
// Style
color: root.activeView === "help" || helpTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "HELP";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "help" || helpTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: helpTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -526,6 +634,7 @@ Rectangle {
}
}
function resetTabButtonColors() {
walletHomeButtonContainer.color = hifi.colors.black;
sendMoneyButtonContainer.color = hifi.colors.black;
@ -604,6 +713,9 @@ Rectangle {
//
function fromScript(message) {
switch (message.method) {
case 'updateWalletReferrer':
walletSetup.referrer = message.referrer;
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}

View file

@ -13,7 +13,8 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import QtQuick.Controls 2.2
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -29,14 +30,6 @@ Item {
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
onBalanceResult : {
balanceText.text = result.data.balance;
}
@ -50,10 +43,6 @@ Item {
}
}
SecurityImageModel {
id: securityImageModel;
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
@ -68,213 +57,195 @@ Item {
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: hfcBalanceContainer.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
// HFC Balance Container
Item {
id: hfcBalanceContainer;
// Anchors
anchors.top: securityImageContainer.top;
anchors.right: securityImageContainer.left;
anchors.rightMargin: 16;
width: 175;
height: 60;
Rectangle {
id: hfcBalanceField;
color: hifi.colors.darkGray;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
height: parent.height - 15;
// "HFC" balance label
FiraSansRegular {
id: balanceLabel;
text: "HFC";
// Text size
size: 20;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: hfcBalanceField.right;
anchors.rightMargin: 4;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
historyReceived = false;
commerce.balance();
commerce.history();
}
}
}
// Balance Text
FiraSansSemiBold {
id: balanceText;
text: "--";
// Text size
size: 28;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: balanceLabel.left;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
}
}
// "balance" text above field
RalewayRegular {
text: "balance";
// Text size
size: 12;
// Anchors
anchors.top: parent.top;
anchors.bottom: hfcBalanceField.top;
anchors.bottomMargin: 4;
anchors.left: hfcBalanceField.left;
anchors.right: hfcBalanceField.right;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
// "HFC" balance label
HiFiGlyphs {
id: balanceLabel;
text: hifi.glyphs.hfc;
// Size
size: 40;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: hifi.colors.white;
}
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
// Balance Text
FiraSansRegular {
id: balanceText;
text: "--";
// Text size
size: 28;
// Anchors
anchors.top: balanceLabel.top;
anchors.bottom: balanceLabel.bottom;
anchors.left: balanceLabel.right;
anchors.leftMargin: 10;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.white;
// Alignment
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
historyReceived = false;
commerce.balance();
commerce.history();
}
}
}
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
Image {
id: securityImageOverlay;
source: "images/lockOverlay.png";
width: securityImage.width * 0.45;
height: securityImage.height * 0.45;
anchors.bottom: securityImage.bottom;
anchors.right: securityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
// "balance" text below field
RalewayRegular {
text: "security image";
text: "BALANCE (HFC)";
// Text size
size: 12;
size: 14;
// Anchors
anchors.top: securityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
anchors.top: balanceLabel.top;
anchors.topMargin: balanceText.paintedHeight + 20;
anchors.bottom: balanceLabel.bottom;
anchors.left: balanceText.left;
anchors.right: balanceText.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.white;
}
}
// Recent Activity
Item {
Rectangle {
id: recentActivityContainer;
anchors.top: securityImageContainer.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
height: 440;
LinearGradient {
anchors.fill: parent;
start: Qt.point(0, 0);
end: Qt.point(0, height);
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.white }
GradientStop { position: 1.0; color: hifi.colors.faintGray }
}
}
RalewayRegular {
RalewaySemiBold {
id: recentActivityText;
text: "Recent Activity";
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.baseGrayHighlight;
}
ListModel {
id: transactionHistoryModel;
}
Rectangle {
Item {
anchors.top: recentActivityText.bottom;
anchors.topMargin: 4;
anchors.topMargin: 26;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
color: "white";
anchors.rightMargin: 24;
ListView {
id: transactionHistory;
ScrollBar.vertical: ScrollBar {
policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
parent: transactionHistory.parent;
anchors.top: transactionHistory.top;
anchors.left: transactionHistory.right;
anchors.leftMargin: 4;
anchors.bottom: transactionHistory.bottom;
width: 20;
}
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height - 12;
height: parent.height;
visible: transactionHistoryModel.count !== 0;
clip: true;
model: transactionHistoryModel;
delegate: Item {
width: parent.width;
height: transactionText.height + 30;
FiraSansRegular {
id: transactionText;
text: model.text;
HifiControlsUit.Separator {
visible: index === 0;
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
AnonymousProRegular {
id: dateText;
text: getFormattedDate(model.created_at * 1000);
// Style
size: 18;
width: parent.width;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
anchors.verticalCenter: parent.verticalCenter;
color: "black";
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignRight;
}
AnonymousProRegular {
id: transactionText;
text: model.text;
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: hifi.colors.baseGrayHighlight;
wrapMode: Text.WordWrap;
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
}
}
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
@ -304,6 +275,30 @@ Item {
//
// FUNCTION DEFINITIONS START
//
function getFormattedDate(timestamp) {
var a = new Date(timestamp);
var year = a.getFullYear();
var month = a.getMonth();
var day = a.getDate();
var hour = a.getHours();
var drawnHour = hour;
if (hour === 0) {
drawnHour = 12;
} else if (hour > 12) {
drawnHour -= 12;
}
var amOrPm = "AM";
if (hour >= 12) {
amOrPm = "PM";
}
var min = a.getMinutes();
var sec = a.getSeconds();
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
}
//
// Function Name: fromScript()
//

View file

@ -0,0 +1,746 @@
//
// modalContainer.qml
// qml/hifi/commerce/wallet
//
// modalContainer
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
property string activeView: "step_1";
property string lastPage;
property bool hasShownSecurityImageTip: false;
property string referrer;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!exists && root.lastPage === "step_2") {
// ERROR! Invalid security image.
root.activeView = "step_2";
} else {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (isAuthenticated) {
root.activeView = "step_4";
} else {
root.activeView = "step_3";
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
}
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
anchors.right: parent.right;
// Wallet icon
HiFiGlyphs {
id: walletIcon;
text: hifi.glyphs.wallet;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
// Title Bar text
RalewayRegular {
id: titleBarText;
text: "Wallet Setup" + (securityImageTip.visible ? "" : " - Step " + root.activeView.split("_")[1] + " of 4");
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: walletIcon.right;
anchors.leftMargin: 8;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
Image {
id: titleBarSecurityImage;
source: "";
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
//
// TITLE BAR END
//
//
// FIRST PAGE START
//
Item {
id: firstPageContainer;
visible: root.activeView === "step_1";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
HiFiGlyphs {
id: bigWalletIcon;
text: hifi.glyphs.wallet;
// Size
size: 180;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.horizontalCenter: parent.horizontalCenter;
// Style
color: hifi.colors.white;
}
RalewayRegular {
id: firstPage_text01;
text: "Let's set up your wallet!";
// Text size
size: 26;
// Anchors
anchors.top: bigWalletIcon.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
width: paintedWidth;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: firstPage_text02;
text: "Set up your wallet to claim your <b>free High Fidelity Coin (HFC)</b> and get items from the Marketplace.<br><br>" +
"<b>No credit card is required.</b>";
// Text size
size: 18;
// Anchors
anchors.top: firstPage_text01.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 65;
anchors.right: parent.right;
anchors.rightMargin: 65;
height: paintedHeight;
width: paintedWidth;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "Set Up" button
HifiControlsUit.Button {
id: firstPage_setUpButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: firstPage_text02.bottom;
anchors.topMargin: 40;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Set Up Wallet";
onClicked: {
root.activeView = "step_2";
}
}
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.none;
colorScheme: hifi.colorSchemes.dark;
anchors.top: firstPage_setUpButton.bottom;
anchors.topMargin: 20;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Cancel";
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
}
//
// FIRST PAGE END
//
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
visible: root.activeView === "step_2";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Text below title bar
RalewayRegular {
id: securityImageTitleHelper;
text: "Choose a Security Pic:";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 300;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 200;
text: "Back"
onClicked: {
root.activeView = "step_1";
}
}
// "Next" button
HifiControlsUit.Button {
enabled: securityImageSelection.currentIndex !== -1;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Next";
onClicked: {
root.lastPage = "step_2";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
root.activeView = "step_3";
passphraseSelection.clearPassphraseFields();
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: securityImageTip;
visible: false;
z: 999;
anchors.fill: root;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Image {
source: "images/wallet-tip-bg.png";
anchors.fill: parent;
}
RalewayRegular {
id: tipText;
text: '<font size="5">Tip:</font><br><br>When you see your security picture like this, you know ' +
"the page asking for your passphrase is legitimate.";
// Text size
size: 18;
// Anchors
anchors.bottom: parent.bottom;
anchors.bottomMargin: 230;
anchors.left: parent.left;
anchors.leftMargin: 60;
height: paintedHeight;
width: 210;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
}
// "Got It" button
HifiControlsUit.Button {
id: tipGotItButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: tipText.bottom;
anchors.topMargin: 20;
anchors.horizontalCenter: tipText.horizontalCenter;
height: 50;
width: 150;
text: "Got It";
onClicked: {
root.hasShownSecurityImageTip = true;
securityImageTip.visible = false;
}
}
}
Item {
id: choosePassphraseContainer;
visible: root.activeView === "step_3";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
onVisibleChanged: {
if (visible) {
commerce.getWalletAuthenticatedStatus();
if (!root.hasShownSecurityImageTip) {
securityImageTip.visible = true;
}
}
}
// Text below title bar
RalewayRegular {
id: passphraseTitleHelper;
text: "Set Your Passphrase:";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
isShowingTip: securityImageTip.visible;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
Connections {
onSendMessageToLightbox: {
if (msg.method === 'statusResult') {
} else {
sendSignalToWallet(msg);
}
}
}
}
// Navigation Bar
Item {
id: passphraseNavBar;
visible: !securityImageTip.visible;
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 200;
text: "Back"
onClicked: {
root.lastPage = "step_3";
root.activeView = "step_2";
}
}
// "Next" button
HifiControlsUit.Button {
id: passphrasePageNextButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Next";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "step_3";
commerce.generateKeyPair();
root.activeView = "step_4";
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
//
// PRIVATE KEYS READY START
//
Item {
id: privateKeysReadyContainer;
visible: root.activeView === "step_4";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
// Text below title bar
RalewayRegular {
id: keysReadyTitleHelper;
text: "Back Up Your Private Keys";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: explanationText;
text: "To protect your privacy, you control your private keys. High Fidelity has no access to your private keys and cannot " +
"recover them for you.<br><br><b>If they are lost, you will not be able to access your money or purchases.</b>";
// Text size
size: 20;
// Anchors
anchors.top: keysReadyTitleHelper.bottom;
anchors.topMargin: 24;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
Rectangle {
id: pathAndInstructionsContainer;
anchors.top: explanationText.bottom;
anchors.topMargin: 24;
anchors.left: parent.left;
anchors.right: parent.right;
height: 240;
color: Qt.rgba(0, 0, 0, 0.5);
Item {
id: instructions01Container;
anchors.fill: parent;
RalewaySemiBold {
id: keyFilePathText;
text: "Private Key File Location:";
size: 18;
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.white;
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: keyFilePathText.bottom;
anchors.topMargin: 8;
height: 24;
width: height;
HiFiGlyphs {
text: hifi.glyphs.folderLg;
// Size
size: parent.height;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.blueHighlight : hifi.colors.faintGray;
}
onClicked: {
Qt.openUrlExternally("file:///" + keyFilePath.text.substring(0, keyFilePath.text.lastIndexOf('/')));
}
}
RalewayRegular {
id: keyFilePath;
size: 18;
anchors.top: clipboardButton.top;
anchors.left: clipboardButton.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
wrapMode: Text.WordWrap;
color: hifi.colors.blueHighlight;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
// "Open Instructions" button
HifiControlsUit.Button {
id: openInstructionsButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: keyFilePath.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 40;
text: "Open Instructions for Later";
onClicked: {
instructions01Container.visible = false;
instructions02Container.visible = true;
keysReadyPageFinishButton.visible = true;
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
Item {
id: instructions02Container;
anchors.fill: parent;
visible: false;
RalewayRegular {
text: "All set!<br>Instructions for backing up your keys have been opened on your desktop. " +
"Be sure to look them over after your session.";
size: 22;
anchors.fill: parent;
anchors.leftMargin: 30;
anchors.rightMargin: 30;
wrapMode: Text.WordWrap;
color: hifi.colors.white;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Finish" button
HifiControlsUit.Button {
id: keysReadyPageFinishButton;
visible: false;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Finish";
onClicked: {
root.visible = false;
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
}
}
}
}
//
// PRIVATE KEYS READY END
//
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -1,502 +0,0 @@
//
// WalletSetupLightbox.qml
// qml/hifi/commerce/wallet
//
// WalletSetupLightbox
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string lastPage: "initialize";
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!exists && root.lastPage === "securityImage") {
// ERROR! Invalid security image.
securityImageContainer.visible = true;
choosePassphraseContainer.visible = false;
}
}
onWalletAuthenticatedStatusResult: {
securityImageContainer.visible = false;
if (isAuthenticated) {
privateKeysReadyContainer.visible = true;
} else {
choosePassphraseContainer.visible = true;
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
}
}
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
// Anchors
anchors.fill: parent;
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 1 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
// "Next" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
root.lastPage = "securityImage";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
securityImageContainer.visible = false;
choosePassphraseContainer.visible = true;
passphraseSelection.clearPassphraseFields();
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: choosePassphraseContainer;
visible: false;
// Anchors
anchors.fill: parent;
onVisibleChanged: {
if (visible) {
commerce.getWalletAuthenticatedStatus();
}
}
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 2 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
Connections {
onSendMessageToLightbox: {
if (msg.method === 'statusResult') {
} else {
sendSignalToWallet(msg);
}
}
}
}
// Navigation Bar
Item {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Back"
onClicked: {
root.lastPage = "choosePassphrase";
choosePassphraseContainer.visible = false;
securityImageContainer.visible = true;
}
}
// "Next" button
HifiControlsUit.Button {
id: passphrasePageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "choosePassphrase";
commerce.generateKeyPair();
choosePassphraseContainer.visible = false;
privateKeysReadyContainer.visible = true;
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
//
// PRIVATE KEYS READY START
//
Item {
id: privateKeysReadyContainer;
visible: false;
// Anchors
anchors.fill: parent;
Item {
id: keysReadyTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 3 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: keysReadyTitleHelper;
text: "Your Private Keys are Ready";
// Text size
size: 24;
// Anchors
anchors.top: keysReadyTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below checkbox
RalewayRegular {
id: explanationText;
text: "Your money and purchases are secured with private keys that only you have access to. " +
"<b>If they are lost, you will not be able to access your money or purchases.</b><br><br>" +
"<b>To protect your privacy, High Fidelity has no access to your private keys and cannot " +
"recover them for any reason.<br><br>To safeguard your private keys, backup this file on a regular basis:</b>";
// Text size
size: 16;
// Anchors
anchors.top: keysReadyTitleHelper.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanationText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
// Size
size: parent.height*1.3;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Next" button
HifiControlsUit.Button {
id: keysReadyPageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Finish";
onClicked: {
root.visible = false;
sendSignalToWallet({method: 'walletSetup_finished'});
}
}
}
}
//
// PRIVATE KEYS READY END
//
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -21,9 +21,9 @@ Item {
property alias text: label.text
property var source
implicitHeight: source.visible ? 2 * label.implicitHeight : 0
implicitHeight: source !== null ? source.visible ? 2 * label.implicitHeight : 0 : 0
implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width
visible: source.visible
visible: source !== null ? source.visible : false
width: parent.width
Item {
@ -42,7 +42,9 @@ Item {
id: checkbox
// FIXME: Should use radio buttons if source.exclusiveGroup.
width: 20
visible: source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup :
false
checked: setChecked()
function setChecked() {
if (!source || source.type !== 1 || !source.checkable) {
@ -58,7 +60,9 @@ Item {
id: radiobutton
// FIXME: Should use radio buttons if source.exclusiveGroup.
width: 20
visible: source.visible && source.type === 1 && source.checkable && source.exclusiveGroup
visible: source !== null ?
source.visible && source.type === 1 && source.checkable && source.exclusiveGroup :
false
checked: setChecked()
function setChecked() {
if (!source || source.type !== 1 || !source.checkable) {
@ -80,9 +84,13 @@ Item {
anchors.left: check.right
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
visible: source.visible
color: source !== null ?
source.enabled ? hifi.colors.baseGrayShadow :
hifi.colors.baseGrayShadow50 :
"transparent"
enabled: source !== null ? source.visible && (source.type !== 0 ? source.enabled : false) : false
visible: source !== null ? source.visible : false
wrapMode: Text.WordWrap
}
@ -93,7 +101,7 @@ Item {
leftMargin: hifi.dimensions.menuPadding.x + check.width
rightMargin: hifi.dimensions.menuPadding.x + tail.width
}
visible: source.type === MenuItemType.Separator
visible: source !== null ? source.type === MenuItemType.Separator : false
Rectangle {
anchors {
@ -117,23 +125,23 @@ Item {
RalewayLight {
id: shortcut
text: source.shortcut ? source.shortcut : ""
text: source !== null ? source.shortcut ? source.shortcut : "" : ""
size: hifi.fontSizes.shortcutText
color: hifi.colors.baseGrayShadow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 15
visible: source.visible && text != ""
visible: source !== null ? source.visible && text != "" : false
}
HiFiGlyphs {
text: hifi.glyphs.disclosureExpand
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25
color: source !== null ? source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25 : "transparent"
size: 70
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
horizontalAlignment: Text.AlignRight
visible: source.visible && (source.type === 2)
visible: source !== null ? source.visible && (source.type === 2) : false
}
}
}

View file

@ -70,7 +70,6 @@ Item {
for (var i = 0; i < items.length; ++i) {
var item = items[i];
if (!item.visible) continue;
switch (item.type) {
case MenuItemType.Menu:
result.append({"name": item.title, "item": item})
@ -216,5 +215,4 @@ Item {
function nextItem() { d.topMenu.nextItem(); }
function selectCurrentItem() { d.topMenu.selectCurrentItem(); }
function previousPage() { d.topMenu.previousPage(); }
}

View file

@ -38,7 +38,6 @@ FocusScope {
//color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
}
ListView {
id: listView
x: 0
@ -70,8 +69,8 @@ FocusScope {
delegate: TabletMenuItem {
text: name
source: item
onImplicitHeightChanged: listView.recalcSize()
onImplicitWidthChanged: listView.recalcSize()
onImplicitHeightChanged: listView !== null ? listView.recalcSize() : 0
onImplicitWidthChanged: listView !== null ? listView.recalcSize() : 0
MouseArea {
anchors.fill: parent
@ -130,8 +129,6 @@ FocusScope {
function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
function selectCurrentItem() { if (listView.currentIndex != -1) root.selected(currentItem.source); }
function previousPage() { root.parent.pop(); }
}

Some files were not shown because too many files have changed in this diff Show more