Merge branch 'master' into otherAvatarMotionStates01
21
.gitignore
vendored
|
@ -17,10 +17,11 @@ Makefile
|
|||
local.properties
|
||||
android/gradle*
|
||||
android/.gradle
|
||||
android/app/src/main/jniLibs
|
||||
android/app/libs
|
||||
android/app/src/main/res/values/libs.xml
|
||||
android/app/src/main/assets/bundled
|
||||
android/**/src/main/jniLibs
|
||||
android/**/libs
|
||||
android/**/src/main/res/values/libs.xml
|
||||
android/**/src/main/assets
|
||||
android/**/gradle*
|
||||
|
||||
# VSCode
|
||||
# List taken from Github Global Ignores master@435c4d92
|
||||
|
@ -83,9 +84,6 @@ npm-debug.log
|
|||
# Android studio files
|
||||
*___jb_old___
|
||||
|
||||
# Generated assets for Android
|
||||
android/app/src/main/assets
|
||||
|
||||
# Resource binary file
|
||||
interface/compiledResources
|
||||
|
||||
|
@ -94,3 +92,12 @@ interface/resources/GPUCache/*
|
|||
|
||||
# package lock file for JSDoc tool
|
||||
tools/jsdoc/package-lock.json
|
||||
|
||||
# Python compile artifacts
|
||||
**/__pycache__
|
||||
|
||||
# ignore unneeded unity project files for avatar exporter
|
||||
tools/unity-avatar-exporter/Library
|
||||
tools/unity-avatar-exporter/Packages
|
||||
tools/unity-avatar-exporter/ProjectSettings
|
||||
tools/unity-avatar-exporter/Temp
|
||||
|
|
20
BUILD.md
|
@ -13,7 +13,7 @@
|
|||
|
||||
### CMake External Project Dependencies
|
||||
|
||||
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
|
||||
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
|
||||
- [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83
|
||||
- [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8
|
||||
- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac)
|
||||
|
@ -22,7 +22,7 @@ These dependencies need not be installed manually. They are automatically downlo
|
|||
- [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3
|
||||
- [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3
|
||||
- [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3
|
||||
- [vcpkg](https://github.com/highfidelity/vcpkg):
|
||||
- [vcpkg](https://github.com/highfidelity/vcpkg):
|
||||
- [VHACD](https://github.com/virneo/v-hacd)
|
||||
- [zlib](http://www.zlib.net/): 1.28 (Win32 only)
|
||||
- [nvtt](https://github.com/highfidelity/nvidia-texture-tools): 2.1.1 (customized)
|
||||
|
@ -48,6 +48,18 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e
|
|||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
#### Vcpkg
|
||||
|
||||
Hifi uses vcpkg to download and build dependencies.
|
||||
You do not need to install vcpkg.
|
||||
|
||||
Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory.
|
||||
However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable:
|
||||
|
||||
export HIFI_VCPKG_BASE=/path/to/directory
|
||||
|
||||
Where /path/to/directory is the path to a directory where you wish the build files to get stored.
|
||||
|
||||
#### Generating build files
|
||||
|
||||
Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
|
||||
|
@ -80,7 +92,7 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
|
||||
### Optional Components
|
||||
|
||||
#### Build Options
|
||||
#### Build Options
|
||||
|
||||
The following build options can be used when running CMake
|
||||
|
||||
|
@ -89,7 +101,7 @@ The following build options can be used when running CMake
|
|||
* BUILD_TESTS
|
||||
* BUILD_TOOLS
|
||||
|
||||
#### Developer Build Options
|
||||
#### Developer Build Options
|
||||
|
||||
* USE_GLES
|
||||
* DISABLE_UI
|
||||
|
|
|
@ -6,13 +6,8 @@ Building is currently supported on OSX, Windows and Linux platforms, but develop
|
|||
|
||||
You will need the following tools to build Android targets.
|
||||
|
||||
* [Gradle](https://gradle.org/install/)
|
||||
* [Android Studio](https://developer.android.com/studio/index.html)
|
||||
|
||||
### Gradle
|
||||
|
||||
Install gradle version 4.1 or higher. Following the instructions to install via [SDKMAN!](http://sdkman.io/install.html) 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
|
||||
|
@ -29,6 +24,8 @@ From the SDK Tools tab select the following
|
|||
* Android SDK Tools
|
||||
* NDK (even if you have the NDK installed separately)
|
||||
|
||||
Make sure the NDK installed version is 18 (or higher)
|
||||
|
||||
# Environment
|
||||
|
||||
Setting up the environment for android builds requires some additional steps
|
||||
|
@ -51,17 +48,17 @@ Enter the repository `android` directory
|
|||
|
||||
`cd hifi/android`
|
||||
|
||||
Execute a gradle pre-build setup. This step should only need to be done once
|
||||
Execute two gradle pre-build steps. This steps should only need to be done once, unless you're working on the Android dependencies
|
||||
|
||||
`gradle setupDependencies`
|
||||
`./gradlew extractDependencies`
|
||||
|
||||
`./gradlew setupDependencies`
|
||||
|
||||
# Building & Running
|
||||
|
||||
* Open Android Studio
|
||||
* Choose _Open Existing Android Studio Project_
|
||||
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
|
||||
* If Android Studio asks you if you want to use the Gradle wrapper, select cancel and tell it where your local gradle installation is. If you used SDKMAN to install gradle it will be located in `$HOME/.sdkman/candidates/gradle/current/`
|
||||
* From the _Build_ menu select _Make Project_
|
||||
* Once the build completes, from the _Run_ menu select _Run App_
|
||||
|
||||
|
|
|
@ -91,19 +91,73 @@ In a server, it does not make sense to compile interface
|
|||
|
||||
### Running the software
|
||||
|
||||
#### Domain server
|
||||
|
||||
Running domain server:
|
||||
```bash
|
||||
./domain-server/domain-server
|
||||
```
|
||||
|
||||
#### Assignment clients
|
||||
|
||||
Running assignment client:
|
||||
```bash
|
||||
./assignment-client/assignment-client -n 6
|
||||
```
|
||||
|
||||
#### Interface
|
||||
|
||||
Running interface:
|
||||
```bash
|
||||
./interface/interface
|
||||
```
|
||||
|
||||
Go to localhost in the running interface.
|
||||
|
||||
##### Ubuntu 18.04 only
|
||||
|
||||
In Ubuntu 18.04 there is a problem related with NVidia driver library version.
|
||||
|
||||
It can be workarounded following these steps:
|
||||
|
||||
Uninstall incompatible nvtt libraries:
|
||||
```bash
|
||||
sudo apt-get remove libnvtt2 libnvtt-dev
|
||||
```
|
||||
|
||||
Install libssl1.0-dev:
|
||||
```bash
|
||||
sudo apt-get -y install libssl1.0-dev
|
||||
```
|
||||
|
||||
Clone castano nvidia-texture-tools:
|
||||
```
|
||||
git clone https://github.com/castano/nvidia-texture-tools
|
||||
cd nvidia-texture-tools/
|
||||
```
|
||||
|
||||
Make these changes in repo:
|
||||
* In file **VERSION** set `2.2.1`
|
||||
* In file **configure**:
|
||||
* set `build="release"`
|
||||
* set `-DNVTT_SHARED=1`
|
||||
|
||||
Configure, build and install:
|
||||
```
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Link compiled files:
|
||||
```
|
||||
sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so
|
||||
sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so
|
||||
sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so
|
||||
sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so
|
||||
```
|
||||
|
||||
After running this steps you can run interface:
|
||||
```
|
||||
interface/interface
|
||||
```
|
||||
|
|
23
BUILD_WIN.md
|
@ -19,7 +19,7 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo
|
|||
|
||||
### Step 2. Installing CMake
|
||||
|
||||
Download and install the latest version of CMake 3.9.
|
||||
Download and install the latest version of CMake 3.9.
|
||||
|
||||
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
|
||||
|
||||
|
@ -35,20 +35,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
|
|||
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
|
||||
* Set "Variable value": `C:\Qt\5.10.1\msvc2017_64\lib\cmake`
|
||||
|
||||
### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
|
||||
* Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg)
|
||||
* Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg
|
||||
* Note, you may need to do these in a _Developer Command Prompt_
|
||||
* Set an environment variable VCPKG_ROOT to the location of the cloned repository
|
||||
* Close and re-open any command prompts after setting the environment variable so that they will pick up the change
|
||||
|
||||
### Step 6. Installing OpenSSL via vcpkg
|
||||
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `.\vcpkg install openssl:x64-windows`
|
||||
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
|
||||
|
||||
### Step 7. Running CMake to Generate Build Files
|
||||
### Step 5. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
```
|
||||
|
@ -58,9 +45,9 @@ cd build
|
|||
cmake .. -G "Visual Studio 15 Win64"
|
||||
```
|
||||
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
|
||||
### Step 8. Making a Build
|
||||
### Step 6. Making a Build
|
||||
|
||||
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
|
||||
|
||||
|
@ -68,7 +55,7 @@ Change the Solution Configuration (menu ribbon under the menu bar, next to the g
|
|||
|
||||
Run from the menu bar `Build > Build Solution`.
|
||||
|
||||
### Step 9. Testing Interface
|
||||
### Step 7. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #4)
|
||||
* Set "Variable name": `_NO_DEBUG_HEAP`
|
||||
|
|
|
@ -139,23 +139,23 @@ dependencies {
|
|||
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.android.support:design:26.1.0'
|
||||
compile 'com.android.support:support-v4:26.1.0'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:support-vector-drawable:26.1.0'
|
||||
api 'com.android.support:support-v4:26.1.0'
|
||||
api 'com.android.support:appcompat-v7:26.1.0'
|
||||
api 'com.android.support:support-vector-drawable:26.1.0'
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||
compile 'com.android.support:cardview-v7:26.1.0'
|
||||
api 'com.android.support:recyclerview-v7:26.1.0'
|
||||
api 'com.android.support:cardview-v7:26.1.0'
|
||||
|
||||
compile 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
api 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
api 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
api 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||
api 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
api 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.hifiinterface">
|
||||
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
|
|
@ -14,7 +14,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,7 +392,7 @@ task extractDependencies(dependsOn: verifyDependencyDownloads) {
|
|||
}
|
||||
|
||||
// Copies the non Qt dependencies. Qt dependencies (primary libraries and plugins) are handled by the qtBundle task
|
||||
task copyDependencies(dependsOn: [ extractDependencies ]) {
|
||||
task copyDependencies() {
|
||||
doLast {
|
||||
packages.each { entry ->
|
||||
def packageName = entry.key
|
||||
|
@ -414,7 +414,7 @@ task copyDependencies(dependsOn: [ extractDependencies ]) {
|
|||
}
|
||||
}
|
||||
|
||||
task extractGvrBinaries(dependsOn: extractDependencies) {
|
||||
task extractGvrBinaries() {
|
||||
doLast {
|
||||
def gvrLibFolder = new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries');
|
||||
zipTree(new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries/sdk-audio-1.101.0.aar')).visit { element ->
|
||||
|
|
4
android/build_android.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -xeuo pipefail
|
||||
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} setupDependencies
|
||||
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} app:${ANDROID_BUILD_TARGET}
|
22
android/containerized_build.sh
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
set -xeuo pipefail
|
||||
|
||||
DOCKER_IMAGE_NAME="hifi_androidbuild"
|
||||
|
||||
docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/Dockerfile docker
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--security-opt seccomp:unconfined \
|
||||
-v "${WORKSPACE}":/home/jenkins/hifi \
|
||||
-e "RELEASE_NUMBER=${RELEASE_NUMBER}" \
|
||||
-e "RELEASE_TYPE=${RELEASE_TYPE}" \
|
||||
-e "ANDROID_BUILD_TARGET=assembleDebug" \
|
||||
-e "CMAKE_BACKTRACE_URL=${CMAKE_BACKTRACE_URL}" \
|
||||
-e "CMAKE_BACKTRACE_TOKEN=${CMAKE_BACKTRACE_TOKEN}" \
|
||||
-e "CMAKE_BACKTRACE_SYMBOLS_TOKEN=${CMAKE_BACKTRACE_SYMBOLS_TOKEN}" \
|
||||
-e "GA_TRACKING_ID=${GA_TRACKING_ID}" \
|
||||
-e "GIT_PR_COMMIT=${GIT_PR_COMMIT}" \
|
||||
-e "VERSION_CODE=${VERSION_CODE}" \
|
||||
"${DOCKER_IMAGE_NAME}" \
|
||||
sh -c "./build_android.sh"
|
92
android/docker/Dockerfile
Normal file
|
@ -0,0 +1,92 @@
|
|||
FROM openjdk:8
|
||||
|
||||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
RUN apt-get update && apt-get -y install \
|
||||
curl \
|
||||
gnupg \
|
||||
software-properties-common \
|
||||
unzip \
|
||||
-
|
||||
|
||||
# --- Versions and Download paths
|
||||
ENV ANDROID_HOME="/usr/local/android-sdk" \
|
||||
ANDROID_NDK_HOME="/usr/local/android-ndk" \
|
||||
ANDROID_SDK_HOME="/usr/local/android-sdk-home" \
|
||||
ANDROID_VERSION=26 \
|
||||
ANDROID_BUILD_TOOLS_VERSION=28.0.3 \
|
||||
ANDROID_NDK_VERSION=r18
|
||||
|
||||
ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" \
|
||||
NDK_URL="https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip"
|
||||
|
||||
# --- Android SDK
|
||||
RUN mkdir -p "$ANDROID_HOME" "$ANDROID_SDK_HOME" && \
|
||||
cd "$ANDROID_HOME" && \
|
||||
curl -s -S -o sdk.zip -L "${SDK_URL}" && \
|
||||
unzip sdk.zip && \
|
||||
rm sdk.zip && \
|
||||
yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
# Install Android Build Tool and Libraries
|
||||
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
|
||||
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
|
||||
"platforms;android-${ANDROID_VERSION}" \
|
||||
"platform-tools"
|
||||
|
||||
RUN chmod -R a+w "${ANDROID_HOME}"
|
||||
RUN chmod -R a+w "${ANDROID_SDK_HOME}"
|
||||
|
||||
# --- Android NDK
|
||||
# download
|
||||
RUN mkdir /usr/local/android-ndk-tmp && \
|
||||
cd /usr/local/android-ndk-tmp && \
|
||||
curl -s -S -o ndk.zip -L "${NDK_URL}" && \
|
||||
unzip -q ndk.zip && \
|
||||
mv ./android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} && \
|
||||
cd ${ANDROID_NDK_HOME} && \
|
||||
rm -rf /usr/local/android-ndk-tmp
|
||||
|
||||
ENV PATH ${PATH}:${ANDROID_NDK_HOME}
|
||||
|
||||
RUN apt-get -y install \
|
||||
g++ \
|
||||
gcc \
|
||||
-
|
||||
|
||||
# --- Gradle
|
||||
ARG BUILD_UID=1001
|
||||
RUN useradd -ms /bin/bash -u $BUILD_UID jenkins
|
||||
USER jenkins
|
||||
WORKDIR /home/jenkins
|
||||
|
||||
# Hifi dependencies
|
||||
ENV HIFI_BASE="/home/jenkins/hifi_android"
|
||||
ENV HIFI_ANDROID_PRECOMPILED="$HIFI_BASE/dependencies"
|
||||
ENV HIFI_VCPKG_BASE="$HIFI_BASE/vcpkg"
|
||||
|
||||
RUN mkdir "$HIFI_BASE" && \
|
||||
mkdir "$HIFI_VCPKG_BASE" && \
|
||||
mkdir "$HIFI_ANDROID_PRECOMPILED"
|
||||
|
||||
RUN git clone https://github.com/jherico/hifi.git && \
|
||||
cd ~/hifi && \
|
||||
git checkout feature/build/gradle-wrapper
|
||||
|
||||
|
||||
WORKDIR /home/jenkins/hifi
|
||||
|
||||
RUN touch .test4 && \
|
||||
git fetch && git reset origin/feature/build/gradle-wrapper --hard
|
||||
|
||||
RUN mkdir build
|
||||
|
||||
# Pre-cache the vcpkg managed dependencies
|
||||
WORKDIR /home/jenkins/hifi/build
|
||||
RUN python3 ../prebuild.py --build-root `pwd` --android
|
||||
|
||||
# Pre-cache the gradle dependencies
|
||||
WORKDIR /home/jenkins/hifi/android
|
||||
RUN ./gradlew -m tasks -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED
|
||||
RUN ./gradlew extractDependencies -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED
|
||||
|
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#Sat Dec 01 08:32:47 PST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
172
android/gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
84
android/gradlew.bat
vendored
Executable file
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -11,7 +11,7 @@ setup_memory_debugger()
|
|||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree gpu graphics fbx hfm entities
|
||||
audio avatars octree gpu graphics shaders fbx hfm entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins midi image
|
||||
)
|
||||
|
|
|
@ -428,6 +428,10 @@ void Agent::executeScript() {
|
|||
using namespace recording;
|
||||
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName());
|
||||
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
|
||||
if (_shouldMuteRecordingAudio) {
|
||||
return;
|
||||
}
|
||||
|
||||
static quint16 audioSequenceNumber{ 0 };
|
||||
|
||||
QByteArray audio(frame->data);
|
||||
|
@ -656,6 +660,8 @@ void Agent::queryAvatars() {
|
|||
ViewFrustum view;
|
||||
view.setPosition(scriptedAvatar->getWorldPosition());
|
||||
view.setOrientation(scriptedAvatar->getHeadOrientation());
|
||||
view.setProjection(DEFAULT_FIELD_OF_VIEW_DEGREES, DEFAULT_ASPECT_RATIO,
|
||||
DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP);
|
||||
view.calculate();
|
||||
ConicalViewFrustum conicalView { view };
|
||||
|
||||
|
@ -753,7 +759,11 @@ void Agent::processAgentAvatarAudio() {
|
|||
int16_t numAvailableSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||
const int16_t* nextSoundOutput = NULL;
|
||||
|
||||
if (_avatarSound) {
|
||||
if (_avatarSound && _avatarSound->isReady()) {
|
||||
if (isPlayingRecording && !_shouldMuteRecordingAudio) {
|
||||
_shouldMuteRecordingAudio = true;
|
||||
}
|
||||
|
||||
auto audioData = _avatarSound->getAudioData();
|
||||
nextSoundOutput = reinterpret_cast<const int16_t*>(audioData->rawData()
|
||||
+ _numAvatarSoundSentBytes);
|
||||
|
@ -779,6 +789,10 @@ void Agent::processAgentAvatarAudio() {
|
|||
_avatarSound.clear();
|
||||
_numAvatarSoundSentBytes = 0;
|
||||
_flushEncoder = true;
|
||||
|
||||
if (_shouldMuteRecordingAudio) {
|
||||
_shouldMuteRecordingAudio = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -876,18 +890,30 @@ void Agent::aboutToFinish() {
|
|||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
||||
// destroy all other created dependencies
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<RecordingScriptingInterface>();
|
||||
DependencyManager::destroy<AnimationCacheScriptingInterface>();
|
||||
DependencyManager::destroy<EntityScriptingInterface>();
|
||||
DependencyManager::destroy<ResourceScriptingInterface>();
|
||||
DependencyManager::destroy<UserActivityLoggerScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<AnimationCache>();
|
||||
|
||||
DependencyManager::destroy<recording::Deck>();
|
||||
DependencyManager::destroy<recording::Recorder>();
|
||||
DependencyManager::destroy<recording::ClipCache>();
|
||||
|
||||
DependencyManager::destroy<AvatarHashMap>();
|
||||
DependencyManager::destroy<AssignmentParentFinder>();
|
||||
DependencyManager::destroy<MessagesClient>();
|
||||
DependencyManager::destroy<ResourceManager>();
|
||||
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
|
||||
// drop our shared pointer to the script engine, then ask ScriptEngines to shutdown scripting
|
||||
// this ensures that the ScriptEngine goes down before ScriptEngines
|
||||
_scriptEngine.clear();
|
||||
|
|
|
@ -107,6 +107,7 @@ private:
|
|||
ResourceRequest* _pendingScriptRequest { nullptr };
|
||||
bool _isListeningToAudioStream = false;
|
||||
SharedSoundPointer _avatarSound;
|
||||
bool _shouldMuteRecordingAudio { false };
|
||||
int _numAvatarSoundSentBytes = 0;
|
||||
bool _isAvatar = false;
|
||||
QTimer* _avatarIdentityTimer = nullptr;
|
||||
|
|
|
@ -129,17 +129,12 @@ void AssignmentClient::stopAssignmentClient() {
|
|||
QThread* currentAssignmentThread = _currentAssignment->thread();
|
||||
|
||||
// ask the current assignment to stop
|
||||
BLOCKING_INVOKE_METHOD(_currentAssignment, "stop");
|
||||
QMetaObject::invokeMethod(_currentAssignment, "stop");
|
||||
|
||||
// ask the current assignment to delete itself on its thread
|
||||
_currentAssignment->deleteLater();
|
||||
|
||||
// when this thread is destroyed we don't need to run our assignment complete method
|
||||
disconnect(currentAssignmentThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
|
||||
|
||||
// wait on the thread from that assignment - it will be gone once the current assignment deletes
|
||||
currentAssignmentThread->quit();
|
||||
currentAssignmentThread->wait();
|
||||
auto PROCESS_EVENTS_INTERVAL_MS = 100;
|
||||
while (!currentAssignmentThread->wait(PROCESS_EVENTS_INTERVAL_MS)) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -337,6 +337,13 @@ void AudioMixerClientData::removeAgentAvatarAudioStream() {
|
|||
|
||||
if (it != _audioStreams.end()) {
|
||||
_audioStreams.erase(it);
|
||||
|
||||
// Clear mixing structures so that they get recreated with up to date
|
||||
// data if the stream comes back
|
||||
setHasReceivedFirstMix(false);
|
||||
_streams.skipped.clear();
|
||||
_streams.inactive.clear();
|
||||
_streams.active.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
if (packetTraitVersion > instanceVersionRef) {
|
||||
if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) {
|
||||
_avatar->processDeletedTraitInstance(traitType, instanceID);
|
||||
// Mixer doesn't need deleted IDs.
|
||||
_avatar->getAndClearRecentlyDetachedIDs();
|
||||
|
||||
// to track a deleted instance but keep version information
|
||||
// the avatar mixer uses the negative value of the sent version
|
||||
|
|
|
@ -110,6 +110,8 @@ public:
|
|||
void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; }
|
||||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||
bool getPrevRequestsDomainListData() { return _prevRequestsDomainListData; }
|
||||
void setPrevRequestsDomainListData(bool requesting) { _prevRequestsDomainListData = requesting; }
|
||||
|
||||
const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
|
||||
|
||||
|
@ -176,6 +178,7 @@ private:
|
|||
int _recentOtherAvatarsOutOfView { 0 };
|
||||
QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary.
|
||||
bool _requestsDomainListData { false };
|
||||
bool _prevRequestsDomainListData{ false };
|
||||
|
||||
AvatarTraits::TraitVersions _lastReceivedTraitVersions;
|
||||
TraitsCheckTimestamp _lastReceivedTraitsChange;
|
||||
|
|
|
@ -268,6 +268,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// When this is true, the AvatarMixer will send Avatar data to a client
|
||||
// about avatars they've ignored or that are out of view
|
||||
bool PALIsOpen = nodeData->getRequestsDomainListData();
|
||||
bool PALWasOpen = nodeData->getPrevRequestsDomainListData();
|
||||
|
||||
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
|
||||
bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick();
|
||||
|
@ -392,6 +393,26 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
|
||||
}
|
||||
|
||||
// If Avatar A's PAL WAS open but is no longer open, AND
|
||||
// Avatar A is ignoring Avatar B OR Avatar B is ignoring Avatar A...
|
||||
//
|
||||
// This is a bit heavy-handed still - there are cases where a kill packet
|
||||
// will be sent when it doesn't need to be (but where it _should_ be OK to send).
|
||||
// However, it's less heavy-handed than using `shouldIgnore`.
|
||||
if (PALWasOpen && !PALIsOpen &&
|
||||
(destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) ||
|
||||
avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()))) {
|
||||
// ...send a Kill Packet to Node A, instructing Node A to kill Avatar B,
|
||||
// then have Node A cleanup the killed Node B.
|
||||
auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
packet->write(avatarNode->getUUID().toRfc4122());
|
||||
packet->writePrimitive(KillAvatarReason::AvatarIgnored);
|
||||
nodeList->sendPacket(std::move(packet), *destinationNode);
|
||||
nodeData->cleanupKilledNode(avatarNode->getUUID(), avatarNode->getLocalID());
|
||||
}
|
||||
|
||||
nodeData->setPrevRequestsDomainListData(PALIsOpen);
|
||||
}
|
||||
|
||||
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
||||
|
|
|
@ -19,14 +19,17 @@
|
|||
#include <AnimUtil.h>
|
||||
#include <ClientTraitsHandler.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <ResourceRequestObserver.h>
|
||||
#include <AvatarLogging.h>
|
||||
|
||||
|
||||
ScriptableAvatar::ScriptableAvatar() {
|
||||
_clientTraitsHandler = std::unique_ptr<ClientTraitsHandler>(new ClientTraitsHandler(this));
|
||||
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
|
||||
}
|
||||
|
||||
QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {
|
||||
_globalPosition = getWorldPosition();
|
||||
return AvatarData::toByteArrayStateful(dataDetail);
|
||||
return AvatarData::toByteArrayStateful(dataDetail, dropFaceTracking);
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,11 +65,28 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() {
|
|||
return _animationDetails;
|
||||
}
|
||||
|
||||
int ScriptableAvatar::getJointIndex(const QString& name) const {
|
||||
// Faux joints:
|
||||
int result = AvatarData::getJointIndex(name);
|
||||
if (result != -1) {
|
||||
return result;
|
||||
}
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointIndices.value(name) - 1;
|
||||
}
|
||||
|
||||
QStringList ScriptableAvatar::getJointNames() const {
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _fstJointNames;
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
_bind.reset();
|
||||
_animSkeleton.reset();
|
||||
|
||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||
updateJointMappings();
|
||||
}
|
||||
|
||||
static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) {
|
||||
|
@ -77,10 +97,6 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation,
|
|||
}
|
||||
|
||||
void ScriptableAvatar::update(float deltatime) {
|
||||
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
|
||||
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
|
||||
}
|
||||
|
||||
// Run animation
|
||||
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) {
|
||||
if (!_animSkeleton) {
|
||||
|
@ -146,6 +162,82 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
_clientTraitsHandler->sendChangedTraitsToMixer();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::updateJointMappings() {
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
_fstJointIndices.clear();
|
||||
_fstJointNames.clear();
|
||||
_jointData.clear();
|
||||
}
|
||||
|
||||
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||
////
|
||||
// TODO: Should we rely upon HTTPResourceRequest for ResourceRequestObserver instead?
|
||||
// HTTPResourceRequest::doSend() covers all of the following and
|
||||
// then some. It doesn't cover the connect() call, so we may
|
||||
// want to add a HTTPResourceRequest::doSend() method that does
|
||||
// connects.
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
DependencyManager::get<ResourceRequestObserver>()->update(
|
||||
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
|
||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||
//
|
||||
////
|
||||
connect(networkReply, &QNetworkReply::finished, this, &ScriptableAvatar::setJointMappingsFromNetworkReply);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setJointMappingsFromNetworkReply() {
|
||||
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
|
||||
// before we process this update, make sure that the skeleton model URL hasn't changed
|
||||
// since we made the FST request
|
||||
if (networkReply->url() != _skeletonModelURL) {
|
||||
qCDebug(avatars) << "Refusing to set joint mappings for FST URL that does not match the current URL";
|
||||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
QByteArray line;
|
||||
while (!(line = networkReply->readLine()).isEmpty()) {
|
||||
line = line.trimmed();
|
||||
if (line.startsWith("filename")) {
|
||||
int filenameIndex = line.indexOf('=') + 1;
|
||||
if (filenameIndex > 0) {
|
||||
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||
}
|
||||
}
|
||||
if (!line.startsWith("jointIndex")) {
|
||||
continue;
|
||||
}
|
||||
int jointNameIndex = line.indexOf('=') + 1;
|
||||
if (jointNameIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
|
||||
if (secondSeparatorIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
|
||||
bool ok;
|
||||
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
||||
if (ok) {
|
||||
while (_fstJointNames.size() < jointIndex + 1) {
|
||||
_fstJointNames.append(QString());
|
||||
}
|
||||
_fstJointNames[jointIndex] = jointName;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _fstJointNames.size(); i++) {
|
||||
_fstJointIndices.insert(_fstJointNames.at(i), i + 1);
|
||||
}
|
||||
}
|
||||
networkReply->deleteLater();
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement) {
|
||||
_headData->setHasProceduralBlinkFaceMovement(hasProceduralBlinkFaceMovement);
|
||||
}
|
||||
|
|
|
@ -153,6 +153,27 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
||||
|
||||
/**jsdoc
|
||||
* Get the names of all the joints in the current avatar.
|
||||
* @function MyAvatar.getJointNames
|
||||
* @returns {string[]} The joint names.
|
||||
* @example <caption>Report the names of all the joints in your current avatar.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointNames()));
|
||||
*/
|
||||
Q_INVOKABLE virtual QStringList getJointNames() const override;
|
||||
|
||||
/**jsdoc
|
||||
* Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by
|
||||
* {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
|
||||
* @function MyAvatar.getJointIndex
|
||||
* @param {string} name - The name of the joint.
|
||||
* @returns {number} The index of the joint.
|
||||
* @example <caption>Report the index of your avatar's left arm joint.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"));
|
||||
*/
|
||||
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
|
||||
Q_INVOKABLE virtual int getJointIndex(const QString& name) const override;
|
||||
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override;
|
||||
|
@ -167,12 +188,23 @@ public:
|
|||
public slots:
|
||||
void update(float deltatime);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setJointMappingsFromNetworkReply
|
||||
*/
|
||||
void setJointMappingsFromNetworkReply();
|
||||
|
||||
private:
|
||||
AnimationPointer _animation;
|
||||
AnimationDetails _animationDetails;
|
||||
QStringList _maskedJoints;
|
||||
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
QHash<QString, int> _fstJointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _fstJointNames; ///< in order of depth-first traversal
|
||||
QUrl _skeletonFBXURL;
|
||||
|
||||
/// Loads the joint indices, names from the FST file (if any)
|
||||
void updateJointMappings();
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptableAvatar_h
|
||||
|
|
|
@ -583,15 +583,29 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> mess
|
|||
void EntityScriptServer::aboutToFinish() {
|
||||
shutdownScriptEngine();
|
||||
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<ResourceScriptingInterface>();
|
||||
DependencyManager::destroy<EntityScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
|
||||
DependencyManager::destroy<ResourceManager>();
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
|
||||
DependencyManager::destroy<MessagesClient>();
|
||||
|
||||
DependencyManager::destroy<AssignmentDynamicFactory>();
|
||||
DependencyManager::destroy<AssignmentParentFinder>();
|
||||
DependencyManager::destroy<AvatarHashMap>();
|
||||
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
DependencyManager::destroy<PluginManager>();
|
||||
|
||||
DependencyManager::destroy<ResourceScriptingInterface>();
|
||||
DependencyManager::destroy<EntityScriptingInterface>();
|
||||
|
||||
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
|
|
@ -364,7 +364,7 @@ function validateInputs() {
|
|||
if (keyVal.length === 0) {
|
||||
empty = true
|
||||
|
||||
markParentRowInvalid(input);
|
||||
markParentRowInvalid(input)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -373,11 +373,13 @@ function validateInputs() {
|
|||
_.each(otherKeys, function(otherKeyCell) {
|
||||
var keyInput = $(otherKeyCell).children('input');
|
||||
|
||||
var lowerNewValue = keyVal.toLowerCase();
|
||||
|
||||
if (keyInput.length) {
|
||||
if ($(keyInput).val() == keyVal) {
|
||||
if ($(keyInput).val().toLowerCase() == lowerNewValue) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
} else if ($(otherKeyCell).html() == keyVal) {
|
||||
} else if ($(otherKeyCell).html().toLowerCase() == lowerNewValue) {
|
||||
duplicateKey = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ $(document).ready(function(){
|
|||
Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex;
|
||||
|
||||
Settings.afterReloadActions = function() {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
|
||||
// call our method to setup the HF account button
|
||||
setupHFAccountButton();
|
||||
|
||||
|
@ -52,6 +49,11 @@ $(document).ready(function(){
|
|||
if (cloudWizardExit != undefined) {
|
||||
$('#cloud-domains-alert').show();
|
||||
}
|
||||
|
||||
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("</br><strong>Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page.</strong>");
|
||||
} else {
|
||||
// append the domain selection modal
|
||||
appendDomainIDButtons();
|
||||
}
|
||||
|
||||
handleAction();
|
||||
|
@ -59,9 +61,9 @@ $(document).ready(function(){
|
|||
|
||||
Settings.handlePostSettings = function(formJSON) {
|
||||
|
||||
if (!verifyAvatarHeights()) {
|
||||
return false;
|
||||
}
|
||||
if (!verifyAvatarHeights()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we've set the basic http password
|
||||
if (formJSON["security"]) {
|
||||
|
|
|
@ -127,6 +127,10 @@ int64_t DomainContentBackupManager::getMostRecentBackupTimeInSecs(const QString&
|
|||
}
|
||||
|
||||
void DomainContentBackupManager::setup() {
|
||||
for (auto& rule : _backupRules) {
|
||||
removeOldBackupVersions(rule);
|
||||
}
|
||||
|
||||
auto backups = getAllBackups();
|
||||
for (auto& backup : backups) {
|
||||
QFile backupFile { backup.absolutePath };
|
||||
|
|
|
@ -3172,24 +3172,34 @@ void DomainServer::processPathQueryPacket(QSharedPointer<ReceivedMessage> messag
|
|||
const QString PATH_VIEWPOINT_KEY = "viewpoint";
|
||||
const QString INDEX_PATH = "/";
|
||||
|
||||
// check out paths in the _configMap to see if we have a match
|
||||
auto keypath = QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY).arg(pathQuery);
|
||||
QVariant pathMatch = _settingsManager.valueForKeyPath(keypath);
|
||||
QString responseViewpoint;
|
||||
|
||||
if (pathMatch.isValid() || pathQuery == INDEX_PATH) {
|
||||
// check out paths in the _configMap to see if we have a match
|
||||
auto pathsVariant = _settingsManager.valueForKeyPath(SETTINGS_PATHS_KEY);
|
||||
|
||||
auto lowerPathQuery = pathQuery.toLower();
|
||||
|
||||
if (pathsVariant.canConvert<QVariantMap>()) {
|
||||
auto pathsMap = pathsVariant.toMap();
|
||||
|
||||
// enumerate the paths and look case-insensitively for a matching one
|
||||
for (auto it = pathsMap.constKeyValueBegin(); it != pathsMap.constKeyValueEnd(); ++it) {
|
||||
if ((*it).first.toLower() == lowerPathQuery) {
|
||||
responseViewpoint = (*it).second.toMap()[PATH_VIEWPOINT_KEY].toString().toLower();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (responseViewpoint.isEmpty() && pathQuery == INDEX_PATH) {
|
||||
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
||||
responseViewpoint = DEFAULT_INDEX_PATH;
|
||||
}
|
||||
|
||||
if (!responseViewpoint.isEmpty()) {
|
||||
// we got a match, respond with the resulting viewpoint
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
QString responseViewpoint;
|
||||
|
||||
// if we didn't match the path BUT this is for the index path then send back our default
|
||||
if (pathMatch.isValid()) {
|
||||
responseViewpoint = pathMatch.toMap()[PATH_VIEWPOINT_KEY].toString();
|
||||
} else {
|
||||
const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1";
|
||||
responseViewpoint = DEFAULT_INDEX_PATH;
|
||||
}
|
||||
|
||||
if (!responseViewpoint.isEmpty()) {
|
||||
QByteArray viewpointUTF8 = responseViewpoint.toUtf8();
|
||||
|
||||
|
|
286
hifi_android.py
Normal file
|
@ -0,0 +1,286 @@
|
|||
import hifi_utils
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
import functools
|
||||
|
||||
print = functools.partial(print, flush=True)
|
||||
|
||||
ANDROID_PACKAGE_URL = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
|
||||
|
||||
ANDROID_PACKAGES = {
|
||||
'qt' : {
|
||||
'file': 'qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': '3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN',
|
||||
'checksum': 'aa449d4bfa963f3bc9a9dfe558ba29df',
|
||||
},
|
||||
'bullet': {
|
||||
'file': 'bullet-2.88_armv8-libcpp.tgz',
|
||||
'versionId': 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
|
||||
'checksum': '81642779ccb110f8c7338e8739ac38a0',
|
||||
},
|
||||
'draco': {
|
||||
'file': 'draco_armv8-libcpp.tgz',
|
||||
'versionId': '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
|
||||
'checksum': '617a80d213a5ec69fbfa21a1f2f738cd',
|
||||
},
|
||||
'glad': {
|
||||
'file': 'glad_armv8-libcpp.zip',
|
||||
'versionId': 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
|
||||
'checksum': 'a8ee8584cf1ccd34766c7ddd9d5e5449',
|
||||
},
|
||||
'gvr': {
|
||||
'file': 'gvrsdk_v1.101.0.tgz',
|
||||
'versionId': 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
|
||||
'checksum': '57fd02baa069176ba18597a29b6b4fc7',
|
||||
},
|
||||
'nvtt': {
|
||||
'file': 'nvtt_armv8-libcpp.zip',
|
||||
'versionId': 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
|
||||
'checksum': 'eb46d0b683e66987190ed124aabf8910',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so']
|
||||
},
|
||||
'oculus': {
|
||||
'file': 'ovr_sdk_mobile_1.19.0.zip',
|
||||
'versionId': 's_RN1vlEvUi3pnT7WPxUC4pQ0RJBs27y',
|
||||
'checksum': '98f0afb62861f1f02dd8110b31ed30eb',
|
||||
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
|
||||
'includeLibs': ['libvrapi.so']
|
||||
},
|
||||
'openssl': {
|
||||
'file': 'openssl-1.1.0g_armv8.tgz',
|
||||
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
|
||||
'checksum': 'cabb681fbccd79594f65fcc266e02f32'
|
||||
},
|
||||
'polyvox': {
|
||||
'file': 'polyvox_armv8-libcpp.tgz',
|
||||
'versionId': 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
|
||||
'checksum': 'dba88b3a098747af4bb169e9eb9af57e',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
|
||||
},
|
||||
'tbb': {
|
||||
'file': 'tbb-2018_U1_armv8_libcpp.tgz',
|
||||
'versionId': 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
|
||||
'checksum': '20768f298f53b195e71b414b0ae240c4',
|
||||
'sharedLibFolder': 'lib/release',
|
||||
'includeLibs': ['libtbb.so', 'libtbbmalloc.so'],
|
||||
},
|
||||
'hifiAC': {
|
||||
'baseUrl': 'http://s3.amazonaws.com/hifi-public/dependencies/',
|
||||
'file': 'codecSDK-android_armv8-2.0.zip',
|
||||
'checksum': '1cbef929675818fc64c4101b72f84a6a'
|
||||
},
|
||||
'etc2comp': {
|
||||
'file': 'etc2comp-patched-armv8-libcpp.tgz',
|
||||
'versionId': 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
|
||||
'checksum': '14b02795d774457a33bbc60e00a786bc'
|
||||
},
|
||||
'breakpad': {
|
||||
'file': 'breakpad.tgz',
|
||||
'versionId': '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI',
|
||||
'checksum': 'ddcb23df336b08017042ba4786db1d9e',
|
||||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': {'libbreakpad_client.a'}
|
||||
}
|
||||
}
|
||||
|
||||
ANDROID_PLATFORM_PACKAGES = {
|
||||
'Darwin' : {
|
||||
'qt': {
|
||||
'file': 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': 'OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup',
|
||||
'checksum': 'c83cc477c08a892e00c71764dca051a0'
|
||||
},
|
||||
},
|
||||
'Windows' : {
|
||||
'qt': {
|
||||
'file': 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz',
|
||||
'versionId': 'JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT',
|
||||
'checksum': '0582191cc55431aa4f660848a542883e'
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
QT5_DEPS = [
|
||||
'Qt5Concurrent',
|
||||
'Qt5Core',
|
||||
'Qt5Gui',
|
||||
'Qt5Multimedia',
|
||||
'Qt5Network',
|
||||
'Qt5OpenGL',
|
||||
'Qt5Qml',
|
||||
'Qt5Quick',
|
||||
'Qt5QuickControls2',
|
||||
'Qt5QuickTemplates2',
|
||||
'Qt5Script',
|
||||
'Qt5ScriptTools',
|
||||
'Qt5Svg',
|
||||
'Qt5WebChannel',
|
||||
'Qt5WebSockets',
|
||||
'Qt5Widgets',
|
||||
'Qt5XmlPatterns',
|
||||
# Android specific
|
||||
'Qt5AndroidExtras',
|
||||
'Qt5WebView',
|
||||
]
|
||||
|
||||
def getPlatformPackages():
|
||||
result = ANDROID_PACKAGES.copy()
|
||||
system = platform.system()
|
||||
if system in ANDROID_PLATFORM_PACKAGES:
|
||||
platformPackages = ANDROID_PLATFORM_PACKAGES[system]
|
||||
result = { **result, **platformPackages }
|
||||
return result
|
||||
|
||||
def getPackageUrl(package):
|
||||
url = ANDROID_PACKAGE_URL
|
||||
if 'baseUrl' in package:
|
||||
url = package['baseUrl']
|
||||
url += package['file']
|
||||
if 'versionId' in package:
|
||||
url += '?versionId=' + package['versionId']
|
||||
return url
|
||||
|
||||
def copyAndroidLibs(packagePath, appPath):
|
||||
androidPackages = getPlatformPackages()
|
||||
jniPath = os.path.join(appPath, 'src/main/jniLibs/arm64-v8a')
|
||||
if not os.path.isdir(jniPath):
|
||||
os.makedirs(jniPath)
|
||||
for packageName in androidPackages:
|
||||
package = androidPackages[packageName]
|
||||
if 'sharedLibFolder' in package:
|
||||
sharedLibFolder = os.path.join(packagePath, packageName, package['sharedLibFolder'])
|
||||
if 'includeLibs' in package:
|
||||
for lib in package['includeLibs']:
|
||||
sourceFile = os.path.join(sharedLibFolder, lib)
|
||||
destFile = os.path.join(jniPath, os.path.split(lib)[1])
|
||||
if not os.path.isfile(destFile):
|
||||
print("Copying {}".format(lib))
|
||||
shutil.copy(sourceFile, destFile)
|
||||
|
||||
class QtPackager:
|
||||
def __init__(self, appPath, qtRootPath):
|
||||
self.appPath = appPath
|
||||
self.qtRootPath = qtRootPath
|
||||
self.jniPath = os.path.join(self.appPath, 'src/main/jniLibs/arm64-v8a')
|
||||
self.assetPath = os.path.join(self.appPath, 'src/main/assets')
|
||||
self.qtAssetPath = os.path.join(self.assetPath, '--Added-by-androiddeployqt--')
|
||||
# Jars go into the qt library
|
||||
self.jarPath = os.path.realpath(os.path.join(self.appPath, '../../libraries/qt/libs'))
|
||||
self.xmlFile = os.path.join(self.appPath, 'src/main/res/values/libs.xml')
|
||||
self.files = []
|
||||
self.features = []
|
||||
self.permissions = []
|
||||
|
||||
def copyQtDeps(self):
|
||||
for lib in QT5_DEPS:
|
||||
libfile = os.path.join(self.qtRootPath, "lib/lib{}.so".format(lib))
|
||||
if not os.path.exists(libfile):
|
||||
continue
|
||||
self.files.append(libfile)
|
||||
androidDeps = os.path.join(self.qtRootPath, "lib/{}-android-dependencies.xml".format(lib))
|
||||
if not os.path.exists(androidDeps):
|
||||
continue
|
||||
|
||||
tree = ET.parse(androidDeps)
|
||||
root = tree.getroot()
|
||||
for item in root.findall('./dependencies/lib/depends/*'):
|
||||
if (item.tag == 'lib') or (item.tag == 'bundled'):
|
||||
relativeFilename = item.attrib['file']
|
||||
if (relativeFilename.startswith('qml')):
|
||||
continue
|
||||
filename = os.path.join(self.qtRootPath, relativeFilename)
|
||||
self.files.extend(hifi_utils.recursiveFileList(filename))
|
||||
elif item.tag == 'jar' and 'bundling' in item.attrib and item.attrib['bundling'] == "1":
|
||||
self.files.append(os.path.join(self.qtRootPath, item.attrib['file']))
|
||||
elif item.tag == 'permission':
|
||||
self.permissions.append(item.attrib['name'])
|
||||
elif item.tag == 'feature':
|
||||
self.features.append(item.attrib['name'])
|
||||
|
||||
def scanQmlImports(self):
|
||||
qmlImportCommandFile = os.path.join(self.qtRootPath, 'bin/qmlimportscanner')
|
||||
system = platform.system()
|
||||
if 'Windows' == system:
|
||||
qmlImportCommandFile += ".exe"
|
||||
if not os.path.isfile(qmlImportCommandFile):
|
||||
raise RuntimeError("Couldn't find qml import scanner")
|
||||
qmlRootPath = hifi_utils.scriptRelative('interface/resources/qml')
|
||||
qmlImportPath = os.path.join(self.qtRootPath, 'qml')
|
||||
commandResult = hifi_utils.executeSubprocessCapture([
|
||||
qmlImportCommandFile,
|
||||
'-rootPath', qmlRootPath,
|
||||
'-importPath', qmlImportPath
|
||||
])
|
||||
qmlImportResults = json.loads(commandResult)
|
||||
for item in qmlImportResults:
|
||||
if 'path' not in item:
|
||||
print("Warning: QML import could not be resolved in any of the import paths: {}".format(item['name']))
|
||||
continue
|
||||
path = os.path.realpath(item['path'])
|
||||
if not os.path.exists(path):
|
||||
continue
|
||||
basePath = path
|
||||
if os.path.isfile(basePath):
|
||||
basePath = os.path.dirname(basePath)
|
||||
basePath = os.path.normcase(basePath)
|
||||
if basePath.startswith(qmlRootPath):
|
||||
continue
|
||||
self.files.extend(hifi_utils.recursiveFileList(path))
|
||||
|
||||
def processFiles(self):
|
||||
self.files = list(set(self.files))
|
||||
self.files.sort()
|
||||
libsXmlRoot = ET.Element('resources')
|
||||
qtLibsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'qt_libs'})
|
||||
bundledLibsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'bundled_in_lib'})
|
||||
bundledAssetsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'bundled_in_assets'})
|
||||
libPrefix = 'lib'
|
||||
for sourceFile in self.files:
|
||||
if not os.path.isfile(sourceFile):
|
||||
raise RuntimeError("Unable to find dependency file " + sourceFile)
|
||||
relativePath = os.path.relpath(sourceFile, self.qtRootPath)
|
||||
destinationFile = None
|
||||
if relativePath.endswith('.so'):
|
||||
garbledFileName = None
|
||||
if relativePath.startswith(libPrefix):
|
||||
garbledFileName = relativePath[4:]
|
||||
p = re.compile(r'lib(Qt5.*).so')
|
||||
m = p.search(garbledFileName)
|
||||
if not m:
|
||||
raise RuntimeError("Huh?")
|
||||
libName = m.group(1)
|
||||
ET.SubElement(qtLibsNode, 'item').text = libName
|
||||
else:
|
||||
garbledFileName = 'lib' + relativePath.replace('\\', '_'[0])
|
||||
value = "{}:{}".format(garbledFileName, relativePath).replace('\\', '/')
|
||||
ET.SubElement(bundledLibsNode, 'item').text = value
|
||||
destinationFile = os.path.join(self.jniPath, garbledFileName)
|
||||
elif relativePath.startswith('jar'):
|
||||
destinationFile = os.path.join(self.jarPath, relativePath[4:])
|
||||
else:
|
||||
value = "--Added-by-androiddeployqt--/{}:{}".format(relativePath,relativePath).replace('\\', '/')
|
||||
ET.SubElement(bundledAssetsNode, 'item').text = value
|
||||
destinationFile = os.path.join(self.qtAssetPath, relativePath)
|
||||
|
||||
destinationParent = os.path.realpath(os.path.dirname(destinationFile))
|
||||
if not os.path.isdir(destinationParent):
|
||||
os.makedirs(destinationParent)
|
||||
if not os.path.isfile(destinationFile):
|
||||
shutil.copy(sourceFile, destinationFile)
|
||||
|
||||
tree = ET.ElementTree(libsXmlRoot)
|
||||
tree.write(self.xmlFile, 'UTF-8', True)
|
||||
|
||||
def bundle(self):
|
||||
if not os.path.isfile(self.xmlFile) or True:
|
||||
self.copyQtDeps()
|
||||
self.scanQmlImports()
|
||||
self.processFiles()
|
||||
|
||||
|
46
hifi_singleton.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import os
|
||||
import platform
|
||||
import time
|
||||
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None
|
||||
|
||||
# Used to ensure only one instance of the script runs at a time
|
||||
class Singleton:
|
||||
def __init__(self, path):
|
||||
self.fh = None
|
||||
self.windows = 'Windows' == platform.system()
|
||||
self.path = path
|
||||
|
||||
def __enter__(self):
|
||||
success = False
|
||||
while not success:
|
||||
try:
|
||||
if self.windows:
|
||||
if os.path.exists(self.path):
|
||||
os.unlink(self.path)
|
||||
self.fh = os.open(self.path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
|
||||
else:
|
||||
self.fh = open(self.path, 'x')
|
||||
fcntl.lockf(self.fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
success = True
|
||||
except EnvironmentError as err:
|
||||
if self.fh is not None:
|
||||
if self.windows:
|
||||
os.close(self.fh)
|
||||
else:
|
||||
self.fh.close()
|
||||
self.fh = None
|
||||
print("Couldn't aquire lock, retrying in 10 seconds")
|
||||
time.sleep(10)
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if self.windows:
|
||||
os.close(self.fh)
|
||||
else:
|
||||
fcntl.lockf(self.fh, fcntl.LOCK_UN)
|
||||
self.fh.close()
|
||||
os.unlink(self.path)
|
124
hifi_utils.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
import os
|
||||
import hashlib
|
||||
import platform
|
||||
import shutil
|
||||
import ssl
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import urllib
|
||||
import urllib.request
|
||||
import zipfile
|
||||
import tempfile
|
||||
import time
|
||||
import functools
|
||||
|
||||
print = functools.partial(print, flush=True)
|
||||
|
||||
def scriptRelative(*paths):
|
||||
scriptdir = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
result = os.path.join(scriptdir, *paths)
|
||||
result = os.path.realpath(result)
|
||||
result = os.path.normcase(result)
|
||||
return result
|
||||
|
||||
|
||||
def recursiveFileList(startPath):
|
||||
result = []
|
||||
if os.path.isfile(startPath):
|
||||
result.append(startPath)
|
||||
elif os.path.isdir(startPath):
|
||||
for dirName, subdirList, fileList in os.walk(startPath):
|
||||
for fname in fileList:
|
||||
result.append(os.path.realpath(os.path.join(startPath, dirName, fname)))
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
def executeSubprocessCapture(processArgs):
|
||||
processResult = subprocess.run(processArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if (0 != processResult.returncode):
|
||||
raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n\nstdout:\n{}\n\nstderr:\n{}'.format(
|
||||
processArgs[0],
|
||||
' '.join(processArgs[1:]),
|
||||
processResult.stdout.decode('utf-8'),
|
||||
processResult.stderr.decode('utf-8')))
|
||||
return processResult.stdout.decode('utf-8')
|
||||
|
||||
def executeSubprocess(processArgs, folder=None, env=None):
|
||||
restoreDir = None
|
||||
if folder != None:
|
||||
restoreDir = os.getcwd()
|
||||
os.chdir(folder)
|
||||
|
||||
process = subprocess.Popen(
|
||||
processArgs, stdout=sys.stdout, stderr=sys.stderr, env=env)
|
||||
process.wait()
|
||||
|
||||
if (0 != process.returncode):
|
||||
raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n'.format(
|
||||
processArgs[0],
|
||||
' '.join(processArgs[1:]),
|
||||
))
|
||||
|
||||
if restoreDir != None:
|
||||
os.chdir(restoreDir)
|
||||
|
||||
|
||||
def hashFile(file, hasher = hashlib.sha512()):
|
||||
with open(file, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hasher.update(chunk)
|
||||
return hasher.hexdigest()
|
||||
|
||||
# Assumes input files are in deterministic order
|
||||
def hashFiles(filenames):
|
||||
hasher = hashlib.sha256()
|
||||
for filename in filenames:
|
||||
with open(filename, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hasher.update(chunk)
|
||||
return hasher.hexdigest()
|
||||
|
||||
def hashFolder(folder):
|
||||
filenames = recursiveFileList(folder)
|
||||
return hashFiles(filenames)
|
||||
|
||||
def downloadFile(url, hash=None, hasher=hashlib.sha512(), retries=3):
|
||||
for i in range(retries):
|
||||
tempFileName = None
|
||||
# OSX Python doesn't support SSL, so we need to bypass it.
|
||||
# However, we still validate the downloaded file's sha512 hash
|
||||
if 'Darwin' == platform.system():
|
||||
tempFileDescriptor, tempFileName = tempfile.mkstemp()
|
||||
context = ssl._create_unverified_context()
|
||||
with urllib.request.urlopen(url, context=context) as response, open(tempFileDescriptor, 'wb') as tempFile:
|
||||
shutil.copyfileobj(response, tempFile)
|
||||
else:
|
||||
tempFileName, headers = urllib.request.urlretrieve(url)
|
||||
|
||||
# for some reason the hash we get back from the downloaded file is sometimes wrong if we check it right away
|
||||
# but if we examine the file later, it is correct.
|
||||
time.sleep(3)
|
||||
downloadHash = hashFile(tempFileName, hasher)
|
||||
# Verify the hash
|
||||
if hash is not None and hash != downloadHash:
|
||||
print("Try {}: Downloaded file {} hash {} does not match expected hash {} for url {}".format(i + 1, tempFileName, downloadHash, hash, url))
|
||||
os.remove(tempFileName)
|
||||
continue
|
||||
|
||||
return tempFileName
|
||||
|
||||
raise RuntimeError("Downloaded file hash {} does not match expected hash {} for\n{}".format(downloadHash, hash, url))
|
||||
|
||||
|
||||
def downloadAndExtract(url, destPath, hash=None, hasher=hashlib.sha512(), isZip=False):
|
||||
tempFileName = downloadFile(url, hash, hasher)
|
||||
if isZip:
|
||||
with zipfile.ZipFile(tempFileName) as zip:
|
||||
zip.extractall(destPath)
|
||||
else:
|
||||
# Extract the archive
|
||||
with tarfile.open(tempFileName, 'r:gz') as tgz:
|
||||
tgz.extractall(destPath)
|
||||
os.remove(tempFileName)
|
216
hifi_vcpkg.py
Normal file
|
@ -0,0 +1,216 @@
|
|||
import hifi_utils
|
||||
import hifi_android
|
||||
import hashlib
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import json
|
||||
import xml.etree.ElementTree as ET
|
||||
import functools
|
||||
|
||||
print = functools.partial(print, flush=True)
|
||||
|
||||
# Encapsulates the vcpkg system
|
||||
class VcpkgRepo:
|
||||
CMAKE_TEMPLATE = """
|
||||
set(CMAKE_TOOLCHAIN_FILE "{}" CACHE FILEPATH "Toolchain file")
|
||||
set(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}")
|
||||
set(VCPKG_INSTALL_ROOT "{}")
|
||||
set(VCPKG_TOOLS_DIR "{}")
|
||||
"""
|
||||
|
||||
CMAKE_TEMPLATE_NON_ANDROID = """
|
||||
# If the cached cmake toolchain path is different from the computed one, exit
|
||||
if(NOT (CMAKE_TOOLCHAIN_FILE_UNCACHED STREQUAL CMAKE_TOOLCHAIN_FILE))
|
||||
message(FATAL_ERROR "CMAKE_TOOLCHAIN_FILE has changed, please wipe the build directory and rerun cmake")
|
||||
endif()
|
||||
"""
|
||||
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
# our custom ports, relative to the script location
|
||||
self.sourcePortsPath = args.ports_path
|
||||
self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8]
|
||||
self.configFilePath = os.path.join(args.build_root, 'vcpkg.cmake')
|
||||
|
||||
# OS dependent information
|
||||
system = platform.system()
|
||||
|
||||
if self.args.vcpkg_root is not None:
|
||||
self.path = args.vcpkg_root
|
||||
else:
|
||||
if 'Darwin' == system:
|
||||
defaultBasePath = os.path.expanduser('~/hifi/vcpkg')
|
||||
else:
|
||||
defaultBasePath = os.path.join(tempfile.gettempdir(), 'hifi', 'vcpkg')
|
||||
self.basePath = os.getenv('HIFI_VCPKG_BASE', defaultBasePath)
|
||||
if self.basePath == defaultBasePath:
|
||||
print("Warning: Environment variable HIFI_VCPKG_BASE not set, using {}".format(defaultBasePath))
|
||||
if self.args.android:
|
||||
self.basePath = os.path.join(self.basePath, 'android')
|
||||
if (not os.path.isdir(self.basePath)):
|
||||
os.makedirs(self.basePath)
|
||||
self.path = os.path.join(self.basePath, self.id)
|
||||
|
||||
print("Using vcpkg path {}".format(self.path))
|
||||
lockDir, lockName = os.path.split(self.path)
|
||||
lockName += '.lock'
|
||||
if not os.path.isdir(lockDir):
|
||||
os.makedirs(lockDir)
|
||||
|
||||
self.lockFile = os.path.join(lockDir, lockName)
|
||||
self.tagFile = os.path.join(self.path, '.id')
|
||||
# A format version attached to the tag file... increment when you want to force the build systems to rebuild
|
||||
# without the contents of the ports changing
|
||||
self.version = 1
|
||||
self.tagContents = "{}_{}".format(self.id, self.version)
|
||||
|
||||
if 'Windows' == system:
|
||||
self.exe = os.path.join(self.path, 'vcpkg.exe')
|
||||
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-win32.tar.gz?versionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ'
|
||||
self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e'
|
||||
self.hostTriplet = 'x64-windows'
|
||||
elif 'Darwin' == system:
|
||||
self.exe = os.path.join(self.path, 'vcpkg')
|
||||
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-osx.tar.gz?versionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc'
|
||||
self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d'
|
||||
self.hostTriplet = 'x64-osx'
|
||||
else:
|
||||
self.exe = os.path.join(self.path, 'vcpkg')
|
||||
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-linux.tar.gz?versionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU'
|
||||
self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d'
|
||||
self.hostTriplet = 'x64-linux'
|
||||
|
||||
if self.args.android:
|
||||
self.triplet = 'arm64-android'
|
||||
self.androidPackagePath = os.path.join(self.path, 'android')
|
||||
else:
|
||||
self.triplet = self.hostTriplet
|
||||
|
||||
def upToDate(self):
|
||||
# Prevent doing a clean if we've explcitly set a directory for vcpkg
|
||||
if self.args.vcpkg_root is not None:
|
||||
return True
|
||||
|
||||
if self.args.force_build:
|
||||
print("Force build, out of date")
|
||||
return False
|
||||
if not os.path.isfile(self.exe):
|
||||
print("Exe file {} not found, out of date".format(self.exe))
|
||||
return False
|
||||
if not os.path.isfile(self.tagFile):
|
||||
print("Tag file {} not found, out of date".format(self.tagFile))
|
||||
return False
|
||||
with open(self.tagFile, 'r') as f:
|
||||
storedTag = f.read()
|
||||
if storedTag != self.tagContents:
|
||||
print("Tag file {} contents don't match computed tag {}, out of date".format(self.tagFile, self.tagContents))
|
||||
return False
|
||||
return True
|
||||
|
||||
def clean(self):
|
||||
print("Cleaning vcpkg installation at {}".format(self.path))
|
||||
if os.path.isdir(self.path):
|
||||
print("Removing {}".format(self.path))
|
||||
shutil.rmtree(self.path, ignore_errors=True)
|
||||
|
||||
# Make sure the VCPKG prerequisites are all there.
|
||||
def bootstrap(self):
|
||||
if self.upToDate():
|
||||
return
|
||||
|
||||
self.clean()
|
||||
|
||||
downloadVcpkg = False
|
||||
if self.args.force_bootstrap:
|
||||
print("Forcing bootstrap")
|
||||
downloadVcpkg = True
|
||||
|
||||
if not downloadVcpkg and not os.path.isfile(self.exe):
|
||||
print("Missing executable, boostrapping")
|
||||
downloadVcpkg = True
|
||||
|
||||
# Make sure we have a vcpkg executable
|
||||
testFile = os.path.join(self.path, '.vcpkg-root')
|
||||
if not downloadVcpkg and not os.path.isfile(testFile):
|
||||
print("Missing {}, bootstrapping".format(testFile))
|
||||
downloadVcpkg = True
|
||||
|
||||
if downloadVcpkg:
|
||||
print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path))
|
||||
hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash)
|
||||
|
||||
print("Replacing port files")
|
||||
portsPath = os.path.join(self.path, 'ports')
|
||||
if (os.path.islink(portsPath)):
|
||||
os.unlink(portsPath)
|
||||
if (os.path.isdir(portsPath)):
|
||||
shutil.rmtree(portsPath, ignore_errors=True)
|
||||
shutil.copytree(self.sourcePortsPath, portsPath)
|
||||
|
||||
def run(self, commands):
|
||||
actualCommands = [self.exe, '--vcpkg-root', self.path]
|
||||
actualCommands.extend(commands)
|
||||
print("Running command")
|
||||
print(actualCommands)
|
||||
hifi_utils.executeSubprocess(actualCommands, folder=self.path)
|
||||
|
||||
def setupDependencies(self):
|
||||
# Special case for android, grab a bunch of binaries
|
||||
# FIXME remove special casing for android builds eventually
|
||||
if self.args.android:
|
||||
print("Installing Android binaries")
|
||||
self.setupAndroidDependencies()
|
||||
|
||||
print("Installing host tools")
|
||||
self.run(['install', '--triplet', self.hostTriplet, 'hifi-host-tools'])
|
||||
|
||||
# If not android, install the hifi-client-deps libraries
|
||||
if not self.args.android:
|
||||
print("Installing build dependencies")
|
||||
self.run(['install', '--triplet', self.triplet, 'hifi-client-deps'])
|
||||
|
||||
def cleanBuilds(self):
|
||||
# Remove temporary build artifacts
|
||||
builddir = os.path.join(self.path, 'buildtrees')
|
||||
if os.path.isdir(builddir):
|
||||
print("Wiping build trees")
|
||||
shutil.rmtree(builddir, ignore_errors=True)
|
||||
|
||||
def setupAndroidDependencies(self):
|
||||
# vcpkg prebuilt
|
||||
if not os.path.isdir(os.path.join(self.path, 'installed', 'arm64-android')):
|
||||
dest = os.path.join(self.path, 'installed')
|
||||
url = "https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-arm64-android.tar.gz"
|
||||
# FIXME I don't know why the hash check frequently fails here. If you examine the file later it has the right hash
|
||||
#hash = "832f82a4d090046bdec25d313e20f56ead45b54dd06eee3798c5c8cbdd64cce4067692b1c3f26a89afe6ff9917c10e4b601c118bea06d23f8adbfe5c0ec12bc3"
|
||||
#hifi_utils.downloadAndExtract(url, dest, hash)
|
||||
hifi_utils.downloadAndExtract(url, dest)
|
||||
|
||||
def writeTag(self):
|
||||
print("Writing tag {} to {}".format(self.tagContents, self.tagFile))
|
||||
with open(self.tagFile, 'w') as f:
|
||||
f.write(self.tagContents)
|
||||
|
||||
def writeConfig(self):
|
||||
print("Writing cmake config to {}".format(self.configFilePath))
|
||||
# Write out the configuration for use by CMake
|
||||
cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake')
|
||||
installPath = os.path.join(self.path, 'installed', self.triplet)
|
||||
toolsPath = os.path.join(self.path, 'installed', self.hostTriplet, 'tools')
|
||||
cmakeTemplate = VcpkgRepo.CMAKE_TEMPLATE
|
||||
if not self.args.android:
|
||||
cmakeTemplate += VcpkgRepo.CMAKE_TEMPLATE_NON_ANDROID
|
||||
cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath).replace('\\', '/')
|
||||
with open(self.configFilePath, 'w') as f:
|
||||
f.write(cmakeConfig)
|
||||
|
||||
def cleanOldBuilds(self):
|
||||
# FIXME because we have the base directory, and because a build will
|
||||
# update the tag file on every run, we can scan the base dir for sub directories containing
|
||||
# a tag file that is older than N days, and if found, delete the directory, recovering space
|
||||
print("Not implemented")
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ endif()
|
|||
link_hifi_libraries(
|
||||
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
||||
pointers
|
||||
recording hfm fbx networking model-networking entities avatars trackers
|
||||
recording hfm fbx networking model-networking model-baker entities avatars trackers
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||
controllers plugins image trackers
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
{
|
||||
"backPlate": {
|
||||
"dimensions": {
|
||||
"x": 0.723600000888109207,
|
||||
"y": 0.022600000724196434,
|
||||
"z": 0.2474999976158142
|
||||
},
|
||||
"position": {
|
||||
"x": -0.3292800903320312,
|
||||
"y": 0.004300000742077827,
|
||||
"z": -0.055427663803100586
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1.000,
|
||||
"x": 0.000,
|
||||
"y": 0.000,
|
||||
"z": 0.000
|
||||
}
|
||||
},
|
||||
"anchor": {
|
||||
"dimensions": {
|
||||
"x": 0.023600000888109207,
|
||||
|
|
|
@ -1,16 +1,2 @@
|
|||
{
|
||||
"RenderMainView": {
|
||||
"RenderShadowTask": {
|
||||
"Enabled": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"RenderDeferredTask": {
|
||||
"AmbientOcclusion": {
|
||||
"Enabled": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
78
interface/resources/controllers/standard_nomovement.json
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name": "Standard to Action (No Movement)",
|
||||
"channels": [
|
||||
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
|
||||
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
|
||||
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
|
||||
{ "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"},
|
||||
{ "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"},
|
||||
{ "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"},
|
||||
{ "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"},
|
||||
{ "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"},
|
||||
{ "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"},
|
||||
{ "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"},
|
||||
{ "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"},
|
||||
{ "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"},
|
||||
{ "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"},
|
||||
{ "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"},
|
||||
{ "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"},
|
||||
{ "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"},
|
||||
{ "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"},
|
||||
{ "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"},
|
||||
{ "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"},
|
||||
{ "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"},
|
||||
{ "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"},
|
||||
{ "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"},
|
||||
{ "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"},
|
||||
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RightHand" },
|
||||
{ "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"},
|
||||
{ "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"},
|
||||
{ "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"},
|
||||
{ "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"},
|
||||
{ "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"},
|
||||
{ "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"},
|
||||
{ "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"},
|
||||
{ "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"},
|
||||
{ "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"},
|
||||
{ "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"},
|
||||
{ "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"},
|
||||
{ "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"},
|
||||
{ "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"},
|
||||
{ "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"},
|
||||
{ "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"},
|
||||
{ "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"},
|
||||
{ "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"},
|
||||
{ "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"},
|
||||
{ "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"},
|
||||
{ "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"},
|
||||
|
||||
{ "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" },
|
||||
{ "from": "Standard.RightFoot", "to": "Actions.RightFoot" },
|
||||
|
||||
{ "from": "Standard.Hips", "to": "Actions.Hips" },
|
||||
{ "from": "Standard.Spine2", "to": "Actions.Spine2" },
|
||||
|
||||
{ "from": "Standard.Head", "to": "Actions.Head" },
|
||||
{ "from": "Standard.LeftArm", "to": "Actions.LeftArm" },
|
||||
{ "from": "Standard.RightArm", "to": "Actions.RightArm" },
|
||||
|
||||
{ "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" },
|
||||
{ "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" },
|
||||
{ "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" },
|
||||
{ "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" },
|
||||
{ "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" },
|
||||
{ "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" },
|
||||
{ "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" },
|
||||
{ "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" },
|
||||
{ "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" },
|
||||
{ "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" },
|
||||
{ "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" },
|
||||
{ "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" },
|
||||
{ "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" },
|
||||
{ "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" },
|
||||
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
|
||||
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
|
||||
]
|
||||
}
|
|
@ -154,4 +154,6 @@
|
|||
<glyph glyph-name="40-reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M396 344l-2 2c-4 4-9 5-15 5-1 0-88-13-124-14-1 0-2 0-3 0-37 0-126 15-127 15-7 1-14-2-18-8l-2-4c-3-4-3-9-2-14 2-5 5-9 10-11 16-7 69-22 85-29 3-1 10-4 10-14 1-11-4-67-10-93-7-26-18-60-19-60-3-9 2-19 11-22l11-4c4-2 9-1 13 1 5 2 8 6 9 10l31 94 28-96c2-5 5-9 9-11 3-1 5-2 8-2 2 0 4 0 7 1l10 4c8 3 12 12 10 20 0 1-8 36-16 65-4 17-6 43-7 64-1 13-1 21-3 30 0 1 2 11 10 14 10 4 81 29 80 28 6 2 10 7 11 13 1 6-1 12-5 16z m-98 54c0-24-19-43-43-43-24 0-43 19-43 43 0 23 19 42 43 42 24 0 43-19 43-42z"/>
|
||||
<glyph glyph-name="steam-square" unicode="" d="M391 327c0 15-5 28-16 39-11 11-24 16-39 16-15 0-28-5-39-16-11-11-16-24-16-39 0-15 5-28 16-39 11-11 24-16 39-16 15 0 28 5 39 16 11 11 16 24 16 39z m-174-168c0-16-5-29-16-40-11-11-25-16-40-16-11 0-21 2-30 8-9 5-16 13-20 22 9-4 19-8 28-12 11-4 22-4 34 1 11 5 19 13 24 25 5 11 5 22 0 34-5 11-13 19-25 24l-23 9c4 1 8 2 12 2 15 0 29-6 40-17 11-11 16-24 16-40z m258 234l0-274c0-23-8-42-24-58-16-16-35-24-58-24l-274 0c-23 0-42 8-58 24-16 16-24 35-24 58l0 44 49-20c4-18 12-32 26-44 14-11 30-17 49-17 19 0 37 7 51 20 15 14 23 30 25 50l99 72c28 0 53 10 73 30 20 20 30 44 30 73 0 28-10 52-30 73-20 20-45 30-73 30-28 0-53-10-73-30-20-20-30-44-30-72l-64-92c-2 0-5 0-8 0-15 0-28-4-40-11l-84 34 0 134c0 23 8 42 24 58 16 16 35 24 58 24l274 0c23 0 42-8 58-24 16-16 24-35 24-58z m-70-66c0-19-7-36-20-49-14-14-30-20-49-20-19 0-36 6-49 20-13 13-20 30-20 49 0 19 7 35 20 48 13 14 30 21 49 21 19 0 35-7 49-20 13-14 20-30 20-49z"/>
|
||||
<glyph glyph-name="oculus" unicode="" d="M431 363c-16 13-34 22-54 26-11 3-23 4-34 5-9 0-18 0-26 0-41 0-82 0-123 0-9 0-18 0-26 0-12-1-24-2-35-5-20-4-38-13-54-26-32-26-51-65-51-106 0-41 19-80 51-106 16-13 34-22 54-27 12-2 23-3 35-4 8 0 17 0 26 0 41 0 82 0 123 0 8 0 17 0 26 0 11 0 23 2 34 4 20 5 38 14 54 27 32 26 51 65 51 106 0 41-19 80-51 106z m-60-143c-6-4-13-7-20-8-7-1-14-1-22-1-49 0-99 0-148 0-8 0-15 0-22 1-7 1-14 4-20 8-12 8-19 22-19 37 0 15 7 29 19 37 6 4 13 7 20 8 7 1 14 1 22 1 49 0 99 0 148 0 8 0 15 0 22-1 7-1 14-4 20-8 12-8 19-22 19-37 0-15-7-29-19-37z"/>
|
||||
</font></defs></svg>
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
|
@ -603,6 +603,14 @@
|
|||
<div class="icon icon-avatar-1"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-1">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-steam-square"></div>
|
||||
<input type="text" readonly="readonly" value="steam-square">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-oculus"></div>
|
||||
<input type="text" readonly="readonly" value="oculus">
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Character mapping</h2>
|
||||
<ul class="glyphs character-mapping">
|
||||
|
@ -1194,6 +1202,14 @@
|
|||
<div data-icon="T" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="T">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe035;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe036;">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>(function() {
|
|
@ -479,3 +479,9 @@
|
|||
.icon-avatar-1:before {
|
||||
content: "\54";
|
||||
}
|
||||
.icon-steam-square:before {
|
||||
content: "\e035";
|
||||
}
|
||||
.icon-oculus:before {
|
||||
content: "\e036";
|
||||
}
|
4
interface/resources/icons/steam.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.4866 0C4.92045 0 0.367395 4.32104 0 9.78734L5.45262 11.9841C5.93184 11.651 6.51333 11.4545 7.14145 11.4545C7.27641 11.4545 7.4083 11.4666 7.53829 11.4841L10.1047 7.72495C10.1083 5.56672 11.861 3.81818 14.0229 3.81818C16.1872 3.81818 17.9416 5.57035 17.9416 7.73182C17.9416 9.89329 16.1872 11.6455 14.0229 11.6455C14.021 11.6455 14.0189 11.6453 14.017 11.6453L10.0936 14.2008C10.0986 14.2712 10.1043 14.3419 10.1043 14.4136C10.1043 16.048 8.77791 17.3727 7.14145 17.3727C5.69539 17.3727 4.49304 16.3376 4.2325 14.969L0.378099 13.3841C1.6334 17.7801 5.6822 21 10.4866 21C16.2931 21 21 16.2991 21 10.5C21 4.70114 16.2931 0 10.4866 0ZM7.14145 16.0364C6.96655 16.0364 6.79833 16.0081 6.64044 15.9569L6.63968 15.9589L6.59151 15.939C6.54506 15.9224 6.49975 15.9037 6.45541 15.8831L5.15462 15.3483C5.50614 16.0927 6.26253 16.6091 7.14145 16.6091C8.35546 16.6091 9.33971 15.6263 9.33971 14.4136C9.33971 13.201 8.35546 12.2182 7.14145 12.2182C6.87269 12.2182 6.61636 12.2688 6.37818 12.357L7.75448 12.9114C7.76404 12.9154 7.77359 12.9188 7.78296 12.923L7.89001 12.9662L7.88714 12.9732C8.40898 13.243 8.76625 13.7861 8.76625 14.4136C8.76625 15.3098 8.03872 16.0364 7.14145 16.0364ZM16.7946 7.73182C16.7946 6.20302 15.5537 4.96364 14.0229 4.96364C12.4922 4.96364 11.2512 6.20302 11.2512 7.73182C11.2512 9.26062 12.4922 10.5 14.0229 10.5C15.5537 10.5 16.7946 9.26062 16.7946 7.73182ZM12.0158 7.73182C12.0158 6.62474 12.9144 5.72727 14.0229 5.72727C15.1314 5.72727 16.03 6.62474 16.03 7.73182C16.03 8.8389 15.1314 9.73636 14.0229 9.73636C12.9144 9.73636 12.0158 8.8389 12.0158 7.73182Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
17
interface/resources/images/high-fidelity-banner.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<svg width="360" height="85" viewBox="0 0 360 85" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M124 30V59H117.974V46.3455H108.026V59H102V30H108.026V41.1899H117.974V30H124Z" fill="#00B4F0"/>
|
||||
<path d="M135 59H129V30H135V59Z" fill="#00B4F0"/>
|
||||
<path d="M151.829 34.9471C150.406 34.9471 149.211 35.0608 148.187 35.3451C147.22 35.6294 146.423 36.0843 145.797 36.7098C145.171 37.3353 144.772 38.2451 144.488 39.3255C144.203 40.4059 144.089 41.7706 144.089 43.3627V45.9216C144.089 47.6275 144.203 48.9922 144.431 50.0726C144.659 51.1529 145.057 52.0059 145.626 52.5745C146.138 53.2 146.878 53.598 147.732 53.8255C148.585 54.0529 149.667 54.1667 150.919 54.1667C151.26 54.1667 151.659 54.1667 152.057 54.1098C152.455 54.1098 152.854 54.0529 153.309 53.9961V46.3765H149.78L150.35 41.7137H159V57.749C157.919 58.1471 156.553 58.4314 154.902 58.6588C153.309 58.8863 151.602 59 149.894 59C147.732 59 145.911 58.7157 144.431 58.2039C142.894 57.6922 141.699 56.8961 140.732 55.8157C139.764 54.7353 139.081 53.4275 138.626 51.7784C138.171 50.1863 138 48.2529 138 46.0922V43.1922C138 41.202 138.228 39.4392 138.626 37.7902C139.081 36.1412 139.821 34.7765 140.846 33.6392C141.87 32.502 143.236 31.5922 144.886 30.9667C146.537 30.3412 148.642 30 151.146 30C152.512 30 153.878 30.1137 155.187 30.3412C156.496 30.5686 157.577 30.8529 158.431 31.1373L157.463 35.6863C156.724 35.4588 155.87 35.2882 154.959 35.1176C154.049 35.0039 153.024 34.9471 151.829 34.9471Z" fill="#00B4F0"/>
|
||||
<path d="M187 30V59H180.959V46.3455H171.041V59H165V30H171.041V41.1899H181.016V30H187Z" fill="#00B4F0"/>
|
||||
<path d="M218 30L217.366 35.0384H207.597V41.0727H216.627L215.993 46.1111H207.597V59H202V30H218Z" fill="#00B4F0"/>
|
||||
<path d="M229 59H223V30H229V59Z" fill="#00B4F0"/>
|
||||
<path d="M233 30H242.404C244.842 30 246.873 30.3508 248.499 30.994C250.124 31.6371 251.401 32.5726 252.388 33.6835C253.375 34.8528 254.013 36.1976 254.42 37.8347C254.826 39.4718 255 41.2258 255 43.0968V45.8448C255 47.7742 254.826 49.5282 254.42 51.1653C254.013 52.8024 253.317 54.1472 252.388 55.3165C251.401 56.4859 250.124 57.3629 248.499 58.006C246.873 58.6492 244.842 59 242.404 59H233.058V30H233ZM239.095 53.9133H241.765C242.926 53.9133 243.913 53.7964 244.842 53.504C245.712 53.2117 246.467 52.744 247.047 52.1008C247.628 51.4577 248.034 50.5806 248.325 49.5282C248.615 48.4758 248.731 47.1311 248.731 45.494V43.3306C248.731 41.6935 248.615 40.3488 248.325 39.2964C248.034 38.2439 247.628 37.3669 247.047 36.7238C246.467 36.0806 245.77 35.6129 244.842 35.379C243.971 35.1452 242.926 34.9698 241.765 34.9698H239.095V53.9133Z" fill="#00B4F0"/>
|
||||
<path d="M275.945 30L275.34 34.8528H264.832V41.1673H274.735L274.184 46.0202H264.887V54.1472H276L275.395 59H259V30H275.945Z" fill="#00B4F0"/>
|
||||
<path d="M286.215 30V53.9616H298L297.296 59H280V30H286.215Z" fill="#00B4F0"/>
|
||||
<path d="M307 59H302V30H307V59Z" fill="#00B4F0"/>
|
||||
<path d="M334 30L333.307 35.0384H325.563V59H319.437V35.0384H311L311.693 30H334Z" fill="#00B4F0"/>
|
||||
<path d="M347.617 41.3071L353.633 30H360L350.537 46.3455V59H344.346V46.3455L335 30H341.776L347.617 41.3071Z" fill="#00B4F0"/>
|
||||
<path d="M42.1132 85C36.4528 85 30.9057 83.856 25.7547 81.6824C20.717 79.5659 16.2453 76.4771 12.3962 72.5875C8.54717 68.6978 5.49057 64.1218 3.39623 59.0882C1.13208 53.7685 -1.07963e-07 48.22 -1.07963e-07 42.4428C-1.07963e-07 36.7227 1.13208 31.1171 3.28302 25.9118C5.37736 20.821 8.43396 16.3022 12.283 12.4125C16.1321 8.52288 20.6604 5.43405 25.6415 3.31763C30.8491 1.08681 36.3396 1.09101e-07 42 1.09101e-07C47.6604 1.09101e-07 53.2076 1.14401 58.3585 3.31763C63.3962 5.43405 67.8679 8.52288 71.717 12.4125C75.566 16.3022 78.6227 20.8782 80.717 25.9118C82.9245 31.1743 84 36.7227 84 42.4428C84 48.1628 82.8679 53.7685 80.717 58.9737C78.6227 64.0646 75.566 68.5834 71.717 72.4731C67.8679 76.3627 63.3396 79.4515 58.3585 81.568C53.3208 83.856 47.7736 85 42.1132 85ZM42.1132 4.34724C21.3396 4.34724 4.41509 21.4502 4.41509 42.4428C4.41509 63.4354 21.3396 80.5384 42.1132 80.5384C62.8868 80.5384 79.8113 63.4354 79.8113 42.4428C79.8113 21.4502 62.8868 4.34724 42.1132 4.34724Z" fill="#00B4F0"/>
|
||||
<path d="M54.8 60.7368V30.5158C56.6857 29.8789 58 28.0842 58 26C58 23.3947 55.8857 21.2526 53.3143 21.2526C50.7429 21.2526 48.6286 23.3947 48.6286 26C48.6286 28.0263 49.8286 29.7053 51.6 30.4579V44.5263L32.1714 35.379V24.2632C34.0571 23.6263 35.3714 21.8316 35.3714 19.7474C35.3714 17.1421 33.2571 15 30.6857 15C28.1143 15 26 17.1421 26 19.7474C26 21.7737 27.2 23.4526 28.9714 24.2053V54.6C27.2571 55.2947 26 57.0316 26 59.0579C26 61.6632 28.1143 63.8053 30.6857 63.8053C33.2571 63.8053 35.3714 61.6632 35.3714 59.0579C35.3714 56.9737 34.0571 55.1789 32.1714 54.5421V39.2L51.6 48.3474V60.7947C49.8857 61.4895 48.6286 63.2263 48.6286 65.2526C48.6286 67.8579 50.7429 70 53.3143 70C55.8857 70 58 67.8579 58 65.2526C58 63.1105 56.6857 61.3737 54.8 60.7368Z" fill="#00B4F0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
|
@ -54,7 +54,7 @@ Windows.Window {
|
|||
onSourceChanged: {
|
||||
if (dynamicContent) {
|
||||
dynamicContent.destroy();
|
||||
dynamicContent = null;
|
||||
dynamicContent = null;
|
||||
}
|
||||
QmlSurface.load(source, contentHolder, function(newObject) {
|
||||
dynamicContent = newObject;
|
||||
|
@ -117,7 +117,7 @@ Windows.Window {
|
|||
console.error("presentationMode should be set.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
// Fix for parent loss on OSX:
|
||||
parent.heightChanged.connect(updateContentParent);
|
||||
|
@ -215,7 +215,7 @@ Windows.Window {
|
|||
nativeWindow.raise();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle message traffic from our loaded QML to the script that launched us
|
||||
signal sendToScript(var message);
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
//
|
||||
// Created by David Rowe on 3 Jun 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
@ -11,32 +10,34 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import "windows"
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
|
||||
import "LoginDialog"
|
||||
|
||||
ModalWindow {
|
||||
FocusScope {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
HifiStylesUit.HifiConstants { id: hifi }
|
||||
objectName: "LoginDialog"
|
||||
implicitWidth: 520
|
||||
implicitHeight: 320
|
||||
closeButtonVisible: true
|
||||
destroyOnCloseButton: true
|
||||
destroyOnHidden: true
|
||||
visible: true
|
||||
property bool shown: true
|
||||
visible: shown
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property bool isTablet: false
|
||||
readonly property bool isOverlay: false
|
||||
|
||||
property string iconText: ""
|
||||
property int iconSize: 50
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isPassword: false
|
||||
property string title: ""
|
||||
property string text: ""
|
||||
property int titleWidth: 0
|
||||
|
||||
keyboardOverride: true // Disable ModalWindow's keyboard.
|
||||
property alias bannerWidth: banner.width
|
||||
property alias bannerHeight: banner.height
|
||||
|
||||
function tryDestroy() {
|
||||
root.destroy()
|
||||
|
@ -47,7 +48,39 @@ ModalWindow {
|
|||
|
||||
Loader {
|
||||
id: bodyLoader
|
||||
source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml"
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: -6
|
||||
id: opaqueRect
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
opacity: 0.65
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Item {
|
||||
z: -5
|
||||
id: bannerContainer
|
||||
width: parent.width
|
||||
height: banner.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 0.18 * parent.height
|
||||
}
|
||||
Image {
|
||||
id: banner
|
||||
anchors.centerIn: parent
|
||||
source: "../images/high-fidelity-banner.svg"
|
||||
horizontalAlignment: Image.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +109,6 @@ ModalWindow {
|
|||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
event.accepted = true
|
||||
destroy()
|
||||
break
|
||||
|
||||
case Qt.Key_Enter:
|
||||
|
@ -85,4 +117,8 @@ ModalWindow {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,109 +12,240 @@ import Hifi 1.0
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls.Styles 1.4 as OriginalStyles
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Item {
|
||||
id: completeProfileBody
|
||||
clip: true
|
||||
width: root.pane.width
|
||||
height: root.pane.height
|
||||
width: root.width
|
||||
height: root.height
|
||||
readonly property string termsContainerText: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
|
||||
readonly property string fontFamily: "Raleway"
|
||||
readonly property int fontSize: 15
|
||||
readonly property bool fontBold: true
|
||||
|
||||
readonly property bool withSteam: withSteam
|
||||
property string errorString: errorString
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minWidthButton: !root.isTablet ? 256 : 174
|
||||
property int maxWidth: root.width
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
readonly property int minHeightButton: 36
|
||||
property int maxHeight: root.height
|
||||
|
||||
function resize() {
|
||||
maxWidth = root.width;
|
||||
maxHeight = root.height;
|
||||
if (root.isTablet === false) {
|
||||
var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth,
|
||||
termsContainer.contentWidth))
|
||||
var targetWidth = Math.max(Math.max(titleWidth, Math.max(additionalTextContainer.width,
|
||||
termsContainer.width)), mainContainer.width)
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||
}
|
||||
var targetHeight = 5 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height + termsContainer.height
|
||||
var targetHeight = Math.max(5 * hifi.dimensions.contentSpacing.y + d.minHeightButton + additionalTextContainer.height + termsContainer.height, d.maxHeight)
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
Item {
|
||||
id: mainContainer
|
||||
width: root.width
|
||||
height: root.height
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 200
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
|
||||
text: qsTr("Create your profile")
|
||||
color: hifi.buttons.blue
|
||||
Item {
|
||||
id: errorContainer
|
||||
width: parent.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: buttons.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: buttons.left;
|
||||
}
|
||||
TextMetrics {
|
||||
id: loginErrorMessageTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: loginErrorMessage.text
|
||||
}
|
||||
Text {
|
||||
id: loginErrorMessage;
|
||||
width: root.bannerWidth
|
||||
color: "red";
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: completeProfileBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: completeProfileBody.errorString
|
||||
visible: true
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (loginErrorMessageTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = 3 * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: loginDialog.createAccountFromStream()
|
||||
Item {
|
||||
id: buttons
|
||||
width: root.bannerWidth
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: (parent.height - additionalTextContainer.height) / 2 - hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - root.bannerWidth) / 2
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
|
||||
text: qsTr("CANCEL")
|
||||
color: hifi.buttons.noneBorderlessWhite
|
||||
|
||||
fontFamily: completeProfileBody.fontFamily
|
||||
fontSize: completeProfileBody.fontSize
|
||||
fontBold: completeProfileBody.fontBold
|
||||
onClicked: {
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: profileButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
|
||||
text: qsTr("Create your profile")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
fontFamily: completeProfileBody.fontFamily
|
||||
fontSize: completeProfileBody.fontSize
|
||||
fontBold: completeProfileBody.fontBold
|
||||
onClicked: {
|
||||
loginErrorMessage.visible = false;
|
||||
loginDialog.createAccountFromSteam();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: additionalTextContainer
|
||||
width: parent.width
|
||||
height: additionalTextMetrics.height
|
||||
anchors {
|
||||
top: buttons.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: additionalTextMetrics
|
||||
font: additionalText.font
|
||||
text: "Already have a High Fidelity profile? Link to an existing profile here."
|
||||
}
|
||||
|
||||
HifiStylesUit.ShortcutText {
|
||||
id: additionalText
|
||||
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
|
||||
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: completeProfileBody.fontSize
|
||||
font.bold: completeProfileBody.fontBold
|
||||
wrapMode: Text.NoWrap
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
|
||||
onLinkActivated: {
|
||||
loginDialog.isLogIn = true;
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "", "withSteam": true, "linkSteam": true });
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (additionalTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
additionalText.width = root.bannerWidth;
|
||||
additionalText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
additionalTextContainer.height = (additionalTextMetrics.width / root.bannerWidth) * additionalTextMetrics.height;
|
||||
additionalTextContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
additionalText.anchors.centerIn = additionalTextContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: termsContainer
|
||||
width: parent.width
|
||||
height: termsTextMetrics.height
|
||||
anchors {
|
||||
top: additionalTextContainer.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
TextMetrics {
|
||||
id: termsTextMetrics
|
||||
font: termsText.font
|
||||
text: completeProfileBody.termsContainerText
|
||||
Component.onCompleted: {
|
||||
// with the link.
|
||||
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.InfoItem {
|
||||
id: termsText
|
||||
text: completeProfileBody.termsContainerText
|
||||
font.family: completeProfileBody.fontFamily
|
||||
font.pixelSize: completeProfileBody.fontSize
|
||||
font.bold: completeProfileBody.fontBold
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.lightGray
|
||||
linkColor: hifi.colors.blueAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link);
|
||||
|
||||
Component.onCompleted: {
|
||||
if (termsTextMetrics.width > root.bannerWidth && root.isTablet) {
|
||||
termsText.width = root.bannerWidth;
|
||||
termsText.wrapMode = Text.WordWrap;
|
||||
additionalText.verticalAlignment = Text.AlignLeft;
|
||||
additionalText.horizontalAlignment = Text.AlignLeft;
|
||||
termsContainer.height = (termsTextMetrics.width / root.bannerWidth) * termsTextMetrics.height;
|
||||
termsContainer.anchors.left = buttons.left;
|
||||
} else {
|
||||
termsText.anchors.centerIn = termsContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Cancel")
|
||||
onClicked: root.tryDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutText {
|
||||
id: additionalTextContainer
|
||||
anchors {
|
||||
top: buttons.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
lineHeight: 2
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
onLinkActivated: {
|
||||
bodyLoader.source = "LinkAccountBody.qml"
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
}
|
||||
|
||||
InfoItem {
|
||||
id: termsContainer
|
||||
anchors {
|
||||
top: additionalTextContainer.bottom
|
||||
margins: 0
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
width: parent.width
|
||||
|
||||
text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
fontSizeMode: Text.HorizontalFit
|
||||
horizontalAlignment: Text.AlignVCenter
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = qsTr("Complete Your Profile")
|
||||
root.iconText = "<"
|
||||
d.resize();
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -122,26 +253,23 @@ Item {
|
|||
onHandleCreateCompleted: {
|
||||
console.log("Create Succeeded")
|
||||
|
||||
loginDialog.loginThroughSteam()
|
||||
loginDialog.loginThroughSteam();
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": true, "linkSteam": false });
|
||||
}
|
||||
onHandleCreateFailed: {
|
||||
console.log("Create Failed: " + error)
|
||||
console.log("Create Failed: " + error);
|
||||
|
||||
bodyLoader.source = "UsernameCollisionBody.qml"
|
||||
if (!root.isTablet) {
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
}
|
||||
onHandleLoginCompleted: {
|
||||
console.log("Login Succeeded")
|
||||
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader });
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//but rise Tablet's one instead for Tablet interface
|
||||
if (root.isTablet || root.isOverlay) {
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
}
|
||||
d.resize();
|
||||
root.text = "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,406 +13,547 @@ import QtQuick 2.7
|
|||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4 as OriginalStyles
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import "." as LoginDialog
|
||||
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Item {
|
||||
z: -2
|
||||
id: linkAccountBody
|
||||
clip: true
|
||||
height: root.pane.height
|
||||
width: root.pane.width
|
||||
property bool failAfterSignUp: false
|
||||
|
||||
function login() {
|
||||
flavorText.visible = false
|
||||
mainTextContainer.visible = false
|
||||
toggleLoading(true)
|
||||
loginDialog.login(usernameField.text, passwordField.text)
|
||||
}
|
||||
height: root.height
|
||||
width: root.width
|
||||
property int textFieldHeight: 31
|
||||
property string fontFamily: "Raleway"
|
||||
property int fontSize: 15
|
||||
property int textFieldFontSize: 18
|
||||
property bool fontBold: true
|
||||
readonly property var passwordImageRatio: 16 / 23
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
property bool withSteam: false
|
||||
property bool linkSteam: linkSteam
|
||||
property bool withOculus: false
|
||||
property string errorString: errorString
|
||||
property bool lostFocus: false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minWidthButton: !root.isTablet ? 256 : 174
|
||||
property int maxWidth: root.width
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
readonly property int minHeightButton: 36
|
||||
property int maxHeight: root.height
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + flavorText.height + mainTextContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y + form.height;
|
||||
|
||||
if (additionalInformation.visible) {
|
||||
targetWidth = Math.max(targetWidth, additionalInformation.width);
|
||||
targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height
|
||||
}
|
||||
maxWidth = root.width;
|
||||
maxHeight = root.height;
|
||||
var targetWidth = Math.max(titleWidth, mainContainer.width);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainContainer.height + 4 * hifi.dimensions.contentSpacing.y;
|
||||
|
||||
var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
if(!isNaN(newWidth)) {
|
||||
if (!isNaN(newWidth)) {
|
||||
parent.width = root.width = newWidth;
|
||||
}
|
||||
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLoading(isLoading) {
|
||||
linkAccountSpinner.visible = isLoading
|
||||
form.visible = !isLoading
|
||||
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
additionalInformation.visible = !isLoading
|
||||
}
|
||||
function login() {
|
||||
loginDialog.login(emailField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
id: linkAccountSpinner
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
function init() {
|
||||
// going to/from sign in/up dialog.
|
||||
loginDialog.isLogIn = true;
|
||||
loginErrorMessage.text = linkAccountBody.errorString;
|
||||
loginErrorMessage.visible = (linkAccountBody.errorString !== "");
|
||||
loginButton.text = !linkAccountBody.linkSteam ? "Log In" : "Link Account";
|
||||
loginButton.color = hifi.buttons.blue;
|
||||
emailField.placeholderText = "Username or Email";
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
if (linkAccountBody.linkSteam) {
|
||||
steamInfoText.anchors.top = passwordField.bottom;
|
||||
keepMeLoggedInCheckbox.anchors.top = steamInfoText.bottom;
|
||||
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
|
||||
loginButton.anchors.right = emailField.right;
|
||||
} else {
|
||||
loginButton.anchors.left = emailField.left;
|
||||
}
|
||||
|
||||
visible: false
|
||||
running: true
|
||||
|
||||
width: 48
|
||||
height: 48
|
||||
loginContainer.visible = true;
|
||||
}
|
||||
|
||||
ShortcutText {
|
||||
id: flavorText
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and get interesting things on the Marketplace!")
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
ShortcutText {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: flavorText.bottom
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
visible: false
|
||||
text: qsTr("Username or password incorrect.")
|
||||
height: flavorText.height - 20
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
id: form
|
||||
width: parent.width
|
||||
Item {
|
||||
z: 10
|
||||
id: mainContainer
|
||||
width: root.width
|
||||
height: root.height
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: 2 * hifi.dimensions.contentSpacing.y
|
||||
|
||||
TextField {
|
||||
id: usernameField
|
||||
text: Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
width: parent.width
|
||||
focus: true
|
||||
placeholderText: "Username or Email"
|
||||
activeFocusOnPress: true
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
ShortcutText {
|
||||
z: 10
|
||||
y: usernameField.height
|
||||
anchors {
|
||||
right: usernameField.right
|
||||
top: usernameField.bottom
|
||||
topMargin: 4
|
||||
}
|
||||
|
||||
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link)
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
}
|
||||
Component.onCompleted: {
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername;
|
||||
}
|
||||
LoginDialog.LoginDialogLightbox {
|
||||
id: lightboxPopup;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordField
|
||||
width: parent.width
|
||||
placeholderText: "Password"
|
||||
activeFocusOnPress: true
|
||||
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
ShortcutText {
|
||||
id: forgotPasswordShortcut
|
||||
y: passwordField.height
|
||||
z: 10
|
||||
anchors {
|
||||
right: passwordField.right
|
||||
top: passwordField.bottom
|
||||
topMargin: 4
|
||||
}
|
||||
|
||||
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
|
||||
onLinkActivated: loginDialog.openUrl(link)
|
||||
Item {
|
||||
id: loginContainer
|
||||
width: emailField.width
|
||||
height: errorContainer.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height + steamInfoTextMetrics.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: root.bannerHeight + 0.25 * parent.height
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - emailField.width) / 2
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
root.isPassword = true;
|
||||
Item {
|
||||
id: errorContainer
|
||||
width: loginErrorMessageTextMetrics.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: emailField.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: emailField.left;
|
||||
}
|
||||
TextMetrics {
|
||||
id: loginErrorMessageTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: loginErrorMessage.text
|
||||
}
|
||||
Text {
|
||||
id: loginErrorMessage;
|
||||
color: "red";
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ""
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: showPasswordHitbox
|
||||
z: 10
|
||||
x: passwordField.width - ((passwordField.height) * 31 / 23)
|
||||
width: parent.width - (parent.width - (parent.height * 31/16))
|
||||
height: parent.height
|
||||
HifiControlsUit.TextField {
|
||||
id: emailField
|
||||
width: root.bannerWidth
|
||||
height: linkAccountBody.textFieldHeight
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
topMargin: errorContainer.height
|
||||
}
|
||||
placeholderText: "Username or Email"
|
||||
activeFocusOnPress: true
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
passwordField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
passwordField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: passwordField
|
||||
width: root.bannerWidth
|
||||
height: linkAccountBody.textFieldHeight
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
placeholderText: "Password"
|
||||
activeFocusOnPress: true
|
||||
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
|
||||
anchors {
|
||||
top: emailField.bottom
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
Image {
|
||||
id: showPasswordImage
|
||||
width: passwordField.height * 16 / 23
|
||||
height: passwordField.height * 16 / 23
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
root.isPassword = focus;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: showPasswordContainer
|
||||
z: 10
|
||||
// width + image's rightMargin
|
||||
width: showPasswordImage.width + 8
|
||||
height: parent.height
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 8
|
||||
top: parent.top
|
||||
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
|
||||
bottom: parent.bottom
|
||||
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
|
||||
}
|
||||
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
|
||||
Image {
|
||||
id: showPasswordImage
|
||||
width: passwordField.height * passwordImageRatio
|
||||
height: passwordField.height * passwordImageRatio
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 8
|
||||
top: parent.top
|
||||
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
|
||||
bottom: parent.bottom
|
||||
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
|
||||
}
|
||||
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
emailField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
linkAccountBody.login();
|
||||
}
|
||||
}
|
||||
|
||||
InfoItem {
|
||||
id: additionalInformation
|
||||
|
||||
visible: loginDialog.isSteamRunning()
|
||||
|
||||
text: qsTr("Your steam account informations will not be exposed to other users.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
spacing: hifi.dimensions.contentSpacing.y*2
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
CheckBox {
|
||||
id: autoLogoutCheckbox
|
||||
checked: Settings.getValue("keepMeLoggedIn", false)
|
||||
text: "Keep me logged in"
|
||||
boxSize: 20;
|
||||
labelFontSize: 15
|
||||
color: hifi.colors.black
|
||||
HifiControlsUit.CheckBox {
|
||||
id: keepMeLoggedInCheckbox
|
||||
checked: Settings.getValue("keepMeLoggedIn", false);
|
||||
text: qsTr("Keep Me Logged In");
|
||||
boxSize: 18;
|
||||
labelFontFamily: linkAccountBody.fontFamily
|
||||
labelFontSize: 18;
|
||||
color: hifi.colors.white;
|
||||
anchors {
|
||||
top: passwordField.bottom;
|
||||
topMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: passwordField.left;
|
||||
}
|
||||
onCheckedChanged: {
|
||||
Settings.setValue("keepMeLoggedIn", checked);
|
||||
if (checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
Settings.setValue("keepMeLoggedIn", checked);
|
||||
Component.onCompleted: {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: linkAccountButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 200
|
||||
|
||||
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton
|
||||
width: (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
|
||||
height: d.minHeightButton
|
||||
text: qsTr("Cancel")
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
color: hifi.buttons.noneBorderlessWhite;
|
||||
visible: linkAccountBody.linkSteam
|
||||
anchors {
|
||||
top: keepMeLoggedInCheckbox.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
onClicked: {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
linkAccountBody.login();
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "errorString": "" });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: loginButton
|
||||
width: passwordField.width
|
||||
height: d.minHeightButton
|
||||
text: qsTr("Log In")
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
anchors {
|
||||
top: keepMeLoggedInCheckbox.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
onClicked: {
|
||||
linkAccountBody.login()
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: steamInfoTextMetrics
|
||||
font: steamInfoText.font
|
||||
text: steamInfoText.text
|
||||
}
|
||||
Text {
|
||||
id: steamInfoText
|
||||
width: root.bannerWidth
|
||||
visible: linkAccountBody.linkSteam
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: emailField.left
|
||||
}
|
||||
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
color: "white"
|
||||
text: qsTr("Your Steam account information will not be exposed to others.");
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Component.onCompleted: {
|
||||
if (steamInfoTextMetrics.width > root.bannerWidth) {
|
||||
steamInfoText.wrapMode = Text.WordWrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: cantAccessTextMetrics
|
||||
font: cantAccessText.font
|
||||
text: "Can't access your account?"
|
||||
}
|
||||
HifiStylesUit.ShortcutText {
|
||||
id: cantAccessText
|
||||
z: 10
|
||||
visible: !linkAccountBody.linkSteam
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: emailField.left
|
||||
}
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
|
||||
text: "<a href='https://highfidelity.com/users/password/new'> Can't access your account?</a>"
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
linkColor: hifi.colors.blueAccent
|
||||
onLinkActivated: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
loginDialog.openUrl(link);
|
||||
lightboxPopup.titleText = "Can't Access Account";
|
||||
lightboxPopup.bodyText = lightboxPopup.cantAccessBodyText;
|
||||
lightboxPopup.button2text = "CLOSE";
|
||||
lightboxPopup.button2method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
// bodyLoader.setSource("CantAccessBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: continueButton;
|
||||
width: emailField.width;
|
||||
height: d.minHeightButton
|
||||
color: hifi.buttons.none;
|
||||
anchors {
|
||||
top: cantAccessText.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: emailField.left
|
||||
}
|
||||
text: qsTr("CONTINUE WITH STEAM")
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
buttonGlyph: hifi.glyphs.steamSquare
|
||||
buttonGlyphSize: 24
|
||||
buttonGlyphRightMargin: 10
|
||||
onClicked: {
|
||||
// if (loginDialog.isOculusStoreRunning()) {
|
||||
// linkAccountBody.withOculus = true;
|
||||
// loginDialog.loginThroughSteam();
|
||||
// } else
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
linkAccountBody.withSteam = true;
|
||||
loginDialog.loginThroughSteam();
|
||||
}
|
||||
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
|
||||
"withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (linkAccountBody.linkSteam) {
|
||||
continueButton.visible = false;
|
||||
return;
|
||||
}
|
||||
// if (loginDialog.isOculusStoreRunning()) {
|
||||
// continueButton.text = qsTr("CONTINUE WITH OCULUS");
|
||||
// continueButton.buttonGlyph = hifi.glyphs.oculus;
|
||||
// } else
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
continueButton.text = qsTr("CONTINUE WITH STEAM");
|
||||
continueButton.buttonGlyph = hifi.glyphs.steamSquare;
|
||||
} else {
|
||||
continueButton.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: leftButton
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: hifi.dimensions.contentSpacing.y*2
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
RalewaySemiBold {
|
||||
size: hifi.fontSizes.inputLabel
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("New to High Fidelity?")
|
||||
Item {
|
||||
id: signUpContainer
|
||||
width: loginContainer.width
|
||||
height: signUpTextMetrics.height
|
||||
visible: !linkAccountBody.linkSteam
|
||||
anchors {
|
||||
left: loginContainer.left
|
||||
top: loginContainer.bottom
|
||||
topMargin: 0.15 * parent.height
|
||||
}
|
||||
TextMetrics {
|
||||
id: signUpTextMetrics
|
||||
font: signUpText.font
|
||||
text: signUpText.text
|
||||
}
|
||||
Text {
|
||||
id: signUpText
|
||||
text: qsTr("Don't have an account?")
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
lineHeight: 1
|
||||
color: "white"
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Sign Up")
|
||||
visible: !loginDialog.isSteamRunning()
|
||||
|
||||
onClicked: {
|
||||
bodyLoader.setSource("SignUpBody.qml")
|
||||
if (!root.isTablet) {
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
HifiStylesUit.ShortcutText {
|
||||
id: signUpShortcutText
|
||||
z: 10
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
anchors {
|
||||
left: signUpText.right
|
||||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
}
|
||||
|
||||
text: "<a href='https://highfidelity.com'>Sign Up</a>"
|
||||
|
||||
linkColor: hifi.colors.blueAccent
|
||||
onLinkActivated: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
bodyLoader.setSource("SignUpBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
|
||||
"errorString": "", "linkSteam": linkAccountBody.linkSteam });
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: dismissButtonTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: dismissButton.text
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: dismissButton
|
||||
width: dismissButtonTextMetrics.width
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: 3 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
color: hifi.buttons.noneBorderlessWhite
|
||||
text: qsTr("No thanks, take me in-world! >")
|
||||
fontCapitalization: Font.MixedCase
|
||||
fontFamily: linkAccountBody.fontFamily
|
||||
fontSize: linkAccountBody.fontSize
|
||||
fontBold: linkAccountBody.fontBold
|
||||
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam;
|
||||
onClicked: {
|
||||
if (loginDialog.getLoginDialogPoppedUp()) {
|
||||
console.log("[ENCOURAGELOGINDIALOG]: user dismissed login screen")
|
||||
var data = {
|
||||
"action": "user dismissed login screen"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
root.tryDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onFocusEnabled: {
|
||||
if (!linkAccountBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
emailField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
}
|
||||
onFocusDisabled: {
|
||||
linkAccountBody.lostFocus = !root.isTablet && !root.isOverlay;
|
||||
if (linkAccountBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
emailField.focus = false;
|
||||
passwordField.focus = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = qsTr("Sign Into High Fidelity")
|
||||
root.iconText = "<"
|
||||
|
||||
//dont rise local keyboard
|
||||
keyboardEnabled = !root.isTablet && HMD.active;
|
||||
//but rise Tablet's one instead for Tablet interface
|
||||
if (root.isTablet) {
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
}
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
root.text = "";
|
||||
d.resize();
|
||||
|
||||
if (failAfterSignUp) {
|
||||
mainTextContainer.text = "Account created successfully."
|
||||
flavorText.visible = true
|
||||
mainTextContainer.visible = true
|
||||
}
|
||||
|
||||
usernameField.forceActiveFocus();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleLoginCompleted: {
|
||||
console.log("Login Succeeded, linking steam account")
|
||||
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
|
||||
if (poppedUp) {
|
||||
console.log("[ENCOURAGELOGINDIALOG]: logging in")
|
||||
var data = {
|
||||
"action": "user logged in"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
Settings.setValue("loginDialogPoppedUp", false);
|
||||
}
|
||||
if (loginDialog.isSteamRunning()) {
|
||||
loginDialog.linkSteam()
|
||||
} else {
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
}
|
||||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
var poppedUp = Settings.getValue("loginDialogPoppedUp", false);
|
||||
if (poppedUp) {
|
||||
console.log("[ENCOURAGELOGINDIALOG]: failed logging in")
|
||||
var data = {
|
||||
"action": "user failed logging in"
|
||||
};
|
||||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
Settings.setValue("loginDialogPoppedUp", false);
|
||||
}
|
||||
flavorText.visible = true
|
||||
mainTextContainer.visible = true
|
||||
toggleLoading(false)
|
||||
}
|
||||
onHandleLinkCompleted: {
|
||||
console.log("Link Succeeded")
|
||||
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
onHandleLinkFailed: {
|
||||
console.log("Link Failed")
|
||||
toggleLoading(false)
|
||||
}
|
||||
init();
|
||||
Qt.callLater(function() {
|
||||
emailField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
linkAccountBody.login()
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
272
interface/resources/qml/LoginDialog/LoggingInBody.qml
Normal file
|
@ -0,0 +1,272 @@
|
|||
//
|
||||
// LoggingInBody.qml
|
||||
//
|
||||
// Created by Wayne Chen on 10/18/18
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4 as OriginalStyles
|
||||
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
|
||||
Item {
|
||||
id: loggingInBody
|
||||
clip: true
|
||||
height: root.height
|
||||
width: root.width
|
||||
property int textFieldHeight: 31
|
||||
property int loggingInGlyphRightMargin: 10
|
||||
property string fontFamily: "Raleway"
|
||||
property int fontSize: 15
|
||||
property bool fontBold: true
|
||||
property bool withSteam: withSteam
|
||||
property bool withOculus: withOculus
|
||||
property bool linkSteam: linkSteam
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int minWidthButton: !root.isTablet ? 256 : 174
|
||||
property int maxWidth: root.width
|
||||
readonly property int minHeight: 120
|
||||
readonly property int minHeightButton: 36
|
||||
property int maxHeight: root.height
|
||||
|
||||
function resize() {
|
||||
maxWidth = root.width;
|
||||
maxHeight = root.height;
|
||||
var targetWidth = Math.max(titleWidth, mainContainer.width);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y;
|
||||
|
||||
var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
if (!isNaN(newWidth)) {
|
||||
parent.width = root.width = newWidth;
|
||||
}
|
||||
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
}
|
||||
|
||||
// timer to kill the dialog upon login success
|
||||
Timer {
|
||||
id: successTimer
|
||||
interval: 1000;
|
||||
running: false;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
if (loginDialog.getLoginDialogPoppedUp()) {
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
root.tryDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
// For the process of logging in.
|
||||
loggingInText.wrapMode = Text.NoWrap;
|
||||
|
||||
if (loggingInBody.linkSteam) {
|
||||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Linking to Steam";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
loginDialog.linkSteam();
|
||||
} else if (loggingInBody.withSteam) {
|
||||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Logging in to Steam";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
} else if (loggingInBody.withOculus) {
|
||||
loggingInGlyph.text = hifi.glyphs.oculus;
|
||||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Logging in to Oculus";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
} else {
|
||||
loggingInText.text = "Logging in";
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
}
|
||||
loggingInSpinner.visible = true;
|
||||
}
|
||||
function loadingSuccess() {
|
||||
loggingInSpinner.visible = false;
|
||||
if (loggingInBody.linkSteam) {
|
||||
loggingInText.text = "Linking to Steam";
|
||||
loginDialog.linkSteam();
|
||||
return;
|
||||
}
|
||||
if (loggingInBody.withSteam) {
|
||||
// reset the flag.
|
||||
loggingInGlyph.visible = false;
|
||||
loggingInText.text = "You are now logged into Steam!";
|
||||
loggingInText.x = 0;
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
loggedInGlyph.visible = true;
|
||||
} else if (loggingInBody.withOculus) {
|
||||
// reset the flag.
|
||||
loggingInGlyph.visible = false;
|
||||
loggingInText.text = "You are now logged into Oculus!";
|
||||
loggingInText.x = 0;
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
loggedInGlyph.text = hifi.glyphs.oculus;
|
||||
loggedInGlyph.visible = true;
|
||||
} else {
|
||||
loggingInText.text = "You are now logged in!";
|
||||
}
|
||||
successTimer.start();
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mainContainer
|
||||
anchors.fill: parent
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Item {
|
||||
id: loggingInContainer
|
||||
anchors.fill: parent
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Item {
|
||||
id: loggingInHeader
|
||||
width: parent.width
|
||||
height: loggingInGlyph.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 0.4 * parent.height
|
||||
left: parent.left
|
||||
}
|
||||
TextMetrics {
|
||||
id: loggingInGlyphTextMetrics;
|
||||
font: loggingInGlyph.font;
|
||||
text: loggingInGlyph.text;
|
||||
}
|
||||
HifiStylesUit.HiFiGlyphs {
|
||||
id: loggingInGlyph;
|
||||
text: hifi.glyphs.steamSquare;
|
||||
// Color
|
||||
color: "white";
|
||||
// Size
|
||||
size: 36;
|
||||
// Anchors
|
||||
anchors.right: loggingInText.left;
|
||||
anchors.rightMargin: loggingInBody.loggingInGlyphRightMargin
|
||||
anchors.bottom: parent.bottom;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
visible: loggingInBody.withSteam || loggingInBody.withOculus;
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: loggingInTextMetrics;
|
||||
font: loggingInText.font;
|
||||
text: loggingInText.text;
|
||||
}
|
||||
Text {
|
||||
id: loggingInText;
|
||||
width: loggingInTextMetrics.width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "white";
|
||||
font.family: loggingInBody.fontFamily
|
||||
font.pixelSize: 24
|
||||
font.bold: loggingInBody.fontBold
|
||||
wrapMode: Text.NoWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "Logging in"
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: loggingInFooter
|
||||
width: parent.width
|
||||
height: 0.5 * parent.height
|
||||
anchors {
|
||||
top: loggingInHeader.bottom
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
AnimatedImage {
|
||||
id: loggingInSpinner
|
||||
source: "images/loader-snake-256.gif"
|
||||
width: 64
|
||||
height: width
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: (parent.width - width) / 2;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
TextMetrics {
|
||||
id: loggedInGlyphTextMetrics;
|
||||
font: loggedInGlyph.font;
|
||||
text: loggedInGlyph.text;
|
||||
}
|
||||
HifiStylesUit.HiFiGlyphs {
|
||||
id: loggedInGlyph;
|
||||
text: hifi.glyphs.steamSquare;
|
||||
// color
|
||||
color: "white"
|
||||
// Size
|
||||
size: 78;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: (parent.width - loggedInGlyph.size) / 2;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: hifi.dimensions.contentSpacing.y
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
visible: false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.resize();
|
||||
loggingInBody.init();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleLinkCompleted: {
|
||||
console.log("Link Succeeded");
|
||||
loggingInBody.linkSteam = false;
|
||||
loggingInBody.loadingSuccess();
|
||||
}
|
||||
onHandleLinkFailed: {
|
||||
console.log("Link Failed: " + error);
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": true, "errorString": error });
|
||||
}
|
||||
|
||||
onHandleLoginCompleted: {
|
||||
console.log("Login Succeeded");
|
||||
loggingInBody.loadingSuccess();
|
||||
}
|
||||
|
||||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
loggingInSpinner.visible = false;
|
||||
var errorString = "";
|
||||
if (loggingInBody.linkSteam && loggingInBody.withSteam) {
|
||||
errorString = "Username or password is incorrect.";
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "linkSteam": loggingInBody.linkSteam, "errorString": errorString });
|
||||
} else if (loggingInBody.withSteam) {
|
||||
loggingInGlyph.visible = false;
|
||||
errorString = "Your Steam authentication has failed. Please make sure you are logged into Steam and try again.";
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "errorString": errorString });
|
||||
} else if (loggingInBody.withOculus) {
|
||||
loggingInGlyph.visible = false;
|
||||
errorString = "Your Oculus authentication has failed. Please make sure you are logged into Oculus and try again."
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": errorString });
|
||||
}
|
||||
else {
|
||||
errorString = "Username or password is incorrect.";
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": errorString });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
170
interface/resources/qml/LoginDialog/LoginDialogLightbox.qml
Normal file
|
@ -0,0 +1,170 @@
|
|||
//
|
||||
// LoginDialogLightbox.qml
|
||||
// qml/LoginDialog
|
||||
//
|
||||
// LoginDialogLightbox
|
||||
//
|
||||
// Created by Wayne Chen on 2018-12-07
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import "qrc:////qml//controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
property string titleText;
|
||||
property string bodyImageSource;
|
||||
property string bodyText;
|
||||
property string button1color: hifi.buttons.noneBorderlessGray;
|
||||
property string button1text;
|
||||
property var button1method;
|
||||
property string button2color: hifi.buttons.blue;
|
||||
property string button2text;
|
||||
property var button2method;
|
||||
property string buttonLayout: "leftright";
|
||||
|
||||
readonly property string cantAccessBodyText: "Please navigate to your default browser to recover your account." +
|
||||
"<br><br>If you are in a VR headset, please take it off.";
|
||||
|
||||
id: root;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0, 0, 0, 0.5);
|
||||
z: 999;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
resetLightbox();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent;
|
||||
width: 376;
|
||||
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.black;
|
||||
size: 24;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
wrapMode: Text.WordWrap;
|
||||
}
|
||||
|
||||
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.black;
|
||||
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: root.buttonLayout === "leftright" ? 70 : 150;
|
||||
|
||||
// Button 1
|
||||
HifiControlsUit.Button {
|
||||
id: button1;
|
||||
color: root.button1color;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.top: root.buttonLayout === "leftright" ? parent.top : parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.buttonLayout === "leftright" ? 30 : 10;
|
||||
anchors.right: root.buttonLayout === "leftright" ? undefined : parent.right;
|
||||
anchors.rightMargin: root.buttonLayout === "leftright" ? undefined : 10;
|
||||
width: root.buttonLayout === "leftright" ? (root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2) :
|
||||
(undefined);
|
||||
height: 40;
|
||||
text: root.button1text;
|
||||
onClicked: {
|
||||
button1method();
|
||||
}
|
||||
visible: (root.button1text !== "");
|
||||
}
|
||||
|
||||
// Button 2
|
||||
HifiControlsUit.Button {
|
||||
id: button2;
|
||||
visible: root.button2text;
|
||||
color: root.button2color;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
anchors.top: root.buttonLayout === "leftright" ? parent.top : button1.bottom;
|
||||
anchors.topMargin: root.buttonLayout === "leftright" ? undefined : 20;
|
||||
anchors.left: root.buttonLayout === "leftright" ? undefined : parent.left;
|
||||
anchors.leftMargin: root.buttonLayout === "leftright" ? undefined : 10;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.buttonLayout === "leftright" ? 30 : 10;
|
||||
width: root.buttonLayout === "leftright" ? parent.width/2 - anchors.rightMargin*2 : undefined;
|
||||
height: 40;
|
||||
text: root.button2text;
|
||||
onClicked: {
|
||||
button2method();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
signal sendToParent(var msg);
|
||||
|
||||
function resetLightbox() {
|
||||
root.titleText = "";
|
||||
root.bodyImageSource = "";
|
||||
root.bodyText = "";
|
||||
root.button1color = hifi.buttons.noneBorderlessGray;
|
||||
root.button1text = "";
|
||||
root.button1method = function() {};
|
||||
root.button2color = hifi.buttons.blue;
|
||||
root.button2text = "";
|
||||
root.button2method = function() {};
|
||||
root.buttonLayout = "leftright";
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
//
|
||||
// SignInBody.qml
|
||||
//
|
||||
// Created by Clement on 7/18/16
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls.Styles 1.4 as OriginalStyles
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
|
||||
Item {
|
||||
id: signInBody
|
||||
clip: true
|
||||
height: root.pane.height
|
||||
width: root.pane.width
|
||||
|
||||
property bool required: false
|
||||
|
||||
function login() {
|
||||
console.log("Trying to log in")
|
||||
loginDialog.loginThroughSteam()
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
root.tryDestroy()
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
|
||||
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
}
|
||||
}
|
||||
|
||||
InfoItem {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
text: required ? qsTr("This domain's owner requires that you sign in:")
|
||||
: qsTr("Sign in to access your user account:")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
lineHeight: 2
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
width: undefined // invalidate so that the image's size sets the width
|
||||
height: undefined // invalidate so that the image's size sets the height
|
||||
focus: true
|
||||
|
||||
background: Image {
|
||||
id: buttonImage
|
||||
source: "../../images/steam-sign-in.png"
|
||||
}
|
||||
onClicked: signInBody.login()
|
||||
}
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Cancel");
|
||||
|
||||
onClicked: signInBody.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = required ? qsTr("Sign In Required")
|
||||
: qsTr("Sign In")
|
||||
root.iconText = ""
|
||||
d.resize();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleLoginCompleted: {
|
||||
console.log("Login Succeeded")
|
||||
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
|
||||
bodyLoader.source = "CompleteProfileBody.qml"
|
||||
if (!root.isTablet) {
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,21 +11,24 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4 as OriginalStyles
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Item {
|
||||
id: signupBody
|
||||
z: -2
|
||||
id: signUpBody
|
||||
clip: true
|
||||
height: root.pane.height
|
||||
width: root.pane.width
|
||||
|
||||
function signup() {
|
||||
mainTextContainer.visible = false
|
||||
toggleLoading(true)
|
||||
loginDialog.signup(emailField.text, usernameField.text, passwordField.text)
|
||||
}
|
||||
height: root.height
|
||||
width: root.width
|
||||
property int textFieldHeight: 31
|
||||
property string fontFamily: "Raleway"
|
||||
property int fontSize: 15
|
||||
property int textFieldFontSize: 18
|
||||
property bool fontBold: true
|
||||
readonly property var passwordImageRatio: 16 / 23
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
|
@ -33,248 +36,416 @@ Item {
|
|||
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
property string errorString: errorString
|
||||
property bool linkSteam: linkSteam
|
||||
property bool lostFocus: false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minWidthButton: !root.isTablet ? 256 : 174
|
||||
property int maxWidth: root.width
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
readonly property int minHeightButton: 36
|
||||
property int maxHeight: root.height
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y + form.height;
|
||||
maxWidth = root.width;
|
||||
maxHeight = root.height;
|
||||
var targetWidth = Math.max(titleWidth, mainContainer.width);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y;
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : 0);
|
||||
var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
if (!isNaN(newWidth)) {
|
||||
parent.width = root.width = newWidth;
|
||||
}
|
||||
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLoading(isLoading) {
|
||||
linkAccountSpinner.visible = isLoading
|
||||
form.visible = !isLoading
|
||||
|
||||
leftButton.visible = !isLoading
|
||||
buttons.visible = !isLoading
|
||||
function signup() {
|
||||
loginDialog.signup(emailField.text, usernameField.text, passwordField.text);
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
id: linkAccountSpinner
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
visible: false
|
||||
running: true
|
||||
|
||||
width: 48
|
||||
height: 48
|
||||
function init() {
|
||||
// going to/from sign in/up dialog.
|
||||
loginDialog.isLogIn = false;
|
||||
emailField.placeholderText = "Email";
|
||||
emailField.text = "";
|
||||
emailField.anchors.top = usernameField.bottom;
|
||||
emailField.anchors.topMargin = 1.5 * hifi.dimensions.contentSpacing.y;
|
||||
passwordField.text = "";
|
||||
usernameField.forceActiveFocus();
|
||||
root.text = "";
|
||||
root.isPassword = false;
|
||||
loginContainer.visible = true;
|
||||
}
|
||||
|
||||
ShortcutText {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
visible: false
|
||||
|
||||
text: qsTr("There was an unknown error while creating your account.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
Column {
|
||||
id: form
|
||||
width: parent.width
|
||||
Item {
|
||||
id: mainContainer
|
||||
width: root.width
|
||||
height: root.height
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: 2 * hifi.dimensions.contentSpacing.y
|
||||
|
||||
TextField {
|
||||
id: emailField
|
||||
width: parent.width
|
||||
label: "Email"
|
||||
activeFocusOnPress: true
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
Item {
|
||||
id: loginContainer
|
||||
width: usernameField.width
|
||||
height: parent.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: root.bannerHeight + 0.25 * parent.height
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - usernameField.width) / 2
|
||||
}
|
||||
}
|
||||
visible: true
|
||||
|
||||
TextField {
|
||||
id: usernameField
|
||||
width: parent.width
|
||||
label: "Username"
|
||||
activeFocusOnPress: true
|
||||
|
||||
ShortcutText {
|
||||
Item {
|
||||
id: errorContainer
|
||||
width: parent.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
verticalCenter: parent.textFieldLabel.verticalCenter
|
||||
left: parent.textFieldLabel.right
|
||||
leftMargin: 10
|
||||
bottom: usernameField.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: usernameField.left;
|
||||
}
|
||||
TextMetrics {
|
||||
id: loginErrorMessageTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: loginErrorMessage.text
|
||||
}
|
||||
Text {
|
||||
id: loginErrorMessage;
|
||||
width: root.bannerWidth
|
||||
color: "red";
|
||||
font.family: signUpBody.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: signUpBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ""
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("No spaces / special chars.")
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
color: hifi.colors.blueAccent
|
||||
HifiControlsUit.TextField {
|
||||
id: usernameField
|
||||
width: root.bannerWidth
|
||||
height: signUpBody.textFieldHeight
|
||||
placeholderText: "Username"
|
||||
font.pixelSize: signUpBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: loginErrorMessage.height
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (!usernameField.visible) {
|
||||
return;
|
||||
}
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
passwordField.focus = true;
|
||||
} else {
|
||||
emailField.focus = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
passwordField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
}
|
||||
signUpBody.signup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordField
|
||||
width: parent.width
|
||||
label: "Password"
|
||||
echoMode: TextInput.Password
|
||||
activeFocusOnPress: true
|
||||
|
||||
ShortcutText {
|
||||
anchors {
|
||||
verticalCenter: parent.textFieldLabel.verticalCenter
|
||||
left: parent.textFieldLabel.right
|
||||
leftMargin: 10
|
||||
}
|
||||
|
||||
text: qsTr("At least 6 characters")
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
color: hifi.colors.blueAccent
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
root.isPassword = focus
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: signupBody.signup()
|
||||
}
|
||||
|
||||
Row {
|
||||
id: leftButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Existing User")
|
||||
|
||||
onClicked: {
|
||||
bodyLoader.setSource("LinkAccountBody.qml")
|
||||
if (!root.isTablet) {
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
HifiControlsUit.TextField {
|
||||
id: emailField
|
||||
width: root.bannerWidth
|
||||
height: signUpBody.textFieldHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
}
|
||||
placeholderText: "Username or Email"
|
||||
font.pixelSize: signUpBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
activeFocusOnPress: true
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
if (usernameField.visible) {
|
||||
usernameField.focus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
passwordField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
usernameField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
}
|
||||
signUpBody.signup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: passwordField
|
||||
width: root.bannerWidth
|
||||
height: signUpBody.textFieldHeight
|
||||
placeholderText: "Password (min. 6 characters)"
|
||||
font.pixelSize: signUpBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
activeFocusOnPress: true
|
||||
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
|
||||
anchors {
|
||||
top: emailField.bottom
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
Button {
|
||||
id: linkAccountButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 200
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
root.isPassword = focus;
|
||||
}
|
||||
|
||||
text: qsTr("Sign Up")
|
||||
color: hifi.buttons.blue
|
||||
Item {
|
||||
id: showPasswordContainer
|
||||
z: 10
|
||||
// width + image's rightMargin
|
||||
width: showPasswordImage.width + 8
|
||||
height: parent.height
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
onClicked: signupBody.signup()
|
||||
Image {
|
||||
id: showPasswordImage
|
||||
width: passwordField.height * passwordImageRatio
|
||||
height: passwordField.height * passwordImageRatio
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 8
|
||||
top: parent.top
|
||||
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
|
||||
bottom: parent.bottom
|
||||
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
|
||||
}
|
||||
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
event.accepted = true;
|
||||
if (event.modifiers === Qt.ShiftModifier) {
|
||||
emailField.focus = true;
|
||||
} else if (usernameField.visible) {
|
||||
usernameField.focus = true;
|
||||
} else {
|
||||
emailField.focus = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = true;
|
||||
emailField.focus = true;
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
}
|
||||
signUpBody.signup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.CheckBox {
|
||||
id: keepMeLoggedInCheckbox
|
||||
checked: Settings.getValue("keepMeLoggedIn", false);
|
||||
text: qsTr("Keep Me Logged In");
|
||||
boxSize: 18;
|
||||
labelFontFamily: signUpBody.fontFamily
|
||||
labelFontSize: 18;
|
||||
color: hifi.colors.white;
|
||||
anchors {
|
||||
top: passwordField.bottom;
|
||||
topMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: passwordField.left;
|
||||
}
|
||||
onCheckedChanged: {
|
||||
Settings.setValue("keepMeLoggedIn", checked);
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
TextMetrics {
|
||||
id: cancelButtonTextMetrics
|
||||
font: loginErrorMessage.font
|
||||
text: cancelButton.text
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton
|
||||
width: (emailField.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
top: keepMeLoggedInCheckbox.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
}
|
||||
color: hifi.buttons.noneBorderlessWhite
|
||||
text: qsTr("CANCEL")
|
||||
fontFamily: signUpBody.fontFamily
|
||||
fontSize: signUpBody.fontSize
|
||||
fontBold: signUpBody.fontBold
|
||||
onClicked: {
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": signUpBody.linkSteam });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: signUpButton
|
||||
width: (emailField.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
color: hifi.buttons.blue
|
||||
text: qsTr("Sign Up")
|
||||
fontFamily: signUpBody.fontFamily
|
||||
fontSize: signUpBody.fontSize
|
||||
fontBold: signUpBody.fontBold
|
||||
anchors {
|
||||
top: cancelButton.top
|
||||
right: passwordField.right
|
||||
}
|
||||
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
signUpBody.signup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: root.tryDestroy()
|
||||
MouseArea {
|
||||
z: -2
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
if (!usernameField.focus && !emailField.focus && !passwordField.focus) {
|
||||
usernameField.focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = qsTr("Create an Account")
|
||||
root.iconText = "<"
|
||||
//dont rise local keyboard
|
||||
keyboardEnabled = !root.isTablet && HMD.active;
|
||||
//but rise Tablet's one instead for Tablet interface
|
||||
if (root.isTablet) {
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
}
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
root.text = "";
|
||||
d.resize();
|
||||
|
||||
emailField.forceActiveFocus();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleSignupCompleted: {
|
||||
console.log("Sign Up Succeeded");
|
||||
|
||||
// now that we have an account, login with that username and password
|
||||
loginDialog.login(usernameField.text, passwordField.text)
|
||||
}
|
||||
onHandleSignupFailed: {
|
||||
console.log("Sign Up Failed")
|
||||
toggleLoading(false)
|
||||
|
||||
mainTextContainer.text = errorString
|
||||
mainTextContainer.visible = true
|
||||
|
||||
d.resize();
|
||||
}
|
||||
onHandleLoginCompleted: {
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack": false })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
onHandleLoginFailed: {
|
||||
// we failed to login, show the LoginDialog so the user will try again
|
||||
bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true })
|
||||
if (!root.isTablet) {
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
}
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
signupBody.signup()
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text);
|
||||
signUpBody.login();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleSignupCompleted: {
|
||||
console.log("Sign Up Completed");
|
||||
|
||||
loginDialog.login(usernameField.text, passwordField.text);
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": false, "linkSteam": false });
|
||||
}
|
||||
onHandleSignupFailed: {
|
||||
console.log("Sign Up Failed")
|
||||
|
||||
if (errorString !== "") {
|
||||
loginErrorMessage.visible = true;
|
||||
var errorStringEdited = errorString.replace(/[\n\r]+/g, "\n");
|
||||
loginErrorMessage.text = errorStringEdited;
|
||||
loginErrorMessageTextMetrics.text = errorString;
|
||||
if (loginErrorMessageTextMetrics.width > usernameField.width) {
|
||||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
loginErrorMessage.verticalAlignment = Text.AlignLeft;
|
||||
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / usernameField.width) * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
errorContainer.anchors.bottom = usernameField.top;
|
||||
errorContainer.anchors.bottomMargin = hifi.dimensions.contentSpacing.y;
|
||||
errorContainer.anchors.left = usernameField.left;
|
||||
}
|
||||
}
|
||||
onFocusEnabled: {
|
||||
if (!signUpBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
emailField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
}
|
||||
onFocusDisabled: {
|
||||
signUpBody.lostFocus = !root.isTablet && !root.isOverlay;
|
||||
if (signUpBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
usernameField.focus = false;
|
||||
emailField.focus = false;
|
||||
passwordField.focus = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,169 +12,228 @@ import Hifi 1.0
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
Item {
|
||||
id: usernameCollisionBody
|
||||
clip: true
|
||||
width: root.pane.width
|
||||
height: root.pane.height
|
||||
width: root.width
|
||||
height: root.height
|
||||
readonly property string fontFamily: "Raleway"
|
||||
readonly property int fontSize: 15
|
||||
readonly property int textFieldFontSize: 18
|
||||
readonly property bool fontBold: true
|
||||
|
||||
function create() {
|
||||
mainTextContainer.visible = false
|
||||
loginDialog.createAccountFromStream(textField.text)
|
||||
loginDialog.createAccountFromSteam(textField.text);
|
||||
}
|
||||
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
property bool lostFocus: false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minWidthButton: !root.isTablet ? 256 : 174
|
||||
property int maxWidth: root.width
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
readonly property int minHeightButton: 36
|
||||
property int maxHeight: root.height
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
|
||||
maxWidth = root.width;
|
||||
maxHeight = root.height;
|
||||
var targetWidth = Math.max(titleWidth, mainContainer.width);
|
||||
var targetHeight = mainTextContainer.height +
|
||||
hifi.dimensions.contentSpacing.y + textField.height +
|
||||
hifi.dimensions.contentSpacing.y + buttons.height
|
||||
hifi.dimensions.contentSpacing.y + buttons.height;
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y)
|
||||
parent.height = root.height = Math.max(Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)), mainContainer.height + hifi.dimensions.contentSpacing.y);
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutText {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
text: qsTr("Your Steam username is not available.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
width: parent.width
|
||||
|
||||
placeholderText: "Choose your own"
|
||||
}
|
||||
|
||||
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
|
||||
Keyboard {
|
||||
raised: keyboardEnabled && keyboardRaised
|
||||
numeric: punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: buttons.top
|
||||
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: 0
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
Item {
|
||||
id: mainContainer
|
||||
width: root.width
|
||||
height: root.height
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
anchors.fill: parent
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 200
|
||||
|
||||
text: qsTr("Create your profile")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
onClicked: usernameCollisionBody.create()
|
||||
TextMetrics {
|
||||
id: mainTextContainerTextMetrics
|
||||
font: mainTextContainer.font
|
||||
text: mainTextContainer.text
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Text {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - mainTextContainer.width) / 2
|
||||
topMargin: parent.height/2 - buttons.anchors.topMargin - textField.height - mainTextContainerTextMetrics.height
|
||||
}
|
||||
|
||||
text: qsTr("Cancel")
|
||||
font.family: usernameCollisionBody.fontFamily
|
||||
font.pixelSize: usernameCollisionBody.fontSize
|
||||
font.bold: usernameCollisionBody.fontBold
|
||||
text: qsTr("Your Steam username is not available.");
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
lineHeight: 1
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
onClicked: root.tryDestroy()
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: textField
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - width) / 2
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
focus: true
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: usernameCollisionBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
width: root.bannerWidth
|
||||
|
||||
placeholderText: "Choose your own"
|
||||
|
||||
onFocusChanged: {
|
||||
root.text = "";
|
||||
if (focus) {
|
||||
root.isPassword = false;
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
usernameCollisionBody.create();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: buttons
|
||||
width: root.bannerWidth
|
||||
height: d.minHeightButton
|
||||
anchors {
|
||||
top: textField.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
leftMargin: (parent.width - root.bannerWidth) / 2
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
|
||||
text: qsTr("CANCEL")
|
||||
color: hifi.buttons.noneBorderlessWhite
|
||||
|
||||
fontFamily: usernameCollisionBody.fontFamily
|
||||
fontSize: usernameCollisionBody.fontSize
|
||||
fontBold: usernameCollisionBody.fontBold
|
||||
onClicked: {
|
||||
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "" });
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: profileButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
|
||||
height: d.minHeightButton
|
||||
|
||||
text: qsTr("Create your profile")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
fontFamily: usernameCollisionBody.fontFamily
|
||||
fontSize: usernameCollisionBody.fontSize
|
||||
fontBold: usernameCollisionBody.fontBold
|
||||
onClicked: {
|
||||
usernameCollisionBody.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = qsTr("Complete Your Profile")
|
||||
root.iconText = "<"
|
||||
//dont rise local keyboard
|
||||
keyboardEnabled = !root.isTablet && HMD.active;
|
||||
//but rise Tablet's one instead for Tablet interface
|
||||
if (root.isTablet) {
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
}
|
||||
|
||||
root.keyboardEnabled = HMD.active;
|
||||
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })
|
||||
root.text = "";
|
||||
d.resize();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loginDialog
|
||||
onHandleCreateCompleted: {
|
||||
console.log("Create Succeeded")
|
||||
|
||||
loginDialog.loginThroughSteam()
|
||||
console.log("Create Succeeded");
|
||||
loginDialog.loginThroughSteam();
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": true, "linkSteam": false })
|
||||
}
|
||||
onHandleCreateFailed: {
|
||||
console.log("Create Failed: " + error)
|
||||
|
||||
mainTextContainer.visible = true
|
||||
mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.")
|
||||
mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.");
|
||||
}
|
||||
onHandleLoginCompleted: {
|
||||
console.log("Login Succeeded")
|
||||
|
||||
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
|
||||
bodyLoader.item.width = root.pane.width
|
||||
bodyLoader.item.height = root.pane.height
|
||||
console.log("Login Succeeded");
|
||||
if (loginDialog.getLoginDialogPoppedUp()) {
|
||||
loginDialog.dismissLoginDialog();
|
||||
}
|
||||
root.tryDestroy();
|
||||
}
|
||||
|
||||
onHandleLoginFailed: {
|
||||
console.log("Login Failed")
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return
|
||||
mainTextContainer.text = "Login Failed";
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
usernameCollisionBody.create()
|
||||
break
|
||||
|
||||
onFocusEnabled: {
|
||||
if (!usernameCollisionBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
textField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
}
|
||||
onFocusDisabled: {
|
||||
usernameCollisionBody.lostFocus = !root.isTablet && !root.isOverlay;
|
||||
if (nusernameCollisionBody.lostFocus) {
|
||||
Qt.callLater(function() {
|
||||
textField.focus = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
//
|
||||
// WelcomeBody.qml
|
||||
//
|
||||
// Created by Clement on 7/18/16
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
|
||||
Item {
|
||||
id: welcomeBody
|
||||
clip: true
|
||||
width: root.pane.width
|
||||
height: root.pane.height
|
||||
|
||||
property bool welcomeBack: false
|
||||
|
||||
function setTitle() {
|
||||
root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
|
||||
root.iconText = ""
|
||||
d.resize();
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWidth: 1280
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
|
||||
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
}
|
||||
}
|
||||
|
||||
InfoItem {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
text: qsTr("You are now signed into High Fidelity")
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
lineHeight: 2
|
||||
lineHeightMode: Text.ProportionalHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 0
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
Button {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("Close");
|
||||
|
||||
onClicked: root.tryDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: welcomeBody.setTitle()
|
||||
|
||||
Connections {
|
||||
target: Account
|
||||
onUsernameChanged: welcomeBody.setTitle()
|
||||
}
|
||||
}
|
BIN
interface/resources/qml/LoginDialog/images/background.jpg
Normal file
After Width: | Height: | Size: 960 KiB |
BIN
interface/resources/qml/LoginDialog/images/background_tablet.jpg
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
interface/resources/qml/LoginDialog/images/loader-snake-256.gif
Normal file
After Width: | Height: | Size: 26 KiB |
154
interface/resources/qml/OverlayLoginDialog.qml
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// LoginDialog.qml
|
||||
//
|
||||
// Created by Wayne Chen
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
|
||||
import "LoginDialog"
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
HifiStylesUit.HifiConstants { id: hifi }
|
||||
objectName: "LoginDialog"
|
||||
visible: true
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property bool isTablet: false
|
||||
readonly property bool isOverlay: true
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isPassword: false
|
||||
property string iconText: ""
|
||||
property int iconSize: 50
|
||||
|
||||
property string title: ""
|
||||
property int titleWidth: 0
|
||||
property alias text: loginKeyboard.mirroredText
|
||||
property alias bannerWidth: banner.width
|
||||
property alias bannerHeight: banner.height
|
||||
|
||||
function tryDestroy() {
|
||||
root.destroy()
|
||||
}
|
||||
|
||||
LoginDialog {
|
||||
id: loginDialog
|
||||
anchors.fill: parent
|
||||
Loader {
|
||||
id: bodyLoader
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: -6
|
||||
id: opaqueRect
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
opacity: 0.65
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Item {
|
||||
z: -5
|
||||
id: bannerContainer
|
||||
width: parent.width
|
||||
height: banner.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 0.18 * parent.height
|
||||
}
|
||||
Image {
|
||||
id: banner
|
||||
anchors.centerIn: parent
|
||||
source: "../images/high-fidelity-banner.svg"
|
||||
horizontalAlignment: Image.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: keyboardTimer
|
||||
repeat: false
|
||||
interval: 200
|
||||
|
||||
onTriggered: {
|
||||
if (MenuInterface.isOptionChecked("Use 3D Keyboard")) {
|
||||
KeyboardScriptingInterface.raised = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: loginKeyboard
|
||||
raised: root.keyboardEnabled && root.keyboardRaised
|
||||
numeric: root.punctuationMode
|
||||
password: root.isPassword
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.modifiers === Qt.ControlModifier)
|
||||
switch (event.key) {
|
||||
case Qt.Key_A:
|
||||
event.accepted = true
|
||||
detailedText.selectAll()
|
||||
break
|
||||
case Qt.Key_C:
|
||||
event.accepted = true
|
||||
detailedText.copy()
|
||||
break
|
||||
case Qt.Key_Period:
|
||||
if (Qt.platform.os === "osx") {
|
||||
event.accepted = true
|
||||
content.reject()
|
||||
}
|
||||
break
|
||||
} else switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
event.accepted = true
|
||||
break
|
||||
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
loginKeyboard.raised = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardTimer.start();
|
||||
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
}
|
||||
}
|
|
@ -19,10 +19,14 @@ Original.Button {
|
|||
|
||||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string fontFamily: "Raleway"
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property bool fontBold: true
|
||||
property int radius: hifi.buttons.radius
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
property int buttonGlyphSize: 34;
|
||||
property int buttonGlyphRightMargin: 0;
|
||||
property int fontCapitalization: Font.AllUppercase
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
|
@ -35,13 +39,13 @@ Original.Button {
|
|||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
@ -89,14 +93,21 @@ Original.Button {
|
|||
}
|
||||
|
||||
contentItem: Item {
|
||||
id: buttonContentItem
|
||||
TextMetrics {
|
||||
id: buttonGlyphTextMetrics;
|
||||
font: buttonGlyph.font;
|
||||
text: buttonGlyph.text;
|
||||
}
|
||||
HiFiGlyphs {
|
||||
id: buttonGlyph;
|
||||
visible: control.buttonGlyph !== "";
|
||||
text: control.buttonGlyph === "" ? hifi.glyphs.question : control.buttonGlyph;
|
||||
// Size
|
||||
size: 34;
|
||||
size: control.buttonGlyphSize;
|
||||
// Anchors
|
||||
anchors.right: buttonText.left;
|
||||
anchors.rightMargin: control.buttonGlyphRightMargin
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
// Style
|
||||
|
@ -106,17 +117,32 @@ Original.Button {
|
|||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayBold {
|
||||
|
||||
TextMetrics {
|
||||
id: buttonTextMetrics;
|
||||
font: buttonText.font;
|
||||
text: buttonText.text;
|
||||
}
|
||||
Text {
|
||||
id: buttonText;
|
||||
anchors.centerIn: parent;
|
||||
width: buttonTextMetrics.width
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
font.capitalization: control.fontCapitalization
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: control.fontSize
|
||||
font.family: control.fontFamily
|
||||
font.pixelSize: control.fontSize
|
||||
font.bold: control.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
Component.onCompleted: {
|
||||
if (control.buttonGlyph !== "") {
|
||||
buttonText.x = buttonContentItem.width/2 - buttonTextMetrics.width/2 + (buttonGlyphTextMetrics.width + control.buttonGlyphRightMargin)/2;
|
||||
} else {
|
||||
buttonText.anchors.centerIn = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,14 +44,14 @@ Rectangle {
|
|||
|
||||
|
||||
onPasswordChanged: {
|
||||
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
|
||||
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
|
||||
if (use3DKeyboard) {
|
||||
KeyboardScriptingInterface.password = password;
|
||||
}
|
||||
}
|
||||
|
||||
onRaisedChanged: {
|
||||
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
|
||||
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
|
||||
if (!use3DKeyboard) {
|
||||
keyboardBase.height = raised ? raisedHeight : 0;
|
||||
keyboardBase.visible = raised;
|
||||
|
|
|
@ -31,6 +31,7 @@ TextField {
|
|||
property bool hasClearButton: false;
|
||||
property string leftPermanentGlyph: "";
|
||||
property string centerPlaceholderGlyph: "";
|
||||
property int styleRenderType: Text.NativeRendering
|
||||
|
||||
placeholderText: textField.placeholderText
|
||||
|
||||
|
@ -44,8 +45,8 @@ TextField {
|
|||
// workaround for https://bugreports.qt.io/browse/QTBUG-49297
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
event.accepted = true;
|
||||
|
||||
// emit accepted signal manually
|
||||
|
@ -156,6 +157,7 @@ TextField {
|
|||
selectionColor: hifi.colors.primaryHighlight
|
||||
padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
renderType: textField.styleRenderType
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
|
|
|
@ -70,6 +70,15 @@ ScrollingWindow {
|
|||
}
|
||||
}
|
||||
|
||||
var useKeyboardPreference = findPreference("User Interface", "Use Virtual Keyboard");
|
||||
var keyboardInputPreference = findPreference("User Interface", "Keyboard laser / mallets");
|
||||
if (useKeyboardPreference && keyboardInputPreference) {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
useKeyboardPreference.valueChanged.connect(function() {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (sections.length) {
|
||||
// Default sections to expanded/collapsed as appropriate for dialog.
|
||||
if (sections.length === 1) {
|
||||
|
@ -112,4 +121,32 @@ ScrollingWindow {
|
|||
onClicked: root.restoreAll()
|
||||
}
|
||||
}
|
||||
|
||||
function findPreference(category, name) {
|
||||
var section = null;
|
||||
var preference = null;
|
||||
var i;
|
||||
|
||||
// Find category section.
|
||||
i = 0;
|
||||
while (!section && i < sections.length) {
|
||||
if (sections[i].name === category) {
|
||||
section = sections[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Find named preference.
|
||||
if (section) {
|
||||
i = 0;
|
||||
while (!preference && i < section.preferences.length) {
|
||||
if (section.preferences[i].preference && section.preferences[i].preference.name === name) {
|
||||
preference = section.preferences[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return preference;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,59 +11,52 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.5
|
||||
|
||||
import controlsUit 1.0
|
||||
import stylesUit 1.0
|
||||
import "../windows"
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
import stylesUit 1.0 as HifiStylesUit
|
||||
|
||||
import "../LoginDialog"
|
||||
|
||||
TabletModalWindow {
|
||||
id: realRoot
|
||||
FocusScope {
|
||||
id: root
|
||||
objectName: "LoginDialog"
|
||||
visible: true
|
||||
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
signal sendToScript(var message);
|
||||
signal canceled();
|
||||
|
||||
property bool isHMD: false
|
||||
property bool gotoPreviousApp: false;
|
||||
color: hifi.colors.baseGray
|
||||
title: qsTr("Sign in to High Fidelity")
|
||||
property alias titleWidth: root.titleWidth
|
||||
property alias punctuationMode: root.punctuationMode
|
||||
|
||||
//fake root for shared components expecting root here
|
||||
property var root: QtObject {
|
||||
id: root
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isPassword: false
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isPassword: false
|
||||
property alias text: loginKeyboard.mirroredText
|
||||
readonly property bool isTablet: true
|
||||
readonly property bool isOverlay: false
|
||||
property alias text: loginKeyboard.mirroredText
|
||||
|
||||
readonly property bool isTablet: true
|
||||
property int titleWidth: 0
|
||||
property alias bannerWidth: banner.width
|
||||
property alias bannerHeight: banner.height
|
||||
property string iconText: hifi.glyphs.avatar
|
||||
property int iconSize: 35
|
||||
|
||||
property alias title: realRoot.title
|
||||
property real width: realRoot.width
|
||||
property real height: realRoot.height
|
||||
|
||||
property int titleWidth: 0
|
||||
property string iconText: hifi.glyphs.avatar
|
||||
property int iconSize: 35
|
||||
|
||||
property var pane: QtObject {
|
||||
property real width: root.width
|
||||
property real height: root.height
|
||||
}
|
||||
|
||||
function tryDestroy() {
|
||||
canceled()
|
||||
}
|
||||
property var pane: QtObject {
|
||||
property real width: root.width
|
||||
property real height: root.height
|
||||
}
|
||||
|
||||
//property int colorScheme: hifi.colorSchemes.dark
|
||||
function tryDestroy() {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
width: realRoot.width
|
||||
height: realRoot.height
|
||||
width: root.width
|
||||
height: root.height
|
||||
}
|
||||
|
||||
property bool keyboardOverride: true
|
||||
|
@ -71,29 +64,11 @@ TabletModalWindow {
|
|||
property var items;
|
||||
property string label: ""
|
||||
|
||||
//onTitleWidthChanged: d.resize();
|
||||
|
||||
//onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
signal canceled();
|
||||
|
||||
property alias bodyLoader: bodyLoader
|
||||
property alias loginDialog: loginDialog
|
||||
property alias hifi: hifi
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
onCanceled: {
|
||||
if (bodyLoader.active === true) {
|
||||
//bodyLoader.active = false
|
||||
}
|
||||
if (gotoPreviousApp) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.returnToPreviousApp();
|
||||
} else {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
|
||||
}
|
||||
}
|
||||
HifiStylesUit.HifiConstants { id: hifi }
|
||||
|
||||
Timer {
|
||||
id: keyboardTimer
|
||||
|
@ -107,48 +82,50 @@ TabletModalWindow {
|
|||
}
|
||||
}
|
||||
|
||||
TabletModalFrame {
|
||||
id: mfRoot
|
||||
LoginDialog {
|
||||
id: loginDialog
|
||||
|
||||
width: root.width
|
||||
height: root.height + frameMarginTop + hifi.dimensions.contentMargin.x
|
||||
anchors.fill: parent
|
||||
Loader {
|
||||
id: bodyLoader
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "../LoginDialog/images/background_tablet.jpg"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
z: -5
|
||||
id: bannerContainer
|
||||
width: parent.width
|
||||
height: banner.height
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
verticalCenterOffset: -loginKeyboard.height / 2
|
||||
top: parent.top
|
||||
topMargin: 0.18 * parent.height
|
||||
}
|
||||
|
||||
LoginDialog {
|
||||
id: loginDialog
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: parent.frameMarginTop
|
||||
leftMargin: hifi.dimensions.contentMargin.x
|
||||
rightMargin: hifi.dimensions.contentMargin.x
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: bodyLoader
|
||||
anchors.fill: parent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: loginDialog.isSteamRunning() ? "../LoginDialog/SignInBody.qml" : "../LoginDialog/LinkAccountBody.qml"
|
||||
}
|
||||
Image {
|
||||
id: banner
|
||||
anchors.centerIn: parent
|
||||
source: "../../images/high-fidelity-banner.svg"
|
||||
horizontalAlignment: Image.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
loginKeyboard.raised = false;
|
||||
KeyboardScriptingInterface.raised = false;
|
||||
Rectangle {
|
||||
z: -6
|
||||
id: opaqueRect
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
opacity: 0.65
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardTimer.start();
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
HifiControlsUit.Keyboard {
|
||||
id: loginKeyboard
|
||||
raised: root.keyboardEnabled && root.keyboardRaised
|
||||
numeric: root.punctuationMode
|
||||
|
@ -182,16 +159,20 @@ TabletModalWindow {
|
|||
}
|
||||
break
|
||||
} else switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
event.accepted = true
|
||||
destroy()
|
||||
break
|
||||
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
loginKeyboard.raised = false;
|
||||
KeyboardScriptingInterface.raised = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardTimer.start();
|
||||
bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@ import controlsUit 1.0
|
|||
Preference {
|
||||
id: root
|
||||
height: spacer.height + Math.max(hifi.dimensions.controlLineHeight, checkBox.implicitHeight)
|
||||
|
||||
property bool value: false
|
||||
Component.onCompleted: {
|
||||
checkBox.checked = preference.value;
|
||||
value = checkBox.checked;
|
||||
preference.value = Qt.binding(function(){ return checkBox.checked; });
|
||||
value = checkBox.checked;
|
||||
}
|
||||
|
||||
function save() {
|
||||
|
@ -47,6 +49,7 @@ Preference {
|
|||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
value = checked;
|
||||
}
|
||||
|
||||
anchors {
|
||||
|
|
|
@ -20,6 +20,11 @@ Preference {
|
|||
|
||||
property int value: 0
|
||||
|
||||
readonly property int visibleBottomPadding: 3
|
||||
readonly property int invisibleBottomPadding: 0
|
||||
readonly property int indentLeftMargin: 20
|
||||
readonly property int nonindentLeftMargin: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
value = preference.value;
|
||||
repeater.itemAt(preference.value).checked = true;
|
||||
|
@ -46,24 +51,24 @@ Preference {
|
|||
preference.save();
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: heading
|
||||
size: hifi.fontSizes.inputLabel
|
||||
text: preference.heading
|
||||
color: hifi.colors.lightGrayText
|
||||
visible: text !== ""
|
||||
bottomPadding: heading.visible ? visibleBottomPadding : invisibleBottomPadding
|
||||
}
|
||||
|
||||
Column {
|
||||
id: control
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: heading.visible ? heading.bottom : heading.top
|
||||
leftMargin: preference.indented ? indentLeftMargin : nonindentLeftMargin
|
||||
}
|
||||
spacing: 3
|
||||
|
||||
RalewaySemiBold {
|
||||
id: heading
|
||||
size: hifi.fontSizes.inputLabel
|
||||
text: preference.heading
|
||||
color: hifi.colors.lightGrayText
|
||||
visible: text !== ""
|
||||
bottomPadding: 3
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: preference.items.length
|
||||
|
|
|
@ -189,15 +189,17 @@ Windows.ScrollingWindow {
|
|||
var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)");
|
||||
|
||||
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
|
||||
var textures = JSON.stringify({ "tex.picture": defaultURL});
|
||||
var shapeType = "box";
|
||||
var dynamic = false;
|
||||
var collisionless = true;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity);
|
||||
Entities.addEntity({
|
||||
type: "Image",
|
||||
name: assetProxyModel.data(treeView.selection.currentIndex),
|
||||
imageURL: defaultURL,
|
||||
keepAspectRatio: false,
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
grabbable: grabbable,
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))),
|
||||
gravity: Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0)
|
||||
});
|
||||
} else {
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import QtQuick 2.7
|
||||
import QtWebEngine 1.5;
|
||||
import Qt.labs.settings 1.0
|
||||
import Qt.labs.settings 1.0 as QtSettings
|
||||
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
|
@ -70,11 +70,11 @@ OriginalDesktop.Desktop {
|
|||
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
|
||||
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
|
||||
x: sysToolbar.x
|
||||
buttonModel: tablet.buttons;
|
||||
shown: tablet.toolbarMode;
|
||||
buttonModel: tablet ? tablet.buttons : null;
|
||||
shown: tablet ? tablet.toolbarMode : false;
|
||||
}
|
||||
|
||||
Settings {
|
||||
QtSettings.Settings {
|
||||
id: settings;
|
||||
category: "toolbar";
|
||||
property bool constrainToolbarToCenterX: true;
|
||||
|
|
|
@ -61,7 +61,7 @@ Rectangle {
|
|||
'username';
|
||||
}
|
||||
sortAscending: connectionsTable.sortIndicatorOrder === Qt.AscendingOrder;
|
||||
itemsPerPage: 10;
|
||||
itemsPerPage: 1000;
|
||||
listView: connectionsTable;
|
||||
processPage: function (data) {
|
||||
return data.users.map(function (user) {
|
||||
|
|
|
@ -75,6 +75,10 @@ Rectangle {
|
|||
if(materialUrlOrJson) {
|
||||
wearable.text = 'Material: ' + materialUrlOrJson;
|
||||
}
|
||||
} else if (wearable.sourceUrl) {
|
||||
wearable.text = extractTitleFromUrl(wearable.sourceUrl);
|
||||
} else if (wearable.name) {
|
||||
wearable.text = wearable.name;
|
||||
}
|
||||
wearablesCombobox.model.append(wearable);
|
||||
}
|
||||
|
|
|
@ -1089,9 +1089,13 @@ Rectangle {
|
|||
root.itemInfoReceived = true;
|
||||
root.itemName = result.data.title;
|
||||
root.itemPrice = result.data.cost;
|
||||
root.itemHref = Account.metaverseServerURL + result.data.path;
|
||||
root.itemAuthor = result.data.creator;
|
||||
root.itemType = result.data.item_type || "unknown";
|
||||
if (root.itemType === "unknown") {
|
||||
root.itemHref = result.data.review_url;
|
||||
} else {
|
||||
root.itemHref = Account.metaverseServerURL + result.data.path;
|
||||
}
|
||||
itemPreviewImage.source = result.data.thumbnail_url;
|
||||
refreshBuyUI();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ Item {
|
|||
property bool isCurrentlySendingAsset: false;
|
||||
property string assetName: "";
|
||||
property string assetCertID: "";
|
||||
property string couponID: "";
|
||||
property string authorizationID: "";
|
||||
property string sendingPubliclyEffectImage;
|
||||
property var http;
|
||||
property var listModelName;
|
||||
|
@ -108,6 +110,27 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
onAuthorizeAssetTransferResult: {
|
||||
if (!root.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.isCurrentlySendingAsset = false;
|
||||
|
||||
if (result.status === 'success') {
|
||||
root.authorizationID = result.data.authorization_id;
|
||||
authorizationIDText.text = root.authorizationID;
|
||||
root.couponID = result.data.coupon_id;
|
||||
couponIDText.text = root.couponID
|
||||
if (couponIDTextField.text !== root.couponID) {
|
||||
console.log("SendAsset: Returned coupon ID doesn't match client-generated coupon ID!");
|
||||
}
|
||||
root.nextActiveView = 'paymentSuccess';
|
||||
} else {
|
||||
root.nextActiveView = 'paymentFailure';
|
||||
}
|
||||
}
|
||||
|
||||
onCertificateInfoResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get certificate info", result.data.message);
|
||||
|
@ -269,7 +292,7 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendAssetText;
|
||||
text: root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:";
|
||||
text: root.assetCertID === "" ? "Send Money To:" : "Send \"" + root.assetName + "\" To:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
|
@ -370,6 +393,51 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: createCouponButton;
|
||||
// Anchors
|
||||
anchors.top: nearbyButton.bottom;
|
||||
anchors.topMargin: 32;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: connectionButton.height;
|
||||
width: connectionButton.width;
|
||||
|
||||
Image {
|
||||
anchors.top: parent.top;
|
||||
source: "./images/coupon.svg";
|
||||
height: 70;
|
||||
width: parent.width;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
horizontalAlignment: Image.AlignHCenter;
|
||||
verticalAlignment: Image.AlignTop;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "Create Coupon";
|
||||
// Anchors
|
||||
anchors.bottom: parent.bottom;
|
||||
height: 15;
|
||||
width: parent.width;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
sendAssetStep.referrer = "createCoupon";
|
||||
sendAssetStep.selectedRecipientNodeID = "";
|
||||
couponIDTextField.text = generateRandomCouponID();
|
||||
|
||||
root.nextActiveView = "sendAssetStep";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: backButton_sendAssetHome;
|
||||
visible: parentAppNavBarHeight === 0;
|
||||
|
@ -860,7 +928,7 @@ Item {
|
|||
id: sendAssetStep;
|
||||
z: 996;
|
||||
|
||||
property string referrer; // either "connections", "nearby", or "payIn"
|
||||
property string referrer; // either "connections", "nearby", "payIn", or "createCoupon"
|
||||
property string selectedRecipientNodeID;
|
||||
property string selectedRecipientDisplayName;
|
||||
property string selectedRecipientUserName;
|
||||
|
@ -872,7 +940,8 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendAssetText_sendAssetStep;
|
||||
text: sendAssetStep.referrer === "payIn" && root.assetCertID !== "" ? "Send \"" + root.assetName + "\":" :
|
||||
text: ((sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon") &&
|
||||
root.assetCertID !== "") ? "Send \"" + root.assetName + "\":" :
|
||||
(root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:");
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
|
@ -901,12 +970,13 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_sendAssetStep;
|
||||
text: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:";
|
||||
text: sendAssetStep.referrer === "createCoupon" ? "Coupon ID:" :
|
||||
(root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
|
@ -915,8 +985,10 @@ Item {
|
|||
}
|
||||
|
||||
RecipientDisplay {
|
||||
visible: sendAssetStep.referrer !== "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_sendAssetStep.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: changeButton.left;
|
||||
anchors.rightMargin: 12;
|
||||
height: parent.height;
|
||||
|
@ -929,6 +1001,68 @@ Item {
|
|||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
}
|
||||
|
||||
Item {
|
||||
id: couponIDContainer;
|
||||
visible: sendAssetStep.referrer === "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_sendAssetStep.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: couponIDHelp;
|
||||
text: "[?]";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: 30;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: true;
|
||||
onEntered: {
|
||||
parent.color = hifi.colors.blueHighlight;
|
||||
}
|
||||
onExited: {
|
||||
parent.color = hifi.colors.blueAccent;
|
||||
}
|
||||
onClicked: {
|
||||
lightboxPopup.titleText = "Coupon ID";
|
||||
lightboxPopup.bodyText = "This alphanumeric text string will be used to ensure " +
|
||||
"that only you can redeem the coupon for the asset that you are sending. Keep it private!";
|
||||
lightboxPopup.button1text = "CLOSE";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: couponIDTextField;
|
||||
colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
|
||||
// Anchors
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: couponIDHelp.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
height: 50;
|
||||
// Style
|
||||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
onAccepted: {
|
||||
optionalMessage.focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "CHANGE" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeButton;
|
||||
|
@ -939,7 +1073,7 @@ Item {
|
|||
height: 35;
|
||||
width: 100;
|
||||
text: "CHANGE";
|
||||
visible: sendAssetStep.referrer !== "payIn";
|
||||
visible: sendAssetStep.referrer !== "payIn" && sendAssetStep.referrer !== "createCoupon";
|
||||
onClicked: {
|
||||
if (sendAssetStep.referrer === "connections") {
|
||||
root.nextActiveView = "chooseRecipientConnection";
|
||||
|
@ -1263,6 +1397,11 @@ Item {
|
|||
root.assetCertID,
|
||||
parseInt(amountTextField.text),
|
||||
optionalMessage.text);
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
Commerce.authorizeAssetTransfer(couponIDTextField.text || "",
|
||||
root.assetCertID,
|
||||
parseInt(amountTextField.text) || 1,
|
||||
optionalMessage.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1334,18 +1473,24 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 125;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 125;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
id: paymentSentText;
|
||||
text: root.assetCertID === "" ? "Payment Sent" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent");
|
||||
text: root.assetCertID === "" ? (sendAssetStep.referrer === "createCoupon" ? "Payment Authorized" : "Payment Sent") :
|
||||
(sendAssetStep.referrer === "createCoupon" ? "Item Transfer Authorized" :
|
||||
(sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent"));
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
|
@ -1383,6 +1528,8 @@ Item {
|
|||
onClicked: {
|
||||
if (sendAssetStep.referrer === "payIn") {
|
||||
sendToScript({method: "closeSendAsset"});
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
showDidYouCopyLightbox();
|
||||
} else {
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
|
@ -1402,38 +1549,176 @@ Item {
|
|||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
height: 80;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_paymentSuccess;
|
||||
text: "Sent To:";
|
||||
// Anchors
|
||||
Item {
|
||||
id: sendToScriptContainer_paymentSuccess;
|
||||
visible: sendAssetStep.referrer === "createCoupon";
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RecipientDisplay {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_paymentSuccess.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
textColor: hifi.colors.blueAccent;
|
||||
height: childrenRect.height;
|
||||
|
||||
displayName: sendAssetStep.selectedRecipientDisplayName;
|
||||
userName: sendAssetStep.selectedRecipientUserName;
|
||||
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
|
||||
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
|
||||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
RalewaySemiBold {
|
||||
id: authorizationIDLabel;
|
||||
text: "Authorization ID:";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: authorizationIDText.top;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: authorizationIDText;
|
||||
text: root.authorizationID;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: authorizationIDLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: authorizationIDClipboardButton.left;
|
||||
anchors.rightMargin: 16;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
wrapMode: Text.WrapAnywhere;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: authorizationIDClipboardButton;
|
||||
source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
// Anchors
|
||||
anchors.right: parent.right;
|
||||
anchors.top: authorizationIDText.top;
|
||||
height: 40;
|
||||
width: height;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
Window.copyToClipboard(root.authorizationID);
|
||||
authorizationIDText.text = "Copied to Clipboard!\n";
|
||||
authorizationIDClipboardTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: authorizationIDClipboardTimer;
|
||||
interval: 2000;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
authorizationIDText.text = root.authorizationID;
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: couponIDLabel;
|
||||
text: "Coupon ID:";
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: couponIDText.top;
|
||||
width: authorizationIDLabel.width;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: couponIDText;
|
||||
text: root.couponID;
|
||||
anchors.top: authorizationIDText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: couponIDLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: couponIDClipboardButton.left;
|
||||
anchors.rightMargin: 16;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
wrapMode: Text.WrapAnywhere;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: couponIDClipboardButton;
|
||||
source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
// Anchors
|
||||
anchors.right: parent.right;
|
||||
anchors.top: couponIDText.top;
|
||||
height: 40;
|
||||
width: height;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
Window.copyToClipboard(root.couponID);
|
||||
couponIDText.text = "Copied to Clipboard!\n";
|
||||
couponIDClipboardTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: couponIDClipboardTimer;
|
||||
interval: 2000;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
couponIDText.text = root.couponID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: sendToRecipientContainer_paymentSuccess;
|
||||
visible: !sendToScriptContainer_paymentSuccess.visible;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 80;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: sendToText_paymentSuccess;
|
||||
text: "Sent To:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 90;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
RecipientDisplay {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendToText_paymentSuccess.right;
|
||||
anchors.right: parent.right;
|
||||
height: parent.height;
|
||||
textColor: hifi.colors.blueAccent;
|
||||
|
||||
displayName: sendAssetStep.selectedRecipientDisplayName;
|
||||
userName: sendAssetStep.selectedRecipientUserName;
|
||||
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
|
||||
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
|
||||
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: giftContainer_paymentSuccess;
|
||||
|
@ -1448,7 +1733,8 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: gift_paymentSuccess;
|
||||
text: sendAssetStep.referrer === "payIn" ? "Item:" : "Gift:";
|
||||
text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon" ?
|
||||
"Item:" : "Gift:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1566,6 +1852,8 @@ Item {
|
|||
onClicked: {
|
||||
if (sendAssetStep.referrer === "payIn") {
|
||||
sendToScript({method: "closeSendAsset"});
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
showDidYouCopyLightbox();
|
||||
} else {
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
|
@ -1599,13 +1887,17 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 150;
|
||||
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 150;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
|
||||
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 50;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 300;
|
||||
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
|
||||
sendAssetStep.referrer === "createCoupon" ? 15 : 300;
|
||||
color: "#FFFFFF";
|
||||
|
||||
RalewaySemiBold {
|
||||
|
@ -1657,8 +1949,9 @@ Item {
|
|||
|
||||
RalewaySemiBold {
|
||||
id: paymentFailureDetailText;
|
||||
text: "The recipient you specified was unable to receive your " +
|
||||
(root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."));
|
||||
text: sendAssetStep.referrer === "createCoupon" ? "The server was unable to handle your request. Please try again later." :
|
||||
("The recipient you specified was unable to receive your " +
|
||||
(root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift.")));
|
||||
anchors.top: paymentFailureText.bottom;
|
||||
anchors.topMargin: 20;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1676,7 +1969,8 @@ Item {
|
|||
|
||||
Item {
|
||||
id: sendToContainer_paymentFailure;
|
||||
visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn";
|
||||
visible: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") &&
|
||||
sendAssetStep.referrer !== "createCoupon";
|
||||
anchors.top: paymentFailureDetailText.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
|
@ -1718,7 +2012,8 @@ Item {
|
|||
Item {
|
||||
id: amountContainer_paymentFailure;
|
||||
visible: root.assetCertID === "";
|
||||
anchors.top: sendToContainer_paymentFailure.bottom;
|
||||
anchors.top: sendToContainer_paymentFailure.visible ?
|
||||
sendToContainer_paymentFailure.bottom : paymentFailureDetailText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
|
@ -1839,6 +2134,11 @@ Item {
|
|||
root.assetCertID,
|
||||
parseInt(amountTextField.text),
|
||||
optionalMessage.text);
|
||||
} else if (sendAssetStep.referrer === "createCoupon") {
|
||||
Commerce.authorizeAssetTransfer(couponIDTextField.text || "",
|
||||
root.assetCertID,
|
||||
parseInt(amountTextField.text) || 1,
|
||||
optionalMessage.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1867,6 +2167,39 @@ Item {
|
|||
sendAssetStep.referrer = "";
|
||||
}
|
||||
|
||||
function generateRandomCouponID() {
|
||||
var RANDOM_COUPON_ID_LENGTH = 25;
|
||||
var randomCouponID = "";
|
||||
var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for (var i = 0; i < RANDOM_COUPON_ID_LENGTH; i++) {
|
||||
randomCouponID += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length));
|
||||
}
|
||||
|
||||
return randomCouponID;
|
||||
}
|
||||
|
||||
function showDidYouCopyLightbox() {
|
||||
lightboxPopup.titleText = "Close Confirmation";
|
||||
lightboxPopup.bodyText = "Did you copy your Authorization ID and your Coupon ID?\n\n" +
|
||||
"You won't be able to see your Authorization ID or your Coupon ID once " +
|
||||
"you close this window.";
|
||||
lightboxPopup.button1text = "GO BACK";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.button2text = "I'M ALL SET";
|
||||
lightboxPopup.button2method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
root.nextActiveView = "sendAssetHome";
|
||||
resetSendAssetData();
|
||||
if (root.assetName !== "") {
|
||||
sendSignalToParent({method: "closeSendAsset"});
|
||||
}
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
|
@ -1908,9 +2241,15 @@ Item {
|
|||
sendAssetStep.referrer = "payIn";
|
||||
sendAssetStep.selectedRecipientNodeID = "";
|
||||
sendAssetStep.selectedRecipientDisplayName = "Determined by script:";
|
||||
sendAssetStep.selectedRecipientUserName = message.username;
|
||||
sendAssetStep.selectedRecipientUserName = message.username || "";
|
||||
optionalMessage.text = message.message || "No Message Provided";
|
||||
|
||||
if (sendAssetStep.selectedRecipientUserName === "") {
|
||||
console.log("SendAsset: Script didn't specify a recipient username!");
|
||||
sendAssetHome.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
root.nextActiveView = "sendAssetStep";
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 44 55" enable-background="new 0 0 44 44" xml:space="preserve"><path d="M30.201,10.811h-1.599v-0.076c0-0.754-0.612-1.367-1.366-1.367h-2.063c0.005-0.074,0.023-0.146,0.023-0.225 c0-1.766-1.432-3.197-3.197-3.197s-3.197,1.432-3.197,3.197c0,0.078,0.018,0.15,0.023,0.225h-2.063 c-0.754,0-1.366,0.613-1.366,1.367v0.076h-1.599c-1.38,0-2.502,1.123-2.502,2.502v22.24c0,1.379,1.122,2.502,2.502,2.502h16.402 c1.38,0,2.502-1.123,2.502-2.502v-22.24C32.703,11.934,31.581,10.811,30.201,10.811z M22,7.893c0.691,0,1.251,0.559,1.251,1.25 s-0.56,1.252-1.251,1.252s-1.251-0.561-1.251-1.252S21.309,7.893,22,7.893z M31.035,35.553c0,0.459-0.374,0.834-0.834,0.834H13.799 c-0.46,0-0.834-0.375-0.834-0.834v-22.24c0-0.459,0.374-0.834,0.834-0.834h1.599v1.443h13.205v-1.443h1.599 c0.46,0,0.834,0.375,0.834,0.834V35.553z"/><rect x="15.397" y="16.648" width="13.205" height="0.975"/><rect x="15.397" y="20.402" width="13.205" height="0.973"/><rect x="15.397" y="24.154" width="13.205" height="0.975"/><rect x="15.397" y="27.908" width="13.205" height="0.973"/><rect x="15.397" y="31.66" width="13.205" height="0.975"/><text x="0" y="59" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Bieutuong Bon</text><text x="0" y="64" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,10 @@
|
|||
<svg width="55" height="67" viewBox="0 0 55 67" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="39" cy="21" r="2" fill="#1398BB"/>
|
||||
<circle cx="45" cy="21" r="2" fill="#1398BB"/>
|
||||
<path d="M45 32H10V65L14.5 60.5L18.5 65L23 60.5L27.5 65L32 60.5L36 65L40.5 60.5L45 65V32Z" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45 2V16H10V2H45Z" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45 42H49C51.2091 42 53 40.2091 53 38V20C53 17.7909 51.2091 16 49 16H6C3.79086 16 2 17.7909 2 20V38C2 40.2091 3.79086 42 6 42H10" stroke="#1398BB" stroke-width="3"/>
|
||||
<path d="M21 47L35 33" stroke="#1398BB" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="34" cy="46" r="3.5" stroke="#1398BB" stroke-width="3"/>
|
||||
<path d="M18.1273 32C17.7319 32.5669 17.5 33.2564 17.5 34C17.5 35.933 19.067 37.5 21 37.5C22.933 37.5 24.5 35.933 24.5 34C24.5 33.2564 24.2681 32.5669 23.8727 32" stroke="#1398BB" stroke-width="3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
|
@ -176,6 +176,7 @@ Item {
|
|||
Item {
|
||||
property alias buttonGlyphText: buttonGlyph.text;
|
||||
property alias buttonText: buttonText.text;
|
||||
property alias glyphSize: buttonGlyph.size;
|
||||
property string buttonColor: hifi.colors.black;
|
||||
property string buttonColor_hover: hifi.colors.blueHighlight;
|
||||
property alias enabled: buttonMouseArea.enabled;
|
||||
|
@ -186,7 +187,8 @@ Item {
|
|||
anchors.top: parent.top;
|
||||
anchors.topMargin: 4;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.bottom: parent.verticalCenter;
|
||||
anchors.bottom: buttonText.visible ? parent.verticalCenter : parent.bottom;
|
||||
anchors.bottomMargin: buttonText.visible ? 0 : 4;
|
||||
width: parent.width;
|
||||
size: 40;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
|
@ -196,6 +198,7 @@ Item {
|
|||
|
||||
RalewayRegular {
|
||||
id: buttonText;
|
||||
visible: text !== "";
|
||||
anchors.top: parent.verticalCenter;
|
||||
anchors.topMargin: 4;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
@ -300,7 +303,7 @@ Item {
|
|||
anchors.right: certificateButton.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 78;
|
||||
width: 72;
|
||||
|
||||
onLoaded: {
|
||||
item.buttonGlyphText = hifi.glyphs.uninstall;
|
||||
|
@ -310,6 +313,10 @@ Item {
|
|||
Commerce.uninstallApp(root.itemHref);
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
trashButton.updateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
@ -319,7 +326,7 @@ Item {
|
|||
anchors.right: uninstallButton.visible ? uninstallButton.left : certificateButton.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: 84;
|
||||
width: 78;
|
||||
|
||||
onLoaded: {
|
||||
item.buttonGlyphText = hifi.glyphs.update;
|
||||
|
@ -339,6 +346,45 @@ Item {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
trashButton.updateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: trashButton;
|
||||
visible: root.itemEdition > 0;
|
||||
sourceComponent: contextCardButton;
|
||||
anchors.right: updateButton.visible ? updateButton.left : (uninstallButton.visible ? uninstallButton.left : certificateButton.left);
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: (updateButton.visible && uninstallButton.visible) ? 15 : 78;
|
||||
|
||||
onLoaded: {
|
||||
item.buttonGlyphText = hifi.glyphs.trash;
|
||||
updateProperties();
|
||||
item.buttonClicked = function() {
|
||||
sendToPurchases({method: 'showTrashLightbox',
|
||||
isInstalled: root.isInstalled,
|
||||
itemHref: root.itemHref,
|
||||
itemName: root.itemName,
|
||||
certID: root.certificateId,
|
||||
itemType: root.itemType,
|
||||
wornEntityID: root.wornEntityID
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateProperties() {
|
||||
if (updateButton.visible && uninstallButton.visible) {
|
||||
item.buttonText = "";
|
||||
item.glyphSize = 20;
|
||||
} else {
|
||||
item.buttonText = "Send to Trash";
|
||||
item.glyphSize = 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -651,6 +651,42 @@ Rectangle {
|
|||
lightboxPopup.visible = false;
|
||||
};
|
||||
lightboxPopup.visible = true;
|
||||
} else if (msg.method === "showTrashLightbox") {
|
||||
lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash";
|
||||
lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " +
|
||||
"and it will be inaccessible to you from Purchases.\n\nThis action cannot be undone.";
|
||||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.button2text = "CONFIRM";
|
||||
lightboxPopup.button2method = function() {
|
||||
if (msg.isInstalled) {
|
||||
Commerce.uninstallApp(msg.itemHref);
|
||||
}
|
||||
|
||||
if (MyAvatar.skeletonModelURL === msg.itemHref) {
|
||||
MyAvatar.useFullAvatarURL('');
|
||||
}
|
||||
|
||||
if (msg.itemType === "wearable" && msg.wornEntityID !== '') {
|
||||
Entities.deleteEntity(msg.wornEntityID);
|
||||
purchasesModel.setProperty(index, 'wornEntityID', '');
|
||||
}
|
||||
|
||||
Commerce.transferAssetToUsername("trashbot", msg.certID, 1, "Sent " + msg.itemName + " to trash.");
|
||||
|
||||
lightboxPopup.titleText = '"' + msg.itemName + '" Sent to Trash';
|
||||
lightboxPopup.button1text = "OK";
|
||||
lightboxPopup.button1method = function() {
|
||||
root.purchasesReceived = false;
|
||||
lightboxPopup.visible = false;
|
||||
getPurchases();
|
||||
}
|
||||
lightboxPopup.button2text = "";
|
||||
lightboxPopup.bodyText = "";
|
||||
};
|
||||
lightboxPopup.visible = true;
|
||||
} else if (msg.method === "showChangeAvatarLightbox") {
|
||||
lightboxPopup.titleText = "Change Avatar";
|
||||
lightboxPopup.bodyText = "This will change your current avatar to " + msg.itemName + " while retaining your wearables.";
|
||||
|
|
|
@ -100,6 +100,9 @@ Rectangle {
|
|||
console.log("Failed to get Available Updates", result.data.message);
|
||||
} else {
|
||||
exchangeMoneyButtonContainer.messagesWaiting = result.data.updates.length > 0;
|
||||
if (!exchangeMoneyButtonContainer.messagesWaiting) {
|
||||
sendToScript({method: 'clearShouldShowDotUpdates'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -270,9 +270,11 @@ Item {
|
|||
model: transactionHistoryModel;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 : ((model.status === "confirmed" || model.status === "invalidated") ? transactionText.height + 30 : 0);
|
||||
height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 :
|
||||
(transactionContainer.visible ? transactionText.height + 30 : 0);
|
||||
|
||||
Item {
|
||||
id: pendingCountContainer;
|
||||
visible: model.transaction_type === "pendingCount" && model.count !== 0;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
@ -291,7 +293,9 @@ Item {
|
|||
}
|
||||
|
||||
Item {
|
||||
visible: model.transaction_type !== "pendingCount" && (model.status === "confirmed" || model.status === "invalidated");
|
||||
id: transactionContainer;
|
||||
visible: model.transaction_type !== "pendingCount" &&
|
||||
(model.status === "confirmed" || model.status === "invalidated");
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: parent.width;
|
||||
|
|
|
@ -15,10 +15,10 @@ import Qt.labs.settings 1.0
|
|||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControls
|
||||
import "../../windows"
|
||||
import "../../windows" as Windows
|
||||
import "../"
|
||||
|
||||
ScrollingWindow {
|
||||
Windows.ScrollingWindow {
|
||||
id: root
|
||||
objectName: "RunningScripts"
|
||||
title: "Running Scripts"
|
||||
|
@ -30,7 +30,7 @@ ScrollingWindow {
|
|||
minSize: Qt.vector2d(424, 300)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
||||
property var scripts: ScriptDiscoveryService;
|
||||
property var scriptsModel: scripts.scriptsModelFilter
|
||||
property var runningScriptsModel: ListModel { }
|
||||
|
@ -45,7 +45,7 @@ ScrollingWindow {
|
|||
|
||||
Component {
|
||||
id: letterBoxMessage
|
||||
Window {
|
||||
Windows.Window {
|
||||
implicitWidth: 400
|
||||
implicitHeight: 300
|
||||
minSize: Qt.vector2d(424, 300)
|
||||
|
@ -63,8 +63,8 @@ ScrollingWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 100
|
||||
|
@ -81,7 +81,7 @@ ScrollingWindow {
|
|||
running: false
|
||||
onTriggered: developerMenuEnabled = MenuInterface.isOptionChecked("Developer Menu");
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: listModelBuilder
|
||||
ListModel { }
|
||||
|
@ -92,7 +92,7 @@ ScrollingWindow {
|
|||
onScriptCountChanged: {
|
||||
runningScriptsModel = listModelBuilder.createObject(root);
|
||||
refreshTimer.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -120,7 +120,7 @@ ScrollingWindow {
|
|||
// Calling `runningScriptsModel.clear()` here instead of creating a new object
|
||||
// triggers some kind of weird heap corruption deep inside Qt. So instead of
|
||||
// modifying the model in place, possibly triggering behaviors in the table
|
||||
// instead we create a new `ListModel`, populate it and update the
|
||||
// instead we create a new `ListModel`, populate it and update the
|
||||
// existing model atomically.
|
||||
var newRunningScriptsModel = listModelBuilder.createObject(root);
|
||||
for (var i = 0; i < runningScripts.length; ++i) {
|
||||
|
@ -177,7 +177,7 @@ ScrollingWindow {
|
|||
if ((script === "controllerScripts.js") || (script === "defaultScripts.js")) {
|
||||
return developerMenuEnabled;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ ScrollingWindow {
|
|||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
||||
TableViewColumn {
|
||||
role: "display";
|
||||
}
|
||||
|
@ -447,4 +447,3 @@ ScrollingWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -189,15 +189,17 @@ Rectangle {
|
|||
var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)");
|
||||
|
||||
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
|
||||
var textures = JSON.stringify({ "tex.picture": defaultURL});
|
||||
var shapeType = "box";
|
||||
var dynamic = false;
|
||||
var collisionless = true;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity);
|
||||
Entities.addEntity({
|
||||
type: "Image",
|
||||
name: assetProxyModel.data(treeView.selection.currentIndex),
|
||||
imageURL: defaultURL,
|
||||
keepAspectRatio: false,
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
grabbable: grabbable,
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))),
|
||||
gravity: Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0)
|
||||
});
|
||||
} else {
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
|
|
|
@ -328,7 +328,7 @@ Rectangle {
|
|||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
text: "Your wallet is not set up.\n" +
|
||||
"Open the ASSETS app to get started.";
|
||||
"Open the INVENTORY app to get started.";
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Text size
|
||||
|
|
|
@ -207,12 +207,12 @@ Flickable {
|
|||
width: 112
|
||||
label: "Y Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
minimumValue: -50
|
||||
maximumValue: 50
|
||||
realStepSize: 1
|
||||
realValue: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -223,14 +223,14 @@ Flickable {
|
|||
id: headZOffset
|
||||
width: 112
|
||||
label: "Z Offset"
|
||||
minimumValue: -10
|
||||
minimumValue: -50
|
||||
maximumValue: 50
|
||||
realStepSize: 1
|
||||
decimals: 1
|
||||
suffix: " cm"
|
||||
realValue: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -319,11 +319,12 @@ Flickable {
|
|||
width: 112
|
||||
suffix: " cm"
|
||||
label: "Y Offset"
|
||||
minimumValue: -10
|
||||
minimumValue: -30
|
||||
maximumValue: 30
|
||||
realStepSize: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -335,12 +336,13 @@ Flickable {
|
|||
width: 112
|
||||
label: "Z Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
minimumValue: -30
|
||||
maximumValue: 30
|
||||
realStepSize: 1
|
||||
decimals: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -574,7 +576,7 @@ Flickable {
|
|||
colorScheme: hifi.colorSchemes.dark
|
||||
realValue: 33.0
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -592,7 +594,7 @@ Flickable {
|
|||
colorScheme: hifi.colorSchemes.dark
|
||||
realValue: 48
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
sendConfigurationSettings();
|
||||
openVrConfiguration.forceActiveFocus();
|
||||
}
|
||||
|
@ -771,7 +773,7 @@ Flickable {
|
|||
realStepSize: 1.0
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
onRealValueChanged: {
|
||||
calibrationTimer.interval = realValue * 1000;
|
||||
openVrConfiguration.countDown = realValue;
|
||||
numberAnimation.duration = calibrationTimer.interval;
|
||||
|
@ -871,7 +873,7 @@ Flickable {
|
|||
|
||||
editable: true
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
model: ["None", "Freeze", "Drop"]
|
||||
model: ["None", "Freeze", "Drop", "DropAfterDelay"]
|
||||
label: ""
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
|
@ -977,6 +979,13 @@ Flickable {
|
|||
var configurationType = settings["trackerConfiguration"];
|
||||
displayTrackerConfiguration(configurationType);
|
||||
|
||||
// default offset for user wearing puck on the center of their forehead.
|
||||
headYOffset.realValue = 4; // (cm), puck is above the head joint.
|
||||
headZOffset.realValue = 8; // (cm), puck is in front of the head joint.
|
||||
|
||||
// defaults for user wearing the pucks on the backs of their palms.
|
||||
handYOffset.realValue = 8; // (cm), puck is past the the hand joint. (set this to zero if puck is on the wrist)
|
||||
handZOffset.realValue = -4; // (cm), puck is on above hand joint.
|
||||
|
||||
var HmdHead = settings["HMDHead"];
|
||||
var viveController = settings["handController"];
|
||||
|
|
|
@ -61,7 +61,7 @@ Item {
|
|||
RalewaySemiBold {
|
||||
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
Layout.alignment: Qt.AlignRight
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ Item {
|
|||
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
|
||||
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
Layout.alignment: Qt.AlignRight
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
@ -115,9 +115,9 @@ Item {
|
|||
property int previousIndex: -1
|
||||
Repeater {
|
||||
id: pageRepeater
|
||||
model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage)
|
||||
model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : 0
|
||||
onItemAdded: {
|
||||
item.proxyModel.sourceModel = tabletProxy.buttons;
|
||||
item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null;
|
||||
item.proxyModel.pageIndex = index;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ Item {
|
|||
property var openBrowser: null;
|
||||
property string subMenu: ""
|
||||
signal showDesktop();
|
||||
signal screenChanged(var type, var url);
|
||||
property bool shown: true
|
||||
property int currentApp: -1;
|
||||
property alias tabletApps: tabletApps
|
||||
|
@ -113,6 +114,8 @@ Item {
|
|||
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
|
||||
loader.item.gotoPreviousApp = true;
|
||||
}
|
||||
|
||||
screenChanged("Web", url)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +269,24 @@ Item {
|
|||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
var type = "Unknown";
|
||||
if (newSource === "") {
|
||||
type = "Closed";
|
||||
} else if (newSource === "hifi/tablet/TabletMenu.qml") {
|
||||
type = "Menu";
|
||||
} else if (newSource === "hifi/tablet/TabletHome.qml") {
|
||||
type = "Home";
|
||||
} else if (newSource === "hifi/tablet/TabletWebView.qml") {
|
||||
// Handled in `callback()`
|
||||
return;
|
||||
} else if (newSource.toLowerCase().indexOf(".qml") > -1) {
|
||||
type = "QML";
|
||||
} else {
|
||||
console.log("newSource is of unknown type!");
|
||||
}
|
||||
|
||||
screenChanged(type, newSource);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ Windows.ScrollingWindow {
|
|||
id: tabletRoot
|
||||
objectName: "tabletRoot"
|
||||
property string username: "Unknown user"
|
||||
signal screenChanged(var type, var url);
|
||||
|
||||
property var rootMenu;
|
||||
property string subMenu: ""
|
||||
|
@ -69,6 +70,8 @@ Windows.ScrollingWindow {
|
|||
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||
loader.item.closeButtonVisible = false;
|
||||
}
|
||||
|
||||
screenChanged("Web", url);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -179,7 +182,25 @@ Windows.ScrollingWindow {
|
|||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
var type = "Unknown";
|
||||
if (newSource === "") {
|
||||
type = "Closed";
|
||||
} else if (newSource === "hifi/tablet/TabletMenu.qml") {
|
||||
type = "Menu";
|
||||
} else if (newSource === "hifi/tablet/TabletHome.qml") {
|
||||
type = "Home";
|
||||
} else if (newSource === "hifi/tablet/TabletWebView.qml") {
|
||||
// Handled in `callback()`
|
||||
return;
|
||||
} else if (newSource.toLowerCase().indexOf(".qml") > -1) {
|
||||
type = "QML";
|
||||
} else {
|
||||
console.log("newSource is of unknown type!");
|
||||
}
|
||||
|
||||
screenChanged(type, newSource);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,12 +123,12 @@ Item {
|
|||
}
|
||||
|
||||
// Runtime customization of preferences.
|
||||
var locomotionPreference = findPreference("VR Movement", "Teleporting only / Walking and teleporting");
|
||||
var locomotionPreference = findPreference("VR Movement", "Walking");
|
||||
var flyingPreference = findPreference("VR Movement", "Jumping and flying");
|
||||
if (locomotionPreference && flyingPreference) {
|
||||
flyingPreference.visible = (locomotionPreference.value === 1);
|
||||
flyingPreference.visible = locomotionPreference.value;
|
||||
locomotionPreference.valueChanged.connect(function () {
|
||||
flyingPreference.visible = (locomotionPreference.value === 1);
|
||||
flyingPreference.visible = locomotionPreference.value;
|
||||
});
|
||||
}
|
||||
if (HMD.isHeadControllerAvailable("Oculus")) {
|
||||
|
@ -138,6 +138,15 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
var useKeyboardPreference = findPreference("User Interface", "Use Virtual Keyboard");
|
||||
var keyboardInputPreference = findPreference("User Interface", "Keyboard laser / mallets");
|
||||
if (useKeyboardPreference && keyboardInputPreference) {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
useKeyboardPreference.valueChanged.connect(function() {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (sections.length) {
|
||||
// Default sections to expanded/collapsed as appropriate for dialog.
|
||||
if (sections.length === 1) {
|
||||
|
|
|
@ -349,5 +349,7 @@ QtObject {
|
|||
readonly property string update: "\ue032"
|
||||
readonly property string uninstall: "\ue033"
|
||||
readonly property string verticalEllipsis: "\ue034"
|
||||
readonly property string steamSquare: "\ue035"
|
||||
readonly property string oculus: "\ue036"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,22 +97,22 @@ Frame {
|
|||
|
||||
|
||||
GlyphButton {
|
||||
id: closeButton
|
||||
visible: window.closeButtonVisible
|
||||
width: 30
|
||||
y: -hifi.dimensions.modalDialogTitleHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
glyph: hifi.glyphs.close
|
||||
size: 23
|
||||
onClicked: {
|
||||
window.clickedCloseButton = true;
|
||||
window.destroy();
|
||||
}
|
||||
}
|
||||
id: closeButton
|
||||
visible: window.closeButtonVisible
|
||||
width: 30
|
||||
y: -hifi.dimensions.modalDialogTitleHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
glyph: hifi.glyphs.close
|
||||
size: 23
|
||||
onClicked: {
|
||||
window.clickedCloseButton = true;
|
||||
window.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import QtQuick 2.5
|
|||
import QtQuick.Controls 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import "."
|
||||
import "." as Windows
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HiFiControls
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
|
||||
// FIXME how do I set the initial position of a window without
|
||||
// overriding places where the a individual client of the window
|
||||
|
@ -25,7 +25,7 @@ import controlsUit 1.0 as HiFiControls
|
|||
// of the desktop? How do I ensure when the desktop resizes all the windows
|
||||
// are still at least partially visible?
|
||||
|
||||
Window {
|
||||
Windows.Window {
|
||||
id: window
|
||||
HifiConstants { id: hifi }
|
||||
children: [ swallower, frame, defocuser, pane, activator ]
|
||||
|
@ -178,7 +178,7 @@ Window {
|
|||
children: [ footer ]
|
||||
}
|
||||
|
||||
HiFiControls.Keyboard {
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard
|
||||
enabled: !keyboardOverride
|
||||
raised: keyboardEnabled && keyboardRaised
|
||||
|
|
|
@ -1,47 +1,32 @@
|
|||
const vec3 COLOR = vec3(0x00, 0xD8, 0x02) / vec3(0xFF);
|
||||
const float CUTOFF = 0.65;
|
||||
const float NOISE_MULT = 8.0;
|
||||
const float NOISE_POWER = 1.0;
|
||||
// Replicate the default skybox texture
|
||||
|
||||
float noise4D(vec4 p) {
|
||||
return fract(sin(dot(p ,vec4(12.9898,78.233,126.7235, 593.2241))) * 43758.5453);
|
||||
}
|
||||
|
||||
float worley4D(vec4 p) {
|
||||
float r = 3.0;
|
||||
vec4 f = floor(p);
|
||||
vec4 x = fract(p);
|
||||
for(int i = -1; i<=1; i++)
|
||||
{
|
||||
for(int j = -1; j<=1; j++)
|
||||
{
|
||||
for(int k = -1; k<=1; k++)
|
||||
{
|
||||
for (int l = -1; l <= 1; l++) {
|
||||
vec4 q = vec4(float(i),float(j),float(k), float(l));
|
||||
vec4 v = q + vec4(noise4D((q+f)*1.11), noise4D((q+f)*1.14), noise4D((q+f)*1.17), noise4D((q+f)*1.20)) - x;
|
||||
float d = dot(v, v);
|
||||
r = min(r, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqrt(r);
|
||||
}
|
||||
|
||||
|
||||
vec3 mainColor(vec3 direction) {
|
||||
float n = worley4D(vec4(direction * NOISE_MULT, iGlobalTime / 3.0));
|
||||
n = 1.0 - n;
|
||||
n = pow(n, NOISE_POWER);
|
||||
if (n < CUTOFF) {
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
n = (n - CUTOFF) / (1.0 - CUTOFF);
|
||||
return COLOR * (1.0 - n);
|
||||
}
|
||||
const int NUM_COLORS = 5;
|
||||
const vec3 WHITISH = vec3(0.471, 0.725, 0.825);
|
||||
const vec3 GREENISH = vec3(0.157, 0.529, 0.588);
|
||||
const vec3 COLORS[NUM_COLORS] = vec3[](
|
||||
GREENISH,
|
||||
GREENISH,
|
||||
WHITISH,
|
||||
WHITISH,
|
||||
vec3(0.6, 0.275, 0.706) // purple
|
||||
);
|
||||
const float PI = 3.14159265359;
|
||||
const vec3 BLACK = vec3(0.0);
|
||||
const vec3 SPACE_BLUE = vec3(0.0, 0.118, 0.392);
|
||||
const float HORIZONTAL_OFFSET = 3.75;
|
||||
|
||||
vec3 getSkyboxColor() {
|
||||
return mainColor(normalize(_normal));
|
||||
vec2 horizontal = vec2(_normal.x, _normal.z);
|
||||
horizontal = normalize(horizontal);
|
||||
float theta = atan(horizontal.y, horizontal.x);
|
||||
theta = 0.5 * (theta / PI + 1.0);
|
||||
float index = theta * NUM_COLORS;
|
||||
index = mod(index + HORIZONTAL_OFFSET, NUM_COLORS);
|
||||
int index1 = int(index) % NUM_COLORS;
|
||||
int index2 = (index1 + 1) % NUM_COLORS;
|
||||
vec3 horizonColor = mix(COLORS[index1], COLORS[index2], index - index1);
|
||||
horizonColor = mix(horizonColor, SPACE_BLUE, smoothstep(0.0, 0.08, _normal.y));
|
||||
horizonColor = mix(horizonColor, BLACK, smoothstep(0.04, 0.15, _normal.y));
|
||||
horizonColor = mix(BLACK, horizonColor, smoothstep(-0.01, 0.0, _normal.y));
|
||||
return pow(horizonColor, vec3(0.4545));;
|
||||
}
|
||||
|
|
BIN
interface/resources/sounds/keySound.mp3
Normal file
|
@ -56,6 +56,7 @@
|
|||
#include "ConnectionMonitor.h"
|
||||
#include "CursorManager.h"
|
||||
#include "gpu/Context.h"
|
||||
#include "LoginStateManager.h"
|
||||
#include "Menu.h"
|
||||
#include "octree/OctreePacketProcessor.h"
|
||||
#include "render/Engine.h"
|
||||
|
@ -70,11 +71,10 @@
|
|||
#include "ui/overlays/Overlays.h"
|
||||
|
||||
#include "workload/GameWorkload.h"
|
||||
#include "graphics/GraphicsEngine.h"
|
||||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <graphics/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
|
||||
#include "Sound.h"
|
||||
|
||||
|
@ -98,6 +98,7 @@ static const UINT UWM_SHOW_APPLICATION =
|
|||
|
||||
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
|
||||
static const QString SCRIPTS_SWITCH = "scripts";
|
||||
static const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "no-login-suggestion";
|
||||
|
||||
class Application;
|
||||
#if defined(qApp)
|
||||
|
@ -119,7 +120,7 @@ class Application : public QApplication,
|
|||
public:
|
||||
// virtual functions required for PluginContainer
|
||||
virtual ui::Menu* getPrimaryMenu() override;
|
||||
virtual void requestReset() override { resetSensors(true); }
|
||||
virtual void requestReset() override { resetSensors(false); }
|
||||
virtual void showDisplayPluginsTools(bool show) override;
|
||||
virtual GLWidget* getPrimaryWidget() override;
|
||||
virtual MainWindow* getPrimaryWindow() override;
|
||||
|
@ -153,7 +154,6 @@ public:
|
|||
void updateSecondaryCameraViewFrustum();
|
||||
|
||||
void updateCamera(RenderArgs& renderArgs, float deltaTime);
|
||||
void paintGL();
|
||||
void resizeGL();
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
|
@ -203,8 +203,8 @@ public:
|
|||
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
|
||||
size_t getRenderFrameCount() const { return _renderFrameCount; }
|
||||
float getRenderLoopRate() const { return _renderLoopCounter.rate(); }
|
||||
size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); }
|
||||
float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); }
|
||||
float getNumCollisionObjects() const;
|
||||
float getTargetRenderFrameRate() const; // frames/second
|
||||
|
||||
|
@ -234,7 +234,7 @@ public:
|
|||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
void setSettingConstrainToolbarPosition(bool setting);
|
||||
|
||||
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; }
|
||||
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; }
|
||||
|
||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||
|
||||
|
@ -275,10 +275,10 @@ public:
|
|||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||
int getMaxOctreePacketsPerSecond() const;
|
||||
|
||||
render::ScenePointer getMain3DScene() override { return _main3DScene; }
|
||||
const render::ScenePointer& getMain3DScene() const { return _main3DScene; }
|
||||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||
render::ScenePointer getMain3DScene() override { return _graphicsEngine.getRenderScene(); }
|
||||
render::EnginePointer getRenderEngine() override { return _graphicsEngine.getRenderEngine(); }
|
||||
gpu::ContextPointer getGPUContext() const { return _graphicsEngine.getGPUContext(); }
|
||||
|
||||
|
||||
const GameWorkload& getGameWorkload() const { return _gameWorkload; }
|
||||
|
||||
|
@ -322,6 +322,10 @@ public:
|
|||
int getOtherAvatarsReplicaCount() { return DependencyManager::get<AvatarHashMap>()->getReplicaCount(); }
|
||||
void setOtherAvatarsReplicaCount(int count) { DependencyManager::get<AvatarHashMap>()->setReplicaCount(count); }
|
||||
|
||||
bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; }
|
||||
void createLoginDialogOverlay();
|
||||
void updateLoginDialogOverlayPosition();
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void beforeEnterBackground();
|
||||
void enterBackground();
|
||||
|
@ -340,7 +344,8 @@ signals:
|
|||
|
||||
void interstitialModeChanged(bool isInInterstitialMode);
|
||||
|
||||
void loginDialogPoppedUp();
|
||||
void loginDialogFocusEnabled();
|
||||
void loginDialogFocusDisabled();
|
||||
|
||||
void miniTabletEnabledChanged(bool enabled);
|
||||
|
||||
|
@ -364,6 +369,8 @@ public slots:
|
|||
|
||||
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
|
||||
|
||||
void showLoginScreen();
|
||||
|
||||
// FIXME: Move addAssetToWorld* methods to own class?
|
||||
void addAssetToWorldFromURL(QString url);
|
||||
void addAssetToWorldFromURLRequestFinished();
|
||||
|
@ -442,7 +449,10 @@ public slots:
|
|||
void setPreferredCursor(const QString& cursor);
|
||||
|
||||
void setIsServerlessMode(bool serverlessDomain);
|
||||
void loadServerlessDomain(QUrl domainURL, bool errorDomain = false);
|
||||
std::map<QString, QString> prepareServerlessDomainContents(QUrl domainURL);
|
||||
|
||||
void loadServerlessDomain(QUrl domainURL);
|
||||
void loadErrorDomain(QUrl domainURL);
|
||||
void setIsInterstitialMode(bool interstitialMode);
|
||||
|
||||
void updateVerboseLogging();
|
||||
|
@ -507,15 +517,18 @@ private slots:
|
|||
void setShowBulletConstraints(bool value);
|
||||
void setShowBulletConstraintLimits(bool value);
|
||||
|
||||
void onDismissedLoginDialog();
|
||||
|
||||
void setShowTrackedObjects(bool value);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void pauseUntilLoginDetermined();
|
||||
void resumeAfterLoginDialogActionTaken();
|
||||
bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event);
|
||||
bool handleFileOpenEvent(QFileOpenEvent* event);
|
||||
void cleanupBeforeQuit();
|
||||
|
||||
bool shouldPaint() const;
|
||||
void idle();
|
||||
void update(float deltaTime);
|
||||
|
||||
|
@ -535,8 +548,6 @@ private:
|
|||
|
||||
void initializeAcceptedFiles();
|
||||
|
||||
void runRenderFrame(RenderArgs* renderArgs/*, Camera& whichCamera, bool selfAvatarOnly = false*/);
|
||||
|
||||
bool importJSONFromURL(const QString& urlString);
|
||||
bool importSVOFromURL(const QString& urlString);
|
||||
bool importFromZIP(const QString& filePath);
|
||||
|
@ -586,18 +597,12 @@ private:
|
|||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
|
||||
uint32_t _renderFrameCount { 0 };
|
||||
|
||||
// Frame Rate Measurement
|
||||
RateCounter<500> _renderLoopCounter;
|
||||
RateCounter<500> _gameLoopCounter;
|
||||
|
||||
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
|
||||
|
||||
QTimer _minimizedWindowTimer;
|
||||
QElapsedTimer _timerStart;
|
||||
QElapsedTimer _lastTimeUpdated;
|
||||
QElapsedTimer _lastTimeRendered;
|
||||
|
||||
int _minimumGPUTextureMemSizeStabilityCount { 30 };
|
||||
|
||||
|
@ -633,6 +638,7 @@ private:
|
|||
Setting::Handle<float> _fieldOfView;
|
||||
Setting::Handle<float> _hmdTabletScale;
|
||||
Setting::Handle<float> _desktopTabletScale;
|
||||
Setting::Handle<bool> _firstRun;
|
||||
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _preferStylusOverLaserSetting;
|
||||
|
@ -660,6 +666,7 @@ private:
|
|||
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
|
||||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<EntityScriptServerLogDialog> _entityScriptServerLogDialog;
|
||||
QDir _defaultScriptsLocation;
|
||||
|
||||
FileLogger* _logger;
|
||||
|
||||
|
@ -680,32 +687,21 @@ private:
|
|||
glm::uvec2 _renderResolution;
|
||||
|
||||
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
bool _interstitialModeEnabled{ false };
|
||||
|
||||
bool _loginDialogPoppedUp = false;
|
||||
bool _developerMenuVisible{ false };
|
||||
QString _previousAvatarSkeletonModel;
|
||||
float _previousAvatarTargetScale;
|
||||
CameraMode _previousCameraMode;
|
||||
OverlayID _loginDialogOverlayID;
|
||||
LoginStateManager _loginStateManager;
|
||||
|
||||
quint64 _lastFaceTrackerUpdate;
|
||||
|
||||
render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) };
|
||||
render::EnginePointer _renderEngine{ new render::RenderEngine() };
|
||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||
|
||||
GameWorkload _gameWorkload;
|
||||
|
||||
mutable QMutex _renderArgsMutex{ QMutex::Recursive };
|
||||
struct AppRenderArgs {
|
||||
render::Args _renderArgs;
|
||||
glm::mat4 _eyeToWorld;
|
||||
glm::mat4 _view;
|
||||
glm::mat4 _eyeOffsets[2];
|
||||
glm::mat4 _eyeProjections[2];
|
||||
glm::mat4 _headPose;
|
||||
glm::mat4 _sensorToWorld;
|
||||
float _sensorToWorldScale { 1.0f };
|
||||
bool _isStereo{ false };
|
||||
};
|
||||
AppRenderArgs _appRenderArgs;
|
||||
|
||||
|
||||
using RenderArgsEditor = std::function <void (AppRenderArgs&)>;
|
||||
void editRenderArgs(RenderArgsEditor editor);
|
||||
GraphicsEngine _graphicsEngine;
|
||||
void updateRenderArgs(float deltaTime);
|
||||
|
||||
|
||||
|
@ -751,8 +747,6 @@ private:
|
|||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
|
||||
QString _returnFromFullScreenMirrorTo;
|
||||
|
||||
ConnectionMonitor _connectionMonitor;
|
||||
|
||||
QTimer _addAssetToWorldResizeTimer;
|
||||
|
@ -786,16 +780,13 @@ private:
|
|||
|
||||
QUrl _avatarOverrideUrl;
|
||||
bool _saveAvatarOverrideUrl { false };
|
||||
QObject* _renderEventHandler{ nullptr };
|
||||
|
||||
friend class RenderEventHandler;
|
||||
|
||||
std::atomic<bool> _pendingIdleEvent { true };
|
||||
std::atomic<bool> _pendingRenderEvent { true };
|
||||
|
||||
bool quitWhenFinished { false };
|
||||
|
||||
bool _showTrackedObjects { false };
|
||||
bool _prevShowTrackedObjects { false };
|
||||
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
//
|
||||
// Application_render.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Application.h"
|
||||
#include <MainWindow.h>
|
||||
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <SceneScriptingInterface.h>
|
||||
#include "ui/Stats.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
// Statically provided display and input plugins
|
||||
extern DisplayPluginList getDisplayPlugins();
|
||||
|
||||
void Application::editRenderArgs(RenderArgsEditor editor) {
|
||||
QMutexLocker renderLocker(&_renderArgsMutex);
|
||||
editor(_appRenderArgs);
|
||||
|
||||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
// Some plugins process message events, allowing paintGL to be called reentrantly.
|
||||
|
||||
_renderFrameCount++;
|
||||
_lastTimeRendered.start();
|
||||
|
||||
auto lastPaintBegin = usecTimestampNow();
|
||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
if (nullptr == _displayPlugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayPluginPointer displayPlugin;
|
||||
{
|
||||
PROFILE_RANGE(render, "/getActiveDisplayPlugin");
|
||||
displayPlugin = getActiveDisplayPlugin();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render, "/pluginBeginFrameRender");
|
||||
// If a display plugin loses it's underlying support, it
|
||||
// needs to be able to signal us to not use it
|
||||
if (!displayPlugin->beginFrameRender(_renderFrameCount)) {
|
||||
QMetaObject::invokeMethod(this, "updateDisplayMode");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RenderArgs renderArgs;
|
||||
glm::mat4 HMDSensorPose;
|
||||
glm::mat4 eyeToWorld;
|
||||
glm::mat4 sensorToWorld;
|
||||
|
||||
bool isStereo;
|
||||
glm::mat4 stereoEyeOffsets[2];
|
||||
glm::mat4 stereoEyeProjections[2];
|
||||
|
||||
{
|
||||
QMutexLocker viewLocker(&_renderArgsMutex);
|
||||
renderArgs = _appRenderArgs._renderArgs;
|
||||
|
||||
// don't render if there is no context.
|
||||
if (!_appRenderArgs._renderArgs._context) {
|
||||
return;
|
||||
}
|
||||
|
||||
HMDSensorPose = _appRenderArgs._headPose;
|
||||
eyeToWorld = _appRenderArgs._eyeToWorld;
|
||||
sensorToWorld = _appRenderArgs._sensorToWorld;
|
||||
isStereo = _appRenderArgs._isStereo;
|
||||
for_each_eye([&](Eye eye) {
|
||||
stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye];
|
||||
stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye];
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render, "/gpuContextReset");
|
||||
_gpuContext->beginFrame(_appRenderArgs._view, HMDSensorPose);
|
||||
// Reset the gpu::Context Stages
|
||||
// Back to the default framebuffer;
|
||||
gpu::doInBatch("Application_render::gpuContextReset", _gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render, "/renderOverlay");
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// NOTE: There is no batch associated with this renderArgs
|
||||
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
|
||||
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render, "/updateCompositor");
|
||||
getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer finalFramebuffer;
|
||||
QSize finalFramebufferSize;
|
||||
{
|
||||
PROFILE_RANGE(render, "/getOutputFramebuffer");
|
||||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
finalFramebufferSize = framebufferCache->getFrameBufferSize();
|
||||
// Final framebuffer that will be handed to the display-plugin
|
||||
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
}
|
||||
|
||||
{
|
||||
if (isStereo) {
|
||||
renderArgs._context->enableStereo(true);
|
||||
renderArgs._context->setStereoProjections(stereoEyeProjections);
|
||||
renderArgs._context->setStereoViews(stereoEyeOffsets);
|
||||
}
|
||||
|
||||
renderArgs._hudOperator = displayPlugin->getHUDOperator();
|
||||
renderArgs._hudTexture = _applicationOverlay.getOverlayTexture();
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
runRenderFrame(&renderArgs);
|
||||
}
|
||||
|
||||
auto frame = _gpuContext->endFrame();
|
||||
frame->frameIndex = _renderFrameCount;
|
||||
frame->framebuffer = finalFramebuffer;
|
||||
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) {
|
||||
auto frameBufferCache = DependencyManager::get<FramebufferCache>();
|
||||
if (frameBufferCache) {
|
||||
frameBufferCache->releaseFramebuffer(framebuffer);
|
||||
}
|
||||
};
|
||||
// deliver final scene rendering commands to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(render, "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
_renderLoopCounter.increment();
|
||||
displayPlugin->submitFrame(frame);
|
||||
}
|
||||
|
||||
// Reset the framebuffer and stereo state
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._context->enableStereo(false);
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
{
|
||||
auto stats = Stats::getInstance();
|
||||
if (stats) {
|
||||
stats->setRenderDetails(renderArgs._details);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin;
|
||||
_frameTimingsScriptingInterface.addValue(lastPaintDuration);
|
||||
}
|
||||
|
||||
|
||||
// WorldBox Render Data & rendering functions
|
||||
|
||||
class WorldBoxRenderData {
|
||||
public:
|
||||
typedef render::Payload<WorldBoxRenderData> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
int _val = 0;
|
||||
static render::ItemID _item; // unique WorldBoxRenderData
|
||||
};
|
||||
|
||||
render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID };
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); }
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
|
||||
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
|
||||
PerformanceTimer perfTimer("worldBox");
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch);
|
||||
renderWorldBox(args, batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::runRenderFrame(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
PerformanceTimer perfTimer("display");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::runRenderFrame()");
|
||||
|
||||
// The pending changes collecting the changes here
|
||||
render::Transaction transaction;
|
||||
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
// render models...
|
||||
PerformanceTimer perfTimer("entities");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::runRenderFrame() ... entities...");
|
||||
|
||||
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
|
||||
|
||||
renderArgs->_debugFlags = renderDebugFlags;
|
||||
}
|
||||
|
||||
// Make sure the WorldBox is in the scene
|
||||
// For the record, this one RenderItem is the first one we created and added to the scene.
|
||||
// We could move that code elsewhere but you know...
|
||||
if (!render::Item::isValidID(WorldBoxRenderData::_item)) {
|
||||
auto worldBoxRenderData = std::make_shared<WorldBoxRenderData>();
|
||||
auto worldBoxRenderPayload = std::make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);
|
||||
|
||||
WorldBoxRenderData::_item = _main3DScene->allocateID();
|
||||
|
||||
transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
|
||||
_main3DScene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("EngineRun");
|
||||
_renderEngine->getRenderContext()->args = renderArgs;
|
||||
_renderEngine->run();
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
|||
EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties);
|
||||
|
||||
entityProperties.setParentID(myNodeID);
|
||||
entityProperties.setClientOnly(true);
|
||||
entityProperties.setEntityHostType(entity::HostType::AVATAR);
|
||||
entityProperties.setOwningAvatarID(myNodeID);
|
||||
entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY);
|
||||
entityProperties.markAllChanged();
|
||||
|
@ -247,25 +247,35 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() {
|
|||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
|
||||
QScriptEngine scriptEngine;
|
||||
QVariantList wearableEntities;
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
auto avatarEntities = myAvatar->getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity || !isWearableEntity(entity)) {
|
||||
continue;
|
||||
|
||||
if (entityTree) {
|
||||
QScriptEngine scriptEngine;
|
||||
auto avatarEntities = myAvatar->getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity || !isWearableEntity(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariantMap avatarEntityData;
|
||||
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
desiredProperties -= PROP_JOINT_ROTATIONS_SET;
|
||||
desiredProperties -= PROP_JOINT_ROTATIONS;
|
||||
desiredProperties -= PROP_JOINT_TRANSLATIONS_SET;
|
||||
desiredProperties -= PROP_JOINT_TRANSLATIONS;
|
||||
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
wearableEntities.append(QVariant(avatarEntityData));
|
||||
}
|
||||
QVariantMap avatarEntityData;
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
wearableEntities.append(QVariant(avatarEntityData));
|
||||
}
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, wearableEntities);
|
||||
return bookmark;
|
||||
|
|
241
interface/src/LoginStateManager.cpp
Normal file
|
@ -0,0 +1,241 @@
|
|||
//
|
||||
// LoginStateManager.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Wayne Chen on 11/5/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "LoginStateManager.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariantMap>
|
||||
|
||||
#include <plugins/PluginUtils.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "controllers/StateController.h"
|
||||
#include "controllers/UserInputMapper.h"
|
||||
#include "raypick/PointerScriptingInterface.h"
|
||||
#include "raypick/RayPickScriptingInterface.h"
|
||||
#include "raypick/PickScriptingInterface.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
|
||||
static const float SEARCH_SPHERE_SIZE = 0.0132f;
|
||||
static const QVariantMap SEARCH_SPHERE = {{"x", SEARCH_SPHERE_SIZE},
|
||||
{"y", SEARCH_SPHERE_SIZE},
|
||||
{"z", SEARCH_SPHERE_SIZE}};
|
||||
|
||||
static const int DEFAULT_SEARCH_SPHERE_DISTANCE = 1000; // how far from camera to search intersection?
|
||||
|
||||
static const QVariantMap COLORS_GRAB_SEARCHING_HALF_SQUEEZE = {{"red", 10},
|
||||
{"green", 10},
|
||||
{"blue", 255}};
|
||||
|
||||
static const QVariantMap COLORS_GRAB_SEARCHING_FULL_SQUEEZE = {{"red", 250},
|
||||
{"green", 10},
|
||||
{"blue", 10}};
|
||||
|
||||
static const QVariantMap COLORS_GRAB_DISTANCE_HOLD = {{"red", 238},
|
||||
{"green", 75},
|
||||
{"blue", 214}};
|
||||
|
||||
|
||||
|
||||
void LoginStateManager::tearDown() {
|
||||
auto pointers = DependencyManager::get<PointerManager>().data();
|
||||
if (pointers) {
|
||||
if (_leftLoginPointerID > PointerEvent::INVALID_POINTER_ID) {
|
||||
pointers->removePointer(_leftLoginPointerID);
|
||||
_leftLoginPointerID = PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
if (_rightLoginPointerID > PointerEvent::INVALID_POINTER_ID) {
|
||||
pointers->removePointer(_rightLoginPointerID);
|
||||
_rightLoginPointerID = PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoginStateManager::setUp() {
|
||||
QVariantMap fullPathRenderState {
|
||||
{"type", "line3d"},
|
||||
{"color", COLORS_GRAB_SEARCHING_FULL_SQUEEZE},
|
||||
{"visible", true},
|
||||
{"alpha", 1.0f},
|
||||
{"solid", true},
|
||||
{"glow", 1.0f},
|
||||
{"ignoreRayIntersection", true}, // always ignore this
|
||||
{"drawInFront", true}, // Even when burried inside of something, show it.
|
||||
{"drawHUDLayer", false}
|
||||
};
|
||||
QVariantMap fullEndRenderState {
|
||||
{"type", "sphere"},
|
||||
{"dimensions", SEARCH_SPHERE},
|
||||
{"solid", true},
|
||||
{"color", COLORS_GRAB_SEARCHING_FULL_SQUEEZE},
|
||||
{"alpha", 0.9f},
|
||||
{"ignoreRayIntersection", true},
|
||||
{"drawInFront", true}, // Even when burried inside of something, show it.
|
||||
{"drawHUDLayer", false},
|
||||
{"visible", true}
|
||||
};
|
||||
QVariantMap halfPathRenderState {
|
||||
{"type", "line3d"},
|
||||
{"color", COLORS_GRAB_SEARCHING_HALF_SQUEEZE},
|
||||
{"visible", true},
|
||||
{"alpha", 1.0f},
|
||||
{"solid", true},
|
||||
{"glow", 1.0f},
|
||||
{"ignoreRayIntersection", true}, // always ignore this
|
||||
{"drawInFront", true}, // Even when burried inside of something, show it.
|
||||
{"drawHUDLayer", false}
|
||||
};
|
||||
QVariantMap halfEndRenderState {
|
||||
{"type", "sphere"},
|
||||
{"dimensions", SEARCH_SPHERE},
|
||||
{"solid", true},
|
||||
{"color", COLORS_GRAB_SEARCHING_HALF_SQUEEZE},
|
||||
{"alpha", 0.9f},
|
||||
{"ignoreRayIntersection", true},
|
||||
{"drawInFront", true}, // Even when burried inside of something, show it.
|
||||
{"drawHUDLayer", false},
|
||||
{"visible", true}
|
||||
};
|
||||
QVariantMap holdPathRenderState {
|
||||
{"type", "line3d"},
|
||||
{"color", COLORS_GRAB_DISTANCE_HOLD},
|
||||
{"visible", true},
|
||||
{"alpha", 1.0f},
|
||||
{"solid", true},
|
||||
{"glow", 1.0f},
|
||||
{"ignoreRayIntersection", true}, // always ignore this
|
||||
{"drawInFront", true}, // Even when burried inside of something, show it.
|
||||
{"drawHUDLayer", false},
|
||||
};
|
||||
QVariantMap halfRenderStateIdentifier {
|
||||
{"name", "half"},
|
||||
{"path", halfPathRenderState},
|
||||
{"end", halfEndRenderState}
|
||||
};
|
||||
QVariantMap fullRenderStateIdentifier {
|
||||
{"name", "full"},
|
||||
{"path", fullPathRenderState},
|
||||
{"end", fullEndRenderState}
|
||||
};
|
||||
QVariantMap holdRenderStateIdentifier {
|
||||
{"name", "hold"},
|
||||
{"path", holdPathRenderState},
|
||||
};
|
||||
|
||||
QVariantMap halfDefaultRenderStateIdentifier {
|
||||
{"name", "half"},
|
||||
{"distance", DEFAULT_SEARCH_SPHERE_DISTANCE},
|
||||
{"path", halfPathRenderState}
|
||||
};
|
||||
QVariantMap fullDefaultRenderStateIdentifier {
|
||||
{"name", "full"},
|
||||
{"distance", DEFAULT_SEARCH_SPHERE_DISTANCE},
|
||||
{"path", fullPathRenderState}
|
||||
};
|
||||
QVariantMap holdDefaultRenderStateIdentifier {
|
||||
{"name", "hold"},
|
||||
{"distance", DEFAULT_SEARCH_SPHERE_DISTANCE},
|
||||
{"path", holdPathRenderState}
|
||||
};
|
||||
|
||||
_renderStates = QList<QVariant>({halfRenderStateIdentifier, fullRenderStateIdentifier, holdRenderStateIdentifier});
|
||||
_defaultRenderStates = QList<QVariant>({halfDefaultRenderStateIdentifier, fullDefaultRenderStateIdentifier, holdDefaultRenderStateIdentifier});
|
||||
|
||||
auto pointers = DependencyManager::get<PointerScriptingInterface>();
|
||||
auto controller = DependencyManager::get<controller::ScriptingInterface>();
|
||||
|
||||
const glm::vec3 grabPointSphereOffsetLeft { -0.04f, 0.13f, 0.039f }; // x = upward, y = forward, z = lateral
|
||||
const glm::vec3 grabPointSphereOffsetRight { 0.04f, 0.13f, 0.039f }; // x = upward, y = forward, z = lateral
|
||||
const glm::vec3 malletOffset {glm::vec3(0.0f, 0.18f - 0.050f, 0.0f)};
|
||||
|
||||
QList<QVariant> leftPointerTriggerProperties;
|
||||
QVariantMap ltClick1 {
|
||||
{ "action", controller->getStandard()["LTClick"] },
|
||||
{ "button", "Focus" }
|
||||
};
|
||||
QVariantMap ltClick2 {
|
||||
{ "action", controller->getStandard()["LTClick"] },
|
||||
{ "button", "Primary" }
|
||||
};
|
||||
|
||||
leftPointerTriggerProperties = QList<QVariant>({ltClick1, ltClick2});
|
||||
const unsigned int leftHand = 0;
|
||||
QVariantMap leftPointerProperties {
|
||||
{ "joint", "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND" },
|
||||
{ "filter", PickScriptingInterface::PICK_OVERLAYS() },
|
||||
{ "triggers", leftPointerTriggerProperties },
|
||||
{ "posOffset", vec3toVariant(grabPointSphereOffsetLeft + malletOffset) },
|
||||
{ "hover", true },
|
||||
{ "scaleWithParent", true },
|
||||
{ "distanceScaleEnd", true },
|
||||
{ "hand", leftHand }
|
||||
};
|
||||
leftPointerProperties["renderStates"] = _renderStates;
|
||||
leftPointerProperties["defaultRenderStates"] = _defaultRenderStates;
|
||||
_leftLoginPointerID = pointers->createPointer(PickQuery::PickType::Ray, leftPointerProperties);
|
||||
pointers->setRenderState(_leftLoginPointerID, "");
|
||||
pointers->enablePointer(_leftLoginPointerID);
|
||||
const unsigned int rightHand = 1;
|
||||
QList<QVariant> rightPointerTriggerProperties;
|
||||
|
||||
QVariantMap rtClick1 {
|
||||
{ "action", controller->getStandard()["RTClick"] },
|
||||
{ "button", "Focus" }
|
||||
};
|
||||
QVariantMap rtClick2 {
|
||||
{ "action", controller->getStandard()["RTClick"] },
|
||||
{ "button", "Primary" }
|
||||
};
|
||||
rightPointerTriggerProperties = QList<QVariant>({rtClick1, rtClick2});
|
||||
QVariantMap rightPointerProperties{
|
||||
{ "joint", "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" },
|
||||
{ "filter", PickScriptingInterface::PICK_OVERLAYS() },
|
||||
{ "triggers", rightPointerTriggerProperties },
|
||||
{ "posOffset", vec3toVariant(grabPointSphereOffsetRight + malletOffset) },
|
||||
{ "hover", true },
|
||||
{ "scaleWithParent", true },
|
||||
{ "distanceScaleEnd", true },
|
||||
{ "hand", rightHand }
|
||||
};
|
||||
rightPointerProperties["renderStates"] = _renderStates;
|
||||
rightPointerProperties["defaultRenderStates"] = _defaultRenderStates;
|
||||
_rightLoginPointerID = pointers->createPointer(PickQuery::PickType::Ray, rightPointerProperties);
|
||||
pointers->setRenderState(_rightLoginPointerID, "");
|
||||
pointers->enablePointer(_rightLoginPointerID);
|
||||
}
|
||||
|
||||
void LoginStateManager::update(const QString dominantHand, const QUuid loginOverlayID) {
|
||||
if (!isSetUp()) {
|
||||
return;
|
||||
}
|
||||
if (_dominantHand != dominantHand) {
|
||||
_dominantHand = dominantHand;
|
||||
}
|
||||
auto pointers = DependencyManager::get<PointerScriptingInterface>();
|
||||
auto raypicks = DependencyManager::get<RayPickScriptingInterface>();
|
||||
if (pointers && raypicks) {
|
||||
const auto rightObjectID = raypicks->getPrevRayPickResult(_rightLoginPointerID)["objectID"].toUuid();
|
||||
const auto leftObjectID = raypicks->getPrevRayPickResult(_leftLoginPointerID)["objectID"].toUuid();
|
||||
const QString leftMode = (leftObjectID.isNull() || leftObjectID != loginOverlayID) ? "" : "full";
|
||||
const QString rightMode = (rightObjectID.isNull() || rightObjectID != loginOverlayID) ? "" : "full";
|
||||
pointers->setRenderState(_leftLoginPointerID, leftMode);
|
||||
pointers->setRenderState(_rightLoginPointerID, rightMode);
|
||||
if (_dominantHand == "left" && !leftObjectID.isNull()) {
|
||||
// dominant is left.
|
||||
pointers->setRenderState(_rightLoginPointerID, "");
|
||||
pointers->setRenderState(_leftLoginPointerID, leftMode);
|
||||
} else if (_dominantHand == "right" && !rightObjectID.isNull()) {
|
||||
// dominant is right.
|
||||
pointers->setRenderState(_leftLoginPointerID, "");
|
||||
pointers->setRenderState(_rightLoginPointerID, rightMode);
|
||||
}
|
||||
}
|
||||
}
|