Merge branch 'master' into tablet_extended_sounds

This commit is contained in:
vladest 2017-10-12 18:01:23 +02:00
commit 145149fedd
345 changed files with 16917 additions and 5389 deletions

9
.gitignore vendored
View file

@ -17,6 +17,15 @@ Makefile
local.properties
android/libraries
# VSCode
# List taken from Github Global Ignores master@435c4d92
# https://github.com/github/gitignore/commits/master/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Xcode
*.xcodeproj
*.xcworkspace

View file

@ -1,29 +1,28 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only macOS specific instructions are found in this file.
### Homebrew
[Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple.
[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some High Fidelity dependencies very simple.
brew tap homebrew/versions
brew install cmake openssl
brew install cmake openssl qt
### OpenSSL
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2l
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
### Qt
Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg).
Assuming you've installed Qt using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installations.
For Qt installed via homebrew, set QT_CMAKE_PREFIX_PATH:
Keep the default components checked when going through the installer.
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.9.1/lib/cmake
Once Qt is installed, you need to manually configure the following:
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory.
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
### Xcode

View file

@ -112,9 +112,8 @@ if (BUILD_CLIENT OR BUILD_SERVER)
add_subdirectory(plugins)
endif()
if (BUILD_TOOLS)
add_subdirectory(tools)
endif()
# BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway
add_subdirectory(tools)
if (BUILD_TESTS)
add_subdirectory(tests)

View file

@ -1162,7 +1162,8 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath,
_pendingBakes.remove(originalAssetHash);
}
void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector<QString> bakedFilePaths) {
void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath,
QString bakedTempOutputDir, QVector<QString> bakedFilePaths) {
bool errorCompletingBake { false };
QString errorReason;
@ -1234,6 +1235,16 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
}
}
for (auto& filePath : bakedFilePaths) {
QFile file(filePath);
if (!file.remove()) {
qWarning() << "Failed to remove temporary file:" << filePath;
}
}
if (!QDir(bakedTempOutputDir).rmdir(".")) {
qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir;
}
if (!errorCompletingBake) {
// create the meta file to store which version of the baking process we just completed
writeMetaFile(originalAssetHash);

View file

@ -100,7 +100,8 @@ private:
void bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath);
/// Move baked content for asset to baked directory and update baked status
void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector<QString> bakedFilePaths);
void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir,
QVector<QString> bakedFilePaths);
void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors);
void handleAbortedBake(QString originalAssetHash, QString assetPath);

View file

@ -24,20 +24,39 @@ BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetP
}
void cleanupTempFiles(QString tempOutputDir, std::vector<QString> files) {
for (const auto& filename : files) {
QFile f { filename };
if (!f.remove()) {
qDebug() << "Failed to remove:" << filename;
}
}
if (!tempOutputDir.isEmpty()) {
QDir dir { tempOutputDir };
if (!dir.rmdir(".")) {
qDebug() << "Failed to remove temporary directory:" << tempOutputDir;
}
}
};
void BakeAssetTask::run() {
_isBaking.store(true);
qRegisterMetaType<QVector<QString> >("QVector<QString>");
TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); };
QString tempOutputDir;
if (_assetPath.endsWith(".fbx")) {
tempOutputDir = PathUtils::generateTemporaryDir();
_baker = std::unique_ptr<FBXBaker> {
new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir())
new FBXBaker(QUrl("file:///" + _filePath), fn, tempOutputDir)
};
} else {
tempOutputDir = PathUtils::generateTemporaryDir();
_baker = std::unique_ptr<TextureBaker> {
new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE,
PathUtils::generateTemporaryDir())
tempOutputDir)
};
}
@ -52,6 +71,8 @@ void BakeAssetTask::run() {
_wasAborted.store(true);
cleanupTempFiles(tempOutputDir, _baker->getOutputFiles());
emit bakeAborted(_assetHash, _assetPath);
} else if (_baker->hasErrors()) {
qDebug() << "Failed to bake: " << _assetHash << _assetPath << _baker->getErrors();
@ -60,6 +81,8 @@ void BakeAssetTask::run() {
_didFinish.store(true);
cleanupTempFiles(tempOutputDir, _baker->getOutputFiles());
emit bakeFailed(_assetHash, _assetPath, errors);
} else {
auto vectorOutputFiles = QVector<QString>::fromStdVector(_baker->getOutputFiles());
@ -68,7 +91,7 @@ void BakeAssetTask::run() {
_didFinish.store(true);
emit bakeComplete(_assetHash, _assetPath, vectorOutputFiles);
emit bakeComplete(_assetHash, _assetPath, tempOutputDir, vectorOutputFiles);
}
}

View file

@ -35,7 +35,7 @@ public:
bool didFinish() const { return _didFinish.load(); }
signals:
void bakeComplete(QString assetHash, QString assetPath, QVector<QString> outputFiles);
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector<QString> outputFiles);
void bakeFailed(QString assetHash, QString assetPath, QString errors);
void bakeAborted(QString assetHash, QString assetPath);

View file

