Merge branch 'master' of https://github.com/highfidelity/hifi into loginDialogBrowser-case-16537
|
@ -1,3 +1,5 @@
|
|||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
|
@ -74,10 +76,12 @@ android {
|
|||
// so our merge has to depend on the external native build
|
||||
variant.externalNativeBuildTasks.each { task ->
|
||||
variant.mergeResources.dependsOn(task)
|
||||
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
|
||||
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
|
||||
runDumpSymsTask.dependsOn(task)
|
||||
variant.assemble.dependsOn(uploadDumpSymsTask)
|
||||
if (Os.isFamily(Os.FAMILY_UNIX)) {
|
||||
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
|
||||
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
|
||||
runDumpSymsTask.dependsOn(task)
|
||||
variant.assemble.dependsOn(uploadDumpSymsTask)
|
||||
}
|
||||
}
|
||||
|
||||
variant.mergeAssets.doLast {
|
||||
|
|
|
@ -304,6 +304,11 @@ Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env
|
|||
return env->NewStringUTF(username.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeBeforeEnterBackground(JNIEnv *env, jobject obj) {
|
||||
AndroidHelper::instance().notifyBeforeEnterBackground();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterBackground(JNIEnv *env, jobject obj) {
|
||||
AndroidHelper::instance().notifyEnterBackground();
|
||||
|
|
|
@ -59,6 +59,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
|
||||
private native void nativeOnDestroy();
|
||||
private native void nativeGotoUrl(String url);
|
||||
private native void nativeBeforeEnterBackground();
|
||||
private native void nativeEnterBackground();
|
||||
private native void nativeEnterForeground();
|
||||
private native long nativeOnExitVr();
|
||||
|
@ -291,6 +292,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
case "Home":
|
||||
case "Privacy Policy":
|
||||
case "Login": {
|
||||
nativeBeforeEnterBackground();
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_FRAGMENT, activityName);
|
||||
intent.putExtra(MainActivity.EXTRA_BACK_TO_SCENE, backToScene);
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Fragment;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
|
@ -32,6 +33,7 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
|
||||
private OnHomeInteractionListener mListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
public native String nativeGetLastLocation();
|
||||
|
||||
|
@ -57,6 +59,7 @@ public class HomeFragment extends Fragment {
|
|||
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
|
||||
|
||||
searchNoResultsView = rootView.findViewById(R.id.searchNoResultsView);
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
|
||||
mDomainsView = rootView.findViewById(R.id.rvDomains);
|
||||
int numberOfColumns = 1;
|
||||
|
@ -76,12 +79,14 @@ public class HomeFragment extends Fragment {
|
|||
searchNoResultsView.setText(R.string.search_no_results);
|
||||
searchNoResultsView.setVisibility(View.VISIBLE);
|
||||
mDomainsView.setVisibility(View.GONE);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonEmptyAdapter() {
|
||||
searchNoResultsView.setVisibility(View.GONE);
|
||||
mDomainsView.setVisibility(View.VISIBLE);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,7 +109,7 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mDomainAdapter.loadDomains(editable.toString());
|
||||
mDomainAdapter.loadDomains(editable.toString(), false);
|
||||
if (editable.length() > 0) {
|
||||
mSearchIconView.setVisibility(View.GONE);
|
||||
mClearSearch.setVisibility(View.VISIBLE);
|
||||
|
@ -130,6 +135,13 @@ public class HomeFragment extends Fragment {
|
|||
|
||||
mClearSearch.setOnClickListener(view -> onSearchClear(view));
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
mDomainAdapter.loadDomains(mSearchView.getText().toString(), true);
|
||||
}
|
||||
});
|
||||
|
||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
|
||||
return rootView;
|
||||
|
|
|
@ -10,7 +10,7 @@ import io.highfidelity.hifiinterface.view.DomainAdapter;
|
|||
|
||||
public interface DomainProvider {
|
||||
|
||||
void retrieve(String filterText, DomainCallback domainCallback);
|
||||
void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh);
|
||||
|
||||
interface DomainCallback {
|
||||
void retrieveOk(List<DomainAdapter.Domain> domain);
|
||||
|
|
|
@ -49,8 +49,8 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized void retrieve(String filterText, DomainCallback domainCallback) {
|
||||
if (!startedToGetFromAPI) {
|
||||
public synchronized void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh) {
|
||||
if (!startedToGetFromAPI || forceRefresh) {
|
||||
startedToGetFromAPI = true;
|
||||
fillDestinations(filterText, domainCallback);
|
||||
} else {
|
||||
|
@ -72,6 +72,7 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
allStories.clear();
|
||||
getUserStoryPage(1, allStories, null,
|
||||
ex -> {
|
||||
suggestions.clear();
|
||||
allStories.forEach(userStory -> {
|
||||
if (taggedStoriesIds.contains(userStory.id)) {
|
||||
userStory.tagFound = true;
|
||||
|
|
|
@ -42,14 +42,14 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
mProtocol = protocol;
|
||||
mLastLocation = lastLocation;
|
||||
domainProvider = new UserStoryDomainProvider(mProtocol);
|
||||
loadDomains("");
|
||||
loadDomains("", true);
|
||||
}
|
||||
|
||||
public void setListener(AdapterListener adapterListener) {
|
||||
mAdapterListener = adapterListener;
|
||||
}
|
||||
|
||||
public void loadDomains(String filterText) {
|
||||
public void loadDomains(String filterText, boolean forceRefresh) {
|
||||
domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<Domain> domain) {
|
||||
|
@ -76,7 +76,7 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
Log.e("DOMAINS", message, e);
|
||||
if (mAdapterListener != null) mAdapterListener.onError(e, message);
|
||||
}
|
||||
});
|
||||
}, forceRefresh);
|
||||
}
|
||||
|
||||
private void overrideDefaultThumbnails(List<Domain> domain) {
|
||||
|
|
|
@ -63,13 +63,18 @@
|
|||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvDomains"
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
app:layout_constraintTop_toBottomOf="@id/searchView"
|
||||
app:layout_constraintBottom_toBottomOf="@id/contentHomeRoot"
|
||||
app:layout_constraintStart_toStartOf="@id/contentHomeRoot"
|
||||
app:layout_constraintEnd_toEndOf="@id/contentHomeRoot"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
android:layout_height="0dp">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvDomains"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
|
|
@ -71,17 +71,17 @@ def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
|
|||
def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
|
||||
def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms")
|
||||
|
||||
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
|
||||
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
|
||||
def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W'
|
||||
def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz'
|
||||
def qtChecksum='f312c47cd8b8dbca824c32af4eec5e66'
|
||||
def qtVersionId='nyCGcb91S4QbYeJhUkawO5x1lrLdSNB_'
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
|
||||
qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og'
|
||||
qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='a0c8b394aec5b0fcd46714ca3a53278a'
|
||||
qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='c3e25db64002d0f43cf565e0ef708911'
|
||||
qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ'
|
||||
qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl.tgz'
|
||||
qtChecksum='d80aed4233ce9e222aae8376e7a94bf9'
|
||||
qtVersionId='iDVXu0i3WEXRFIxQCtzcJ2XuKrE8RIqB'
|
||||
}
|
||||
|
||||
def packages = [
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QSaveFile>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtCore/QVector>
|
||||
|
@ -1042,7 +1043,7 @@ bool AssetServer::loadMappingsFromFile() {
|
|||
bool AssetServer::writeMappingsToFile() {
|
||||
auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME);
|
||||
|
||||
QFile mapFile { mapFilePath };
|
||||
QSaveFile mapFile { mapFilePath };
|
||||
if (mapFile.open(QIODevice::WriteOnly)) {
|
||||
QJsonObject root;
|
||||
|
||||
|
@ -1053,8 +1054,12 @@ bool AssetServer::writeMappingsToFile() {
|
|||
QJsonDocument jsonDocument { root };
|
||||
|
||||
if (mapFile.write(jsonDocument.toJson()) != -1) {
|
||||
qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath;
|
||||
return true;
|
||||
if (mapFile.commit()) {
|
||||
qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath;
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(asset_server) << "Failed to commit JSON mappings to file at" << mapFilePath;
|
||||
}
|
||||
} else {
|
||||
qCWarning(asset_server) << "Failed to write JSON mappings to file at" << mapFilePath;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,13 @@ elseif ((NOT MSVC12) AND (NOT MSVC14))
|
|||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
|
|
2
cmake/externals/GifCreator/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME GifCreator)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/GifCreator.zip
|
||||
URL https://public.highfidelity.com/dependencies/GifCreator.zip
|
||||
URL_MD5 8ac8ef5196f47c658dce784df5ecdb70
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
4
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -17,7 +17,7 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
|
||||
URL_MD5 06804ff9727b910dcd04a37c800053b5
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
|
||||
|
@ -38,7 +38,7 @@ elseif(APPLE)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://static.oculus.com/sdk-downloads/ovr_sdk_macos_0.5.0.1.tar.gz
|
||||
URL https://public.highfidelity.com/dependencies/ovr_sdk_macos_0.5.0.1.tar.gz
|
||||
URL_MD5 0a0785a04fb285f64f62267388344ad6
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
|
@ -9,7 +9,7 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/OVRPlatformSDK_v1.10.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/OVRPlatformSDK_v1.10.0.zip
|
||||
URL_MD5 e6c8264af16d904e6506acd5172fa0a9
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
2
cmake/externals/boostconfig/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(ExternalProject)
|
|||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
#URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/config-boost-1.58.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/config-boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
4
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -17,7 +17,7 @@ include(ExternalProject)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -28,7 +28,7 @@ if (WIN32)
|
|||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
47
cmake/externals/crashpad/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://public.highfidelity.com/dependencies/crashpad_062317.1.zip
|
||||
URL https://public.highfidelity.com/dependencies/crashpad_062317.1.zip
|
||||
URL_MD5 9c84b77f5f23daf939da1371825ed2b1
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -16,19 +16,46 @@ if (WIN32)
|
|||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of Crashpad include directories")
|
||||
|
||||
set(BIN_RELEASE_PATH "${SOURCE_DIR}/out/Release_x64")
|
||||
set(BIN_EXT ".exe")
|
||||
set(LIB_RELEASE_PATH "${SOURCE_DIR}/out/Release_x64/lib_MD")
|
||||
set(LIB_DEBUG_PATH "${SOURCE_DIR}/out/Debug_x64/lib_MD")
|
||||
set(LIB_PREFIX "")
|
||||
set(LIB_EXT "lib")
|
||||
elseif (APPLE)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://public.highfidelity.com/dependencies/crashpad_mac_070318.zip
|
||||
URL_MD5 ba1501dc163591ac2d1be74946967e2a
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util release library")
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util debug library")
|
||||
set(BIN_RELEASE_PATH "${SOURCE_DIR}/out/Release")
|
||||
set(BIN_EXT "")
|
||||
set(LIB_RELEASE_PATH "${SOURCE_DIR}/out/Release/lib")
|
||||
set(LIB_DEBUG_PATH "${SOURCE_DIR}/out/Debug/lib")
|
||||
set(LIB_PREFIX "lib")
|
||||
set(LIB_EXT "a")
|
||||
endif ()
|
||||
|
||||
set(CRASHPAD_HANDLER_EXE_PATH ${SOURCE_DIR}/out/Release_x64/crashpad_handler.exe CACHE FILEPATH "Path to the Crashpad handler executable")
|
||||
if (WIN32 OR APPLE)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of Crashpad include directories")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util release library")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util debug library")
|
||||
|
||||
set(CRASHPAD_HANDLER_EXE_PATH ${BIN_RELEASE_PATH}/crashpad_handler${BIN_EXT} CACHE FILEPATH "Path to the Crashpad handler binary")
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
|
|
2
cmake/externals/draco/CMakeLists.txt
vendored
|
@ -11,7 +11,7 @@ endif ()
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/draco-1.1.0.zip
|
||||
URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>-$<CONFIG> ${EXTRA_CMAKE_FLAGS}
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/etc2comp/CMakeLists.txt
vendored
|
@ -15,7 +15,7 @@ include(ExternalProject)
|
|||
# that would override CMAKE_CXX_FLAGS
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip
|
||||
URL https://public.highfidelity.com/dependencies/etc2comp-patched.zip
|
||||
URL_MD5 4c96153eb179acbe619e3d99d3330595
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS}
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/glad32es/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad32es.zip
|
||||
URL_MD5 6a641d8c49dee4c895fa59315f5682a6
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/glad41/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad41.zip
|
||||
URL_MD5 1324eeec33abe91e67d19ae551ba624d
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/glad45/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ include(SelectLibraryConfigurations)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip
|
||||
URL https://public.highfidelity.com/dependencies/glad/glad45.zip
|
||||
URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05
|
||||
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/gli/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME gli)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/gli-0.8.1.0.zip
|
||||
URL https://public.highfidelity.com/dependencies/gli-0.8.1.0.zip
|
||||
URL_MD5 00c990f59c12bbf367956ef399d6f798
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
|
|
2
cmake/externals/glm/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME glm)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip
|
||||
URL https://public.highfidelity.com/dependencies/glm-0.9.8.5-patched.zip
|
||||
URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
|
|
|
@ -6,16 +6,16 @@ set(EXTERNAL_NAME hifiAudioCodec)
|
|||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-win-2.0.zip)
|
||||
set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
|
||||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-mac-2.0.zip)
|
||||
set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
|
||||
elseif (ANDROID)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-android-2.0.zip)
|
||||
set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
|
||||
elseif (UNIX)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-linux-2.0.zip)
|
||||
set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
|
||||
else()
|
||||
return()
|
||||
|
|
2
cmake/externals/neuron/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ set(EXTERNAL_NAME neuron)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.2.zip")
|
||||
set(NEURON_URL "https://public.highfidelity.com/dependencies/neuron_datareader_b.12.2.zip")
|
||||
set(NEURON_URL_MD5 "84273ad2200bf86a9279d1f412a822ca")
|
||||
|
||||
ExternalProject_Add(${EXTERNAL_NAME}
|
||||
|
|
4
cmake/externals/nvtt/CMakeLists.txt
vendored
|
@ -8,7 +8,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip
|
||||
URL https://public.highfidelity.com/dependencies/nvtt-win-2.1.0.hifi.zip
|
||||
URL_MD5 10da01cf601f88f6dc12a6bc13c89136
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -29,7 +29,7 @@ else ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip
|
||||
URL https://public.highfidelity.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip
|
||||
URL_MD5 602776e08515b54bfa1b8dc455003f0f
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
2
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v1.0.6.zip
|
||||
URL https://public.highfidelity.com/dependencies/openvr-1.0.6.zip
|
||||
URL_MD5 f6892cd3a3078f505d03b4297f5a1951
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
2
cmake/externals/polyvox/CMakeLists.txt
vendored
|
@ -3,7 +3,7 @@ set(EXTERNAL_NAME polyvox)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip
|
||||
URL https://public.highfidelity.com/dependencies/polyvox-master-2015-7-15.zip
|
||||
URL_MD5 9ec6323b87e849ae36e562ae1c7494a9
|
||||
CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -13,7 +13,7 @@ endif ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip
|
||||
URL https://public.highfidelity.com/dependencies/quazip-0.7.3.zip
|
||||
URL_MD5 ed03754d39b9da1775771819b8001d45
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS ${QUAZIP_CMAKE_ARGS}
|
||||
|
|
7
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-devel-2.0.3-VC.zip
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-devel-2.0.3-VC.zip
|
||||
URL_MD5 30a333bcbe94bc5016e8799c73e86233
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -18,7 +18,8 @@ elseif (APPLE)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.zip
|
||||
URL_MD5 55f1eae5142d20db11c844d8d4d6deed
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DVIDEO_OPENGL=OFF
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -49,7 +50,7 @@ else ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://www.libsdl.org/release/SDL2-2.0.3.tar.gz
|
||||
URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.tar.gz
|
||||
URL_MD5 fe6c61d2e9df9ef570e7e80c6e822537
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
6
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -4,15 +4,15 @@ set(EXTERNAL_NAME sixense)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip")
|
||||
#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_062612.zip")
|
||||
#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc")
|
||||
#set(SIXENSE_NEW_LAYOUT 0)
|
||||
|
||||
set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
|
||||
set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_071615.zip")
|
||||
set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
|
||||
set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
|
||||
#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_102215.zip")
|
||||
#set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
|
||||
#set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
|
|
2
cmake/externals/steamworks/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ set(EXTERNAL_NAME steamworks)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(STEAMWORKS_URL "https://s3.amazonaws.com/hifi-public/dependencies/steamworks_sdk_137.zip")
|
||||
set(STEAMWORKS_URL "https://public.highfidelity.com/dependencies/steamworks_sdk_137.zip")
|
||||
set(STEAMWORKS_URL_MD5 "95ba9d0e3ddc04f8a8be17d2da806cbb")
|
||||
|
||||
ExternalProject_Add(
|
||||
|
|
6
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -3,13 +3,13 @@ set(EXTERNAL_NAME tbb)
|
|||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_win_slim.zip)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_win_slim.zip)
|
||||
set(DOWNLOAD_MD5 065934458e3db88397f3d10e7eea536c)
|
||||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
|
||||
else ()
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
|
||||
endif ()
|
||||
|
||||
|
|
2
cmake/externals/vhacd/CMakeLists.txt
vendored
|
@ -7,7 +7,7 @@ endif ()
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/v-hacd-master.zip
|
||||
URL https://public.highfidelity.com/dependencies/v-hacd-master.zip
|
||||
URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
|
|
2
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ if (WIN32)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip
|
||||
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi10.zip
|
||||
URL_MD5 4f40e49715a420fb67b45b9cee19052c
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
|
3
cmake/externals/zlib/CMakeLists.txt
vendored
|
@ -5,7 +5,8 @@ include(ExternalProject)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/zlib128.zip
|
||||
URL https://public.highfidelity.com/dependencies/zlib128.zip
|
||||
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
|
|
|
@ -23,7 +23,7 @@ macro(add_crashpad)
|
|||
set(CMAKE_BACKTRACE_TOKEN $ENV{CMAKE_BACKTRACE_TOKEN})
|
||||
endif()
|
||||
|
||||
if (WIN32 AND USE_CRASHPAD)
|
||||
if ((WIN32 OR APPLE) AND USE_CRASHPAD)
|
||||
get_property(CRASHPAD_CHECKED GLOBAL PROPERTY CHECKED_FOR_CRASHPAD_ONCE)
|
||||
if (NOT CRASHPAD_CHECKED)
|
||||
|
||||
|
@ -42,6 +42,10 @@ macro(add_crashpad)
|
|||
|
||||
if (WIN32)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "/ignore:4099")
|
||||
elseif (APPLE)
|
||||
find_library(Security Security)
|
||||
target_link_libraries(${TARGET_NAME} ${Security})
|
||||
target_link_libraries(${TARGET_NAME} "-lbsm")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
|
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 92 KiB |
BIN
interface/resources/html/img/tablet-help-windowsMR.jpg
Normal file
After Width: | Height: | Size: 94 KiB |
|
@ -66,7 +66,7 @@
|
|||
<script>
|
||||
var handControllerImageURL = null;
|
||||
var index = 0;
|
||||
var count = 4;
|
||||
var count = 5;
|
||||
|
||||
function showKbm() {
|
||||
document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg");
|
||||
|
@ -102,9 +102,13 @@
|
|||
showHandControllers();
|
||||
break;
|
||||
case 2:
|
||||
showGamepad();
|
||||
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
|
||||
showHandControllers();
|
||||
break;
|
||||
case 3:
|
||||
showGamepad();
|
||||
break;
|
||||
case 4:
|
||||
showKbm();
|
||||
break;
|
||||
|
||||
|
@ -144,7 +148,10 @@
|
|||
handControllerImageURL = "img/tablet-help-oculus.jpg";
|
||||
index = 0;
|
||||
break;
|
||||
|
||||
case "windowsMR":
|
||||
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
|
||||
index = 2;
|
||||
break;
|
||||
case "vive":
|
||||
default:
|
||||
handControllerImageURL = "img/tablet-help-vive.jpg";
|
||||
|
@ -154,7 +161,7 @@
|
|||
switch (params.defaultTab) {
|
||||
case "gamepad":
|
||||
showGamepad();
|
||||
index = 2;
|
||||
index = 3;
|
||||
break;
|
||||
|
||||
case "handControllers":
|
||||
|
@ -164,7 +171,7 @@
|
|||
case "kbm":
|
||||
default:
|
||||
showKbm();
|
||||
index = 3;
|
||||
index = 4;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -263,6 +263,12 @@ Item {
|
|||
}
|
||||
StatText {
|
||||
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: "GPU (Per pixel): " + root.gpuFrameTimePerPixel.toFixed(5) + " ns/pp"
|
||||
}
|
||||
StatText {
|
||||
text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y
|
||||
}
|
||||
StatText {
|
||||
text: "Triangles: " + root.triangles +
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
//
|
||||
// Button.qml
|
||||
//
|
||||
// Created by David Rowe on 16 Feb 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
import "../styles-uit"
|
||||
|
||||
Original.Button {
|
||||
id: root;
|
||||
|
||||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string buttonGlyph: "";
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
height: hifi.dimensions.controlLineHeight
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
||||
background: Rectangle {
|
||||
radius: hifi.buttons.radius
|
||||
|
||||
border.width: (control.color === hifi.buttons.none ||
|
||||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
||||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
|
||||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
|
||||
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
|
||||
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.2
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
hifi.buttons.disabledColorStart[control.colorScheme]
|
||||
} else if (control.pressed) {
|
||||
hifi.buttons.pressedColor[control.color]
|
||||
} else if (control.hovered) {
|
||||
hifi.buttons.hoveredColor[control.color]
|
||||
} else if (!control.hovered && control.focus) {
|
||||
hifi.buttons.focusedColor[control.color]
|
||||
} else {
|
||||
hifi.buttons.colorStart[control.color]
|
||||
}
|
||||
}
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
hifi.buttons.disabledColorFinish[control.colorScheme]
|
||||
} else if (control.pressed) {
|
||||
hifi.buttons.pressedColor[control.color]
|
||||
} else if (control.hovered) {
|
||||
hifi.buttons.hoveredColor[control.color]
|
||||
} else if (!control.hovered && control.focus) {
|
||||
hifi.buttons.focusedColor[control.color]
|
||||
} else {
|
||||
hifi.buttons.colorFinish[control.color]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label: Item {
|
||||
HiFiGlyphs {
|
||||
id: buttonGlyph;
|
||||
visible: root.buttonGlyph !== "";
|
||||
text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph;
|
||||
// Size
|
||||
size: 34;
|
||||
// Anchors
|
||||
anchors.right: buttonText.left;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
// Style
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme];
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewayBold {
|
||||
id: buttonText;
|
||||
anchors.centerIn: parent;
|
||||
font.capitalization: Font.AllUppercase
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: hifi.fontSizes.buttonLabel
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
//
|
||||
// Table.qml
|
||||
//
|
||||
// Created by David Rowe on 18 Feb 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Controls 2.2 as QQC2
|
||||
|
||||
import "../styles-uit"
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
|
||||
property bool expandSelectedRow: false
|
||||
property bool centerHeaderText: false
|
||||
readonly property real headerSpacing: 3 //spacing between sort indicator and table header title
|
||||
property var titlePaintedPos: [] // storing extra data position behind painted
|
||||
// title text and sort indicatorin table's header
|
||||
signal titlePaintedPosSignal(int column) //signal that extradata position gets changed
|
||||
|
||||
model: ListModel { }
|
||||
|
||||
Component.onCompleted: {
|
||||
if (flickableItem !== null && flickableItem !== undefined) {
|
||||
tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ScrollBar {
|
||||
id: scrollbar
|
||||
parent: tableView.flickableItem
|
||||
policy: QQC2.ScrollBar.AsNeeded
|
||||
orientation: Qt.Vertical
|
||||
visible: size < 1.0
|
||||
topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1
|
||||
anchors.top: tableView.top
|
||||
anchors.left: tableView.right
|
||||
anchors.bottom: tableView.bottom
|
||||
|
||||
background: Item {
|
||||
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
|
||||
Rectangle {
|
||||
anchors {
|
||||
fill: parent;
|
||||
topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0
|
||||
}
|
||||
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight
|
||||
: hifi.colors.tableScrollBackgroundDark
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: hifi.dimensions.scrollbarHandleWidth
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: (width - 4)/2
|
||||
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headerVisible: false
|
||||
headerDelegate: Rectangle {
|
||||
height: hifi.dimensions.tableHeaderHeight
|
||||
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||
|
||||
|
||||
RalewayRegular {
|
||||
id: titleText
|
||||
x: centerHeaderText ? (parent.width - paintedWidth -
|
||||
((sortIndicatorVisible &&
|
||||
sortIndicatorColumn === styleData.column) ?
|
||||
(titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 :
|
||||
hifi.dimensions.tablePadding
|
||||
text: styleData.value
|
||||
size: hifi.fontSizes.tableHeading
|
||||
font.capitalization: Font.AllUppercase
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
//actual image of sort indicator in glyph font only 20% of real font size
|
||||
//i.e. if the charachter size set to 60 pixels, actual image is 12 pixels
|
||||
HiFiGlyphs {
|
||||
id: titleSort
|
||||
text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn
|
||||
color: hifi.colors.darkGray
|
||||
opacity: 0.6;
|
||||
size: hifi.fontSizes.tableHeadingIcon
|
||||
anchors.verticalCenter: titleText.verticalCenter
|
||||
anchors.left: titleText.right
|
||||
anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing
|
||||
visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column
|
||||
onXChanged: {
|
||||
titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth +
|
||||
paintedWidth / 5 + tableView.headerSpacing*2
|
||||
titlePaintedPosSignal(styleData.column)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
topMargin: 1
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 2
|
||||
}
|
||||
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||
visible: styleData.column > 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||
}
|
||||
}
|
||||
|
||||
// Use rectangle to draw border with rounded corners.
|
||||
frameVisible: false
|
||||
Rectangle {
|
||||
color: "#00000000"
|
||||
anchors { fill: parent; margins: -2 }
|
||||
border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||
border.width: 2
|
||||
}
|
||||
anchors.margins: 2 // Shrink TableView to lie within border.
|
||||
|
||||
backgroundVisible: true
|
||||
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
|
||||
style: TableViewStyle {
|
||||
// Needed in order for rows to keep displaying rows after end of table entries.
|
||||
backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||
alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
|
||||
padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0
|
||||
}
|
||||
|
||||
rowDelegate: Rectangle {
|
||||
height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight
|
||||
color: styleData.selected
|
||||
? hifi.colors.primaryHighlight
|
||||
: tableView.isLightColorScheme
|
||||
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
|
||||
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
|
||||
}
|
||||
}
|
|
@ -46,7 +46,6 @@ FocusScope {
|
|||
hoverEnabled: true
|
||||
visible: true
|
||||
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
|
||||
textRole: "text"
|
||||
|
||||
function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; }
|
||||
function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; }
|
||||
|
|
|
@ -1,575 +0,0 @@
|
|||
//
|
||||
// Desktop.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 15 Apr 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../dialogs"
|
||||
import "../js/Utils.js" as Utils
|
||||
|
||||
// This is our primary 'desktop' object to which all VR dialogs and windows are childed.
|
||||
FocusScope {
|
||||
id: desktop
|
||||
objectName: "desktop"
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property int invalid_position: -9999;
|
||||
property rect recommendedRect: Qt.rect(0,0,0,0);
|
||||
property var expectedChildren;
|
||||
property bool repositionLocked: true
|
||||
property bool hmdHandMouseActive: false
|
||||
|
||||
onRepositionLockedChanged: {
|
||||
if (!repositionLocked) {
|
||||
d.handleSizeChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onHeightChanged: d.handleSizeChanged();
|
||||
|
||||
onWidthChanged: d.handleSizeChanged();
|
||||
|
||||
// Controls and windows can trigger this signal to ensure the desktop becomes visible
|
||||
// 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
|
||||
|
||||
// The VR version of the primary menu
|
||||
property var rootMenu: Menu {
|
||||
id: rootMenuId
|
||||
objectName: "rootMenu"
|
||||
|
||||
property var exclusionGroups: ({});
|
||||
property Component exclusiveGroupMaker: Component {
|
||||
ExclusiveGroup {
|
||||
}
|
||||
}
|
||||
|
||||
function addExclusionGroup(qmlAction, exclusionGroup) {
|
||||
|
||||
var exclusionGroupId = exclusionGroup.toString();
|
||||
if(!exclusionGroups[exclusionGroupId]) {
|
||||
exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId);
|
||||
}
|
||||
|
||||
qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId]
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX/AMD
|
||||
// because shaders are 4.2, and do not include #version declarations.
|
||||
property bool gradientsSupported: Qt.platform.os != "osx" && !~GL.vendor.indexOf("ATI")
|
||||
|
||||
readonly property alias zLevels: zLevels
|
||||
QtObject {
|
||||
id: zLevels;
|
||||
readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls.
|
||||
readonly property real top: 2000
|
||||
readonly property real modal: 4000
|
||||
readonly property real menu: 8000
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function handleSizeChanged() {
|
||||
if (desktop.repositionLocked) {
|
||||
return;
|
||||
}
|
||||
var oldRecommendedRect = recommendedRect;
|
||||
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
|
||||
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
|
||||
newRecommendedRectJS.width,
|
||||
newRecommendedRectJS.height);
|
||||
|
||||
var oldChildren = expectedChildren;
|
||||
var newChildren = d.getRepositionChildren();
|
||||
if (oldRecommendedRect != Qt.rect(0,0,0,0) && oldRecommendedRect != Qt.rect(0,0,1,1)
|
||||
&& (oldRecommendedRect != newRecommendedRect
|
||||
|| oldChildren != newChildren)
|
||||
) {
|
||||
expectedChildren = newChildren;
|
||||
d.repositionAll();
|
||||
}
|
||||
recommendedRect = newRecommendedRect;
|
||||
}
|
||||
|
||||
function findChild(item, name) {
|
||||
for (var i = 0; i < item.children.length; ++i) {
|
||||
if (item.children[i].objectName === name) {
|
||||
return item.children[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findParentMatching(item, predicate) {
|
||||
while (item) {
|
||||
if (predicate(item)) {
|
||||
break;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
function findMatchingChildren(item, predicate) {
|
||||
var results = [];
|
||||
for (var i in item.children) {
|
||||
var child = item.children[i];
|
||||
if (predicate(child)) {
|
||||
results.push(child);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function isTopLevelWindow(item) {
|
||||
return item.topLevelWindow;
|
||||
}
|
||||
|
||||
function isAlwaysOnTopWindow(window) {
|
||||
return window.alwaysOnTop;
|
||||
}
|
||||
|
||||
function isModalWindow(window) {
|
||||
return window.modality !== Qt.NonModal;
|
||||
}
|
||||
|
||||
function getTopLevelWindows(predicate) {
|
||||
return findMatchingChildren(desktop, function(child) {
|
||||
return (isTopLevelWindow(child) && (!predicate || predicate(child)));
|
||||
});
|
||||
}
|
||||
|
||||
function getDesktopWindow(item) {
|
||||
return findParentMatching(item, isTopLevelWindow)
|
||||
}
|
||||
|
||||
function fixupZOrder(windows, basis, topWindow) {
|
||||
windows.sort(function(a, b){ return a.z - b.z; });
|
||||
|
||||
if ((topWindow.z >= basis) && (windows[windows.length - 1] === topWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastZ = -1;
|
||||
var lastTargetZ = basis - 1;
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
var window = windows[i];
|
||||
if (!window.visible) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (topWindow && (topWindow === window)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (window.z > lastZ) {
|
||||
lastZ = window.z;
|
||||
++lastTargetZ;
|
||||
}
|
||||
if (DebugQML) {
|
||||
console.log("Assigning z order " + lastTargetZ + " to " + window)
|
||||
}
|
||||
|
||||
window.z = lastTargetZ;
|
||||
}
|
||||
if (topWindow) {
|
||||
++lastTargetZ;
|
||||
if (DebugQML) {
|
||||
console.log("Assigning z order " + lastTargetZ + " to " + topWindow)
|
||||
}
|
||||
topWindow.z = lastTargetZ;
|
||||
}
|
||||
|
||||
return lastTargetZ;
|
||||
}
|
||||
|
||||
function raiseWindow(targetWindow) {
|
||||
var predicate;
|
||||
var zBasis;
|
||||
if (isModalWindow(targetWindow)) {
|
||||
predicate = isModalWindow;
|
||||
zBasis = zLevels.modal
|
||||
} else if (isAlwaysOnTopWindow(targetWindow)) {
|
||||
predicate = function(window) {
|
||||
return (isAlwaysOnTopWindow(window) && !isModalWindow(window));
|
||||
}
|
||||
zBasis = zLevels.top
|
||||
} else {
|
||||
predicate = function(window) {
|
||||
return (!isAlwaysOnTopWindow(window) && !isModalWindow(window));
|
||||
}
|
||||
zBasis = zLevels.normal
|
||||
}
|
||||
|
||||
var windows = getTopLevelWindows(predicate);
|
||||
fixupZOrder(windows, zBasis, targetWindow);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
|
||||
focusHack.start();
|
||||
}
|
||||
|
||||
function onWindowFocusChanged() {
|
||||
//console.log("Focus item is " + offscreenWindow.activeFocusItem);
|
||||
|
||||
// FIXME this needs more testing before it can go into production
|
||||
// and I already cant produce any way to have a modal dialog lose focus
|
||||
// to a non-modal one.
|
||||
/*
|
||||
var focusedWindow = getDesktopWindow(offscreenWindow.activeFocusItem);
|
||||
|
||||
if (isModalWindow(focusedWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new focused window is not modal... check if there are any modal windows
|
||||
var windows = getTopLevelWindows(isModalWindow);
|
||||
if (0 === windows.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There are modal windows present, force focus back to the top-most modal window
|
||||
windows.sort(function(a, b){ return a.z - b.z; });
|
||||
windows[windows.length - 1].focus = true;
|
||||
*/
|
||||
|
||||
// var focusedItem = offscreenWindow.activeFocusItem ;
|
||||
// if (DebugQML && focusedItem) {
|
||||
// var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height);
|
||||
// focusDebugger.x = rect.x;
|
||||
// focusDebugger.y = rect.y;
|
||||
// focusDebugger.width = rect.width
|
||||
// focusDebugger.height = rect.height
|
||||
// }
|
||||
}
|
||||
|
||||
function getRepositionChildren(predicate) {
|
||||
return findMatchingChildren(desktop, function(child) {
|
||||
return (child.shouldReposition === true && (!predicate || predicate(child)));
|
||||
});
|
||||
}
|
||||
|
||||
function repositionAll() {
|
||||
if (desktop.repositionLocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
var oldRecommendedRect = recommendedRect;
|
||||
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
|
||||
var newRecommendedRect = Controller.getRecommendedHUDRect();
|
||||
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
|
||||
var windows = d.getTopLevelWindows();
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
var targetWindow = windows[i];
|
||||
if (targetWindow.visible) {
|
||||
repositionWindow(targetWindow, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||
}
|
||||
}
|
||||
|
||||
// also reposition the other children that aren't top level windows but want to be repositioned
|
||||
var otherChildren = d.getRepositionChildren();
|
||||
for (var i = 0; i < otherChildren.length; ++i) {
|
||||
var child = otherChildren[i];
|
||||
repositionWindow(child, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
property bool pinned: false
|
||||
property var hiddenChildren: []
|
||||
|
||||
function togglePinned() {
|
||||
pinned = !pinned
|
||||
}
|
||||
|
||||
function isPointOnWindow(point) {
|
||||
for (var i = 0; i < desktop.visibleChildren.length; i++) {
|
||||
var child = desktop.visibleChildren[i];
|
||||
if (child.hasOwnProperty("modality")) {
|
||||
var mappedPoint = mapToItem(child, point.x, point.y);
|
||||
if (child.hasOwnProperty("frame")) {
|
||||
var outLine = child.frame.children[2];
|
||||
var framePoint = outLine.mapFromGlobal(point.x, point.y);
|
||||
if (outLine.contains(framePoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (child.contains(mappedPoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setPinned(newPinned) {
|
||||
pinned = newPinned
|
||||
}
|
||||
|
||||
property real unpinnedAlpha: 1.0;
|
||||
|
||||
Behavior on unpinnedAlpha {
|
||||
NumberAnimation {
|
||||
easing.type: Easing.Linear;
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
|
||||
state: "NORMAL"
|
||||
states: [
|
||||
State {
|
||||
name: "NORMAL"
|
||||
PropertyChanges { target: desktop; unpinnedAlpha: 1.0 }
|
||||
},
|
||||
State {
|
||||
name: "PINNED"
|
||||
PropertyChanges { target: desktop; unpinnedAlpha: 0.0 }
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
NumberAnimation { properties: "unpinnedAlpha"; duration: 300 }
|
||||
}
|
||||
]
|
||||
|
||||
onPinnedChanged: {
|
||||
if (pinned) {
|
||||
d.raiseWindow(desktop);
|
||||
desktop.focus = true;
|
||||
desktop.forceActiveFocus();
|
||||
|
||||
// recalculate our non-pinned children
|
||||
hiddenChildren = d.findMatchingChildren(desktop, function(child){
|
||||
return !d.isTopLevelWindow(child) && child.visible && !child.pinned;
|
||||
});
|
||||
|
||||
hiddenChildren.forEach(function(child){
|
||||
child.opacity = Qt.binding(function(){ return desktop.unpinnedAlpha });
|
||||
});
|
||||
}
|
||||
state = pinned ? "PINNED" : "NORMAL"
|
||||
}
|
||||
|
||||
onShowDesktop: pinned = false
|
||||
|
||||
function raise(item) {
|
||||
var targetWindow = d.getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix up the Z-order (takes into account if this is a modal window)
|
||||
d.raiseWindow(targetWindow);
|
||||
var setFocus = true;
|
||||
if (!d.isModalWindow(targetWindow)) {
|
||||
var modalWindows = d.getTopLevelWindows(d.isModalWindow);
|
||||
if (modalWindows.length) {
|
||||
setFocus = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (setFocus) {
|
||||
targetWindow.focus = true;
|
||||
}
|
||||
|
||||
showDesktop();
|
||||
}
|
||||
|
||||
function ensureTitleBarVisible(targetWindow) {
|
||||
// Reposition window to ensure that title bar is vertically inside window.
|
||||
if (targetWindow.frame && targetWindow.frame.decoration) {
|
||||
var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value.
|
||||
targetWindow.y = Math.max(targetWindow.y, topMargin);
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnVisible(item) {
|
||||
var targetWindow = d.getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof Controller === "undefined") {
|
||||
console.warn("Controller not yet available... can't center");
|
||||
return;
|
||||
}
|
||||
|
||||
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
|
||||
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
|
||||
newRecommendedRectJS.width,
|
||||
newRecommendedRectJS.height);
|
||||
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
|
||||
var newX = newRecommendedRect.x + ((newRecommendedRect.width - targetWindow.width) / 2);
|
||||
var newY = newRecommendedRect.y + ((newRecommendedRect.height - targetWindow.height) / 2);
|
||||
targetWindow.x = newX;
|
||||
targetWindow.y = newY;
|
||||
|
||||
ensureTitleBarVisible(targetWindow);
|
||||
|
||||
// If we've noticed that our recommended desktop rect has changed, record that change here.
|
||||
if (recommendedRect != newRecommendedRect) {
|
||||
recommendedRect = newRecommendedRect;
|
||||
}
|
||||
}
|
||||
|
||||
function repositionOnVisible(item) {
|
||||
var targetWindow = d.getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof Controller === "undefined") {
|
||||
console.warn("Controller not yet available... can't reposition targetWindow:" + targetWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
var oldRecommendedRect = recommendedRect;
|
||||
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
|
||||
var newRecommendedRect = Controller.getRecommendedHUDRect();
|
||||
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
|
||||
repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||
}
|
||||
|
||||
function repositionWindow(targetWindow, forceReposition,
|
||||
oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions) {
|
||||
|
||||
if (desktop.width === 0 || desktop.height === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
var recommended = Controller.getRecommendedHUDRect();
|
||||
var maxX = recommended.x + recommended.width;
|
||||
var maxY = recommended.y + recommended.height;
|
||||
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
|
||||
|
||||
// if we asked to force reposition, or if the window is completely outside of the recommended rectangle, reposition it
|
||||
if (forceReposition || (targetWindow.x > maxX || (targetWindow.x + targetWindow.width) < recommended.x) ||
|
||||
(targetWindow.y > maxY || (targetWindow.y + targetWindow.height) < recommended.y)) {
|
||||
newPosition.x = -1
|
||||
newPosition.y = -1
|
||||
}
|
||||
|
||||
if (newPosition.x === -1 && newPosition.y === -1) {
|
||||
var originRelativeX = (targetWindow.x - oldRecommendedRect.x);
|
||||
var originRelativeY = (targetWindow.y - oldRecommendedRect.y);
|
||||
if (isNaN(originRelativeX)) {
|
||||
originRelativeX = 0;
|
||||
}
|
||||
if (isNaN(originRelativeY)) {
|
||||
originRelativeY = 0;
|
||||
}
|
||||
var fractionX = Utils.clamp(originRelativeX / oldRecommendedDimmensions.x, 0, 1);
|
||||
var fractionY = Utils.clamp(originRelativeY / oldRecommendedDimmensions.y, 0, 1);
|
||||
var newX = (fractionX * newRecommendedDimmensions.x) + newRecommendedRect.x;
|
||||
var newY = (fractionY * newRecommendedDimmensions.y) + newRecommendedRect.y;
|
||||
newPosition = Qt.vector2d(newX, newY);
|
||||
}
|
||||
targetWindow.x = newPosition.x;
|
||||
targetWindow.y = newPosition.y;
|
||||
|
||||
ensureTitleBarVisible(targetWindow);
|
||||
}
|
||||
|
||||
Component { id: messageDialogBuilder; MessageDialog { } }
|
||||
function messageBox(properties) {
|
||||
return messageDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: inputDialogBuilder; QueryDialog { } }
|
||||
function inputDialog(properties) {
|
||||
return inputDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: customInputDialogBuilder; CustomQueryDialog { } }
|
||||
function customInputDialog(properties) {
|
||||
return customInputDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: fileDialogBuilder; FileDialog { } }
|
||||
function fileDialog(properties) {
|
||||
return fileDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
Component { id: assetDialogBuilder; AssetDialog { } }
|
||||
function assetDialog(properties) {
|
||||
return assetDialogBuilder.createObject(desktop, properties);
|
||||
}
|
||||
|
||||
function unfocusWindows() {
|
||||
// First find the active focus item, and unfocus it, all the way
|
||||
// up the parent chain to the window
|
||||
var currentFocus = offscreenWindow.activeFocusItem;
|
||||
var targetWindow = d.getDesktopWindow(currentFocus);
|
||||
while (currentFocus) {
|
||||
if (currentFocus === targetWindow) {
|
||||
break;
|
||||
}
|
||||
currentFocus.focus = false;
|
||||
currentFocus = currentFocus.parent;
|
||||
}
|
||||
|
||||
// Unfocus all windows
|
||||
var windows = d.getTopLevelWindows();
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
windows[i].focus = false;
|
||||
}
|
||||
|
||||
// For the desktop to have active focus
|
||||
desktop.focus = true;
|
||||
desktop.forceActiveFocus();
|
||||
}
|
||||
|
||||
function openBrowserWindow(request, profile) {
|
||||
var component = Qt.createComponent("../Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
newWindow.webView.profile = profile;
|
||||
request.openIn(newWindow.webView);
|
||||
}
|
||||
|
||||
FocusHack { id: focusHack; }
|
||||
|
||||
Rectangle {
|
||||
id: focusDebugger;
|
||||
objectName: "focusDebugger"
|
||||
z: 9999; visible: false; color: "red"
|
||||
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
|
||||
}
|
||||
|
||||
Action {
|
||||
text: "Toggle Focus Debugger"
|
||||
shortcut: "Ctrl+Shift+F"
|
||||
enabled: DebugQML
|
||||
onTriggered: focusDebugger.visible = !focusDebugger.visible
|
||||
}
|
||||
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
//
|
||||
// CustomQueryDialog.qml
|
||||
//
|
||||
// Created by Zander Otavka on 7/14/16
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7;
|
||||
import QtQuick.Dialogs 1.2 as OriginalDialogs;
|
||||
import QtQuick.Controls 1.4;
|
||||
|
||||
import "../controls-uit";
|
||||
import "../styles-uit";
|
||||
import "../windows";
|
||||
|
||||
ModalWindow {
|
||||
id: root;
|
||||
HifiConstants { id: hifi; }
|
||||
implicitWidth: 640;
|
||||
implicitHeight: 320;
|
||||
visible: true;
|
||||
keyboardOverride: true // Disable ModalWindow's keyboard.
|
||||
|
||||
signal selected(var result);
|
||||
signal canceled();
|
||||
|
||||
property int icon: hifi.icons.none;
|
||||
property string iconText: "";
|
||||
property int iconSize: 35;
|
||||
onIconChanged: updateIcon();
|
||||
|
||||
property var textInput;
|
||||
property var comboBox;
|
||||
property var checkBox;
|
||||
onTextInputChanged: {
|
||||
if (textInput && textInput.text !== undefined) {
|
||||
textField.text = textInput.text;
|
||||
}
|
||||
}
|
||||
onComboBoxChanged: {
|
||||
if (comboBox && comboBox.index !== undefined) {
|
||||
comboBoxField.currentIndex = comboBox.index;
|
||||
}
|
||||
}
|
||||
onCheckBoxChanged: {
|
||||
if (checkBox && checkBox.checked !== undefined) {
|
||||
checkBoxField.checked = checkBox.checked;
|
||||
}
|
||||
}
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
property var warning: "";
|
||||
property var result;
|
||||
|
||||
property var implicitCheckState: null;
|
||||
|
||||
property int titleWidth: 0;
|
||||
onTitleWidthChanged: d.resize();
|
||||
|
||||
function updateIcon() {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
iconText = hifi.glyphForIcon(root.icon);
|
||||
}
|
||||
|
||||
function updateCheckbox() {
|
||||
if (checkBox.disableForItems) {
|
||||
var currentItemInDisableList = false;
|
||||
for (var i in checkBox.disableForItems) {
|
||||
if (comboBoxField.currentIndex === checkBox.disableForItems[i]) {
|
||||
currentItemInDisableList = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentItemInDisableList) {
|
||||
checkBoxField.enabled = false;
|
||||
if (checkBox.checkStateOnDisable !== null && checkBox.checkStateOnDisable !== undefined) {
|
||||
root.implicitCheckState = checkBoxField.checked;
|
||||
checkBoxField.checked = checkBox.checkStateOnDisable;
|
||||
}
|
||||
root.warning = checkBox.warningOnDisable;
|
||||
} else {
|
||||
checkBoxField.enabled = true;
|
||||
if (root.implicitCheckState !== null) {
|
||||
checkBoxField.checked = root.implicitCheckState;
|
||||
root.implicitCheckState = null;
|
||||
}
|
||||
root.warning = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
clip: true;
|
||||
width: pane.width;
|
||||
height: pane.height;
|
||||
anchors.margins: 0;
|
||||
|
||||
QtObject {
|
||||
id: d;
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWdith: 1280
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, pane.width);
|
||||
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
|
||||
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
|
||||
(buttons.height + 3 * hifi.dimensions.contentSpacing.y) +
|
||||
((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0);
|
||||
|
||||
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
|
||||
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
|
||||
d.maxHeight : targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors {
|
||||
top: parent.top;
|
||||
bottom: extraInputs.visible ? extraInputs.top : buttons.top;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
margins: 0;
|
||||
}
|
||||
|
||||
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||
TextField {
|
||||
id: textField;
|
||||
label: root.textInput.label;
|
||||
focus: root.textInput ? true : false;
|
||||
visible: root.textInput ? true : false;
|
||||
anchors {
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
bottom: keyboard.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
raised: keyboardEnabled && keyboardRaised
|
||||
numeric: punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
bottomMargin: raised ? hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: extraInputs;
|
||||
visible: Boolean(root.checkBox || root.comboBox);
|
||||
anchors {
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
bottom: buttons.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
height: comboBoxField.controlHeight;
|
||||
onHeightChanged: d.resize();
|
||||
onWidthChanged: d.resize();
|
||||
|
||||
CheckBox {
|
||||
id: checkBoxField;
|
||||
text: root.checkBox.label;
|
||||
focus: Boolean(root.checkBox);
|
||||
visible: Boolean(root.checkBox);
|
||||
anchors {
|
||||
left: parent.left;
|
||||
bottom: parent.bottom;
|
||||
leftMargin: 6; // Magic number to align with warning icon
|
||||
bottomMargin: 6;
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: comboBoxField;
|
||||
label: root.comboBox.label;
|
||||
focus: Boolean(root.comboBox);
|
||||
visible: Boolean(root.comboBox);
|
||||
Binding on x {
|
||||
when: comboBoxField.visible
|
||||
value: !checkBoxField.visible ? buttons.x : acceptButton.x
|
||||
}
|
||||
|
||||
Binding on width {
|
||||
when: comboBoxField.visible
|
||||
value: !checkBoxField.visible ? buttons.width : buttons.width - acceptButton.x
|
||||
}
|
||||
anchors {
|
||||
right: parent.right;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
model: root.comboBox ? root.comboBox.items : [];
|
||||
onAccepted: {
|
||||
updateCheckbox();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons;
|
||||
focus: true;
|
||||
spacing: hifi.dimensions.contentSpacing.x;
|
||||
layoutDirection: Qt.RightToLeft;
|
||||
onHeightChanged: d.resize();
|
||||
onWidthChanged: {
|
||||
d.resize();
|
||||
resizeWarningText();
|
||||
}
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
}
|
||||
|
||||
function resizeWarningText() {
|
||||
var rowWidth = buttons.width;
|
||||
var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2;
|
||||
var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x;
|
||||
warningText.width = rowWidth - buttonsWidth - warningIconWidth;
|
||||
}
|
||||
|
||||
Button {
|
||||
id: cancelButton;
|
||||
action: cancelAction;
|
||||
}
|
||||
|
||||
Button {
|
||||
id: acceptButton;
|
||||
action: acceptAction;
|
||||
}
|
||||
|
||||
Text {
|
||||
id: warningText;
|
||||
visible: Boolean(root.warning);
|
||||
text: root.warning;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.italic: true;
|
||||
maximumLineCount: 3;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: warningIcon;
|
||||
visible: Boolean(root.warning);
|
||||
text: hifi.glyphs.alert;
|
||||
size: hifi.dimensions.controlLineHeight;
|
||||
width: 20 // Line up with checkbox.
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction;
|
||||
text: qsTr("Cancel");
|
||||
shortcut: "Esc";
|
||||
onTriggered: {
|
||||
root.result = null;
|
||||
root.canceled();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: acceptAction;
|
||||
text: qsTr("Add");
|
||||
shortcut: "Return"
|
||||
onTriggered: {
|
||||
var result = {};
|
||||
if (textInput) {
|
||||
result.textInput = textField.text;
|
||||
}
|
||||
if (comboBox) {
|
||||
result.comboBox = comboBoxField.currentIndex;
|
||||
result.comboBoxText = comboBoxField.currentText;
|
||||
}
|
||||
if (checkBox) {
|
||||
result.checkBox = checkBoxField.enabled ? checkBoxField.checked : null;
|
||||
}
|
||||
root.result = JSON.stringify(result);
|
||||
root.selected(root.result);
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
cancelAction.trigger();
|
||||
event.accepted = true;
|
||||
break;
|
||||
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
acceptAction.trigger();
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
updateIcon();
|
||||
updateCheckbox();
|
||||
d.resize();
|
||||
textField.forceActiveFocus();
|
||||
}
|
||||
}
|
|
@ -1,840 +0,0 @@
|
|||
//
|
||||
// FileDialog.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 14 Jan 2016
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import Qt.labs.folderlistmodel 2.1
|
||||
import Qt.labs.settings 1.0
|
||||
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import ".."
|
||||
import "../controls-uit"
|
||||
import "../styles-uit"
|
||||
import "../windows"
|
||||
|
||||
import "fileDialog"
|
||||
|
||||
//FIXME implement shortcuts for favorite location
|
||||
ModalWindow {
|
||||
id: root
|
||||
resizable: true
|
||||
implicitWidth: 480
|
||||
implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0)
|
||||
|
||||
minSize: Qt.vector2d(360, 240)
|
||||
draggable: true
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
property var filesModel: ListModel { }
|
||||
|
||||
Settings {
|
||||
category: "FileDialog"
|
||||
property alias width: root.width
|
||||
property alias height: root.height
|
||||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
}
|
||||
|
||||
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias caption: root.title;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias dir: fileTableModel.folder;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property alias filter: selectionType.filtersString;
|
||||
// Set from OffscreenUi::getOpenFile()
|
||||
property int options; // <-- FIXME unused
|
||||
|
||||
property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : ""
|
||||
property int iconSize: 40
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: true;
|
||||
// FIXME implement
|
||||
property bool multiSelect: false;
|
||||
property bool saveDialog: false;
|
||||
property var helper: fileDialogHelper
|
||||
property alias model: fileTableView.model
|
||||
property var drives: helper.drives()
|
||||
|
||||
property int titleWidth: 0
|
||||
|
||||
signal selectedFile(var file);
|
||||
signal canceled();
|
||||
signal selected(int button);
|
||||
function click(button) {
|
||||
clickedButton = button;
|
||||
selected(button);
|
||||
destroy();
|
||||
}
|
||||
|
||||
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Helper " + helper + " drives " + drives);
|
||||
|
||||
fileDialogItem.keyboardEnabled = HMD.active;
|
||||
|
||||
// HACK: The following lines force the model to initialize properly such that the go-up button
|
||||
// works properly from the initial screen.
|
||||
var initialFolder = folderListModel.folder;
|
||||
fileTableModel.folder = helper.pathToUrl(drives[0]);
|
||||
fileTableModel.folder = initialFolder;
|
||||
|
||||
iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";
|
||||
|
||||
// Clear selection when click on external frame.
|
||||
frameClicked.connect(function() { d.clearSelection(); });
|
||||
|
||||
if (selectDirectory) {
|
||||
currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder));
|
||||
d.currentSelectionIsFolder = true;
|
||||
d.currentSelectionUrl = initialFolder;
|
||||
}
|
||||
|
||||
helper.contentsChanged.connect(function() {
|
||||
if (folderListModel) {
|
||||
// Make folderListModel refresh.
|
||||
var save = folderListModel.folder;
|
||||
folderListModel.folder = "";
|
||||
folderListModel.folder = save;
|
||||
}
|
||||
});
|
||||
|
||||
focusTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: focusTimer
|
||||
interval: 10
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
fileTableView.contentItem.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: fileDialogItem
|
||||
clip: true
|
||||
width: pane.width
|
||||
height: pane.height
|
||||
anchors.margins: 0
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
MouseArea {
|
||||
// Clear selection when click on internal unused area.
|
||||
anchors.fill: parent
|
||||
drag.target: root
|
||||
onClicked: {
|
||||
d.clearSelection();
|
||||
// Defocus text field so that the keyboard gets hidden.
|
||||
// Clicking also breaks keyboard navigation apart from backtabbing to cancel
|
||||
frame.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: navControls
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: hifi.dimensions.contentMargin.y
|
||||
left: parent.left
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
|
||||
GlyphButton {
|
||||
id: upButton
|
||||
glyph: hifi.glyphs.levelUp
|
||||
width: height
|
||||
size: 30
|
||||
enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""
|
||||
onClicked: d.navigateUp();
|
||||
Keys.onReturnPressed: { d.navigateUp(); }
|
||||
KeyNavigation.tab: homeButton
|
||||
KeyNavigation.backtab: upButton
|
||||
KeyNavigation.left: upButton
|
||||
KeyNavigation.right: homeButton
|
||||
}
|
||||
|
||||
GlyphButton {
|
||||
id: homeButton
|
||||
property var destination: helper.home();
|
||||
glyph: hifi.glyphs.home
|
||||
size: 28
|
||||
width: height
|
||||
enabled: d.homeDestination ? true : false
|
||||
onClicked: d.navigateHome();
|
||||
Keys.onReturnPressed: { d.navigateHome(); }
|
||||
KeyNavigation.tab: fileTableView.contentItem
|
||||
KeyNavigation.backtab: upButton
|
||||
KeyNavigation.left: upButton
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: pathSelector
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: hifi.dimensions.contentMargin.y
|
||||
left: navControls.right
|
||||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
property var lastValidFolder: helper.urlToPath(fileTableModel.folder)
|
||||
|
||||
function calculatePathChoices(folder) {
|
||||
var folders = folder.split("/"),
|
||||
choices = [],
|
||||
i, length;
|
||||
|
||||
if (folders[folders.length - 1] === "") {
|
||||
folders.pop();
|
||||
}
|
||||
|
||||
choices.push(folders[0]);
|
||||
|
||||
for (i = 1, length = folders.length; i < length; i++) {
|
||||
choices.push(choices[i - 1] + "/" + folders[i]);
|
||||
}
|
||||
|
||||
if (folders[0] === "") {
|
||||
// Special handling for OSX root dir.
|
||||
choices[0] = "/";
|
||||
}
|
||||
|
||||
choices.reverse();
|
||||
|
||||
if (drives && drives.length > 1) {
|
||||
choices.push("This PC");
|
||||
}
|
||||
|
||||
if (choices.length > 0) {
|
||||
pathSelector.model = choices;
|
||||
}
|
||||
}
|
||||
|
||||
onLastValidFolderChanged: {
|
||||
var folder = d.capitalizeDrive(lastValidFolder);
|
||||
calculatePathChoices(folder);
|
||||
}
|
||||
|
||||
onCurrentTextChanged: {
|
||||
var folder = currentText;
|
||||
|
||||
if (/^[a-zA-z]:$/.test(folder)) {
|
||||
folder = "file:///" + folder + "/";
|
||||
} else if (folder === "This PC") {
|
||||
folder = "file:///";
|
||||
} else {
|
||||
folder = helper.pathToUrl(folder);
|
||||
}
|
||||
|
||||
if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) {
|
||||
if (root.selectDirectory) {
|
||||
currentSelection.text = currentText !== "This PC" ? currentText : "";
|
||||
d.currentSelectionUrl = helper.pathToUrl(currentText);
|
||||
}
|
||||
fileTableModel.folder = folder;
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.up: fileTableView.contentItem
|
||||
KeyNavigation.down: fileTableView.contentItem
|
||||
KeyNavigation.tab: fileTableView.contentItem
|
||||
KeyNavigation.backtab: fileTableView.contentItem
|
||||
KeyNavigation.left: fileTableView.contentItem
|
||||
KeyNavigation.right: fileTableView.contentItem
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property var currentSelectionUrl;
|
||||
readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl);
|
||||
property bool currentSelectionIsFolder;
|
||||
property var backStack: []
|
||||
property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); }
|
||||
property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); }
|
||||
property var homeDestination: helper.home();
|
||||
|
||||
function capitalizeDrive(path) {
|
||||
// Consistently capitalize drive letter for Windows.
|
||||
if (/[a-zA-Z]:/.test(path)) {
|
||||
return path.charAt(0).toUpperCase() + path.slice(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function update() {
|
||||
var row = fileTableView.currentRow;
|
||||
|
||||
if (row === -1) {
|
||||
if (!root.selectDirectory) {
|
||||
currentSelection.text = "";
|
||||
currentSelectionIsFolder = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
|
||||
currentSelectionIsFolder = fileTableView.model !== filesModel ?
|
||||
fileTableView.model.isFolder(row) :
|
||||
fileTableModel.isFolder(row);
|
||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
|
||||
} else {
|
||||
currentSelection.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
function navigateUp() {
|
||||
if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") {
|
||||
fileTableModel.folder = fileTableModel.parentFolder;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function navigateHome() {
|
||||
fileTableModel.folder = homeDestination;
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
fileTableView.selection.clear();
|
||||
fileTableView.currentRow = -1;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
FolderListModel {
|
||||
id: folderListModel
|
||||
nameFilters: selectionType.currentFilter
|
||||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
showHidden: root.showHidden
|
||||
Component.onCompleted: {
|
||||
showFiles = !root.selectDirectory
|
||||
showHidden = root.showHidden
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
d.clearSelection();
|
||||
fileTableModel.update(); // Update once the data from the folder change is available.
|
||||
}
|
||||
|
||||
function getItem(index, field) {
|
||||
return get(index, field);
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
// Emulates FolderListModel but contains drive data.
|
||||
id: driveListModel
|
||||
|
||||
property int count: 1
|
||||
|
||||
Component.onCompleted: initialize();
|
||||
|
||||
function initialize() {
|
||||
var drive,
|
||||
i;
|
||||
|
||||
count = drives.length;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
drive = drives[i].slice(0, -1); // Remove trailing "/".
|
||||
append({
|
||||
fileName: drive,
|
||||
fileModified: new Date(0),
|
||||
fileSize: 0,
|
||||
filePath: drive + "/",
|
||||
fileIsDir: true,
|
||||
fileNameSort: drive.toLowerCase()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getItem(index, field) {
|
||||
return get(index)[field];
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: filesModelBuilder
|
||||
ListModel { }
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: fileTableModel
|
||||
|
||||
// FolderListModel has a couple of problems:
|
||||
// 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757
|
||||
// 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901
|
||||
//
|
||||
// To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with
|
||||
// drive information when viewing at the computer level.
|
||||
|
||||
property var folder
|
||||
property int sortOrder: Qt.AscendingOrder
|
||||
property int sortColumn: 0
|
||||
property var model: folderListModel
|
||||
property string parentFolder: calculateParentFolder();
|
||||
|
||||
readonly property string rootFolder: "file:///"
|
||||
|
||||
function calculateParentFolder() {
|
||||
if (model === folderListModel) {
|
||||
if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) {
|
||||
return rootFolder;
|
||||
} else {
|
||||
return folderListModel.parentFolder;
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
if (folder === rootFolder) {
|
||||
model = driveListModel;
|
||||
helper.monitorDirectory("");
|
||||
update();
|
||||
} else {
|
||||
var needsUpdate = model === driveListModel && folder === folderListModel.folder;
|
||||
|
||||
model = folderListModel;
|
||||
folderListModel.folder = folder;
|
||||
helper.monitorDirectory(helper.urlToPath(folder));
|
||||
|
||||
if (needsUpdate) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isFolder(row) {
|
||||
if (row === -1) {
|
||||
return false;
|
||||
}
|
||||
return filesModel.get(row).fileIsDir;
|
||||
}
|
||||
|
||||
function get(row) {
|
||||
return filesModel.get(row)
|
||||
}
|
||||
|
||||
function update() {
|
||||
var dataFields = ["fileName", "fileModified", "fileSize"],
|
||||
sortFields = ["fileNameSort", "fileModified", "fileSize"],
|
||||
dataField = dataFields[sortColumn],
|
||||
sortField = sortFields[sortColumn],
|
||||
sortValue,
|
||||
fileName,
|
||||
fileIsDir,
|
||||
comparisonFunction,
|
||||
lower,
|
||||
middle,
|
||||
upper,
|
||||
rows = 0,
|
||||
i;
|
||||
|
||||
filesModel = filesModelBuilder.createObject(root);
|
||||
|
||||
comparisonFunction = sortOrder === Qt.AscendingOrder
|
||||
? function(a, b) { return a < b; }
|
||||
: function(a, b) { return a > b; }
|
||||
|
||||
for (i = 0; i < model.count; i++) {
|
||||
fileName = model.getItem(i, "fileName");
|
||||
fileIsDir = model.getItem(i, "fileIsDir");
|
||||
|
||||
sortValue = model.getItem(i, dataField);
|
||||
if (dataField === "fileName") {
|
||||
// Directories first by prefixing a "*".
|
||||
// Case-insensitive.
|
||||
sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase();
|
||||
}
|
||||
|
||||
lower = 0;
|
||||
upper = rows;
|
||||
while (lower < upper) {
|
||||
middle = Math.floor((lower + upper) / 2);
|
||||
var lessThan;
|
||||
if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
|
||||
lessThan = true;
|
||||
upper = middle;
|
||||
} else {
|
||||
lessThan = false;
|
||||
lower = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
filesModel.insert(lower, {
|
||||
fileName: fileName,
|
||||
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
|
||||
fileSize: model.getItem(i, "fileSize"),
|
||||
filePath: model.getItem(i, "filePath"),
|
||||
fileIsDir: fileIsDir,
|
||||
fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase()
|
||||
});
|
||||
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Table {
|
||||
id: fileTableView
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
anchors {
|
||||
top: navControls.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: currentSelection.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
|
||||
}
|
||||
headerVisible: !selectDirectory
|
||||
onDoubleClicked: navigateToRow(row);
|
||||
Keys.onReturnPressed: navigateToCurrentRow();
|
||||
Keys.onEnterPressed: navigateToCurrentRow();
|
||||
|
||||
sortIndicatorColumn: 0
|
||||
sortIndicatorOrder: Qt.AscendingOrder
|
||||
sortIndicatorVisible: true
|
||||
|
||||
model: filesModel
|
||||
|
||||
function updateSort() {
|
||||
fileTableModel.sortOrder = sortIndicatorOrder;
|
||||
fileTableModel.sortColumn = sortIndicatorColumn;
|
||||
fileTableModel.update();
|
||||
}
|
||||
|
||||
onSortIndicatorColumnChanged: { updateSort(); }
|
||||
|
||||
onSortIndicatorOrderChanged: { updateSort(); }
|
||||
|
||||
itemDelegate: Item {
|
||||
clip: true
|
||||
|
||||
FiraSansSemiBold {
|
||||
text: getText();
|
||||
elide: styleData.elideMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
right: parent.right
|
||||
rightMargin: hifi.dimensions.tablePadding
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
size: hifi.fontSizes.tableText
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
font.family: (styleData.row !== -1 && fileTableView.model.get(styleData.row).fileIsDir)
|
||||
? "Fira Sans SemiBold" : "Fira Sans"
|
||||
|
||||
function getText() {
|
||||
if (styleData.row === -1) {
|
||||
return styleData.value;
|
||||
}
|
||||
|
||||
switch (styleData.column) {
|
||||
case 1: return fileTableView.model.get(styleData.row).fileIsDir ? "" : styleData.value;
|
||||
case 2: return fileTableView.model.get(styleData.row).fileIsDir ? "" : formatSize(styleData.value);
|
||||
default: return styleData.value;
|
||||
}
|
||||
}
|
||||
function formatSize(size) {
|
||||
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
||||
var suffixIndex = 0
|
||||
while ((size / 1024.0) > 1.1) {
|
||||
size /= 1024.0;
|
||||
++suffixIndex;
|
||||
}
|
||||
|
||||
size = Math.round(size*1000)/1000;
|
||||
size = size.toLocaleString()
|
||||
|
||||
return size + " " + suffixes[suffixIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
id: fileNameColumn
|
||||
role: "fileName"
|
||||
title: "Name"
|
||||
width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width
|
||||
movable: false
|
||||
resizable: true
|
||||
}
|
||||
TableViewColumn {
|
||||
id: fileModifiedColumn
|
||||
role: "fileModified"
|
||||
title: "Date"
|
||||
width: 0.3 * fileTableView.width
|
||||
movable: false
|
||||
resizable: true
|
||||
visible: !selectDirectory
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "fileSize"
|
||||
title: "Size"
|
||||
width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width
|
||||
movable: false
|
||||
resizable: true
|
||||
visible: !selectDirectory
|
||||
}
|
||||
|
||||
function navigateToRow(row) {
|
||||
currentRow = row;
|
||||
navigateToCurrentRow();
|
||||
}
|
||||
|
||||
function navigateToCurrentRow() {
|
||||
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
|
||||
var row = fileTableView.currentRow
|
||||
var isFolder = currentModel.isFolder(row);
|
||||
var file = currentModel.get(row).filePath;
|
||||
if (isFolder) {
|
||||
currentModel.folder = helper.pathToUrl(file);
|
||||
} else {
|
||||
okAction.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
property string prefix: ""
|
||||
|
||||
function addToPrefix(event) {
|
||||
if (!event.text || event.text === "") {
|
||||
return false;
|
||||
}
|
||||
var newPrefix = prefix + event.text.toLowerCase();
|
||||
var matchedIndex = -1;
|
||||
for (var i = 0; i < model.count; ++i) {
|
||||
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
|
||||
filesModel.get(i).fileName.toLowerCase();
|
||||
if (0 === name.indexOf(newPrefix)) {
|
||||
matchedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedIndex !== -1) {
|
||||
fileTableView.selection.clear();
|
||||
fileTableView.selection.select(matchedIndex);
|
||||
fileTableView.currentRow = matchedIndex;
|
||||
fileTableView.prefix = newPrefix;
|
||||
}
|
||||
prefixClearTimer.restart();
|
||||
return true;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: prefixClearTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: fileTableView.prefix = "";
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Tab:
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = false;
|
||||
break;
|
||||
case Qt.Key_Escape:
|
||||
event.accepted = true;
|
||||
root.click(OriginalDialogs.StandardButton.Cancel);
|
||||
break;
|
||||
default:
|
||||
if (addToPrefix(event)) {
|
||||
event.accepted = true
|
||||
} else {
|
||||
event.accepted = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: root.saveDialog ? currentSelection : openButton
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: currentSelection
|
||||
label: selectDirectory ? "Directory:" : "File name:"
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: selectionType.visible ? selectionType.left: parent.right
|
||||
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
|
||||
bottom: keyboard.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
readOnly: !root.saveDialog
|
||||
activeFocusOnTab: !readOnly
|
||||
onActiveFocusChanged: if (activeFocus) { selectAll(); }
|
||||
onAccepted: okAction.trigger();
|
||||
KeyNavigation.up: fileTableView.contentItem
|
||||
KeyNavigation.down: openButton
|
||||
KeyNavigation.tab: openButton
|
||||
KeyNavigation.backtab: fileTableView.contentItem
|
||||
}
|
||||
|
||||
FileTypeSelection {
|
||||
id: selectionType
|
||||
anchors {
|
||||
top: currentSelection.top
|
||||
left: buttonRow.left
|
||||
right: parent.right
|
||||
}
|
||||
visible: !selectDirectory && filtersCount > 1
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: buttonRow.top
|
||||
bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.y
|
||||
|
||||
Button {
|
||||
id: openButton
|
||||
color: hifi.buttons.blue
|
||||
action: okAction
|
||||
Keys.onReturnPressed: okAction.trigger()
|
||||
KeyNavigation.right: cancelButton
|
||||
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
|
||||
KeyNavigation.tab: cancelButton
|
||||
}
|
||||
|
||||
Button {
|
||||
id: cancelButton
|
||||
action: cancelAction
|
||||
Keys.onReturnPressed: { cancelAction.trigger() }
|
||||
KeyNavigation.left: openButton
|
||||
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
|
||||
KeyNavigation.backtab: openButton
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: okAction
|
||||
text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open"
|
||||
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
|
||||
onTriggered: {
|
||||
if (!root.selectDirectory && !d.currentSelectionIsFolder
|
||||
|| root.selectDirectory && fileTableView.currentRow === -1) {
|
||||
okActionTimer.start();
|
||||
} else {
|
||||
fileTableView.navigateToCurrentRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: okActionTimer
|
||||
interval: 50
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!root.saveDialog) {
|
||||
selectedFile(d.currentSelectionUrl);
|
||||
root.destroy()
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the ambiguity between different cases
|
||||
// * typed name (with or without extension)
|
||||
// * full path vs relative vs filename only
|
||||
var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter);
|
||||
|
||||
if (!selection) {
|
||||
desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" })
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper.urlIsDir(selection)) {
|
||||
root.dir = selection;
|
||||
currentSelection.text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the file is a valid target
|
||||
if (!helper.urlIsWritable(selection)) {
|
||||
desktop.messageBox({
|
||||
icon: OriginalDialogs.StandardIcon.Warning,
|
||||
text: "Unable to write to location " + selection
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper.urlExists(selection)) {
|
||||
var messageBox = desktop.messageBox({
|
||||
icon: OriginalDialogs.StandardIcon.Question,
|
||||
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
|
||||
text: "Do you wish to overwrite " + selection + "?",
|
||||
});
|
||||
var result = messageBox.exec();
|
||||
if (OriginalDialogs.StandardButton.Yes !== result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Selecting " + selection)
|
||||
selectedFile(selection);
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: "Cancel"
|
||||
onTriggered: { canceled(); root.shown = false; }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Backspace:
|
||||
event.accepted = d.navigateUp();
|
||||
break;
|
||||
|
||||
case Qt.Key_Home:
|
||||
event.accepted = d.navigateHome();
|
||||
break;
|
||||
|
||||
case Qt.Key_Escape:
|
||||
event.accepted = true;
|
||||
root.click(OriginalDialogs.StandardButton.Cancel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
//
|
||||
// QueryDialog.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 22 Jan 2016
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../controls-uit"
|
||||
import "../styles-uit"
|
||||
import "../windows"
|
||||
|
||||
ModalWindow {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
implicitWidth: 640
|
||||
implicitHeight: 320
|
||||
visible: true
|
||||
keyboardOverride: true // Disable ModalWindow's keyboard.
|
||||
|
||||
signal selected(var result);
|
||||
signal canceled();
|
||||
|
||||
property int icon: hifi.icons.none
|
||||
property string iconText: ""
|
||||
property int iconSize: 35
|
||||
onIconChanged: updateIcon();
|
||||
|
||||
property var items;
|
||||
property string label
|
||||
property var result;
|
||||
property alias current: textResult.text
|
||||
|
||||
// For text boxes
|
||||
property alias placeholderText: textResult.placeholderText
|
||||
|
||||
// For combo boxes
|
||||
property bool editable: true;
|
||||
|
||||
property int titleWidth: 0
|
||||
onTitleWidthChanged: d.resize();
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
function updateIcon() {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
iconText = hifi.glyphForIcon(root.icon);
|
||||
}
|
||||
|
||||
Item {
|
||||
id: modalWindowItem
|
||||
clip: true
|
||||
width: pane.width
|
||||
height: pane.height
|
||||
anchors.margins: 0
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
readonly property int maxWdith: 1280
|
||||
readonly property int minHeight: 120
|
||||
readonly property int maxHeight: 720
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, pane.width)
|
||||
var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height
|
||||
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
|
||||
root.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: keyboard.top;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
margins: 0
|
||||
bottomMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||
TextField {
|
||||
id: textResult
|
||||
label: root.label
|
||||
visible: items ? false : true
|
||||
anchors {
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
bottom: parent.bottom
|
||||
}
|
||||
KeyNavigation.down: acceptButton
|
||||
KeyNavigation.tab: acceptButton
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: comboBox
|
||||
label: root.label
|
||||
visible: items ? true : false
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
model: items ? items : []
|
||||
KeyNavigation.down: acceptButton
|
||||
KeyNavigation.tab: acceptButton
|
||||
}
|
||||
}
|
||||
|
||||
property alias keyboardOverride: root.keyboardOverride
|
||||
property alias keyboardRaised: root.keyboardRaised
|
||||
property alias punctuationMode: root.punctuationMode
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
raised: keyboardEnabled && keyboardRaised
|
||||
numeric: punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: buttons.top
|
||||
bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
|
||||
Flow {
|
||||
id: buttons
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
layoutDirection: Qt.RightToLeft
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: 0
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
Button {
|
||||
id: cancelButton
|
||||
action: cancelAction
|
||||
KeyNavigation.left: acceptButton
|
||||
KeyNavigation.up: items ? comboBox : textResult
|
||||
KeyNavigation.backtab: acceptButton
|
||||
}
|
||||
Button {
|
||||
id: acceptButton
|
||||
action: acceptAction
|
||||
KeyNavigation.right: cancelButton
|
||||
KeyNavigation.up: items ? comboBox : textResult
|
||||
KeyNavigation.tab: cancelButton
|
||||
KeyNavigation.backtab: items ? comboBox : textResult
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: qsTr("Cancel");
|
||||
shortcut: "Esc"
|
||||
onTriggered: {
|
||||
root.canceled();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: acceptAction
|
||||
text: qsTr("OK");
|
||||
shortcut: "Return"
|
||||
onTriggered: {
|
||||
root.result = items ? comboBox.currentText : textResult.text
|
||||
root.selected(root.result);
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!visible) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
cancelAction.trigger()
|
||||
event.accepted = true;
|
||||
break;
|
||||
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (acceptButton.focus) {
|
||||
acceptAction.trigger()
|
||||
} else if (cancelButton.focus) {
|
||||
cancelAction.trigger()
|
||||
} else if (comboBox.focus || comboBox.popup.focus) {
|
||||
comboBox.showList()
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
updateIcon();
|
||||
d.resize();
|
||||
if (items) {
|
||||
comboBox.forceActiveFocus()
|
||||
} else {
|
||||
textResult.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,533 +0,0 @@
|
|||
//
|
||||
// AssetDialogContent.qml
|
||||
//
|
||||
// Created by David Rowe on 19 Apr 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.7
|
||||
import QtQuick.Controls 1.5
|
||||
|
||||
import "../../controls-uit"
|
||||
import "../../styles-uit"
|
||||
|
||||
import "../fileDialog"
|
||||
|
||||
Item {
|
||||
// Set from OffscreenUi::assetDialog()
|
||||
property alias dir: assetTableModel.folder
|
||||
property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
|
||||
property int options // Not used.
|
||||
|
||||
property bool selectDirectory: false
|
||||
|
||||
// Not implemented.
|
||||
//property bool saveDialog: false;
|
||||
//property bool multiSelect: false;
|
||||
|
||||
property bool singleClickNavigate: false
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
Component.onCompleted: {
|
||||
homeButton.destination = dir;
|
||||
|
||||
if (selectDirectory) {
|
||||
d.currentSelectionIsFolder = true;
|
||||
d.currentSelectionPath = assetTableModel.folder;
|
||||
}
|
||||
|
||||
assetTableView.forceActiveFocus();
|
||||
}
|
||||
|
||||
Item {
|
||||
id: assetDialogItem
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
// Clear selection when click on internal unused area.
|
||||
anchors.fill: parent
|
||||
drag.target: root
|
||||
onClicked: {
|
||||
d.clearSelection();
|
||||
frame.forceActiveFocus();
|
||||
assetTableView.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: navControls
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: hifi.dimensions.contentMargin.y
|
||||
left: parent.left
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
|
||||
GlyphButton {
|
||||
id: upButton
|
||||
glyph: hifi.glyphs.levelUp
|
||||
width: height
|
||||
size: 30
|
||||
enabled: assetTableModel.parentFolder !== ""
|
||||
onClicked: d.navigateUp();
|
||||
}
|
||||
|
||||
GlyphButton {
|
||||
id: homeButton
|
||||
property string destination: ""
|
||||
glyph: hifi.glyphs.home
|
||||
size: 28
|
||||
width: height
|
||||
enabled: destination !== ""
|
||||
//onClicked: d.navigateHome();
|
||||
onClicked: assetTableModel.folder = destination;
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: pathSelector
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: hifi.dimensions.contentMargin.y
|
||||
left: navControls.right
|
||||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
right: parent.right
|
||||
}
|
||||
z: 10
|
||||
|
||||
property string lastValidFolder: assetTableModel.folder
|
||||
|
||||
function calculatePathChoices(folder) {
|
||||
var folders = folder.split("/"),
|
||||
choices = [],
|
||||
i, length;
|
||||
|
||||
if (folders[folders.length - 1] === "") {
|
||||
folders.pop();
|
||||
}
|
||||
|
||||
choices.push(folders[0]);
|
||||
|
||||
for (i = 1, length = folders.length; i < length; i++) {
|
||||
choices.push(choices[i - 1] + "/" + folders[i]);
|
||||
}
|
||||
|
||||
if (folders[0] === "") {
|
||||
choices[0] = "/";
|
||||
}
|
||||
|
||||
choices.reverse();
|
||||
|
||||
if (choices.length > 0) {
|
||||
pathSelector.model = choices;
|
||||
}
|
||||
}
|
||||
|
||||
onLastValidFolderChanged: {
|
||||
var folder = lastValidFolder;
|
||||
calculatePathChoices(folder);
|
||||
}
|
||||
|
||||
onCurrentTextChanged: {
|
||||
var folder = currentText;
|
||||
|
||||
if (folder !== "/") {
|
||||
folder += "/";
|
||||
}
|
||||
|
||||
if (folder !== assetTableModel.folder) {
|
||||
if (root.selectDirectory) {
|
||||
currentSelection.text = currentText;
|
||||
d.currentSelectionPath = currentText;
|
||||
}
|
||||
assetTableModel.folder = folder;
|
||||
assetTableView.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property string currentSelectionPath
|
||||
property bool currentSelectionIsFolder
|
||||
property var tableViewConnection: Connections { target: assetTableView; onCurrentRowChanged: d.update(); }
|
||||
|
||||
function update() {
|
||||
var row = assetTableView.currentRow;
|
||||
|
||||
if (row === -1) {
|
||||
if (!root.selectDirectory) {
|
||||
currentSelection.text = "";
|
||||
currentSelectionIsFolder = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var rowInfo = assetTableModel.get(row);
|
||||
currentSelectionPath = rowInfo.filePath;
|
||||
currentSelectionIsFolder = rowInfo.fileIsDir;
|
||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||
currentSelection.text = currentSelectionPath;
|
||||
} else {
|
||||
currentSelection.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
function navigateUp() {
|
||||
if (assetTableModel.parentFolder !== "") {
|
||||
assetTableModel.folder = assetTableModel.parentFolder;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function navigateHome() {
|
||||
assetTableModel.folder = homeButton.destination;
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
assetTableView.selection.clear();
|
||||
assetTableView.currentRow = -1;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: assetTableModel
|
||||
|
||||
property string folder
|
||||
property string parentFolder: ""
|
||||
readonly property string rootFolder: "/"
|
||||
|
||||
onFolderChanged: {
|
||||
parentFolder = calculateParentFolder();
|
||||
update();
|
||||
}
|
||||
|
||||
function calculateParentFolder() {
|
||||
if (folder !== "/") {
|
||||
return folder.slice(0, folder.slice(0, -1).lastIndexOf("/") + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function isFolder(row) {
|
||||
if (row === -1) {
|
||||
return false;
|
||||
}
|
||||
return get(row).fileIsDir;
|
||||
}
|
||||
|
||||
function onGetAllMappings(error, map) {
|
||||
var mappings,
|
||||
fileTypeFilter,
|
||||
index,
|
||||
path,
|
||||
fileName,
|
||||
fileType,
|
||||
fileIsDir,
|
||||
isValid,
|
||||
subDirectory,
|
||||
subDirectories = [],
|
||||
fileNameSort,
|
||||
rows = 0,
|
||||
lower,
|
||||
middle,
|
||||
upper,
|
||||
i,
|
||||
length;
|
||||
|
||||
clear();
|
||||
|
||||
if (error === "") {
|
||||
mappings = Object.keys(map);
|
||||
fileTypeFilter = filter.replace("*", "").toLowerCase();
|
||||
|
||||
for (i = 0, length = mappings.length; i < length; i++) {
|
||||
index = mappings[i].lastIndexOf("/");
|
||||
|
||||
path = mappings[i].slice(0, mappings[i].lastIndexOf("/") + 1);
|
||||
fileName = mappings[i].slice(path.length);
|
||||
fileType = fileName.slice(fileName.lastIndexOf("."));
|
||||
fileIsDir = false;
|
||||
isValid = false;
|
||||
|
||||
if (fileType.toLowerCase() === fileTypeFilter) {
|
||||
if (path === folder) {
|
||||
isValid = !selectDirectory;
|
||||
} else if (path.length > folder.length) {
|
||||
subDirectory = path.slice(folder.length);
|
||||
index = subDirectory.indexOf("/");
|
||||
if (index === subDirectory.lastIndexOf("/")) {
|
||||
fileName = subDirectory.slice(0, index);
|
||||
if (subDirectories.indexOf(fileName) === -1) {
|
||||
fileIsDir = true;
|
||||
isValid = true;
|
||||
subDirectories.push(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
fileNameSort = (fileIsDir ? "*" : "") + fileName.toLowerCase();
|
||||
|
||||
lower = 0;
|
||||
upper = rows;
|
||||
while (lower < upper) {
|
||||
middle = Math.floor((lower + upper) / 2);
|
||||
var lessThan;
|
||||
if (fileNameSort < get(middle)["fileNameSort"]) {
|
||||
lessThan = true;
|
||||
upper = middle;
|
||||
} else {
|
||||
lessThan = false;
|
||||
lower = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
insert(lower, {
|
||||
fileName: fileName,
|
||||
filePath: path + (fileIsDir ? "" : fileName),
|
||||
fileIsDir: fileIsDir,
|
||||
fileNameSort: fileNameSort
|
||||
});
|
||||
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log("Error getting mappings from Asset Server");
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
d.clearSelection();
|
||||
clear();
|
||||
Assets.getAllMappings(onGetAllMappings);
|
||||
}
|
||||
}
|
||||
|
||||
Table {
|
||||
id: assetTableView
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
anchors {
|
||||
top: navControls.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: currentSelection.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
|
||||
}
|
||||
|
||||
model: assetTableModel
|
||||
|
||||
focus: true
|
||||
|
||||
onClicked: {
|
||||
if (singleClickNavigate) {
|
||||
navigateToRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: navigateToRow(row);
|
||||
Keys.onReturnPressed: navigateToCurrentRow();
|
||||
Keys.onEnterPressed: navigateToCurrentRow();
|
||||
|
||||
itemDelegate: Item {
|
||||
clip: true
|
||||
|
||||
FiraSansSemiBold {
|
||||
text: styleData.value
|
||||
elide: styleData.elideMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
right: parent.right
|
||||
rightMargin: hifi.dimensions.tablePadding
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
size: hifi.fontSizes.tableText
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
font.family: (styleData.row !== -1 && assetTableView.model.get(styleData.row).fileIsDir)
|
||||
? "Fira Sans SemiBold" : "Fira Sans"
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
id: fileNameColumn
|
||||
role: "fileName"
|
||||
title: "Name"
|
||||
width: assetTableView.width
|
||||
movable: false
|
||||
resizable: false
|
||||
}
|
||||
|
||||
function navigateToRow(row) {
|
||||
currentRow = row;
|
||||
navigateToCurrentRow();
|
||||
}
|
||||
|
||||
function navigateToCurrentRow() {
|
||||
if (model.isFolder(currentRow)) {
|
||||
model.folder = model.get(currentRow).filePath;
|
||||
} else {
|
||||
okAction.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: prefixClearTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: assetTableView.prefix = "";
|
||||
}
|
||||
|
||||
property string prefix: ""
|
||||
|
||||
function addToPrefix(event) {
|
||||
if (!event.text || event.text === "") {
|
||||
return false;
|
||||
}
|
||||
var newPrefix = prefix + event.text.toLowerCase();
|
||||
var matchedIndex = -1;
|
||||
for (var i = 0; i < model.count; ++i) {
|
||||
var name = model.get(i).fileName.toLowerCase();
|
||||
if (0 === name.indexOf(newPrefix)) {
|
||||
matchedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedIndex !== -1) {
|
||||
assetTableView.selection.clear();
|
||||
assetTableView.selection.select(matchedIndex);
|
||||
assetTableView.currentRow = matchedIndex;
|
||||
assetTableView.prefix = newPrefix;
|
||||
}
|
||||
prefixClearTimer.restart();
|
||||
return true;
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Tab:
|
||||
case Qt.Key_Backtab:
|
||||
event.accepted = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (addToPrefix(event)) {
|
||||
event.accepted = true
|
||||
} else {
|
||||
event.accepted = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: currentSelection
|
||||
label: selectDirectory ? "Directory:" : "File name:"
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: selectionType.visible ? selectionType.left: parent.right
|
||||
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
|
||||
bottom: buttonRow.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
readOnly: true
|
||||
activeFocusOnTab: !readOnly
|
||||
onActiveFocusChanged: if (activeFocus) { selectAll(); }
|
||||
onAccepted: okAction.trigger();
|
||||
}
|
||||
|
||||
FileTypeSelection {
|
||||
id: selectionType
|
||||
anchors {
|
||||
top: currentSelection.top
|
||||
left: buttonRow.left
|
||||
right: parent.right
|
||||
}
|
||||
visible: !selectDirectory && filtersCount > 1
|
||||
KeyNavigation.left: assetTableView
|
||||
KeyNavigation.right: openButton
|
||||
}
|
||||
|
||||
Action {
|
||||
id: okAction
|
||||
text: currentSelection.text && root.selectDirectory && assetTableView.currentRow === -1 ? "Choose" : "Open"
|
||||
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
|
||||
onTriggered: {
|
||||
if (!root.selectDirectory && !d.currentSelectionIsFolder
|
||||
|| root.selectDirectory && assetTableView.currentRow === -1) {
|
||||
selectedAsset(d.currentSelectionPath);
|
||||
root.destroy();
|
||||
} else {
|
||||
assetTableView.navigateToCurrentRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action {
|
||||
id: cancelAction
|
||||
text: "Cancel"
|
||||
onTriggered: {
|
||||
canceled();
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.y
|
||||
|
||||
Button {
|
||||
id: openButton
|
||||
color: hifi.buttons.blue
|
||||
action: okAction
|
||||
Keys.onReturnPressed: okAction.trigger()
|
||||
KeyNavigation.up: selectionType
|
||||
KeyNavigation.left: selectionType
|
||||
KeyNavigation.right: cancelButton
|
||||
}
|
||||
|
||||
Button {
|
||||
id: cancelButton
|
||||
action: cancelAction
|
||||
KeyNavigation.up: selectionType
|
||||
KeyNavigation.left: openButton
|
||||
KeyNavigation.right: assetTableView.contentItem
|
||||
Keys.onReturnPressed: { canceled(); root.enabled = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Backspace:
|
||||
event.accepted = d.navigateUp();
|
||||
break;
|
||||
|
||||
case Qt.Key_Home:
|
||||
event.accepted = d.navigateHome();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1108,7 +1108,7 @@ Rectangle {
|
|||
function findNearbySessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
||||
var data = optionalData || nearbyUserModelData, length = data.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (data[i].sessionId === sessionId) {
|
||||
if (data[i].sessionId === sessionId.toString()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -1120,7 +1120,7 @@ Rectangle {
|
|||
var data = message.params;
|
||||
var index = -1;
|
||||
iAmAdmin = Users.canKick;
|
||||
index = findNearbySessionIndex('', data);
|
||||
index = findNearbySessionIndex("", data);
|
||||
if (index !== -1) {
|
||||
myData = data[index];
|
||||
data.splice(index, 1);
|
||||
|
@ -1197,8 +1197,8 @@ Rectangle {
|
|||
for (var userId in message.params) {
|
||||
var audioLevel = message.params[userId][0];
|
||||
var avgAudioLevel = message.params[userId][1];
|
||||
// If the userId is 0, we're updating "myData".
|
||||
if (userId == 0) {
|
||||
// If the userId is "", we're updating "myData".
|
||||
if (userId === "") {
|
||||
myData.audioLevel = audioLevel;
|
||||
myCard.audioLevel = audioLevel; // Defensive programming
|
||||
myData.avgAudioLevel = avgAudioLevel;
|
||||
|
|
|
@ -125,7 +125,6 @@ Rectangle {
|
|||
id: wearablesCombobox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
comboBox.textRole: "text"
|
||||
|
||||
model: ListModel {
|
||||
function findIndexById(id) {
|
||||
|
|
|
@ -129,7 +129,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
onAppInstalled: {
|
||||
if (appHref === root.itemHref) {
|
||||
if (appID === root.itemId) {
|
||||
root.isInstalled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,13 +67,13 @@ Item {
|
|||
}
|
||||
|
||||
onAppInstalled: {
|
||||
if (appHref === root.itemHref) {
|
||||
if (appID === root.itemId) {
|
||||
root.isInstalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
onAppUninstalled: {
|
||||
if (appHref === root.itemHref) {
|
||||
if (appID === root.itemId) {
|
||||
root.isInstalled = false;
|
||||
}
|
||||
}
|
||||
|
@ -328,7 +328,15 @@ Item {
|
|||
item.buttonColor = "#E2334D";
|
||||
item.buttonClicked = function() {
|
||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||
sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl});
|
||||
sendToPurchases({
|
||||
method: 'updateItemClicked',
|
||||
itemId: root.itemId,
|
||||
itemEdition: root.itemEdition,
|
||||
upgradeUrl: root.upgradeUrl,
|
||||
itemHref: root.itemHref,
|
||||
itemType: root.itemType,
|
||||
isInstalled: root.isInstalled
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
onAppInstalled: {
|
||||
root.installedApps = Commerce.getInstalledApps();
|
||||
root.installedApps = Commerce.getInstalledApps(appID);
|
||||
}
|
||||
|
||||
onAppUninstalled: {
|
||||
|
@ -706,7 +706,23 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
} else if (msg.method === "updateItemClicked") {
|
||||
sendToScript(msg);
|
||||
if (msg.itemType === "app" && msg.isInstalled) {
|
||||
lightboxPopup.titleText = "Uninstall App";
|
||||
lightboxPopup.bodyText = "The app that you are trying to update is installed.<br><br>" +
|
||||
"If you proceed, the current version of the app will be uninstalled.";
|
||||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.button2text = "CONFIRM";
|
||||
lightboxPopup.button2method = function() {
|
||||
Commerce.uninstallApp(msg.itemHref);
|
||||
sendToScript(msg);
|
||||
};
|
||||
lightboxPopup.visible = true;
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
} else if (msg.method === "giftAsset") {
|
||||
sendAsset.assetName = msg.itemName;
|
||||
sendAsset.assetCertID = msg.certId;
|
||||
|
|
|
@ -35,6 +35,10 @@ void AndroidHelper::notifyEnterForeground() {
|
|||
emit enterForeground();
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyBeforeEnterBackground() {
|
||||
emit beforeEnterBackground();
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyEnterBackground() {
|
||||
emit enterBackground();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
void requestActivity(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void notifyLoadComplete();
|
||||
void notifyEnterForeground();
|
||||
void notifyBeforeEnterBackground();
|
||||
void notifyEnterBackground();
|
||||
|
||||
void performHapticFeedback(int duration);
|
||||
|
@ -39,6 +40,7 @@ signals:
|
|||
void androidActivityRequested(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void qtAppLoadComplete();
|
||||
void enterForeground();
|
||||
void beforeEnterBackground();
|
||||
void enterBackground();
|
||||
|
||||
void hapticFeedbackRequested(int duration);
|
||||
|
|
|
@ -1448,8 +1448,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// add firstRun flag from settings to launch event
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
|
||||
QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
|
||||
auto& userActivityLogger = UserActivityLogger::getInstance();
|
||||
if (userActivityLogger.isEnabled()) {
|
||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||
|
@ -1501,13 +1499,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
properties["first_run"] = firstRun.get();
|
||||
|
||||
// add the user's machine ID to the launch event
|
||||
QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
properties["machine_fingerprint"] = machineFingerPrint;
|
||||
|
||||
userActivityLogger.logAction("launch", properties);
|
||||
}
|
||||
|
||||
setCrashAnnotation("machine_fingerprint", machineFingerPrint.toStdString());
|
||||
|
||||
_entityEditSender.setMyAvatar(myAvatar.get());
|
||||
|
||||
// The entity octree will have to know about MyAvatar for the parentJointName import
|
||||
|
@ -2261,6 +2258,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
connect(&AndroidHelper::instance(), &AndroidHelper::beforeEnterBackground, this, &Application::beforeEnterBackground);
|
||||
connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground);
|
||||
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
|
||||
AndroidHelper::instance().notifyLoadComplete();
|
||||
|
@ -3223,6 +3221,7 @@ void Application::setSettingConstrainToolbarPosition(bool setting) {
|
|||
void Application::showHelp() {
|
||||
static const QString HAND_CONTROLLER_NAME_VIVE = "vive";
|
||||
static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus";
|
||||
static const QString HAND_CONTROLLER_NAME_WINDOWS_MR = "windowsMR";
|
||||
|
||||
static const QString TAB_KEYBOARD_MOUSE = "kbm";
|
||||
static const QString TAB_GAMEPAD = "gamepad";
|
||||
|
@ -3237,9 +3236,13 @@ void Application::showHelp() {
|
|||
} else if (PluginUtils::isOculusTouchControllerAvailable()) {
|
||||
defaultTab = TAB_HAND_CONTROLLERS;
|
||||
handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH;
|
||||
} else if (qApp->getActiveDisplayPlugin()->getName() == "WindowMS") {
|
||||
defaultTab = TAB_HAND_CONTROLLERS;
|
||||
handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR;
|
||||
} else if (PluginUtils::isXboxControllerAvailable()) {
|
||||
defaultTab = TAB_GAMEPAD;
|
||||
}
|
||||
// TODO need some way to detect windowsMR to load controls reference default tab in Help > Controls Reference menu.
|
||||
|
||||
QUrlQuery queryString;
|
||||
queryString.addQueryItem("handControllerName", handControllerName);
|
||||
|
@ -3265,13 +3268,22 @@ void Application::resizeGL() {
|
|||
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
|
||||
// Otherwise, it must rebuild the FBOs
|
||||
uvec2 framebufferSize = displayPlugin->getRecommendedRenderSize();
|
||||
float renderResolutionScale = getRenderResolutionScale();
|
||||
uvec2 renderSize = uvec2(vec2(framebufferSize) * renderResolutionScale);
|
||||
uvec2 renderSize = uvec2(framebufferSize);
|
||||
if (_renderResolution != renderSize) {
|
||||
_renderResolution = renderSize;
|
||||
DependencyManager::get<FramebufferCache>()->setFrameBufferSize(fromGlm(renderSize));
|
||||
}
|
||||
|
||||
auto renderResolutionScale = getRenderResolutionScale();
|
||||
if (displayPlugin->getRenderResolutionScale() != renderResolutionScale) {
|
||||
auto renderConfig = _renderEngine->getConfiguration();
|
||||
assert(renderConfig);
|
||||
auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask");
|
||||
assert(mainView);
|
||||
mainView->setProperty("resolutionScale", renderResolutionScale);
|
||||
displayPlugin->setRenderResolutionScale(renderResolutionScale);
|
||||
}
|
||||
|
||||
// FIXME the aspect ratio for stereo displays is incorrect based on this.
|
||||
float aspectRatio = displayPlugin->getRecommendedAspectRatio();
|
||||
_myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio,
|
||||
|
@ -3283,7 +3295,6 @@ void Application::resizeGL() {
|
|||
}
|
||||
|
||||
DependencyManager::get<OffscreenUi>()->resize(fromGlm(displayPlugin->getRecommendedUiSize()));
|
||||
displayPlugin->setRenderResolutionScale(renderResolutionScale);
|
||||
}
|
||||
|
||||
void Application::handleSandboxStatus(QNetworkReply* reply) {
|
||||
|
@ -8332,6 +8343,13 @@ void Application::copyToClipboard(const QString& text) {
|
|||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void Application::beforeEnterBackground() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setSendDomainServerCheckInEnabled(false);
|
||||
nodeList->reset(true);
|
||||
clearDomainOctreeDetails();
|
||||
}
|
||||
|
||||
void Application::enterBackground() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
|
||||
"stop", Qt::BlockingQueuedConnection);
|
||||
|
@ -8346,6 +8364,8 @@ void Application::enterForeground() {
|
|||
if (!getActiveDisplayPlugin() || getActiveDisplayPlugin()->isActive() || !getActiveDisplayPlugin()->activate()) {
|
||||
qWarning() << "Could not re-activate display plugin";
|
||||
}
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setSendDomainServerCheckInEnabled(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -313,6 +313,7 @@ public:
|
|||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void beforeEnterBackground();
|
||||
void enterBackground();
|
||||
void enterForeground();
|
||||
#endif
|
||||
|
|
|
@ -102,7 +102,7 @@ void Application::paintGL() {
|
|||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// NOTE: There is no batch associated with this renderArgs
|
||||
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
|
||||
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize() * getRenderResolutionScale());
|
||||
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ void Application::paintGL() {
|
|||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
finalFramebufferSize = framebufferCache->getFrameBufferSize();
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
// Final framebuffer that will be handed to the display-plugin
|
||||
finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
bool startCrashHandler();
|
||||
bool startCrashHandler(std::string appPath);
|
||||
void setCrashAnnotation(std::string name, std::string value);
|
||||
|
||||
|
||||
#endif
|
||||
#endif // hifi_CrashHandler_h
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#if HAS_BREAKPAD
|
||||
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <client/linux/handler/exception_handler.h>
|
||||
|
@ -23,8 +23,10 @@
|
|||
#include <QtCore/QFileInfo>
|
||||
#include <QtAndroidExtras/QAndroidJniObject>
|
||||
|
||||
#include <SettingHelpers.h>
|
||||
#include <BuildInfo.h>
|
||||
#include <FingerprintUtils.h>
|
||||
#include <SettingHelpers.h>
|
||||
#include <UUID.h>
|
||||
|
||||
google_breakpad::ExceptionHandler* gBreakpadHandler;
|
||||
|
||||
|
@ -55,11 +57,14 @@ void flushAnnotations() {
|
|||
settings.sync();
|
||||
}
|
||||
|
||||
bool startCrashHandler() {
|
||||
bool startCrashHandler(std::string appPath) {
|
||||
annotations["version"] = BuildInfo::VERSION;
|
||||
annotations["build_number"] = BuildInfo::BUILD_NUMBER;
|
||||
annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING;
|
||||
|
||||
auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
annotations["machine_fingerprint"] = machineFingerPrint;
|
||||
|
||||
flushAnnotations();
|
||||
|
||||
gBreakpadHandler = new google_breakpad::ExceptionHandler(
|
||||
|
|
|
@ -9,21 +9,24 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if HAS_CRASHPAD
|
||||
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#if HAS_CRASHPAD
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wc++14-extensions"
|
||||
#endif
|
||||
|
||||
#include <client/crashpad_client.h>
|
||||
#include <client/crash_report_database.h>
|
||||
|
@ -31,19 +34,32 @@
|
|||
#include <client/annotation_list.h>
|
||||
#include <client/crashpad_info.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <BuildInfo.h>
|
||||
#include <FingerprintUtils.h>
|
||||
#include <UUID.h>
|
||||
|
||||
using namespace crashpad;
|
||||
|
||||
static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
|
||||
static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
|
||||
|
||||
extern QString qAppFileName();
|
||||
|
||||
CrashpadClient* client { nullptr };
|
||||
std::mutex annotationMutex;
|
||||
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler.exe" };
|
||||
#else
|
||||
static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler" };
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
|
||||
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
|
||||
if (!client) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
@ -56,8 +72,9 @@ LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
|
|||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool startCrashHandler() {
|
||||
bool startCrashHandler(std::string appPath) {
|
||||
if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -73,6 +90,10 @@ bool startCrashHandler() {
|
|||
annotations["build_number"] = BuildInfo::BUILD_NUMBER.toStdString();
|
||||
annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString();
|
||||
|
||||
auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
annotations["machine_fingerprint"] = machineFingerPrint.toStdString();
|
||||
|
||||
|
||||
arguments.push_back("--no-rate-limit");
|
||||
|
||||
// Setup Crashpad DB directory
|
||||
|
@ -82,7 +103,10 @@ bool startCrashHandler() {
|
|||
const auto crashpadDbPath = crashpadDbDir.toStdString() + "/" + crashpadDbName;
|
||||
|
||||
// Locate Crashpad handler
|
||||
const std::string CRASHPAD_HANDLER_PATH = QFileInfo(qAppFileName()).absolutePath().toStdString() + "/crashpad_handler.exe";
|
||||
const QFileInfo interfaceBinary { QString::fromStdString(appPath) };
|
||||
const QDir interfaceDir = interfaceBinary.dir();
|
||||
assert(interfaceDir.exists(CRASHPAD_HANDLER_NAME));
|
||||
const std::string CRASHPAD_HANDLER_PATH = interfaceDir.filePath(CRASHPAD_HANDLER_NAME).toStdString();
|
||||
|
||||
// Setup different file paths
|
||||
base::FilePath::StringType dbPath;
|
||||
|
@ -101,20 +125,24 @@ bool startCrashHandler() {
|
|||
// Enable automated uploads.
|
||||
database->GetSettings()->SetUploadsEnabled(true);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
AddVectoredExceptionHandler(0, vectoredExceptionHandler);
|
||||
#endif
|
||||
|
||||
return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
|
||||
}
|
||||
|
||||
void setCrashAnnotation(std::string name, std::string value) {
|
||||
std::lock_guard<std::mutex> guard(annotationMutex);
|
||||
if (!crashpadAnnotations) {
|
||||
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
|
||||
crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
crashpad_info->set_simple_annotations(crashpadAnnotations);
|
||||
if (client) {
|
||||
std::lock_guard<std::mutex> guard(annotationMutex);
|
||||
if (!crashpadAnnotations) {
|
||||
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
|
||||
crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
crashpad_info->set_simple_annotations(crashpadAnnotations);
|
||||
}
|
||||
std::replace(value.begin(), value.end(), ',', ';');
|
||||
crashpadAnnotations->SetKeyValue(name, value);
|
||||
}
|
||||
std::replace(value.begin(), value.end(), ',', ';');
|
||||
crashpadAnnotations->SetKeyValue(name, value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,14 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if !defined(HAS_CRASHPAD) && !defined(HAS_BREAKPAD)
|
||||
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#if !defined(HAS_CRASHPAD) && !defined(HAS_BREAKPAD)
|
||||
|
||||
bool startCrashHandler() {
|
||||
bool startCrashHandler(std::string appPath) {
|
||||
qDebug() << "No crash handler available.";
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
|
@ -668,3 +669,49 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QJsonArray palData;
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
while (itr != avatarMap.end()) {
|
||||
std::shared_ptr<Avatar> avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
QString currentSessionUUID = avatar->getSessionUUID().toString();
|
||||
if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) {
|
||||
QJsonObject thisAvatarPalData;
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
if (currentSessionUUID == myAvatar->getSessionUUID().toString()) {
|
||||
currentSessionUUID = "";
|
||||
}
|
||||
|
||||
thisAvatarPalData.insert("sessionUUID", currentSessionUUID);
|
||||
thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName());
|
||||
thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness());
|
||||
thisAvatarPalData.insert("isReplicated", avatar->getIsReplicated());
|
||||
|
||||
glm::vec3 position = avatar->getWorldPosition();
|
||||
QJsonObject jsonPosition;
|
||||
jsonPosition.insert("x", position.x);
|
||||
jsonPosition.insert("y", position.y);
|
||||
jsonPosition.insert("z", position.z);
|
||||
thisAvatarPalData.insert("position", jsonPosition);
|
||||
|
||||
float palOrbOffset = 0.2f;
|
||||
int headIndex = avatar->getJointIndex("Head");
|
||||
if (headIndex > 0) {
|
||||
glm::vec3 jointTranslation = avatar->getAbsoluteJointTranslationInObjectFrame(headIndex);
|
||||
palOrbOffset = jointTranslation.y / 2;
|
||||
}
|
||||
thisAvatarPalData.insert("palOrbOffset", palOrbOffset);
|
||||
|
||||
palData.append(thisAvatarPalData);
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
QJsonObject doc;
|
||||
doc.insert("data", palData);
|
||||
return doc.toVariantMap();
|
||||
}
|
||||
|
|
|
@ -157,6 +157,17 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||
|
||||
/**jsdoc
|
||||
* Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster
|
||||
* than iterating over each avatar and obtaining data about them in JavaScript, as that method
|
||||
* locks and unlocks each avatar's data structure potentially hundreds of times per update tick.
|
||||
* @function AvatarManager.getPalData
|
||||
* @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about
|
||||
* which you want to get PAL data
|
||||
* @returns {object}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPalData(const QList<QString> specificAvatarIdentifiers = QList<QString>());
|
||||
|
||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||
int getIdentityRequestsSent() const { return _identityRequestsSent; }
|
||||
|
||||
|
|
|
@ -3551,6 +3551,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Rotation);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
||||
}
|
||||
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
||||
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||
|
@ -3568,6 +3569,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
} else {
|
||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||
activate(Rotation);
|
||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
||||
setForceActivateRotation(false);
|
||||
}
|
||||
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||
|
|
|
@ -883,6 +883,7 @@ public:
|
|||
virtual void rebuildCollisionShape() override;
|
||||
|
||||
const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
|
||||
void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; }
|
||||
float getCurrentStandingHeight() const { return _currentStandingHeight; }
|
||||
void setCurrentStandingHeight(float newMode) { _currentStandingHeight = newMode; }
|
||||
const glm::quat getAverageHeadRotation() const { return _averageHeadRotation; }
|
||||
|
|
|
@ -208,7 +208,7 @@ void QmlCommerce::alreadyOwned(const QString& marketplaceId) {
|
|||
ledger->alreadyOwned(marketplaceId);
|
||||
}
|
||||
|
||||
QString QmlCommerce::getInstalledApps() {
|
||||
QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
|
||||
QString installedAppsFromMarketplace;
|
||||
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
|
||||
|
||||
|
@ -217,6 +217,18 @@ QString QmlCommerce::getInstalledApps() {
|
|||
foreach(QString appFileName, apps) {
|
||||
installedAppsFromMarketplace += appFileName;
|
||||
installedAppsFromMarketplace += ",";
|
||||
|
||||
// If we were supplied a "justInstalledAppID" argument, that means we're entering this function
|
||||
// to get the new list of installed apps immediately after installing an app.
|
||||
// In that case, the app we installed may not yet have its associated script running -
|
||||
// that task is asynchronous and takes a nonzero amount of time. This is especially true
|
||||
// for apps that are not in Interface's script cache.
|
||||
// Thus, we protect against deleting the .app.json from the user's disk (below)
|
||||
// by skipping that check for the app we just installed.
|
||||
if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QFile appFile(_appsPath + appFileName);
|
||||
if (appFile.open(QIODevice::ReadOnly)) {
|
||||
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
|
||||
|
@ -291,7 +303,8 @@ bool QmlCommerce::installApp(const QString& itemHref) {
|
|||
return false;
|
||||
}
|
||||
|
||||
emit appInstalled(itemHref);
|
||||
QFileInfo appFileInfo(appFile);
|
||||
emit appInstalled(appFileInfo.baseName());
|
||||
return true;
|
||||
});
|
||||
request->send();
|
||||
|
@ -321,7 +334,8 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
|
|||
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName();
|
||||
}
|
||||
|
||||
emit appUninstalled(itemHref);
|
||||
QFileInfo appFileInfo(appFile);
|
||||
emit appUninstalled(appFileInfo.baseName());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ signals:
|
|||
|
||||
void contentSetChanged(const QString& contentSetHref);
|
||||
|
||||
void appInstalled(const QString& appHref);
|
||||
void appUninstalled(const QString& appHref);
|
||||
void appInstalled(const QString& appID);
|
||||
void appUninstalled(const QString& appID);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void getWalletStatus();
|
||||
|
@ -86,7 +86,7 @@ protected:
|
|||
|
||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
|
||||
|
||||
Q_INVOKABLE QString getInstalledApps();
|
||||
Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = "");
|
||||
Q_INVOKABLE bool installApp(const QString& appHref);
|
||||
Q_INVOKABLE bool uninstallApp(const QString& appHref);
|
||||
Q_INVOKABLE bool openApp(const QString& appHref);
|
||||
|
|
|
@ -91,7 +91,7 @@ int main(int argc, const char* argv[]) {
|
|||
qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled();
|
||||
|
||||
if (ual.isEnabled()) {
|
||||
auto crashHandlerStarted = startCrashHandler();
|
||||
auto crashHandlerStarted = startCrashHandler(argv[0]);
|
||||
qDebug() << "Crash handler started:" << crashHandlerStarted;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,6 @@ void WindowScriptingInterface::raise() {
|
|||
});
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::raiseMainWindow() {
|
||||
raise();
|
||||
}
|
||||
|
||||
/// Display an alert box
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
|
|
|
@ -80,13 +80,6 @@ public slots:
|
|||
*/
|
||||
void raise();
|
||||
|
||||
/**jsdoc
|
||||
* Raise the Interface window if it is minimized. If raised, the window gains focus.
|
||||
* @function Window.raiseMainWindow
|
||||
* @deprecated Use {@link Window.raise|raise} instead.
|
||||
*/
|
||||
void raiseMainWindow();
|
||||
|
||||
/**jsdoc
|
||||
* Display a dialog with the specified message and an "OK" button. The dialog is non-modal; the script continues without
|
||||
* waiting for a user response.
|
||||
|
|
|
@ -179,7 +179,7 @@ static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPT
|
|||
void ApplicationOverlay::buildFramebufferObject() {
|
||||
PROFILE_RANGE(app, __FUNCTION__);
|
||||
|
||||
auto uiSize = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale());
|
||||
auto uiSize = glm::uvec2(qApp->getUiSize());
|
||||
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
|
||||
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ApplicationOverlay"));
|
||||
}
|
||||
|
|
|
@ -333,7 +333,13 @@ void Stats::updateStats(bool force) {
|
|||
}
|
||||
|
||||
auto gpuContext = qApp->getGPUContext();
|
||||
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
if (displayPlugin) {
|
||||
QVector2D dims(displayPlugin->getRecommendedRenderSize().x, displayPlugin->getRecommendedRenderSize().y);
|
||||
dims *= displayPlugin->getRenderResolutionScale();
|
||||
STAT_UPDATE(gpuFrameSize, dims);
|
||||
STAT_UPDATE(gpuFrameTimePerPixel, (float)(gpuContext->getFrameTimerGPUAverage()*1000000.0 / double(dims.x()*dims.y())));
|
||||
}
|
||||
// Update Frame timing (in ms)
|
||||
STAT_UPDATE(gpuFrameTime, (float)gpuContext->getFrameTimerGPUAverage());
|
||||
STAT_UPDATE(batchFrameTime, (float)gpuContext->getFrameTimerBatchAverage());
|
||||
|
|
|
@ -276,7 +276,9 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, gpuTextureExternalMemory, 0)
|
||||
STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString())
|
||||
STATS_PROPERTY(int, gpuFreeMemory, 0)
|
||||
STATS_PROPERTY(QVector2D, gpuFrameSize, QVector2D(0,0))
|
||||
STATS_PROPERTY(float, gpuFrameTime, 0)
|
||||
STATS_PROPERTY(float, gpuFrameTimePerPixel, 0)
|
||||
STATS_PROPERTY(float, batchFrameTime, 0)
|
||||
STATS_PROPERTY(float, engineFrameTime, 0)
|
||||
STATS_PROPERTY(float, avatarSimulationTime, 0)
|
||||
|
@ -962,6 +964,20 @@ signals:
|
|||
*/
|
||||
void gpuFrameTimeChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>gpuFrameTime</code> property changes.
|
||||
* @function Stats.gpuFrameTimeChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void gpuFrameSizeChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>gpuFrameTime</code> property changes.
|
||||
* @function Stats.gpuFrameTimeChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void gpuFrameTimePerPixelChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>batchFrameTime</code> property changes.
|
||||
* @function Stats.batchFrameTimeChanged
|
||||
|
|
|
@ -97,6 +97,10 @@ static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f;
|
|||
|
||||
void ContextOverlayInterface::setEnabled(bool enabled) {
|
||||
_enabled = enabled;
|
||||
if (!enabled) {
|
||||
// Destroy any potentially-active ContextOverlays when disabling the interface
|
||||
createOrDestroyContextOverlay(EntityItemID(), PointerEvent());
|
||||
}
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
|
|
|
@ -116,7 +116,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
auto size = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale());
|
||||
auto size = glm::uvec2(qApp->getUiSize());
|
||||
int width = size.x;
|
||||
int height = size.y;
|
||||
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
|
||||
|
|
|
@ -381,7 +381,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Get the rotation of the left palm in world coordinates.
|
||||
* @function MyAvatar.getLeftPalmRotation
|
||||
* @returns {Vec3} The rotation of the left palm in world coordinates.
|
||||
* @returns {Quat} The rotation of the left palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's left palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getLeftPalmRotation()));
|
||||
*/
|
||||
|
@ -398,7 +398,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Get the rotation of the right palm in world coordinates.
|
||||
* @function MyAvatar.getRightPalmRotation
|
||||
* @returns {Vec3} The rotation of the right palm in world coordinates.
|
||||
* @returns {Quat} The rotation of the right palm in world coordinates.
|
||||
* @example <caption>Report the rotation of your avatar's right palm.</caption>
|
||||
* print(JSON.stringify(MyAvatar.getRightPalmRotation()));
|
||||
*/
|
||||
|
|
|
@ -275,7 +275,7 @@ bool CompositorHelper::getReticleOverDesktop() const {
|
|||
// as being over the desktop.
|
||||
if (isHMD()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale();
|
||||
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize());
|
||||
static const glm::vec2 minOverlayPosition;
|
||||
if (glm::any(glm::lessThan(_reticlePositionInHMD, minOverlayPosition)) ||
|
||||
glm::any(glm::greaterThan(_reticlePositionInHMD, maxOverlayPosition))) {
|
||||
|
@ -317,7 +317,7 @@ void CompositorHelper::sendFakeMouseEvent() {
|
|||
|
||||
void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) {
|
||||
if (isHMD()) {
|
||||
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale();
|
||||
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize());
|
||||
// FIXME don't allow negative mouseExtra
|
||||
glm::vec2 mouseExtra = (MOUSE_EXTENTS_PIXELS - maxOverlayPosition) / 2.0f;
|
||||
glm::vec2 minMouse = vec2(0) - mouseExtra;
|
||||
|
|
|
@ -888,7 +888,7 @@ OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
|
|||
}
|
||||
|
||||
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
|
||||
auto renderSize = glm::uvec2(glm::vec2(getRecommendedRenderSize()) * getRenderResolutionScale());
|
||||
auto renderSize = glm::uvec2(getRecommendedRenderSize());
|
||||
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
|
||||
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
|
||||
}
|
||||
|
|
|
@ -363,12 +363,18 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
|
|||
return false;
|
||||
}
|
||||
|
||||
void EntityRenderer::updateModelTransform() {
|
||||
void EntityRenderer::updateModelTransformAndBound() {
|
||||
bool success = false;
|
||||
auto newModelTransform = _entity->getTransformToCenter(success);
|
||||
if (success) {
|
||||
_modelTransform = newModelTransform;
|
||||
}
|
||||
|
||||
success = false;
|
||||
auto bound = _entity->getAABox(success);
|
||||
if (success) {
|
||||
_bound = bound;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
|
||||
|
@ -380,15 +386,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
}
|
||||
_prevIsTransparent = transparent;
|
||||
|
||||
bool success = false;
|
||||
auto bound = entity->getAABox(success);
|
||||
if (success) {
|
||||
_bound = bound;
|
||||
}
|
||||
auto newModelTransform = entity->getTransformToCenter(success);
|
||||
if (success) {
|
||||
_modelTransform = newModelTransform;
|
||||
}
|
||||
updateModelTransformAndBound();
|
||||
|
||||
_moving = entity->isMovingRelativeToParent();
|
||||
_visible = entity->getVisible();
|
||||
|
|
|
@ -97,7 +97,7 @@ protected:
|
|||
virtual void doRender(RenderArgs* args) = 0;
|
||||
|
||||
bool isFading() const { return _isFading; }
|
||||
void updateModelTransform();
|
||||
void updateModelTransformAndBound();
|
||||
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
|
||||
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
|
||||
withWriteLock([&] {
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -106,10 +106,8 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_position = entity->getWorldPosition();
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
_orientation = entity->getWorldOrientation();
|
||||
bool success = false;
|
||||
auto newModelTransform = entity->getTransformToCenter(success);
|
||||
_renderTransform = success ? newModelTransform : getModelTransform();
|
||||
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
if (_shape == entity::Sphere) {
|
||||
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
|
||||
withWriteLock([&] {
|
||||
_dimensions = entity->getScaledDimensions();
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -206,7 +206,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransform();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
|
|
|
@ -22,13 +22,14 @@ const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f;
|
|||
|
||||
bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) {
|
||||
return
|
||||
|
||||
(a._currentFrame == b._currentFrame) &&
|
||||
(a._running == b._running) &&
|
||||
(a._loop == b._loop) &&
|
||||
(a._hold == b._hold) &&
|
||||
(a._firstFrame == b._firstFrame) &&
|
||||
(a._lastFrame == b._lastFrame) &&
|
||||
(a._fps == b._fps) &&
|
||||
(a._allowTranslation == b._allowTranslation) &&
|
||||
(a._url == b._url);
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,8 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
|
|||
(a._hold != b._hold) ||
|
||||
(a._firstFrame != b._firstFrame) ||
|
||||
(a._lastFrame != b._lastFrame) ||
|
||||
(a._fps != b._fps) ||
|
||||
(a._allowTranslation != b._allowTranslation) ||
|
||||
(a._url != b._url);
|
||||
}
|
||||
|
||||
|
|
|
@ -2414,11 +2414,7 @@ bool EntityItem::shouldSuppressLocationEdits() const {
|
|||
}
|
||||
|
||||
// if any of the ancestors are MyAvatar, suppress
|
||||
if (isChildOfMyAvatar()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return isChildOfMyAvatar();
|
||||
}
|
||||
|
||||
QList<EntityDynamicPointer> EntityItem::getActionsOfType(EntityDynamicType typeToGet) const {
|
||||
|
|
|
@ -21,14 +21,17 @@
|
|||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <qfile.h>
|
||||
#include <qfileinfo.h>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <ResourceManager.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
||||
|
@ -786,13 +789,18 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
QVector<glm::vec3> raw_vertices;
|
||||
QVector<glm::vec3> raw_normals;
|
||||
|
||||
addArrayOfType(indicesBuffer.blob,
|
||||
bool success = addArrayOfType(indicesBuffer.blob,
|
||||
indicesBufferview.byteOffset + indicesAccBoffset,
|
||||
indicesBufferview.byteLength,
|
||||
indicesAccessor.count,
|
||||
part.triangleIndices,
|
||||
indicesAccessor.type,
|
||||
indicesAccessor.componentType);
|
||||
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
|
||||
QList<QString> keys = primitive.attributes.values.keys();
|
||||
|
||||
foreach(auto &key, keys) {
|
||||
|
@ -805,44 +813,60 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
|
||||
if (key == "POSITION") {
|
||||
QVector<float> vertices;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength, vertices,
|
||||
accessor.count, vertices,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < vertices.size(); n = n + 3) {
|
||||
mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2]));
|
||||
}
|
||||
} else if (key == "NORMAL") {
|
||||
QVector<float> normals;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
normals,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < normals.size(); n = n + 3) {
|
||||
mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
|
||||
}
|
||||
} else if (key == "TEXCOORD_0") {
|
||||
QVector<float> texcoords;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
texcoords,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < texcoords.size(); n = n + 2) {
|
||||
mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
|
||||
}
|
||||
} else if (key == "TEXCOORD_1") {
|
||||
QVector<float> texcoords;
|
||||
addArrayOfType(buffer.blob,
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
bufferview.byteLength,
|
||||
accessor.count,
|
||||
texcoords,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < texcoords.size(); n = n + 2) {
|
||||
mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
|
||||
}
|
||||
|
@ -888,8 +912,16 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) {
|
|||
|
||||
FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping,
|
||||
const QUrl& url, bool loadLightmaps, float lightmapLevel) {
|
||||
|
||||
_url = url;
|
||||
|
||||
// Normalize url for local files
|
||||
QUrl normalizeUrl = DependencyManager::get<ResourceManager>()->normalizeURL(url);
|
||||
if (normalizeUrl.scheme().isEmpty() || (normalizeUrl.scheme() == "file")) {
|
||||
QString localFileName = PathUtils::expandToLocalDataAbsolutePath(normalizeUrl).toLocalFile();
|
||||
_url = QUrl(QFileInfo(localFileName).absoluteFilePath());
|
||||
}
|
||||
|
||||
parseGLTF(model);
|
||||
//_file.dump();
|
||||
FBXGeometry* geometryPtr = new FBXGeometry();
|
||||
|
@ -904,6 +936,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping
|
|||
|
||||
bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) {
|
||||
QUrl binaryUrl = _url.resolved(QUrl(url).fileName());
|
||||
|
||||
qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url;
|
||||
bool success;
|
||||
std::tie<bool, QByteArray>(success, outdata) = requestData(binaryUrl);
|
||||
|
@ -1018,13 +1051,12 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia
|
|||
fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.useAlbedoMap = true;
|
||||
fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]);
|
||||
fbxmat.useMetallicMap = true;
|
||||
}
|
||||
if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) {
|
||||
fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
|
||||
fbxmat.useRoughnessMap = true;
|
||||
|
||||
fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]);
|
||||
fbxmat.useMetallicMap = true;
|
||||
}
|
||||
if (material.pbrMetallicRoughness.defined["roughnessFactor"]) {
|
||||
fbxmat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor);
|
||||
|
@ -1043,7 +1075,7 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia
|
|||
}
|
||||
|
||||
template<typename T, typename L>
|
||||
bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<L>& outarray, int accessorType) {
|
||||
|
||||
QDataStream blobstream(bin);
|
||||
|
@ -1051,142 +1083,77 @@ bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength
|
|||
blobstream.setVersion(QDataStream::Qt_5_9);
|
||||
blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision);
|
||||
|
||||
int vsize = byteLength / sizeof(T);
|
||||
|
||||
qCDebug(modelformat) << "size1: " << vsize;
|
||||
qCDebug(modelformat) << "size1: " << count;
|
||||
int dataskipped = blobstream.skipRawData(byteOffset);
|
||||
qCDebug(modelformat) << "dataskipped: " << dataskipped;
|
||||
|
||||
|
||||
while (outarray.size() < vsize) {
|
||||
|
||||
T value1, value2, value3, value4,
|
||||
value5, value6, value7, value8,
|
||||
value9, value10, value11, value12,
|
||||
value13, value14, value15, value16;
|
||||
|
||||
if (accessorType == GLTFAccessorType::SCALAR) {
|
||||
|
||||
blobstream >> value1;
|
||||
|
||||
outarray.push_back(value1);
|
||||
} else if (accessorType == GLTFAccessorType::VEC2) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
} else if (accessorType == GLTFAccessorType::VEC3) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
} else if (accessorType == GLTFAccessorType::VEC4 || accessorType == GLTFAccessorType::MAT2) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
} else if (accessorType == GLTFAccessorType::MAT3) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
blobstream >> value5;
|
||||
blobstream >> value6;
|
||||
blobstream >> value7;
|
||||
blobstream >> value8;
|
||||
blobstream >> value9;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
outarray.push_back(value5);
|
||||
outarray.push_back(value6);
|
||||
outarray.push_back(value7);
|
||||
outarray.push_back(value8);
|
||||
outarray.push_back(value9);
|
||||
} else if (accessorType == GLTFAccessorType::MAT4) {
|
||||
|
||||
blobstream >> value1;
|
||||
blobstream >> value2;
|
||||
blobstream >> value3;
|
||||
blobstream >> value4;
|
||||
blobstream >> value5;
|
||||
blobstream >> value6;
|
||||
blobstream >> value7;
|
||||
blobstream >> value8;
|
||||
blobstream >> value9;
|
||||
blobstream >> value10;
|
||||
blobstream >> value11;
|
||||
blobstream >> value12;
|
||||
blobstream >> value13;
|
||||
blobstream >> value14;
|
||||
blobstream >> value15;
|
||||
blobstream >> value16;
|
||||
|
||||
outarray.push_back(value1);
|
||||
outarray.push_back(value2);
|
||||
outarray.push_back(value3);
|
||||
outarray.push_back(value4);
|
||||
outarray.push_back(value5);
|
||||
outarray.push_back(value6);
|
||||
outarray.push_back(value7);
|
||||
outarray.push_back(value8);
|
||||
outarray.push_back(value9);
|
||||
outarray.push_back(value10);
|
||||
outarray.push_back(value11);
|
||||
outarray.push_back(value12);
|
||||
outarray.push_back(value13);
|
||||
outarray.push_back(value14);
|
||||
outarray.push_back(value15);
|
||||
outarray.push_back(value16);
|
||||
|
||||
int bufferCount = 0;
|
||||
switch (accessorType) {
|
||||
case GLTFAccessorType::SCALAR:
|
||||
bufferCount = 1;
|
||||
break;
|
||||
case GLTFAccessorType::VEC2:
|
||||
bufferCount = 2;
|
||||
break;
|
||||
case GLTFAccessorType::VEC3:
|
||||
bufferCount = 3;
|
||||
break;
|
||||
case GLTFAccessorType::VEC4:
|
||||
bufferCount = 4;
|
||||
break;
|
||||
case GLTFAccessorType::MAT2:
|
||||
bufferCount = 4;
|
||||
break;
|
||||
case GLTFAccessorType::MAT3:
|
||||
bufferCount = 9;
|
||||
break;
|
||||
case GLTFAccessorType::MAT4:
|
||||
bufferCount = 16;
|
||||
break;
|
||||
default:
|
||||
qWarning(modelformat) << "Unknown accessorType: " << accessorType;
|
||||
blobstream.unsetDevice();
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int j = 0; j < bufferCount; j++) {
|
||||
if (!blobstream.atEnd()) {
|
||||
T value;
|
||||
blobstream >> value;
|
||||
outarray.push_back(value);
|
||||
} else {
|
||||
blobstream.unsetDevice();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blobstream.unsetDevice();
|
||||
return true;
|
||||
}
|
||||
template<typename T>
|
||||
bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<T>& outarray, int accessorType, int componentType) {
|
||||
|
||||
switch (componentType) {
|
||||
case GLTFAccessorComponentType::BYTE: {}
|
||||
case GLTFAccessorComponentType::UNSIGNED_BYTE: {
|
||||
readArray<uchar>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<uchar>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::SHORT: {
|
||||
readArray<short>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<short>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::UNSIGNED_INT: {
|
||||
readArray<quint32>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<uint>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::UNSIGNED_SHORT: {
|
||||
readArray<ushort>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<ushort>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
case GLTFAccessorComponentType::FLOAT: {
|
||||
readArray<float>(bin, byteOffset, byteLength, outarray, accessorType);
|
||||
break;
|
||||
return readArray<float>(bin, byteOffset, count, outarray, accessorType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLTFReader::retriangulate(const QVector<int>& inIndices, const QVector<glm::vec3>& in_vertices,
|
||||
|
|
|
@ -762,11 +762,11 @@ private:
|
|||
bool readBinary(const QString& url, QByteArray& outdata);
|
||||
|
||||
template<typename T, typename L>
|
||||
bool readArray(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool readArray(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<L>& outarray, int accessorType);
|
||||
|
||||
template<typename T>
|
||||
bool addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength,
|
||||
bool addArrayOfType(const QByteArray& bin, int byteOffset, int count,
|
||||
QVector<T>& outarray, int accessorType, int componentType);
|
||||
|
||||
void retriangulate(const QVector<int>& in_indices, const QVector<glm::vec3>& in_vertices,
|
||||
|
|
|
@ -177,9 +177,9 @@ void Haze::setHazeBaseReference(const float hazeBaseReference) {
|
|||
|
||||
void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (params.hazeBackgroundBlend != hazeBackgroundBlend) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = hazeBackgroundBlend;
|
||||
auto newBlend = 1.0f - hazeBackgroundBlend;
|
||||
if (params.hazeBackgroundBlend != newBlend) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = newBlend;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -289,6 +289,12 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
|
|||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
|
||||
if (!_sendDomainServerCheckInEnabled) {
|
||||
qCDebug(networking) << "Refusing to send a domain-server check in while it is disabled.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "sendDomainServerCheckIn", Qt::QueuedConnection);
|
||||
return;
|
||||
|
|
|
@ -90,6 +90,9 @@ public:
|
|||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool isRequesting);
|
||||
|
||||
bool getSendDomainServerCheckInEnabled() { return _sendDomainServerCheckInEnabled; }
|
||||
void setSendDomainServerCheckInEnabled(bool enabled) { _sendDomainServerCheckInEnabled = enabled; }
|
||||
|
||||
void removeFromIgnoreMuteSets(const QUuid& nodeID);
|
||||
|
||||
virtual bool isDomainServer() const override { return false; }
|
||||
|
@ -169,6 +172,8 @@ private:
|
|||
QTimer _keepAlivePingTimer;
|
||||
bool _requestsDomainListData { false };
|
||||
|
||||
bool _sendDomainServerCheckInEnabled { true };
|
||||
|
||||
mutable QReadWriteLock _ignoredSetLock;
|
||||
tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDs;
|
||||
mutable QReadWriteLock _personalMutedSetLock;
|
||||
|
|
|
@ -262,24 +262,53 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
|
|||
btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling.
|
||||
btVector3 endPos = startPos + linearDisplacement;
|
||||
|
||||
// resolve the simple linearDisplacement
|
||||
_followLinearDisplacement += linearDisplacement;
|
||||
|
||||
// now for the rotational part...
|
||||
btQuaternion startRot = bodyTransform.getRotation();
|
||||
btQuaternion desiredRot = _followDesiredBodyTransform.getRotation();
|
||||
if (desiredRot.dot(startRot) < 0.0f) {
|
||||
desiredRot = -desiredRot;
|
||||
|
||||
// startRot as default rotation
|
||||
btQuaternion endRot = startRot;
|
||||
|
||||
// the dot product between two quaternions is equal to +/- cos(angle/2)
|
||||
// where 'angle' is that of the rotation between them
|
||||
float qDot = desiredRot.dot(startRot);
|
||||
|
||||
// when the abs() value of the dot product is approximately 1.0
|
||||
// then the two rotations are effectively adjacent
|
||||
const float MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS = 0.99999f; // corresponds to approx 0.5 degrees
|
||||
if (fabsf(qDot) < MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS) {
|
||||
if (qDot < 0.0f) {
|
||||
// the quaternions are actually on opposite hyperhemispheres
|
||||
// so we move one to agree with the other and negate qDot
|
||||
desiredRot = -desiredRot;
|
||||
qDot = -qDot;
|
||||
}
|
||||
btQuaternion deltaRot = desiredRot * startRot.inverse();
|
||||
|
||||
// the axis is the imaginary part, but scaled by sin(angle/2)
|
||||
btVector3 axis(deltaRot.getX(), deltaRot.getY(), deltaRot.getZ());
|
||||
axis /= sqrtf(1.0f - qDot * qDot);
|
||||
|
||||
// compute the angle we will resolve for this dt, but don't overshoot
|
||||
float angle = 2.0f * acosf(qDot);
|
||||
if ( dt < _followTimeRemaining) {
|
||||
angle *= dt / _followTimeRemaining;
|
||||
}
|
||||
|
||||
// accumulate rotation
|
||||
deltaRot = btQuaternion(axis, angle);
|
||||
_followAngularDisplacement = (deltaRot * _followAngularDisplacement).normalize();
|
||||
|
||||
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account.
|
||||
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset);
|
||||
|
||||
endRot = deltaRot * startRot;
|
||||
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
|
||||
_followLinearDisplacement += swingDisplacement;
|
||||
}
|
||||
btQuaternion deltaRot = desiredRot * startRot.inverse();
|
||||
float angularSpeed = deltaRot.getAngle() / _followTimeRemaining;
|
||||
glm::vec3 rotationAxis = glm::normalize(glm::axis(bulletToGLM(deltaRot))); // deltaRot.getAxis() is inaccurate
|
||||
btQuaternion angularDisplacement = btQuaternion(glmToBullet(rotationAxis), angularSpeed * dt);
|
||||
btQuaternion endRot = angularDisplacement * startRot;
|
||||
|
||||
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account.
|
||||
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset);
|
||||
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
|
||||
|
||||
_followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement;
|
||||
_followAngularDisplacement = angularDisplacement * _followAngularDisplacement;
|
||||
|
||||
_rigidBody->setWorldTransform(btTransform(endRot, endPos));
|
||||
}
|
||||
_followTime += dt;
|
||||
|
|
|
@ -234,7 +234,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
return;
|
||||
}
|
||||
assert(entityTreeIsLocked());
|
||||
if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) {
|
||||
if (_motionType == MOTION_TYPE_KINEMATIC) {
|
||||
BT_PROFILE("kinematicIntegration");
|
||||
// This is physical kinematic motion which steps strictly by the subframe count
|
||||
// of the physics simulation and uses full gravity for acceleration.
|
||||
|
@ -327,13 +327,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool parentTransformSuccess;
|
||||
Transform localToWorld = _entity->getParentTransform(parentTransformSuccess);
|
||||
Transform worldToLocal;
|
||||
if (parentTransformSuccess) {
|
||||
localToWorld.evalInverse(worldToLocal);
|
||||
}
|
||||
|
||||
int numSteps = simulationStep - _lastStep;
|
||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
|
||||
|
@ -361,6 +354,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_body->isStaticOrKinematicObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_lastStep = simulationStep;
|
||||
if (glm::length2(_serverVelocity) > 0.0f) {
|
||||
// the entity-server doesn't know where avatars are, so it doesn't do simple extrapolation for children of
|
||||
|
@ -388,6 +385,12 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
// TODO: compensate for _worldOffset offset here
|
||||
|
||||
// compute position error
|
||||
bool parentTransformSuccess;
|
||||
Transform localToWorld = _entity->getParentTransform(parentTransformSuccess);
|
||||
Transform worldToLocal;
|
||||
if (parentTransformSuccess) {
|
||||
localToWorld.evalInverse(worldToLocal);
|
||||
}
|
||||
|
||||
btTransform worldTrans = _body->getWorldTransform();
|
||||
glm::vec3 position = worldToLocal.transform(bulletToGLM(worldTrans.getOrigin()));
|
||||
|
@ -407,20 +410,23 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
|
||||
if (glm::length2(_serverAngularVelocity) > 0.0f) {
|
||||
// compute rotation error
|
||||
float attenuation = powf(1.0f - _body->getAngularDamping(), dt);
|
||||
_serverAngularVelocity *= attenuation;
|
||||
//
|
||||
|
||||
// Bullet caps the effective rotation velocity inside its rotation integration step, therefore
|
||||
// we must integrate with the same algorithm and timestep in order achieve similar results.
|
||||
for (int i = 0; i < numSteps; ++i) {
|
||||
_serverRotation = glm::normalize(computeBulletRotationStep(_serverAngularVelocity,
|
||||
PHYSICS_ENGINE_FIXED_SUBSTEP) * _serverRotation);
|
||||
float attenuation = powf(1.0f - _body->getAngularDamping(), PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
_serverAngularVelocity *= attenuation;
|
||||
glm::quat rotation = computeBulletRotationStep(_serverAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
for (int i = 1; i < numSteps; ++i) {
|
||||
_serverAngularVelocity *= attenuation;
|
||||
rotation = computeBulletRotationStep(_serverAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP) * rotation;
|
||||
}
|
||||
_serverRotation = glm::normalize(rotation * _serverRotation);
|
||||
const float MIN_ROTATION_DOT = 0.99999f; // This corresponds to about 0.5 degrees of rotation
|
||||
glm::quat actualRotation = worldToLocal.getRotation() * bulletToGLM(worldTrans.getRotation());
|
||||
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
||||
}
|
||||
const float MIN_ROTATION_DOT = 0.99999f; // This corresponds to about 0.5 degrees of rotation
|
||||
glm::quat actualRotation = worldToLocal.getRotation() * bulletToGLM(worldTrans.getRotation());
|
||||
|
||||
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||
|
|
|
@ -59,7 +59,10 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
_entitiesToAddToPhysics.insert(entity);
|
||||
}
|
||||
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +153,10 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
removeOwnershipData(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_incomingChanges.insert(motionState);
|
||||
|
@ -160,11 +166,20 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
||||
// Perhaps it's shape has changed and it can now be added?
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
} else {
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +227,6 @@ const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhys
|
|||
assert(motionState);
|
||||
// TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen
|
||||
if (motionState) {
|
||||
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead() && entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
|
@ -255,7 +269,10 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
|||
// this entity should no longer be on the internal _entitiesToAddToPhysics
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr == _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
}
|
||||
} else if (entity->isReadyToComputeShape()) {
|
||||
ShapeInfo shapeInfo;
|
||||
|
@ -375,19 +392,21 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
|
||||
if (!getEntityTree()->isServerlessMode()) {
|
||||
motionState->initForBid();
|
||||
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||
_bids.push_back(motionState);
|
||||
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
|
||||
if (getEntityTree()->isServerlessMode()) {
|
||||
return;
|
||||
}
|
||||
motionState->initForBid();
|
||||
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||
_bids.push_back(motionState);
|
||||
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
|
||||
if (!getEntityTree()->isServerlessMode()) {
|
||||
motionState->initForOwned();
|
||||
_owned.push_back(motionState);
|
||||
if (getEntityTree()->isServerlessMode()) {
|
||||
return;
|
||||
}
|
||||
motionState->initForOwned();
|
||||
_owned.push_back(motionState);
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) {
|
||||
|
@ -426,7 +445,9 @@ void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) {
|
||||
bool serverlessMode = getEntityTree()->isServerlessMode();
|
||||
if (getEntityTree()->isServerlessMode()) {
|
||||
return;
|
||||
}
|
||||
PROFILE_RANGE_EX(simulation_physics, "Update", 0x00000000, (uint64_t)_owned.size());
|
||||
uint32_t i = 0;
|
||||
while (i < _owned.size()) {
|
||||
|
@ -438,7 +459,7 @@ void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) {
|
|||
}
|
||||
_owned.remove(i);
|
||||
} else {
|
||||
if (!serverlessMode && _owned[i]->shouldSendUpdate(numSubsteps)) {
|
||||
if (_owned[i]->shouldSendUpdate(numSubsteps)) {
|
||||
_owned[i]->sendUpdate(_entityPacketSender, numSubsteps);
|
||||
}
|
||||
++i;
|
||||
|
|