Merge branch 'master' into otherAvatarMotionStates01

This commit is contained in:
luiscuenca 2018-12-30 10:55:05 -07:00
commit bd21d0589a
496 changed files with 14968 additions and 6862 deletions

21
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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')
}

View file

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

View file

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

Binary file not shown.

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

View file

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

View file

@ -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();

View file

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

View file

@ -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();
}
}
}

View file

@ -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();
}
}

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

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

View file

@ -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>();

View file

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

View file

@ -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"]) {

View file

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

View file

@ -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
View 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
View 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
View 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
View 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")

View file

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

View file

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

View file

@ -1,16 +1,2 @@
{
"RenderMainView": {
"RenderShadowTask": {
"Enabled": {
"enabled": true
}
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"Enabled": {
"enabled": true
}
}
}
}
}

View 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" }
]
}

View file

@ -154,4 +154,6 @@
<glyph glyph-name="40-reload" unicode="&#70;" 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="&#68;" 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="&#84;" 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="&#57397;" 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="&#57398;" 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

View file

@ -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="&#xe035;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe035;">
</li>
<li>
<div data-icon="&#xe036;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe036;">
</li>
</ul>
</div>
<script>(function() {

View file

@ -479,3 +479,9 @@
.icon-avatar-1:before {
content: "\54";
}
.icon-steam-square:before {
content: "\e035";
}
.icon-oculus:before {
content: "\e036";
}

View 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

View 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

View file

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

View file

@ -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 });
}
}

View file

@ -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 = "";
}
}

View file

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

View 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 });
}
}
}
}

View 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
//
}

View file

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

View file

@ -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;
});
}
}
}
}

View file

@ -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;
});
}
}
}
}

View file

@ -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()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View 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 });
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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 });
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

@ -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();
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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.";

View file

@ -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'});
}
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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"];

View file

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

View file

@ -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);
});
}
}

View file

@ -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);
});
}
}

View file

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

View file

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

View file

@ -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();
}
}
}
}

View file

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

View file

@ -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));;
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View 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

View file

@ -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();
}
}

View file

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

View 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);
}
}
}

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