@ -97,7 +97,11 @@ void AudioMixerSlavePool::run(ConstIter begin, ConstIter end) {
#else
// fill the queue
std::for_each(_begin, _end, [&](const SharedNodePointer& node) {
#if defined(__clang__) && defined(Q_OS_LINUX)
_queue.push(node);
#else
_queue.emplace(node);
#endif
});
{

View file

@ -97,7 +97,11 @@ void AvatarMixerSlavePool::run(ConstIter begin, ConstIter end) {
#else
// fill the queue
std::for_each(_begin, _end, [&](const SharedNodePointer& node) {
#if defined(__clang__) && defined(Q_OS_LINUX)
_queue.push(node);
#else
_queue.emplace(node);
#endif
});
{

View file

@ -9,7 +9,7 @@ ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple_1.13.0.zip
URL_MD5 73f833649e904257b35bf4e84f8bdfb5
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1

View file

@ -31,7 +31,7 @@ else ()
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi.zip
URL_MD5 5794b950f8b265a9a41b2839b3bf7ebb
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1

View file

@ -14,9 +14,17 @@ endif ()
if (HIFI_MEMORY_DEBUGGING)
if (UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -U_FORTIFY_SOURCE -fno-stack-protector -fno-omit-frame-pointer")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# for clang on Linux
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
else ()
# for gcc on Linux
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -U_FORTIFY_SOURCE -fno-stack-protector -fno-omit-frame-pointer")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=undefined -fsanitize=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=undefined -fsanitize=address")
endif()
endif (UNIX)
endif ()
endmacro(SETUP_MEMORY_DEBUGGER)

View file

@ -44,7 +44,11 @@ endfunction()
# Sets the QT_CMAKE_PREFIX_PATH and QT_DIR variables
# Also enables CMAKE_AUTOMOC and CMAKE_AUTORCC
macro(setup_qt)
set(QT_CMAKE_PREFIX_PATH "$ENV{QT_CMAKE_PREFIX_PATH}")
# if QT_CMAKE_PREFIX_PATH was not specified before hand,
# try to use the environment variable
if (NOT QT_CMAKE_PREFIX_PATH)
set(QT_CMAKE_PREFIX_PATH "$ENV{QT_CMAKE_PREFIX_PATH}")
endif()
if (("QT_CMAKE_PREFIX_PATH" STREQUAL "") OR (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}"))
calculate_default_qt_dir(QT_DIR)
set(QT_CMAKE_PREFIX_PATH "${QT_DIR}/lib/cmake")
@ -81,4 +85,4 @@ macro(setup_qt)
add_paths_to_fixup_libs("${QT_DIR}/bin")
endif ()
endmacro()
endmacro()

View file

@ -1,3 +1,3 @@
// Here you can put a script that will be run by an assignment-client (AC)
// For examples, please go to http://public.highfidelity.io/scripts
// For examples, please go to https://github.com/highfidelity/hifi/tree/master/script-archive/acScripts
// The directory named acScripts contains assignment-client specific scripts you can try.

View file

@ -262,7 +262,13 @@ target_link_libraries(
)
if (UNIX)
target_link_libraries(${TARGET_NAME} pthread)
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
# Linux
target_link_libraries(${TARGET_NAME} pthread atomic)
else ()
# OSX
target_link_libraries(${TARGET_NAME} pthread)
endif ()
endif(UNIX)
# assume we are using a Qt build without bearer management

View file

@ -0,0 +1,609 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body
{
margin: 0;
padding: 0;
color: #333;
background-color: #eee;
font: 300 16px/22px "Lato", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: rgba(74,219,255,1);
background: -moz-linear-gradient(-45deg, rgba(74,219,255,1) 0%, rgba(61,171,255,1) 41%, rgba(54,124,209,1) 100%);
background: -webkit-gradient(left top, right bottom, color-stop(0%, rgba(74,219,255,1)), color-stop(41%, rgba(61,171,255,1)), color-stop(100%, rgba(54,124,209,1)));
background: -webkit-linear-gradient(-45deg, rgba(74,219,255,1) 0%, rgba(61,171,255,1) 41%, rgba(54,124,209,1) 100%);
background: -o-linear-gradient(-45deg, rgba(74,219,255,1) 0%, rgba(61,171,255,1) 41%, rgba(54,124,209,1) 100%);
background: -ms-linear-gradient(-45deg, rgba(74,219,255,1) 0%, rgba(61,171,255,1) 41%, rgba(54,124,209,1) 100%);
background: linear-gradient(135deg, rgba(74,219,255,1) 0%, rgba(61,171,255,1) 41%, rgba(54,124,209,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4adbff', endColorstr='#367cd1', GradientType=1 );
}
h1,h2,h3,h4,h5,h6
{
margin: 0 0 .5em;
font-weight: 500;
line-height: 1.1;
}
h1 { font-size: 2.25em; } /* 36px */
h2 { font-size: 1.75em; } /* 28px */
h3 { font-size: 1.375em; } /* 22px */
h4 { font-size: 1.125em; } /* 18px */
h5 { font-size: 1em; } /* 16px */
h6 { font-size: .875em; } /* 14px */
p
{
margin: 0 0 1.5em;
line-height: 1.5;
}
blockquote
{
padding: 1em 2em;
margin: 0 0 2em;
border-left: 5px solid #eee;
}
hr
{
height: 0;
margin-top: 1em;
margin-bottom: 2em;
border: 0;
border-top: 1px solid #ddd;
}
table
{
background-color: transparent;
border-spacing: 0;
border-collapse: collapse;
border-top: 1px solid #ddd;
}
th, td
{
padding: .5em 1em;
vertical-align: top;
text-align: left;
border-bottom: 1px solid #ddd;
}
a:link { color: #0093C5; }
a:visited { color: #0093C5; }
a:focus { color: black; }
a:hover { color: #00B4EF; }
a:active { color: #00B4EF; }
/* -----------------------
Layout styles
------------------------*/
.container
{
max-width: 50em;
margin: 0 auto;
background-color: #fff;
}
.header
{
color: #fff;
background: #333;
padding: 1em 1.25em;
}
.header-heading { margin: 0; }
.nav-bar
{
background: #000;
padding: 0;
}
.content { padding: 1em 1.25em; }
.footer
{
color: #fff;
background: #000;
}
.alert
{
padding: 1em 1.25em;
border-radius: 2px;
border-width: thin;
border-color: #ccc;
background: #eee;
}
/* -----------------------
Nav
------------------------*/
.nav
{
margin: 0;
padding: 0;
list-style: none;
}
.nav li
{
display: inline;
margin: 0;
}
.nav a
{
display: block;
padding: .7em 1.25em;
color: #fff;
text-decoration: none;
border-bottom: 1px solid gray;
}
.nav a:link { color: white; }
.nav a:visited { color: white; }
.nav a:focus
{
color: black;
background-color: white;
}
.nav a:hover
{
color: white;
background-color: green;
}
.nav a:active
{
color: white;
background-color: red;
}
/* -----------------------
Single styles
------------------------*/
.img-responsive { max-width: 100%; }
.btn
{
color: #fff !important;
background-color: royalblue;
border-color: #222;
display: inline-block;
padding: .5em 1em;
margin-bottom: 0;
font-weight: 400;
line-height: 1.2;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: .2em;
text-decoration: none;
}
.btn:hover
{
color: #fff !important;
background-color: #0093C5;
}
.btn:focus
{
color: #fff !important;
background-color: black;
}
.btn:active
{
color: #fff !important;
background-color: black;
}
.table
{
width: 100%;
max-width: 100%;
margin-bottom: 20px;
}
.list-unstyled
{
padding-left: 0;
list-style: none;
}
.list-inline
{
padding-left: 0;
margin-left: -5px;
list-style: none;
}
.list-inline > li
{
display: inline-block;
padding-right: 5px;
padding-left: 5px;
}
/* -----------------------
Wide styles
------------------------*/
@media (min-width: 42em)
{
.header { padding: 1.5em 3em; }
.nav-bar { padding: 1em 3em; }
.content { padding: 2em 3em; }
.footer { padding: 2em 3em; }
.nav li
{
display: inline;
margin: 0 1em 0 0;
}
.nav a
{
display: inline;
padding: 0;
border-bottom: 0;
}
}
</style>
<title>Backing Up Your Private Keys | High Fidelity</title>
</head>
<body>
<div class="container">
<div class="header">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1200 95" style="enable-background:new 0 0 1200 95;" xml:space="preserve">
<style type="text/css">
.st15{fill:#10bcff;}
.st16{fill:#939598;}
</style>
<g id="Layer_1_1_">
<path class="st15" d="M144,35.8v29.2h-6.3V52.3h-10.3v12.8h-6.3V35.8h6.3v11.3h10.3V35.8H144z"/>
<path class="st15" d="M155.1,65.1h-6.3V35.8h6.3V65.1z"/>
<path class="st15" d="M173.2,40.5c-1.5,0-2.7,0.1-3.8,0.4c-1,0.3-1.8,0.8-2.5,1.4c-0.6,0.6-1.1,1.6-1.4,2.7s-0.4,2.5-0.4,4.2v2.7
c0,1.8,0.1,3.2,0.4,4.3c0.2,1.1,0.6,2,1.2,2.6c0.5,0.6,1.3,1.1,2.2,1.3c0.9,0.2,2,0.4,3.3,0.4c0.4,0,0.8,0,1.2-0.1
c0.4,0,0.8-0.1,1.3-0.1v-7.9h-3.7l0.6-4.8h9v16.7c-1.1,0.4-2.5,0.7-4.3,0.9c-1.7,0.2-3.4,0.4-5.2,0.4c-2.2,0-4.1-0.3-5.7-0.8
c-1.6-0.5-2.8-1.4-3.8-2.5c-1-1.1-1.7-2.5-2.2-4.2c-0.5-1.7-0.6-3.7-0.6-5.9v-3c0-2.1,0.2-3.9,0.6-5.6c0.5-1.7,1.2-3.1,2.3-4.3
c1.1-1.2,2.5-2.1,4.2-2.8c1.7-0.6,3.9-1,6.5-1c1.4,0,2.8,0.1,4.2,0.4c1.4,0.2,2.5,0.5,3.4,0.8l-1,4.7c-0.8-0.2-1.7-0.4-2.6-0.6
C175.5,40.5,174.5,40.5,173.2,40.5z"/>
<path class="st15" d="M208.8,35.8v29.2h-6.3V52.3h-10.3v12.8H186V35.8h6.3v11.3h10.3V35.8H208.8z"/>
<path class="st15" d="M242.2,35.8l-0.7,5.1h-10.9V47h10.1l-0.7,5.1h-9.4v13h-6.3V35.8H242.2z"/>
<path class="st15" d="M252.3,65.1h-6.3V35.8h6.3V65.1z"/>
<path class="st15" d="M257.4,35.8h9.6c2.5,0,4.5,0.4,6.2,1s3,1.6,4,2.7c1,1.2,1.7,2.5,2.1,4.2c0.4,1.7,0.6,3.4,0.6,5.3v2.8
c0,1.9-0.2,3.7-0.6,5.4c-0.4,1.7-1.1,3-2.1,4.2c-1,1.2-2.3,2.1-4,2.7c-1.7,0.6-3.7,1-6.2,1h-9.5L257.4,35.8L257.4,35.8z M263.6,60
h2.7c1.2,0,2.2-0.1,3.1-0.4c0.9-0.3,1.7-0.8,2.2-1.4s1-1.5,1.3-2.6c0.3-1.1,0.4-2.4,0.4-4.1v-2.2c0-1.7-0.1-3-0.4-4.1
c-0.3-1.1-0.7-1.9-1.3-2.6s-1.3-1.1-2.2-1.4c-0.9-0.2-1.9-0.4-3.1-0.4h-2.7V60z"/>
<path class="st15" d="M302,35.8l-0.6,4.9h-11.3v6.4h10.6l-0.6,4.9h-10v8.2h11.9l-0.6,4.9h-17.6V35.8H302z"/>
<path class="st15" d="M312.4,35.8V60h11.9l-0.7,5.1h-17.4V35.8H312.4z"/>
<path class="st15" d="M334.6,65.1h-6.3V35.8h6.3V65.1z"/>
<path class="st15" d="M361.6,35.8l-0.7,5.1H353v24.2h-6.3V40.9h-8.6l0.7-5.1C338.8,35.8,361.6,35.8,361.6,35.8z"/>
<path class="st15" d="M376.5,47.2l6.1-11.4h6.4l-9.6,16.5v12.8h-6.3V52.3l-9.5-16.5h6.9L376.5,47.2z"/>
<g>
<path class="st15" d="M58.8,92c-5.9,0-11.7-1.2-17.1-3.4c-5.3-2.2-9.9-5.4-13.9-9.4c-4-4-7.2-8.7-9.4-13.9C16,59.7,14.8,54,14.8,48
c0-5.9,1.2-11.7,3.4-17.1c2.2-5.3,5.4-9.9,9.4-13.9s8.7-7.2,13.9-9.4C47,5.3,52.7,4.2,58.7,4.2s11.7,1.2,17.1,3.4
C81,9.8,85.7,13,89.7,17s7.2,8.7,9.4,13.9c2.3,5.4,3.4,11.2,3.4,17.1s-1.2,11.7-3.4,17.1c-2.2,5.3-5.4,9.9-9.4,13.9
c-4,4-8.7,7.2-13.9,9.4C70.5,90.8,64.7,92,58.8,92z M58.8,8.7C37.1,8.7,19.4,26.4,19.4,48s17.7,39.4,39.4,39.4S98.1,69.7,98.1,48
S80.5,8.7,58.8,8.7z"/>
<path class="st15" d="M72.2,66.7V35.9c1.9-0.6,3.3-2.5,3.3-4.6c0-2.7-2.2-4.8-4.8-4.8c-2.7,0-4.8,2.2-4.8,4.8
c0,2.1,1.2,3.8,3.1,4.5v14.4l-20.1-9.3V29.5c1.9-0.6,3.3-2.5,3.3-4.6c0-2.7-2.2-4.8-4.8-4.8c-2.7,0-4.8,2.2-4.8,4.8
c0,2.1,1.2,3.8,3.1,4.5v31c-1.8,0.7-3.1,2.5-3.1,4.5c0,2.7,2.2,4.8,4.8,4.8c2.7,0,4.8-2.2,4.8-4.8c0-2.1-1.4-4-3.3-4.6V44.7
l20.1,9.3v12.7c-1.8,0.7-3.1,2.5-3.1,4.5c0,2.7,2.2,4.8,4.8,4.8c2.7,0,4.8-2.2,4.8-4.8C75.5,69.1,74.1,67.4,72.2,66.7z"/>
</g>
</g>
<path class="st16" d="M418.8,34h2.6v38.6h-2.6V34z"/>
<path class="st16" d="M471.1,35.3v29.9h-2.7V50.8h-15.6v14.4h-2.7V35.3h2.7v13h15.6v-13H471.1z"/>
<path class="st16" d="M480.1,55.4c0,1.5,0.2,2.7,0.4,3.8c0.3,1,0.7,1.9,1.3,2.5c0.6,0.7,1.5,1.1,2.5,1.4s2.4,0.4,4,0.4
c0.9,0,1.8-0.1,2.8-0.2c1-0.1,1.8-0.3,2.5-0.4l-0.3,2.1c-0.6,0.2-1.5,0.4-2.5,0.5c-1,0.1-2,0.2-3,0.2c-1.9,0-3.5-0.2-4.8-0.7
c-1.3-0.4-2.4-1.1-3.2-1.9c-0.8-0.9-1.4-1.9-1.7-3.2c-0.4-1.3-0.5-2.8-0.5-4.5v-1.6c0-1.6,0.2-3,0.5-4.3c0.3-1.3,0.9-2.4,1.6-3.3
c0.7-0.9,1.7-1.6,2.8-2.1c1.1-0.5,2.5-0.7,4.2-0.7c1.4,0,2.7,0.2,3.7,0.7c1,0.4,1.9,1,2.6,1.8c0.7,0.8,1.2,1.7,1.5,2.7
c0.3,1,0.5,2.2,0.5,3.5v1.2c0,0.8-0.1,1.4-0.4,1.8c-0.2,0.3-0.8,0.5-1.6,0.5H480.1z M486.5,45.4c-1.1,0-2,0.1-2.8,0.4
c-0.8,0.3-1.5,0.7-2,1.4s-0.9,1.4-1.2,2.5c-0.3,1-0.4,2.3-0.4,3.7h12.3v-1.5c0-2.2-0.5-3.8-1.4-4.9C490.1,46,488.6,45.4,486.5,45.4z
"/>
<path class="st16" d="M500.8,34h2.6v31.2h-2.6V34z"/>
<path class="st16" d="M528.3,55.5c0,1.8-0.2,3.3-0.7,4.6c-0.4,1.3-1.1,2.3-1.9,3.2c-0.8,0.8-1.8,1.4-2.9,1.8
c-1.1,0.4-2.4,0.6-3.7,0.6c-0.7,0-1.3,0-1.8-0.1c-0.5-0.1-1.1-0.1-1.6-0.3c-0.5-0.1-1-0.3-1.5-0.5c-0.5-0.2-1-0.4-1.6-0.7V75h-2.6
V43.7h2.2l0.3,2.9c1.5-2.3,4-3.4,7.4-3.4c1.2,0,2.4,0.2,3.4,0.5c1,0.4,1.9,1,2.6,1.8c0.7,0.8,1.3,1.9,1.7,3.2
c0.4,1.3,0.6,2.9,0.6,4.8V55.5z M525.7,54.1c0-1.3-0.1-2.5-0.3-3.6c-0.2-1.1-0.5-2-1-2.7c-0.5-0.7-1.1-1.3-2-1.7
c-0.8-0.4-1.8-0.6-3.1-0.6c-1.3,0-2.3,0.2-3.2,0.5c-0.9,0.3-1.5,0.8-2.1,1.4c-0.5,0.6-0.9,1.4-1.1,2.3c-0.2,0.9-0.3,2-0.3,3.2v9
c1,0.5,1.9,0.9,2.8,1.2c0.9,0.2,2,0.4,3.2,0.4c1.3,0,2.4-0.2,3.3-0.5c0.9-0.3,1.6-0.9,2.1-1.6c0.5-0.7,0.9-1.6,1.2-2.6
c0.2-1.1,0.4-2.3,0.4-3.6V54.1z"/>
</svg>
</div>
<div class="content">
<div class="main">
<h1>Backing Up Your Private Keys</h1>
<hr>
<!-- Paragraphs -->
<h3>What are private keys?</h3>
<p>A private key is a secret piece of text that is used to prove ownership, unlock confidential information and sign transactions.</p>
<p>In High Fidelity, your private keys are used to securely access the contents of your Wallet and Purchases.</p>
<hr>
<h3>Where are my private keys stored?"</h3>
<p>By default, your private keys are only stored on your hard drive in High Fidelity Interface's <code>AppData</code> directory.</p>
<p>Here is the file path of your hifikey - you can browse to it using your file explorer.</p>
<div class="alert"> <code>HIFIKEY_PATH_REPLACEME</code> </div>
<hr>
<h3>How should I make a backup of my private keys?</h3>
<p>You should backup your <code>.hifikey</code> file above by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. Restore your backup by replacing the file in Interface's AppData directory with your backed-up copy.</p>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 497.3 186.1" style="enable-background:new 0 0 497.3 186.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CECECE;}
.st1{fill:#FFFFFF;}
.st2{fill:#FEFEFE;}
.st3{fill:#0DA860;}
.st4{fill:#F9C942;}
.st5{fill:#367CF3;}
.st6{fill:#2D6FDC;}
.st7{fill:#E4B83D;}
.st8{fill:#08137C;}
.st9{fill:#EF3B4E;}
.st10{fill:#B8355B;}
.st11{fill:#A7C5D1;}
.st12{fill:#7C909B;}
.st13{fill:#CBF2FD;}
.st14{fill:#000222;}
</style>
<circle class="st0" cx="255" cy="69.5" r="61.1"/>
<circle class="st0" cx="88" cy="69.5" r="61.1"/>
<circle class="st1" cx="88" cy="65.5" r="61.1"/>
<path class="st2" d="M133.4,78c0-0.1-0.1-0.2-0.1-0.3c-5-8.7-10-17.4-15-26c-4.7-8.2-9.4-16.3-14.1-24.5c-0.2-0.3-0.4-0.4-0.7-0.4
c-10.2,0-20.4,0-30.6,0c-0.2,0-0.3,0-0.5,0c-0.1,0.1-0.2,0.2-0.2,0.3c-9.7,16.8-19.4,33.6-29.1,50.5c-0.2,0.3-0.2,0.5,0,0.8
c4.2,7.2,8.4,14.5,12.6,21.7c1,1.7,2,3.5,3,5.2c0.1,0.1,0.3,0.1,0.5,0.1c19.4,0,38.8,0,58.2,0c0.4,0,0.6-0.1,0.8-0.4
c4.3-7.4,8.6-14.9,12.9-22.3C131.6,81.1,132.5,79.6,133.4,78z"/>
<path class="st3" d="M58.5,105.3c-1-1.7-2-3.4-3-5.2c-4.2-7.2-8.4-14.5-12.6-21.7c-0.2-0.3-0.2-0.5,0-0.8
C52.6,60.8,62.3,43.9,72,27.1c0.1-0.1,0.1-0.2,0.2-0.3c0.1,0.1,0.2,0.2,0.2,0.4c5.1,8.9,10.3,17.8,15.4,26.6
c0.1,0.1,0.2,0.2,0.2,0.4c-0.1,0.1-0.2,0.2-0.2,0.3c-3.2,5.5-6.4,11-9.6,16.5c-1.3,2.3-2.6,4.6-4,6.9c-0.1,0.1-0.2,0.2-0.2,0.3
c-1.9,3.3-3.8,6.6-5.7,9.9c-2.7,4.7-5.5,9.4-8.2,14.2c-0.5,0.9-1.1,1.8-1.5,2.7c0,0,0,0,0,0c0,0.1-0.1,0.1-0.1,0.2
C58.6,105.2,58.5,105.3,58.5,105.3z"/>
<path class="st4" d="M88.1,54.2c0-0.2-0.1-0.3-0.2-0.4C82.8,44.9,77.6,36,72.5,27.2c-0.1-0.1-0.2-0.2-0.2-0.4c0.2-0.1,0.3,0,0.5,0
c10.2,0,20.4,0,30.6,0c0.3,0,0.5,0.1,0.7,0.4c4.7,8.2,9.4,16.3,14.1,24.5c5,8.7,10,17.4,15,26c0.1,0.1,0.1,0.2,0.1,0.3
c-0.1,0-0.1,0-0.2,0c0-0.1-0.1-0.1-0.2-0.1c-3.6-1.1-7.2-2.2-10.7-3.3c-2.5-0.8-4.9-1.5-7.4-2.3c-4.2-1.3-8.4-2.6-12.6-3.9
c-2.3-0.7-4.6-1.4-6.8-2.1c-0.1,0-0.3-0.1-0.4,0c0,0-0.1-0.1-0.1-0.1c-1.6-2.7-3.1-5.4-4.7-8.1C89.6,56.8,88.8,55.5,88.1,54.2z"/>
<path class="st5" d="M133.2,78c0.1,0,0.1,0,0.2,0c-0.9,1.5-1.8,3.1-2.6,4.6c-4.3,7.4-8.6,14.8-12.9,22.3c-0.2,0.3-0.4,0.4-0.8,0.4
c-19.4,0-38.8,0-58.2,0c-0.2,0-0.3,0.1-0.5-0.1c0,0,0.1-0.1,0.1-0.1c0.1,0,0.1-0.1,0.1-0.2c0,0,0,0,0,0c0.3-0.1,0.5-0.3,0.8-0.5
c1.1-1,2.3-2.1,3.4-3.2c1.6-1.5,3.2-3,4.8-4.5c1.1-1.1,2.3-2.1,3.4-3.2c1.6-1.5,3.3-3,4.9-4.5c1.2-1.1,2.3-2.1,3.5-3.2
c1.8-1.7,3.6-3.4,5.4-5c0.8-0.8,1.7-1.5,2.5-2.4c0.2-0.2,0.5-0.2,0.5-0.5c1.5,0,3,0,4.5,0c3.1,0,6.2,0,9.3,0
c0.2,0.1,0.3,0.1,0.5,0.1c10.1,0,20.3,0,30.4,0C132.9,78,133.1,78,133.2,78z"/>
<path class="st2" d="M101.8,78c-3.1,0-6.2,0-9.3,0c-1.5,0-3,0-4.5,0c-0.2,0-0.4,0-0.5,0c-3.9,0-7.8,0-11.7,0c-0.5,0-0.9,0-1.4,0
c1.3-2.3,2.6-4.6,4-6.9c3.2-5.5,6.4-11,9.6-16.5c0.1-0.1,0.1-0.2,0.2-0.3c0.8,1.3,1.5,2.6,2.3,3.9c1.6,2.7,3.1,5.4,4.7,8.1
c0,0,0.1,0.1,0.1,0.1c1.2,2,2.3,4,3.5,6C99.6,74.2,100.7,76.1,101.8,78z"/>
<path class="st6" d="M74.3,78c0.5,0,0.9,0,1.4,0c3.9,0,7.8,0,11.7,0c0.2,0,0.4,0,0.5,0c-0.1,0.3-0.3,0.4-0.5,0.5
c-0.8,0.8-1.7,1.6-2.5,2.4c-1.8,1.7-3.6,3.4-5.4,5c-1.2,1.1-2.3,2.1-3.5,3.2c-1.6,1.5-3.2,3-4.9,4.5c-1.2,1.1-2.3,2.1-3.4,3.2
c-1.6,1.5-3.2,3-4.8,4.5c-1.1,1.1-2.3,2.1-3.4,3.2c-0.2,0.2-0.4,0.4-0.8,0.5c0.4-0.9,1-1.8,1.5-2.7c2.7-4.7,5.4-9.4,8.2-14.2
c1.9-3.3,3.8-6.6,5.7-9.9C74.2,78.1,74.2,78.1,74.3,78z"/>
<path class="st6" d="M58.7,105c0,0.1,0,0.2-0.1,0.2C58.6,105.1,58.7,105.1,58.7,105z"/>
<path class="st7" d="M101.8,78c-1.1-1.9-2.2-3.8-3.3-5.7c-1.1-2-2.3-4-3.5-6c0.1-0.2,0.3-0.1,0.4,0c2.3,0.7,4.6,1.4,6.8,2.1
c4.2,1.3,8.4,2.6,12.6,3.9c2.5,0.8,4.9,1.5,7.4,2.3c3.6,1.1,7.2,2.2,10.7,3.3c0.1,0,0.2,0,0.2,0.1c-0.2,0-0.3,0-0.5,0
c-10.1,0-20.3,0-30.4,0C102.1,78,101.9,78.1,101.8,78z"/>
<g>
<path class="st1" d="M315.5,65.6c0.1-33.6-27.3-60.9-61-61c-33.6-0.1-61,27.2-61,60.9c-0.1,33.6,27.2,61,61,61.1
C288.1,126.6,315.5,99.3,315.5,65.6z"/>
<path class="st1" d="M315.5,65.6c-0.1,33.7-27.4,61.1-61,61c-33.7-0.1-61-27.4-61-61.1c0.1-33.7,27.4-61,61-60.9
C288.3,4.6,315.6,31.9,315.5,65.6z M297.6,45.8c-0.1-0.1-0.2-0.1-0.3-0.2c-7-4.4-14-8.9-20.9-13.4c-0.3-0.2-0.4-0.1-0.7,0
c-6.9,4.4-13.9,8.8-20.8,13.3c-0.4,0.3-0.4,0.3,0,0.5c6.9,4.4,13.8,8.8,20.7,13.2c0.1,0.1,0.3,0.2,0.4,0.3
c-7.2,4.6-14.3,9.1-21.5,13.7c0.2,0.1,0.3,0.2,0.5,0.3c6.9,4.4,13.8,8.8,20.7,13.2c0.3,0.2,0.5,0.2,0.8,0c6.7-4.3,13.3-8.5,20-12.7
c0.4-0.2,0.8-0.5,1.2-0.8c-7.2-4.6-14.4-9.1-21.6-13.7C283.2,55,290.4,50.4,297.6,45.8z M254.5,73.3c-7.2-4.6-14.4-9.2-21.6-13.7
c0.2-0.1,0.4-0.2,0.5-0.3c6.9-4.4,13.7-8.7,20.6-13.1c0.4-0.3,0.4-0.3,0-0.6c-6.9-4.4-13.8-8.8-20.7-13.2c-0.3-0.2-0.5-0.2-0.8,0
c-6.9,4.4-13.8,8.8-20.7,13.2c-0.4,0.3-0.4,0.3,0,0.6c5.3,3.4,10.6,6.8,15.9,10.1c1.7,1.1,3.4,2.2,5.2,3.3
c-7.2,4.6-14.4,9.1-21.6,13.7c0.2,0.1,0.3,0.2,0.5,0.3c6.9,4.4,13.8,8.8,20.7,13.2c0.3,0.2,0.5,0.2,0.8,0
c5.1-3.3,10.2-6.5,15.4-9.8C250.6,75.8,252.5,74.5,254.5,73.3z M275.9,91.6c-0.1-0.1-0.2-0.2-0.3-0.2c-6.9-4.4-13.9-8.8-20.8-13.3
c-0.2-0.2-0.4-0.2-0.7,0c-6.9,4.4-13.9,8.9-20.8,13.3c-0.1,0.1-0.2,0.2-0.4,0.3c0.2,0.1,0.3,0.2,0.4,0.3
c6.9,4.4,13.9,8.8,20.8,13.3c0.3,0.2,0.5,0.1,0.7,0c6.9-4.4,13.8-8.8,20.7-13.2C275.7,91.8,275.8,91.7,275.9,91.6z"/>
<path class="st8" d="M297.6,45.8c-7.2,4.6-14.4,9.1-21.6,13.7c7.2,4.6,14.4,9.1,21.6,13.7c-0.4,0.3-0.8,0.5-1.2,0.8
c-6.7,4.2-13.3,8.5-20,12.7c-0.3,0.2-0.5,0.2-0.8,0c-6.9-4.4-13.8-8.8-20.7-13.2c-0.1-0.1-0.3-0.2-0.5-0.3
c7.2-4.6,14.3-9.1,21.5-13.7c-0.2-0.1-0.3-0.2-0.4-0.3c-6.9-4.4-13.8-8.8-20.7-13.2c-0.4-0.3-0.4-0.3,0-0.5
c6.9-4.4,13.9-8.8,20.8-13.3c0.2-0.2,0.4-0.2,0.7,0c7,4.5,14,8.9,20.9,13.4C297.4,45.7,297.4,45.7,297.6,45.8z"/>
<path class="st8" d="M254.5,73.3c-2,1.3-3.9,2.5-5.8,3.7c-5.1,3.3-10.3,6.5-15.4,9.8c-0.3,0.2-0.5,0.2-0.8,0
c-6.9-4.4-13.8-8.8-20.7-13.2c-0.1-0.1-0.3-0.2-0.5-0.3c7.2-4.6,14.3-9.1,21.6-13.7c-1.8-1.1-3.5-2.2-5.2-3.3
c-5.3-3.4-10.6-6.8-15.9-10.1c-0.4-0.3-0.4-0.3,0-0.6c6.9-4.4,13.8-8.8,20.7-13.2c0.3-0.2,0.5-0.2,0.8,0
c6.9,4.4,13.8,8.8,20.7,13.2c0.4,0.3,0.4,0.3,0,0.6c-6.9,4.4-13.7,8.7-20.6,13.1c-0.2,0.1-0.3,0.2-0.5,0.3
C240.1,64.1,247.2,68.7,254.5,73.3z"/>
<path class="st8" d="M275.9,91.6c-0.1,0.2-0.3,0.2-0.4,0.3c-6.9,4.4-13.8,8.8-20.7,13.2c-0.2,0.2-0.4,0.2-0.7,0
c-6.9-4.4-13.9-8.9-20.8-13.3c-0.1-0.1-0.2-0.1-0.4-0.3c0.1-0.1,0.3-0.2,0.4-0.3c6.9-4.4,13.9-8.8,20.8-13.3c0.3-0.2,0.4-0.1,0.7,0
c6.9,4.4,13.9,8.9,20.8,13.3C275.7,91.4,275.8,91.5,275.9,91.6z"/>
</g>
<path class="st9" d="M389.9,117c-6.8-6.8-13.7-13.6-20.5-20.5c-1.9-2-1.8-5.6,0.1-7.7c0.2-0.3,0.5-0.5,0.8-0.8
c15.9-15.9,31.8-31.7,47.6-47.6c2.6-2.6,5.9-1.9,7.8-0.1c0.3,0.3,0.6,0.6,0.9,0.9c6.2,6.2,12.5,12.5,18.7,18.7
c0.3,0.3,0.6,0.6,0.9,1c0.1,0.3,0.3,0.6,0.4,0.8c0.8,1.7,0.4,4-0.9,5.2c-16.4,16.3-32.7,32.7-49.1,49c-0.2,0.2-0.4,0.4-0.7,0.6
C394,117.8,392,117.9,389.9,117z M423.3,62.2c-2,0.5-3.9,1-5.8,1.6c-1.6,0.4-1.6,0.4-0.4,1.6c0.4,0.4,0.5,0.7,0,1.1
c-5,5-9.9,9.9-14.9,14.9c-0.1,0.1-0.3,0.4-0.5,0.2c-0.1-0.1-0.1-0.3-0.1-0.5c0-1.5-0.1-2.9-0.1-4.4c0-0.6,0.2-1.1,0.7-1.6
c1.2-1.2,2.4-2.4,3.6-3.6c0.3-0.3,0.6-0.5,1.2-0.4c1.7,0.2,3.2-1.2,3.2-2.9c0-1.9-1.4-3.2-3.2-3.1c-1.8,0.1-3.1,1.6-2.8,3.4
c0.1,0.4-0.1,0.7-0.4,1c-1.2,1.1-2.3,2.3-3.5,3.4c-1.2,1.1-1.7,2.4-1.6,4c0.1,2.2,0.1,4.5,0.1,6.7c0,1.3-1,2-1.7,2.7
c-0.5,0.5-1,0-1.5-0.1c-2.2-0.5-4.4,0.7-5.3,2.7c-0.9,2.2-0.1,4.5,1.8,5.7c1.8,1.1,3.8,0.9,5.6-0.5c1.2-1,1.8-3.1,1.2-4.9
c-0.2-0.6-0.1-1,0.3-1.4c1.7-1.6,3.3-3.3,4.9-4.9c0.5-0.5,1-0.7,1.6-0.7c2.1,0,4.2,0,6.3,0.2c1.7,0.1,3.3-0.3,4.5-1.6
c0.4-0.5,0.9-0.9,1.4-1.4c0.5-0.6,0.9-0.6,1.3,0c0.3,0.4,0.6,0.5,1,0c1.1-1.1,2.2-2.2,3.3-3.3c0.4-0.4,0.4-0.6,0-1
c-1.1-1.1-2.2-2.2-3.3-3.3c-0.4-0.4-0.7-0.3-1,0.1c-1,1-2,2.1-3.1,3.1c-0.5,0.5-0.7,0.8,0,1.3c0.3,0.2,0.6,0.5,0.2,0.9
c-0.6,0.6-1.3,1.3-1.9,1.9c-0.4,0.3-0.9,0.6-1.5,0.5c-1.5-0.2-2.9-0.1-4.4-0.1c-0.2,0-0.5,0.1-0.6-0.1c0-0.1,0-0.1,0-0.2
c3.7-3.7,7.4-7.4,11.1-11.1c0.4-0.4,0.6-0.2,0.9,0.1c1.3,1.2,1.3,1.1,1.7-0.5C422.3,66,422.8,64.1,423.3,62.2z"/>
<path class="st10" d="M389.9,117c2.1,0.9,4.1,0.9,6-0.4c0.3-0.2,0.5-0.4,0.7-0.6c16.4-16.3,32.7-32.7,49.1-49
c1.3-1.3,1.7-3.5,0.9-5.2c-0.1-0.3-0.2-0.6-0.4-0.8c1.4,1.4,2.7,2.7,4.1,4.1c0.6,0.6,1.3,1.2,1.8,1.9c1.7,2.2,1.5,4.8-0.5,6.8
c-7.8,7.8-15.7,15.7-23.5,23.5c-8.3,8.3-16.5,16.5-24.8,24.8c-1,1-2.1,1.6-3.4,1.9c-1.8,0.3-3.4-0.1-4.7-1.4
C393.5,120.5,391.7,118.8,389.9,117z"/>
<path class="st11" d="M445.3,59.9c-6.2-6.2-12.5-12.5-18.7-18.7c5-5,10.1-10,15.1-15.1c0.6-0.6,0.8-0.6,1.4,0c6,6,12,12,18,18
C455.9,49.4,450.6,54.7,445.3,59.9z M439.3,37.5c-0.5-0.5-0.9-1-1.4-1.4c-0.5-0.5-1.1-0.5-1.6,0c-0.4,0.4-0.8,0.8-1.2,1.2
c-0.5,0.6-0.5,1.1,0,1.7c0.5,0.6,1.1,1.1,1.7,1.7c2,2.2,2,1.7,3.9-0.1c0,0,0,0,0.1-0.1c0.5-0.5,0.5-1.1,0-1.6
C440.3,38.4,439.8,38,439.3,37.5z M449,47.2c-0.5-0.5-1-1-1.5-1.5c-0.7-0.6-1.1-0.5-2.5,1c-0.8,0.8-0.9,1.3-0.3,1.9
c0.9,1,1.9,1.9,2.9,2.9c0.7,0.6,1.1,0.5,2.5-1c0.8-0.8,0.9-1.3,0.3-1.9C449.9,48.1,449.5,47.6,449,47.2z"/>
<path class="st12" d="M445.3,59.9c5.3-5.3,10.5-10.5,15.8-15.8c1.5,1.5,3,3,4.5,4.4c0.5,0.5,0.4,0.7,0,1.1
c-4.9,4.8-9.7,9.7-14.6,14.5c-0.2,0.2-0.5,0.5-0.7,0.7c-1.4-1.4-2.7-2.7-4.1-4.1C445.9,60.6,445.6,60.2,445.3,59.9z"/>
<path class="st1" d="M423.3,62.2c-0.5,2-1,3.8-1.5,5.6c-0.5,1.7-0.5,1.7-1.7,0.5c-0.3-0.3-0.6-0.4-0.9-0.1
c-3.7,3.7-7.4,7.4-11.1,11.1c0,0,0,0.1,0,0.2c0.2,0.2,0.4,0.1,0.6,0.1c1.5,0.1,2.9-0.1,4.4,0.1c0.6,0.1,1.1-0.2,1.5-0.5
c0.7-0.6,1.3-1.2,1.9-1.9c0.4-0.4,0.1-0.7-0.2-0.9c-0.6-0.5-0.5-0.8,0-1.3c1.1-1,2.1-2,3.1-3.1c0.4-0.4,0.6-0.5,1-0.1
c1.1,1.1,2.2,2.2,3.3,3.3c0.4,0.4,0.4,0.6,0,1c-1.1,1.1-2.2,2.2-3.3,3.3c-0.4,0.4-0.7,0.4-1,0c-0.5-0.6-0.9-0.6-1.3,0
c-0.4,0.5-0.9,0.9-1.4,1.4c-1.2,1.4-2.7,1.7-4.5,1.6c-2.1-0.1-4.2-0.1-6.3-0.2c-0.6,0-1.2,0.3-1.6,0.7c-1.6,1.7-3.3,3.3-4.9,4.9
c-0.4,0.4-0.6,0.8-0.3,1.4c0.6,1.7,0,3.9-1.2,4.9c-1.8,1.4-3.8,1.6-5.6,0.5c-2-1.2-2.7-3.6-1.8-5.7c0.8-2.1,3.1-3.2,5.3-2.7
c0.5,0.1,1,0.6,1.5,0.1c0.8-0.8,1.8-1.4,1.7-2.7c-0.1-2.2-0.1-4.5-0.1-6.7c0-1.6,0.5-2.9,1.6-4c1.2-1.1,2.3-2.3,3.5-3.4
c0.3-0.3,0.4-0.6,0.4-1c-0.2-1.8,1.1-3.3,2.8-3.4c1.8-0.1,3.2,1.3,3.2,3.1c0,1.7-1.5,3.1-3.2,2.9c-0.5-0.1-0.8,0.1-1.2,0.4
c-1.2,1.2-2.4,2.4-3.6,3.6c-0.5,0.4-0.7,0.9-0.7,1.6c0,1.5,0.1,2.9,0.1,4.4c0,0.2,0,0.4,0.1,0.5c0.2,0.1,0.3-0.1,0.5-0.2
c5-5,9.9-9.9,14.9-14.9c0.4-0.4,0.4-0.7,0-1.1c-1.1-1.2-1.1-1.2,0.4-1.6C419.4,63.2,421.3,62.7,423.3,62.2z"/>
<path class="st13" d="M439.3,37.5c0.5,0.5,0.9,0.9,1.4,1.4c0.5,0.5,0.5,1,0,1.6c0,0,0,0-0.1,0.1c-1.9,1.9-1.9,2.3-3.9,0.1
c-0.5-0.6-1.1-1.1-1.7-1.7c-0.6-0.6-0.6-1.1,0-1.7c0.4-0.4,0.8-0.8,1.2-1.2c0.5-0.5,1.1-0.5,1.6,0C438.4,36.6,438.9,37.1,439.3,37.5
z"/>
<path class="st13" d="M449,47.2c0.5,0.5,0.9,0.9,1.4,1.4c0.6,0.6,0.5,1.1-0.3,1.9c-1.4,1.5-1.8,1.6-2.5,1c-1-0.9-1.9-1.9-2.9-2.9
c-0.6-0.6-0.5-1.1,0.3-1.9c1.4-1.5,1.8-1.6,2.5-1C448,46.2,448.5,46.7,449,47.2z"/>
<g>
<path class="st14" d="M32.1,158.8l0.2-1.2h4.2v7.7c-0.2,0.1-0.5,0.2-0.8,0.3c-0.3,0.1-0.7,0.2-1,0.2c-0.4,0.1-0.7,0.1-1.1,0.1
s-0.7,0-1,0c-1.4,0-2.6-0.2-3.5-0.5c-0.9-0.4-1.5-0.8-2-1.5c-0.5-0.6-0.8-1.4-0.9-2.3c-0.1-0.9-0.2-1.9-0.2-2.9v-1.6
c0-1.2,0.1-2.2,0.3-3.1c0.2-0.9,0.6-1.7,1.1-2.3s1.2-1.1,2.1-1.4c0.9-0.3,2-0.5,3.3-0.5c0.6,0,1.2,0.1,1.8,0.2
c0.6,0.1,1.1,0.2,1.6,0.4l-0.3,1.1c-0.4-0.1-0.9-0.2-1.4-0.3c-0.5-0.1-1.1-0.1-1.7-0.1c-1,0-1.8,0.1-2.4,0.3s-1.2,0.5-1.6,1
c-0.4,0.5-0.7,1.1-0.9,1.9s-0.3,1.8-0.3,3v1.5c0,1.2,0.1,2.1,0.3,2.9c0.2,0.8,0.5,1.4,0.9,1.8c0.4,0.5,0.9,0.8,1.6,1
c0.7,0.2,1.5,0.3,2.4,0.3c0.5,0,0.9,0,1.4-0.1c0.4,0,0.8-0.1,1.2-0.2v-5.6H32.1z"/>
<path class="st14" d="M44.5,154.2c1,0,1.7,0.1,2.4,0.4c0.6,0.2,1.1,0.6,1.5,1.1c0.4,0.5,0.6,1,0.8,1.7c0.2,0.7,0.2,1.4,0.2,2.2v1.2
c0,0.8-0.1,1.5-0.3,2.2c-0.2,0.7-0.5,1.2-0.8,1.7c-0.4,0.5-0.9,0.8-1.5,1.1c-0.6,0.2-1.4,0.4-2.3,0.4c-1,0-1.7-0.1-2.4-0.4
c-0.6-0.2-1.1-0.6-1.5-1.1s-0.6-1-0.8-1.7c-0.2-0.7-0.2-1.4-0.2-2.2v-1.2c0-0.8,0.1-1.5,0.3-2.2c0.2-0.7,0.5-1.2,0.9-1.7
s0.9-0.8,1.5-1.1C42.8,154.3,43.6,154.2,44.5,154.2z M44.5,164.8c0.7,0,1.2-0.1,1.7-0.2s0.8-0.4,1.1-0.7c0.3-0.3,0.5-0.8,0.6-1.3
c0.1-0.5,0.2-1.2,0.2-1.9v-1c0-0.8-0.1-1.4-0.2-2s-0.3-1-0.6-1.3c-0.3-0.3-0.6-0.6-1.1-0.7s-1-0.2-1.6-0.2c-0.7,0-1.2,0.1-1.7,0.2
c-0.5,0.2-0.8,0.4-1.1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,1.9v1c0,0.8,0.1,1.4,0.2,1.9c0.1,0.5,0.3,1,0.6,1.3
c0.3,0.3,0.6,0.6,1.1,0.7C43.2,164.7,43.8,164.8,44.5,164.8z"/>
<path class="st14" d="M57.1,154.2c1,0,1.7,0.1,2.4,0.4c0.6,0.2,1.1,0.6,1.5,1.1c0.4,0.5,0.6,1,0.8,1.7c0.2,0.7,0.2,1.4,0.2,2.2v1.2
c0,0.8-0.1,1.5-0.3,2.2c-0.2,0.7-0.5,1.2-0.8,1.7c-0.4,0.5-0.9,0.8-1.5,1.1c-0.6,0.2-1.4,0.4-2.3,0.4c-1,0-1.7-0.1-2.4-0.4
c-0.6-0.2-1.1-0.6-1.5-1.1s-0.6-1-0.8-1.7c-0.2-0.7-0.2-1.4-0.2-2.2v-1.2c0-0.8,0.1-1.5,0.3-2.2c0.2-0.7,0.5-1.2,0.9-1.7
s0.9-0.8,1.5-1.1C55.4,154.3,56.2,154.2,57.1,154.2z M57.1,164.8c0.7,0,1.2-0.1,1.7-0.2s0.8-0.4,1.1-0.7c0.3-0.3,0.5-0.8,0.6-1.3
c0.1-0.5,0.2-1.2,0.2-1.9v-1c0-0.8-0.1-1.4-0.2-2s-0.3-1-0.6-1.3c-0.3-0.3-0.6-0.6-1.1-0.7s-1-0.2-1.6-0.2c-0.7,0-1.2,0.1-1.7,0.2
c-0.5,0.2-0.8,0.4-1.1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,1.9v1c0,0.8,0.1,1.4,0.2,1.9c0.1,0.5,0.3,1,0.6,1.3
c0.3,0.3,0.6,0.6,1.1,0.7C55.9,164.7,56.4,164.8,57.1,164.8z"/>
<path class="st14" d="M74.3,165.5c0,0.8-0.1,1.6-0.3,2.3c-0.2,0.7-0.5,1.2-0.9,1.7c-0.4,0.5-0.9,0.8-1.6,1.1
c-0.6,0.2-1.4,0.4-2.3,0.4c-1.3,0-2.7-0.2-4-0.6l0.3-1.1c0.6,0.2,1.2,0.3,1.8,0.4c0.6,0.1,1.2,0.1,1.9,0.1s1.3-0.1,1.8-0.3
c0.5-0.2,0.8-0.5,1.1-0.9c0.3-0.4,0.5-0.9,0.6-1.4c0.1-0.6,0.2-1.2,0.2-1.9v-1.3c-0.1,0.2-0.3,0.5-0.5,0.7
c-0.2,0.2-0.5,0.4-0.8,0.6c-0.3,0.2-0.7,0.3-1.1,0.4c-0.4,0.1-0.9,0.1-1.5,0.1c-0.6,0-1.2-0.1-1.8-0.3c-0.5-0.2-1-0.5-1.4-0.9
c-0.4-0.4-0.7-1-0.9-1.6c-0.2-0.7-0.3-1.5-0.3-2.5v-0.7c0-0.9,0.1-1.7,0.4-2.4s0.6-1.2,1.1-1.7c0.5-0.5,1-0.8,1.6-1
c0.6-0.2,1.3-0.3,2.1-0.3c0.8,0,1.6,0.1,2.3,0.3c0.7,0.2,1.4,0.4,2,0.8V165.5z M66.1,160.1c0,0.7,0.1,1.3,0.2,1.9s0.3,1,0.5,1.4
c0.3,0.4,0.6,0.7,1,0.8s0.9,0.3,1.6,0.3c1.2,0,2.1-0.3,2.7-0.9c0.6-0.6,0.9-1.6,0.9-3V156c-0.4-0.2-0.8-0.4-1.3-0.5
c-0.4-0.1-1-0.2-1.6-0.2c-1.3,0-2.3,0.3-3,1s-1,1.8-1,3.4V160.1z"/>
<path class="st14" d="M77.8,149.3h1.3v16.3h-1.3V149.3z"/>
<path class="st14" d="M83.6,160.5c0,0.8,0.1,1.4,0.2,2c0.1,0.5,0.4,1,0.7,1.3c0.3,0.3,0.8,0.6,1.3,0.7c0.5,0.2,1.2,0.2,2.1,0.2
c0.5,0,0.9,0,1.5-0.1c0.5-0.1,1-0.1,1.3-0.2l-0.2,1.1c-0.3,0.1-0.8,0.2-1.3,0.3c-0.5,0.1-1.1,0.1-1.6,0.1c-1,0-1.8-0.1-2.5-0.3
c-0.7-0.2-1.2-0.6-1.7-1s-0.7-1-0.9-1.7c-0.2-0.7-0.3-1.5-0.3-2.4v-0.9c0-0.8,0.1-1.6,0.3-2.3c0.2-0.7,0.5-1.3,0.8-1.7
c0.4-0.5,0.9-0.9,1.5-1.1c0.6-0.3,1.3-0.4,2.2-0.4c0.7,0,1.4,0.1,1.9,0.3c0.5,0.2,1,0.5,1.3,0.9s0.6,0.9,0.8,1.4
c0.2,0.5,0.2,1.2,0.2,1.8v0.6c0,0.4-0.1,0.7-0.2,0.9c-0.1,0.2-0.4,0.3-0.8,0.3H83.6z M86.9,155.3c-0.6,0-1,0.1-1.5,0.2
s-0.8,0.4-1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,2h6.5v-0.8c0-1.1-0.2-2-0.7-2.6S88,155.3,86.9,155.3z"/>
<path class="st14" d="M100.2,150h4.9c1.3,0,2.3,0.2,3.1,0.5c0.8,0.3,1.5,0.8,2,1.4c0.5,0.6,0.8,1.3,1,2.2c0.2,0.8,0.3,1.7,0.3,2.7
v2c0,1-0.1,1.9-0.3,2.8c-0.2,0.8-0.6,1.6-1.1,2.2c-0.5,0.6-1.1,1.1-2,1.4s-1.8,0.5-3.1,0.5h-4.8V150z M101.6,164.5h3.3
c0.8,0,1.5-0.1,2.2-0.3c0.6-0.2,1.2-0.5,1.6-0.9c0.4-0.4,0.8-1,1-1.8c0.2-0.7,0.3-1.6,0.3-2.8V157c0-1.1-0.1-2-0.3-2.7
c-0.2-0.7-0.5-1.3-0.9-1.8c-0.4-0.4-0.9-0.8-1.6-1c-0.6-0.2-1.4-0.3-2.2-0.3h-3.4V164.5z"/>
<path class="st14" d="M120.7,155.6c-0.2,0-0.4-0.1-0.7-0.1c-0.2,0-0.4,0-0.6,0c-1.1,0-1.9,0.3-2.4,1c-0.5,0.7-0.8,1.7-0.8,3.2v6.1
h-1.4v-11.2h1.1l0.2,1.8c0.7-1.3,1.9-1.9,3.5-1.9c0.2,0,0.5,0,0.7,0.1c0.2,0,0.4,0.1,0.6,0.1L120.7,155.6z"/>
<path class="st14" d="M123.8,150.1c0.7,0,1,0.3,1,1c0,0.6-0.3,1-1,1c-0.6,0-1-0.3-1-1C122.9,150.4,123.2,150.1,123.8,150.1z
M123.2,154.4h1.4v11.2h-1.4V154.4z"/>
<path class="st14" d="M127.1,154.4h1.5l3.8,9.9l3.8-9.9h1.5l-4.5,11.2h-1.5L127.1,154.4z"/>
<path class="st14" d="M140.6,160.5c0,0.8,0.1,1.4,0.2,2c0.1,0.5,0.4,1,0.7,1.3c0.3,0.3,0.8,0.6,1.3,0.7c0.5,0.2,1.2,0.2,2.1,0.2
c0.5,0,0.9,0,1.5-0.1c0.5-0.1,1-0.1,1.3-0.2l-0.2,1.1c-0.3,0.1-0.8,0.2-1.3,0.3c-0.5,0.1-1.1,0.1-1.6,0.1c-1,0-1.8-0.1-2.5-0.3
c-0.7-0.2-1.2-0.6-1.7-1s-0.7-1-0.9-1.7c-0.2-0.7-0.3-1.5-0.3-2.4v-0.9c0-0.8,0.1-1.6,0.3-2.3c0.2-0.7,0.5-1.3,0.8-1.7
c0.4-0.5,0.9-0.9,1.5-1.1c0.6-0.3,1.3-0.4,2.2-0.4c0.7,0,1.4,0.1,1.9,0.3c0.5,0.2,1,0.5,1.3,0.9s0.6,0.9,0.8,1.4
c0.2,0.5,0.2,1.2,0.2,1.8v0.6c0,0.4-0.1,0.7-0.2,0.9c-0.1,0.2-0.4,0.3-0.8,0.3H140.6z M143.9,155.3c-0.6,0-1,0.1-1.5,0.2
s-0.8,0.4-1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,2h6.5v-0.8c0-1.1-0.2-2-0.7-2.6S145,155.3,143.9,155.3z"/>
</g>
<g>
<path class="st14" d="M217.3,150h4.9c1.3,0,2.3,0.2,3.1,0.5c0.8,0.3,1.5,0.8,2,1.4c0.5,0.6,0.8,1.3,1,2.2c0.2,0.8,0.3,1.7,0.3,2.7
v2c0,1-0.1,1.9-0.3,2.8c-0.2,0.8-0.6,1.6-1.1,2.2c-0.5,0.6-1.1,1.1-2,1.4s-1.8,0.5-3.1,0.5h-4.8V150z M218.8,164.5h3.3
c0.8,0,1.5-0.1,2.2-0.3c0.6-0.2,1.2-0.5,1.6-0.9c0.4-0.4,0.8-1,1-1.8c0.2-0.7,0.3-1.6,0.3-2.8V157c0-1.1-0.1-2-0.3-2.7
c-0.2-0.7-0.5-1.3-0.9-1.8c-0.4-0.4-0.9-0.8-1.6-1c-0.6-0.2-1.4-0.3-2.2-0.3h-3.4V164.5z"/>
<path class="st14" d="M237.9,155.6c-0.2,0-0.4-0.1-0.7-0.1c-0.2,0-0.4,0-0.6,0c-1.1,0-1.9,0.3-2.4,1c-0.5,0.7-0.8,1.7-0.8,3.2v6.1
H232v-11.2h1.1l0.2,1.8c0.7-1.3,1.9-1.9,3.5-1.9c0.2,0,0.5,0,0.7,0.1c0.2,0,0.4,0.1,0.6,0.1L237.9,155.6z"/>
<path class="st14" d="M244.3,154.2c1,0,1.7,0.1,2.4,0.4c0.6,0.2,1.1,0.6,1.5,1.1c0.4,0.5,0.6,1,0.8,1.7c0.2,0.7,0.2,1.4,0.2,2.2
v1.2c0,0.8-0.1,1.5-0.3,2.2c-0.2,0.7-0.5,1.2-0.8,1.7c-0.4,0.5-0.9,0.8-1.5,1.1c-0.6,0.2-1.4,0.4-2.3,0.4c-1,0-1.7-0.1-2.4-0.4
c-0.6-0.2-1.1-0.6-1.5-1.1s-0.6-1-0.8-1.7c-0.2-0.7-0.2-1.4-0.2-2.2v-1.2c0-0.8,0.1-1.5,0.3-2.2c0.2-0.7,0.5-1.2,0.9-1.7
s0.9-0.8,1.5-1.1C242.7,154.3,243.4,154.2,244.3,154.2z M244.3,164.8c0.7,0,1.2-0.1,1.7-0.2s0.8-0.4,1.1-0.7
c0.3-0.3,0.5-0.8,0.6-1.3c0.1-0.5,0.2-1.2,0.2-1.9v-1c0-0.8-0.1-1.4-0.2-2s-0.3-1-0.6-1.3c-0.3-0.3-0.6-0.6-1.1-0.7s-1-0.2-1.6-0.2
c-0.7,0-1.2,0.1-1.7,0.2c-0.5,0.2-0.8,0.4-1.1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,1.9v1c0,0.8,0.1,1.4,0.2,1.9
c0.1,0.5,0.3,1,0.6,1.3c0.3,0.3,0.6,0.6,1.1,0.7C243.1,164.7,243.7,164.8,244.3,164.8z"/>
<path class="st14" d="M261.9,160.6c0,0.9-0.1,1.7-0.4,2.4c-0.2,0.7-0.6,1.2-1,1.7s-0.9,0.8-1.5,1s-1.2,0.3-1.9,0.3
c-0.4,0-0.7,0-1,0c-0.3,0-0.6-0.1-0.8-0.1c-0.3-0.1-0.5-0.2-0.8-0.3c-0.3-0.1-0.5-0.2-0.8-0.4v5.7h-1.3v-16.3h1.1l0.2,1.5
c0.8-1.2,2.1-1.8,3.9-1.8c0.6,0,1.2,0.1,1.8,0.3c0.5,0.2,1,0.5,1.4,0.9c0.4,0.4,0.7,1,0.9,1.7c0.2,0.7,0.3,1.5,0.3,2.5V160.6z
M260.5,159.9c0-0.7,0-1.3-0.1-1.9s-0.3-1-0.5-1.4c-0.3-0.4-0.6-0.7-1-0.9c-0.4-0.2-1-0.3-1.6-0.3c-0.7,0-1.2,0.1-1.7,0.3
c-0.4,0.2-0.8,0.4-1.1,0.7s-0.5,0.7-0.6,1.2c-0.1,0.5-0.2,1-0.2,1.7v4.7c0.5,0.3,1,0.5,1.5,0.6c0.5,0.1,1,0.2,1.7,0.2
c0.7,0,1.3-0.1,1.7-0.3s0.8-0.5,1.1-0.8c0.3-0.4,0.5-0.8,0.6-1.4s0.2-1.2,0.2-1.9V159.9z"/>
<path class="st14" d="M269.4,165.9c-0.7,0-1.5-0.1-2.2-0.3c-0.7-0.2-1.4-0.5-2.1-0.8v-15.5h1.3v6.6c0.4-0.6,0.9-1.1,1.5-1.3
c0.6-0.3,1.3-0.4,2.2-0.4c0.7,0,1.3,0.1,1.8,0.3c0.6,0.2,1,0.5,1.4,0.9s0.7,1,0.9,1.6c0.2,0.7,0.3,1.5,0.3,2.5v1
c0,1.8-0.4,3.2-1.3,4.1C272.4,165.5,271.1,165.9,269.4,165.9z M269.3,164.8c0.7,0,1.3-0.1,1.8-0.3c0.5-0.2,0.9-0.4,1.2-0.8
c0.3-0.4,0.5-0.8,0.7-1.4c0.1-0.6,0.2-1.2,0.2-2v-0.5c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.3-1-0.5-1.4s-0.6-0.7-1-0.9
c-0.4-0.2-1-0.3-1.6-0.3c-0.5,0-1,0.1-1.4,0.2s-0.8,0.4-1.1,0.7c-0.3,0.3-0.5,0.7-0.7,1.2c-0.2,0.5-0.3,1.1-0.3,1.8v4.9
c0.4,0.2,0.9,0.4,1.3,0.5C268.2,164.7,268.7,164.8,269.3,164.8z"/>
<path class="st14" d="M282.3,154.2c1,0,1.7,0.1,2.4,0.4c0.6,0.2,1.1,0.6,1.5,1.1c0.4,0.5,0.6,1,0.8,1.7c0.2,0.7,0.2,1.4,0.2,2.2
v1.2c0,0.8-0.1,1.5-0.3,2.2c-0.2,0.7-0.5,1.2-0.8,1.7c-0.4,0.5-0.9,0.8-1.5,1.1c-0.6,0.2-1.4,0.4-2.3,0.4c-1,0-1.7-0.1-2.4-0.4
c-0.6-0.2-1.1-0.6-1.5-1.1s-0.6-1-0.8-1.7c-0.2-0.7-0.2-1.4-0.2-2.2v-1.2c0-0.8,0.1-1.5,0.3-2.2c0.2-0.7,0.5-1.2,0.9-1.7
s0.9-0.8,1.5-1.1C280.6,154.3,281.4,154.2,282.3,154.2z M282.3,164.8c0.7,0,1.2-0.1,1.7-0.2s0.8-0.4,1.1-0.7
c0.3-0.3,0.5-0.8,0.6-1.3c0.1-0.5,0.2-1.2,0.2-1.9v-1c0-0.8-0.1-1.4-0.2-2s-0.3-1-0.6-1.3c-0.3-0.3-0.6-0.6-1.1-0.7s-1-0.2-1.6-0.2
c-0.7,0-1.2,0.1-1.7,0.2c-0.5,0.2-0.8,0.4-1.1,0.7c-0.3,0.3-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,1.9v1c0,0.8,0.1,1.4,0.2,1.9
c0.1,0.5,0.3,1,0.6,1.3c0.3,0.3,0.6,0.6,1.1,0.7C281.1,164.7,281.6,164.8,282.3,164.8z"/>
<path class="st14" d="M288.8,154.4h1.5l3.1,4.4l3.1-4.4h1.5l-3.9,5.4l4.2,5.8h-1.6l-3.4-4.9l-3.4,4.9h-1.5l4.2-5.9L288.8,154.4z"/>
</g>
<g>
<path class="st14" d="M379.5,166c-1.1,0-2-0.1-2.8-0.4s-1.3-0.7-1.7-1.2c-0.4-0.6-0.7-1.2-0.9-2c-0.2-0.8-0.2-1.8-0.2-2.8V150h1.4
v9.5c0,1,0.1,1.9,0.2,2.5c0.2,0.7,0.4,1.2,0.7,1.6c0.3,0.4,0.8,0.7,1.3,0.9c0.5,0.2,1.2,0.3,2,0.3c0.8,0,1.5-0.1,2-0.3
c0.5-0.2,1-0.5,1.3-0.9c0.3-0.4,0.6-1,0.7-1.6s0.2-1.5,0.2-2.5V150h1.4v9.5c0,1.1-0.1,2-0.3,2.8s-0.5,1.5-0.9,2
c-0.4,0.5-1,0.9-1.7,1.2S380.6,166,379.5,166z"/>
<path class="st14" d="M394.4,157.1c0.7,0.1,1.3,0.3,1.8,0.5c0.5,0.2,0.9,0.5,1.2,0.9s0.5,0.8,0.6,1.2c0.1,0.5,0.2,0.9,0.2,1.5v0.3
c0,0.6-0.1,1.2-0.3,1.8c-0.2,0.5-0.6,1-1,1.4c-0.5,0.4-1,0.7-1.8,0.9c-0.7,0.2-1.5,0.3-2.5,0.3c-0.8,0-1.6-0.1-2.4-0.2
c-0.8-0.1-1.4-0.3-2-0.5l0.3-1.2c0.6,0.2,1.2,0.3,1.9,0.4c0.7,0.1,1.4,0.2,2.2,0.2c1.5,0,2.6-0.3,3.2-0.8c0.6-0.5,1-1.4,1-2.5
c0-0.4,0-0.7-0.1-1c-0.1-0.3-0.2-0.6-0.4-0.9c-0.2-0.3-0.5-0.5-0.9-0.6c-0.4-0.2-0.9-0.3-1.4-0.4l-2-0.3c-1.3-0.2-2.3-0.6-2.8-1.3
c-0.6-0.7-0.8-1.6-0.8-2.7V154c0-0.7,0.1-1.3,0.4-1.8c0.3-0.5,0.6-1,1.1-1.3c0.5-0.4,1.1-0.6,1.8-0.8c0.7-0.2,1.5-0.3,2.3-0.3
c0.7,0,1.5,0.1,2.1,0.2c0.7,0.1,1.2,0.3,1.7,0.5l-0.3,1.1c-0.5-0.2-1.1-0.3-1.7-0.4s-1.3-0.2-2-0.2c-1.5,0-2.5,0.3-3.2,0.8
c-0.6,0.5-1,1.3-1,2.3c0,0.8,0.2,1.4,0.6,1.8c0.4,0.4,1.1,0.7,2.2,0.8L394.4,157.1z"/>
<path class="st14" d="M401.4,165.7V150h5.5c0.8,0,1.5,0.1,2,0.3c0.5,0.2,1,0.5,1.3,0.8c0.3,0.3,0.6,0.7,0.7,1.2
c0.1,0.4,0.2,0.9,0.2,1.4v0.5c0,0.8-0.2,1.5-0.6,2c-0.4,0.5-0.9,0.9-1.6,1.2c0.5,0.1,0.8,0.3,1.2,0.5c0.3,0.2,0.6,0.5,0.8,0.9
c0.2,0.3,0.4,0.7,0.5,1.1c0.1,0.4,0.2,0.8,0.2,1.3v0.5c0,0.6-0.1,1.1-0.3,1.6c-0.2,0.5-0.5,0.9-0.9,1.3c-0.4,0.4-0.9,0.6-1.5,0.8
s-1.3,0.3-2.1,0.3H401.4z M402.8,156.9h4.2c0.9,0,1.7-0.2,2.2-0.7c0.5-0.4,0.7-1.1,0.7-1.8v-0.5c0-1.8-1-2.8-3.1-2.8h-3.9V156.9z
M402.8,164.5h4.2c1.1,0,1.9-0.2,2.4-0.7c0.5-0.4,0.8-1.2,0.8-2.2v-0.5c0-2-1.1-3.1-3.3-3.1h-4.2V164.5z"/>
<path class="st14" d="M421.6,150v7.1l7-7.1h1.6l-7.5,7.5l8,8.1h-1.8l-7.3-7.5v7.5h-1.3V150H421.6z"/>
<path class="st14" d="M433.1,160.5c0,0.8,0.1,1.4,0.2,2c0.1,0.5,0.4,1,0.7,1.3c0.3,0.3,0.8,0.6,1.3,0.7c0.5,0.2,1.2,0.2,2.1,0.2
c0.5,0,0.9,0,1.5-0.1c0.5-0.1,1-0.1,1.3-0.2l-0.2,1.1c-0.3,0.1-0.8,0.2-1.3,0.3c-0.5,0.1-1.1,0.1-1.6,0.1c-1,0-1.8-0.1-2.5-0.3
c-0.7-0.2-1.2-0.6-1.7-1s-0.7-1-0.9-1.7c-0.2-0.7-0.3-1.5-0.3-2.4v-0.9c0-0.8,0.1-1.6,0.3-2.3c0.2-0.7,0.5-1.3,0.8-1.7
c0.4-0.5,0.9-0.9,1.5-1.1c0.6-0.3,1.3-0.4,2.2-0.4c0.7,0,1.4,0.1,1.9,0.3c0.5,0.2,1,0.5,1.3,0.9s0.6,0.9,0.8,1.4
c0.2,0.5,0.2,1.2,0.2,1.8v0.6c0,0.4-0.1,0.7-0.2,0.9c-0.1,0.2-0.4,0.3-0.8,0.3H433.1z M436.4,155.3c-0.6,0-1,0.1-1.5,0.2
s-0.8,0.4-1,0.7s-0.5,0.8-0.6,1.3c-0.1,0.5-0.2,1.2-0.2,2h6.5v-0.8c0-1.1-0.2-2-0.7-2.6S437.5,155.3,436.4,155.3z"/>
<path class="st14" d="M451.7,154.4h1.5l-4.9,12.6c-0.3,0.8-0.6,1.4-0.9,1.9c-0.3,0.5-0.6,0.9-0.9,1.2c-0.3,0.3-0.6,0.5-1,0.6
c-0.4,0.1-0.7,0.2-1.2,0.2c-0.3,0-0.6,0-0.9-0.1c-0.3-0.1-0.5-0.1-0.8-0.2l0.2-1c0.2,0,0.4,0.1,0.6,0.1c0.2,0,0.5,0,0.7,0
c0.3,0,0.6,0,0.9-0.1c0.3-0.1,0.5-0.2,0.7-0.4c0.2-0.2,0.4-0.5,0.6-0.8c0.2-0.4,0.4-0.9,0.6-1.5l0.4-1.2l-4.8-11.4h1.5l3.9,9.7
L451.7,154.4z"/>
</g>
</svg>
<hr>
<h3>What happens if I lose my passphrase?</h3>
<p>Your passphrase is used to encrypt your private keys. If you lose your passphrase, you will no longer be able to decrypt your private key file nor have access to the contents of your Wallet or My Purchases.</p>
<p>In order to guarantee your privacy, nobody can help you recover your passphrase, including High Fidelity.
<p><b>Please write it down and store it securely.</b></p>
<p>&nbsp;</p>
<hr>
<h2>Want to learn more?</h2>
<p>You can find out much more about the blockchain and about commerce in High Fidelity by visiting our Docs site:</p>
<p><a href="http://docs.highfidelity.com" class="btn">Visit High Fidelity's Docs</a></p>
<hr>
</div>
</div>
<div class="footer">
High Fidelity | <a href="http://highfidelity.com" target="_blank">highfidelity.com</a>
</div>
</div>
</body>
</html>

View file

@ -27,6 +27,8 @@ ModalWindow {
destroyOnHidden: true
visible: true
readonly property bool isTablet: false
property string iconText: ""
property int iconSize: 50
@ -35,6 +37,10 @@ ModalWindow {
keyboardOverride: true // Disable ModalWindow's keyboard.
function tryDestroy() {
root.destroy()
}
LoginDialog {
id: loginDialog

View file

@ -29,11 +29,12 @@ Item {
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth,
termsContainer.contentWidth))
if (root.isTablet === false) {
var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth,
termsContainer.contentWidth))
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
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))
}
}
@ -61,11 +62,8 @@ Item {
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
onClicked: root.tryDestroy()
}
}
@ -96,17 +94,19 @@ Item {
id: termsContainer
anchors {
top: additionalTextContainer.bottom
left: parent.left
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
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.HorizontalFit
horizontalAlignment: Text.AlignVCenter
onLinkActivated: loginDialog.openUrl(link)
}
@ -128,8 +128,10 @@ Item {
console.log("Create Failed: " + error)
bodyLoader.source = "UsernameCollisionBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
if (!root.isTablet) {
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
onHandleLoginCompleted: {
console.log("Login Succeeded")

View file

@ -9,7 +9,7 @@
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
@ -45,8 +45,7 @@ Item {
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth);
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
hifi.dimensions.contentSpacing.y + buttons.height;
4 * hifi.dimensions.contentSpacing.y + form.height;
if (additionalInformation.visible) {
targetWidth = Math.max(targetWidth, additionalInformation.width);
@ -66,9 +65,6 @@ Item {
if (loginDialog.isSteamRunning()) {
additionalInformation.visible = !isLoading
}
leftButton.visible = !isLoading
buttons.visible = !isLoading
}
BusyIndicator {
@ -108,30 +104,27 @@ Item {
Column {
id: form
width: parent.width
onHeightChanged: d.resize(); onWidthChanged: d.resize();
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: 2 * hifi.dimensions.contentSpacing.y
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Username or Email"
}
TextField {
id: usernameField
width: parent.width
focus: true
label: "Username or Email"
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
verticalCenter: usernameField.textFieldLabel.verticalCenter
left: usernameField.textFieldLabel.right
leftMargin: 10
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
@ -143,23 +136,19 @@ Item {
onLinkActivated: loginDialog.openUrl(link)
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
TextField {
id: passwordField
width: parent.width
label: "Password"
echoMode: TextInput.Password
}
label: "Password"
echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
verticalCenter: passwordField.textFieldLabel.verticalCenter
left: passwordField.textFieldLabel.right
leftMargin: 10
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
@ -172,25 +161,76 @@ Item {
}
}
}
InfoItem {
id: additionalInformation
anchors {
top: form.bottom
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
CheckBoxQQC2 {
id: showPassword
text: "Show password"
}
visible: loginDialog.isSteamRunning()
InfoItem {
id: additionalInformation
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
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
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
color: hifi.buttons.blue
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.tryDestroy()
}
}
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("Don't have an account?")
}
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
}
}
}
}
}
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
@ -200,70 +240,22 @@ Item {
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottom: parent.bottom
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Row {
id: leftButton
anchors {
left: parent.left
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Sign Up")
visible: !loginDialog.isSteamRunning()
onClicked: {
bodyLoader.setSource("SignUpBody.qml")
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}
Row {
id: buttons
anchors {
right: parent.right
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
color: hifi.buttons.blue
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Sign Into High Fidelity")
root.iconText = "<"
keyboardEnabled = HMD.active;
//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; })
}
d.resize();
if (failAfterSignUp) {
@ -311,11 +303,11 @@ Item {
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
}
}
}

View file

@ -9,7 +9,7 @@
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick 2.7
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
@ -18,8 +18,8 @@ import "../styles-uit"
Item {
id: signInBody
clip: true
width: root.pane.width
height: root.pane.height
width: root.pane.width
property bool required: false
@ -29,7 +29,7 @@ Item {
}
function cancel() {
root.destroy()
root.tryDestroy()
}
QtObject {
@ -121,8 +121,10 @@ Item {
console.log("Login Failed")
bodyLoader.source = "CompleteProfileBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
if (!root.isTablet) {
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}
}

View file

@ -9,7 +9,7 @@
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
@ -44,8 +44,7 @@ Item {
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth);
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
hifi.dimensions.contentSpacing.y + buttons.height;
4 * hifi.dimensions.contentSpacing.y + form.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))
@ -96,44 +95,31 @@ Item {
Column {
id: form
width: parent.width
onHeightChanged: d.resize(); onWidthChanged: d.resize();
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: 2 * hifi.dimensions.contentSpacing.y
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: emailField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Email"
}
TextField {
id: emailField
width: parent.width
label: "Email"
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Username"
}
TextField {
id: usernameField
width: parent.width
label: "Username"
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
verticalCenter: parent.textFieldLabel.verticalCenter
left: parent.textFieldLabel.right
leftMargin: 10
}
text: qsTr("No spaces / special chars.")
@ -145,23 +131,17 @@ Item {
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Password"
echoMode: TextInput.Password
}
TextField {
id: passwordField
width: parent.width
label: "Password"
echoMode: TextInput.Password
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
verticalCenter: parent.textFieldLabel.verticalCenter
left: parent.textFieldLabel.right
leftMargin: 10
}
text: qsTr("At least 6 characters")
@ -173,6 +153,53 @@ Item {
}
}
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
}
}
}
}
Row {
id: buttons
anchors.horizontalCenter: parent.horizontalCenter
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Sign Up")
color: hifi.buttons.blue
onClicked: signupBody.signup()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.tryDestroy()
}
}
}
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
@ -182,69 +209,21 @@ Item {
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottom: parent.bottom
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Row {
id: leftButton
anchors {
left: parent.left
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
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")
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}
Row {
id: buttons
anchors {
right: parent.right
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Sign Up")
color: hifi.buttons.blue
onClicked: signupBody.signup()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Create an Account")
root.iconText = "<"
keyboardEnabled = HMD.active;
//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; })
}
d.resize();
emailField.forceActiveFocus();
@ -275,8 +254,10 @@ Item {
onHandleLoginFailed: {
// we failed to login, show the LoginDialog so the user will try again
bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
if (!root.isTablet) {
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}

View file

@ -79,7 +79,7 @@ Item {
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
width: 250
width: parent.width
placeholderText: "Choose your own"
}
@ -102,7 +102,7 @@ Item {
bottom: parent.bottom
right: parent.right
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
@ -122,14 +122,21 @@ Item {
text: qsTr("Cancel")
onClicked: root.destroy()
onClicked: root.tryDestroy()
}
}
Component.onCompleted: {
root.title = qsTr("Complete Your Profile")
root.iconText = "<"
keyboardEnabled = HMD.active;
//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; })
}
d.resize();
}

View file

@ -77,7 +77,7 @@ Item {
text: qsTr("Close");
onClicked: root.destroy()
onClicked: root.tryDestroy()
}
}

View file

@ -55,7 +55,11 @@ Item {
text: "Avatars: " + root.avatarCount
}
StatText {
text: "Frame Rate: " + root.framerate.toFixed(2);
text: "Game Rate: " + root.gameLoopRate
}
StatText {
visible: root.expanded
text: root.gameUpdateStats
}
StatText {
text: "Render Rate: " + root.renderrate.toFixed(2);
@ -64,21 +68,17 @@ Item {
text: "Present Rate: " + root.presentrate.toFixed(2);
}
StatText {
text: "Present New Rate: " + root.presentnewrate.toFixed(2);
visible: root.expanded
text: " Present New Rate: " + root.presentnewrate.toFixed(2);
}
StatText {
text: "Present Drop Rate: " + root.presentdroprate.toFixed(2);
visible: root.expanded
text: " Present Drop Rate: " + root.presentdroprate.toFixed(2);
}
StatText {
text: "Stutter Rate: " + root.stutterrate.toFixed(3);
visible: root.stutterrate != -1;
}
StatText {
text: "Simrate: " + root.simrate
}
StatText {
text: "Avatar Simrate: " + root.avatarSimrate
}
StatText {
text: "Missed Frame Count: " + root.appdropped;
visible: root.appdropped > 0;
@ -261,9 +261,6 @@ Item {
StatText {
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
StatText {
text: "Avatar: " + root.avatarSimulationTime.toFixed(1) + " ms"
}
StatText {
text: "Triangles: " + root.triangles +
" / Material Switches: " + root.materialSwitches

View file

@ -1,124 +0,0 @@
//
// CompleteProfileBody.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 QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: completeProfileBody
clip: true
QtObject {
id: d
function resize() {}
}
Row {
id: buttons
anchors {
top: parent.top
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: 200
text: qsTr("Create your profile")
color: hifi.buttons.blue
onClicked: loginDialog.createAccountFromStream()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: bodyLoader.popup()
}
}
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.setSource("LinkAccountBody.qml")
}
}
InfoItem {
id: termsContainer
anchors {
top: additionalTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
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
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
Component.onCompleted: {
loginDialogRoot.title = qsTr("Complete Your Profile")
loginDialogRoot.iconText = "<"
d.resize();
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
loginDialog.loginThroughSteam()
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)
bodyLoadersetSource("UsernameCollisionBody.qml")
}
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
}
onHandleLoginFailed: {
console.log("Login Failed")
}
}
}

View file

@ -1,296 +0,0 @@
//
// LinkAccountBody.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 QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: linkAccountBody
clip: true
height: parent.height
width: parent.width
property bool failAfterSignUp: false
function login() {
mainTextContainer.visible = false
toggleLoading(true)
loginDialog.login(usernameField.text, passwordField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
function resize() {}
}
function toggleLoading(isLoading) {
linkAccountSpinner.visible = isLoading
form.visible = !isLoading
if (loginDialog.isSteamRunning()) {
additionalInformation.visible = !isLoading
}
leftButton.visible = !isLoading
buttons.visible = !isLoading
}
BusyIndicator {
id: linkAccountSpinner
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
running: true
width: 48
height: 48
}
ShortcutText {
id: mainTextContainer
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
text: qsTr("Username or password incorrect.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
Column {
id: form
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: 2 * hifi.dimensions.contentSpacing.y
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Username or Email"
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
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)
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Password"
echoMode: TextInput.Password
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
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)
}
}
}
InfoItem {
id: additionalInformation
anchors {
top: form.bottom
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
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
}
// 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: leftButton
anchors {
left: parent.left
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Sign Up")
visible: !loginDialog.isSteamRunning()
onClicked: {
bodyLoader.setSource("SignUpBody.qml")
}
}
}
Row {
id: buttons
anchors {
right: parent.right
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
color: hifi.buttons.blue
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: {
bodyLoader.popup()
}
}
}
Component.onCompleted: {
loginDialogRoot.title = qsTr("Sign Into High Fidelity")
loginDialogRoot.iconText = "<"
keyboardEnabled = HMD.active;
d.resize();
if (failAfterSignUp) {
mainTextContainer.text = "Account created successfully."
mainTextContainer.visible = true
}
usernameField.forceActiveFocus();
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded, linking steam account")
if (loginDialog.isSteamRunning()) {
loginDialog.linkSteam()
} else {
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
}
}
onHandleLoginFailed: {
console.log("Login Failed")
mainTextContainer.visible = true
toggleLoading(false)
}
onHandleLinkCompleted: {
console.log("Link Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
}
onHandleLinkFailed: {
console.log("Link Failed")
toggleLoading(false)
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
}
}
}

View file

@ -1,109 +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.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: signInBody
clip: true
property bool required: false
function login() {
console.log("Trying to log in")
loginDialog.loginThroughSteam()
}
function cancel() {
bodyLoader.popup()
}
QtObject {
id: d
function resize() {}
}
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
style: OriginalStyles.ButtonStyle {
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: {
loginDialogRoot.title = required ? qsTr("Sign In Required")
: qsTr("Sign In")
loginDialogRoot.iconText = ""
d.resize();
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
}
onHandleLoginFailed: {
console.log("Login Failed")
bodyLoader.setSource("CompleteProfileBody.qml")
}
}
}

View file

@ -1,276 +0,0 @@
//
// SignUpBody.qml
//
// Created by Stephen Birarda on 7 Dec 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: signupBody
clip: true
// height: parent.height
// width: parent.width
function signup() {
mainTextContainer.visible = false
toggleLoading(true)
loginDialog.signup(emailField.text, usernameField.text, passwordField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
function resize() {}
}
function toggleLoading(isLoading) {
linkAccountSpinner.visible = isLoading
form.visible = !isLoading
leftButton.visible = !isLoading
buttons.visible = !isLoading
}
BusyIndicator {
id: linkAccountSpinner
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
running: true
width: 48
height: 48
}
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
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: 2 * hifi.dimensions.contentSpacing.y
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: emailField
anchors {
verticalCenter: parent.verticalCenter
}
width: 300
label: "Email"
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 300
label: "Username"
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: qsTr("No spaces / special chars.")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.blueAccent
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 300
label: "Password"
echoMode: TextInput.Password
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: qsTr("At least 6 characters")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.blueAccent
}
}
}
// 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: leftButton
anchors {
left: parent.left
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
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")
}
}
}
Row {
id: buttons
anchors {
right: parent.right
bottom: parent.bottom
bottomMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Sign Up")
color: hifi.buttons.blue
onClicked: signupBody.signup()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: bodyLoader.popup()
}
}
Component.onCompleted: {
loginDialogRoot.title = qsTr("Create an Account")
loginDialogRoot.iconText = "<"
keyboardEnabled = HMD.active;
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 })
}
onHandleLoginFailed: {
// we failed to login, show the LoginDialog so the user will try again
bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true })
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
signupBody.signup()
break
}
}
}

View file

@ -1,157 +0,0 @@
//
// UsernameCollisionBody.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 QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: usernameCollisionBody
clip: true
width: parent.width
height: parent.height
function create() {
mainTextContainer.visible = false
loginDialog.createAccountFromStream(textField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
function resize() {}
}
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: 250
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
topMargin: hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Create your profile")
color: hifi.buttons.blue
onClicked: usernameCollisionBody.create()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: bodyLoader.popup()
}
}
Component.onCompleted: {
loginDialogRoot.title = qsTr("Complete Your Profile")
loginDialogRoot.iconText = "<"
keyboardEnabled = HMD.active;
d.resize();
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
loginDialog.loginThroughSteam()
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)
mainTextContainer.visible = true
mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.")
}
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
}
onHandleLoginFailed: {
console.log("Login Failed")
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
usernameCollisionBody.create()
break
}
}
}

View file

@ -1,79 +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 "../controls-uit"
import "../styles-uit"
Item {
id: welcomeBody
clip: true
property bool welcomeBack: false
function setTitle() {
loginDialogRoot.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
loginDialogRoot.iconText = ""
d.resize();
}
QtObject {
id: d
function resize() {}
}
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: bodyLoader.popup()
}
}
Component.onCompleted: {
welcomeBody.setTitle()
}
Connections {
target: Account
onUsernameChanged: welcomeBody.setTitle()
}
}

View file

@ -35,6 +35,7 @@ TextField {
font.pixelSize: hifi.fontSizes.textFieldInput
font.italic: textField.text == ""
height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered.
property alias textFieldLabel: textFieldLabel
y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0

View file

@ -88,7 +88,7 @@ FocusScope {
return;
}
var oldRecommendedRect = recommendedRect;
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedOverlayRect();
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
newRecommendedRectJS.width,
newRecommendedRectJS.height);
@ -271,7 +271,7 @@ FocusScope {
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedOverlayRect();
var newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
var windows = d.getTopLevelWindows();
for (var i = 0; i < windows.length; ++i) {
@ -298,6 +298,23 @@ FocusScope {
pinned = !pinned
}
function isPointOnWindow(point) {
for (var i = 0; i < desktop.visibleChildren.length; i++) {
var child = desktop.visibleChildren[i];
if (child.visible) {
if (child.hasOwnProperty("modality")) {
var mappedPoint = child.mapFromGlobal(point.x, point.y);
var outLine = child.frame.children[2];
var framePoint = outLine.mapFromGlobal(point.x, point.y);
if (child.contains(mappedPoint) || outLine.contains(framePoint)) {
return true;
}
}
}
}
return false;
}
function setPinned(newPinned) {
pinned = newPinned
}
@ -393,7 +410,7 @@ FocusScope {
return;
}
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedOverlayRect();
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
newRecommendedRectJS.width,
newRecommendedRectJS.height);
@ -425,7 +442,7 @@ FocusScope {
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedOverlayRect();
var newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
}
@ -442,7 +459,7 @@ FocusScope {
return;
}
var recommended = Controller.getRecommendedOverlayRect();
var recommended = Controller.getRecommendedHUDRect();
var maxX = recommended.x + recommended.width;
var maxY = recommended.y + recommended.height;
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);

View file

@ -16,48 +16,66 @@ import "../controls-uit"
import "../styles-uit"
import "../windows"
import "../LoginDialog"
TabletModalWindow {
id: loginDialogRoot
id: realRoot
objectName: "LoginDialog"
signal sendToScript(var message);
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
readonly property bool isTablet: true
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 int colorScheme: hifi.colorSchemes.dark
property int colorScheme: hifi.colorSchemes.dark
property int titleWidth: 0
property string iconText: ""
property int icon: hifi.icons.none
property int iconSize: 35
MouseArea {
width: parent.width
height: parent.height
width: realRoot.width
height: realRoot.height
}
property bool keyboardOverride: true
onIconChanged: updateIcon();
property var items;
property string label: ""
onTitleWidthChanged: d.resize();
//onTitleWidthChanged: d.resize();
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
//onKeyboardRaisedChanged: d.resize();
signal canceled();
function updateIcon() {
if (!root) {
return;
}
iconText = hifi.glyphForIcon(root.icon);
}
property alias bodyLoader: bodyLoader
property alias loginDialog: loginDialog
property alias hifi: hifi
@ -65,9 +83,10 @@ TabletModalWindow {
HifiConstants { id: hifi }
onCanceled: {
if (loginDialogRoot.Stack.view) {
loginDialogRoot.Stack.view.pop();
} else if (gotoPreviousApp) {
if (bodyLoader.active === true) {
//bodyLoader.active = false
}
if (gotoPreviousApp) {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.returnToPreviousApp();
} else {
@ -75,45 +94,81 @@ TabletModalWindow {
}
}
LoginDialog {
id: loginDialog
width: parent.width
height: parent.height
StackView {
id: bodyLoader
property var item: currentItem
property var props
property string source: ""
TabletModalFrame {
id: mfRoot
onCurrentItemChanged: {
//cleanup source for future usage
source = ""
width: root.width
height: root.height + frameMarginTop + hifi.dimensions.contentMargin.x
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
verticalCenterOffset: -loginKeyboard.height / 2
}
LoginDialog {
id: loginDialog
anchors {
fill: parent
topMargin: parent.frameMarginTop
leftMargin: hifi.dimensions.contentMargin.x
rightMargin: hifi.dimensions.contentMargin.x
horizontalCenter: parent.horizontalCenter
}
function setSource(src, props) {
source = "../TabletLoginDialog/" + src
bodyLoader.props = props
}
function popup() {
bodyLoader.pop()
//check if last screen, if yes, dialog is popped out
if (depth === 1)
loginDialogRoot.canceled()
}
anchors.fill: parent
anchors.margins: 10
onSourceChanged: {
if (source !== "") {
bodyLoader.push(Qt.resolvedUrl(source), props)
}
}
Component.onCompleted: {
setSource(loginDialog.isSteamRunning() ?
"SignInBody.qml" :
"LinkAccountBody.qml")
Loader {
id: bodyLoader
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
source: loginDialog.isSteamRunning() ? "../LoginDialog/SignInBody.qml" : "../LoginDialog/LinkAccountBody.qml"
}
}
}
Keyboard {
id: loginKeyboard
raised: root.keyboardEnabled && root.keyboardRaised
numeric: root.punctuationMode
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
destroy()
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
break
}
}
}

View file

@ -14,10 +14,10 @@ import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import Qt.labs.settings 1.0
import "styles-uit"
import "controls-uit" as HifiControls
import "windows"
import "dialogs"
import "../styles-uit"
import "../controls-uit" as HifiControls
import "../windows"
import "../dialogs"
ScrollingWindow {
id: root
@ -38,7 +38,7 @@ ScrollingWindow {
property var assetMappingsModel: Assets.mappingModel;
property var currentDirectory;
property var selectedItems: treeView.selection.selectedIndexes.length;
Settings {
category: "Overlay.AssetServer"
property alias x: root.x
@ -58,6 +58,23 @@ ScrollingWindow {
assetMappingsModel.autoRefreshEnabled = false;
}
function letterbox(headerGlyph, headerText, message) {
letterboxMessage.headerGlyph = headerGlyph;
letterboxMessage.headerText = headerText;
letterboxMessage.text = message;
letterboxMessage.visible = true;
letterboxMessage.popupRadius = 0;
}
function errorMessageBox(message) {
return desktop.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
}
function doDeleteFile(path) {
console.log("Deleting " + path);
@ -154,10 +171,7 @@ ScrollingWindow {
}
function handleGetMappingsError(errorString) {
errorMessageBox(
"There was a problem retreiving the list of assets from your Asset Server.\n"
+ errorString
);
errorMessageBox("There was a problem retrieving the list of assets from your Asset Server.\n" + errorString);
}
function addToWorld() {
@ -332,7 +346,7 @@ ScrollingWindow {
if (!path) {
return;
}
var modalMessage = "";
var items = selectedItems.toString();
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
@ -448,19 +462,16 @@ ScrollingWindow {
});
}
}
function errorMessageBox(message) {
return desktop.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
}
Item {
width: pane.contentWidth
height: pane.height
// The letterbox used for popup messages
LetterboxMessage {
id: letterboxMessage;
z: 999; // Force the popup on top of everything else
}
HifiControls.ContentSection {
id: assetDirectory
@ -476,21 +487,21 @@ ScrollingWindow {
HifiControls.Button {
text: "Add To World"
color: hifi.buttons.black
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: 120
enabled: canAddToWorld(assetProxyModel.data(treeView.selection.currentIndex, 0x100))
onClicked: root.addToWorld()
}
HifiControls.Button {
text: "Rename"
color: hifi.buttons.black
colorScheme: root.colorScheme
width: 80
onClicked: root.renameFile()
enabled: canRename()
}
@ -513,6 +524,7 @@ ScrollingWindow {
id: treeView
anchors.top: assetDirectory.bottom
anchors.bottom: infoRow.top
anchors.bottomMargin: 2 * hifi.dimensions.contentSpacing.y
anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border
anchors.left: parent.left
anchors.right: parent.right
@ -555,7 +567,7 @@ ScrollingWindow {
case "Not Baked":
return hifi.glyphs.circleSlash;
case "Baked":
return hifi.glyphs.check_2_01;
return hifi.glyphs.checkmark;
case "Error":
return hifi.glyphs.alert;
default:
@ -584,8 +596,24 @@ ScrollingWindow {
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
elide: Text.ElideRight
horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft
elide: Text.ElideMiddle
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
onEntered: {
if (parent.truncated) {
treeLabelToolTip.show(parent);
}
}
onExited: treeLabelToolTip.hide();
}
}
}
Component {
@ -668,6 +696,42 @@ ScrollingWindow {
}
}
Rectangle {
id: treeLabelToolTip
visible: false
z: 100 // Render on top
width: toolTipText.width + 2 * hifi.dimensions.textPadding
height: hifi.dimensions.tableRowHeight
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
border.color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
FiraSansSemiBold {
id: toolTipText
anchors.centerIn: parent
size: hifi.fontSizes.tableText
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
}
Timer {
id: showTimer
interval: 1000
onTriggered: { treeLabelToolTip.visible = true; }
}
function show(item) {
var coord = item.mapToItem(parent, item.x, item.y);
toolTipText.text = item.text;
treeLabelToolTip.x = coord.x - hifi.dimensions.textPadding;
treeLabelToolTip.y = coord.y;
showTimer.start();
}
function hide() {
showTimer.stop();
treeLabelToolTip.visible = false;
}
}
MouseArea {
propagateComposedEvents: true
@ -717,26 +781,35 @@ ScrollingWindow {
anchors.left: treeView.left
anchors.right: treeView.right
anchors.bottom: uploadSection.top
anchors.bottomMargin: hifi.dimensions.contentSpacing.y
spacing: hifi.dimensions.contentSpacing.x
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter
function makeText() {
var numPendingBakes = assetMappingsModel.numPendingBakes;
if (selectedItems > 1 || numPendingBakes === 0) {
return selectedItems + " items selected";
} else {
return numPendingBakes + " bakes pending"
}
}
size: hifi.fontSizes.sectionName
font.capitalization: Font.AllUppercase
text: selectedItems + " items selected"
text: makeText()
color: hifi.colors.lightGrayText
}
HifiControls.CheckBox {
function isChecked() {
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
var bakingDisabled = (status === "Not Baked" || status === "--");
return selectedItems === 1 && !bakingDisabled;
}
HifiControls.HorizontalSpacer { }
text: "Use baked (optimized) versions"
HifiControls.CheckBox {
id: bakingCheckbox
anchors.leftMargin: 2 * hifi.dimensions.contentSpacing.x
anchors.verticalCenter: parent.verticalCenter
text: " Use baked version"
colorScheme: root.colorScheme
enabled: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) !== "--"
enabled: isEnabled()
checked: isChecked()
onClicked: {
var mappings = [];
@ -752,7 +825,66 @@ ScrollingWindow {
checked = Qt.binding(isChecked);
}
function isEnabled() {
if (!treeView.selection.hasSelection) {
return false;
}
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
if (status === "--") {
return false;
}
var bakingEnabled = status !== "Not Baked";
for (var i in treeView.selection.selectedIndexes) {
var thisStatus = assetProxyModel.data(treeView.selection.selectedIndexes[i], 0x105);
if (thisStatus === "--") {
return false;
}
var thisBakingEnalbed = (thisStatus !== "Not Baked");
if (bakingEnabled !== thisBakingEnalbed) {
return false;
}
}
return true;
}
function isChecked() {
if (!treeView.selection.hasSelection) {
return false;
}
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
return isEnabled() && status !== "Not Baked";
}
}
Item {
anchors.verticalCenter: parent.verticalCenter
width: infoGlyph.size;
height: infoGlyph.size;
HiFiGlyphs {
id: infoGlyph;
anchors.fill: parent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
text: hifi.glyphs.question;
size: 35;
color: hifi.colors.lightGrayText;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: infoGlyph.color = hifi.colors.blueHighlight;
onExited: infoGlyph.color = hifi.colors.lightGrayText;
onClicked: letterbox(hifi.glyphs.question,
"What is baking?",
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
}
}
}
HifiControls.ContentSection {
@ -790,7 +922,7 @@ ScrollingWindow {
id: image
width: 24
height: 24
source: "../images/Loading-Outer-Ring.png"
source: "../../images/Loading-Outer-Ring.png"
RotationAnimation on rotation {
loops: Animation.Infinite
from: 0
@ -801,7 +933,7 @@ ScrollingWindow {
Image {
width: 24
height: 24
source: "../images/Loading-Inner-H.png"
source: "../../images/Loading-Inner-H.png"
}
HifiControls.Label {
id: uploadProgressLabel
@ -811,10 +943,9 @@ ScrollingWindow {
text: "In progress..."
colorScheme: root.colorScheme
}
}
}
}
}
}

View file

@ -167,6 +167,9 @@ Rectangle {
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
width: margins.sizeText + margins.sizeLevel
anchors.left: parent.left
anchors.leftMargin: margins.sizeCheckBox
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE INPUT DEVICE");

View file

@ -29,12 +29,22 @@ RowLayout {
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: Audio.playSystemSound should not require position
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(function() { isPlaying = false; sample = null; });
if (sample === null && !isPlaying) {
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(reset);
}
}
function stopSound() {
sample && sample.stop();
if (sample && isPlaying) {
sample.stop();
}
}
function reset() {
sample.finished.disconnect(reset);
isPlaying = false;
sample = null;
}
Component.onCompleted: createSampleSound();

View file

@ -26,74 +26,60 @@ Rectangle {
HifiConstants { id: hifi; }
id: root;
objectName: "checkout"
property string activeView: "initialize";
property bool purchasesReceived: false;
property bool balanceReceived: false;
property bool securityImageResultReceived: false;
property string itemName;
property string itemId;
property string itemPreviewImageUrl;
property string itemHref;
property double balanceAfterPurchase;
property bool alreadyOwned: false;
property int itemPrice: 0;
property int itemPrice: -1;
property bool itemIsJson: true;
property bool shouldBuyWithControlledFailure: false;
property bool debugCheckoutSuccess: false;
property bool canRezCertifiedItems: false;
property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified;
// Style
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
if (root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
}
} else if (walletStatus === 1) {
if (root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
}
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
}
} else if (walletStatus === 3) {
authSuccessStep();
} else {
console.log("ERROR in Checkout.qml: Unknown wallet status: " + walletStatus);
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
commerce.account();
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
}
onSecurityImageResult: {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
authSuccessStep();
commerce.getWalletStatus();
}
}
onBuyResult: {
if (result.status !== 'success') {
failureErrorText.text = "Here's some more info about the error:<br><br>" + (result.message);
failureErrorText.text = result.message;
root.activeView = "checkoutFailure";
} else {
root.itemHref = result.data.download_url;
root.activeView = "checkoutSuccess";
}
}
@ -123,11 +109,24 @@ Rectangle {
}
}
onItemIdChanged: {
commerce.inventory();
itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
}
onItemHrefChanged: {
itemIsJson = root.itemHref.endsWith('.json');
}
onItemPriceChanged: {
commerce.balance();
}
Timer {
id: notSetUpTimer;
interval: 200;
onTriggered: {
sendToScript({method: 'checkout_walletNotSetUp'});
sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId});
}
}
@ -176,6 +175,13 @@ Rectangle {
}
}
}
MouseArea {
enabled: titleBarContainer.usernameDropdownVisible;
anchors.fill: parent;
onClicked: {
titleBarContainer.usernameDropdownVisible = false;
}
}
//
// TITLE BAR END
//
@ -190,10 +196,9 @@ Rectangle {
color: hifi.colors.white;
Component.onCompleted: {
securityImageResultReceived = false;
purchasesReceived = false;
balanceReceived = false;
commerce.getLoginStatus();
commerce.getWalletStatus();
}
}
@ -281,7 +286,6 @@ Rectangle {
Image {
id: itemPreviewImage;
source: root.itemPreviewImageUrl;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
@ -291,6 +295,7 @@ Rectangle {
RalewaySemiBold {
id: itemNameText;
text: root.itemName;
// Text size
size: 26;
// Anchors
@ -315,19 +320,19 @@ Rectangle {
anchors.top: parent.top;
anchors.right: parent.right;
height: 30;
width: childrenRect.width;
width: itemPriceTextLabel.width + itemPriceText.width + 20;
// "HFC" balance label
HiFiGlyphs {
id: itemPriceTextLabel;
text: hifi.glyphs.hfc;
// Size
size: 36;
size: 30;
// Anchors
anchors.right: itemPriceText.left;
anchors.rightMargin: 4;
anchors.top: parent.top;
anchors.topMargin: -4;
anchors.topMargin: 0;
width: paintedWidth;
height: paintedHeight;
// Style
@ -335,7 +340,7 @@ Rectangle {
}
FiraSansSemiBold {
id: itemPriceText;
text: "--";
text: (root.itemPrice === -1) ? "--" : root.itemPrice;
// Text size
size: 26;
// Anchors
@ -405,7 +410,7 @@ Rectangle {
verticalAlignment: Text.AlignTop;
}
RalewaySemiBold {
RalewayRegular {
id: buyText;
// Text size
size: 18;
@ -424,7 +429,7 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
sendToScript({method: 'checkout_goToPurchases', filterText: itemNameText.text});
sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName});
}
}
}
@ -508,7 +513,7 @@ Rectangle {
RalewaySemiBold {
id: completeText2;
text: "The item " + '<font color="' + hifi.colors.blueAccent + '"><a href="#">' + itemNameText.text + '</a></font>' +
text: "The item " + '<font color="' + hifi.colors.blueAccent + '"><a href="#">' + root.itemName + '</a></font>' +
" has been added to your Purchases and a receipt will appear in your Wallet's transaction history.";
// Text size
size: 20;
@ -570,8 +575,8 @@ Rectangle {
anchors.right: parent.right;
text: "Rez It"
onClicked: {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
}
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
@ -709,7 +714,9 @@ Rectangle {
anchors.top: titleBarContainer.bottom;
anchors.bottom: root.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
RalewayRegular {
id: failureHeaderText;
@ -718,57 +725,65 @@ Rectangle {
size: 24;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 80;
anchors.topMargin: 40;
height: paintedHeight;
anchors.left: parent.left;
anchors.right: parent.right;
// Style
color: hifi.colors.black;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: failureErrorText;
// Text size
size: 16;
// Anchors
Rectangle {
id: failureErrorTextContainer;
anchors.top: failureHeaderText.bottom;
anchors.topMargin: 35;
height: paintedHeight;
anchors.left: parent.left;
anchors.right: parent.right;
// Style
color: hifi.colors.black;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
height: failureErrorText.height + 30;
radius: 4;
border.width: 2;
border.color: "#F3808F";
color: "#FFC3CD";
AnonymousProRegular {
id: failureErrorText;
// Text size
size: 16;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: paintedHeight;
// Style
color: hifi.colors.black;
wrapMode: Text.Wrap;
verticalAlignment: Text.AlignVCenter;
}
}
Item {
id: backToMarketplaceButtonContainer;
// Size
width: root.width;
height: 130;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
anchors.bottomMargin: 16;
// "Back to Marketplace" button
HifiControlsUit.Button {
id: backToMarketplaceButton;
color: hifi.buttons.black;
color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: parent.width/2 - anchors.leftMargin*2;
text: "Back to Marketplace";
onClicked: {
sendToScript({method: 'checkout_continueShopping', itemId: itemId});
@ -814,15 +829,9 @@ Rectangle {
switch (message.method) {
case 'updateCheckoutQML':
itemId = message.params.itemId;
itemNameText.text = message.params.itemName;
itemName = message.params.itemName;
root.itemPrice = message.params.itemPrice;
itemPriceText.text = root.itemPrice === 0 ? "Free" : root.itemPrice;
itemHref = message.params.itemHref;
itemPreviewImageUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
if (itemHref.indexOf('.json') === -1) {
root.itemIsJson = false;
}
root.canRezCertifiedItems = message.canRezCertifiedItems;
setBuyText();
break;
default:
@ -845,10 +854,10 @@ Rectangle {
if (root.purchasesReceived && root.balanceReceived) {
if (root.balanceAfterPurchase < 0) {
if (root.alreadyOwned) {
buyText.text = "Your Wallet does not have sufficient funds to purchase this item again.<br>" +
'<font color="' + hifi.colors.blueAccent + '"><a href="#">View the copy you own in My Purchases</a></font>';
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.<br>" +
'<font color="' + hifi.colors.blueAccent + '"><a href="#">View the copy you own in My Purchases</a></font></b>';
} else {
buyText.text = "Your Wallet does not have sufficient funds to purchase this item.";
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
}
buyTextContainer.color = "#FFC3CD";
buyTextContainer.border.color = "#F3808F";
@ -856,8 +865,8 @@ Rectangle {
buyGlyph.size = 54;
} else {
if (root.alreadyOwned) {
buyText.text = 'You already own this item.<br>Purchasing it will buy another copy.<br><font color="'
+ hifi.colors.blueAccent + '"><a href="#">View this item in My Purchases</a></font>';
buyText.text = '<b>You already own this item.<br>Purchasing it will buy another copy.<br><font color="'
+ hifi.colors.blueAccent + '"><a href="#">View this item in My Purchases</a></font></b>';
buyTextContainer.color = "#FFD6AD";
buyTextContainer.border.color = "#FAC07D";
buyGlyph.text = hifi.glyphs.alert;
@ -870,7 +879,7 @@ Rectangle {
buyText.text = "";
}
} else {
buyText.text = "This Marketplace item isn't an entity. It <b>will not</b> be added to your <b>Purchases</b>.";
buyText.text = "This free item <b>will not</b> be added to your <b>Purchases</b>. Non-entities can't yet be purchased for HFC.";
buyTextContainer.color = "#FFD6AD";
buyTextContainer.border.color = "#FAC07D";
buyGlyph.text = hifi.glyphs.alert;
@ -884,12 +893,10 @@ Rectangle {
} else {
root.activeView = "checkoutSuccess";
}
if (!balanceReceived) {
commerce.balance();
}
if (!purchasesReceived) {
commerce.inventory();
}
root.balanceReceived = false;
root.purchasesReceived = false;
commerce.inventory();
commerce.balance();
}
//

View file

@ -82,6 +82,7 @@ Rectangle {
height: 140;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
RalewayRegular {

View file

@ -27,23 +27,28 @@ Item {
id: root;
property string referrerURL: "https://metaverse.highfidelity.com/marketplace?";
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
property alias usernameDropdownVisible: usernameDropdown.visible;
height: mainContainer.height + additionalDropdownHeight;
Hifi.QmlCommerce {
id: commerce;
onLoginStatusResult: {
if (!isLoggedIn) {
onWalletStatusResult: {
if (walletStatus === 0) {
sendToParent({method: "needsLogIn"});
} else if (walletStatus === 3) {
commerce.getSecurityImage();
} else if (walletStatus > 3) {
console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus);
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
onLoginStatusResult: {
if (!isLoggedIn) {
sendToParent({method: "needsLogIn"});
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
commerce.getWalletStatus();
}
}
@ -56,8 +61,7 @@ Item {
}
Component.onCompleted: {
commerce.getLoginStatus();
commerce.getSecurityImage();
commerce.getWalletStatus();
}
Connections {
@ -210,6 +214,7 @@ Item {
anchors.bottomMargin: 16;
width: height;
mipmap: true;
cache: false;
MouseArea {
enabled: securityImage.visible;

View file

@ -0,0 +1,68 @@
//
// SortableListModel.qml
// qml/hifi/commerce/common
//
// SortableListModel
//
// Created by Zach Fox on 2017-09-28
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
ListModel {
id: root;
property string sortColumnName: "";
property bool isSortingDescending: true;
function swap(a, b) {
if (a < b) {
move(a, b, 1);
move (b - 1, a, 1);
} else if (a > b) {
move(b, a, 1);
move(a - 1, b, 1);
}
}
function partition(begin, end, pivot) {
var piv = get(pivot)[sortColumnName];
swap(pivot, end - 1);
var store = begin;
for (var i = begin; i < end - 1; ++i) {
if (isSortingDescending) {
if (get(i)[sortColumnName] < piv) {
swap(store, i);
++store;
}
} else {
if (get(i)[sortColumnName] > piv) {
swap(store, i);
++store;
}
}
}
swap(end - 1, store);
return store;
}
function qsort(begin, end) {
if (end - 1 > begin) {
var pivot = begin + Math.floor(Math.random() * (end - begin));
pivot = partition(begin, end, pivot);
qsort(begin, pivot);
qsort(pivot + 1, end);
}
}
function quickSort() {
qsort(0, count)
}
}

View file

@ -25,17 +25,76 @@ Rectangle {
HifiConstants { id: hifi; }
id: root;
property string marketplaceId: "";
property string marketplaceUrl;
property string certificateId;
property string itemName: "--";
property string itemOwner: "--";
property string itemEdition: "--";
property string dateOfPurchase: "";
property bool closeGoesToPurchases: false;
property string dateOfPurchase: "--";
property bool isLightbox: false;
property bool isMyCert: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
id: commerce;
}
onCertificateInfoResult: {
if (result.status !== 'success') {
console.log("Failed to get certificate info", result.message);
} else {
root.marketplaceUrl = result.data.marketplace_item_url;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
root.itemOwner = root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
root.dateOfPurchase = getFormattedDate(result.data.transfer_created_at * 1000);
root.itemName = result.data.marketplace_item_name;
if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") {
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
if (result.data.invalid_reason) {
errorText.text = result.data.invalid_reason;
}
} else if (result.data.transfer_status[0] === "pending") {
titleBarText.text = "Certificate Pending";
errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " +
"this entity will be cleaned up by the domain.";
errorText.color = hifi.colors.baseGray;
}
}
}
}
onCertificateIdChanged: {
if (certificateId !== "") {
commerce.certificateInfo(certificateId);
}
}
onVisibleChanged: {
if (!visible) {
titleBarText.text = "Certificate";
popText.text = "PROOF OF PURCHASE";
root.certificateId = "";
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.marketplaceUrl = "";
root.isMyCert = false;
errorText.text = "";
}
}
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Image {
anchors.fill: parent;
@ -107,7 +166,7 @@ Rectangle {
size: 28;
// Anchors
anchors.top: itemNameHeader.bottom;
anchors.topMargin: 4;
anchors.topMargin: 8;
anchors.left: itemNameHeader.left;
anchors.right: itemNameHeader.right;
height: paintedHeight;
@ -118,7 +177,7 @@ Rectangle {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = hifi.colors.blueAccent;
@ -132,14 +191,20 @@ Rectangle {
size: 16;
// Anchors
anchors.top: itemName.bottom;
anchors.topMargin: 20;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.lightGray;
}
FontLoader { id: ralewayRegular; source: "../../../../fonts/Raleway-Regular.ttf"; }
TextMetrics {
id: textMetrics;
font.family: ralewayRegular.name
text: root.itemOwner;
}
RalewayRegular {
id: ownedBy;
@ -148,14 +213,31 @@ Rectangle {
size: 22;
// Anchors
anchors.top: ownedByHeader.bottom;
anchors.topMargin: 4;
anchors.topMargin: 8;
anchors.left: ownedByHeader.left;
anchors.right: ownedByHeader.right;
height: paintedHeight;
height: textMetrics.height;
width: root.isMyCert ? textMetrics.width + 25 : ownedByHeader.width;
// Style
color: hifi.colors.darkGray;
elide: Text.ElideRight;
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert;
text: "(Private)";
size: 18;
// Anchors
anchors.top: ownedBy.top;
anchors.topMargin: 4;
anchors.bottom: ownedBy.bottom;
anchors.left: ownedBy.right;
anchors.leftMargin: 4;
anchors.right: ownedByHeader.right;
// Style
color: hifi.colors.lightGray;
elide: Text.ElideRight;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: editionHeader;
@ -164,23 +246,23 @@ Rectangle {
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.lightGray;
}
AnonymousProRegular {
id: edition;
text: root.itemEdition;
// Text size
size: 22;
size: 18;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.topMargin: 8;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
height: paintedHeight;
@ -191,31 +273,29 @@ Rectangle {
RalewayRegular {
id: dateOfPurchaseHeader;
text: "DATE OF PURCHASE";
visible: root.dateOfPurchase !== "";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.top: edition.bottom;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.lightGray;
}
AnonymousProRegular {
id: dateOfPurchase;
text: root.dateOfPurchase;
visible: root.dateOfPurchase !== "";
// Text size
size: 22;
size: 18;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
anchors.top: dateOfPurchaseHeader.bottom;
anchors.topMargin: 8;
anchors.left: dateOfPurchaseHeader.left;
anchors.right: dateOfPurchaseHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
@ -223,15 +303,13 @@ Rectangle {
RalewayRegular {
id: errorText;
text: "Here we will display some text if there's an <b>error</b> with the certificate " +
"(DMCA takedown, invalid cert, location of item updated)";
// Text size
size: 20;
// Anchors
anchors.top: root.dateOfPurchase !== "" ? dateOfPurchase.bottom : edition.bottom;
anchors.topMargin: 40;
anchors.left: root.dateOfPurchase !== "" ? dateOfPurchase.left : edition.left;
anchors.right: root.dateOfPurchase !== "" ? dateOfPurchase.right : edition.right;
anchors.top: dateOfPurchase.bottom;
anchors.topMargin: 36;
anchors.left: dateOfPurchase.left;
anchors.right: dateOfPurchase.right;
anchors.bottom: parent.bottom;
// Style
wrapMode: Text.WordWrap;
@ -246,7 +324,7 @@ Rectangle {
Item {
id: buttonsContainer;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
anchors.bottomMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
@ -262,13 +340,18 @@ Rectangle {
height: 50;
text: "close";
onClicked: {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
if (root.isLightbox) {
root.visible = false;
} else {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
}
}
}
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
enabled: root.marketplaceUrl;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
@ -278,7 +361,7 @@ Rectangle {
height: 50;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
}
}
@ -301,20 +384,42 @@ Rectangle {
//
function fromScript(message) {
switch (message.method) {
case 'inspectionCertificate_setMarketplaceId':
root.marketplaceId = message.marketplaceId;
root.closeGoesToPurchases = message.closeGoesToPurchases;
break;
case 'inspectionCertificate_setItemInfo':
root.itemName = message.itemName;
root.itemOwner = message.itemOwner;
root.itemEdition = message.itemEdition;
case 'inspectionCertificate_setCertificateId':
root.certificateId = message.certificateId;
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
function getFormattedDate(timestamp) {
function addLeadingZero(n) {
return n < 10 ? '0' + n : '' + n;
}
var a = new Date(timestamp);
var year = a.getFullYear();
var month = addLeadingZero(a.getMonth());
var day = addLeadingZero(a.getDate());
var hour = a.getHours();
var drawnHour = hour;
if (hour === 0) {
drawnHour = 12;
} else if (hour > 12) {
drawnHour -= 12;
}
drawnHour = addLeadingZero(drawnHour);
var amOrPm = "AM";
if (hour >= 12) {
amOrPm = "PM";
}
var min = addLeadingZero(a.getMinutes());
var sec = addLeadingZero(a.getSeconds());
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
}
//
// FUNCTION DEFINITIONS END
//

View file

@ -13,6 +13,7 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
@ -24,40 +25,133 @@ Rectangle {
HifiConstants { id: hifi; }
id: root;
property string activeView: "step_1";
// Style
color: hifi.colors.baseGray;
property int activeView: 1;
Image {
anchors.fill: parent;
source: "images/Purchase-First-Run-" + root.activeView + ".jpg";
}
// 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;
}
Item {
id: header;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
Image {
id: marketplaceHeaderImage;
source: "../common/images/marketplaceHeaderImage.png";
anchors.top: parent.top;
anchors.topMargin: 2;
anchors.left: parent.left;
anchors.leftMargin: 8;
width: 140;
height: 58;
fillMode: Image.PreserveAspectFit;
visible: false;
}
ColorOverlay {
anchors.fill: marketplaceHeaderImage;
source: marketplaceHeaderImage;
color: "#FFFFFF"
}
RalewayRegular {
id: introText1;
text: "INTRODUCTION TO";
// Text size
size: 15;
// Anchors
anchors.top: marketplaceHeaderImage.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 12;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
}
RalewayRegular {
id: introText2;
text: "My Purchases";
// Text size
size: 28;
// Anchors
anchors.top: introText1.bottom;
anchors.left: parent.left;
anchors.leftMargin: 12;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
}
}
//
// "STEP 1" START
//
Item {
id: step_1;
visible: root.activeView === "step_1";
anchors.top: parent.top;
visible: root.activeView === 1;
anchors.top: header.bottom;
anchors.topMargin: 100;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
anchors.bottom: parent.bottom;
RalewayRegular {
id: step1text;
text: "<b>This is the first-time Purchases tutorial.</b><br><br>Here is some <b>bold text</b> " +
"inside Step 1.";
text: "The <b>'REZ IT'</b> button makes your purchase appear in front of you.";
// Text size
size: 24;
size: 20;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: 180;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
}
// "Next" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: step1text.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
width: 150;
height: 40;
text: "Next";
onClicked: {
root.activeView++;
}
}
// "SKIP" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 32;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
width: 150;
height: 40;
text: "SKIP";
onClicked: {
sendSignalToParent({method: 'tutorial_finished'});
}
}
}
//
@ -69,127 +163,52 @@ Rectangle {
//
Item {
id: step_2;
visible: root.activeView === "step_2";
anchors.top: parent.top;
visible: root.activeView === 2;
anchors.top: header.bottom;
anchors.topMargin: 45;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
anchors.bottom: parent.bottom;
RalewayRegular {
id: step2text;
text: "<b>STEP TWOOO!!!</b>";
text: "If you rez an item twice, the first one will disappear.";
// Text size
size: 24;
size: 20;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
width: 180;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "GOT IT" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: step2text.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
width: 150;
height: 40;
text: "GOT IT";
onClicked: {
sendSignalToParent({method: 'tutorial_finished'});
}
}
}
//
// "STEP 2" END
//
Item {
id: tutorialActionButtonsContainer;
// Size
width: root.width;
height: 70;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
// "Skip" or "Back" button
HifiControlsUit.Button {
id: skipOrBackButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: root.activeView === "step_1" ? "Skip" : "Back";
onClicked: {
if (root.activeView === "step_1") {
sendSignalToParent({method: 'tutorial_skipClicked'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) - 1);
}
}
}
// "Next" or "Finish" button
HifiControlsUit.Button {
id: nextButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: root.activeView === "step_2" ? "Finish" : "Next";
onClicked: {
// If this is the final step...
if (root.activeView === "step_2") {
sendSignalToParent({method: 'tutorial_finished'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) + 1);
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'updatePurchases':
referrerURL = message.referrerURL;
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendSignalToParent(var message);
//
// FUNCTION DEFINITIONS END
//

View file

@ -34,18 +34,25 @@ Item {
property string itemId;
property string itemPreviewImageUrl;
property string itemHref;
property int ownedItemCount;
property string certificateId;
property int displayedItemCount;
property int itemEdition;
property int numberSold;
property int limitedRun;
property string originalStatusText;
property string originalStatusColor;
height: 110;
width: parent.width;
onPurchaseStatusChangedChanged: {
if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") {
root.originalStatusText = statusText.text;
root.originalStatusColor = statusText.color;
statusText.text = "CONFIRMED!";
statusText.color = hifi.colors.blueAccent;
confirmedTimer.start();
root.purchaseStatusChanged = false;
}
}
@ -53,7 +60,9 @@ Item {
id: confirmedTimer;
interval: 3000;
onTriggered: {
root.purchaseStatus = "";
statusText.text = root.originalStatusText;
statusText.color = root.originalStatusColor;
root.purchaseStatusChanged = false;
}
}
@ -160,7 +169,7 @@ Item {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemCertificateClicked', itemMarketplaceId: root.itemId});
sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
}
onEntered: {
certificateIcon.color = hifi.colors.black;
@ -174,9 +183,30 @@ Item {
}
Item {
id: statusContainer;
id: editionContainer;
visible: root.displayedItemCount > 1 && !statusContainer.visible;
anchors.left: itemName.left;
anchors.top: certificateContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.right: buttonContainer.left;
anchors.rightMargin: 2;
visible: root.purchaseStatus || root.ownedItemCount > 1;
FiraSansRegular {
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
width: paintedWidth;
text: "#" + root.itemEdition;
size: 15;
color: "#cc6a6a6a";
verticalAlignment: Text.AlignTop;
}
}
Item {
id: statusContainer;
visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged;
anchors.left: itemName.left;
anchors.top: certificateContainer.bottom;
anchors.topMargin: 8;
@ -195,8 +225,8 @@ Item {
"PENDING..."
} else if (root.purchaseStatus === "invalidated") {
"INVALIDATED"
} else if (root.ownedItemCount > 1) {
"<font color='#6a6a6a'>(#" + root.itemEdition + ")</font> <u>You own " + root.ownedItemCount + " others</u>"
} else if (root.numberSold !== -1) {
("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun))
} else {
""
}
@ -207,8 +237,6 @@ Item {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
@ -240,8 +268,6 @@ Item {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
@ -257,8 +283,6 @@ Item {
sendToPurchases({method: 'showPendingLightbox'});
} else if (root.purchaseStatus === "invalidated") {
sendToPurchases({method: 'showInvalidatedLightbox'});
} else if (root.ownedItemCount > 1) {
sendToPurchases({method: 'setFilterText', filterText: root.itemName});
}
}
onEntered: {
@ -268,9 +292,6 @@ Item {
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redAccent;
statusIcon.color = hifi.colors.redAccent;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueHighlight;
statusIcon.color = hifi.colors.blueHighlight;
}
}
onExited: {
@ -280,9 +301,6 @@ Item {
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redHighlight;
statusIcon.color = hifi.colors.redHighlight;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueAccent;
statusIcon.color = hifi.colors.blueAccent;
}
}
}

View file

@ -19,6 +19,7 @@ import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon
import "../inspectionCertificate" as HifiInspectionCertificate
// references XXX from root context
@ -31,65 +32,63 @@ Rectangle {
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
property bool canRezCertifiedItems: false;
property bool canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified;
property bool pendingInventoryReply: true;
property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false;
// Style
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
if (root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
}
} else if (walletStatus === 1) {
if (root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
}
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
}
} else if (walletStatus === 3) {
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
} else {
console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus);
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
commerce.account();
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
}
onSecurityImageResult: {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
sendToScript({method: 'purchases_getIsFirstUse'});
commerce.getWalletStatus();
}
}
onInventoryResult: {
purchasesReceived = true;
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
if (result.status !== 'success') {
console.log("Failed to get purchases", result.message);
} else {
var inventoryResult = processInventoryResult(result.data.assets);
purchasesModel.clear();
purchasesModel.append(result.data.assets);
purchasesModel.append(inventoryResult);
if (previousPurchasesModel.count !== 0) {
checkIfAnyItemStatusChanged();
@ -100,13 +99,9 @@ Rectangle {
purchasesModel.setProperty(i, "statusChanged", false);
}
}
previousPurchasesModel.append(result.data.assets);
previousPurchasesModel.append(inventoryResult);
buildFilteredPurchasesModel();
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
}
root.pendingInventoryReply = false;
@ -117,7 +112,20 @@ Rectangle {
id: notSetUpTimer;
interval: 200;
onTriggered: {
sendToScript({method: 'checkout_walletNotSetUp'});
sendToScript({method: 'purchases_walletNotSetUp'});
}
}
HifiInspectionCertificate.InspectionCertificate {
id: inspectionCertificate;
z: 999;
visible: false;
anchors.fill: parent;
Connections {
onSendToScript: {
sendToScript(message);
}
}
}
@ -165,6 +173,13 @@ Rectangle {
}
}
}
MouseArea {
enabled: titleBarContainer.usernameDropdownVisible;
anchors.fill: parent;
onClicked: {
titleBarContainer.usernameDropdownVisible = false;
}
}
//
// TITLE BAR END
//
@ -182,7 +197,7 @@ Rectangle {
Component.onCompleted: {
securityImageResultReceived = false;
purchasesReceived = false;
commerce.getLoginStatus();
commerce.getWalletStatus();
}
}
@ -218,7 +233,7 @@ Rectangle {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
root.activeView = "initialize";
sendToScript({method: 'purchases_getIsFirstUse'});
commerce.getWalletStatus();
} else {
sendToScript(msg);
}
@ -228,19 +243,16 @@ Rectangle {
FirstUseTutorial {
id: firstUseTutorial;
z: 999;
visible: root.activeView === "firstUseTutorial";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.fill: parent;
Connections {
onSendSignalToParent: {
switch (message.method) {
case 'tutorial_skipClicked':
case 'tutorial_finished':
sendToScript({method: 'purchases_setIsFirstUse'});
Settings.setValue("isFirstUseOfPurchases", false);
root.activeView = "purchasesMain";
commerce.inventory();
break;
@ -278,7 +290,7 @@ Rectangle {
anchors.topMargin: 4;
RalewayRegular {
id: myPurchasesText;
id: myText;
anchors.top: parent.top;
anchors.topMargin: 10;
anchors.bottom: parent.bottom;
@ -286,7 +298,7 @@ Rectangle {
anchors.left: parent.left;
anchors.leftMargin: 4;
width: paintedWidth;
text: "My Purchases";
text: isShowingMyItems ? "My Items" : "My Purchases";
color: hifi.colors.baseGray;
size: 28;
}
@ -296,7 +308,7 @@ Rectangle {
colorScheme: hifi.colorSchemes.faintGray;
hasClearButton: true;
hasRoundedBorder: true;
anchors.left: myPurchasesText.right;
anchors.left: myText.right;
anchors.leftMargin: 16;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
@ -331,7 +343,7 @@ Rectangle {
ListModel {
id: previousPurchasesModel;
}
ListModel {
HifiCommerceCommon.SortableListModel {
id: filteredPurchasesModel;
}
@ -400,7 +412,7 @@ Rectangle {
ListView {
id: purchasesContentsList;
visible: purchasesModel.count !== 0;
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
clip: true;
model: filteredPurchasesModel;
// Anchors
@ -414,9 +426,14 @@ Rectangle {
itemName: title;
itemId: id;
itemPreviewImageUrl: preview;
itemHref: root_file_url;
itemHref: download_url;
certificateId: certificate_id;
purchaseStatus: status;
purchaseStatusChanged: statusChanged;
itemEdition: model.edition_number;
numberSold: model.number_sold;
limitedRun: model.limited_run;
displayedItemCount: model.displayedItemCount;
anchors.topMargin: 12;
anchors.bottomMargin: 12;
@ -425,6 +442,8 @@ Rectangle {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
} else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
sendToScript(msg);
} else if (msg.method === "showInvalidatedLightbox") {
lightboxPopup.titleText = "Item Invalidated";
@ -448,9 +467,55 @@ Rectangle {
}
}
Item {
id: noItemsAlertContainer;
visible: !purchasesContentsList.visible && root.purchasesReceived && root.isShowingMyItems && filterBar.text === "";
anchors.top: filterBarContainer.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
// Explanitory text
RalewayRegular {
id: noItemsYet;
text: "<b>You haven't submitted anything to the Marketplace yet!</b><br><br>Submit an item to the Marketplace to add it to My Items.";
// Text size
size: 22;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 150;
anchors.left: parent.left;
anchors.leftMargin: 24;
anchors.right: parent.right;
anchors.rightMargin: 24;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
}
// "Go To Marketplace" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: noItemsYet.bottom;
anchors.topMargin: 20;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width * 2 / 3;
height: 50;
text: "Visit Marketplace";
onClicked: {
sendToScript({method: 'purchases_goToMarketplaceClicked'});
}
}
}
Item {
id: noPurchasesAlertContainer;
visible: !purchasesContentsList.visible && root.purchasesReceived;
visible: !purchasesContentsList.visible && root.purchasesReceived && !root.isShowingMyItems && filterBar.text === "";
anchors.top: filterBarContainer.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
@ -478,7 +543,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter;
}
// "Set Up" button
// "Go To Marketplace" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
@ -530,17 +595,54 @@ Rectangle {
// FUNCTION DEFINITIONS START
//
function processInventoryResult(inventory) {
for (var i = 0; i < inventory.length; i++) {
if (inventory[i].status.length > 1) {
console.log("WARNING: Inventory result index " + i + " has a status of length >1!")
}
inventory[i].status = inventory[i].status[0];
inventory[i].categories = inventory[i].categories.join(';');
}
return inventory;
}
function populateDisplayedItemCounts() {
var itemCountDictionary = {};
var currentItemId;
for (var i = 0; i < filteredPurchasesModel.count; i++) {
currentItemId = filteredPurchasesModel.get(i).id;
if (itemCountDictionary[currentItemId] === undefined) {
itemCountDictionary[currentItemId] = 1;
} else {
itemCountDictionary[currentItemId]++;
}
}
for (var i = 0; i < filteredPurchasesModel.count; i++) {
filteredPurchasesModel.setProperty(i, "displayedItemCount", itemCountDictionary[filteredPurchasesModel.get(i).id]);
}
}
function sortByDate() {
filteredPurchasesModel.sortColumnName = "purchase_date";
filteredPurchasesModel.isSortingDescending = false;
filteredPurchasesModel.quickSort();
}
function buildFilteredPurchasesModel() {
filteredPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed") {
if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
filteredPurchasesModel.insert(0, purchasesModel.get(i));
} else {
} else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === -1) || !root.isShowingMyItems) {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
}
populateDisplayedItemCounts();
sortByDate();
}
function checkIfAnyItemStatusChanged() {
@ -581,16 +683,13 @@ Rectangle {
case 'updatePurchases':
referrerURL = message.referrerURL;
titleBarContainer.referrerURL = message.referrerURL;
root.canRezCertifiedItems = message.canRezCertifiedItems;
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
case 'inspectionCertificate_setCertificateId':
inspectionCertificate.fromScript(message);
break;
case 'purchases_showMyItems':
root.isShowingMyItems = true;
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -30,12 +30,14 @@ Item {
id: commerce;
onKeyFilePathIfExistsResult: {
keyFilePath = path;
root.keyFilePath = path;
}
}
Component.onCompleted: {
commerce.getKeyFilePathIfExists();
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
RalewaySemiBold {
@ -103,7 +105,7 @@ Item {
ListElement {
isExpanded: false;
question: "What is a 'Security Pic'?"
answer: qsTr("Your Security Pic is an encrypted image that you selected during Wallet Setup. <b>It acts as an extra layer of Wallet security.</b><br><br>When you see your Security Pic, you know that your actions and data are securely making use of your private keys.<br><br><b>If you don't see your Security Pic on a page that is asking you for your Wallet passphrase, someone untrustworthy may be trying to gain access to your Wallet.</b><br><br>The Pic is stored on your hard drive inside the same file as your private keys.");
answer: qsTr("Your Security Pic is an encrypted image that you selected during Wallet Setup. <b>It acts as an extra layer of Wallet security.</b><br><br>When you see your Security Pic, you know that your actions and data are securely making use of your private keys.<br><br><b>If you don't see your Security Pic on a page that is asking you for your Wallet passphrase, someone untrustworthy may be trying to gain access to your Wallet.</b><br><br>The encrypted Pic is stored on your hard drive inside the same file as your private keys.");
}
ListElement {
isExpanded: false;

View file

@ -90,7 +90,7 @@ Item {
} else {
// Error submitting new passphrase
resetSubmitButton();
passphraseSelection.setErrorText("Backend error");
passphraseSelection.setErrorText("Current passphrase incorrect - try again");
}
} else {
sendSignalToWallet(msg);
@ -137,9 +137,10 @@ Item {
width: 150;
text: "Submit";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
passphraseSubmitButton.text = "Submitting...";
passphraseSubmitButton.enabled = false;
passphraseSubmitButton.text = "Submitting...";
passphraseSubmitButton.enabled = false;
if (!passphraseSelection.validateAndSubmitPassphrase()) {
resetSubmitButton();
}
}
}

View file

@ -129,6 +129,7 @@ Item {
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
MouseArea {
enabled: titleBarSecurityImage.visible;
@ -196,6 +197,8 @@ Item {
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
activeFocusOnPress: true;
activeFocusOnTab: true;
onFocusChanged: {
root.keyboardRaised = focus;
@ -205,8 +208,8 @@ Item {
anchors.fill: parent;
onClicked: {
parent.focus = true;
root.keyboardRaised = true;
mouse.accepted = false;
}
}

View file

@ -26,6 +26,7 @@ Item {
id: root;
property bool isChangingPassphrase: false;
property bool isShowingTip: false;
property bool shouldImmediatelyFocus: true;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
@ -42,8 +43,8 @@ Item {
passphrasePageSecurityImage.source = "image://security/securityImage";
}
onWalletAuthenticatedStatusResult: {
sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
onChangePassphraseStatusResult: {
sendMessageToLightbox({method: 'statusResult', status: changeSuccess});
}
}
@ -53,10 +54,8 @@ Item {
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
if (root.isChangingPassphrase) {
currentPassphraseField.focus = true;
} else {
passphraseField.focus = true;
if (root.shouldImmediatelyFocus) {
focusFirstTextField();
}
sendMessageToLightbox({method: 'disableHmdPreview'});
} else {
@ -76,6 +75,8 @@ Item {
height: 50;
echoMode: TextInput.Password;
placeholderText: "enter current passphrase";
activeFocusOnPress: true;
activeFocusOnTab: true;
onFocusChanged: {
if (focus) {
@ -87,9 +88,9 @@ Item {
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
onPressed: {
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
mouse.accepted = false;
}
}
@ -109,6 +110,16 @@ Item {
height: 50;
echoMode: TextInput.Password;
placeholderText: root.isShowingTip ? "" : "enter new passphrase";
activeFocusOnPress: true;
activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
mouse.accepted = false;
}
}
onFocusChanged: {
if (focus) {
@ -118,18 +129,11 @@ Item {
}
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard'});
}
}
onAccepted: {
passphraseFieldAgain.focus = true;
}
}
HifiControlsUit.TextField {
id: passphraseFieldAgain;
colorScheme: hifi.colorSchemes.dark;
@ -140,6 +144,16 @@ Item {
height: 50;
echoMode: TextInput.Password;
placeholderText: root.isShowingTip ? "" : "re-enter new passphrase";
activeFocusOnPress: true;
activeFocusOnTab: true;
MouseArea {
anchors.fill: parent;
onPressed: {
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
mouse.accepted = false;
}
}
onFocusChanged: {
if (focus) {
@ -149,14 +163,6 @@ Item {
}
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard'});
}
}
onAccepted: {
focus = false;
}
@ -304,7 +310,7 @@ Item {
passphraseFieldAgain.error = false;
currentPassphraseField.error = false;
setErrorText("");
commerce.setPassphrase(passphraseField.text);
commerce.changePassphrase(currentPassphraseField.text, passphraseField.text);
return true;
}
}
@ -320,5 +326,13 @@ Item {
setErrorText("");
}
function focusFirstTextField() {
if (root.isChangingPassphrase) {
currentPassphraseField.focus = true;
} else {
passphraseField.focus = true;
}
}
signal sendMessageToLightbox(var msg);
}

View file

@ -25,13 +25,13 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath: "";
property string keyFilePath;
Hifi.QmlCommerce {
id: commerce;
onKeyFilePathIfExistsResult: {
keyFilePath = path;
root.keyFilePath = path;
}
}
@ -232,6 +232,12 @@ Item {
anchors.rightMargin: 55;
anchors.bottom: parent.bottom;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
HiFiGlyphs {
id: yourPrivateKeysImage;
text: hifi.glyphs.walletKey;
@ -280,6 +286,34 @@ Item {
verticalAlignment: Text.AlignVCenter;
}
Rectangle {
id: removeHmdContainer;
z: 998;
visible: false;
color: hifi.colors.blueHighlight;
anchors.fill: backupInstructionsButton;
radius: 5;
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
RalewayBold {
anchors.fill: parent;
text: "INSTRUCTIONS OPEN ON DESKTOP";
size: 15;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignHCenter;
}
Timer {
id: removeHmdContainerTimer;
interval: 5000;
onTriggered: removeHmdContainer.visible = false
}
}
HifiControlsUit.Button {
id: backupInstructionsButton;
text: "View Backup Instructions";
@ -292,7 +326,11 @@ Item {
height: 40;
onClicked: {
Qt.openUrlExternally("https://www.highfidelity.com/");
var keyPath = "file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/'));
Qt.openUrlExternally(keyPath + "/backup_instructions.html");
Qt.openUrlExternally(keyPath);
removeHmdContainer.visible = true;
removeHmdContainerTimer.start();
}
}
}

View file

@ -65,6 +65,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
}
MouseArea {

View file

@ -38,48 +38,41 @@ Rectangle {
Hifi.QmlCommerce {
id: commerce;
onWalletStatusResult: {
if (walletStatus === 0) {
if (root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
}
} else if (walletStatus === 1) {
if (root.activeView !== "walletSetup") {
root.activeView = "walletSetup";
}
} else if (walletStatus === 2) {
if (root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
}
} else if (walletStatus === 3) {
root.activeView = "walletHome";
commerce.getSecurityImage();
} else {
console.log("ERROR in Wallet.qml: Unknown wallet status: " + walletStatus);
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
commerce.account();
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "walletSetup") {
root.activeView = "walletSetup";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
commerce.getWalletStatus();
}
}
onSecurityImageResult: {
if (!exists && root.activeView !== "walletSetup") { // "If security image is not set up"
root.activeView = "walletSetup";
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
if (exists) {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && passphraseModal && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
root.activeView = "walletHome";
}
}
}
SecurityImageModel {
@ -149,6 +142,7 @@ Rectangle {
anchors.bottomMargin: 6;
width: height;
mipmap: true;
cache: false;
MouseArea {
enabled: titleBarSecurityImage.visible;
@ -179,9 +173,11 @@ Rectangle {
if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') {
root.activeView = "initialize";
commerce.getLoginStatus();
commerce.getWalletStatus();
} else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'});
} else {
sendToScript({method: 'goToMarketplaceItemPage', itemId: msg.referrer});
}
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
@ -254,7 +250,7 @@ Rectangle {
color: hifi.colors.baseGray;
Component.onCompleted: {
commerce.getLoginStatus();
commerce.getWalletStatus();
}
}
@ -289,7 +285,7 @@ Rectangle {
Connections {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
root.activeView = "walletHome";
commerce.getWalletStatus();
} else {
sendToScript(msg);
}

View file

@ -26,6 +26,7 @@ Item {
id: root;
property bool historyReceived: false;
property int pendingCount: 0;
Hifi.QmlCommerce {
id: commerce;
@ -39,6 +40,8 @@ Item {
if (result.status === 'success') {
transactionHistoryModel.clear();
transactionHistoryModel.append(result.data.history);
calculatePendingAndInvalidated();
}
}
}
@ -200,55 +203,74 @@ Item {
model: transactionHistoryModel;
delegate: Item {
width: parent.width;
height: transactionText.height + 30;
height: (model.transaction_type === "pendingCount" && root.pendingCount !== 0) ? 40 : ((model.status === "confirmed" || model.status === "invalidated") ? transactionText.height + 30 : 0);
HifiControlsUit.Separator {
visible: index === 0;
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
AnonymousProRegular {
id: dateText;
text: getFormattedDate(model.created_at * 1000);
// Style
size: 18;
Item {
visible: model.transaction_type === "pendingCount" && root.pendingCount !== 0;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignRight;
}
width: parent.width;
height: visible ? parent.height : 0;
AnonymousProRegular {
id: transactionText;
text: model.text;
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: hifi.colors.baseGrayHighlight;
wrapMode: Text.WordWrap;
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
AnonymousProRegular {
id: pendingCountText;
anchors.fill: parent;
text: root.pendingCount + ' Transaction' + (root.pendingCount > 1 ? 's' : '') + ' Pending';
size: 18;
color: hifi.colors.blueAccent;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignHCenter;
}
}
HifiControlsUit.Separator {
colorScheme: 1;
Item {
visible: model.transaction_type !== "pendingCount" && (model.status === "confirmed" || model.status === "invalidated");
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
width: parent.width;
height: visible ? parent.height : 0;
AnonymousProRegular {
id: dateText;
text: model.created_at ? getFormattedDate(model.created_at * 1000) : "";
// Style
size: 18;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignRight;
}
AnonymousProRegular {
id: transactionText;
text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : "";
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
wrapMode: Text.WordWrap;
font.strikeout: model.status === "invalidated";
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
}
}
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
}
onAtYEndChanged: {
@ -277,10 +299,14 @@ Item {
//
function getFormattedDate(timestamp) {
function addLeadingZero(n) {
return n < 10 ? '0' + n : '' + n;
}
var a = new Date(timestamp);
var year = a.getFullYear();
var month = a.getMonth();
var day = a.getDate();
var month = addLeadingZero(a.getMonth());
var day = addLeadingZero(a.getDate());
var hour = a.getHours();
var drawnHour = hour;
if (hour === 0) {
@ -288,17 +314,31 @@ Item {
} else if (hour > 12) {
drawnHour -= 12;
}
drawnHour = addLeadingZero(drawnHour);
var amOrPm = "AM";
if (hour >= 12) {
amOrPm = "PM";
}
var min = a.getMinutes();
var sec = a.getSeconds();
var min = addLeadingZero(a.getMinutes());
var sec = addLeadingZero(a.getSeconds());
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
}
function calculatePendingAndInvalidated(startingPendingCount) {
var pendingCount = startingPendingCount ? startingPendingCount : 0;
for (var i = 0; i < transactionHistoryModel.count; i++) {
if (transactionHistoryModel.get(i).status === "pending") {
pendingCount++;
}
}
root.pendingCount = pendingCount;
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
}
//
// Function Name: fromScript()
//

View file

@ -30,6 +30,7 @@ Item {
property string lastPage;
property bool hasShownSecurityImageTip: false;
property string referrer;
property string keyFilePath;
Image {
anchors.fill: parent;
@ -43,7 +44,7 @@ Item {
if (!exists && root.lastPage === "step_2") {
// ERROR! Invalid security image.
root.activeView = "step_2";
} else {
} else if (exists) {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
@ -58,7 +59,7 @@ Item {
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
root.keyFilePath = path;
}
}
@ -116,7 +117,7 @@ Item {
Image {
id: titleBarSecurityImage;
source: "";
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "";
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "" && root.activeView !== "step_1" && root.activeView !== "step_2";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
@ -125,6 +126,7 @@ Item {
anchors.bottomMargin: 6;
width: height;
mipmap: true;
cache: false;
MouseArea {
enabled: titleBarSecurityImage.visible;
@ -422,6 +424,7 @@ Item {
onClicked: {
root.hasShownSecurityImageTip = true;
securityImageTip.visible = false;
passphraseSelection.focusFirstTextField();
}
}
}
@ -466,6 +469,7 @@ Item {
PassphraseSelection {
id: passphraseSelection;
shouldImmediatelyFocus: root.hasShownSecurityImageTip;
isShowingTip: securityImageTip.visible;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
@ -605,7 +609,7 @@ Item {
anchors.fill: parent;
RalewaySemiBold {
id: keyFilePathText;
id: keyFilePathHelperText;
text: "Private Key File Location:";
size: 18;
anchors.top: parent.top;
@ -624,7 +628,7 @@ Item {
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: keyFilePathText.bottom;
anchors.top: keyFilePathHelperText.bottom;
anchors.topMargin: 8;
height: 24;
width: height;
@ -640,11 +644,12 @@ Item {
}
onClicked: {
Qt.openUrlExternally("file:///" + keyFilePath.text.substring(0, keyFilePath.text.lastIndexOf('/')));
Qt.openUrlExternally("file:///" + keyFilePath.substring(0, keyFilePath.lastIndexOf('/')));
}
}
RalewayRegular {
id: keyFilePath;
id: keyFilePathText;
text: root.keyFilePath;
size: 18;
anchors.top: clipboardButton.top;
anchors.left: clipboardButton.right;
@ -667,7 +672,7 @@ Item {
id: openInstructionsButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: keyFilePath.bottom;
anchors.top: keyFilePathText.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
@ -679,7 +684,9 @@ Item {
instructions01Container.visible = false;
instructions02Container.visible = true;
keysReadyPageFinishButton.visible = true;
Qt.openUrlExternally("https://www.highfidelity.com/");
var keyPath = "file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/'));
Qt.openUrlExternally(keyPath + "/backup_instructions.html");
Qt.openUrlExternally(keyPath);
}
}
}

View file

@ -17,6 +17,7 @@ import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import ".."
Rectangle {
id: root
@ -57,6 +58,23 @@ Rectangle {
Component.onDestruction: {
assetMappingsModel.autoRefreshEnabled = false;
}
function letterbox(headerGlyph, headerText, message) {
letterboxMessage.headerGlyph = headerGlyph;
letterboxMessage.headerText = headerText;
letterboxMessage.text = message;
letterboxMessage.visible = true;
letterboxMessage.popupRadius = 0;
}
function errorMessageBox(message) {
return tabletRoot.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
}
function doDeleteFile(path) {
console.log("Deleting " + path);
@ -154,10 +172,7 @@ Rectangle {
}
function handleGetMappingsError(errorString) {
errorMessageBox(
"There was a problem retreiving the list of assets from your Asset Server.\n"
+ errorString
);
errorMessageBox("There was a problem retrieving the list of assets from your Asset Server.\n" + errorString);
}
function addToWorld() {
@ -448,14 +463,11 @@ Rectangle {
});
}
}
function errorMessageBox(message) {
return tabletRoot.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
// The letterbox used for popup messages
LetterboxMessage {
id: letterboxMessage;
z: 999; // Force the popup on top of everything else
}
Column {
@ -477,7 +489,7 @@ Rectangle {
HifiControls.Button {
text: "Add To World"
color: hifi.buttons.black
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: 120
@ -554,7 +566,7 @@ Rectangle {
case "Not Baked":
return hifi.glyphs.circleSlash;
case "Baked":
return hifi.glyphs.check_2_01;
return hifi.glyphs.checkmark;
case "Error":
return hifi.glyphs.alert;
default:
@ -583,8 +595,24 @@ Rectangle {
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
elide: Text.ElideRight
horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft
elide: Text.ElideMiddle
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
onEntered: {
if (parent.truncated) {
treeLabelToolTip.show(parent);
}
}
onExited: treeLabelToolTip.hide();
}
}
}
Component {
@ -667,6 +695,42 @@ Rectangle {
}
}
Rectangle {
id: treeLabelToolTip
visible: false
z: 100 // Render on top
width: toolTipText.width + 2 * hifi.dimensions.textPadding
height: hifi.dimensions.tableRowHeight
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
border.color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
FiraSansSemiBold {
id: toolTipText
anchors.centerIn: parent
size: hifi.fontSizes.tableText
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
}
Timer {
id: showTimer
interval: 1000
onTriggered: { treeLabelToolTip.visible = true; }
}
function show(item) {
var coord = item.mapToItem(parent, item.x, item.y);
toolTipText.text = item.text;
treeLabelToolTip.x = coord.x - hifi.dimensions.textPadding;
treeLabelToolTip.y = coord.y;
showTimer.start();
}
function hide() {
showTimer.stop();
treeLabelToolTip.visible = false;
}
}
MouseArea {
propagateComposedEvents: true
@ -716,25 +780,35 @@ Rectangle {
anchors.left: treeView.left
anchors.right: treeView.right
anchors.bottomMargin: hifi.dimensions.contentSpacing.y
spacing: hifi.dimensions.contentSpacing.x
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter
function makeText() {
var numPendingBakes = assetMappingsModel.numPendingBakes;
if (selectedItems > 1 || numPendingBakes === 0) {
return selectedItems + " items selected";
} else {
return numPendingBakes + " bakes pending"
}
}
size: hifi.fontSizes.sectionName
font.capitalization: Font.AllUppercase
text: selectedItems + " items selected"
text: makeText()
color: hifi.colors.lightGrayText
}
HifiControls.CheckBox {
function isChecked() {
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
var bakingDisabled = (status === "Not Baked" || status === "--");
return selectedItems === 1 && !bakingDisabled;
}
HifiControls.HorizontalSpacer { }
text: "Use baked (optimized) versions"
HifiControls.CheckBox {
id: bakingCheckbox
anchors.leftMargin: 2 * hifi.dimensions.contentSpacing.x
anchors.verticalCenter: parent.verticalCenter
text: " Use baked version"
colorScheme: root.colorScheme
enabled: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) !== "--"
enabled: isEnabled()
checked: isChecked()
onClicked: {
var mappings = [];
@ -750,7 +824,66 @@ Rectangle {
checked = Qt.binding(isChecked);
}
function isEnabled() {
if (!treeView.selection.hasSelection) {
return false;
}
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
if (status === "--") {
return false;
}
var bakingEnabled = status !== "Not Baked";
for (var i in treeView.selection.selectedIndexes) {
var thisStatus = assetProxyModel.data(treeView.selection.selectedIndexes[i], 0x105);
if (thisStatus === "--") {
return false;
}
var thisBakingEnalbed = (thisStatus !== "Not Baked");
if (bakingEnabled !== thisBakingEnalbed) {
return false;
}
}
return true;
}
function isChecked() {
if (!treeView.selection.hasSelection) {
return false;
}
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
return isEnabled() && status !== "Not Baked";
}
}
Item {
anchors.verticalCenter: parent.verticalCenter
width: infoGlyph.size;
height: infoGlyph.size;
HiFiGlyphs {
id: infoGlyph;
anchors.fill: parent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
text: hifi.glyphs.question;
size: 35;
color: hifi.colors.lightGrayText;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: infoGlyph.color = hifi.colors.blueHighlight;
onExited: infoGlyph.color = hifi.colors.lightGrayText;
onClicked: letterbox(hifi.glyphs.question,
"What is baking?",
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
}
}
}
HifiControls.TabletContentSection {

View file

@ -85,7 +85,7 @@ Item {
}
function urlHelper(src) {
if (src.match(/\bhttp/)) {
if (src.match(/\bhttp/) || src.match(/\bfile:/)) {
return src;
} else {
return "../../../" + src;
@ -171,7 +171,7 @@ Item {
PropertyChanges {
target: text
color: "#ffffff"
color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
text: tabletButton.hoverText
}
@ -197,7 +197,7 @@ Item {
PropertyChanges {
target: text
color: "#333333"
color: captionColorOverride !== "" ? captionColorOverride: "#333333"
text: tabletButton.activeText
}
@ -228,7 +228,7 @@ Item {
PropertyChanges {
target: text
color: "#333333"
color: captionColorOverride !== "" ? captionColorOverride: "#333333"
text: tabletButton.activeHoverText
}

View file

@ -34,7 +34,7 @@ StateImage {
}
function urlHelper(src) {
if (src.match(/\bhttp/)) {
if (src.match(/\bhttp/) || src.match(/\bfile:/)) {
return src;
} else {
return "../../../" + src;

View file

@ -26,6 +26,7 @@ Item {
readonly property int frameMarginRight: frame.decoration ? frame.decoration.frameMarginRight : 0
readonly property int frameMarginTop: frame.decoration ? frame.decoration.frameMarginTop : 0
readonly property int frameMarginBottom: frame.decoration ? frame.decoration.frameMarginBottom : 0
readonly property int offsetCorrection: 20
// Frames always fill their parents, but their decorations may extend
// beyond the window via negative margin sizes
@ -73,7 +74,7 @@ Item {
Rectangle {
id: sizeOutline
x: -frameMarginLeft
y: -frameMarginTop
y: -frameMarginTop - offsetCorrection
width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0
height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0
color: hifi.colors.baseGrayHighlight15

View file

@ -15,7 +15,7 @@ import "."
Rectangle {
id: modalWindow
layer.enabled: true
property var title: "Modal"
property var title: "Open"
width: tabletRoot.width
height: tabletRoot.height
color: "#80000000"

View file

@ -1,30 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
uniform sampler2D sampler;
struct OverlayData {
mat4 mvp;
float alpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
in vec2 vTexCoord;
out vec4 FragColor;
void main() {
FragColor = texture(sampler, vTexCoord);
FragColor.a *= overlay.alpha;
if (FragColor.a <= 0.0) {
discard;
}
}

View file

@ -1,28 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
struct OverlayData {
mat4 mvp;
float alpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
mat4 mvp = overlay.mvp;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = mvp * vec4(Position, 1);
vTexCoord = TexCoord;
}

File diff suppressed because it is too large Load diff

View file

@ -76,6 +76,7 @@
#include <procedural/ProceduralSkybox.h>
#include <model/Skybox.h>
#include <ModelScriptingInterface.h>
#include "FrameTimingsScriptingInterface.h"
#include "Sound.h"
@ -147,6 +148,8 @@ public:
void initializeGL();
void initializeUi();
void updateCamera(RenderArgs& renderArgs);
void paintGL();
void resizeGL();
@ -157,7 +160,7 @@ public:
QRect getRenderingGeometry() const;
glm::uvec2 getUiSize() const;
QRect getRecommendedOverlayRect() const;
QRect getRecommendedHUDRect() const;
QSize getDeviceSize() const;
bool hasFocus() const;
@ -173,7 +176,6 @@ public:
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
void copyShadowViewFrustum(ViewFrustum& viewOut) const override;
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
QUndoStack* getUndoStack() { return &_undoStack; }
@ -192,10 +194,9 @@ public:
Overlays& getOverlays() { return _overlays; }
size_t getFrameCount() const { return _frameCount; }
float getFps() const { return _frameCounter.rate(); }
float getTargetFrameRate() const; // frames/second
size_t getRenderFrameCount() const { return _renderFrameCount; }
float getRenderLoopRate() const { return _renderLoopCounter.rate(); }
float getTargetRenderFrameRate() const; // frames/second
float getFieldOfView() { return _fieldOfView.get(); }
void setFieldOfView(float fov);
@ -266,8 +267,7 @@ public:
void updateMyAvatarLookAtPosition();
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
void takeSecondaryCameraSnapshot();
@ -469,8 +469,6 @@ private:
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed);
int sendNackPackets();
void sendAvatarViewFrustum();
@ -480,7 +478,7 @@ private:
void initializeAcceptedFiles();
void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false);
void runRenderFrame(RenderArgs* renderArgs/*, Camera& whichCamera, bool selfAvatarOnly = false*/);
bool importJSONFromURL(const QString& urlString);
bool importSVOFromURL(const QString& urlString);
@ -531,12 +529,13 @@ private:
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;
uint32_t _frameCount { 0 };
uint32_t _renderFrameCount { 0 };
// Frame Rate Measurement
RateCounter<> _frameCounter;
RateCounter<> _avatarSimCounter;
RateCounter<> _simCounter;
RateCounter<500> _renderLoopCounter;
RateCounter<500> _gameLoopCounter;
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
QTimer _minimizedWindowTimer;
QElapsedTimer _timerStart;
@ -553,7 +552,6 @@ private:
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
ViewFrustum _displayViewFrustum;
ViewFrustum _shadowViewFrustum;
quint64 _lastQueriedTime;
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
@ -624,6 +622,24 @@ private:
render::EnginePointer _renderEngine{ new render::Engine() };
gpu::ContextPointer _gpuContext; // initialized during window creation
mutable QMutex _renderArgsMutex{ QMutex::Recursive };
struct AppRenderArgs {
render::Args _renderArgs;
glm::mat4 _eyeToWorld;
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);
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
OverlayConductor _overlayConductor;
@ -650,8 +666,6 @@ private:
Qt::CursorShape _desiredCursor{ Qt::BlankCursor };
bool _cursorNeedsChanging { false };
QThread* _deadlockWatchdogThread;
std::map<void*, std::function<void()>> _postUpdateLambdas;
std::mutex _postUpdateLambdasLock;

View file

@ -0,0 +1,230 @@
//
// 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 "ui/Stats.h"
#include <SceneScriptingInterface.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.
if (_aboutToQuit || _window->isMinimized()) {
return;
}
_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)) {
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;
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(HMDSensorPose);
// Reset the gpu::Context Stages
// Back to the default framebuffer;
gpu::doInBatch(_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
QSize size = getDeviceSize();
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
_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 handled 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) {
DependencyManager::get<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);
{
Stats::getInstance()->setRenderDetails(renderArgs._details);
}
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(); }
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;
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
renderDebugFlags = static_cast<RenderArgs::DebugFlags>(renderDebugFlags |
static_cast<int>(RenderArgs::RENDER_DEBUG_HULLS));
}
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 meoee 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

@ -78,7 +78,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
request->send();
} else {
++_errorCount;
qWarning() << "Count not create request for asset at" << migrationURL.toString();
qWarning() << "Could not create request for asset at" << migrationURL.toString();
}
};

View file

@ -252,11 +252,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
qApp->getMain3DScene()->enqueueTransaction(transaction);
}
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
_numAvatarsUpdated = numAvatarsUpdated;
_numAvatarsNotUpdated = numAVatarsNotUpdated;
simulateAvatarFades(deltaTime);
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
}
void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scene) {

View file

@ -73,9 +73,9 @@ public:
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
const QScriptValue& avatarIdsToInclude = QScriptValue(),
const QScriptValue& avatarIdsToDiscard = QScriptValue());
RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
const QVector<EntityItemID>& avatarsToInclude,
const QVector<EntityItemID>& avatarsToDiscard);
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
const QVector<EntityItemID>& avatarsToInclude,
const QVector<EntityItemID>& avatarsToDiscard);
// TODO: remove this HACK once we settle on optimal default sort coefficients
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);

View file

@ -13,7 +13,6 @@
#include <QJsonArray>
#include <QTimeZone>
#include <QJsonDocument>
#include "AccountManager.h"
#include "Wallet.h"
#include "Ledger.h"
#include "CommerceLogging.h"
@ -48,13 +47,13 @@ Handler(receiveAt)
Handler(balance)
Handler(inventory)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) {
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>();
const QString URL = "/api/v1/commerce/";
JSONCallbackParameters callbackParams(this, success, this, fail);
qCInfo(commerce) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact);
accountManager->sendRequest(URL + endpoint,
AccountManagerAuth::Required,
authType,
method,
callbackParams,
QJsonDocument(request).toJson());
@ -70,14 +69,14 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con
} else {
request["signature"] = QString("controlled failure!");
}
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, request);
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request);
}
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) {
auto wallet = DependencyManager::get<Wallet>();
QJsonObject request;
request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, request);
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request);
}
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) {
@ -164,11 +163,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
// be explicit
#ifdef Q_OS_MAC
QDateTime createdAt = QDateTime::fromTime_t(valueObject["created_at"].toInt(), Qt::UTC);
#else
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
#endif
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
valueObject["text"] = QString("%1 sent %2 %3 with message \"%4\"").
arg(from, to, coloredQuantityAndAssetTitle, valueObject["message"].toString());
@ -196,7 +191,7 @@ void Ledger::history(const QStringList& keys) {
void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); }
void Ledger::reset() {
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject());
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject());
}
void Ledger::accountSuccess(QNetworkReply& reply) {
@ -221,7 +216,7 @@ void Ledger::accountFailure(QNetworkReply& reply) {
failResponse("account", reply);
}
void Ledger::account() {
send("hfc_account", "accountSuccess", "accountFailure", QNetworkAccessManager::PutOperation, QJsonObject());
send("hfc_account", "accountSuccess", "accountFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject());
}
// The api/failResponse is called just for the side effect of logging.
@ -238,3 +233,28 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
}
void Ledger::certificateInfoSuccess(QNetworkReply& reply) {
auto wallet = DependencyManager::get<Wallet>();
auto accountManager = DependencyManager::get<AccountManager>();
QByteArray response = reply.readAll();
QJsonObject replyObject = QJsonDocument::fromJson(response).object();
QStringList keys = wallet->listPublicKeys();
if (keys.count() != 0) {
QJsonObject data = replyObject["data"].toObject();
if (data["transfer_recipient_key"].toString() == keys[0]) {
replyObject.insert("isMyCert", true);
}
}
qInfo(commerce) << "certificateInfo" << "response" << QJsonDocument(replyObject).toJson(QJsonDocument::Compact);
emit certificateInfoResult(replyObject);
}
void Ledger::certificateInfoFailure(QNetworkReply& reply) { failResponse("certificateInfo", reply); }
void Ledger::certificateInfo(const QString& certificateId) {
QString endpoint = "proof_of_purchase_status/transfer";
QJsonObject request;
request["certificate_id"] = certificateId;
send(endpoint, "certificateInfoSuccess", "certificateInfoFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request);
}

View file

@ -17,6 +17,7 @@
#include <QJsonObject>
#include <DependencyManager.h>
#include <QtNetwork/QNetworkReply>
#include "AccountManager.h"
class Ledger : public QObject, public Dependency {
@ -32,6 +33,7 @@ public:
void account();
void reset();
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
void certificateInfo(const QString& certificateId);
signals:
void buyResult(QJsonObject result);
@ -41,6 +43,7 @@ signals:
void historyResult(QJsonObject result);
void accountResult(QJsonObject result);
void locationUpdateResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
public slots:
void buySuccess(QNetworkReply& reply);
@ -59,11 +62,13 @@ public slots:
void accountFailure(QNetworkReply& reply);
void updateLocationSuccess(QNetworkReply& reply);
void updateLocationFailure(QNetworkReply& reply);
void certificateInfoSuccess(QNetworkReply& reply);
void certificateInfoFailure(QNetworkReply& reply);
private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
QJsonObject failResponse(const QString& label, QNetworkReply& reply);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail);
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false);
};

View file

@ -28,6 +28,13 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult);
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult);
connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult);
connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult);
}
void QmlCommerce::getWalletStatus() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getWalletStatus();
}
void QmlCommerce::getLoginStatus() {
@ -36,7 +43,7 @@ void QmlCommerce::getLoginStatus() {
void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get<Wallet>();
wallet->sendKeyFilePathIfExists();
emit keyFilePathIfExistsResult(wallet->getKeyFilePath());
}
void QmlCommerce::getWalletAuthenticatedStatus() {
@ -85,13 +92,20 @@ void QmlCommerce::history() {
ledger->history(wallet->listPublicKeys());
}
void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) {
auto wallet = DependencyManager::get<Wallet>();
if (wallet->getPassphrase()->isEmpty()) {
emit changePassphraseStatusResult(wallet->setPassphrase(newPassphrase));
} else if (wallet->getPassphrase() == oldPassphrase && !newPassphrase.isEmpty()) {
emit changePassphraseStatusResult(wallet->changePassphrase(newPassphrase));
} else {
emit changePassphraseStatusResult(false);
}
}
void QmlCommerce::setPassphrase(const QString& passphrase) {
auto wallet = DependencyManager::get<Wallet>();
if(wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty() && !passphrase.isEmpty()) {
wallet->changePassphrase(passphrase);
} else {
wallet->setPassphrase(passphrase);
}
wallet->setPassphrase(passphrase);
getWalletAuthenticatedStatus();
}
@ -112,3 +126,8 @@ void QmlCommerce::account() {
auto ledger = DependencyManager::get<Ledger>();
ledger->account();
}
void QmlCommerce::certificateInfo(const QString& certificateId) {
auto ledger = DependencyManager::get<Ledger>();
ledger->certificateInfo(certificateId);
}

View file

@ -28,10 +28,13 @@ public:
QmlCommerce(QQuickItem* parent = nullptr);
signals:
void walletStatusResult(uint walletStatus);
void loginStatusResult(bool isLoggedIn);
void keyFilePathIfExistsResult(const QString& path);
void securityImageResult(bool exists);
void walletAuthenticatedStatusResult(bool isAuthenticated);
void changePassphraseStatusResult(bool changeSuccess);
void buyResult(QJsonObject result);
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
@ -40,8 +43,11 @@ signals:
void inventoryResult(QJsonObject result);
void historyResult(QJsonObject result);
void accountResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
protected:
Q_INVOKABLE void getWalletStatus();
Q_INVOKABLE void getLoginStatus();
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void getSecurityImage();
@ -49,6 +55,7 @@ protected:
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void changePassphrase(const QString& oldPassphrase, const QString& newPassphrase);
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
Q_INVOKABLE void balance();
@ -57,6 +64,8 @@ protected:
Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset();
Q_INVOKABLE void account();
Q_INVOKABLE void certificateInfo(const QString& certificateId);
};
#endif // hifi_QmlCommerce_h

View file

@ -41,6 +41,7 @@
#endif
static const char* KEY_FILE = "hifikey";
static const char* INSTRUCTIONS_FILE = "backup_instructions.html";
static const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
static const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n";
@ -104,6 +105,38 @@ RSA* readKeys(const char* filename) {
return key;
}
bool writeBackupInstructions() {
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
QString filename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
QFile outputFile(filename);
bool retval = false;
if (QFile::exists(filename))
{
QFile::remove(filename);
}
QFile::copy(inputFilename, filename);
if (QFile::exists(filename) && outputFile.open(QIODevice::ReadWrite)) {
QByteArray fileData = outputFile.readAll();
QString text(fileData);
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
outputFile.seek(0); // go to the beginning of the file
outputFile.write(text.toUtf8()); // write the new text back to the file
outputFile.close(); // close the file handle.
retval = true;
qCDebug(commerce) << "wrote html file successfully";
} else {
qCDebug(commerce) << "failed to open output html file" << filename;
}
return retval;
}
bool writeKeys(const char* filename, RSA* keys) {
FILE* fp;
bool retval = false;
@ -121,6 +154,8 @@ bool writeKeys(const char* filename, RSA* keys) {
QFile(QString(filename)).remove();
return retval;
}
writeBackupInstructions();
retval = true;
qCDebug(commerce) << "wrote keys successfully";
@ -282,9 +317,32 @@ void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArra
Wallet::Wallet() {
auto nodeList = DependencyManager::get<NodeList>();
auto ledger = DependencyManager::get<Ledger>();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "verifyOwnerChallenge");
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
connect(ledger.data(), &Ledger::accountResult, this, [&]() {
auto wallet = DependencyManager::get<Wallet>();
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
uint status;
if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) {
status = (uint)WalletStatus::WALLET_STATUS_NOT_SET_UP;
} else if (!wallet->walletIsAuthenticatedWithPassphrase()) {
status = (uint)WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED;
} else {
status = (uint)WalletStatus::WALLET_STATUS_READY;
}
walletScriptingInterface->setWalletStatus(status);
emit walletStatusResult(status);
});
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
getWalletStatus();
});
}
Wallet::~Wallet() {
@ -293,13 +351,15 @@ Wallet::~Wallet() {
}
}
void Wallet::setPassphrase(const QString& passphrase) {
bool Wallet::setPassphrase(const QString& passphrase) {
if (_passphrase) {
delete _passphrase;
}
_passphrase = new QString(passphrase);
_publicKeys.clear();
return true;
}
bool Wallet::writeSecurityImage(const QPixmap* pixmap, const QString& outputFilePath) {
@ -468,7 +528,6 @@ bool Wallet::generateKeyPair() {
// TODO: redo this soon -- need error checking and so on
writeSecurityImage(_securityImage, keyFilePath());
sendKeyFilePathIfExists();
QString oldKey = _publicKeys.count() == 0 ? "" : _publicKeys.last();
QString key = keyPair.first->toBase64();
_publicKeys.push_back(key);
@ -559,14 +618,14 @@ void Wallet::chooseSecurityImage(const QString& filename) {
emit securityImageResult(success);
}
void Wallet::getSecurityImage() {
bool Wallet::getSecurityImage() {
unsigned char* data;
int dataLen;
// if already decrypted, don't do it again
if (_securityImage) {
emit securityImageResult(true);
return;
return true;
}
bool success = false;
@ -585,14 +644,15 @@ void Wallet::getSecurityImage() {
success = true;
}
emit securityImageResult(success);
return success;
}
void Wallet::sendKeyFilePathIfExists() {
QString Wallet::getKeyFilePath() {
QString filePath(keyFilePath());
QFileInfo fileInfo(filePath);
if (fileInfo.exists()) {
emit keyFilePathIfExistsResult(filePath);
return filePath;
} else {
emit keyFilePathIfExistsResult("");
return "";
}
}
@ -625,6 +685,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) {
QFile(QString(keyFilePath())).remove();
QFile(tempFileName).rename(QString(keyFilePath()));
qCDebug(commerce) << "wallet written successfully";
emit keyFilePathIfExistsResult(getKeyFilePath());
return true;
} else {
qCDebug(commerce) << "couldn't write security image to temp wallet";
@ -679,3 +740,23 @@ bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString
decryptedText = QString("hello");
return true;
}
void Wallet::account() {
auto ledger = DependencyManager::get<Ledger>();
ledger->account();
}
void Wallet::getWalletStatus() {
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
uint status;
if (DependencyManager::get<AccountManager>()->isLoggedIn()) {
// This will set account info for the wallet, allowing us to decrypt and display the security image.
account();
} else {
status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN;
emit walletStatusResult(status);
walletScriptingInterface->setWalletStatus(status);
return;
}
}

View file

@ -17,6 +17,7 @@
#include <DependencyManager.h>
#include <Node.h>
#include <ReceivedMessage.h>
#include "scripting/WalletScriptingInterface.h"
#include <QPixmap>
@ -32,8 +33,8 @@ public:
QStringList listPublicKeys();
QString signWithKey(const QByteArray& text, const QString& key);
void chooseSecurityImage(const QString& imageFile);
void getSecurityImage();
void sendKeyFilePathIfExists();
bool getSecurityImage();
QString getKeyFilePath();
void setSalt(const QByteArray& salt) { _salt = salt; }
QByteArray getSalt() { return _salt; }
@ -42,7 +43,7 @@ public:
void setCKey(const QByteArray& ckey) { _ckey = ckey; }
QByteArray getCKey() { return _ckey; }
void setPassphrase(const QString& passphrase);
bool setPassphrase(const QString& passphrase);
QString* getPassphrase() { return _passphrase; }
bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
bool walletIsAuthenticatedWithPassphrase();
@ -50,10 +51,20 @@ public:
void reset();
void getWalletStatus();
enum WalletStatus {
WALLET_STATUS_NOT_LOGGED_IN = 0,
WALLET_STATUS_NOT_SET_UP,
WALLET_STATUS_NOT_AUTHENTICATED,
WALLET_STATUS_READY
};
signals:
void securityImageResult(bool exists);
void keyFilePathIfExistsResult(const QString& path);
void walletStatusResult(uint walletStatus);
private slots:
void handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
@ -71,6 +82,8 @@ private:
bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen);
bool verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText);
void account();
};
#endif // hifi_Wallet_h

View file

@ -49,6 +49,10 @@ int main(int argc, const char* argv[]) {
CrashReporter crashReporter { BUG_SPLAT_DATABASE, BUG_SPLAT_APPLICATION_NAME, BuildInfo::VERSION };
#endif
#ifdef Q_OS_LINUX
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
#endif
disableQtBearerPoll(); // Fixes wifi ping spikes
QElapsedTimer startupTime;

View file

@ -49,11 +49,13 @@ LaserPointer::~LaserPointer() {
}
void LaserPointer::enable() {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->enableRayPick(_rayPickUID);
_renderingEnabled = true;
}
void LaserPointer::disable() {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->disableRayPick(_rayPickUID);
_renderingEnabled = false;
if (!_currentRenderState.empty()) {
@ -67,6 +69,7 @@ void LaserPointer::disable() {
}
void LaserPointer::setRenderState(const std::string& state) {
QWriteLocker lock(getLock());
if (!_currentRenderState.empty() && state != _currentRenderState) {
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
disableRenderState(_renderStates[_currentRenderState]);
@ -79,6 +82,7 @@ void LaserPointer::setRenderState(const std::string& state) {
}
void LaserPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) {
QWriteLocker lock(getLock());
updateRenderStateOverlay(_renderStates[state].getStartID(), startProps);
updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps);
updateRenderStateOverlay(_renderStates[state].getEndID(), endProps);
@ -92,6 +96,11 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant&
}
}
const RayPickResult LaserPointer::getPrevRayPickResult() {
QReadLocker lock(getLock());
return DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID);
}
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) {
if (!renderState.getStartID().isNull()) {
QVariantMap startProps;
@ -183,6 +192,8 @@ void LaserPointer::disableRenderState(const RenderState& renderState) {
}
void LaserPointer::update() {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
QReadLocker lock(getLock());
RayPickResult prevRayPickResult = DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID);
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
(prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
@ -198,6 +209,51 @@ void LaserPointer::update() {
}
}
void LaserPointer::setPrecisionPicking(const bool precisionPicking) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setPrecisionPicking(_rayPickUID, precisionPicking);
}
void LaserPointer::setLaserLength(const float laserLength) {
QWriteLocker lock(getLock());
_laserLength = laserLength;
}
void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) {
QWriteLocker lock(getLock());
_objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay);
}
void LaserPointer::setIgnoreEntities(const QScriptValue& ignoreEntities) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities);
}
void LaserPointer::setIncludeEntities(const QScriptValue& includeEntities) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities);
}
void LaserPointer::setIgnoreOverlays(const QScriptValue& ignoreOverlays) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays);
}
void LaserPointer::setIncludeOverlays(const QScriptValue& includeOverlays) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIncludeOverlays(_rayPickUID, includeOverlays);
}
void LaserPointer::setIgnoreAvatars(const QScriptValue& ignoreAvatars) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIgnoreAvatars(_rayPickUID, ignoreAvatars);
}
void LaserPointer::setIncludeAvatars(const QScriptValue& includeAvatars) {
QWriteLocker lock(getLock());
DependencyManager::get<RayPickScriptingInterface>()->setIncludeAvatars(_rayPickUID, includeAvatars);
}
RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) :
_startID(startID), _pathID(pathID), _endID(endID)
{

View file

@ -58,22 +58,24 @@ public:
QUuid getRayUID() { return _rayPickUID; }
void enable();
void disable();
const RayPickResult getPrevRayPickResult() { return DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID); }
const RayPickResult getPrevRayPickResult();
void setRenderState(const std::string& state);
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
void setPrecisionPicking(const bool precisionPicking) { DependencyManager::get<RayPickScriptingInterface>()->setPrecisionPicking(_rayPickUID, precisionPicking); }
void setLaserLength(const float laserLength) { _laserLength = laserLength; }
void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities); }
void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities); }
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); }
void setIncludeOverlays(const QScriptValue& includeOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeOverlays(_rayPickUID, includeOverlays); }
void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreAvatars(_rayPickUID, ignoreAvatars); }
void setIncludeAvatars(const QScriptValue& includeAvatars) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeAvatars(_rayPickUID, includeAvatars); }
void setPrecisionPicking(const bool precisionPicking);
void setLaserLength(const float laserLength);
void setLockEndUUID(QUuid objectID, const bool isOverlay);
void setLockEndUUID(QUuid objectID, const bool isOverlay) { _objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay); }
void setIgnoreEntities(const QScriptValue& ignoreEntities);
void setIncludeEntities(const QScriptValue& includeEntities);
void setIgnoreOverlays(const QScriptValue& ignoreOverlays);
void setIncludeOverlays(const QScriptValue& includeOverlays);
void setIgnoreAvatars(const QScriptValue& ignoreAvatars);
void setIncludeAvatars(const QScriptValue& includeAvatars);
QReadWriteLock* getLock() { return &_lock; }
void update();
@ -89,6 +91,7 @@ private:
std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(QUuid(), false)};
QUuid _rayPickUID;
QReadWriteLock _lock;
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState);

View file

@ -17,7 +17,6 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const La
QWriteLocker containsLock(&_containsLock);
QUuid id = QUuid::createUuid();
_laserPointers[id] = laserPointer;
_laserPointerLocks[id] = std::make_shared<QReadWriteLock>();
return id;
}
return QUuid();
@ -26,46 +25,45 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const La
void LaserPointerManager::removeLaserPointer(const QUuid uid) {
QWriteLocker lock(&_containsLock);
_laserPointers.remove(uid);
_laserPointerLocks.remove(uid);
}
void LaserPointerManager::enableLaserPointer(const QUuid uid) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->enable();
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->enable();
}
}
void LaserPointerManager::disableLaserPointer(const QUuid uid) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->disable();
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->disable();
}
}
void LaserPointerManager::setRenderState(QUuid uid, const std::string& renderState) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setRenderState(renderState);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setRenderState(renderState);
}
}
void LaserPointerManager::editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->editRenderState(state, startProps, pathProps, endProps);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->editRenderState(state, startProps, pathProps, endProps);
}
}
const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QReadLocker laserLock(_laserPointerLocks[uid].get());
return _laserPointers[uid]->getPrevRayPickResult();
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
return laserPointer.value()->getPrevRayPickResult();
}
return RayPickResult();
}
@ -73,80 +71,79 @@ const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) {
void LaserPointerManager::update() {
QReadLocker lock(&_containsLock);
for (QUuid& uid : _laserPointers.keys()) {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
QReadLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->update();
auto laserPointer = _laserPointers.find(uid);
laserPointer.value()->update();
}
}
void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setPrecisionPicking(precisionPicking);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setPrecisionPicking(precisionPicking);
}
}
void LaserPointerManager::setLaserLength(QUuid uid, const float laserLength) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setLaserLength(laserLength);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setLaserLength(laserLength);
}
}
void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIgnoreEntities(ignoreEntities);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIgnoreEntities(ignoreEntities);
}
}
void LaserPointerManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIncludeEntities(includeEntities);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIncludeEntities(includeEntities);
}
}
void LaserPointerManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIgnoreOverlays(ignoreOverlays);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIgnoreOverlays(ignoreOverlays);
}
}
void LaserPointerManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIncludeOverlays(includeOverlays);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIncludeOverlays(includeOverlays);
}
}
void LaserPointerManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIgnoreAvatars(ignoreAvatars);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIgnoreAvatars(ignoreAvatars);
}
}
void LaserPointerManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setIncludeAvatars(includeAvatars);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setIncludeAvatars(includeAvatars);
}
}
void LaserPointerManager::setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setLockEndUUID(objectID, isOverlay);
auto laserPointer = _laserPointers.find(uid);
if (laserPointer != _laserPointers.end()) {
laserPointer.value()->setLockEndUUID(objectID, isOverlay);
}
}

View file

@ -13,7 +13,6 @@
#include <memory>
#include <glm/glm.hpp>
#include <QReadWriteLock>
#include "LaserPointer.h"
@ -46,7 +45,6 @@ public:
private:
QHash<QUuid, std::shared_ptr<LaserPointer>> _laserPointers;
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _laserPointerLocks;
QReadWriteLock _containsLock;
};

View file

@ -16,3 +16,47 @@ RayPick::RayPick(const RayPickFilter& filter, const float maxDistance, const boo
_enabled(enabled)
{
}
void RayPick::enable() {
QWriteLocker lock(getLock());
_enabled = true;
}
void RayPick::disable() {
QWriteLocker lock(getLock());
_enabled = false;
}
const RayPickResult& RayPick::getPrevRayPickResult() {
QReadLocker lock(getLock());
return _prevResult;
}
void RayPick::setIgnoreEntities(const QScriptValue& ignoreEntities) {
QWriteLocker lock(getLock());
_ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities);
}
void RayPick::setIncludeEntities(const QScriptValue& includeEntities) {
QWriteLocker lock(getLock());
_includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities);
}
void RayPick::setIgnoreOverlays(const QScriptValue& ignoreOverlays) {
QWriteLocker lock(getLock());
_ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays);
}
void RayPick::setIncludeOverlays(const QScriptValue& includeOverlays) {
QWriteLocker lock(getLock());
_includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays);
}
void RayPick::setIgnoreAvatars(const QScriptValue& ignoreAvatars) {
QWriteLocker lock(getLock());
_ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars);
}
void RayPick::setIncludeAvatars(const QScriptValue& includeAvatars) {
QWriteLocker lock(getLock());
_includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars);
}

View file

@ -16,6 +16,7 @@
#include "EntityItemID.h"
#include "ui/overlays/Overlay.h"
#include <QReadWriteLock>
class RayPickFilter {
public:
@ -102,13 +103,13 @@ public:
virtual const PickRay getPickRay(bool& valid) const = 0;
void enable() { _enabled = true; }
void disable() { _enabled = false; }
void enable();
void disable();
const RayPickFilter& getFilter() { return _filter; }
float getMaxDistance() { return _maxDistance; }
bool isEnabled() { return _enabled; }
const RayPickResult& getPrevRayPickResult() { return _prevResult; }
const RayPickResult& getPrevRayPickResult();
void setPrecisionPicking(bool precisionPicking) { _filter.setFlag(RayPickFilter::PICK_COURSE, !precisionPicking); }
@ -120,12 +121,14 @@ public:
const QVector<OverlayID>& getIncludeOverlays() { return _includeOverlays; }
const QVector<EntityItemID>& getIgnoreAvatars() { return _ignoreAvatars; }
const QVector<EntityItemID>& getIncludeAvatars() { return _includeAvatars; }
void setIgnoreEntities(const QScriptValue& ignoreEntities) { _ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities); }
void setIncludeEntities(const QScriptValue& includeEntities) { _includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities); }
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { _ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays); }
void setIncludeOverlays(const QScriptValue& includeOverlays) { _includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays); }
void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { _ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars); }
void setIncludeAvatars(const QScriptValue& includeAvatars) { _includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars); }
void setIgnoreEntities(const QScriptValue& ignoreEntities);
void setIncludeEntities(const QScriptValue& includeEntities);
void setIgnoreOverlays(const QScriptValue& ignoreOverlays);
void setIncludeOverlays(const QScriptValue& includeOverlays);
void setIgnoreAvatars(const QScriptValue& ignoreAvatars);
void setIncludeAvatars(const QScriptValue& includeAvatars);
QReadWriteLock* getLock() { return &_lock; }
private:
RayPickFilter _filter;
@ -139,6 +142,8 @@ private:
QVector<OverlayID> _includeOverlays;
QVector<EntityItemID> _ignoreAvatars;
QVector<EntityItemID> _includeAvatars;
QReadWriteLock _lock;
};
#endif // hifi_RayPick_h

View file

@ -47,6 +47,7 @@ void RayPickManager::update() {
RayPickCache results;
for (auto& uid : _rayPicks.keys()) {
std::shared_ptr<RayPick> rayPick = _rayPicks[uid];
QWriteLocker lock(rayPick->getLock());
if (!rayPick->isEnabled() || rayPick->getFilter().doesPickNothing() || rayPick->getMaxDistance() < 0.0f) {
continue;
}
@ -114,7 +115,6 @@ void RayPickManager::update() {
}
}
QWriteLocker lock(_rayPickLocks[uid].get());
if (rayPick->getMaxDistance() == 0.0f || (rayPick->getMaxDistance() > 0.0f && res.distance < rayPick->getMaxDistance())) {
rayPick->setRayPickResult(res);
} else {
@ -127,7 +127,6 @@ QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec
QWriteLocker lock(&_containsLock);
QUuid id = QUuid::createUuid();
_rayPicks[id] = std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled);
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
return id;
}
@ -135,7 +134,6 @@ QUuid RayPickManager::createRayPick(const RayPickFilter& filter, const float max
QWriteLocker lock(&_containsLock);
QUuid id = QUuid::createUuid();
_rayPicks[id] = std::make_shared<MouseRayPick>(filter, maxDistance, enabled);
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
return id;
}
@ -143,93 +141,91 @@ QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3&
QWriteLocker lock(&_containsLock);
QUuid id = QUuid::createUuid();
_rayPicks[id] = std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled);
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
return id;
}
void RayPickManager::removeRayPick(const QUuid uid) {
QWriteLocker lock(&_containsLock);
_rayPicks.remove(uid);
_rayPickLocks.remove(uid);
}
void RayPickManager::enableRayPick(const QUuid uid) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker rayPickLock(_rayPickLocks[uid].get());
_rayPicks[uid]->enable();
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->enable();
}
}
void RayPickManager::disableRayPick(const QUuid uid) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker rayPickLock(_rayPickLocks[uid].get());
_rayPicks[uid]->disable();
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->disable();
}
}
const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QReadLocker lock(_rayPickLocks[uid].get());
return _rayPicks[uid]->getPrevRayPickResult();
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
return rayPick.value()->getPrevRayPickResult();
}
return RayPickResult();
}
void RayPickManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setPrecisionPicking(precisionPicking);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setPrecisionPicking(precisionPicking);
}
}
void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIgnoreEntities(ignoreEntities);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIgnoreEntities(ignoreEntities);
}
}
void RayPickManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIncludeEntities(includeEntities);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIncludeEntities(includeEntities);
}
}
void RayPickManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIgnoreOverlays(ignoreOverlays);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIgnoreOverlays(ignoreOverlays);
}
}
void RayPickManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIncludeOverlays(includeOverlays);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIncludeOverlays(includeOverlays);
}
}
void RayPickManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIgnoreAvatars(ignoreAvatars);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIgnoreAvatars(ignoreAvatars);
}
}
void RayPickManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setIncludeAvatars(includeAvatars);
auto rayPick = _rayPicks.find(uid);
if (rayPick != _rayPicks.end()) {
rayPick.value()->setIncludeAvatars(includeAvatars);
}
}

View file

@ -15,7 +15,6 @@
#include <memory>
#include <QtCore/QObject>
#include <QReadWriteLock>
#include "RegisteredMetaTypes.h"
@ -47,7 +46,6 @@ public:
private:
QHash<QUuid, std::shared_ptr<RayPick>> _rayPicks;
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _rayPickLocks;
QReadWriteLock _containsLock;
typedef QHash<QPair<glm::vec3, glm::vec3>, std::unordered_map<RayPickFilter::Flags, RayPickResult>> RayPickCache;

View file

@ -21,6 +21,7 @@
#include <NetworkLogging.h>
#include <NodeList.h>
#include <OffscreenUi.h>
#include <UserActivityLogger.h>
static const int AUTO_REFRESH_INTERVAL = 1000;
@ -104,6 +105,21 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
startedCallback.call();
QFileInfo fileInfo { path };
int64_t size { fileInfo.size() };
QString extension = "";
auto idx = path.lastIndexOf(".");
if (idx >= 0) {
extension = path.mid(idx + 1);
}
UserActivityLogger::getInstance().logAction("uploading_asset", {
{ "size", (qint64)size },
{ "mapping", mapping },
{ "extension", extension}
});
auto upload = DependencyManager::get<AssetClient>()->createUpload(path);
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
if (upload->getError() != AssetUpload::NoError) {
@ -239,6 +255,7 @@ void AssetMappingModel::refresh() {
connect(request, &GetAllMappingsRequest::finished, this, [this](GetAllMappingsRequest* request) mutable {
if (request->getError() == MappingRequest::NoError) {
int numPendingBakes = 0;
auto mappings = request->getMappings();
auto existingPaths = _pathToItemMap.keys();
for (auto& mapping : mappings) {
@ -287,6 +304,9 @@ void AssetMappingModel::refresh() {
auto statusString = isFolder ? "--" : bakingStatusToString(mapping.second.status);
lastItem->setData(statusString, Qt::UserRole + 5);
lastItem->setData(mapping.second.bakingErrors, Qt::UserRole + 6);
if (mapping.second.status == Pending) {
++numPendingBakes;
}
}
Q_ASSERT(fullPath == path);
@ -334,6 +354,11 @@ void AssetMappingModel::refresh() {
item = nextItem;
}
}
if (numPendingBakes != _numPendingBakes) {
_numPendingBakes = numPendingBakes;
emit numPendingBakesChanged(_numPendingBakes);
}
} else {
emit errorGettingMappings(request->getErrorString());
}
@ -364,4 +389,4 @@ void AssetMappingModel::setupRoles() {
roleNames[Qt::DisplayRole] = "name";
roleNames[Qt::UserRole + 5] = "baked";
setItemRoleNames(roleNames);
}
}

View file

@ -26,6 +26,7 @@
class AssetMappingModel : public QStandardItemModel {
Q_OBJECT
Q_PROPERTY(bool autoRefreshEnabled READ isAutoRefreshEnabled WRITE setAutoRefreshEnabled)
Q_PROPERTY(int numPendingBakes READ getNumPendingBakes NOTIFY numPendingBakesChanged)
public:
AssetMappingModel();
@ -38,10 +39,13 @@ public:
bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); }
bool isKnownFolder(QString path) const;
int getNumPendingBakes() const { return _numPendingBakes; }
public slots:
void clear();
signals:
void numPendingBakesChanged(int newCount);
void errorGettingMappings(QString errorString);
void updated();
@ -50,6 +54,7 @@ private:
QHash<QString, QStandardItem*> _pathToItemMap;
QTimer _autoRefreshTimer;
int _numPendingBakes{ 0 };
};
Q_DECLARE_METATYPE(AssetMappingModel*)

View file

@ -13,23 +13,10 @@
#include <avatar/AvatarManager.h>
#include <avatar/MyAvatar.h>
#include <HFBackEvent.h>
#include <plugins/PluginManager.h>
#include "Application.h"
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
if (event->type() == HFActionEvent::startType()) {
emit actionStartEvent(static_cast<HFActionEvent&>(*event));
} else if (event->type() == HFActionEvent::endType()) {
emit actionEndEvent(static_cast<HFActionEvent&>(*event));
} else if (event->type() == HFBackEvent::startType()) {
emit backStartEvent();
} else if (event->type() == HFBackEvent::endType()) {
emit backEndEvent();
}
}
bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
return isKeyCaptured(KeyEvent(*event));
}
@ -91,8 +78,8 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
return qApp->getUiSize();
}
QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
auto rect = qApp->getRecommendedOverlayRect();
QVariant ControllerScriptingInterface::getRecommendedHUDRect() const {
auto rect = qApp->getRecommendedHUDRect();
return qRectToVariant(rect);
}

View file

@ -17,7 +17,6 @@
#include <controllers/UserInputMapper.h>
#include <controllers/ScriptingInterface.h>
#include <HFActionEvent.h>
#include <KeyEvent.h>
#include <MouseEvent.h>
#include <SpatialEvent.h>
@ -36,8 +35,6 @@ public:
void emitKeyPressEvent(QKeyEvent* event);
void emitKeyReleaseEvent(QKeyEvent* event);
void handleMetaEvent(HFMetaEvent* event);
void emitMouseMoveEvent(QMouseEvent* event);
void emitMousePressEvent(QMouseEvent* event);
void emitMouseDoublePressEvent(QMouseEvent* event);
@ -66,18 +63,12 @@ public slots:
virtual void releaseEntityClickEvents();
virtual glm::vec2 getViewportDimensions() const;
virtual QVariant getRecommendedOverlayRect() const;
virtual QVariant getRecommendedHUDRect() const;
signals:
void keyPressEvent(const KeyEvent& event);
void keyReleaseEvent(const KeyEvent& event);
void actionStartEvent(const HFActionEvent& event);
void actionEndEvent(const HFActionEvent& event);
void backStartEvent();
void backEndEvent();
void mouseMoveEvent(const MouseEvent& event);
void mousePressEvent(const MouseEvent& event);
void mouseDoublePressEvent(const MouseEvent& event);

View file

@ -29,7 +29,7 @@ int DesktopScriptingInterface::getHeight() {
return size.height();
}
void DesktopScriptingInterface::setOverlayAlpha(float alpha) {
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
qApp->getApplicationCompositor().setAlpha(alpha);
}

View file

@ -22,7 +22,7 @@ class DesktopScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(int height READ getHeight) // Physical height of screen(s) including task bars and system menus
public:
Q_INVOKABLE void setOverlayAlpha(float alpha);
Q_INVOKABLE void setHUDAlpha(float alpha);
Q_INVOKABLE void show(const QString& path, const QString& title);
int getWidth();

View file

@ -22,16 +22,14 @@ class RatesScriptingInterface : public QObject {
Q_PROPERTY(float newFrame READ getNewFrameRate)
Q_PROPERTY(float dropped READ getDropRate)
Q_PROPERTY(float simulation READ getSimulationRate)
Q_PROPERTY(float avatar READ getAvatarRate)
public:
RatesScriptingInterface(QObject* parent) : QObject(parent) {}
float getRenderRate() { return qApp->getFps(); }
float getRenderRate() { return qApp->getRenderLoopRate(); }
float getPresentRate() { return qApp->getActiveDisplayPlugin()->presentRate(); }
float getNewFrameRate() { return qApp->getActiveDisplayPlugin()->newFramePresentRate(); }
float getDropRate() { return qApp->getActiveDisplayPlugin()->droppedFrameRate(); }
float getSimulationRate() { return qApp->getAverageSimsPerSecond(); }
float getAvatarRate() { return qApp->getAvatarSimrate(); }
float getSimulationRate() { return qApp->getGameLoopRate(); }
};
#endif // HIFI_INTERFACE_RATES_SCRIPTING_INTERFACE_H

View file

@ -0,0 +1,52 @@
//
// WalletScriptingInterface.cpp
// interface/src/scripting
//
// Created by Zach Fox on 2017-09-29.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "WalletScriptingInterface.h"
CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
}
WalletScriptingInterface::WalletScriptingInterface() {
}
void WalletScriptingInterface::refreshWalletStatus() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getWalletStatus();
}
static const QString CHECKOUT_QML_PATH = qApp->applicationDirPath() + "../../../qml/hifi/commerce/checkout/Checkout.qml";
void WalletScriptingInterface::buy(const QString& name, const QString& id, const int& price, const QString& href) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "buy", Q_ARG(const QString&, name), Q_ARG(const QString&, id), Q_ARG(const int&, price), Q_ARG(const QString&, href));
return;
}
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(CHECKOUT_QML_PATH);
DependencyManager::get<HMDScriptingInterface>()->openTablet();
QQuickItem* root = nullptr;
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !qApp->isHMDMode())) {
root = DependencyManager::get<OffscreenUi>()->getRootItem();
} else {
root = tablet->getTabletRoot();
}
CheckoutProxy* checkout = new CheckoutProxy(root->findChild<QObject*>("checkout"));
// Example: Wallet.buy("Test Flaregun", "0d90d21c-ce7a-4990-ad18-e9d2cf991027", 17, "http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json");
checkout->writeProperty("itemName", name);
checkout->writeProperty("itemId", id);
checkout->writeProperty("itemPrice", price);
checkout->writeProperty("itemHref", href);
}

View file

@ -0,0 +1,54 @@
// WalletScriptingInterface.h
// interface/src/scripting
//
// Created by Zach Fox on 2017-09-29.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_WalletScriptingInterface_h
#define hifi_WalletScriptingInterface_h
#include <QtCore/QObject>
#include <DependencyManager.h>
#include "scripting/HMDScriptingInterface.h"
#include <ui/TabletScriptingInterface.h>
#include <ui/QmlWrapper.h>
#include <OffscreenUi.h>
#include "Application.h"
#include "commerce/Wallet.h"
class CheckoutProxy : public QmlWrapper {
Q_OBJECT
public:
CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr);
};
class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged)
public:
WalletScriptingInterface();
Q_INVOKABLE void refreshWalletStatus();
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
void setWalletStatus(const uint& status) { _walletStatus = status; }
Q_INVOKABLE void buy(const QString& name, const QString& id, const int& price, const QString& href);
signals:
void walletStatusChanged();
void walletNotSetup();
private:
uint _walletStatus;
};
#endif // hifi_WalletScriptingInterface_h

