Merge remote-tracking branch 'upstream/master' into 21180-C++
|
@ -3,7 +3,7 @@ This is a stand-alone guide for creating your first High Fidelity build for Wind
|
|||
## Building High Fidelity
|
||||
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
|
||||
|
||||
Note: The prerequisites will require about 10 GB of space on your drive.
|
||||
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
|
||||
|
||||
### Step 1. Visual Studio 2017
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@ if (WIN32)
|
|||
cmake_policy(SET CMP0020 NEW)
|
||||
endif (WIN32)
|
||||
|
||||
if (POLICY CMP0028)
|
||||
cmake_policy(SET CMP0028 OLD)
|
||||
endif ()
|
||||
|
||||
if (POLICY CMP0043)
|
||||
cmake_policy(SET CMP0043 OLD)
|
||||
endif ()
|
||||
|
@ -83,7 +79,7 @@ endif(WIN32)
|
|||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.3")
|
||||
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
|
||||
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
|
||||
add_definitions(-DGLM_FORCE_PURE)
|
||||
endif()
|
||||
endif()
|
||||
|
|
BIN
Test Plan 2.docx
Normal file
|
@ -184,6 +184,9 @@ void Agent::run() {
|
|||
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);
|
||||
|
||||
// make sure we hear about dissappearing nodes so we can clear the entity tree if an entity server goes away
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &Agent::nodeKilled);
|
||||
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
|
||||
});
|
||||
|
@ -259,6 +262,13 @@ void Agent::nodeActivated(SharedNodePointer activatedNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void Agent::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (killedNode->getType() == NodeType::EntityServer) {
|
||||
// an entity server has gone away, ask the headless viewer to clear its tree
|
||||
_entityViewer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::negotiateAudioFormat() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto negotiateFormatPacket = NLPacket::create(PacketType::NegotiateAudioFormat);
|
||||
|
|
|
@ -77,6 +77,7 @@ private slots:
|
|||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void nodeActivated(SharedNodePointer activatedNode);
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
|
||||
void processAgentAvatar();
|
||||
void processAgentAvatarAudio();
|
||||
|
@ -109,7 +110,7 @@ private:
|
|||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||
|
||||
AudioGate _audioGate;
|
||||
bool _audioGateOpen { false };
|
||||
bool _audioGateOpen { true };
|
||||
bool _isNoiseGateEnabled { false };
|
||||
|
||||
CodecPluginPointer _codec;
|
||||
|
|
|
@ -127,7 +127,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> mess
|
|||
// construct a "fake" audio received message from the byte array and packet list information
|
||||
auto audioData = message->getMessage().mid(NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
PacketType rewrittenType = REPLICATED_PACKET_MAPPING.key(message->getType());
|
||||
PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType());
|
||||
|
||||
if (rewrittenType == PacketType::Unknown) {
|
||||
qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";
|
||||
|
|
|
@ -125,11 +125,11 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
|
|||
// now make sure it's a packet type that we want to replicate
|
||||
|
||||
// first check if it is an original type that we should replicate
|
||||
PacketType mirroredType = REPLICATED_PACKET_MAPPING.value(message.getType());
|
||||
PacketType mirroredType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType());
|
||||
|
||||
if (mirroredType == PacketType::Unknown) {
|
||||
// if it wasn't check if it is a replicated type that we should re-replicate
|
||||
if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) {
|
||||
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
|
||||
mirroredType = message.getType();
|
||||
} else {
|
||||
qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";
|
||||
|
|
|
@ -144,10 +144,10 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node
|
|||
// check if this is a packet type we replicate
|
||||
// which means it must be a packet type present in REPLICATED_PACKET_MAPPING or must be the
|
||||
// replicated version of one of those packet types
|
||||
PacketType replicatedType = REPLICATED_PACKET_MAPPING.value(message.getType());
|
||||
PacketType replicatedType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType());
|
||||
|
||||
if (replicatedType == PacketType::Unknown) {
|
||||
if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) {
|
||||
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
|
||||
replicatedType = message.getType();
|
||||
} else {
|
||||
qDebug() << __FUNCTION__ << "called without replicatable packet type - returning";
|
||||
|
|
|
@ -935,39 +935,7 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
|
|||
// so here we just store a special file at our persist path
|
||||
// and then force a stop of the server so that it can pick it up when it relaunches
|
||||
if (!_persistAbsoluteFilePath.isEmpty()) {
|
||||
|
||||
// before we restart the server and make it try and load this data, let's make sure it is valid
|
||||
auto compressedOctree = message->getMessage();
|
||||
QByteArray jsonOctree;
|
||||
|
||||
// assume we have GZipped content
|
||||
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
|
||||
if (!wasCompressed) {
|
||||
// the source was not compressed, assume we were sent regular JSON data
|
||||
jsonOctree = compressedOctree;
|
||||
}
|
||||
|
||||
// check the JSON data to verify it is an object
|
||||
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
|
||||
if (!wasCompressed) {
|
||||
// source was not compressed, we compress it before we write it locally
|
||||
gzip(jsonOctree, compressedOctree);
|
||||
}
|
||||
|
||||
// write the compressed octree data to a special file
|
||||
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
|
||||
QFile replacementFile(replacementFilePath);
|
||||
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
|
||||
// we've now written our replacement file, time to take the server down so it can
|
||||
// process it when it comes back up
|
||||
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
||||
setFinished(true);
|
||||
} else {
|
||||
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
||||
}
|
||||
replaceContentFromMessageData(message->getMessage());
|
||||
} else {
|
||||
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
|
||||
}
|
||||
|
@ -977,6 +945,68 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
|
|||
}
|
||||
}
|
||||
|
||||
// Message->getMessage() contains a QByteArray representation of the URL to download from
|
||||
void OctreeServer::handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message) {
|
||||
qInfo() << "Received request to replace content from a url";
|
||||
if (!_isFinished && !_isShuttingDown) {
|
||||
// This call comes from Interface, so we skip our domain server check
|
||||
// but confirm that we have permissions to replace content sets
|
||||
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
|
||||
if (!_persistAbsoluteFilePath.isEmpty()) {
|
||||
// Convert message data into our URL
|
||||
QString url(message->getMessage());
|
||||
QUrl modelsURL = QUrl(url, QUrl::StrictMode);
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(modelsURL);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
|
||||
QNetworkReply::NetworkError networkError = reply->error();
|
||||
if (networkError == QNetworkReply::NoError) {
|
||||
QByteArray contents = reply->readAll();
|
||||
replaceContentFromMessageData(contents);
|
||||
} else {
|
||||
qDebug() << "Error downloading JSON from specified file";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::replaceContentFromMessageData(QByteArray content) {
|
||||
//Assume we have compressed data
|
||||
auto compressedOctree = content;
|
||||
QByteArray jsonOctree;
|
||||
|
||||
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
|
||||
if (!wasCompressed) {
|
||||
// the source was not compressed, assume we were sent regular JSON data
|
||||
jsonOctree = compressedOctree;
|
||||
}
|
||||
// check the JSON data to verify it is an object
|
||||
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
|
||||
if (!wasCompressed) {
|
||||
// source was not compressed, we compress it before we write it locally
|
||||
gzip(jsonOctree, compressedOctree);
|
||||
}
|
||||
// write the compressed octree data to a special file
|
||||
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
|
||||
QFile replacementFile(replacementFilePath);
|
||||
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
|
||||
// we've now written our replacement file, time to take the server down so it can
|
||||
// process it when it comes back up
|
||||
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
||||
setFinished(true);
|
||||
} else {
|
||||
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
||||
}
|
||||
}
|
||||
|
||||
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {
|
||||
result = false; // assume it doesn't exist
|
||||
bool optionAvailable = false;
|
||||
|
@ -1202,6 +1232,7 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
|
||||
|
||||
readConfiguration();
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ private slots:
|
|||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
|
||||
void removeSendThread();
|
||||
|
||||
protected:
|
||||
|
@ -161,6 +162,8 @@ protected:
|
|||
UniqueSendThread createSendThread(const SharedNodePointer& node);
|
||||
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
|
||||
|
||||
void replaceContentFromMessageData(QByteArray content);
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
|
|
99
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -2,57 +2,30 @@ set(EXTERNAL_NAME tbb)
|
|||
|
||||
include(ExternalProject)
|
||||
|
||||
if (ANDROID)
|
||||
|
||||
find_program(NDK_BUILD_COMMAND NAMES ndk-build DOC "Path to the ndk-build command")
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
|
||||
URL_MD5 bf090eaa86cf89ea014b7b462786a440
|
||||
BUILD_COMMAND ${NDK_BUILD_COMMAND} --directory=jni target=android tbb tbbmalloc arch=arm
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -DTBB_LIBS_SUFFIX=so -P ${CMAKE_CURRENT_SOURCE_DIR}/TBBLibCopy.cmake
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_win_slim.zip)
|
||||
set(DOWNLOAD_MD5 065934458e3db88397f3d10e7eea536c)
|
||||
elseif (APPLE)
|
||||
find_program(MAKE_COMMAND NAMES make DOC "Path to the make command")
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
|
||||
URL_MD5 bf090eaa86cf89ea014b7b462786a440
|
||||
BUILD_COMMAND ${MAKE_COMMAND} tbb_os=macos
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -DTBB_LIBS_SUFFIX=dylib -P ${CMAKE_CURRENT_SOURCE_DIR}/TBBLibCopy.cmake
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
|
||||
elseif (ANDROID)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_and_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 04d50b64e1d81245a1be5f75f34d64c7)
|
||||
else ()
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_win.zip)
|
||||
set(DOWNLOAD_MD5 d250d40bb93b255f75bcbb19e976a440)
|
||||
else ()
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_lin.tgz)
|
||||
set(DOWNLOAD_MD5 7830ba2bc62438325fba2ec0c95367a5)
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL ${DOWNLOAD_URL}
|
||||
URL_MD5 ${DOWNLOAD_MD5}
|
||||
BUILD_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD ON
|
||||
)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL ${DOWNLOAD_URL}
|
||||
URL_MD5 ${DOWNLOAD_MD5}
|
||||
BUILD_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD ON
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
|
@ -70,22 +43,32 @@ if (APPLE)
|
|||
change-install-name
|
||||
COMMENT "Calling install_name_tool on TBB libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_TBB_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
DEPENDEES download
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
elseif (WIN32)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/vc12")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/vc12" CACHE PATH "Path to TBB DLLs")
|
||||
if (MSVC_VERSION GREATER_EQUAL 1900)
|
||||
set(_TBB_MSVC_DIR "vc14")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1800)
|
||||
set(_TBB_MSVC_DIR "vc12")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1700)
|
||||
set(_TBB_MSVC_DIR "vc11")
|
||||
else()
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs")
|
||||
message(FATAL_ERROR "MSVC ${MSVC_VERSION} not supported by Intel TBB")
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/${_TBB_MSVC_DIR}")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/${_TBB_MSVC_DIR}" CACHE PATH "Path to TBB DLLs")
|
||||
else()
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/${_TBB_MSVC_DIR}")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/${_TBB_MSVC_DIR}" CACHE PATH "Path to TBB DLLs")
|
||||
endif()
|
||||
|
||||
set(_LIB_EXT "lib")
|
||||
elseif (ANDROID)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib")
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/android")
|
||||
set(_LIB_PREFIX "lib")
|
||||
set(_LIB_EXT "so")
|
||||
elseif (UNIX)
|
||||
|
@ -103,15 +86,15 @@ elseif (UNIX)
|
|||
OUTPUT_VARIABLE GCC_VERSION
|
||||
)
|
||||
|
||||
if (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
|
||||
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.7")
|
||||
elseif (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.4")
|
||||
elseif (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.1")
|
||||
else ()
|
||||
message(STATUS "Could not find a compatible version of Threading Building Blocks library for your compiler.")
|
||||
endif ()
|
||||
|
||||
|
||||
endif ()
|
||||
|
||||
if (DEFINED _TBB_LIB_DIR)
|
||||
|
|
4
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -6,8 +6,8 @@ if (WIN32)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi8.zip
|
||||
URL_MD5 b01510437ea15527156bc25cdf733bd9
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
|
||||
URL_MD5 94f4765bdbcd53cd099f349ae031e769
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -22,23 +22,17 @@ macro(GENERATE_INSTALLERS)
|
|||
set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}")
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
|
||||
if (PR_BUILD)
|
||||
set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2")
|
||||
endif ()
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME})
|
||||
|
||||
|
||||
if (WIN32)
|
||||
# include CMake module that will install compiler system libraries
|
||||
# so that we have msvcr120 and msvcp120 installed with targets
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
|
||||
|
||||
# as long as we're including sixense plugin with installer
|
||||
# we need re-distributables for VS 2011 as well
|
||||
# this should be removed if/when sixense support is pulled
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
|
||||
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll"
|
||||
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll"
|
||||
)
|
||||
|
||||
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
||||
|
||||
# install and reference the Add/Remove icon
|
||||
|
@ -90,3 +84,4 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
include(CPack)
|
||||
endmacro()
|
||||
|
||||
|
|
|
@ -22,9 +22,12 @@ macro(install_beside_console)
|
|||
else ()
|
||||
# setup install of executable and things copied by fixup/windeployqt
|
||||
install(
|
||||
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
DESTINATION ${COMPONENT_INSTALL_DIR}
|
||||
COMPONENT ${SERVER_COMPONENT}
|
||||
PATTERN "*.pdb" EXCLUDE
|
||||
PATTERN "*.lib" EXCLUDE
|
||||
PATTERN "*.exp" EXCLUDE
|
||||
)
|
||||
|
||||
# on windows for PR and production builds, sign the executable
|
||||
|
|
|
@ -111,14 +111,14 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
# shortcut names
|
||||
if (PRODUCTION_BUILD)
|
||||
set(INTERFACE_SHORTCUT_NAME "Interface")
|
||||
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface")
|
||||
set(CONSOLE_SHORTCUT_NAME "Sandbox")
|
||||
else ()
|
||||
set(INTERFACE_SHORTCUT_NAME "Interface - ${BUILD_VERSION}")
|
||||
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION}")
|
||||
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION}")
|
||||
endif ()
|
||||
|
||||
set(INTERFACE_HF_SHORTCUT_NAME "High Fidelity ${INTERFACE_SHORTCUT_NAME}")
|
||||
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
|
||||
set(CONSOLE_HF_SHORTCUT_NAME "High Fidelity ${CONSOLE_SHORTCUT_NAME}")
|
||||
|
||||
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "High Fidelity")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
#
|
||||
# FindTBB.cmake
|
||||
#
|
||||
#
|
||||
# Try to find the Intel Threading Building Blocks library
|
||||
#
|
||||
# You can provide a TBB_ROOT_DIR which contains lib and include directories
|
||||
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("tbb")
|
||||
|
@ -34,31 +34,43 @@ elseif (UNIX AND NOT ANDROID)
|
|||
else()
|
||||
set(_TBB_ARCH_DIR "ia32")
|
||||
endif()
|
||||
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION
|
||||
)
|
||||
|
||||
if (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
|
||||
|
||||
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
|
||||
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.7")
|
||||
elseif (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
|
||||
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.4")
|
||||
elseif (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1)
|
||||
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.1")
|
||||
else ()
|
||||
message(FATAL_ERROR "Could not find a compatible version of Threading Building Blocks library for your compiler.")
|
||||
endif ()
|
||||
|
||||
|
||||
elseif (WIN32)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_TBB_ARCH_DIR "intel64")
|
||||
else()
|
||||
set(_TBB_ARCH_DIR "ia32")
|
||||
endif()
|
||||
|
||||
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/vc12")
|
||||
|
||||
find_path(TBB_DLL_PATH tbb_debug.dll PATH_SUFFIXES "bin/${_TBB_ARCH_DIR}/vc12" HINTS ${TBB_SEARCH_DIRS})
|
||||
|
||||
|
||||
if (MSVC_VERSION GREATER_EQUAL 1900)
|
||||
set(_TBB_MSVC_DIR "vc14")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1800)
|
||||
set(_TBB_MSVC_DIR "vc12")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1700)
|
||||
set(_TBB_MSVC_DIR "vc11")
|
||||
else()
|
||||
message(FATAL_ERROR "MSVC ${MSVC_VERSION} not supported by Intel TBB")
|
||||
endif()
|
||||
|
||||
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/${_TBB_MSVC_DIR}")
|
||||
|
||||
find_path(TBB_DLL_PATH tbb_debug.dll PATH_SUFFIXES "bin/${_TBB_ARCH_DIR}/${_TBB_MSVC_DIR}" HINTS ${TBB_SEARCH_DIRS})
|
||||
|
||||
elseif (ANDROID)
|
||||
set(_TBB_DEFAULT_INSTALL_DIR "/tbb")
|
||||
set(_TBB_LIB_NAME "tbb")
|
||||
|
|
|
@ -11,34 +11,28 @@
|
|||
|
||||
include(BundleUtilities)
|
||||
|
||||
# replace copy_resolved_item_into_bundle
|
||||
#
|
||||
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
|
||||
# the resolved item matches the resolved embedded item. This not not really an issue that
|
||||
# should rise to the level of a "warning" so we replace this message with a "status:"
|
||||
#
|
||||
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
||||
if (WIN32)
|
||||
# ignore case on Windows
|
||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
||||
else()
|
||||
set(resolved_item_compare "${resolved_item}")
|
||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
||||
function(gp_resolved_file_type_override resolved_file type_var)
|
||||
if( file MATCHES ".*VCRUNTIME140.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
||||
# this is our only change from the original version
|
||||
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
|
||||
else()
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
||||
endif()
|
||||
if( file MATCHES ".*concrt140.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
if( file MATCHES ".*msvcp140.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
if( file MATCHES ".*vcruntime140.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
if( file MATCHES ".*api-ms-win-crt-conio.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
if( file MATCHES ".*api-ms-win-core-winrt.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
|
||||
message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}")
|
||||
|
||||
|
@ -52,3 +46,4 @@ endif()
|
|||
|
||||
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@")
|
||||
|
||||
|
|
|
@ -484,16 +484,14 @@ Function InstallTypesPage
|
|||
StrCpy $OffsetUnits u
|
||||
StrCpy $Express "0"
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
|
||||
pop $ExpressInstallRadioButton
|
||||
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
|
||||
pop $ExpressInstallRadioButton
|
||||
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
|
||||
pop $CustomInstallRadioButton
|
||||
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
|
||||
${EndIf}
|
||||
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
|
||||
pop $CustomInstallRadioButton
|
||||
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
|
||||
|
||||
; Express Install selected by default
|
||||
${NSD_Check} $ExpressInstallRadioButton
|
||||
|
@ -518,7 +516,6 @@ FunctionEnd
|
|||
|
||||
Function AbortFunction
|
||||
; Check if Express is set, if so, abort the post install options page
|
||||
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
|
||||
StrCmp $Express "1" 0 end
|
||||
Abort
|
||||
end:
|
||||
|
@ -529,10 +526,15 @@ Function PostInstallOptionsPage
|
|||
|
||||
nsDialogs::Create 1018
|
||||
Pop $PostInstallDialog
|
||||
|
||||
|
||||
${If} $PostInstallDialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Check if Express is set, if so, abort the post install options page
|
||||
StrCmp $Express "1" 0 end
|
||||
Abort
|
||||
end:
|
||||
|
||||
StrCpy $CurrentOffset 0
|
||||
StrCpy $OffsetUnits u
|
||||
|
@ -617,12 +619,6 @@ Function PostInstallOptionsPage
|
|||
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
; Check if Express is set, if so, abort the post install options page
|
||||
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
|
||||
StrCmp $Express "1" 0 end
|
||||
Abort
|
||||
end:
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
|
@ -638,15 +634,30 @@ Var LaunchServerNowState
|
|||
Var LaunchClientNowState
|
||||
Var CopyFromProductionState
|
||||
Var CleanInstallState
|
||||
Var ExpressInstallState
|
||||
Var ExpressInstallState
|
||||
Var CustomInstallState
|
||||
|
||||
Function ReadInstallTypes
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
; check if the user asked for express/custom install
|
||||
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
|
||||
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
|
||||
; check if the user asked for express/custom install
|
||||
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
|
||||
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
|
||||
|
||||
${If} $ExpressInstallState == ${BST_CHECKED}
|
||||
StrCpy $Express "1"
|
||||
|
||||
StrCpy $DesktopClientState ${BST_CHECKED}
|
||||
StrCpy $ServerStartupState ${BST_CHECKED}
|
||||
StrCpy $LaunchServerNowState ${BST_CHECKED}
|
||||
StrCpy $LaunchClientNowState ${BST_CHECKED}
|
||||
StrCpy $CleanInstallState ${BST_UNCHECKED}
|
||||
StrCpy $DesktopServerState ${BST_UNCHECKED}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
StrCpy $CopyFromProductionState ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function ReadPostInstallOptions
|
||||
|
@ -684,28 +695,6 @@ Function ReadPostInstallOptions
|
|||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function HandleInstallTypes
|
||||
${If} $ExpressInstallState == ${BST_CHECKED}
|
||||
|
||||
StrCpy $Express "1"
|
||||
|
||||
; over ride custom checkboxes and select defaults
|
||||
${NSD_SetState} $DesktopClientCheckbox ${BST_CHECKED}
|
||||
${NSD_SetState} $ServerStartupCheckbox ${BST_CHECKED}
|
||||
${NSD_SetState} $LaunchServerNowCheckbox ${BST_CHECKED}
|
||||
${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected
|
||||
Call ReadPostInstallOptions
|
||||
Call HandlePostInstallOptions
|
||||
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function HandlePostInstallOptions
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
; check if the user asked for a desktop shortcut to High Fidelity
|
||||
|
@ -853,6 +842,8 @@ Section "-Core installation"
|
|||
; Rename the incorrectly cased Raleway font
|
||||
Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml"
|
||||
|
||||
ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart"
|
||||
|
||||
; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
|
||||
RMDir /r "$INSTDIR\Interface"
|
||||
Delete "$INSTDIR\vcredist_x64.exe"
|
||||
|
@ -952,9 +943,9 @@ Section "-Core installation"
|
|||
Call ConditionalAddToRegisty
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
|
||||
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
|
||||
|
||||
|
||||
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
|
||||
|
||||
; Handle whichever post install options were set
|
||||
Call HandlePostInstallOptions
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 1.7,
|
||||
"version": 1.8,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -112,7 +112,6 @@
|
|||
"label": "Adult (18+)"
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"name": "hosts",
|
||||
|
@ -161,15 +160,15 @@
|
|||
"label": "HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Password used for basic HTTP authentication. Leave this alone if you do not want to change it.",
|
||||
"password_placeholder" : "******",
|
||||
"password_placeholder": "******",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "verify_http_password",
|
||||
"label": "Verify HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Must match the password entered above for change to be saved.",
|
||||
"value-hidden": true
|
||||
"name": "verify_http_password",
|
||||
"label": "Verify HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Must match the password entered above for change to be saved.",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "maximum_user_capacity",
|
||||
|
@ -208,21 +207,19 @@
|
|||
"name": "standard_permissions",
|
||||
"type": "table",
|
||||
"label": "Domain-Wide User Permissions",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"caption": "Standard Permissions",
|
||||
"can_add_new_rows": false,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "Type of User",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -276,9 +273,15 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
],
|
||||
|
||||
"non-deletable-row-key": "permissions_id",
|
||||
"non-deletable-row-values": ["localhost", "anonymous", "logged-in"]
|
||||
},
|
||||
|
@ -291,18 +294,16 @@
|
|||
"can_add_new_rows": false,
|
||||
"new_category_placeholder": "Add Group",
|
||||
"new_category_message": "Save and reload to see ranks",
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "Rank",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -381,6 +382,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -393,18 +401,16 @@
|
|||
"can_add_new_rows": false,
|
||||
"new_category_placeholder": "Add Blacklist Group",
|
||||
"new_category_message": "Save and reload to see ranks",
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "Rank",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -480,6 +486,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -488,18 +501,16 @@
|
|||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -553,6 +564,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -567,11 +585,10 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -625,6 +642,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -639,11 +663,10 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -697,6 +720,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -711,11 +741,10 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 7
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -769,6 +798,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -784,7 +820,6 @@
|
|||
"label": "Persistent Scripts",
|
||||
"help": "Add the URLs for scripts that you would like to ensure are always running in your domain.",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "url",
|
||||
|
@ -946,7 +981,6 @@
|
|||
"help": "In this table you can define a set of zones in which you can specify various audio properties.",
|
||||
"numbered": false,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"key": {
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
|
@ -999,7 +1033,6 @@
|
|||
"numbered": true,
|
||||
"can_order": true,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "source",
|
||||
|
@ -1028,7 +1061,6 @@
|
|||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
|
||||
"numbered": true,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "zone",
|
||||
|
@ -1063,7 +1095,9 @@
|
|||
{
|
||||
"name": "audio_buffer",
|
||||
"label": "Audio Buffers",
|
||||
"assignment-types": [0],
|
||||
"assignment-types": [
|
||||
0
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "dynamic_jitter_buffer",
|
||||
|
@ -1082,35 +1116,37 @@
|
|||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "max_frames_over_desired",
|
||||
"deprecated": true
|
||||
"name": "max_frames_over_desired",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_starve_threshold",
|
||||
"deprecated": true
|
||||
"name": "window_starve_threshold",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_calc_on_too_many_starves",
|
||||
"deprecated": true
|
||||
"name": "window_seconds_for_desired_calc_on_too_many_starves",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_reduction",
|
||||
"deprecated": true
|
||||
"name": "window_seconds_for_desired_reduction",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "use_stdev_for_desired_calc",
|
||||
"deprecated": true
|
||||
"name": "use_stdev_for_desired_calc",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "repetition_with_fade",
|
||||
"deprecated": true
|
||||
"name": "repetition_with_fade",
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entity_server_settings",
|
||||
"label": "Entity Server Settings",
|
||||
"assignment-types": [6],
|
||||
"assignment-types": [
|
||||
6
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "maxTmpLifetime",
|
||||
|
@ -1167,13 +1203,32 @@
|
|||
"help": "In this table you can define a set of rules for how frequently to backup copies of your entites content file.",
|
||||
"numbered": false,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"default": [
|
||||
{"Name":"Half Hourly Rolling","backupInterval":1800,"format":".backup.halfhourly.%N","maxBackupVersions":5},
|
||||
{"Name":"Daily Rolling","backupInterval":86400,"format":".backup.daily.%N","maxBackupVersions":7},
|
||||
{"Name":"Weekly Rolling","backupInterval":604800,"format":".backup.weekly.%N","maxBackupVersions":4},
|
||||
{"Name":"Thirty Day Rolling","backupInterval":2592000,"format":".backup.thirtyday.%N","maxBackupVersions":12}
|
||||
],
|
||||
"default": [
|
||||
{
|
||||
"Name": "Half Hourly Rolling",
|
||||
"backupInterval": 1800,
|
||||
"format": ".backup.halfhourly.%N",
|
||||
"maxBackupVersions": 5
|
||||
},
|
||||
{
|
||||
"Name": "Daily Rolling",
|
||||
"backupInterval": 86400,
|
||||
"format": ".backup.daily.%N",
|
||||
"maxBackupVersions": 7
|
||||
},
|
||||
{
|
||||
"Name": "Weekly Rolling",
|
||||
"backupInterval": 604800,
|
||||
"format": ".backup.weekly.%N",
|
||||
"maxBackupVersions": 4
|
||||
},
|
||||
{
|
||||
"Name": "Thirty Day Rolling",
|
||||
"backupInterval": 2592000,
|
||||
"format": ".backup.thirtyday.%N",
|
||||
"maxBackupVersions": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
{
|
||||
"name": "Name",
|
||||
|
@ -1309,7 +1364,9 @@
|
|||
{
|
||||
"name": "avatar_mixer",
|
||||
"label": "Avatar Mixer",
|
||||
"assignment-types": [1],
|
||||
"assignment-types": [
|
||||
1
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "max_node_send_bandwidth",
|
||||
|
@ -1362,7 +1419,10 @@
|
|||
{
|
||||
"name": "downstream_servers",
|
||||
"label": "Receiving Servers",
|
||||
"assignment-types": [0,1],
|
||||
"assignment-types": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
|
@ -1402,7 +1462,10 @@
|
|||
{
|
||||
"name": "upstream_servers",
|
||||
"label": "Broadcasting Servers",
|
||||
"assignment-types": [0,1],
|
||||
"assignment-types": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
|
@ -1442,4 +1505,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
if (message->getSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream packetStream(message->getMessage());
|
||||
|
||||
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
|
||||
|
@ -269,6 +270,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
} else {
|
||||
// this node is an agent
|
||||
const QHostAddress& addr = node->getLocalSocket().getAddress();
|
||||
|
@ -357,6 +359,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
newNode->setPermissions(userPerms);
|
||||
return newNode;
|
||||
}
|
||||
|
|
|
@ -458,7 +458,7 @@ const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
|
|||
|
||||
|
||||
|
||||
bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
|
||||
bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
||||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
|
||||
|
||||
|
@ -471,7 +471,48 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
|
|||
DomainGatekeeper::sendProtocolMismatchConnectionDenial(packet.getSenderSockAddr());
|
||||
}
|
||||
|
||||
// let the normal nodeList implementation handle all other packets.
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) {
|
||||
// this is a sourced packet - first check if we have a node that matches
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID);
|
||||
|
||||
if (sourceNode) {
|
||||
// unverified DS packets (due to a lack of connection secret between DS + node)
|
||||
// must come either from the same public IP address or a local IP address (set by RFC 1918)
|
||||
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sourceNode->getLinkedData());
|
||||
|
||||
bool exactAddressMatch = nodeData->getSendingSockAddr() == packet.getSenderSockAddr();
|
||||
bool bothPrivateAddresses = nodeData->getSendingSockAddr().hasPrivateAddress()
|
||||
&& packet.getSenderSockAddr().hasPrivateAddress();
|
||||
|
||||
if (nodeData && (exactAddressMatch || bothPrivateAddresses)) {
|
||||
// to the best of our ability we've verified that this packet comes from the right place
|
||||
// let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again)
|
||||
return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data());
|
||||
} else {
|
||||
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
|
||||
|
||||
qDebug() << "Packet of type" << headerType
|
||||
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID);
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
|
||||
|
||||
qDebug() << "Packet of type" << headerType
|
||||
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to allow the normal NodeList implementation to verify packets
|
||||
return nodeList->isPacketVerified(packet);
|
||||
}
|
||||
|
||||
|
@ -570,7 +611,7 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
addStaticAssignmentsToQueue();
|
||||
|
||||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
nodeList->setPacketFilterOperator(&DomainServer::packetVersionMatch);
|
||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
}
|
||||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
|
@ -853,7 +894,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
||||
QDataStream packetStream(message->getMessage());
|
||||
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ public slots:
|
|||
|
||||
void restart();
|
||||
|
||||
private slots:
|
||||
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
|
||||
|
@ -79,7 +80,6 @@ public slots:
|
|||
void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
private slots:
|
||||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
|
@ -129,7 +129,7 @@ private:
|
|||
|
||||
void getTemporaryName(bool force = false);
|
||||
|
||||
static bool packetVersionMatch(const udt::Packet& packet);
|
||||
static bool isPacketVerified(const udt::Packet& packet);
|
||||
|
||||
bool resetAccountManagerAccessToken();
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||
const QString EDITORS_CAN_REPLACE_CONTENT_KEYPATH = "security.editors_can_replace_content";
|
||||
|
||||
qDebug() << "Previous domain-server settings version was"
|
||||
<< QString::number(oldVersion, 'g', 8) << "and the new version is"
|
||||
|
@ -294,6 +295,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
// persist the new config so the user config file has the correctly merged config
|
||||
persistToFile();
|
||||
}
|
||||
|
||||
if (oldVersion < 1.8) {
|
||||
unpackPermissions();
|
||||
// This was prior to addition of domain content replacement, add that to localhost permissions by default
|
||||
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canReplaceDomainContent);
|
||||
packPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
unpackPermissions();
|
||||
|
|
|
@ -18,6 +18,7 @@ find_package(Qt5LinguistTools REQUIRED)
|
|||
find_package(Qt5LinguistToolsMacros)
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -bigobj")
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
endif()
|
||||
|
@ -183,6 +184,12 @@ if (WIN32)
|
|||
add_dependency_external_projects(steamworks)
|
||||
endif()
|
||||
|
||||
# include OPENSSL
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
# append OpenSSL to our list of libraries to link
|
||||
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
|
||||
|
||||
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||
# This will prevent the following linker warnings
|
||||
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
|
||||
|
@ -309,9 +316,12 @@ else (APPLE)
|
|||
|
||||
# setup install of executable and things copied by fixup/windeployqt
|
||||
install(
|
||||
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
DESTINATION ${INTERFACE_INSTALL_DIR}
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
PATTERN "*.pdb" EXCLUDE
|
||||
PATTERN "*.lib" EXCLUDE
|
||||
PATTERN "*.exp" EXCLUDE
|
||||
)
|
||||
|
||||
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}")
|
||||
|
|
|
@ -126,24 +126,6 @@
|
|||
"weightVar": "headWeight",
|
||||
"weight": 4.0,
|
||||
"flexCoefficients": [1, 0.5, 0.25, 0.2, 0.1]
|
||||
},
|
||||
{
|
||||
"jointName": "LeftArm",
|
||||
"positionVar": "leftArmPosition",
|
||||
"rotationVar": "leftArmRotation",
|
||||
"typeVar": "leftArmType",
|
||||
"weightVar": "leftArmWeight",
|
||||
"weight": 0.75,
|
||||
"flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0]
|
||||
},
|
||||
{
|
||||
"jointName": "RightArm",
|
||||
"positionVar": "rightArmPosition",
|
||||
"rotationVar": "rightArmRotation",
|
||||
"typeVar": "rightArmType",
|
||||
"weightVar": "rightArmWeight",
|
||||
"weight": 0.75,
|
||||
"flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{ "comment" : "Mouse turn need to be small continuous increments",
|
||||
"from": { "makeAxis" : [
|
||||
[ "Keyboard.MouseMoveLeft" ],
|
||||
[ "Keyboard.MouseMoveRight" ]
|
||||
[ "Keyboard.MouseMoveRight" ]
|
||||
]
|
||||
},
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ],
|
||||
|
@ -31,8 +31,8 @@
|
|||
{ "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint",
|
||||
"from": { "makeAxis" : [
|
||||
[ "Keyboard.TouchpadLeft" ],
|
||||
[ "Keyboard.TouchpadRight" ]
|
||||
]
|
||||
[ "Keyboard.TouchpadRight" ]
|
||||
]
|
||||
},
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn" ],
|
||||
"to": "Actions.StepYaw",
|
||||
|
@ -131,6 +131,6 @@
|
|||
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
|
||||
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
|
||||
{ "from": "Keyboard.T", "to": "Actions.ACTION2" },
|
||||
{ "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" }
|
||||
{ "from": "Keyboard.Tab", "to": "Actions.ContextMenu" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -109,6 +109,23 @@
|
|||
|
||||
{ "from": "Standard.Head", "to": "Actions.Head" },
|
||||
{ "from": "Standard.LeftArm", "to": "Actions.LeftArm" },
|
||||
{ "from": "Standard.RightArm", "to": "Actions.RightArm" }
|
||||
{ "from": "Standard.RightArm", "to": "Actions.RightArm" },
|
||||
|
||||
{ "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" },
|
||||
{ "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" },
|
||||
{ "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" },
|
||||
{ "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" },
|
||||
{ "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" },
|
||||
{ "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" },
|
||||
{ "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" },
|
||||
{ "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" },
|
||||
{ "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" },
|
||||
{ "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" },
|
||||
{ "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" },
|
||||
{ "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" },
|
||||
{ "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" },
|
||||
{ "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" },
|
||||
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
|
||||
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -77,6 +77,23 @@
|
|||
{ "from": "Vive.Head", "to" : "Standard.Head"},
|
||||
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" },
|
||||
|
||||
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
|
||||
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
|
||||
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },
|
||||
{ "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" },
|
||||
{ "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" },
|
||||
{ "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" },
|
||||
{ "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" },
|
||||
{ "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" },
|
||||
{ "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" },
|
||||
{ "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" },
|
||||
{ "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" },
|
||||
{ "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" },
|
||||
{ "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" },
|
||||
{ "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" },
|
||||
{ "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" },
|
||||
{ "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" }
|
||||
]
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 312 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 232 KiB |
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 307 KiB |
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 268 KiB |
180
interface/resources/icons/tablet-icons/wallet-a.svg
Normal file
After Width: | Height: | Size: 52 KiB |
276
interface/resources/icons/tablet-icons/wallet-i.svg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
interface/resources/images/fadeMask.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
interface/resources/images/inspect-icon.png
Normal file
After Width: | Height: | Size: 27 KiB |
|
@ -39,7 +39,7 @@ Item {
|
|||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||
|
||||
profile: HFTabletWebEngineProfile;
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
|
|
@ -15,6 +15,11 @@ import "controls" as Controls
|
|||
|
||||
Controls.WebView {
|
||||
|
||||
// This is for JS/QML communication, which is unused in a Web3DOverlay,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
function onWebEventReceived(event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
|
|
|
@ -27,6 +27,7 @@ TreeView {
|
|||
|
||||
model: treeModel
|
||||
selection: ItemSelectionModel {
|
||||
id: selectionModel
|
||||
model: treeModel
|
||||
}
|
||||
|
||||
|
@ -215,6 +216,10 @@ TreeView {
|
|||
|
||||
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
|
||||
|
||||
onClicked: {
|
||||
selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect);
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
var path = scriptsModel.data(index, 0x100)
|
||||
if (path) {
|
||||
|
|
48
interface/resources/qml/controls-uit/WebGlyphButton.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// GlyphButton.qml
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 2017-06-21
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "../styles-uit"
|
||||
|
||||
Original.Button {
|
||||
id: control
|
||||
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string glyph: ""
|
||||
property int size: 32
|
||||
//colors
|
||||
readonly property color normalColor: "#AFAFAF"
|
||||
readonly property color hoverColor: "#00B4EF"
|
||||
readonly property color clickedColor: "#FFFFFF"
|
||||
readonly property color disabledColor: "#575757"
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Item {}
|
||||
|
||||
|
||||
label: HiFiGlyphs {
|
||||
color: control.enabled ? (control.pressed ? control.clickedColor :
|
||||
(control.hovered ? control.hoverColor : control.normalColor)) :
|
||||
control.disabledColor
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors {
|
||||
// Tweak horizontal alignment so that it looks right.
|
||||
left: parent.left
|
||||
leftMargin: -0.5
|
||||
}
|
||||
text: control.glyph
|
||||
size: control.size
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ Item {
|
|||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||
|
||||
profile: HFTabletWebEngineProfile;
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Item {
|
|||
property alias webView: webview
|
||||
property alias profile: webview.profile
|
||||
property bool remove: false
|
||||
property bool closeButtonVisible: true
|
||||
|
||||
// Manage own browse history because WebEngineView history is wiped when a new URL is loaded via
|
||||
// onNewViewRequested, e.g., as happens when a social media share button is clicked.
|
||||
|
@ -64,6 +65,7 @@ Item {
|
|||
disabledColor: hifi.colors.lightGrayText
|
||||
enabled: true
|
||||
text: "CLOSE"
|
||||
visible: closeButtonVisible
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
@ -142,7 +144,7 @@ Item {
|
|||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
|
||||
anchors.top: buttons.bottom
|
||||
profile: HFTabletWebEngineProfile;
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
@ -182,8 +184,6 @@ Item {
|
|||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
|
|
|
@ -73,7 +73,6 @@ Item {
|
|||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
|
|
|
@ -41,6 +41,11 @@ FocusScope {
|
|||
// when they're opened.
|
||||
signal showDesktop();
|
||||
|
||||
// This is for JS/QML communication, which is unused in the Desktop,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
// Allows QML/JS to find the desktop through the parent chain
|
||||
property bool desktopRoot: true
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ Item {
|
|||
property int stackShadowNarrowing: 5;
|
||||
property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif");
|
||||
property int shadowHeight: 10;
|
||||
property bool hovered: false
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
function pastTime(timestamp) { // Answer a descriptive string
|
||||
|
@ -231,6 +233,13 @@ Item {
|
|||
// to that which is being hovered over.
|
||||
property var hoverThunk: function () { };
|
||||
property var unhoverThunk: function () { };
|
||||
Rectangle {
|
||||
anchors.fill: parent;
|
||||
visible: root.hovered
|
||||
color: "transparent";
|
||||
border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight;
|
||||
z: 1;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
acceptedButtons: Qt.LeftButton;
|
||||
|
|
158
interface/resources/qml/hifi/DesktopLetterboxMessage.qml
Normal file
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// LetterboxMessage.qml
|
||||
// qml/hifi
|
||||
//
|
||||
// Created by Dante Ruiz on 7/21/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../styles-uit"
|
||||
|
||||
Item {
|
||||
property alias text: popupText.text
|
||||
property alias headerGlyph: headerGlyph.text
|
||||
property alias headerText: headerText.text
|
||||
property alias headerGlyphSize: headerGlyph.size
|
||||
property real popupRadius: hifi.dimensions.borderRadius
|
||||
property real headerTextPixelSize: 22
|
||||
property real popupTextPixelSize: 16
|
||||
property real headerTextMargin: -5
|
||||
property real headerGlyphMargin: -15
|
||||
property bool isDesktop: false
|
||||
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
||||
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||
visible: false
|
||||
id: letterbox
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
id: textContainer;
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
radius: popupRadius
|
||||
color: "white"
|
||||
Item {
|
||||
id: contentContainer
|
||||
width: parent.width - 50
|
||||
height: childrenRect.height
|
||||
anchors.centerIn: parent
|
||||
Item {
|
||||
id: popupHeaderContainer
|
||||
visible: headerText.text !== "" || headerGlyph.text !== ""
|
||||
height: 30
|
||||
// Anchors
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
// Header Glyph
|
||||
HiFiGlyphs {
|
||||
id: headerGlyph
|
||||
visible: headerGlyph.text !== ""
|
||||
// Size
|
||||
height: parent.height
|
||||
// Anchors
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: headerGlyphMargin
|
||||
// Text Size
|
||||
size: headerTextPixelSize*2.5
|
||||
// Style
|
||||
horizontalAlignment: Text.AlignHLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: hifi.colors.darkGray
|
||||
}
|
||||
// Header Text
|
||||
Text {
|
||||
id: headerText
|
||||
visible: headerText.text !== ""
|
||||
// Size
|
||||
|
||||
height: parent.height
|
||||
// Anchors
|
||||
anchors.left: headerGlyph.right
|
||||
anchors.leftMargin: headerTextMargin
|
||||
// Text Size
|
||||
font.pixelSize: headerTextPixelSize
|
||||
// Style
|
||||
font.family: ralewaySemiBold.name
|
||||
color: hifi.colors.darkGray
|
||||
horizontalAlignment: Text.AlignHLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
}
|
||||
// Popup Text
|
||||
Text {
|
||||
id: popupText
|
||||
// Size
|
||||
width: parent.width
|
||||
// Anchors
|
||||
anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.bottom : parent.top
|
||||
anchors.topMargin: popupHeaderContainer.visible ? 15 : 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
// Text alignment
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHLeft
|
||||
// Style
|
||||
font.pixelSize: popupTextPixelSize
|
||||
font.family: ralewayRegular.name
|
||||
color: hifi.colors.darkGray
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.StyledText
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Left gray MouseArea
|
||||
MouseArea {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: textContainer.left;
|
||||
anchors.top: textContainer.top;
|
||||
anchors.bottom: textContainer.bottom;
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
letterbox.visible = false
|
||||
}
|
||||
}
|
||||
// Right gray MouseArea
|
||||
MouseArea {
|
||||
anchors.left: textContainer.left;
|
||||
anchors.right: parent.left;
|
||||
anchors.top: textContainer.top;
|
||||
anchors.bottom: textContainer.bottom;
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
letterbox.visible = false
|
||||
}
|
||||
}
|
||||
// Top gray MouseArea
|
||||
MouseArea {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: textContainer.top;
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
letterbox.visible = false
|
||||
}
|
||||
}
|
||||
// Bottom gray MouseArea
|
||||
MouseArea {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: textContainer.bottom;
|
||||
anchors.bottom: parent.bottom;
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
letterbox.visible = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@ Column {
|
|||
property string metaverseServerUrl: '';
|
||||
property string actions: 'snapshot';
|
||||
// sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick.
|
||||
Component.onCompleted: delay.start();
|
||||
property string labelText: actions;
|
||||
property string filter: '';
|
||||
onFilterChanged: filterChoicesByText();
|
||||
|
@ -125,11 +124,9 @@ Column {
|
|||
cb();
|
||||
});
|
||||
}
|
||||
property var delay: Timer { // No setTimeout or nextTick in QML.
|
||||
interval: 0;
|
||||
onTriggered: fillDestinations();
|
||||
}
|
||||
function fillDestinations() { // Public
|
||||
console.debug('Feed::fillDestinations()')
|
||||
|
||||
function report(label, error) {
|
||||
console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
|
||||
}
|
||||
|
@ -206,9 +203,9 @@ Column {
|
|||
id: scroll;
|
||||
model: suggestions;
|
||||
orientation: ListView.Horizontal;
|
||||
highlightFollowsCurrentItem: false
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight; z: 1; }
|
||||
currentIndex: -1;
|
||||
|
||||
spacing: 12;
|
||||
|
@ -237,9 +234,8 @@ Column {
|
|||
textSizeSmall: root.textSizeSmall;
|
||||
stackShadowNarrowing: root.stackShadowNarrowing;
|
||||
shadowHeight: root.stackedCardShadowHeight;
|
||||
|
||||
hoverThunk: function () { scrollToIndex(index); }
|
||||
unhoverThunk: function () { scrollToIndex(-1); }
|
||||
hoverThunk: function () { hovered = true }
|
||||
unhoverThunk: function () { hovered = false }
|
||||
}
|
||||
}
|
||||
NumberAnimation {
|
||||
|
|
|
@ -17,9 +17,12 @@ Item {
|
|||
property alias text: popupText.text
|
||||
property alias headerGlyph: headerGlyph.text
|
||||
property alias headerText: headerText.text
|
||||
property alias headerGlyphSize: headerGlyph.size
|
||||
property real popupRadius: hifi.dimensions.borderRadius
|
||||
property real headerTextPixelSize: 22
|
||||
property real popupTextPixelSize: 16
|
||||
property real headerTextMargin: -5
|
||||
property real headerGlyphMargin: -15
|
||||
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
||||
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||
visible: false
|
||||
|
@ -59,7 +62,7 @@ Item {
|
|||
height: parent.height
|
||||
// Anchors
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -15
|
||||
anchors.leftMargin: headerGlyphMargin
|
||||
// Text Size
|
||||
size: headerTextPixelSize*2.5
|
||||
// Style
|
||||
|
@ -75,7 +78,7 @@ Item {
|
|||
height: parent.height
|
||||
// Anchors
|
||||
anchors.left: headerGlyph.right
|
||||
anchors.leftMargin: -5
|
||||
anchors.leftMargin: headerTextMargin
|
||||
// Text Size
|
||||
font.pixelSize: headerTextPixelSize
|
||||
// Style
|
||||
|
|
173
interface/resources/qml/hifi/SkyboxChanger.qml
Normal file
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// skyboxchanger.qml
|
||||
//
|
||||
//
|
||||
// Created by Cain Kilgore on 9th August 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "Skybox Changer";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewaySemiBold {
|
||||
id: titleBarDesc;
|
||||
text: "Click an image to choose a new Skybox.";
|
||||
wrapMode: Text.Wrap
|
||||
// Text size
|
||||
size: 14;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.top: titleBarText.bottom
|
||||
anchors.leftMargin: 16;
|
||||
anchors.rightMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// This RowLayout could be a GridLayout instead for further expandability.
|
||||
// As this SkyboxChanger task only required 6 images, implementing GridLayout wasn't necessary.
|
||||
// In the future if this is to be expanded to add more Skyboxes, it might be worth changing this.
|
||||
RowLayout {
|
||||
id: row1
|
||||
anchors.top: titleBarContainer.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
Layout.fillWidth: true
|
||||
anchors.topMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg"
|
||||
clip: true
|
||||
id: preview1
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg'});
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_2.jpg"
|
||||
clip: true
|
||||
id: preview2
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/2.png'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
id: row2
|
||||
anchors.top: row1.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
Layout.fillWidth: true
|
||||
anchors.leftMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_3.jpg"
|
||||
clip: true
|
||||
id: preview3
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/3.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_4.jpg"
|
||||
clip: true
|
||||
id: preview4
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/4.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
id: row3
|
||||
anchors.top: row2.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
Layout.fillWidth: true
|
||||
anchors.leftMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_5.jpg"
|
||||
clip: true
|
||||
id: preview5
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/5.png'});
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_6.jpg"
|
||||
clip: true
|
||||
id: preview6
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/6.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
||||
}
|
253
interface/resources/qml/hifi/WebBrowser.qml
Normal file
|
@ -0,0 +1,253 @@
|
|||
//
|
||||
// WebBrowser.qml
|
||||
//
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 06/22/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.5 as QQControls
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import QtWebEngine 1.2
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import "../styles-uit"
|
||||
import "../controls-uit" as HifiControls
|
||||
import "../windows"
|
||||
import "../controls"
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
property string title: "";
|
||||
signal sendToScript(var message);
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
// only show the title if loaded through a "loader"
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: parent.width;
|
||||
|
||||
RowLayout {
|
||||
width: parent.width;
|
||||
height: 48
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webEngineView.canGoBack
|
||||
glyph: hifi.glyphs.backward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
webEngineView.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webEngineView.canGoForward
|
||||
glyph: hifi.glyphs.forward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
webEngineView.goForward()
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.TextField {
|
||||
id: addressBar
|
||||
|
||||
Image {
|
||||
anchors.verticalCenter: addressBar.verticalCenter;
|
||||
x: 5
|
||||
z: 2
|
||||
id: faviconImage
|
||||
width: 16; height: 16
|
||||
sourceSize: Qt.size(width, height)
|
||||
source: webEngineView.icon
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
z: 2
|
||||
x: addressBar.width - 28
|
||||
onClicked: {
|
||||
if (webEngineView.loading) {
|
||||
webEngineView.stop()
|
||||
} else {
|
||||
reloadTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
style: TextFieldStyle {
|
||||
padding {
|
||||
left: 26;
|
||||
right: 26
|
||||
}
|
||||
}
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
text: webEngineView.url
|
||||
onAccepted: webEngineView.url = text
|
||||
}
|
||||
HifiControls.WebGlyphButton {
|
||||
checkable: true
|
||||
//only QtWebEngine 1.3
|
||||
//checked: webEngineView.audioMuted
|
||||
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
onClicked: {
|
||||
webEngineView.triggerWebAction(WebEngineView.ToggleMediaMute)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.ProgressBar {
|
||||
id: loadProgressBar
|
||||
style: ProgressBarStyle {
|
||||
background: Rectangle {
|
||||
color: "#6A6A6A"
|
||||
}
|
||||
progress: Rectangle{
|
||||
color: "#00B4EF"
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width;
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
value: webEngineView.loadProgress
|
||||
height: 2
|
||||
}
|
||||
|
||||
HifiControls.BaseWebView {
|
||||
id: webEngineView
|
||||
focus: true
|
||||
objectName: "tabletWebEngineView"
|
||||
|
||||
url: "http://www.highfidelity.com"
|
||||
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
|
||||
|
||||
width: parent.width;
|
||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: webEngineView.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
settings.autoLoadImages: true
|
||||
settings.javascriptEnabled: true
|
||||
settings.errorPageEnabled: true
|
||||
settings.pluginsEnabled: true
|
||||
settings.fullScreenSupportEnabled: false
|
||||
//from WebEngine 1.3
|
||||
// settings.autoLoadIconsForPage: false
|
||||
// settings.touchIconsEnabled: false
|
||||
|
||||
onCertificateError: {
|
||||
error.defer();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
webEngineView.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
if (!request.userInitiated) {
|
||||
print("Warning: Blocked a popup window.");
|
||||
}
|
||||
}
|
||||
|
||||
onRenderProcessTerminated: {
|
||||
var status = "";
|
||||
switch (terminationStatus) {
|
||||
case WebEngineView.NormalTerminationStatus:
|
||||
status = "(normal exit)";
|
||||
break;
|
||||
case WebEngineView.AbnormalTerminationStatus:
|
||||
status = "(abnormal exit)";
|
||||
break;
|
||||
case WebEngineView.CrashedTerminationStatus:
|
||||
status = "(crashed)";
|
||||
break;
|
||||
case WebEngineView.KilledTerminationStatus:
|
||||
status = "(killed)";
|
||||
break;
|
||||
}
|
||||
|
||||
print("Render process exited with code " + exitCode + " " + status);
|
||||
reloadTimer.running = true;
|
||||
}
|
||||
|
||||
onWindowCloseRequested: {
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: reloadTimer
|
||||
interval: 0
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: webEngineView.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ Rectangle {
|
|||
|
||||
// only show the title if loaded through a "loader"
|
||||
function showTitle() {
|
||||
return root.parent.objectName == "loader";
|
||||
return (root.parent !== null) && root.parent.objectName == "loader";
|
||||
}
|
||||
|
||||
Column {
|
||||
|
|
|
@ -27,7 +27,7 @@ Rectangle {
|
|||
|
||||
color: "#00000000";
|
||||
border {
|
||||
width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0;
|
||||
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
|
||||
color: colors.border;
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ Rectangle {
|
|||
drag.target: dragTarget;
|
||||
}
|
||||
|
||||
Item {
|
||||
QtObject {
|
||||
id: colors;
|
||||
|
||||
readonly property string unmuted: "#FFF";
|
||||
|
@ -72,7 +72,7 @@ Rectangle {
|
|||
readonly property string red: colors.muted;
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted;
|
||||
readonly property string icon: Audio.muted ? muted : unmuted;
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -92,10 +92,8 @@ Rectangle {
|
|||
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
|
||||
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
|
||||
|
||||
function exclusiveOr(a, b) { return (a || b) && !(a && b); }
|
||||
|
||||
id: image;
|
||||
source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon;
|
||||
source: Audio.muted ? mutedIcon : unmutedIcon;
|
||||
|
||||
width: 30;
|
||||
height: 30;
|
||||
|
@ -118,9 +116,9 @@ Rectangle {
|
|||
Item {
|
||||
id: status;
|
||||
|
||||
readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted;
|
||||
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
|
||||
|
||||
visible: Audio.muted || mouseArea.containsMouse;
|
||||
visible: Audio.muted;
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
|
@ -133,14 +131,14 @@ Rectangle {
|
|||
|
||||
Text {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
color: parent.color;
|
||||
|
||||
text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE";
|
||||
font.pointSize: 12;
|
||||
text: Audio.muted ? "MUTED" : "MUTE";
|
||||
font.pointSize: 12;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -150,7 +148,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
width: 50;
|
||||
height: 4;
|
||||
height: 4;
|
||||
color: parent.color;
|
||||
}
|
||||
|
||||
|
@ -161,7 +159,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
width: 50;
|
||||
height: 4;
|
||||
height: 4;
|
||||
color: parent.color;
|
||||
}
|
||||
}
|
||||
|
|
463
interface/resources/qml/hifi/commerce/Checkout.qml
Normal file
|
@ -0,0 +1,463 @@
|
|||
//
|
||||
// Checkout.qml
|
||||
// qml/hifi/commerce
|
||||
//
|
||||
// Checkout
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-07
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
import "./wallet" as HifiWallet
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: checkoutRoot;
|
||||
property bool inventoryReceived: false;
|
||||
property bool balanceReceived: false;
|
||||
property string itemId: "";
|
||||
property string itemHref: "";
|
||||
property int balanceAfterPurchase: 0;
|
||||
property bool alreadyOwned: false;
|
||||
property int itemPriceFull: 0;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onBuyResult: {
|
||||
if (result.status !== 'success') {
|
||||
buyButton.text = result.message;
|
||||
buyButton.enabled = false;
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||
}
|
||||
}
|
||||
onBalanceResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get balance", result.message);
|
||||
} else {
|
||||
balanceReceived = true;
|
||||
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||
balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
|
||||
}
|
||||
}
|
||||
onInventoryResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get inventory", result.message);
|
||||
} else {
|
||||
inventoryReceived = true;
|
||||
console.log('inventory fixme', JSON.stringify(result));
|
||||
if (inventoryContains(result.data.assets, itemId)) {
|
||||
alreadyOwned = true;
|
||||
} else {
|
||||
alreadyOwned = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: checkoutRoot.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "Checkout";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
||||
//
|
||||
// ITEM DESCRIPTION START
|
||||
//
|
||||
Item {
|
||||
id: itemDescriptionContainer;
|
||||
// Size
|
||||
width: checkoutRoot.width;
|
||||
height: childrenRect.height + 20;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
|
||||
// Item Name text
|
||||
Item {
|
||||
id: itemNameContainer;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: itemNameTextLabel;
|
||||
text: "Item Name:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: itemNameText;
|
||||
// Text size
|
||||
size: itemNameTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: itemNameTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Item Author text
|
||||
Item {
|
||||
id: itemAuthorContainer;
|
||||
// Anchors
|
||||
anchors.top: itemNameContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: itemAuthorTextLabel;
|
||||
text: "Item Author:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: itemAuthorText;
|
||||
// Text size
|
||||
size: itemAuthorTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: itemAuthorTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// HFC Balance text
|
||||
Item {
|
||||
id: hfcBalanceContainer;
|
||||
// Anchors
|
||||
anchors.top: itemAuthorContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: hfcBalanceTextLabel;
|
||||
text: "HFC Balance:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: hfcBalanceText;
|
||||
text: "--";
|
||||
// Text size
|
||||
size: hfcBalanceTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: hfcBalanceTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Item Price text
|
||||
Item {
|
||||
id: itemPriceContainer;
|
||||
// Anchors
|
||||
anchors.top: hfcBalanceContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: itemPriceTextLabel;
|
||||
text: "Item Price:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: itemPriceText;
|
||||
// Text size
|
||||
size: itemPriceTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: itemPriceTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// HFC "Balance After Purchase" text
|
||||
Item {
|
||||
id: hfcBalanceAfterPurchaseContainer;
|
||||
// Anchors
|
||||
anchors.top: itemPriceContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: hfcBalanceAfterPurchaseTextLabel;
|
||||
text: "HFC Balance After Purchase:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: hfcBalanceAfterPurchaseText;
|
||||
text: balanceAfterPurchase;
|
||||
// Text size
|
||||
size: hfcBalanceAfterPurchaseTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: hfcBalanceAfterPurchaseTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: (balanceAfterPurchase >= 0) ? hifi.colors.lightGrayText : hifi.colors.redHighlight;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// ITEM DESCRIPTION END
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
Item {
|
||||
id: actionButtonsContainer;
|
||||
// Size
|
||||
width: checkoutRoot.width;
|
||||
height: 40;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendToScript({method: 'checkout_cancelClicked', params: itemId});
|
||||
}
|
||||
}
|
||||
|
||||
// "Buy" button
|
||||
HifiControlsUit.Button {
|
||||
id: buyButton;
|
||||
enabled: balanceAfterPurchase >= 0 && inventoryReceived && balanceReceived;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
|
||||
onClicked: {
|
||||
if (!alreadyOwned) {
|
||||
commerce.buy(itemId, parseFloat(itemPriceText.text*100));
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// ACTION BUTTONS END
|
||||
//
|
||||
|
||||
//
|
||||
// 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 'updateCheckoutQML':
|
||||
itemId = message.params.itemId;
|
||||
itemNameText.text = message.params.itemName;
|
||||
itemAuthorText.text = message.params.itemAuthor;
|
||||
checkoutRoot.itemPriceFull = message.params.itemPrice;
|
||||
itemPriceText.text = parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
|
||||
itemHref = message.params.itemHref;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendToScript(var message);
|
||||
|
||||
function inventoryContains(inventoryJson, id) {
|
||||
for (var idx = 0; idx < inventoryJson.length; idx++) {
|
||||
if(inventoryJson[idx].id === id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
280
interface/resources/qml/hifi/commerce/Inventory.qml
Normal file
|
@ -0,0 +1,280 @@
|
|||
//
|
||||
// Inventory.qml
|
||||
// qml/hifi/commerce
|
||||
//
|
||||
// Inventory
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-10
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
import "./wallet" as HifiWallet
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: inventoryRoot;
|
||||
property string referrerURL: "";
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onBalanceResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get balance", result.message);
|
||||
} else {
|
||||
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||
}
|
||||
}
|
||||
onInventoryResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get inventory", result.message);
|
||||
} else {
|
||||
inventoryContentsList.model = result.data.assets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "Inventory";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
||||
//
|
||||
// HFC BALANCE START
|
||||
//
|
||||
Item {
|
||||
id: hfcBalanceContainer;
|
||||
// Size
|
||||
width: inventoryRoot.width;
|
||||
height: childrenRect.height + 20;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 4;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: hfcBalanceTextLabel;
|
||||
text: "HFC Balance:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayRegular {
|
||||
id: hfcBalanceText;
|
||||
text: "--";
|
||||
// Text size
|
||||
size: hfcBalanceTextLabel.size;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: hfcBalanceTextLabel.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
//
|
||||
// HFC BALANCE END
|
||||
//
|
||||
|
||||
//
|
||||
// INVENTORY CONTENTS START
|
||||
//
|
||||
Item {
|
||||
id: inventoryContentsContainer;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
anchors.top: hfcBalanceContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.bottom: actionButtonsContainer.top;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: inventoryContentsLabel;
|
||||
text: "Inventory:";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
width: paintedWidth;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
ListView {
|
||||
id: inventoryContentsList;
|
||||
clip: true;
|
||||
// Anchors
|
||||
anchors.top: inventoryContentsLabel.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: 30;
|
||||
RalewayRegular {
|
||||
id: thisItemId;
|
||||
// Text size
|
||||
size: 20;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
text: modelData.title;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
sendToScript({method: 'inventory_itemClicked', itemId: modelData.id});
|
||||
}
|
||||
onEntered: {
|
||||
thisItemId.color = hifi.colors.blueHighlight;
|
||||
}
|
||||
onExited: {
|
||||
thisItemId.color = hifi.colors.blueAccent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// INVENTORY CONTENTS END
|
||||
//
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
Item {
|
||||
id: actionButtonsContainer;
|
||||
// Size
|
||||
width: inventoryRoot.width;
|
||||
height: 40;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
// "Back" button
|
||||
HifiControlsUit.Button {
|
||||
id: backButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Back"
|
||||
onClicked: {
|
||||
sendToScript({method: 'inventory_backClicked', referrerURL: referrerURL});
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// ACTION BUTTONS END
|
||||
//
|
||||
|
||||
//
|
||||
// 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 'updateInventory':
|
||||
referrerURL = message.referrerURL;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendToScript(var message);
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
BIN
interface/resources/qml/hifi/commerce/images/01cat.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
interface/resources/qml/hifi/commerce/images/02car.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
interface/resources/qml/hifi/commerce/images/03dog.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
interface/resources/qml/hifi/commerce/images/04stars.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
interface/resources/qml/hifi/commerce/images/05plane.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
interface/resources/qml/hifi/commerce/images/06gingerbread.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
73
interface/resources/qml/hifi/commerce/wallet/Help.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// SendMoney.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// SendMoney
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
}
|
||||
|
||||
// "Unavailable"
|
||||
RalewayRegular {
|
||||
text: "Help me!";
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
127
interface/resources/qml/hifi/commerce/wallet/NotSetUp.qml
Normal file
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// NotSetUp.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// NotSetUp
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
}
|
||||
|
||||
//
|
||||
// TAB CONTENTS START
|
||||
//
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: notSetUpText;
|
||||
text: "Your Wallet Account Has Not Been Set Up";
|
||||
// Text size
|
||||
size: 22;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 100;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Explanitory text
|
||||
RalewayRegular {
|
||||
text: "To buy and sell items in High Fidelity Coin (HFC), you first need " +
|
||||
"to set up your wallet.<br><b>You do not need to submit a credit card or personal information to set up your wallet.</b>";
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: notSetUpText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
height: 100;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "Set Up" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 150;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
width: parent.width/2;
|
||||
height: 50;
|
||||
text: "Set Up My Wallet";
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'setUpClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TAB CONTENTS END
|
||||
//
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
//
|
||||
// PassphraseSelection.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// PassphraseSelection
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onSecurityImageResult: {
|
||||
passphrasePageSecurityImage.source = "";
|
||||
passphrasePageSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
|
||||
onPassphraseSetupStatusResult: {
|
||||
sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup});
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
passphraseField.focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: gridModel;
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: passphraseField;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 30;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
width: 280;
|
||||
height: 50;
|
||||
echoMode: TextInput.Password;
|
||||
placeholderText: "passphrase";
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: passphraseFieldAgain;
|
||||
anchors.top: passphraseField.bottom;
|
||||
anchors.topMargin: 10;
|
||||
anchors.left: passphraseField.left;
|
||||
anchors.right: passphraseField.right;
|
||||
height: 50;
|
||||
echoMode: TextInput.Password;
|
||||
placeholderText: "re-enter passphrase";
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Security Image
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
// Anchors
|
||||
anchors.top: passphraseField.top;
|
||||
anchors.left: passphraseField.right;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.right: parent.right;
|
||||
Image {
|
||||
id: passphrasePageSecurityImage;
|
||||
anchors.top: parent.top;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: 75;
|
||||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
// "Security picture" text below pic
|
||||
RalewayRegular {
|
||||
text: "security picture";
|
||||
// Text size
|
||||
size: 12;
|
||||
// Anchors
|
||||
anchors.top: passphrasePageSecurityImage.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: securityImageContainer.left;
|
||||
anchors.right: securityImageContainer.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Error text below TextFields
|
||||
RalewaySemiBold {
|
||||
id: errorText;
|
||||
text: "";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: passphraseFieldAgain.bottom;
|
||||
anchors.topMargin: 0;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 30;
|
||||
// Style
|
||||
color: hifi.colors.redHighlight;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Text below TextFields
|
||||
RalewaySemiBold {
|
||||
id: passwordReqs;
|
||||
text: "Passphrase must be at least 4 characters";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: passphraseFieldAgain.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 30;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Show passphrase text
|
||||
HifiControlsUit.CheckBox {
|
||||
id: showPassphrase;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.top: passwordReqs.bottom;
|
||||
anchors.topMargin: 16;
|
||||
height: 30;
|
||||
text: "Show passphrase as plain text";
|
||||
boxSize: 24;
|
||||
onClicked: {
|
||||
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
|
||||
passphraseFieldAgain.echoMode = checked ? TextInput.Normal : TextInput.Password;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below checkbox
|
||||
RalewayRegular {
|
||||
text: "Your passphrase is used to encrypt your private keys. <b>Please write it down.</b> If it is lost, you will not be able to recover it.";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: showPassphrase.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
function validateAndSubmitPassphrase() {
|
||||
if (passphraseField.text.length < 4) {
|
||||
setErrorText("Passphrase too short.");
|
||||
return false;
|
||||
} else if (passphraseField.text !== passphraseFieldAgain.text) {
|
||||
setErrorText("Passphrases don't match.");
|
||||
return false;
|
||||
} else {
|
||||
setErrorText("");
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function setErrorText(text) {
|
||||
errorText.text = text;
|
||||
}
|
||||
|
||||
signal sendMessageToLightbox(var msg);
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
//
|
||||
// PassphraseSelectionLightbox.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// PassphraseSelectionLightbox
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
root.resetSubmitButton();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: passphraseSelection;
|
||||
onSendMessageToLightbox: {
|
||||
if (msg.method === 'statusResult') {
|
||||
if (msg.status) {
|
||||
// Success submitting new passphrase
|
||||
root.resetSubmitButton();
|
||||
root.visible = false;
|
||||
} else {
|
||||
// Error submitting new passphrase
|
||||
root.resetSubmitButton();
|
||||
passphraseSelection.setErrorText("Backend error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SECURE PASSPHRASE SELECTION START
|
||||
//
|
||||
Item {
|
||||
id: choosePassphraseContainer;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
Item {
|
||||
id: passphraseTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "CHANGE PASSPHRASE";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: passphraseTitleHelper;
|
||||
text: "Choose a Secure Passphrase";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: passphraseTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
PassphraseSelection {
|
||||
id: passphraseSelection;
|
||||
anchors.top: passphraseTitleHelper.bottom;
|
||||
anchors.topMargin: 30;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: passphraseNavBar.top;
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
id: passphraseNavBar;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 100;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// "Submit" button
|
||||
HifiControlsUit.Button {
|
||||
id: passphraseSubmitButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 100;
|
||||
text: "Submit";
|
||||
onClicked: {
|
||||
if (passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
passphraseSubmitButton.text = "Submitting...";
|
||||
passphraseSubmitButton.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// SECURE PASSPHRASE SELECTION END
|
||||
//
|
||||
|
||||
function resetSubmitButton() {
|
||||
passphraseSubmitButton.enabled = true;
|
||||
passphraseSubmitButton.text = "Submit";
|
||||
}
|
||||
}
|
322
interface/resources/qml/hifi/commerce/wallet/Security.qml
Normal file
|
@ -0,0 +1,322 @@
|
|||
//
|
||||
// Security.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// Security
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (exists) { // "If security image is set up"
|
||||
var path = "image://security/securityImage";
|
||||
topSecurityImage.source = "";
|
||||
topSecurityImage.source = path;
|
||||
changeSecurityImageImage.source = "";
|
||||
changeSecurityImageImage.source = path;
|
||||
changePassphraseImage.source = "";
|
||||
changePassphraseImage.source = path;
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathResult: {
|
||||
if (path !== "") {
|
||||
keyFilePath.text = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
// Username Text
|
||||
RalewayRegular {
|
||||
id: usernameText;
|
||||
text: Account.username;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
elide: Text.ElideRight;
|
||||
// Anchors
|
||||
anchors.top: securityImageContainer.top;
|
||||
anchors.bottom: securityImageContainer.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: securityImageContainer.left;
|
||||
}
|
||||
|
||||
// Security Image
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.right: parent.right;
|
||||
width: 75;
|
||||
height: childrenRect.height;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: topSecurityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: parent.width - 10;
|
||||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
}
|
||||
// "Security picture" text below pic
|
||||
RalewayRegular {
|
||||
text: "security picture";
|
||||
// Text size
|
||||
size: 12;
|
||||
// Anchors
|
||||
anchors.top: topSecurityImage.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: securityImageContainer.left;
|
||||
anchors.right: securityImageContainer.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: securityContainer;
|
||||
anchors.top: securityImageContainer.bottom;
|
||||
anchors.topMargin: 20;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewayRegular {
|
||||
id: securityText;
|
||||
text: "Security";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: changePassphraseContainer;
|
||||
anchors.top: securityText.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 75;
|
||||
|
||||
Image {
|
||||
id: changePassphraseImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
height: parent.height;
|
||||
width: height;
|
||||
source: "image://security/securityImage";
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
}
|
||||
// "Change Passphrase" button
|
||||
HifiControlsUit.Button {
|
||||
id: changePassphraseButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: changePassphraseImage.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: 250;
|
||||
height: 50;
|
||||
text: "Change My Passphrase";
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSecurity_changePassphrase'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: changeSecurityImageContainer;
|
||||
anchors.top: changePassphraseContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 75;
|
||||
|
||||
Image {
|
||||
id: changeSecurityImageImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
height: parent.height;
|
||||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
source: "image://security/securityImage";
|
||||
}
|
||||
// "Change Security Image" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeSecurityImageButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: changeSecurityImageImage.right;
|
||||
anchors.leftMargin: 16;
|
||||
width: 250;
|
||||
height: 50;
|
||||
text: "Change My Security Image";
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: yourPrivateKeysContainer;
|
||||
anchors.top: securityContainer.bottom;
|
||||
anchors.topMargin: 20;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: yourPrivateKeysText;
|
||||
text: "Your Private Keys";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
}
|
||||
|
||||
// Text below "your private keys"
|
||||
RalewayRegular {
|
||||
id: explanitoryText;
|
||||
text: "Your money and purchases are secured with private keys that only you " +
|
||||
"have access to. <b>If they are lost, you will not be able to access your money or purchases. " +
|
||||
"To safeguard your private keys, back up this file regularly:</b>";
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: yourPrivateKeysText.bottom;
|
||||
anchors.topMargin: 10;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
HifiControlsUit.TextField {
|
||||
id: keyFilePath;
|
||||
anchors.top: explanitoryText.bottom;
|
||||
anchors.topMargin: 10;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: clipboardButton.left;
|
||||
height: 40;
|
||||
readOnly: true;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getKeyFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: clipboardButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: keyFilePath.top;
|
||||
anchors.bottom: keyFilePath.bottom;
|
||||
width: height;
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.question;
|
||||
// Size
|
||||
size: parent.height*1.3;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Style
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
Window.copyToClipboard(keyFilePath.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// SecurityImageModel.qml
|
||||
// qml/hifi/commerce
|
||||
//
|
||||
// SecurityImageModel
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-17
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
|
||||
ListModel {
|
||||
id: root;
|
||||
ListElement{
|
||||
sourcePath: "images/01cat.jpg"
|
||||
securityImageEnumValue: 1;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/02car.jpg"
|
||||
securityImageEnumValue: 2;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/03dog.jpg"
|
||||
securityImageEnumValue: 3;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/04stars.jpg"
|
||||
securityImageEnumValue: 4;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/05plane.jpg"
|
||||
securityImageEnumValue: 5;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/06gingerbread.jpg"
|
||||
securityImageEnumValue: 6;
|
||||
}
|
||||
|
||||
function getImagePathFromImageID(imageID) {
|
||||
return (imageID ? root.get(imageID - 1).sourcePath : "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// SecurityImageSelection.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// SecurityImageSelection
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-17
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onSecurityImageResult: {
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: gridModel;
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: securityImageGrid;
|
||||
clip: true;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
currentIndex: -1;
|
||||
cellWidth: width / 3;
|
||||
cellHeight: height / 2;
|
||||
model: gridModel;
|
||||
delegate: Item {
|
||||
width: securityImageGrid.cellWidth;
|
||||
height: securityImageGrid.cellHeight;
|
||||
Item {
|
||||
anchors.fill: parent;
|
||||
Image {
|
||||
width: parent.width - 8;
|
||||
height: parent.height - 8;
|
||||
source: sourcePath;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
onClicked: {
|
||||
securityImageGrid.currentIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
highlight: Rectangle {
|
||||
width: securityImageGrid.cellWidth;
|
||||
height: securityImageGrid.cellHeight;
|
||||
color: hifi.colors.blueHighlight;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
signal sendToScript(var message);
|
||||
|
||||
function getImagePathFromImageID(imageID) {
|
||||
return (imageID ? gridModel.getImagePathFromImageID(imageID) : "");
|
||||
}
|
||||
|
||||
function getSelectedImageIndex() {
|
||||
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// SecurityImageSelectionLightbox.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// SecurityImageSelectionLightbox
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property bool justSubmitted: false;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
root.resetSubmitButton();
|
||||
}
|
||||
}
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (exists) { // Success submitting new security image
|
||||
if (root.justSubmitted) {
|
||||
root.resetSubmitButton();
|
||||
root.visible = false;
|
||||
root.justSubmitted = false;
|
||||
}
|
||||
} else if (root.justSubmitted) {
|
||||
// Error submitting new security image.
|
||||
root.resetSubmitButton();
|
||||
root.justSubmitted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SECURITY IMAGE SELECTION START
|
||||
//
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
Item {
|
||||
id: securityImageTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "CHANGE SECURITY IMAGE";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: securityImageTitleHelper;
|
||||
text: "Choose a Security Picture:";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: securityImageTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: 50;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
// Anchors
|
||||
anchors.top: securityImageTitleHelper.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 280;
|
||||
}
|
||||
|
||||
// Text below security images
|
||||
RalewayRegular {
|
||||
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: securityImageSelection.bottom;
|
||||
anchors.topMargin: 40;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
id: securityImageNavBar;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 100;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// "Submit" button
|
||||
HifiControlsUit.Button {
|
||||
id: securityImageSubmitButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 100;
|
||||
text: "Submit";
|
||||
onClicked: {
|
||||
root.justSubmitted = true;
|
||||
securityImageSubmitButton.text = "Submitting...";
|
||||
securityImageSubmitButton.enabled = false;
|
||||
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
|
||||
commerce.chooseSecurityImage(securityImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// SECURITY IMAGE SELECTION END
|
||||
//
|
||||
|
||||
function resetSubmitButton() {
|
||||
securityImageSubmitButton.enabled = true;
|
||||
securityImageSubmitButton.text = "Submit";
|
||||
}
|
||||
}
|
73
interface/resources/qml/hifi/commerce/wallet/SendMoney.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// SendMoney.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// SendMoney
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
}
|
||||
|
||||
// "Unavailable"
|
||||
RalewayRegular {
|
||||
text: "You currently cannot send money to other High Fidelity users.";
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
479
interface/resources/qml/hifi/commerce/wallet/Wallet.qml
Normal file
|
@ -0,0 +1,479 @@
|
|||
//
|
||||
// Wallet.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// Wallet
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-17
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
property string activeView: "walletHome";
|
||||
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (!exists) { // "If security image is not set up"
|
||||
if (root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathResult: {
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: walletSetupLightbox;
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletSetup_cancelClicked') {
|
||||
walletSetupLightbox.visible = false;
|
||||
} else if (msg.method === 'walletSetup_finished') {
|
||||
root.activeView = "walletHome";
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: notSetUp;
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'setUpClicked') {
|
||||
walletSetupLightbox.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: walletSetupLightboxContainer;
|
||||
visible: walletSetupLightbox.visible || passphraseSelectionLightbox.visible || securityImageSelectionLightbox.visible;
|
||||
z: 998;
|
||||
anchors.fill: parent;
|
||||
color: "black";
|
||||
opacity: 0.5;
|
||||
}
|
||||
WalletSetupLightbox {
|
||||
id: walletSetupLightbox;
|
||||
visible: false;
|
||||
z: 999;
|
||||
anchors.centerIn: walletSetupLightboxContainer;
|
||||
width: walletSetupLightboxContainer.width - 50;
|
||||
height: walletSetupLightboxContainer.height - 50;
|
||||
}
|
||||
PassphraseSelectionLightbox {
|
||||
id: passphraseSelectionLightbox;
|
||||
visible: false;
|
||||
z: 999;
|
||||
anchors.centerIn: walletSetupLightboxContainer;
|
||||
width: walletSetupLightboxContainer.width - 50;
|
||||
height: walletSetupLightboxContainer.height - 50;
|
||||
}
|
||||
SecurityImageSelectionLightbox {
|
||||
id: securityImageSelectionLightbox;
|
||||
visible: false;
|
||||
z: 999;
|
||||
anchors.centerIn: walletSetupLightboxContainer;
|
||||
width: walletSetupLightboxContainer.width - 50;
|
||||
height: walletSetupLightboxContainer.height - 50;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "WALLET";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
||||
//
|
||||
// TAB CONTENTS START
|
||||
//
|
||||
NotSetUp {
|
||||
id: notSetUp;
|
||||
visible: root.activeView === "notSetUp";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
}
|
||||
|
||||
WalletHome {
|
||||
id: walletHome;
|
||||
visible: root.activeView === "walletHome";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.bottomMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
}
|
||||
|
||||
SendMoney {
|
||||
id: sendMoney;
|
||||
visible: root.activeView === "sendMoney";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.bottomMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
}
|
||||
|
||||
Security {
|
||||
id: security;
|
||||
visible: root.activeView === "security";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.bottomMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
}
|
||||
Connections {
|
||||
target: security;
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletSecurity_changePassphrase') {
|
||||
passphraseSelectionLightbox.visible = true;
|
||||
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
|
||||
securityImageSelectionLightbox.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Help {
|
||||
id: help;
|
||||
visible: root.activeView === "help";
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.bottom: tabButtonsContainer.top;
|
||||
anchors.bottomMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TAB CONTENTS END
|
||||
//
|
||||
|
||||
//
|
||||
// TAB BUTTONS START
|
||||
//
|
||||
Item {
|
||||
id: tabButtonsContainer;
|
||||
property int numTabs: 5;
|
||||
// Size
|
||||
width: root.width;
|
||||
height: 80;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
}
|
||||
|
||||
// "WALLET HOME" tab button
|
||||
Rectangle {
|
||||
id: walletHomeButtonContainer;
|
||||
visible: !notSetUp.visible;
|
||||
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width / tabButtonsContainer.numTabs;
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "WALLET HOME";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
MouseArea {
|
||||
enabled: !walletSetupLightboxContainer.visible;
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
root.activeView = "walletHome";
|
||||
tabButtonsContainer.resetTabButtonColors();
|
||||
}
|
||||
onEntered: parent.color = hifi.colors.blueHighlight;
|
||||
onExited: parent.color = root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
commerce.balance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "SEND MONEY" tab button
|
||||
Rectangle {
|
||||
id: sendMoneyButtonContainer;
|
||||
visible: !notSetUp.visible;
|
||||
color: hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: walletHomeButtonContainer.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width / tabButtonsContainer.numTabs;
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "SEND MONEY";
|
||||
// Text size
|
||||
size: 14;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.lightGray50;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// "EXCHANGE MONEY" tab button
|
||||
Rectangle {
|
||||
id: exchangeMoneyButtonContainer;
|
||||
visible: !notSetUp.visible;
|
||||
color: hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: sendMoneyButtonContainer.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width / tabButtonsContainer.numTabs;
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "EXCHANGE MONEY";
|
||||
// Text size
|
||||
size: 14;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.lightGray50;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// "SECURITY" tab button
|
||||
Rectangle {
|
||||
id: securityButtonContainer;
|
||||
visible: !notSetUp.visible;
|
||||
color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: exchangeMoneyButtonContainer.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width / tabButtonsContainer.numTabs;
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "SECURITY";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
MouseArea {
|
||||
enabled: !walletSetupLightboxContainer.visible;
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
root.activeView = "security";
|
||||
tabButtonsContainer.resetTabButtonColors();
|
||||
}
|
||||
onEntered: parent.color = hifi.colors.blueHighlight;
|
||||
onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
commerce.getKeyFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "HELP" tab button
|
||||
Rectangle {
|
||||
id: helpButtonContainer;
|
||||
visible: !notSetUp.visible;
|
||||
color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: securityButtonContainer.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width / tabButtonsContainer.numTabs;
|
||||
|
||||
RalewaySemiBold {
|
||||
text: "HELP";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
MouseArea {
|
||||
enabled: !walletSetupLightboxContainer.visible;
|
||||
anchors.fill: parent;
|
||||
hoverEnabled: enabled;
|
||||
onClicked: {
|
||||
root.activeView = "help";
|
||||
tabButtonsContainer.resetTabButtonColors();
|
||||
}
|
||||
onEntered: parent.color = hifi.colors.blueHighlight;
|
||||
onExited: parent.color = root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
|
||||
}
|
||||
}
|
||||
|
||||
function resetTabButtonColors() {
|
||||
walletHomeButtonContainer.color = hifi.colors.black;
|
||||
sendMoneyButtonContainer.color = hifi.colors.black;
|
||||
securityButtonContainer.color = hifi.colors.black;
|
||||
helpButtonContainer.color = hifi.colors.black;
|
||||
if (root.activeView === "walletHome") {
|
||||
walletHomeButtonContainer.color = hifi.colors.blueAccent;
|
||||
} else if (root.activeView === "sendMoney") {
|
||||
sendMoneyButtonContainer.color = hifi.colors.blueAccent;
|
||||
} else if (root.activeView === "security") {
|
||||
securityButtonContainer.color = hifi.colors.blueAccent;
|
||||
} else if (root.activeView === "help") {
|
||||
helpButtonContainer.color = hifi.colors.blueAccent;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// TAB BUTTONS END
|
||||
//
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendToScript(var message);
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
357
interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
Normal file
|
@ -0,0 +1,357 @@
|
|||
//
|
||||
// WalletHome.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// WalletHome
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-18
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (exists) {
|
||||
// just set the source again (to be sure the change was noticed)
|
||||
securityImage.source = "";
|
||||
securityImage.source = "image://security/securityImage";
|
||||
}
|
||||
}
|
||||
|
||||
onBalanceResult : {
|
||||
balanceText.text = parseFloat(result.data.balance/100).toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
usernameText.text = Account.username;
|
||||
}
|
||||
}
|
||||
|
||||
// Username Text
|
||||
RalewayRegular {
|
||||
id: usernameText;
|
||||
text: Account.username;
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
elide: Text.ElideRight;
|
||||
// Anchors
|
||||
anchors.top: securityImageContainer.top;
|
||||
anchors.bottom: securityImageContainer.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: hfcBalanceContainer.left;
|
||||
}
|
||||
|
||||
// HFC Balance Container
|
||||
Item {
|
||||
id: hfcBalanceContainer;
|
||||
// Anchors
|
||||
anchors.top: securityImageContainer.top;
|
||||
anchors.right: securityImageContainer.left;
|
||||
anchors.rightMargin: 16;
|
||||
width: 175;
|
||||
height: 60;
|
||||
Rectangle {
|
||||
id: hfcBalanceField;
|
||||
anchors.right: parent.right;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
height: parent.height - 15;
|
||||
|
||||
// "HFC" balance label
|
||||
RalewayRegular {
|
||||
id: balanceLabel;
|
||||
text: "HFC";
|
||||
// Text size
|
||||
size: 20;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: hfcBalanceField.right;
|
||||
anchors.rightMargin: 4;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.balance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Balance Text
|
||||
FiraSansRegular {
|
||||
id: balanceText;
|
||||
text: "--";
|
||||
// Text size
|
||||
size: 28;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: balanceLabel.left;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
// "balance" text above field
|
||||
RalewayRegular {
|
||||
text: "balance";
|
||||
// Text size
|
||||
size: 12;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: hfcBalanceField.top;
|
||||
anchors.bottomMargin: 4;
|
||||
anchors.left: hfcBalanceField.left;
|
||||
anchors.right: hfcBalanceField.right;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Security Image
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.right: parent.right;
|
||||
width: 75;
|
||||
height: childrenRect.height;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: securityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: parent.width - 10;
|
||||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
cache: false;
|
||||
source: "image://security/securityImage";
|
||||
}
|
||||
// "Security picture" text below pic
|
||||
RalewayRegular {
|
||||
text: "security picture";
|
||||
// Text size
|
||||
size: 12;
|
||||
// Anchors
|
||||
anchors.top: securityImage.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: securityImageContainer.left;
|
||||
anchors.right: securityImageContainer.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Recent Activity
|
||||
Item {
|
||||
id: recentActivityContainer;
|
||||
anchors.top: securityImageContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: homeMessage.visible ? homeMessage.top : root.bottom;
|
||||
anchors.bottomMargin: 10;
|
||||
|
||||
RalewayRegular {
|
||||
id: recentActivityText;
|
||||
text: "Recent Activity";
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: transactionHistory;
|
||||
anchors.top: recentActivityText.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.bottom: toggleFullHistoryButton.top;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
// some placeholder stuff
|
||||
RalewayRegular {
|
||||
text: homeMessage.visible ? "you <b>CANNOT</b> scroll through this." : "you <b>CAN</b> scroll through this";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: toggleFullHistoryButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: parent.right;
|
||||
width: 250;
|
||||
height: 40;
|
||||
text: homeMessage.visible ? "See Full Transaction History" : "Collapse Transaction History";
|
||||
onClicked: {
|
||||
if (homeMessage.visible) {
|
||||
homeMessage.visible = false;
|
||||
} else {
|
||||
homeMessage.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Item for "messages" - like "Welcome"
|
||||
Item {
|
||||
id: homeMessage;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
height: childrenRect.height;
|
||||
|
||||
RalewayRegular {
|
||||
id: messageText;
|
||||
text: "<b>Welcome! Let's get you some spending money.</b><br><br>" +
|
||||
"Now that your account is all set up, click the button below to request your starter money. " +
|
||||
"A robot will promptly review your request and put money into your account.";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 130;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: homeMessageButtons;
|
||||
anchors.top: messageText.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 40;
|
||||
HifiControlsUit.Button {
|
||||
id: noThanksButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
width: 100;
|
||||
text: "No Thanks"
|
||||
onClicked: {
|
||||
messageText.text = "Okay...weird. Who doesn't like free money? If you change your mind, too bad. Sorry."
|
||||
homeMessageButtons.visible = false;
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: freeMoneyButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: parent.right;
|
||||
width: 210;
|
||||
text: "Free Money Please"
|
||||
onClicked: {
|
||||
messageText.text = "Go, MoneyRobots, Go!"
|
||||
homeMessageButtons.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
//
|
||||
// Function Name: fromScript()
|
||||
//
|
||||
// Relevant Variables:
|
||||
// None
|
||||
//
|
||||
// Arguments:
|
||||
// message: The message sent from the JavaScript.
|
||||
// Messages are in format "{method, params}", like json-rpc.
|
||||
//
|
||||
// Description:
|
||||
// Called when a message is received from a script.
|
||||
//
|
||||
function fromScript(message) {
|
||||
switch (message.method) {
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
|
@ -0,0 +1,633 @@
|
|||
//
|
||||
// WalletSetupLightbox.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// WalletSetupLightbox
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-17
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property string lastPage: "login";
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (isLoggedIn) {
|
||||
securityImageContainer.visible = true;
|
||||
} else {
|
||||
loginPageContainer.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (!exists && root.lastPage === "securityImage") {
|
||||
// ERROR! Invalid security image.
|
||||
securityImageContainer.visible = true;
|
||||
choosePassphraseContainer.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
onPassphraseSetupStatusResult: {
|
||||
securityImageContainer.visible = false;
|
||||
if (passphraseIsSetup) {
|
||||
privateKeysReadyContainer.visible = true;
|
||||
} else {
|
||||
choosePassphraseContainer.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathResult: {
|
||||
if (path !== "") {
|
||||
keyFilePath.text = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// LOGIN PAGE START
|
||||
//
|
||||
Item {
|
||||
id: loginPageContainer;
|
||||
visible: false;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getLoginStatus();
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loginTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "WALLET SETUP - LOGIN";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: loginTitleHelper;
|
||||
text: "Please Log In to High Fidelity";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: loginTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Text below helper text
|
||||
RalewaySemiBold {
|
||||
id: loginDetailText;
|
||||
text: "To set up your wallet, you must first log in to High Fidelity.";
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: loginTitleHelper.bottom;
|
||||
anchors.topMargin: 25;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: loginDetailText.bottom;
|
||||
anchors.topMargin: 25;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
width: 150;
|
||||
height: 50;
|
||||
text: "Log In"
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSetup_loginClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 100;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// LOGIN PAGE END
|
||||
//
|
||||
|
||||
//
|
||||
// SECURITY IMAGE SELECTION START
|
||||
//
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
visible: false;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: securityImageTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "WALLET SETUP - STEP 1 OF 3";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: securityImageTitleHelper;
|
||||
text: "Choose a Security Picture:";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: securityImageTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: 50;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
// Anchors
|
||||
anchors.top: securityImageTitleHelper.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 280;
|
||||
}
|
||||
|
||||
// Text below security images
|
||||
RalewayRegular {
|
||||
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
|
||||
// Text size
|
||||
size: 18;
|
||||
// Anchors
|
||||
anchors.top: securityImageSelection.bottom;
|
||||
anchors.topMargin: 40;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 100;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
// "Next" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 100;
|
||||
text: "Next";
|
||||
onClicked: {
|
||||
root.lastPage = "securityImage";
|
||||
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
|
||||
commerce.chooseSecurityImage(securityImagePath);
|
||||
securityImageContainer.visible = false;
|
||||
choosePassphraseContainer.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// SECURITY IMAGE SELECTION END
|
||||
//
|
||||
|
||||
//
|
||||
// SECURE PASSPHRASE SELECTION START
|
||||
//
|
||||
Item {
|
||||
id: choosePassphraseContainer;
|
||||
visible: false;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getPassphraseSetupStatus();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: passphraseTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "WALLET SETUP - STEP 2 OF 3";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: passphraseTitleHelper;
|
||||
text: "Choose a Secure Passphrase";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: passphraseTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
PassphraseSelection {
|
||||
id: passphraseSelection;
|
||||
anchors.top: passphraseTitleHelper.bottom;
|
||||
anchors.topMargin: 30;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: passphraseNavBar.top;
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
id: passphraseNavBar;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
// "Back" button
|
||||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 100;
|
||||
text: "Back"
|
||||
onClicked: {
|
||||
root.lastPage = "choosePassphrase";
|
||||
choosePassphraseContainer.visible = false;
|
||||
securityImageContainer.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// "Next" button
|
||||
HifiControlsUit.Button {
|
||||
id: passphrasePageNextButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 100;
|
||||
text: "Next";
|
||||
onClicked: {
|
||||
if (passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
root.lastPage = "passphrase";
|
||||
choosePassphraseContainer.visible = false;
|
||||
privateKeysReadyContainer.visible = true;
|
||||
commerce.balance(); // Do this here so that keys are generated. Order might change as backend changes?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// SECURE PASSPHRASE SELECTION END
|
||||
//
|
||||
|
||||
//
|
||||
// PRIVATE KEYS READY START
|
||||
//
|
||||
Item {
|
||||
id: privateKeysReadyContainer;
|
||||
visible: false;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
|
||||
Item {
|
||||
id: keysReadyTitle;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
text: "WALLET SETUP - STEP 3 OF 3";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Text below title bar
|
||||
RalewaySemiBold {
|
||||
id: keysReadyTitleHelper;
|
||||
text: "Your Private Keys are Ready";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Anchors
|
||||
anchors.top: keysReadyTitle.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 50;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Text below checkbox
|
||||
RalewayRegular {
|
||||
id: explanationText;
|
||||
text: "Your money and purchases are secured with private keys that only you have access to. " +
|
||||
"<b>If they are lost, you will not be able to access your money or purchases.</b><br><br>" +
|
||||
"<b>To protect your privacy, High Fidelity has no access to your private keys and cannot " +
|
||||
"recover them for any reason.<br><br>To safeguard your private keys, backup this file on a regular basis:</b>";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: keysReadyTitleHelper.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: keyFilePath;
|
||||
anchors.top: explanationText.bottom;
|
||||
anchors.topMargin: 10;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: clipboardButton.left;
|
||||
height: 40;
|
||||
readOnly: true;
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getKeyFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: clipboardButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
anchors.top: keyFilePath.top;
|
||||
anchors.bottom: keyFilePath.bottom;
|
||||
width: height;
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.question;
|
||||
// Size
|
||||
size: parent.height*1.3;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
// Style
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
Window.copyToClipboard(keyFilePath.text);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation Bar
|
||||
Item {
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 100;
|
||||
// Anchors:
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
// "Next" button
|
||||
HifiControlsUit.Button {
|
||||
id: keysReadyPageNextButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 100;
|
||||
text: "Finish";
|
||||
onClicked: {
|
||||
root.visible = false;
|
||||
sendSignalToWallet({method: 'walletSetup_finished'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// PRIVATE KEYS READY END
|
||||
//
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
signal sendSignalToWallet(var msg);
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
BIN
interface/resources/qml/hifi/commerce/wallet/images/01cat.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/02car.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/03dog.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/04stars.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/05plane.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 74 KiB |
|
@ -28,6 +28,11 @@ ScrollingWindow {
|
|||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
// This is for JS/QML communication, which is unused in the AttachmentsDialog,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
property var settings: Settings {
|
||||
category: "AttachmentsDialog"
|
||||
property alias x: root.x
|
||||
|
|
|
@ -16,6 +16,7 @@ import Qt.labs.settings 1.0
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../windows"
|
||||
import "../"
|
||||
|
||||
ScrollingWindow {
|
||||
id: root
|
||||
|
@ -28,10 +29,11 @@ ScrollingWindow {
|
|||
minSize: Qt.vector2d(424, 300)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
||||
property var scripts: ScriptDiscoveryService;
|
||||
property var scriptsModel: scripts.scriptsModelFilter
|
||||
property var runningScriptsModel: ListModel { }
|
||||
property bool developerMenuEnabled: false
|
||||
property bool isHMD: false
|
||||
|
||||
Settings {
|
||||
|
@ -39,6 +41,28 @@ ScrollingWindow {
|
|||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
}
|
||||
|
||||
Component {
|
||||
id: letterBoxMessage
|
||||
Window {
|
||||
implicitWidth: 400
|
||||
implicitHeight: 300
|
||||
minSize: Qt.vector2d(424, 300)
|
||||
DesktopLetterboxMessage {
|
||||
visible: true
|
||||
headerGlyph: hifi.glyphs.lock
|
||||
headerText: "Developer Mode only"
|
||||
text: ( "In order to edit, delete or reload this script," +
|
||||
" turn on Developer Mode by going to:" +
|
||||
" Menu > Settings > Developer Menus")
|
||||
popupRadius: 0
|
||||
headerGlyphSize: 20
|
||||
headerTextMargin: 2
|
||||
headerGlyphMargin: -3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
|
@ -47,6 +71,15 @@ ScrollingWindow {
|
|||
running: false
|
||||
onTriggered: updateRunningScripts();
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: checkMenu
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
|
||||
}
|
||||
|
||||
Component {
|
||||
id: listModelBuilder
|
||||
|
@ -64,6 +97,8 @@ ScrollingWindow {
|
|||
Component.onCompleted: {
|
||||
isHMD = HMD.active;
|
||||
updateRunningScripts();
|
||||
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
|
||||
checkMenu.restart();
|
||||
}
|
||||
|
||||
function updateRunningScripts() {
|
||||
|
@ -110,7 +145,17 @@ ScrollingWindow {
|
|||
|
||||
function reloadAll() {
|
||||
console.log("Reload all scripts");
|
||||
scripts.reloadAllScripts();
|
||||
if (!developerMenuEnabled) {
|
||||
for (var index = 0; index < runningScriptsModel.count; index++) {
|
||||
var url = runningScriptsModel.get(index).url;
|
||||
var fileName = url.substring(url.lastIndexOf('/')+1);
|
||||
if (canEditScript(fileName)) {
|
||||
scripts.stopScript(url, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scripts.reloadAllScripts();
|
||||
}
|
||||
}
|
||||
|
||||
function loadDefaults() {
|
||||
|
@ -120,7 +165,22 @@ ScrollingWindow {
|
|||
|
||||
function stopAll() {
|
||||
console.log("Stop all scripts");
|
||||
scripts.stopAllScripts();
|
||||
for (var index = 0; index < runningScriptsModel.count; index++) {
|
||||
var url = runningScriptsModel.get(index).url;
|
||||
var fileName = url.substring(url.lastIndexOf('/')+1);
|
||||
if (canEditScript(fileName)) {
|
||||
scripts.stopScript(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function canEditScript(script) {
|
||||
if ((script === "controllerScripts.js") || (script === "defaultScripts.js")) {
|
||||
return developerMenuEnabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Column {
|
||||
|
@ -146,6 +206,14 @@ ScrollingWindow {
|
|||
color: hifi.buttons.red
|
||||
onClicked: stopAll()
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
visible: root.developerMenuEnabled;
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
|
@ -162,6 +230,7 @@ ScrollingWindow {
|
|||
expandSelectedRow: true
|
||||
|
||||
itemDelegate: Item {
|
||||
property bool canEdit: canEditScript(styleData.value);
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
|
@ -185,8 +254,9 @@ ScrollingWindow {
|
|||
|
||||
HiFiGlyphs {
|
||||
id: reloadButton
|
||||
text: hifi.glyphs.reloadSmall
|
||||
text: ((canEditScript(styleData.value)) ? hifi.glyphs.reload : hifi.glyphs.lock)
|
||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
size: 21
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: stopButton.left
|
||||
|
@ -195,7 +265,13 @@ ScrollingWindow {
|
|||
MouseArea {
|
||||
id: reloadButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: reloadScript(model.url)
|
||||
onClicked: {
|
||||
if (canEdit) {
|
||||
reloadScript(model.url)
|
||||
} else {
|
||||
letterBoxMessage.createObject(desktop)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +279,7 @@ ScrollingWindow {
|
|||
id: stopButton
|
||||
text: hifi.glyphs.closeSmall
|
||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
visible: canEditScript(styleData.value)
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
|
@ -211,7 +288,11 @@ ScrollingWindow {
|
|||
MouseArea {
|
||||
id: stopButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: stopScript(model.url)
|
||||
onClicked: {
|
||||
if (canEdit) {
|
||||
stopScript(model.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,13 +345,6 @@ ScrollingWindow {
|
|||
height: 26
|
||||
onClickedQueued: ApplicationInterface.loadDialog()
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
|
|
@ -16,6 +16,7 @@ import Qt.labs.settings 1.0
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../windows"
|
||||
import "../"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
@ -26,26 +27,90 @@ Rectangle {
|
|||
property var scripts: ScriptDiscoveryService;
|
||||
property var scriptsModel: scripts.scriptsModelFilter
|
||||
property var runningScriptsModel: ListModel { }
|
||||
property bool developerMenuEnabled: false
|
||||
property bool isHMD: false
|
||||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
|
||||
LetterboxMessage {
|
||||
id: letterBoxMessage
|
||||
z: 999
|
||||
visible: false
|
||||
}
|
||||
|
||||
function letterBox(glyph, text, message) {
|
||||
letterBoxMessage.headerGlyph = glyph;
|
||||
letterBoxMessage.headerText = text;
|
||||
letterBoxMessage.text = message;
|
||||
letterBoxMessage.visible = true;
|
||||
letterBoxMessage.popupRadius = 0;
|
||||
letterBoxMessage.headerGlyphSize = 20
|
||||
letterBoxMessage.headerTextMargin = 2
|
||||
letterBoxMessage.headerGlyphMargin = -3
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: updateRunningScripts();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: checkMenu
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
|
||||
}
|
||||
|
||||
Component {
|
||||
id: listModelBuilder
|
||||
ListModel {}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ScriptDiscoveryService
|
||||
onScriptCountChanged: updateRunningScripts();
|
||||
onScriptCountChanged: {
|
||||
runningScriptsModel = listModelBuilder.createObject(root);
|
||||
refreshTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
isHMD = HMD.active;
|
||||
updateRunningScripts();
|
||||
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
|
||||
checkMenu.restart();
|
||||
}
|
||||
|
||||
function updateRunningScripts() {
|
||||
var runningScripts = ScriptDiscoveryService.getRunning();
|
||||
runningScriptsModel.clear()
|
||||
for (var i = 0; i < runningScripts.length; ++i) {
|
||||
runningScriptsModel.append(runningScripts[i]);
|
||||
function simplify(path) {
|
||||
// trim URI querystring/fragment
|
||||
path = (path+'').replace(/[#?].*$/,'');
|
||||
// normalize separators and grab last path segment (ie: just the filename)
|
||||
path = path.replace(/\\/g, '/').split('/').pop();
|
||||
// return lowercased because we want to sort mnemonically
|
||||
return path.toLowerCase();
|
||||
}
|
||||
var runningScripts = ScriptDiscoveryService.getRunning();
|
||||
runningScripts.sort(function(a,b) {
|
||||
a = simplify(a.path);
|
||||
b = simplify(b.path);
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
});
|
||||
// Calling `runningScriptsModel.clear()` here instead of creating a new object
|
||||
// triggers some kind of weird heap corruption deep inside Qt. So instead of
|
||||
// modifying the model in place, possibly triggering behaviors in the table
|
||||
// instead we create a new `ListModel`, populate it and update the
|
||||
// existing model atomically.
|
||||
var newRunningScriptsModel = listModelBuilder.createObject(root);
|
||||
for (var i = 0; i < runningScripts.length; ++i) {
|
||||
newRunningScriptsModel.append(runningScripts[i]);
|
||||
}
|
||||
runningScriptsModel = newRunningScriptsModel;
|
||||
}
|
||||
|
||||
function loadScript(script) {
|
||||
|
@ -65,7 +130,17 @@ Rectangle {
|
|||
|
||||
function reloadAll() {
|
||||
console.log("Reload all scripts");
|
||||
scripts.reloadAllScripts();
|
||||
if (!developerMenuEnabled) {
|
||||
for (var index = 0; index < runningScriptsModel.count; index++) {
|
||||
var url = runningScriptsModel.get(index).url;
|
||||
var fileName = url.substring(url.lastIndexOf('/')+1);
|
||||
if (canEditScript(fileName)) {
|
||||
scripts.stopScript(url, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scripts.reloadAllScripts();
|
||||
}
|
||||
}
|
||||
|
||||
function loadDefaults() {
|
||||
|
@ -75,7 +150,22 @@ Rectangle {
|
|||
|
||||
function stopAll() {
|
||||
console.log("Stop all scripts");
|
||||
scripts.stopAllScripts();
|
||||
for (var index = 0; index < runningScriptsModel.count; index++) {
|
||||
var url = runningScriptsModel.get(index).url;
|
||||
console.log(url);
|
||||
var fileName = url.substring(url.lastIndexOf('/')+1);
|
||||
if (canEditScript(fileName)) {
|
||||
scripts.stopScript(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function canEditScript(script) {
|
||||
if ((script === "controllerScripts.js") || (script === "defaultScripts.js")) {
|
||||
return developerMenuEnabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Flickable {
|
||||
|
@ -110,6 +200,14 @@ Rectangle {
|
|||
color: hifi.buttons.red
|
||||
onClicked: stopAll()
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
visible: root.developerMenuEnabled;
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
|
@ -125,6 +223,7 @@ Rectangle {
|
|||
expandSelectedRow: true
|
||||
|
||||
itemDelegate: Item {
|
||||
property bool canEdit: canEditScript(styleData.value);
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
|
@ -148,8 +247,9 @@ Rectangle {
|
|||
|
||||
HiFiGlyphs {
|
||||
id: reloadButton
|
||||
text: hifi.glyphs.reloadSmall
|
||||
text: ((canEditScript(styleData.value)) ? hifi.glyphs.reload : hifi.glyphs.lock)
|
||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
size: 21
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: stopButton.left
|
||||
|
@ -158,7 +258,17 @@ Rectangle {
|
|||
MouseArea {
|
||||
id: reloadButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: reloadScript(model.url)
|
||||
onClicked: {
|
||||
if (canEdit) {
|
||||
reloadScript(model.url)
|
||||
} else {
|
||||
letterBox(hifi.glyphs.lock,
|
||||
"Developer Mode only",
|
||||
"In order to edit, delete or reload this script," +
|
||||
" turn on Developer Mode by going to:" +
|
||||
" Menu > Settings > Developer Menus");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,6 +276,7 @@ Rectangle {
|
|||
id: stopButton
|
||||
text: hifi.glyphs.closeSmall
|
||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
visible: canEditScript(styleData.value)
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
|
@ -174,7 +285,11 @@ Rectangle {
|
|||
MouseArea {
|
||||
id: stopButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: stopScript(model.url)
|
||||
onClicked: {
|
||||
if (canEdit) {
|
||||
stopScript(model.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,13 +365,6 @@ Rectangle {
|
|||
onTriggered: ApplicationInterface.loadDialog();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
|
|
@ -50,7 +50,7 @@ Item {
|
|||
margins: 4
|
||||
}
|
||||
clip: true
|
||||
snapMode: ListView.SnapToItem
|
||||
cacheBuffer: 4000
|
||||
|
||||
model: ListModel {}
|
||||
delegate: Item {
|
||||
|
|
|
@ -145,12 +145,13 @@ Rectangle {
|
|||
visible: headPuckBox.checked
|
||||
HifiControls.SpinBox {
|
||||
id: headYOffset
|
||||
decimals: 4
|
||||
decimals: 1
|
||||
width: 112
|
||||
label: "Y: offset"
|
||||
label: "Y Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
stepSize: 0.0254
|
||||
value: -0.05
|
||||
stepSize: 1
|
||||
value: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -162,11 +163,12 @@ Rectangle {
|
|||
HifiControls.SpinBox {
|
||||
id: headZOffset
|
||||
width: 112
|
||||
label: "Z: offset"
|
||||
label: "Z Offset"
|
||||
minimumValue: -10
|
||||
stepSize: 0.0254
|
||||
decimals: 4
|
||||
value: -0.05
|
||||
stepSize: 1
|
||||
decimals: 1
|
||||
suffix: " cm"
|
||||
value: -5
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -175,7 +177,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
RalewayBold {
|
||||
id: hands
|
||||
|
||||
|
@ -254,11 +255,12 @@ Rectangle {
|
|||
|
||||
HifiControls.SpinBox {
|
||||
id: handYOffset
|
||||
decimals: 4
|
||||
decimals: 1
|
||||
width: 112
|
||||
label: "Y: offset"
|
||||
suffix: " cm"
|
||||
label: "Y Offset"
|
||||
minimumValue: -10
|
||||
stepSize: 0.0254
|
||||
stepSize: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -270,10 +272,11 @@ Rectangle {
|
|||
HifiControls.SpinBox {
|
||||
id: handZOffset
|
||||
width: 112
|
||||
label: "Z: offset"
|
||||
label: "Z Offset"
|
||||
suffix: " cm"
|
||||
minimumValue: -10
|
||||
stepSize: 0.0254
|
||||
decimals: 4
|
||||
stepSize: 1
|
||||
decimals: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
onEditingFinished: {
|
||||
|
@ -488,15 +491,55 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: shoulderAdditionalConfig
|
||||
visible: shoulderBox.checked
|
||||
anchors.top: shoulderConfig.bottom
|
||||
anchors.topMargin: 5
|
||||
anchors.left: openVrConfiguration.left
|
||||
anchors.leftMargin: leftMargin + 20
|
||||
spacing: 10
|
||||
|
||||
HifiControls.SpinBox {
|
||||
id: armCircumference
|
||||
decimals: 1
|
||||
width: 160
|
||||
suffix: " cm"
|
||||
label: "Arm Circumference"
|
||||
minimumValue: 0
|
||||
stepSize: 1.0
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
value: 33.0
|
||||
|
||||
onEditingFinished: {
|
||||
sendConfigurationSettings();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.SpinBox {
|
||||
id: shoulderWidth
|
||||
width: 160
|
||||
label: "Shoulder Width"
|
||||
suffix: " cm"
|
||||
minimumValue: 0
|
||||
stepSize: 1.0
|
||||
decimals: 1
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
value: 48
|
||||
|
||||
onEditingFinished: {
|
||||
sendConfigurationSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: bottomSeperator
|
||||
width: parent.width
|
||||
anchors.top: shoulderConfig.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom
|
||||
anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: calibrationButton
|
||||
property int color: hifi.buttons.blue
|
||||
|
@ -835,6 +878,9 @@ Rectangle {
|
|||
var viveController = settings["handController"];
|
||||
var desktopMode = settings["desktopMode"];
|
||||
|
||||
armCircumference.value = settings.armCircumference;
|
||||
shoulderWidth.value = settings.shoulderWidth;
|
||||
|
||||
if (HmdHead) {
|
||||
headBox.checked = true;
|
||||
headPuckBox.checked = false;
|
||||
|
@ -1010,6 +1056,8 @@ Rectangle {
|
|||
"bodyConfiguration": trackerConfiguration,
|
||||
"headConfiguration": headObject,
|
||||
"handConfiguration": handObject,
|
||||
"armCircumference": armCircumference.value,
|
||||
"shoulderWidth": shoulderWidth.value,
|
||||
"desktopMode": viveInDesktop.checked
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,24 @@ StackView {
|
|||
property var rpcCounter: 0;
|
||||
signal sendToScript(var message);
|
||||
function rpc(method, parameters, callback) {
|
||||
console.debug('TabletAddressDialog: rpc: method = ', method, 'parameters = ', parameters, 'callback = ', callback)
|
||||
|
||||
rpcCalls[rpcCounter] = callback;
|
||||
var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"};
|
||||
sendToScript(message);
|
||||
}
|
||||
function fromScript(message) {
|
||||
if (message.method === 'refreshFeeds') {
|
||||
var feeds = [happeningNow, places, snapshots];
|
||||
console.debug('TabletAddressDialog::fromScript: refreshFeeds', 'feeds = ', feeds);
|
||||
|
||||
feeds.forEach(function(feed) {
|
||||
Qt.callLater(feed.fillDestinations);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var callback = rpcCalls[message.id];
|
||||
if (!callback) {
|
||||
console.log('No callback for message fromScript', JSON.stringify(message));
|
||||
|
|
|
@ -116,6 +116,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
preventStealing: true
|
||||
onClicked: {
|
||||
console.log("Tablet Button Clicked!");
|
||||
if (tabletButton.inDebugMode) {
|
||||
|
|
|
@ -114,6 +114,7 @@ Item {
|
|||
}
|
||||
|
||||
function clearMenus() {
|
||||
topMenu = null
|
||||
d.clear()
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,11 @@ Item {
|
|||
loader.source = "";
|
||||
loader.source = "TabletWebView.qml";
|
||||
}
|
||||
|
||||
function loadTabletWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "../../controls/TabletWebView.qml";
|
||||
}
|
||||
|
||||
function returnToPreviousApp() {
|
||||
tabletApps.remove(currentApp);
|
||||
|
@ -121,6 +126,9 @@ Item {
|
|||
loader.item.url = url;
|
||||
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||
tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url});
|
||||
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||
loader.item.closeButtonVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// used to send a message from qml to interface script.
|
||||
|
@ -202,5 +210,11 @@ Item {
|
|||
width: 480
|
||||
height: 706
|
||||
|
||||
function setShown(value) {}
|
||||
function setShown(value) {
|
||||
if (value === true) {
|
||||
HMD.openTablet()
|
||||
} else {
|
||||
HMD.closeTablet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,17 @@ Windows.ScrollingWindow {
|
|||
loader.source = "WindowWebView.qml";
|
||||
}
|
||||
|
||||
function loadTabletWebBase() {
|
||||
loader.source = "";
|
||||
loader.source = "../../controls/TabletWebView.qml";
|
||||
}
|
||||
|
||||
function loadWebUrl(url, injectedJavaScriptUrl) {
|
||||
loader.item.url = url;
|
||||
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||
if (loader.item.hasOwnProperty("closeButtonVisible")) {
|
||||
loader.item.closeButtonVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// used to send a message from qml to interface script.
|
||||
|
|
|
@ -337,5 +337,6 @@ Item {
|
|||
readonly property string playback_play: "\ue01d"
|
||||
readonly property string stop_square: "\ue01e"
|
||||
readonly property string avatarTPose: "\ue01f"
|
||||
readonly property string lock: "\ue006"
|
||||
}
|
||||
}
|
||||
|
|
30
interface/resources/shaders/hmd_ui.frag
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
|
@ -8,12 +8,7 @@
|
|||
|
||||
struct OverlayData {
|
||||
mat4 mvp;
|
||||
vec4 glowPoints;
|
||||
vec4 glowColors[2];
|
||||
vec4 resolutionRadiusAlpha;
|
||||
|
||||
vec4 extraGlowColor;
|
||||
vec2 extraGlowPoint;
|
||||
float alpha;
|
||||
};
|
||||
|
||||
layout(std140) uniform overlayBuffer {
|
||||
|
@ -25,11 +20,9 @@ mat4 mvp = overlay.mvp;
|
|||
layout(location = 0) in vec3 Position;
|
||||
layout(location = 3) in vec2 TexCoord;
|
||||
|
||||
out vec3 vPosition;
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = mvp * vec4(Position, 1);
|
||||
vTexCoord = TexCoord;
|
||||
vPosition = Position;
|
||||
}
|
|
@ -1,86 +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;
|
||||
vec4 glowPoints;
|
||||
vec4 glowColors[2];
|
||||
vec4 resolutionRadiusAlpha;
|
||||
|
||||
vec4 extraGlowColor;
|
||||
vec2 extraGlowPoint;
|
||||
};
|
||||
|
||||
layout(std140) uniform overlayBuffer {
|
||||
OverlayData overlay;
|
||||
};
|
||||
|
||||
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
|
||||
float radius = overlay.resolutionRadiusAlpha.z;
|
||||
float alpha = overlay.resolutionRadiusAlpha.w;
|
||||
vec4 glowPoints = overlay.glowPoints;
|
||||
vec4 glowColors[2] = overlay.glowColors;
|
||||
|
||||
vec2 extraGlowPoint = overlay.extraGlowPoint;
|
||||
vec4 extraGlowColor = overlay.extraGlowColor;
|
||||
|
||||
in vec3 vPosition;
|
||||
in vec2 vTexCoord;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
float easeInOutCubic(float f) {
|
||||
const float d = 1.0;
|
||||
const float b = 0.0;
|
||||
const float c = 1.0;
|
||||
float t = f;
|
||||
if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b;
|
||||
return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b;
|
||||
}
|
||||
|
||||
void main() {
|
||||
FragColor = texture(sampler, vTexCoord);
|
||||
|
||||
vec2 aspect = resolution;
|
||||
aspect /= resolution.x;
|
||||
|
||||
float glowIntensity = 0.0;
|
||||
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
|
||||
float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect);
|
||||
float dist3 = distance(vTexCoord * aspect, extraGlowPoint * aspect);
|
||||
float distX = min(dist1, dist2);
|
||||
float dist = min(distX, dist3);
|
||||
vec3 glowColor = glowColors[0].rgb;
|
||||
if (dist2 < dist1) {
|
||||
glowColor = glowColors[1].rgb;
|
||||
}
|
||||
if (dist3 < dist2) {
|
||||
glowColor = extraGlowColor.rgb;
|
||||
}
|
||||
|
||||
if (dist <= radius) {
|
||||
glowIntensity = 1.0 - (dist / radius);
|
||||
glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity));
|
||||
glowIntensity = easeInOutCubic(glowIntensity);
|
||||
glowIntensity = pow(glowIntensity, 0.5);
|
||||
}
|
||||
|
||||
if (alpha <= 0.0) {
|
||||
if (glowIntensity <= 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
FragColor = vec4(glowColor, glowIntensity);
|
||||
return;
|
||||
}
|
||||
|
||||
FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity);
|
||||
FragColor.a *= alpha;
|
||||
}
|
|
@ -70,7 +70,7 @@
|
|||
#include <EntityScriptClient.h>
|
||||
#include <EntityScriptServerLogClient.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <HoverOverlayInterface.h>
|
||||
#include "ui/overlays/ContextOverlayInterface.h"
|
||||
#include <ErrorDialog.h>
|
||||
#include <FileScriptingInterface.h>
|
||||
#include <Finally.h>
|
||||
|
@ -183,6 +183,7 @@
|
|||
#include "ui/UpdateDialog.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "ui/DomainConnectionModel.h"
|
||||
#include "ui/ImageProvider.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
#include "ui/OctreeStatsProvider.h"
|
||||
|
@ -194,6 +195,13 @@
|
|||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
#include <raypick/RayPickScriptingInterface.h>
|
||||
#include <raypick/LaserPointerScriptingInterface.h>
|
||||
|
||||
#include "commerce/Ledger.h"
|
||||
#include "commerce/Wallet.h"
|
||||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -222,6 +230,7 @@ static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
|
|||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
static const QString SVO_EXTENSION = ".svo";
|
||||
static const QString SVO_JSON_EXTENSION = ".svo.json";
|
||||
static const QString JSON_GZ_EXTENSION = ".json.gz";
|
||||
static const QString JSON_EXTENSION = ".json";
|
||||
static const QString JS_EXTENSION = ".js";
|
||||
static const QString FST_EXTENSION = ".fst";
|
||||
|
@ -229,6 +238,7 @@ static const QString FBX_EXTENSION = ".fbx";
|
|||
static const QString OBJ_EXTENSION = ".obj";
|
||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||
static const QString WEB_VIEW_TAG = "noDownload=true";
|
||||
static const QString ZIP_EXTENSION = ".zip";
|
||||
|
||||
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
|
||||
|
||||
|
@ -254,13 +264,17 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
|
|||
|
||||
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
|
||||
static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0";
|
||||
|
||||
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
|
||||
{ SVO_EXTENSION, &Application::importSVOFromURL },
|
||||
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
|
||||
{ AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl },
|
||||
{ JSON_EXTENSION, &Application::importJSONFromURL },
|
||||
{ JS_EXTENSION, &Application::askToLoadScript },
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl }
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl },
|
||||
{ JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },
|
||||
{ ZIP_EXTENSION, &Application::importFromZIP }
|
||||
};
|
||||
|
||||
class DeadlockWatchdogThread : public QThread {
|
||||
|
@ -595,7 +609,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<Snapshot>();
|
||||
DependencyManager::set<CloseEventSender>();
|
||||
DependencyManager::set<ResourceManager>();
|
||||
DependencyManager::set<HoverOverlayInterface>();
|
||||
DependencyManager::set<ContextOverlayInterface>();
|
||||
DependencyManager::set<Ledger>();
|
||||
DependencyManager::set<Wallet>();
|
||||
|
||||
DependencyManager::set<LaserPointerScriptingInterface>();
|
||||
DependencyManager::set<RayPickScriptingInterface>();
|
||||
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
@ -705,7 +724,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
|
||||
_window->setWindowTitle("Interface");
|
||||
_window->setWindowTitle("High Fidelity Interface");
|
||||
|
||||
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
|
||||
|
||||
|
@ -956,6 +975,53 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
|
||||
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
|
||||
|
||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||
static const QString TESTER = "HIFI_TESTER";
|
||||
auto gpuIdent = GPUIdent::getInstance();
|
||||
auto glContextData = getGLContextData();
|
||||
QJsonObject properties = {
|
||||
{ "version", applicationVersion() },
|
||||
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
|
||||
{ "previousSessionCrashed", _previousSessionCrashed },
|
||||
{ "previousSessionRuntime", sessionRunTime.get() },
|
||||
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
|
||||
{ "kernel_type", QSysInfo::kernelType() },
|
||||
{ "kernel_version", QSysInfo::kernelVersion() },
|
||||
{ "os_type", QSysInfo::productType() },
|
||||
{ "os_version", QSysInfo::productVersion() },
|
||||
{ "gpu_name", gpuIdent->getName() },
|
||||
{ "gpu_driver", gpuIdent->getDriver() },
|
||||
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
|
||||
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
|
||||
{ "gl_version", glContextData["version"] },
|
||||
{ "gl_vender", glContextData["vendor"] },
|
||||
{ "gl_sl_version", glContextData["sl_version"] },
|
||||
{ "gl_renderer", glContextData["renderer"] },
|
||||
{ "ideal_thread_count", QThread::idealThreadCount() }
|
||||
};
|
||||
auto macVersion = QSysInfo::macVersion();
|
||||
if (macVersion != QSysInfo::MV_None) {
|
||||
properties["os_osx_version"] = QSysInfo::macVersion();
|
||||
}
|
||||
auto windowsVersion = QSysInfo::windowsVersion();
|
||||
if (windowsVersion != QSysInfo::WV_None) {
|
||||
properties["os_win_version"] = QSysInfo::windowsVersion();
|
||||
}
|
||||
|
||||
ProcessorInfo procInfo;
|
||||
if (getProcessorInfo(procInfo)) {
|
||||
properties["processor_core_count"] = procInfo.numProcessorCores;
|
||||
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
|
||||
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
|
||||
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
|
||||
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
|
||||
}
|
||||
|
||||
// add firstRun flag from settings to launch event
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
|
||||
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
|
||||
|
@ -1327,12 +1393,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Keyboard focus handling for Web overlays.
|
||||
auto overlays = &(qApp->getOverlays());
|
||||
|
||||
connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
setKeyboardFocusOverlay(overlayID);
|
||||
connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) {
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlays->getOverlay(overlayID));
|
||||
// Only Web overlays can have keyboard focus.
|
||||
if (thisOverlay) {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
setKeyboardFocusOverlay(overlayID);
|
||||
}
|
||||
});
|
||||
|
||||
connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) {
|
||||
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
|
||||
if (overlayID == _keyboardFocusedOverlay.get()) {
|
||||
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
|
||||
}
|
||||
|
@ -1347,6 +1417,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
});
|
||||
|
||||
connect(overlays,
|
||||
SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)),
|
||||
DependencyManager::get<ContextOverlayInterface>().data(),
|
||||
SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&)));
|
||||
|
||||
connect(overlays,
|
||||
SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)),
|
||||
DependencyManager::get<ContextOverlayInterface>().data(),
|
||||
SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&)));
|
||||
|
||||
connect(overlays,
|
||||
SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)),
|
||||
DependencyManager::get<ContextOverlayInterface>().data(),
|
||||
SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&)));
|
||||
|
||||
// Add periodic checks to send user activity data
|
||||
static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000;
|
||||
static int NEARBY_AVATAR_RADIUS_METERS = 10;
|
||||
|
@ -1473,7 +1558,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
properties["atp_mapping_requests"] = atpMappingRequests;
|
||||
|
||||
properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
|
||||
|
||||
|
||||
QJsonObject bytesDownloaded;
|
||||
bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt();
|
||||
bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt();
|
||||
|
@ -1654,6 +1739,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||
|
||||
// Setup the mouse ray pick and related operators
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(_rayPickManager.createRayPick(
|
||||
RayPickFilter(DependencyManager::get<RayPickScriptingInterface>()->PICK_ENTITIES() | DependencyManager::get<RayPickScriptingInterface>()->PICK_INCLUDE_NONCOLLIDABLE()),
|
||||
0.0f, true));
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](QUuid rayPickID) {
|
||||
RayToEntityIntersectionResult entityResult;
|
||||
RayPickResult result = _rayPickManager.getPrevRayPickResult(rayPickID);
|
||||
entityResult.intersects = result.type != DependencyManager::get<RayPickScriptingInterface>()->INTERSECTED_NONE();
|
||||
if (entityResult.intersects) {
|
||||
entityResult.intersection = result.intersection;
|
||||
entityResult.distance = result.distance;
|
||||
entityResult.surfaceNormal = result.surfaceNormal;
|
||||
entityResult.entityID = result.objectID;
|
||||
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
|
||||
}
|
||||
return entityResult;
|
||||
});
|
||||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) {
|
||||
_rayPickManager.setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
}
|
||||
|
||||
|
@ -2039,6 +2145,7 @@ void Application::initializeUi() {
|
|||
LoginDialog::registerType();
|
||||
Tooltip::registerType();
|
||||
UpdateDialog::registerType();
|
||||
QmlCommerce::registerType();
|
||||
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
|
||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||
|
||||
|
@ -2062,6 +2169,9 @@ void Application::initializeUi() {
|
|||
qApp->quit();
|
||||
});
|
||||
|
||||
// register the pixmap image provider (used only for security image, for now)
|
||||
engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider());
|
||||
|
||||
setupPreferences();
|
||||
|
||||
// For some reason there is already an "Application" object in the QML context,
|
||||
|
@ -2134,7 +2244,7 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
|
||||
|
||||
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
|
||||
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
|
||||
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
|
||||
|
@ -2240,7 +2350,7 @@ void Application::paintGL() {
|
|||
QMutexLocker viewLocker(&_viewMutex);
|
||||
_viewFrustum.calculate();
|
||||
}
|
||||
renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(),
|
||||
renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
{
|
||||
|
@ -2326,7 +2436,7 @@ void Application::paintGL() {
|
|||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (isHMDMode()) {
|
||||
auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
|
||||
auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
|
||||
|
||||
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
|
||||
// Mirror HMD yaw and roll
|
||||
|
@ -2348,7 +2458,7 @@ void Application::paintGL() {
|
|||
+ mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ mirrorBodyOrientation * hmdOffset);
|
||||
} else {
|
||||
_myCamera.setOrientation(myAvatar->getWorldAlignedOrientation()
|
||||
_myCamera.setOrientation(myAvatar->getOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0)
|
||||
|
@ -2393,6 +2503,7 @@ void Application::paintGL() {
|
|||
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
}
|
||||
|
||||
mat4 eyeProjections[2];
|
||||
{
|
||||
PROFILE_RANGE(render, "/mainRender");
|
||||
PerformanceTimer perfTimer("mainRender");
|
||||
|
@ -2414,7 +2525,6 @@ void Application::paintGL() {
|
|||
_myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection()));
|
||||
renderArgs._context->enableStereo(true);
|
||||
mat4 eyeOffsets[2];
|
||||
mat4 eyeProjections[2];
|
||||
auto baseProjection = renderArgs.getViewFrustum().getProjection();
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
float IPDScale = hmdInterface->getIPDScale();
|
||||
|
@ -2445,6 +2555,19 @@ void Application::paintGL() {
|
|||
displaySide(&renderArgs, _myCamera);
|
||||
}
|
||||
|
||||
gpu::Batch postCompositeBatch;
|
||||
{
|
||||
PROFILE_RANGE(render, "/postComposite");
|
||||
PerformanceTimer perfTimer("postComposite");
|
||||
renderArgs._batch = &postCompositeBatch;
|
||||
renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()));
|
||||
renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView());
|
||||
for_each_eye([&](Eye eye) {
|
||||
renderArgs._batch->setProjectionTransform(eyeProjections[eye]);
|
||||
_overlays.render3DHUDOverlays(&renderArgs);
|
||||
});
|
||||
}
|
||||
|
||||
auto frame = _gpuContext->endFrame();
|
||||
frame->frameIndex = _frameCount;
|
||||
frame->framebuffer = finalFramebuffer;
|
||||
|
@ -2452,6 +2575,7 @@ void Application::paintGL() {
|
|||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||
};
|
||||
frame->overlay = _applicationOverlay.getOverlayTexture();
|
||||
frame->postCompositeBatch = postCompositeBatch;
|
||||
// deliver final scene rendering commands to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(render, "/pluginOutput");
|
||||
|
@ -2740,7 +2864,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
bool Application::importJSONFromURL(const QString& urlString) {
|
||||
// we only load files that terminate in just .json (not .svo.json and not .ava.json)
|
||||
// if they come from the High Fidelity Marketplace Assets CDN
|
||||
|
||||
QUrl jsonURL { urlString };
|
||||
|
||||
if (jsonURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
|
||||
|
@ -2756,6 +2879,25 @@ bool Application::importSVOFromURL(const QString& urlString) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Application::importFromZIP(const QString& filePath) {
|
||||
qDebug() << "A zip file has been dropped in: " << filePath;
|
||||
QUrl empty;
|
||||
// handle Blocks download from Marketplace
|
||||
if (filePath.contains("vr.google.com/downloads")) {
|
||||
addAssetToWorldFromURL(filePath);
|
||||
} else {
|
||||
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::onPresent(quint32 frameCount) {
|
||||
if (shouldPaint()) {
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
||||
}
|
||||
}
|
||||
|
||||
bool _renderRequested { false };
|
||||
|
||||
bool Application::event(QEvent* event) {
|
||||
|
@ -2772,23 +2914,9 @@ bool Application::event(QEvent* event) {
|
|||
// Explicit idle keeps the idle running at a lower interval, but without any rendering
|
||||
// see (windowMinimizedChanged)
|
||||
case Event::Idle:
|
||||
{
|
||||
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||
_lastTimeUpdated.start();
|
||||
idle(nsecsElapsed);
|
||||
}
|
||||
return true;
|
||||
|
||||
case Event::Present:
|
||||
if (!_renderRequested) {
|
||||
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||
if (shouldPaint(nsecsElapsed)) {
|
||||
_renderRequested = true;
|
||||
_lastTimeUpdated.start();
|
||||
idle(nsecsElapsed);
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
||||
}
|
||||
}
|
||||
idle();
|
||||
// Clear the event queue of pending idle calls
|
||||
removePostedEvents(this, Idle);
|
||||
return true;
|
||||
|
||||
case Event::Paint:
|
||||
|
@ -2796,9 +2924,8 @@ bool Application::event(QEvent* event) {
|
|||
// or AvatarInputs will mysteriously move to the bottom-right
|
||||
AvatarInputs::getInstance()->update();
|
||||
paintGL();
|
||||
// wait for the next present event before starting idle / paint again
|
||||
removePostedEvents(this, Present);
|
||||
_renderRequested = false;
|
||||
// Clear the event queue of pending paint calls
|
||||
removePostedEvents(this, Paint);
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -3617,7 +3744,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
|
|||
|
||||
static uint32_t _renderedFrameIndex { INVALID_FRAME };
|
||||
|
||||
bool Application::shouldPaint(float nsecsElapsed) {
|
||||
bool Application::shouldPaint() {
|
||||
if (_aboutToQuit) {
|
||||
return false;
|
||||
}
|
||||
|
@ -3638,10 +3765,8 @@ bool Application::shouldPaint(float nsecsElapsed) {
|
|||
}
|
||||
#endif
|
||||
|
||||
float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC;
|
||||
|
||||
// Throttle if requested
|
||||
if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) {
|
||||
if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3858,7 +3983,7 @@ void setupCpuMonitorThread() {
|
|||
#endif
|
||||
|
||||
|
||||
void Application::idle(float nsecsElapsed) {
|
||||
void Application::idle() {
|
||||
PerformanceTimer perfTimer("idle");
|
||||
|
||||
// Update the deadlock watchdog
|
||||
|
@ -3915,7 +4040,8 @@ void Application::idle(float nsecsElapsed) {
|
|||
steamClient->runCallbacks();
|
||||
}
|
||||
|
||||
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
|
||||
float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND;
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
||||
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
||||
|
@ -4149,6 +4275,7 @@ void Application::loadSettings() {
|
|||
//DependencyManager::get<LODManager>()->setAutomaticLODAdjust(false);
|
||||
|
||||
Menu::getInstance()->loadSettings();
|
||||
|
||||
// If there is a preferred plugin, we probably messed it up with the menu settings, so fix it.
|
||||
auto pluginManager = PluginManager::getInstance();
|
||||
auto plugins = pluginManager->getPreferredDisplayPlugins();
|
||||
|
@ -4162,24 +4289,44 @@ void Application::loadSettings() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
bool isFirstPerson = false;
|
||||
if (firstRun.get()) {
|
||||
// If this is our first run, and no preferred devices were set, default to
|
||||
// an HMD device if available.
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
if (firstRun.get()) {
|
||||
auto displayPlugins = pluginManager->getDisplayPlugins();
|
||||
for (auto& plugin : displayPlugins) {
|
||||
if (plugin->isHmd()) {
|
||||
if (auto action = menu->getActionForOption(plugin->getName())) {
|
||||
action->setChecked(true);
|
||||
action->trigger();
|
||||
break;
|
||||
}
|
||||
auto displayPlugins = pluginManager->getDisplayPlugins();
|
||||
for (auto& plugin : displayPlugins) {
|
||||
if (plugin->isHmd()) {
|
||||
if (auto action = menu->getActionForOption(plugin->getName())) {
|
||||
action->setChecked(true);
|
||||
action->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isFirstPerson = (qApp->isHMDMode());
|
||||
} else {
|
||||
// if this is not the first run, the camera will be initialized differently depending on user settings
|
||||
|
||||
if (qApp->isHMDMode()) {
|
||||
// if the HMD is active, use first-person camera, unless the appropriate setting is checked
|
||||
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD);
|
||||
} else {
|
||||
// if HMD is not active, only use first person if the menu option is checked
|
||||
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson);
|
||||
}
|
||||
}
|
||||
|
||||
// finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings
|
||||
// dictated that we should be in first person
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson);
|
||||
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON);
|
||||
cameraMenuChanged();
|
||||
|
||||
auto inputs = pluginManager->getInputPlugins();
|
||||
for (auto plugin : inputs) {
|
||||
if (!plugin->isActive()) {
|
||||
|
@ -4228,7 +4375,6 @@ void Application::init() {
|
|||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
|
||||
DependencyManager::get<AvatarManager>()->init();
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
|
||||
_timerStart.start();
|
||||
_lastTimeUpdated.start();
|
||||
|
@ -4383,10 +4529,9 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
}
|
||||
} else {
|
||||
// I am not looking at anyone else, so just look forward
|
||||
auto headPose = myAvatar->getHeadControllerPoseInSensorFrame();
|
||||
auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix();
|
||||
lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE));
|
||||
lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE));
|
||||
} else {
|
||||
lookAtSpot = myAvatar->getHead()->getEyePosition() +
|
||||
(myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||
|
@ -4489,11 +4634,9 @@ void Application::cameraModeChanged() {
|
|||
void Application::cameraMenuChanged() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (isHMDMode()) {
|
||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
} else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
|
@ -4802,52 +4945,76 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
|
||||
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
||||
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
||||
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||
auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
|
||||
static const std::vector<controller::Action> avatarControllerActions = {
|
||||
controller::Action::LEFT_HAND,
|
||||
controller::Action::RIGHT_HAND,
|
||||
controller::Action::LEFT_FOOT,
|
||||
controller::Action::RIGHT_FOOT,
|
||||
controller::Action::HIPS,
|
||||
controller::Action::SPINE2,
|
||||
controller::Action::HEAD,
|
||||
controller::Action::LEFT_HAND_THUMB1,
|
||||
controller::Action::LEFT_HAND_THUMB2,
|
||||
controller::Action::LEFT_HAND_THUMB3,
|
||||
controller::Action::LEFT_HAND_THUMB4,
|
||||
controller::Action::LEFT_HAND_INDEX1,
|
||||
controller::Action::LEFT_HAND_INDEX2,
|
||||
controller::Action::LEFT_HAND_INDEX3,
|
||||
controller::Action::LEFT_HAND_INDEX4,
|
||||
controller::Action::LEFT_HAND_MIDDLE1,
|
||||
controller::Action::LEFT_HAND_MIDDLE2,
|
||||
controller::Action::LEFT_HAND_MIDDLE3,
|
||||
controller::Action::LEFT_HAND_MIDDLE4,
|
||||
controller::Action::LEFT_HAND_RING1,
|
||||
controller::Action::LEFT_HAND_RING2,
|
||||
controller::Action::LEFT_HAND_RING3,
|
||||
controller::Action::LEFT_HAND_RING4,
|
||||
controller::Action::LEFT_HAND_PINKY1,
|
||||
controller::Action::LEFT_HAND_PINKY2,
|
||||
controller::Action::LEFT_HAND_PINKY3,
|
||||
controller::Action::LEFT_HAND_PINKY4,
|
||||
controller::Action::RIGHT_HAND_THUMB1,
|
||||
controller::Action::RIGHT_HAND_THUMB2,
|
||||
controller::Action::RIGHT_HAND_THUMB3,
|
||||
controller::Action::RIGHT_HAND_THUMB4,
|
||||
controller::Action::RIGHT_HAND_INDEX1,
|
||||
controller::Action::RIGHT_HAND_INDEX2,
|
||||
controller::Action::RIGHT_HAND_INDEX3,
|
||||
controller::Action::RIGHT_HAND_INDEX4,
|
||||
controller::Action::RIGHT_HAND_MIDDLE1,
|
||||
controller::Action::RIGHT_HAND_MIDDLE2,
|
||||
controller::Action::RIGHT_HAND_MIDDLE3,
|
||||
controller::Action::RIGHT_HAND_MIDDLE4,
|
||||
controller::Action::RIGHT_HAND_RING1,
|
||||
controller::Action::RIGHT_HAND_RING2,
|
||||
controller::Action::RIGHT_HAND_RING3,
|
||||
controller::Action::RIGHT_HAND_RING4,
|
||||
controller::Action::RIGHT_HAND_PINKY1,
|
||||
controller::Action::RIGHT_HAND_PINKY2,
|
||||
controller::Action::RIGHT_HAND_PINKY3,
|
||||
controller::Action::RIGHT_HAND_PINKY4,
|
||||
controller::Action::LEFT_ARM,
|
||||
controller::Action::RIGHT_ARM,
|
||||
controller::Action::LEFT_SHOULDER,
|
||||
controller::Action::RIGHT_SHOULDER,
|
||||
controller::Action::LEFT_FORE_ARM,
|
||||
controller::Action::RIGHT_FORE_ARM,
|
||||
controller::Action::LEFT_LEG,
|
||||
controller::Action::RIGHT_LEG,
|
||||
controller::Action::LEFT_UP_LEG,
|
||||
controller::Action::RIGHT_UP_LEG,
|
||||
controller::Action::LEFT_TOE_BASE,
|
||||
controller::Action::RIGHT_TOE_BASE
|
||||
};
|
||||
|
||||
// If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if
|
||||
// fingers are not being controlled, finger joints are not updated in MySkeletonModel.
|
||||
// Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint.
|
||||
MyAvatar::FingerPosesMap leftHandFingerPoses;
|
||||
if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0
|
||||
|| userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) {
|
||||
for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) {
|
||||
leftHandFingerPoses[i] = {
|
||||
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
|
||||
userInputMapper->getActionName((controller::Action)i)
|
||||
};
|
||||
}
|
||||
// copy controller poses from userInputMapper to myAvatar.
|
||||
glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||
glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||
for (auto& action : avatarControllerActions) {
|
||||
controller::Pose pose = userInputMapper->getPoseState(action);
|
||||
myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix));
|
||||
}
|
||||
MyAvatar::FingerPosesMap rightHandFingerPoses;
|
||||
if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0
|
||||
|| userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) {
|
||||
for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) {
|
||||
rightHandFingerPoses[i] = {
|
||||
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
|
||||
userInputMapper->getActionName((controller::Action)i)
|
||||
};
|
||||
}
|
||||
}
|
||||
myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses);
|
||||
|
||||
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
|
||||
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
|
||||
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));
|
||||
|
||||
controller::Pose hipsPose = userInputMapper->getPoseState(controller::Action::HIPS);
|
||||
controller::Pose spine2Pose = userInputMapper->getPoseState(controller::Action::SPINE2);
|
||||
myAvatar->setSpineControllerPosesInSensorFrame(hipsPose.transform(avatarToSensorMatrix), spine2Pose.transform(avatarToSensorMatrix));
|
||||
|
||||
controller::Pose headPose = userInputMapper->getPoseState(controller::Action::HEAD);
|
||||
myAvatar->setHeadControllerPoseInSensorFrame(headPose.transform(avatarToSensorMatrix));
|
||||
|
||||
controller::Pose leftArmPose = userInputMapper->getPoseState(controller::Action::LEFT_ARM);
|
||||
controller::Pose rightArmPose = userInputMapper->getPoseState(controller::Action::RIGHT_ARM);
|
||||
myAvatar->setArmControllerPosesInSensorFrame(leftArmPose.transform(avatarToSensorMatrix), rightArmPose.transform(avatarToSensorMatrix));
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
|
@ -4968,6 +5135,16 @@ void Application::update(float deltaTime) {
|
|||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "RayPickManager");
|
||||
_rayPickManager.update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "LaserPointerManager");
|
||||
_laserPointerManager.update();
|
||||
}
|
||||
|
||||
// Update _viewFrustum with latest camera and view frustum data...
|
||||
// NOTE: we get this from the view frustum, to make it simpler, since the
|
||||
// loadViewFrumstum() method will get the correct details from the camera
|
||||
|
@ -5405,6 +5582,17 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
}
|
||||
renderArgs->_debugFlags = renderDebugFlags;
|
||||
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction);
|
||||
|
||||
RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE;
|
||||
auto contextOverlayInterface = DependencyManager::get<ContextOverlayInterface>();
|
||||
if (contextOverlayInterface->getEnabled()) {
|
||||
if (DependencyManager::get<ContextOverlayInterface>()->getIsInMarketplaceInspectionMode()) {
|
||||
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE;
|
||||
} else {
|
||||
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES;
|
||||
}
|
||||
}
|
||||
renderArgs->_outlineFlags = renderOutlineFlags;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5818,6 +6006,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("RayPick", DependencyManager::get<RayPickScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("LaserPointers", DependencyManager::get<LaserPointerScriptingInterface>().data());
|
||||
|
||||
// Caches
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||
|
@ -5868,7 +6059,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
|
||||
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
|
||||
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
|
||||
scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
|
||||
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
|
@ -6075,6 +6266,55 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Application::askToReplaceDomainContent(const QString& url) {
|
||||
QString methodDetails;
|
||||
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
|
||||
QUrl originURL { url };
|
||||
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
|
||||
// Create a confirmation dialog when this call is made
|
||||
const int MAX_CHARACTERS_PER_LINE = 90;
|
||||
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
|
||||
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "
|
||||
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
|
||||
"\nhttps://docs.highfidelity.com/create-and-explore/start-working-in-your-sandbox/restoring-sandbox-content";
|
||||
|
||||
bool agreeToReplaceContent = false; // assume false
|
||||
agreeToReplaceContent = QMessageBox::Yes == OffscreenUi::question("Are you sure you want to replace this domain's content set?",
|
||||
infoText, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (agreeToReplaceContent) {
|
||||
// Given confirmation, send request to domain server to replace content
|
||||
qCDebug(interfaceapp) << "Attempting to replace domain content: " << url;
|
||||
QByteArray urlData(url.toUtf8());
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) {
|
||||
return node->getType() == NodeType::EntityServer && node->getActiveSocket();
|
||||
}, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) {
|
||||
auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true);
|
||||
octreeFilePacket->write(urlData);
|
||||
limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode);
|
||||
});
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(DOMAIN_SPAWNING_POINT);
|
||||
methodDetails = "SuccessfulRequestToReplaceContent";
|
||||
} else {
|
||||
methodDetails = "UserDeclinedToReplaceContent";
|
||||
}
|
||||
} else {
|
||||
methodDetails = "ContentSetDidNotOriginateFromMarketplace";
|
||||
}
|
||||
} else {
|
||||
methodDetails = "UserDoesNotHavePermissionToReplaceContent";
|
||||
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
QJsonObject messageProperties = {
|
||||
{ "status", methodDetails },
|
||||
{ "content_set_url", url }
|
||||
};
|
||||
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::displayAvatarAttachmentWarning(const QString& message) const {
|
||||
auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
|
||||
OffscreenUi::warning(avatarAttachmentWarningTitle, message);
|
||||
|
@ -6151,7 +6391,14 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
void Application::addAssetToWorldFromURL(QString url) {
|
||||
qInfo(interfaceapp) << "Download model and add to world from" << url;
|
||||
|
||||
QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
QString filename;
|
||||
if (url.contains("filename")) {
|
||||
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
}
|
||||
if (url.contains("vr.google.com/downloads")) {
|
||||
filename = url.section('/', -1);
|
||||
filename.remove(".zip?noDownload=false");
|
||||
}
|
||||
|
||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
||||
|
@ -6172,7 +6419,17 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
|||
auto url = request->getUrl().toString();
|
||||
auto result = request->getResult();
|
||||
|
||||
QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
|
||||
QString filename;
|
||||
bool isBlocks = false;
|
||||
|
||||
if (url.contains("filename")) {
|
||||
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
}
|
||||
if (url.contains("vr.google.com/downloads")) {
|
||||
filename = url.section('/', -1);
|
||||
filename.remove(".zip?noDownload=false");
|
||||
isBlocks = true;
|
||||
}
|
||||
|
||||
if (result == ResourceRequest::Success) {
|
||||
qInfo(interfaceapp) << "Downloaded model from" << url;
|
||||
|
@ -6187,7 +6444,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
|||
if (tempFile.open(QIODevice::WriteOnly)) {
|
||||
tempFile.write(request->getData());
|
||||
addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key.
|
||||
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true);
|
||||
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false, isBlocks);
|
||||
} else {
|
||||
QString errorInfo = "Couldn't open temporary file for download";
|
||||
qWarning(interfaceapp) << errorInfo;
|
||||
|
@ -6217,12 +6474,23 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) {
|
|||
addAssetToWorldError(filename, "Couldn't unzip file " + filename + ".");
|
||||
}
|
||||
|
||||
void Application::addAssetToWorld(QString filePath) {
|
||||
void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks) {
|
||||
// Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget().
|
||||
|
||||
QString path = QUrl(filePath).toLocalFile();
|
||||
QString mapping;
|
||||
QString path = filePath;
|
||||
QString filename = filenameFromPath(path);
|
||||
QString mapping = "/" + filename;
|
||||
if (isZip) {
|
||||
QString assetFolder = zipFile.section("/", -1);
|
||||
assetFolder.remove(".zip");
|
||||
mapping = "/" + assetFolder + "/" + filename;
|
||||
} else if (isBlocks) {
|
||||
qCDebug(interfaceapp) << "Path to asset folder: " << zipFile;
|
||||
QString assetFolder = zipFile.section('/', -1);
|
||||
assetFolder.remove(".zip?noDownload=false");
|
||||
mapping = "/" + assetFolder + "/" + filename;
|
||||
} else {
|
||||
mapping = "/" + filename;
|
||||
}
|
||||
|
||||
// Test repeated because possibly different code paths.
|
||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||
|
@ -6303,7 +6571,13 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q
|
|||
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||
} else {
|
||||
addAssetToWorldAddEntity(filePath, mapping);
|
||||
// to prevent files that aren't models from being loaded into world automatically
|
||||
if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) {
|
||||
addAssetToWorldAddEntity(filePath, mapping);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Zipped contents are not supported entity files";
|
||||
addAssetToWorldInfoDone(filenameFromPath(filePath));
|
||||
}
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
|
@ -6593,15 +6867,18 @@ void Application::onAssetToWorldMessageBoxClosed() {
|
|||
}
|
||||
|
||||
|
||||
void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) {
|
||||
void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip, bool isBlocks) {
|
||||
if (autoAdd) {
|
||||
if (!unzipFile.isEmpty()) {
|
||||
addAssetToWorld(unzipFile);
|
||||
for (int i = 0; i < unzipFile.length(); i++) {
|
||||
qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i);
|
||||
addAssetToWorld(unzipFile.at(i), zipFile, isZip, isBlocks);
|
||||
}
|
||||
} else {
|
||||
addAssetToWorldUnzipFailure(zipFile);
|
||||
}
|
||||
} else {
|
||||
showAssetServerWidget(unzipFile);
|
||||
showAssetServerWidget(unzipFile.first());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6726,6 +7003,12 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
});
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot() {
|
||||
postLambdaEvent([this] {
|
||||
Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
|
||||
});
|
||||
}
|
||||
|
||||
void Application::shareSnapshot(const QString& path, const QUrl& href) {
|
||||
postLambdaEvent([path, href] {
|
||||
// not much to do here, everything is done in snapshot code...
|
||||
|
@ -7064,6 +7347,7 @@ void Application::updateDisplayMode() {
|
|||
|
||||
auto oldDisplayPlugin = _displayPlugin;
|
||||
if (_displayPlugin) {
|
||||
disconnect(_displayPluginPresentConnection);
|
||||
_displayPlugin->deactivate();
|
||||
}
|
||||
|
||||
|
@ -7104,6 +7388,7 @@ void Application::updateDisplayMode() {
|
|||
_offscreenContext->makeCurrent();
|
||||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
_displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent);
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
#include "ui/overlays/Overlays.h"
|
||||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
#include "raypick/RayPickManager.h"
|
||||
#include "raypick/LaserPointerManager.h"
|
||||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <model/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
@ -129,8 +132,7 @@ public:
|
|||
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
|
||||
|
||||
enum Event {
|
||||
Present = DisplayPlugin::Present,
|
||||
Paint,
|
||||
Paint = QEvent::User + 1,
|
||||
Idle,
|
||||
Lambda
|
||||
};
|
||||
|
@ -275,6 +277,7 @@ public:
|
|||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void takeSecondaryCameraSnapshot();
|
||||
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
|
||||
|
||||
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
|
@ -298,9 +301,13 @@ public:
|
|||
QUuid getTabletFrameID() const; // may be an entity or an overlay
|
||||
|
||||
void setAvatarOverrideUrl(const QUrl& url, bool save);
|
||||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||
|
||||
LaserPointerManager& getLaserPointerManager() { return _laserPointerManager; }
|
||||
RayPickManager& getRayPickManager() { return _rayPickManager; }
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
|
@ -332,14 +339,14 @@ public slots:
|
|||
// FIXME: Move addAssetToWorld* methods to own class?
|
||||
void addAssetToWorldFromURL(QString url);
|
||||
void addAssetToWorldFromURLRequestFinished();
|
||||
void addAssetToWorld(QString filePath);
|
||||
void addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks);
|
||||
void addAssetToWorldUnzipFailure(QString filePath);
|
||||
void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy);
|
||||
void addAssetToWorldUpload(QString filePath, QString mapping);
|
||||
void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash);
|
||||
void addAssetToWorldAddEntity(QString filePath, QString mapping);
|
||||
|
||||
void handleUnzip(QString sourceFile, QString destinationFile, bool autoAdd);
|
||||
void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip, bool isBlocks);
|
||||
|
||||
FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; }
|
||||
|
||||
|
@ -409,6 +416,7 @@ private slots:
|
|||
void clearDomainOctreeDetails();
|
||||
void clearDomainAvatars();
|
||||
void onAboutToQuit();
|
||||
void onPresent(quint32 frameCount);
|
||||
|
||||
void resettingDomain();
|
||||
|
||||
|
@ -430,6 +438,8 @@ private slots:
|
|||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
bool askToReplaceDomainContent(const QString& url);
|
||||
|
||||
void setSessionUUID(const QUuid& sessionUUID) const;
|
||||
|
||||
void domainChanged(const QString& domainHostname);
|
||||
|
@ -455,8 +465,8 @@ private:
|
|||
|
||||
void cleanupBeforeQuit();
|
||||
|
||||
bool shouldPaint(float nsecsElapsed);
|
||||
void idle(float nsecsElapsed);
|
||||
bool shouldPaint();
|
||||
void idle();
|
||||
void update(float deltaTime);
|
||||
|
||||
// Various helper functions called during update()
|
||||
|
@ -481,6 +491,7 @@ private:
|
|||
|
||||
bool importJSONFromURL(const QString& urlString);
|
||||
bool importSVOFromURL(const QString& urlString);
|
||||
bool importFromZIP(const QString& filePath);
|
||||
|
||||
bool nearbyEntitiesAreReadyForPhysics();
|
||||
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
|
||||
|
@ -518,6 +529,7 @@ private:
|
|||
|
||||
OffscreenGLCanvas* _offscreenContext { nullptr };
|
||||
DisplayPluginPointer _displayPlugin;
|
||||
QMetaObject::Connection _displayPluginPresentConnection;
|
||||
mutable std::mutex _displayPluginLock;
|
||||
InputPluginList _activeInputPlugins;
|
||||
|
||||
|
@ -695,5 +707,8 @@ private:
|
|||
QUrl _avatarOverrideUrl;
|
||||
bool _saveAvatarOverrideUrl { false };
|
||||
|
||||
LaserPointerManager _laserPointerManager;
|
||||
RayPickManager _rayPickManager;
|
||||
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -560,6 +560,8 @@ Menu::Menu() {
|
|||
avatar.get(), SLOT(setEnableDebugDrawIKConstraints(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKChains, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawIKChains(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderDetailedCollision, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawDetailedCollision(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
|
|
|
@ -162,6 +162,7 @@ namespace MenuOption {
|
|||
const QString RenderIKTargets = "Show IK Targets";
|
||||
const QString RenderIKConstraints = "Show IK Constraints";
|
||||
const QString RenderIKChains = "Show IK Chains";
|
||||
const QString RenderDetailedCollision = "Show Detailed Collision";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
|
|
|
@ -134,9 +134,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
|
|||
// fetch the hand controller pose
|
||||
controller::Pose pose;
|
||||
if (isRightHand) {
|
||||
pose = myAvatar->getRightHandControllerPoseInWorldFrame();
|
||||
pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND);
|
||||
} else {
|
||||
pose = myAvatar->getLeftHandControllerPoseInWorldFrame();
|
||||
pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::LEFT_HAND);
|
||||
}
|
||||
|
||||
if (pose.isValid()) {
|
||||
|
|
|
@ -273,19 +273,15 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
return;
|
||||
}
|
||||
|
||||
const float SHRINK_RATE = 0.15f;
|
||||
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
while (avatarItr != _avatarsToFade.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
|
||||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->updateFadingStatus(scene);
|
||||
if (!avatar->isFading()) {
|
||||
// fading to zero is such a rare event we push a unique transaction for each
|
||||
if (avatar->isInScene()) {
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(*avatarItr, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
@ -323,6 +319,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
// remove from node sets, if present
|
||||
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
|
||||
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
|
||||
avatar->fadeOut(qApp->getMain3DScene(), removalReason);
|
||||
}
|
||||
_avatarsToFade.push_back(removedAvatar);
|
||||
}
|
||||
|
@ -476,19 +473,25 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
|
|||
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude,
|
||||
const QScriptValue& avatarIdsToDiscard) {
|
||||
RayToAvatarIntersectionResult result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersection",
|
||||
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
||||
Q_ARG(const PickRay&, ray),
|
||||
Q_ARG(const QScriptValue&, avatarIdsToInclude),
|
||||
Q_ARG(const QScriptValue&, avatarIdsToDiscard));
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
|
||||
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
|
||||
|
||||
return findRayIntersectionVector(ray, avatarsToInclude, avatarsToDiscard);
|
||||
}
|
||||
|
||||
RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const PickRay& ray,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard) {
|
||||
RayToAvatarIntersectionResult result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersectionVector",
|
||||
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
||||
Q_ARG(const PickRay&, ray),
|
||||
Q_ARG(const QVector<EntityItemID>&, avatarsToInclude),
|
||||
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard));
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 normDirection = glm::normalize(ray.direction);
|
||||
|
||||
for (auto avatarData : _avatarHash) {
|
||||
|
|
|
@ -73,6 +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);
|
||||
|
||||
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
||||
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
||||
|
|
|
@ -429,7 +429,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
}
|
||||
|
||||
#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
|
||||
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getHeadControllerPoseInAvatarFrame() *
|
||||
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getControllerPoseInAvatarFrame(controller::Pose::HEAD) *
|
||||
glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
|
||||
DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));
|
||||
p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() +
|
||||
|
@ -664,7 +664,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
|||
|
||||
_hmdSensorPosition = newHmdSensorPosition;
|
||||
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
|
||||
auto headPose = _headControllerPoseInSensorFrameCache.get();
|
||||
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
_headControllerFacing = getFacingDir2D(headPose.rotation);
|
||||
} else {
|
||||
|
@ -760,37 +760,37 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
}
|
||||
|
||||
glm::vec3 MyAvatar::getLeftHandPosition() const {
|
||||
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||
auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getRightHandPosition() const {
|
||||
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||
auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
|
||||
const float TIP_LENGTH = 0.3f;
|
||||
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||
auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
||||
const float TIP_LENGTH = 0.3f;
|
||||
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||
auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandPose() const {
|
||||
return getLeftHandControllerPoseInAvatarFrame();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandPose() const {
|
||||
return getRightHandControllerPoseInAvatarFrame();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
||||
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||
auto pose = getLeftHandPose();
|
||||
glm::vec3 tipTrans = getLeftHandTipPosition();
|
||||
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
|
||||
pose.translation = tipTrans;
|
||||
|
@ -798,7 +798,7 @@ controller::Pose MyAvatar::getLeftHandTipPose() const {
|
|||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandTipPose() const {
|
||||
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||
auto pose = getRightHandPose();
|
||||
glm::vec3 tipTrans = getRightHandTipPosition();
|
||||
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
|
||||
pose.translation = tipTrans;
|
||||
|
@ -1063,6 +1063,10 @@ void MyAvatar::setEnableDebugDrawIKConstraints(bool isEnabled) {
|
|||
_enableDebugDrawIKConstraints = isEnabled;
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableDebugDrawDetailedCollision(bool isEnabled) {
|
||||
_enableDebugDrawDetailedCollision = isEnabled;
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
|
||||
_enableDebugDrawIKChains = isEnabled;
|
||||
}
|
||||
|
@ -1318,7 +1322,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() {
|
|||
}
|
||||
|
||||
glm::vec3 MyAvatar::getDefaultEyePosition() const {
|
||||
return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
|
||||
return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
|
||||
}
|
||||
|
||||
const float SCRIPT_PRIORITY = 1.0f + 1.0f;
|
||||
|
@ -1449,159 +1453,43 @@ void MyAvatar::rebuildCollisionShape() {
|
|||
_characterController.setLocalBoundingBox(corner, diagonal);
|
||||
}
|
||||
|
||||
|
||||
void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
_leftHandControllerPoseInSensorFrameCache.set(left);
|
||||
_rightHandControllerPoseInSensorFrameCache.set(right);
|
||||
void MyAvatar::setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose) {
|
||||
std::lock_guard<std::mutex> guard(_controllerPoseMapMutex);
|
||||
auto iter = _controllerPoseMap.find(action);
|
||||
if (iter != _controllerPoseMap.end()) {
|
||||
iter->second = pose;
|
||||
} else {
|
||||
_controllerPoseMap.insert({ action, pose });
|
||||
}
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const {
|
||||
return _leftHandControllerPoseInSensorFrameCache.get();
|
||||
controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action action) const {
|
||||
std::lock_guard<std::mutex> guard(_controllerPoseMapMutex);
|
||||
auto iter = _controllerPoseMap.find(action);
|
||||
if (iter != _controllerPoseMap.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
return controller::Pose(); // invalid pose
|
||||
}
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const {
|
||||
return _rightHandControllerPoseInSensorFrameCache.get();
|
||||
controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const {
|
||||
auto pose = getControllerPoseInSensorFrame(action);
|
||||
if (pose.valid) {
|
||||
return pose.transform(getSensorToWorldMatrix());
|
||||
} else {
|
||||
return controller::Pose(); // invalid pose
|
||||
}
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
|
||||
return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
|
||||
return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) {
|
||||
_leftHandFingerPosesInSensorFramceCache.set(left);
|
||||
_rightHandFingerPosesInSensorFramceCache.set(right);
|
||||
}
|
||||
|
||||
MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const {
|
||||
return _leftHandFingerPosesInSensorFramceCache.get();
|
||||
}
|
||||
|
||||
MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const {
|
||||
return _rightHandFingerPosesInSensorFramceCache.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
_leftFootControllerPoseInSensorFrameCache.set(left);
|
||||
_rightFootControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const {
|
||||
return _leftFootControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightFootControllerPoseInSensorFrame() const {
|
||||
return _rightFootControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftFootControllerPoseInWorldFrame() const {
|
||||
return _leftFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightFootControllerPoseInWorldFrame() const {
|
||||
return _rightFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftFootControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getLeftFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) {
|
||||
_hipsControllerPoseInSensorFrameCache.set(hips);
|
||||
_spine2ControllerPoseInSensorFrameCache.set(spine2);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const {
|
||||
return _hipsControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getSpine2ControllerPoseInSensorFrame() const {
|
||||
return _spine2ControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHipsControllerPoseInWorldFrame() const {
|
||||
return _hipsControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getSpine2ControllerPoseInWorldFrame() const {
|
||||
return _spine2ControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHipsControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getHipsControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) {
|
||||
_headControllerPoseInSensorFrameCache.set(head);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const {
|
||||
return _headControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHeadControllerPoseInWorldFrame() const {
|
||||
return _headControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
void MyAvatar::setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
_leftArmControllerPoseInSensorFrameCache.set(left);
|
||||
_rightArmControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftArmControllerPoseInSensorFrame() const {
|
||||
return _leftArmControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightArmControllerPoseInSensorFrame() const {
|
||||
return _rightArmControllerPoseInSensorFrameCache.get();
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftArmControllerPoseInWorldFrame() const {
|
||||
return getLeftArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightArmControllerPoseInWorldFrame() const {
|
||||
return getRightArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix());
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftArmControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getLeftArmControllerPoseInWorldFrame().transform(worldToAvatarMat);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getRightArmControllerPoseInAvatarFrame() const {
|
||||
glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return getRightArmControllerPoseInWorldFrame().transform(worldToAvatarMat);
|
||||
controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const {
|
||||
auto pose = getControllerPoseInWorldFrame(action);
|
||||
if (pose.valid) {
|
||||
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||
return pose.transform(invAvatarMatrix);
|
||||
} else {
|
||||
return controller::Pose(); // invalid pose
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotors() {
|
||||
|
@ -1613,9 +1501,14 @@ void MyAvatar::updateMotors() {
|
|||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
} else {
|
||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||
// so we decompose camera's rotation and store the twist part in motorRotation
|
||||
// we decompose camera's rotation and store the twist part in motorRotation
|
||||
// however, we need to perform the decomposition in the avatar-frame
|
||||
// using the local UP axis and then transform back into world-frame
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame
|
||||
glm::quat liftRotation;
|
||||
swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation);
|
||||
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
|
||||
motorRotation = orientation * motorRotation;
|
||||
}
|
||||
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
|
||||
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
|
||||
|
@ -1659,7 +1552,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
_characterController.setParentVelocity(parentVelocity);
|
||||
|
||||
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
||||
auto headPose = getHeadControllerPoseInAvatarFrame();
|
||||
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput());
|
||||
} else {
|
||||
|
@ -1669,11 +1562,31 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
_prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix);
|
||||
}
|
||||
|
||||
// There are a number of possible strategies for this set of tools through endRender, below.
|
||||
void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||
bool success;
|
||||
Transform trans = getTransform(success);
|
||||
if (!success) {
|
||||
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
|
||||
return;
|
||||
}
|
||||
trans.setTranslation(position);
|
||||
trans.setRotation(orientation);
|
||||
SpatiallyNestable::setTransform(trans, success);
|
||||
if (!success) {
|
||||
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
|
||||
}
|
||||
updateAttitude(orientation);
|
||||
}
|
||||
|
||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 position;
|
||||
glm::quat orientation;
|
||||
if (_characterController.isEnabledAndReady()) {
|
||||
_characterController.getPositionAndOrientation(position, orientation);
|
||||
} else {
|
||||
position = getPosition();
|
||||
orientation = getOrientation();
|
||||
}
|
||||
nextAttitude(position, orientation);
|
||||
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||
|
@ -1893,8 +1806,8 @@ void MyAvatar::postUpdate(float deltaTime) {
|
|||
}
|
||||
|
||||
if (_enableDebugDrawHandControllers) {
|
||||
auto leftHandPose = getLeftHandControllerPoseInWorldFrame();
|
||||
auto rightHandPose = getRightHandControllerPoseInWorldFrame();
|
||||
auto leftHandPose = getControllerPoseInWorldFrame(controller::Action::LEFT_HAND);
|
||||
auto rightHandPose = getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND);
|
||||
|
||||
if (leftHandPose.isValid()) {
|
||||
DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1));
|
||||
|
@ -1915,6 +1828,37 @@ void MyAvatar::postUpdate(float deltaTime) {
|
|||
AnimPose postUpdateRoomPose(_sensorToWorldMatrix);
|
||||
|
||||
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
||||
|
||||
if (_enableDebugDrawDetailedCollision) {
|
||||
AnimPose rigToWorldPose(glm::vec3(1.0f), getRotation() * Quaternions::Y_180, getPosition());
|
||||
const int NUM_DEBUG_COLORS = 8;
|
||||
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
|
||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.0f, 1.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.25f, 0.25f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 1.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.25f, 1.0f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.25f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.65f, 0.0f, 1.0f) // Orange you glad I added this color?
|
||||
};
|
||||
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
const Rig& rig = _skeletonModel->getRig();
|
||||
const FBXGeometry& geometry = _skeletonModel->getFBXGeometry();
|
||||
for (int i = 0; i < rig.getJointStateCount(); i++) {
|
||||
AnimPose jointPose;
|
||||
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
|
||||
const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo;
|
||||
const AnimPose pose = rigToWorldPose * jointPose;
|
||||
for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) {
|
||||
glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]);
|
||||
glm::vec3 pointB = pose.xformPoint(shapeInfo.debugLines[2 * j + 1]);
|
||||
DebugDraw::getInstance().drawRay(pointA, pointB, DEBUG_COLORS[i % NUM_DEBUG_COLORS]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
||||
|
@ -2008,8 +1952,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI));
|
||||
}
|
||||
|
||||
// Use head/HMD roll to turn while walking or flying.
|
||||
if (qApp->isHMDMode() && _hmdRollControlEnabled) {
|
||||
// Use head/HMD roll to turn while walking or flying, but not when standing still
|
||||
if (qApp->isHMDMode() && _hmdRollControlEnabled && hasDriveInput()) {
|
||||
// Turn with head roll.
|
||||
const float MIN_CONTROL_SPEED = 0.01f;
|
||||
float speed = glm::length(getVelocity());
|
||||
|
@ -2047,7 +1991,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
|
||||
auto headPose = getHeadControllerPoseInAvatarFrame();
|
||||
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
|
||||
// these angles will be in radians
|
||||
|
@ -2684,10 +2628,10 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
|
|||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||
glm::vec3 headPosition;
|
||||
glm::quat headOrientation;
|
||||
auto headPose = getHeadControllerPoseInSensorFrame();
|
||||
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
headPosition = getHeadControllerPoseInSensorFrame().translation;
|
||||
headOrientation = getHeadControllerPoseInSensorFrame().rotation * Quaternions::Y_180;
|
||||
headPosition = headPose.translation;
|
||||
headOrientation = headPose.rotation * Quaternions::Y_180;
|
||||
}
|
||||
const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation);
|
||||
|
||||
|
@ -2869,7 +2813,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
|||
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
|
||||
|
||||
if (myAvatar.getHMDLeanRecenterEnabled()) {
|
||||
if (myAvatar.getHMDLeanRecenterEnabled() &&
|
||||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Rotation);
|
||||
}
|
||||
|
@ -2996,19 +2941,19 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
|||
|
||||
switch (index) {
|
||||
case CONTROLLER_LEFTHAND_INDEX: {
|
||||
return getLeftHandControllerPoseInAvatarFrame().getRotation();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getRotation();
|
||||
}
|
||||
case CONTROLLER_RIGHTHAND_INDEX: {
|
||||
return getRightHandControllerPoseInAvatarFrame().getRotation();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getRotation();
|
||||
}
|
||||
case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: {
|
||||
auto pose = _leftHandControllerPoseInSensorFrameCache.get();
|
||||
auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND);
|
||||
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
|
||||
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
|
||||
return glmExtractRotation(result);
|
||||
}
|
||||
case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: {
|
||||
auto pose = _rightHandControllerPoseInSensorFrameCache.get();
|
||||
auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND);
|
||||
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
|
||||
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
|
||||
return glmExtractRotation(result);
|
||||
|
@ -3033,19 +2978,19 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
|||
|
||||
switch (index) {
|
||||
case CONTROLLER_LEFTHAND_INDEX: {
|
||||
return getLeftHandControllerPoseInAvatarFrame().getTranslation();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation();
|
||||
}
|
||||
case CONTROLLER_RIGHTHAND_INDEX: {
|
||||
return getRightHandControllerPoseInAvatarFrame().getTranslation();
|
||||
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation();
|
||||
}
|
||||
case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: {
|
||||
auto pose = _leftHandControllerPoseInSensorFrameCache.get();
|
||||
auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND);
|
||||
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
|
||||
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
|
||||
return extractTranslation(result);
|
||||
}
|
||||
case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: {
|
||||
auto pose = _rightHandControllerPoseInSensorFrameCache.get();
|
||||
auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND);
|
||||
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
|
||||
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
|
||||
return extractTranslation(result);
|
||||
|
|