View file

@ -171,6 +171,11 @@ void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& loc
Setting::Handle<QVariant>(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);
}
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->isPointOnDesktopWindow(point);
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const {

View file

@ -72,6 +72,7 @@ public slots:
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
bool isPhysicsEnabled();
bool setDisplayTexture(const QString& name);
bool isPointOnDesktopWindow(QVariant point);
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);

View file

@ -8,6 +8,7 @@
#include "Stats.h"
#include <queue>
#include <sstream>
#include <QFontDatabase>
@ -116,12 +117,6 @@ void Stats::updateStats(bool force) {
}
}
bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded();
if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) {
PerformanceTimer::setActive(shouldDisplayTimingDetail);
}
auto nodeList = DependencyManager::get<NodeList>();
auto avatarManager = DependencyManager::get<AvatarManager>();
// we need to take one avatar out so we don't include ourselves
@ -129,7 +124,7 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated());
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE_FLOAT(framerate, qApp->getFps(), 0.1f);
STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f);
if (qApp->getActiveDisplayPlugin()) {
auto displayPlugin = qApp->getActiveDisplayPlugin();
auto stats = displayPlugin->getHardwareStats();
@ -137,7 +132,6 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(longrenders, stats["long_render_count"].toInt());
STAT_UPDATE(longsubmits, stats["long_submit_count"].toInt());
STAT_UPDATE(longframes, stats["long_frame_count"].toInt());
STAT_UPDATE_FLOAT(renderrate, displayPlugin->renderRate(), 0.1f);
STAT_UPDATE_FLOAT(presentrate, displayPlugin->presentRate(), 0.1f);
STAT_UPDATE_FLOAT(presentnewrate, displayPlugin->newFramePresentRate(), 0.1f);
STAT_UPDATE_FLOAT(presentdroprate, displayPlugin->droppedFrameRate(), 0.1f);
@ -150,8 +144,7 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(presentnewrate, -1);
STAT_UPDATE(presentdroprate, -1);
}
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate());
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
@ -406,14 +399,21 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
}
bool performanceTimerIsActive = PerformanceTimer::isActive();
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
if (displayPerf && performanceTimerIsActive) {
if (!_timingExpanded) {
_timingExpanded = true;
bool performanceTimerShouldBeActive = Menu::getInstance()->isOptionChecked(MenuOption::Stats) && _expanded;
if (performanceTimerShouldBeActive != PerformanceTimer::isActive()) {
PerformanceTimer::setActive(performanceTimerShouldBeActive);
}
if (performanceTimerShouldBeActive) {
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
}
if (performanceTimerShouldBeActive &&
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
if (!_showTimingDetails) {
_showTimingDetails = true;
emit timingExpandedChanged();
}
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
// we will also include room for 1 line per timing record and a header of 4 lines
// Timing details...
@ -453,10 +453,55 @@ void Stats::updateStats(bool force) {
}
_timingStats = perfLines;
emit timingStatsChanged();
} else if (_timingExpanded) {
_timingExpanded = false;
} else if (_showTimingDetails) {
_showTimingDetails = false;
emit timingExpandedChanged();
}
if (_expanded && performanceTimerShouldBeActive) {
if (!_showGameUpdateStats) {
_showGameUpdateStats = true;
}
class SortableStat {
public:
SortableStat(QString a, float p) : message(a), priority(p) {}
QString message;
float priority;
bool operator<(const SortableStat& other) const { return priority < other.priority; }
};
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
std::priority_queue<SortableStat> idleUpdateStats;
auto itr = allRecords.find("/idle/update");
if (itr != allRecords.end()) {
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
_gameUpdateStats = QString("/idle/update = %1 ms").arg(dt);
QVector<QString> categories = { "devices", "physics", "otherAvatars", "MyAvatar", "misc" };
for (int32_t j = 0; j < categories.size(); ++j) {
QString recordKey = "/idle/update/" + categories[j];
itr = allRecords.find(recordKey);
if (itr != allRecords.end()) {
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
idleUpdateStats.push(SortableStat(message, dt));
}
}
while (!idleUpdateStats.empty()) {
SortableStat stat = idleUpdateStats.top();
_gameUpdateStats += stat.message;
idleUpdateStats.pop();
}
emit gameUpdateStatsChanged();
} else if (_gameUpdateStats != "") {
_gameUpdateStats = "";
emit gameUpdateStatsChanged();
}
} else if (_showGameUpdateStats) {
_showGameUpdateStats = false;
_gameUpdateStats = "";
emit gameUpdateStatsChanged();
}
}
void Stats::setRenderDetails(const render::RenderDetails& details) {

View file

@ -32,8 +32,6 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, serverCount, 0)
// How often the app is creating new gpu::Frames
STATS_PROPERTY(float, framerate, 0)
// How often the display plugin is executing a given frame
STATS_PROPERTY(float, renderrate, 0)
// How often the display plugin is presenting to the device
STATS_PROPERTY(float, presentrate, 0)
@ -47,8 +45,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, presentnewrate, 0)
STATS_PROPERTY(float, presentdroprate, 0)
STATS_PROPERTY(int, simrate, 0)
STATS_PROPERTY(int, avatarSimrate, 0)
STATS_PROPERTY(int, gameLoopRate, 0)
STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, updatedAvatarCount, 0)
STATS_PROPERTY(int, notUpdatedAvatarCount, 0)
@ -108,6 +105,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(QString, packetStats, QString())
STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, gameUpdateStats, QString())
STATS_PROPERTY(int, serverElements, 0)
STATS_PROPERTY(int, serverInternal, 0)
STATS_PROPERTY(int, serverLeaves, 0)
@ -148,7 +146,7 @@ public:
void updateStats(bool force = false);
bool isExpanded() { return _expanded; }
bool isTimingExpanded() { return _timingExpanded; }
bool isTimingExpanded() { return _showTimingDetails; }
void setExpanded(bool expanded) {
if (_expanded != expanded) {
@ -167,7 +165,6 @@ signals:
void longrendersChanged();
void longframesChanged();
void appdroppedChanged();
void framerateChanged();
void expandedChanged();
void timingExpandedChanged();
void serverCountChanged();
@ -176,8 +173,7 @@ signals:
void presentnewrateChanged();
void presentdroprateChanged();
void stutterrateChanged();
void simrateChanged();
void avatarSimrateChanged();
void gameLoopRateChanged();
void avatarCountChanged();
void updatedAvatarCountChanged();
void notUpdatedAvatarCountChanged();
@ -242,6 +238,7 @@ signals:
void localInternalChanged();
void localLeavesChanged();
void timingStatsChanged();
void gameUpdateStatsChanged();
void glContextSwapchainMemoryChanged();
void qmlTextureMemoryChanged();
void texturePendingTransfersChanged();
@ -267,7 +264,8 @@ private:
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon{ true };
bool _expanded{ false };
bool _timingExpanded{ false };
bool _showTimingDetails{ false };
bool _showGameUpdateStats{ false };
QString _monospaceFont;
const AudioIOStats* _audioStats;
QStringList _downloadUrls = QStringList();

View file

@ -26,7 +26,8 @@ Base3DOverlay::Base3DOverlay() :
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_ignoreRayIntersection(false),
_drawInFront(false)
_drawInFront(false),
_drawHUDLayer(false)
{
}
@ -38,6 +39,7 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
_drawInFront(base3DOverlay->_drawInFront),
_drawHUDLayer(base3DOverlay->_drawHUDLayer),
_isGrabbable(base3DOverlay->_isGrabbable)
{
setTransform(base3DOverlay->getTransform());
@ -125,13 +127,19 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
bool needRenderItemUpdate = false;
auto drawInFront = properties["drawInFront"];
if (drawInFront.isValid()) {
bool value = drawInFront.toBool();
setDrawInFront(value);
needRenderItemUpdate = true;
}
auto drawHUDLayer = properties["drawHUDLayer"];
if (drawHUDLayer.isValid()) {
bool value = drawHUDLayer.toBool();
setDrawHUDLayer(value);
needRenderItemUpdate = true;
}
auto isGrabbable = properties["grabbable"];
if (isGrabbable.isValid()) {
setIsGrabbable(isGrabbable.toBool());
@ -265,15 +273,14 @@ void Base3DOverlay::parentDeleted() {
}
void Base3DOverlay::update(float duration) {
// In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true.
// then the correct transform used for rendering is computed in the update transaction and assigned.
if (_renderTransformDirty) {
auto itemID = getRenderItemID();
// Capture the render transform value in game loop before
auto latestTransform = evalRenderTransform();
_renderTransformDirty = false;
if (render::Item::isValidID(itemID)) {
// Capture the render transform value in game loop before
auto latestTransform = evalRenderTransform();
_renderTransformDirty = false;
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
transaction.updateItem<Overlay>(itemID, [latestTransform](Overlay& data) {
@ -283,8 +290,6 @@ void Base3DOverlay::update(float duration) {
}
});
scene->enqueueTransaction(transaction);
} else {
setRenderTransform(latestTransform);
}
}
}

View file

@ -41,6 +41,7 @@ public:
bool getIsSolidLine() const { return !_isDashedLine; }
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
bool getDrawInFront() const { return _drawInFront; }
bool getDrawHUDLayer() const { return _drawHUDLayer; }
bool getIsGrabbable() const { return _isGrabbable; }
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
@ -48,6 +49,7 @@ public:
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
virtual void setDrawInFront(bool value) { _drawInFront = value; }
virtual void setDrawHUDLayer(bool value) { _drawHUDLayer = value; }
void setIsGrabbable(bool value) { _isGrabbable = value; }
virtual AABox getBounds() const override = 0;
@ -81,6 +83,7 @@ protected:
bool _isDashedLine;
bool _ignoreRayIntersection;
bool _drawInFront;
bool _drawHUDLayer;
bool _isGrabbable { false };
mutable bool _renderTransformDirty{ true };

View file

@ -263,7 +263,7 @@ const render::ShapeKey Circle3DOverlay::getShapeKey() {
if (isTransparent()) {
builder.withTranslucent();
}
if (!getIsSolid() || shouldDrawHUDLayer()) {
if (!getIsSolid()) {
builder.withUnlit().withDepthBias();
}
return builder.build();

View file

@ -41,6 +41,7 @@ ContextOverlayInterface::ContextOverlayInterface() {
_entityPropertyFlags += PROP_MARKETPLACE_ID;
_entityPropertyFlags += PROP_DIMENSIONS;
_entityPropertyFlags += PROP_REGISTRATION_POINT;
_entityPropertyFlags += PROP_CERTIFICATE_ID;
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>().data();
connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&)));
@ -176,7 +177,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) {
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
return (entityProperties.getMarketplaceID().length() != 0);
Setting::Handle<bool> _settingSwitch{ "commerce", false };
if (_settingSwitch.get()) {
return (entityProperties.getCertificateID().length() != 0);
} else {
return (entityProperties.getMarketplaceID().length() != 0);
}
}
bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
@ -201,7 +207,8 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
if (_commerceSettingSwitch.get()) {
Setting::Handle<bool> _settingSwitch{ "commerce", false };
if (_settingSwitch.get()) {
openInspectionCertificate();
} else {
openMarketplace();

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