diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index d63e8e6620..81e25cc7ba 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -1172,15 +1172,15 @@ void OctreeServer::domainSettingsRequestComplete() {
pathToCopyFrom = oldDefaultPersistPath;
}
+ QDir persistFileDirectory = QDir(persistPath).filePath("..");
+ if (!persistFileDirectory.exists()) {
+ qDebug() << "Creating data directory " << persistFileDirectory.absolutePath();
+ persistFileDirectory.mkpath(".");
+ }
+
if (shouldCopy) {
qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath;
- QDir persistFileDirectory = QDir(persistPath).filePath("..");
-
- if (!persistFileDirectory.exists()) {
- qDebug() << "Creating data directory " << persistFileDirectory.path();
- persistFileDirectory.mkpath(".");
- }
QFile::copy(pathToCopyFrom, persistPath);
} else {
qDebug() << "No existing persist file found";
diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake
index e0523f32d3..b4744aa172 100644
--- a/cmake/macros/GenerateInstallers.cmake
+++ b/cmake/macros/GenerateInstallers.cmake
@@ -28,6 +28,15 @@ macro(GENERATE_INSTALLERS)
# include CMake module that will install compiler system libraries
# so that we have msvcr120 and msvcp120 installed with targets
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
+
+ # as long as we're including sixense plugin with installer
+ # we need re-distributables for VS 2011 as well
+ # this should be removed if/when sixense support is pulled
+ set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
+ "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll"
+ "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll"
+ )
+
include(InstallRequiredSystemLibraries)
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake
index b0f093977a..92e3273f67 100644
--- a/cmake/macros/SetPackagingParameters.cmake
+++ b/cmake/macros/SetPackagingParameters.cmake
@@ -82,10 +82,14 @@ macro(SET_PACKAGING_PARAMETERS)
set(DS_EXEC_NAME "domain-server.exe")
set(AC_EXEC_NAME "assignment-client.exe")
- # start menu shortcuts
- set(INTERFACE_SM_SHORTCUT_NAME "High Fidelity")
- set(CONSOLE_SM_SHORTCUT_NAME "Server Console")
-
+ # shortcut names
+ if (PRODUCTION_BUILD)
+ set(INTERFACE_SHORTCUT_NAME "High Fidelity")
+ set(CONSOLE_SHORTCUT_NAME "Server Console")
+ else ()
+ set(INTERFACE_SHORTCUT_NAME "High Fidelity - ${BUILD_VERSION}")
+ set(CONSOLE_SHORTCUT_NAME "Server Console - ${BUILD_VERSION}")
+ endif ()
# check if we need to find signtool
if (PRODUCTION_BUILD OR PR_BUILD)
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")
diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in
index 285de84d39..164e432706 100644
--- a/cmake/templates/CPackProperties.cmake.in
+++ b/cmake/templates/CPackProperties.cmake.in
@@ -9,10 +9,10 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
-set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SM_SHORTCUT_NAME@")
+set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
-set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SM_SHORTCUT_NAME@")
+set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in
index 0b37cd5853..b5699cb3b3 100644
--- a/cmake/templates/NSIS.template.in
+++ b/cmake/templates/NSIS.template.in
@@ -12,13 +12,7 @@
Var MUI_TEMP
Var STARTMENU_FOLDER
- Var SV_ALLUSERS
Var START_MENU
- Var DO_NOT_ADD_TO_PATH
- Var ADD_TO_PATH_ALL_USERS
- Var ADD_TO_PATH_CURRENT_USER
- Var INSTALL_DESKTOP
- Var IS_DEFAULT_INSTALLDIR
;--------------------------------
;Include Modern UI
@@ -205,346 +199,15 @@ Var AR_RegFlags
!define MUI_HEADERIMAGE_UNBITMAP "@UNINSTALLER_HEADER_IMAGE@"
!define MUI_ABORTWARNING
-;--------------------------------
-; path functions
-
-!verbose 3
-!include "WinMessages.NSH"
-!verbose 4
-
-;----------------------------------------
-; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02"
-;----------------------------------------
-!verbose 3
-!include "WinMessages.NSH"
-!verbose 4
-;====================================================
-; get_NT_environment
-; Returns: the selected environment
-; Output : head of the stack
-;====================================================
-!macro select_NT_profile UN
-Function ${UN}select_NT_profile
- StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single
- DetailPrint "Selected environment for all users"
- Push "all"
- Return
- environment_single:
- DetailPrint "Selected environment for current user only."
- Push "current"
- Return
-FunctionEnd
-!macroend
-!insertmacro select_NT_profile ""
-!insertmacro select_NT_profile "un."
-;----------------------------------------------------
-!define NT_current_env 'HKCU "Environment"'
-!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
-
-!ifndef WriteEnvStr_RegKey
- !ifdef ALL_USERS
- !define WriteEnvStr_RegKey \
- 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
- !else
- !define WriteEnvStr_RegKey 'HKCU "Environment"'
- !endif
-!endif
-
-; AddToPath - Adds the given dir to the search path.
-; Input - head of the stack
-; Note - Win9x systems requires reboot
-
-Function AddToPath
- Exch $0
- Push $1
- Push $2
- Push $3
-
- # don't add if the path doesn't exist
- IfFileExists "$0\*.*" "" AddToPath_done
-
- ReadEnvStr $1 PATH
- ; if the path is too long for a NSIS variable NSIS will return a 0
- ; length string. If we find that, then warn and skip any path
- ; modification as it will trash the existing path.
- StrLen $2 $1
- IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done
- CheckPathLength_ShowPathWarning:
- Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!"
- Goto AddToPath_done
- CheckPathLength_Done:
- Push "$1;"
- Push "$0;"
- Call StrStr
- Pop $2
- StrCmp $2 "" "" AddToPath_done
- Push "$1;"
- Push "$0\;"
- Call StrStr
- Pop $2
- StrCmp $2 "" "" AddToPath_done
- GetFullPathName /SHORT $3 $0
- Push "$1;"
- Push "$3;"
- Call StrStr
- Pop $2
- StrCmp $2 "" "" AddToPath_done
- Push "$1;"
- Push "$3\;"
- Call StrStr
- Pop $2
- StrCmp $2 "" "" AddToPath_done
-
- Call IsNT
- Pop $1
- StrCmp $1 1 AddToPath_NT
- ; Not on NT
- StrCpy $1 $WINDIR 2
- FileOpen $1 "$1\autoexec.bat" a
- FileSeek $1 -1 END
- FileReadByte $1 $2
- IntCmp $2 26 0 +2 +2 # DOS EOF
- FileSeek $1 -1 END # write over EOF
- FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n"
- FileClose $1
- SetRebootFlag true
- Goto AddToPath_done
-
- AddToPath_NT:
- StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey
- ReadRegStr $1 ${NT_current_env} "PATH"
- Goto DoTrim
- ReadAllKey:
- ReadRegStr $1 ${NT_all_env} "PATH"
- DoTrim:
- StrCmp $1 "" AddToPath_NTdoIt
- Push $1
- Call Trim
- Pop $1
- StrCpy $0 "$1;$0"
- AddToPath_NTdoIt:
- StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey
- WriteRegExpandStr ${NT_current_env} "PATH" $0
- Goto DoSend
- WriteAllKey:
- WriteRegExpandStr ${NT_all_env} "PATH" $0
- DoSend:
- SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
-
- AddToPath_done:
- Pop $3
- Pop $2
- Pop $1
- Pop $0
-FunctionEnd
-
-
-; RemoveFromPath - Remove a given dir from the path
-; Input: head of the stack
-
-Function un.RemoveFromPath
- Exch $0
- Push $1
- Push $2
- Push $3
- Push $4
- Push $5
- Push $6
-
- IntFmt $6 "%c" 26 # DOS EOF
-
- Call un.IsNT
- Pop $1
- StrCmp $1 1 unRemoveFromPath_NT
- ; Not on NT
- StrCpy $1 $WINDIR 2
- FileOpen $1 "$1\autoexec.bat" r
- GetTempFileName $4
- FileOpen $2 $4 w
- GetFullPathName /SHORT $0 $0
- StrCpy $0 "SET PATH=%PATH%;$0"
- Goto unRemoveFromPath_dosLoop
-
- unRemoveFromPath_dosLoop:
- FileRead $1 $3
- StrCpy $5 $3 1 -1 # read last char
- StrCmp $5 $6 0 +2 # if DOS EOF
- StrCpy $3 $3 -1 # remove DOS EOF so we can compare
- StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine
- StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine
- StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine
- StrCmp $3 "" unRemoveFromPath_dosLoopEnd
- FileWrite $2 $3
- Goto unRemoveFromPath_dosLoop
- unRemoveFromPath_dosLoopRemoveLine:
- SetRebootFlag true
- Goto unRemoveFromPath_dosLoop
-
- unRemoveFromPath_dosLoopEnd:
- FileClose $2
- FileClose $1
- StrCpy $1 $WINDIR 2
- Delete "$1\autoexec.bat"
- CopyFiles /SILENT $4 "$1\autoexec.bat"
- Delete $4
- Goto unRemoveFromPath_done
-
- unRemoveFromPath_NT:
- StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey
- ReadRegStr $1 ${NT_current_env} "PATH"
- Goto unDoTrim
- unReadAllKey:
- ReadRegStr $1 ${NT_all_env} "PATH"
- unDoTrim:
- StrCpy $5 $1 1 -1 # copy last char
- StrCmp $5 ";" +2 # if last char != ;
- StrCpy $1 "$1;" # append ;
- Push $1
- Push "$0;"
- Call un.StrStr ; Find `$0;` in $1
- Pop $2 ; pos of our dir
- StrCmp $2 "" unRemoveFromPath_done
- ; else, it is in path
- # $0 - path to add
- # $1 - path var
- StrLen $3 "$0;"
- StrLen $4 $2
- StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
- StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
- StrCpy $3 $5$6
-
- StrCpy $5 $3 1 -1 # copy last char
- StrCmp $5 ";" 0 +2 # if last char == ;
- StrCpy $3 $3 -1 # remove last char
-
- StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey
- WriteRegExpandStr ${NT_current_env} "PATH" $3
- Goto unDoSend
- unWriteAllKey:
- WriteRegExpandStr ${NT_all_env} "PATH" $3
- unDoSend:
- SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
-
- unRemoveFromPath_done:
- Pop $6
- Pop $5
- Pop $4
- Pop $3
- Pop $2
- Pop $1
- Pop $0
-FunctionEnd
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Uninstall sutff
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
###########################################
# Utility Functions #
###########################################
-;====================================================
-; IsNT - Returns 1 if the current system is NT, 0
-; otherwise.
-; Output: head of the stack
-;====================================================
-; IsNT
-; no input
-; output, top of the stack = 1 if NT or 0 if not
-;
-; Usage:
-; Call IsNT
-; Pop $R0
-; ($R0 at this point is 1 or 0)
-
-!macro IsNT un
-Function ${un}IsNT
- Push $0
- ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
- StrCmp $0 "" 0 IsNT_yes
- ; we are not NT.
- Pop $0
- Push 0
- Return
-
- IsNT_yes:
- ; NT!!!
- Pop $0
- Push 1
-FunctionEnd
-!macroend
-!insertmacro IsNT ""
-!insertmacro IsNT "un."
-
-; StrStr
-; input, top of stack = string to search for
-; top of stack-1 = string to search in
-; output, top of stack (replaces with the portion of the string remaining)
-; modifies no other variables.
-;
-; Usage:
-; Push "this is a long ass string"
-; Push "ass"
-; Call StrStr
-; Pop $R0
-; ($R0 at this point is "ass string")
-
-!macro StrStr un
-Function ${un}StrStr
-Exch $R1 ; st=haystack,old$R1, $R1=needle
- Exch ; st=old$R1,haystack
- Exch $R2 ; st=old$R1,old$R2, $R2=haystack
- Push $R3
- Push $R4
- Push $R5
- StrLen $R3 $R1
- StrCpy $R4 0
- ; $R1=needle
- ; $R2=haystack
- ; $R3=len(needle)
- ; $R4=cnt
- ; $R5=tmp
- loop:
- StrCpy $R5 $R2 $R3 $R4
- StrCmp $R5 $R1 done
- StrCmp $R5 "" done
- IntOp $R4 $R4 + 1
- Goto loop
-done:
- StrCpy $R1 $R2 "" $R4
- Pop $R5
- Pop $R4
- Pop $R3
- Pop $R2
- Exch $R1
-FunctionEnd
-!macroend
-!insertmacro StrStr ""
-!insertmacro StrStr "un."
-
-Function Trim ; Added by Pelaca
- Exch $R1
- Push $R2
-Loop:
- StrCpy $R2 "$R1" 1 -1
- StrCmp "$R2" " " RTrim
- StrCmp "$R2" "$\n" RTrim
- StrCmp "$R2" "$\r" RTrim
- StrCmp "$R2" ";" RTrim
- GoTo Done
-RTrim:
- StrCpy $R1 "$R1" -1
- Goto Loop
-Done:
- Pop $R2
- Exch $R1
-FunctionEnd
-
Function ConditionalAddToRegisty
Pop $0
Pop $1
StrCmp "$0" "" ConditionalAddToRegisty_EmptyString
- WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \
"$1" "$0"
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
DetailPrint "Set install registry entry: '$1' to '$0'"
@@ -598,11 +261,10 @@ FunctionEnd
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
- Page custom InstallOptionsPage
!insertmacro MUI_PAGE_DIRECTORY
;Start Menu Folder Page Configuration
- !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX"
+ !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
@@ -678,7 +340,6 @@ FunctionEnd
;Keep these lines before any File command
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
- ReserveFile "NSIS.InstallOptions.ini"
ReserveFile "@POST_INSTALL_OPTIONS_PATH@"
;--------------------------------
@@ -717,7 +378,7 @@ Section "-Core installation"
@CPACK_NSIS_FULL_INSTALL@
;Store installation folder
- WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
+ WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
;Package the signed uninstaller produced by the inner loop
!ifndef INNER
@@ -768,10 +429,10 @@ Section "-Core installation"
Push "Contact"
Push "@CPACK_NSIS_CONTACT@"
Call ConditionalAddToRegisty
- !insertmacro INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State"
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
;Create shortcuts
+
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
@CPACK_NSIS_CREATE_ICONS@
@CPACK_NSIS_CREATE_ICONS_EXTRA@
@@ -799,27 +460,10 @@ Section "-Core installation"
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"
- ;Read a value from an InstallOptions INI file
- !insertmacro INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
- !insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State"
- !insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State"
-
; Write special uninstall registry entries
Push "StartMenu"
Push "$STARTMENU_FOLDER"
Call ConditionalAddToRegisty
- Push "DoNotAddToPath"
- Push "$DO_NOT_ADD_TO_PATH"
- Call ConditionalAddToRegisty
- Push "AddToPathAllUsers"
- Push "$ADD_TO_PATH_ALL_USERS"
- Call ConditionalAddToRegisty
- Push "AddToPathCurrentUser"
- Push "$ADD_TO_PATH_CURRENT_USER"
- Call ConditionalAddToRegisty
- Push "InstallToDesktop"
- Push "$INSTALL_DESKTOP"
- Call ConditionalAddToRegisty
!insertmacro MUI_STARTMENU_WRITE_END
@@ -827,22 +471,6 @@ Section "-Core installation"
SectionEnd
-Section "-Add to path"
- Push $INSTDIR\bin
- StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath
- StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0
- Call AddToPath
- doNotAddToPath:
-SectionEnd
-
-;--------------------------------
-; Create custom pages
-Function InstallOptionsPage
- !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@"
- !insertmacro INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini"
-
-FunctionEnd
-
; Make sure nsDialogs is included before we use it
!include "nsdialogs.nsh"
@@ -908,7 +536,6 @@ Function PostInstallOptionsPage
; set the checkbox state depending on what is present in the registry
!insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
-
${EndIf}
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
@@ -941,6 +568,16 @@ Function PostInstallOptionsPage
!insertmacro SetPostInstallOption $LaunchNowCheckbox @LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
${If} @PR_BUILD@ == 1
+ ; a PR build defaults all install options expect LaunchNowCheckbox and the settings copy to unchecked
+ ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
+ ${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED}
+ ${EndIf}
+
+ ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
+ ${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED}
+ ${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED}
+ ${EndIf}
+
; push the offset
IntOp $CurrentOffset $CurrentOffset + 15
@@ -993,8 +630,17 @@ Function HandlePostInstallOptions
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
${If} $ServerStartupState == ${BST_CHECKED}
+ ; in case we added a shortcut in the global context, pull that now
+ SetShellVarContext all
+ Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
+
+ ; make a startup shortcut in this user's current context
+ SetShellVarContext current
CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
+ ; reset the shell var context back
+ SetShellVarContext all
+
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
${Else}
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
@@ -1002,6 +648,7 @@ Function HandlePostInstallOptions
${EndIf}
${If} @PR_BUILD@ == 1
+
; check if we need to copy settings/content from production for this PR build
${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState
@@ -1025,6 +672,8 @@ Function HandlePostInstallOptions
"There was a problem copying your production content and settings to $0 for this PR build.$\r$\n$\r$\nPlease copy them manually."
NoError:
+
+ SetShellVarContext all
${EndIf}
${EndIf}
@@ -1050,19 +699,30 @@ FunctionEnd
!include nsProcess.nsh
!macro PromptForRunningApplication applicationName displayName action prompter
- ${nsProcess::FindProcess} ${applicationName} $R0
+ !define UniqueID ${__LINE__}
- ${If} $R0 == 0
- ; the process is running, ask the user if they want us to close it
- MessageBox MB_OK|MB_ICONEXCLAMATION \
- "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and try again."
- Abort
- ${EndIf}
+ Prompt_${UniqueID}:
+
+ ${nsProcess::FindProcess} ${applicationName} $R0
+
+ ${If} $R0 == 0
+
+ ; the process is running, ask the user to close it
+ MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \
+ "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and click Retry to continue." \
+ /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0
+
+ ; If the user decided to cancel, stop the current installer/uninstaller
+ Abort
+
+ ${EndIf}
+
+ !undef UniqueID
!macroend
!macro CheckForRunningApplications action prompter
- !insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
- !insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
+ !insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "High Fidelity client" ${action} ${prompter}
+ !insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "Server Console" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
!macroend
@@ -1109,26 +769,6 @@ Function un.onInit
Quit
${EndSwitch}
- ClearErrors
- UserInfo::GetName
- IfErrors noLM
- Pop $0
- UserInfo::GetAccountType
- Pop $1
- StrCmp $1 "Admin" 0 +3
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Admin group'
- Goto done
- StrCmp $1 "Power" 0 +3
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Power Users group'
- Goto done
-
- noLM:
- ;Get installation folder from registry if available
-
- done:
-
FunctionEnd
;--- Add/Remove callback functions: ---
@@ -1186,19 +826,12 @@ FunctionEnd
!ifdef INNER
Section "Uninstall"
- ReadRegStr $START_MENU SHCTX \
+ ; use all users context for data/startup folders
+ SetShellVarContext all
+
+ ReadRegStr $START_MENU HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu"
;MessageBox MB_OK "Start menu is in: $START_MENU"
- ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \
- "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath"
- ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \
- "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers"
- ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \
- "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser"
- ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS"
- ReadRegStr $INSTALL_DESKTOP SHCTX \
- "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop"
- ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP "
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
@@ -1217,13 +850,13 @@ Section "Uninstall"
;Remove the uninstaller itself.
Delete "$INSTDIR\@UNINSTALLER_NAME@"
- DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
;Remove the installation directory if it is empty.
RMDir "$INSTDIR"
; Remove the registry entries.
- DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
+ DeleteRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
; Removes all optional components
!insertmacro SectionList "RemoveSection_CPack"
@@ -1235,7 +868,12 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk"
+
+ ; if it exists, delete the startup shortcut for the current user
+ SetShellVarContext current
Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk"
+ SetShellVarContext all
+
@CPACK_NSIS_DELETE_ICONS@
@CPACK_NSIS_DELETE_ICONS_EXTRA@
@@ -1264,6 +902,8 @@ Section "Uninstall"
; try to fix it.
StrCpy $MUI_TEMP "$START_MENU"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
+ Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
+ Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
@CPACK_NSIS_DELETE_ICONS_EXTRA@
;Delete empty start menu parent diretories
@@ -1279,24 +919,12 @@ Section "Uninstall"
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
secondStartMenuDeleteLoopDone:
- DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
+ DeleteRegKey /ifempty HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
- Push $INSTDIR\bin
- StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0
- Call un.RemoveFromPath
- doNotRemoveFromPath:
SectionEnd
!endif
-;--------------------------------
-; determine admin versus local install
-; Is install for "AllUsers" or "JustMe"?
-; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
-; This function is used for the very first "custom page" of the installer.
-; This custom page does not show up visibly, but it executes prior to the
-; first visible page and sets up $INSTDIR properly...
-; Choose different default installation folder based on SV_ALLUSERS...
-; "Program Files" for AllUsers, "My Documents" for JustMe...
+InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ""
Function .onInit
@@ -1342,48 +970,6 @@ inst:
; Reads components status for registry
!insertmacro SectionList "InitSection"
- ; check to see if /D has been used to change
- ; the install directory by comparing it to the
- ; install directory that is expected to be the
- ; default
- StrCpy $IS_DEFAULT_INSTALLDIR 0
- StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2
- StrCpy $IS_DEFAULT_INSTALLDIR 1
-
- StrCpy $SV_ALLUSERS "JustMe"
- ; if default install dir then change the default
- ; if it is installed for JustMe
- StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
- StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
-
- ClearErrors
- UserInfo::GetName
- IfErrors noLM
- Pop $0
- UserInfo::GetAccountType
- Pop $1
- StrCmp $1 "Admin" 0 +4
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Admin group'
- StrCpy $SV_ALLUSERS "AllUsers"
- Goto done
- StrCmp $1 "Power" 0 +4
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Power Users group'
- StrCpy $SV_ALLUSERS "AllUsers"
- Goto done
-
- noLM:
- StrCpy $SV_ALLUSERS "AllUsers"
- ;Get installation folder from registry if available
-
- done:
- StrCmp $SV_ALLUSERS "AllUsers" 0 +3
- StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
- StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
-
- StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
- !insertmacro INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
-
- noOptionsPage:
+ ; use all users for context of data/startup folders
+ SetShellVarContext all
FunctionEnd
diff --git a/examples/junkyard/junkyardClientReset.js b/examples/junkyard/junkyardClientReset.js
new file mode 100644
index 0000000000..c4dfa19c4f
--- /dev/null
+++ b/examples/junkyard/junkyardClientReset.js
@@ -0,0 +1,31 @@
+//
+// junkyardClientReset.js
+// examples/junkyard
+//
+// Created by Eric Levin on 1/21/16.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This script resets the junkyard scene
+//
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json";
+var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0};
+reset();
+
+function reset() {
+ // Delete everything and re-import the junkyard arf
+ var e = Entities.findEntities(MyAvatar.position, 1000);
+ for (i = 0; i < e.length; i++) {
+ Entities.deleteEntity(e[i]);
+ }
+ importAssetResourceFile();
+}
+
+function importAssetResourceFile() {
+ Clipboard.importEntities(IMPORT_URL);
+ Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION);
+}
\ No newline at end of file
diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js
new file mode 100644
index 0000000000..9a330d0cdc
--- /dev/null
+++ b/examples/junkyard/junkyardResetEntityScript.js
@@ -0,0 +1,53 @@
+// junkyardResetEntityScript.js
+//
+// Script Type: Entity
+// Created by Eric Levin on 1/20/16.
+// Copyright 2016 High Fidelity, Inc.
+//
+// This entity script resets the junkyard when triggered
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+(function() {
+ Script.include("../libraries/utils.js");
+ var _this;
+ var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json";
+ var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0};
+ var JunkyardResetter = function() {
+ _this = this;
+ };
+
+ JunkyardResetter.prototype = {
+
+ clickReleaseOnEntity: function(entityId, mouseEvent) {
+ if (!mouseEvent.isLeftButton) {
+ return;
+ }
+ this.reset();
+
+ },
+
+ reset: function() {
+ // Delete everything and re-import the junkyard arf
+ var e = Entities.findEntities(MyAvatar.position, 1000);
+ for (i = 0; i < e.length; i++) {
+ // Don't delete our reset entity
+ if (JSON.stringify(this.entityID) !== JSON.stringify(e[i])) {
+ Entities.deleteEntity(e[i]);
+ }
+ }
+ this.importAssetResourceFile();
+ },
+
+ importAssetResourceFile: function() {
+ Clipboard.importEntities(IMPORT_URL);
+ Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION);
+ },
+
+ preload: function(entityID) {
+ this.entityID = entityID;
+ },
+ };
+ return new JunkyardResetter();
+});
diff --git a/examples/junkyard/junkyardResetEntitySpawner.js b/examples/junkyard/junkyardResetEntitySpawner.js
new file mode 100644
index 0000000000..f467e0e22f
--- /dev/null
+++ b/examples/junkyard/junkyardResetEntitySpawner.js
@@ -0,0 +1,45 @@
+ //
+ // junkyardResetEntitySpawner.js
+ // examples/junkyard
+ //
+ // Created by Eric Levin on 1/20/16.
+ // Copyright 2016 High Fidelity, Inc.
+ //
+ // This script spawns an entity which, when triggered, will reset the junkyard
+ //
+ //
+ // Distributed under the Apache License, Version 2.0.
+ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+ //
+
+ var orientation = Camera.getOrientation();
+ orientation = Quat.safeEulerAngles(orientation);
+ orientation.x = 0;
+ orientation = Quat.fromVec3Degrees(orientation);
+ var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
+
+
+ var SCRIPT_URL = Script.resolvePath("junkyardResetEntityScript.js");
+ var MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Blueprint%20Objects/Asylum/Asylum_Table/Asylum_Table.fbx";
+ var resetEntity = Entities.addEntity({
+ type: "Model",
+ modelURL: MODEL_URL,
+ position: center,
+ script: SCRIPT_URL,
+ dimensions: {
+ x: 2.8,
+ y: 1.76,
+ z: 1.32
+ },
+ color: {
+ red: 200,
+ green: 10,
+ blue: 200
+ }
+ });
+
+ function cleanup() {
+ Entities.deleteEntity(resetEntity);
+ }
+
+ Script.scriptEnding.connect(cleanup);
\ No newline at end of file
diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml
index 8e42f67ebc..9aad639ff3 100644
--- a/interface/resources/qml/ToolWindow.qml
+++ b/interface/resources/qml/ToolWindow.qml
@@ -103,6 +103,8 @@ Windows.Window {
if (index < 0) {
return;
}
+ var tab = tabView.getTab(index);
+ tab.enabledChanged.disconnect(updateVisiblity);
tabView.removeTab(index);
console.log("Updating visibility based on child tab removed");
updateVisiblity();
@@ -137,10 +139,7 @@ Windows.Window {
}
console.log("Updating visibility based on child tab added");
- newTab.enabledChanged.connect(function() {
- console.log("Updating visibility based on child tab enabled change");
- updateVisiblity();
- })
+ newTab.enabledChanged.connect(updateVisiblity)
updateVisiblity();
return newTab
}
diff --git a/interface/resources/qml/controls/ComboBox.qml b/interface/resources/qml/controls/ComboBox.qml
new file mode 100644
index 0000000000..e22bc8e664
--- /dev/null
+++ b/interface/resources/qml/controls/ComboBox.qml
@@ -0,0 +1,148 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+
+import "." as VrControls
+
+FocusScope {
+ id: root
+ property alias model: comboBox.model;
+ readonly property alias currentText: comboBox.currentText;
+ property alias currentIndex: comboBox.currentIndex;
+ implicitHeight: comboBox.height;
+ focus: true
+
+ readonly property ComboBox control: comboBox
+
+ Rectangle {
+ id: background
+ gradient: Gradient {
+ GradientStop {color: control.pressed ? "#bababa" : "#fefefe" ; position: 0}
+ GradientStop {color: control.pressed ? "#ccc" : "#e3e3e3" ; position: 1}
+ }
+ anchors.fill: parent
+ border.color: control.activeFocus ? "#47b" : "#999"
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ color: control.activeFocus ? "#47b" : "white"
+ opacity: control.hovered || control.activeFocus ? 0.1 : 0
+ Behavior on opacity {NumberAnimation{ duration: 100 }}
+ }
+ }
+
+ SystemPalette { id: palette }
+
+ ComboBox {
+ id: comboBox
+ anchors.fill: parent
+ visible: false
+ }
+
+ Text {
+ id: textField
+ anchors { left: parent.left; leftMargin: 2; right: dropIcon.left; verticalCenter: parent.verticalCenter }
+ text: comboBox.currentText
+ elide: Text.ElideRight
+ }
+
+ Item {
+ id: dropIcon
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ width: 20
+ height: textField.height
+ VrControls.FontAwesome {
+ anchors.centerIn: parent; size: 16;
+ text: "\uf0d7"
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: toggleList();
+ }
+
+ function toggleList() {
+ if (popup.visible) {
+ hideList();
+ } else {
+ showList();
+ }
+ }
+
+ function showList() {
+ var r = desktop.mapFromItem(root, 0, 0, root.width, root.height);
+ listView.currentIndex = root.currentIndex
+ scrollView.x = r.x;
+ scrollView.y = r.y + r.height;
+ var bottom = scrollView.y + scrollView.height;
+ if (bottom > desktop.height) {
+ scrollView.y -= bottom - desktop.height + 8;
+ }
+ popup.visible = true;
+ popup.forceActiveFocus();
+ }
+
+ function hideList() {
+ popup.visible = false;
+ }
+
+ FocusScope {
+ id: popup
+ parent: desktop
+ anchors.fill: parent
+ visible: false
+ focus: true
+ MouseArea {
+ anchors.fill: parent
+ onClicked: hideList();
+ }
+
+ function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; }
+ function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
+ function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); }
+
+ Keys.onUpPressed: previousItem();
+ Keys.onDownPressed: nextItem();
+ Keys.onSpacePressed: selectCurrentItem();
+ Keys.onRightPressed: selectCurrentItem();
+ Keys.onReturnPressed: selectCurrentItem();
+ Keys.onEscapePressed: hideList();
+
+ ScrollView {
+ id: scrollView
+ height: 480
+ width: root.width
+
+ ListView {
+ id: listView
+ height: textView.height * count
+ model: root.model
+ highlight: Rectangle{
+ width: listView.currentItem ? listView.currentItem.width : 0
+ height: listView.currentItem ? listView.currentItem.height : 0
+ color: "red"
+ }
+ delegate: Rectangle {
+ width: root.width
+ height: popupText.implicitHeight * 1.4
+ color: ListView.isCurrentItem ? palette.highlight : palette.base
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ id: popupText
+ x: 3
+ text: listView.model[index]
+ }
+ MouseArea {
+ id: popupHover
+ anchors.fill: parent;
+ hoverEnabled: true
+ onEntered: listView.currentIndex = index;
+ onClicked: popup.selectCurrentItem()
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml
index 96c84b49c0..8c4f73c200 100644
--- a/interface/resources/qml/desktop/Desktop.qml
+++ b/interface/resources/qml/desktop/Desktop.qml
@@ -13,6 +13,9 @@ FocusScope {
anchors.fill: parent;
objectName: "desktop"
+ onHeightChanged: d.repositionAll();
+ onWidthChanged: d.repositionAll();
+
// Allows QML/JS to find the desktop through the parent chain
property bool desktopRoot: true
@@ -50,10 +53,6 @@ FocusScope {
return item;
}
- function isDesktop(item) {
- return item.desktopRoot;
- }
-
function isTopLevelWindow(item) {
return item.topLevelWindow;
}
@@ -147,6 +146,53 @@ FocusScope {
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 repositionAll() {
+ var windows = d.getTopLevelWindows();
+ for (var i = 0; i < windows.length; ++i) {
+ reposition(windows[i]);
+ }
+ }
}
function raise(item) {
@@ -167,7 +213,7 @@ FocusScope {
}
if (setFocus) {
- focus = true;
+ targetWindow.focus = true;
}
reposition(targetWindow);
@@ -187,27 +233,20 @@ FocusScope {
var windowRect = targetWindow.framedRect();
var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
- var newPosition;
- if (targetWindow.x === -1 && targetWindow.y === -1) {
+ var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
+ if (newPosition.x === -1 && newPosition.y === -1) {
// Set initial window position
- newPosition = Utils.randomPosition(minPosition, maxPosition);
- } else {
- newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition);
+ // newPosition = Utils.randomPosition(minPosition, maxPosition);
+ console.log("Target has no defined position, putting in center of the screen")
+ newPosition = Qt.vector2d(desktop.width / 2 - windowRect.width / 2,
+ desktop.height / 2 - windowRect.height / 2);
}
+
+ newPosition = Utils.clampVector(newPosition, minPosition, maxPosition);
targetWindow.x = newPosition.x;
targetWindow.y = newPosition.y;
}
- function repositionAll() {
- var windows = d.getTopLevelWindows();
- for (var i = 0; i < windows.length; ++i) {
- reposition(windows[i]);
- }
- }
-
- onHeightChanged: repositionAll();
- onWidthChanged: repositionAll();
-
Component { id: messageDialogBuilder; MessageDialog { } }
function messageBox(properties) {
return messageDialogBuilder.createObject(desktop, properties);
@@ -252,39 +291,21 @@ FocusScope {
desktop.focus = true;
}
- // Debugging help for figuring out focus issues
- property var offscreenWindow;
- onOffscreenWindowChanged: {
- offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
- focusHack.start();
- }
-
FocusHack { id: focusHack; }
- function onWindowFocusChanged() {
- console.log("Focus item is " + offscreenWindow.activeFocusItem);
- 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
- }
- }
-
Rectangle {
id: 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
}
+
}
diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/dialogs/RunningScripts.qml
index 059c765f0e..eb56b59b66 100644
--- a/interface/resources/qml/dialogs/RunningScripts.qml
+++ b/interface/resources/qml/dialogs/RunningScripts.qml
@@ -184,9 +184,28 @@ Window {
anchors.bottom: filterEdit.top
anchors.bottomMargin: 8
anchors.right: parent.right
+
+ // For some reason trigginer an API that enters
+ // an internal event loop directly from the button clicked
+ // trigger below causes the appliction to behave oddly.
+ // Most likely because the button onClicked handling is never
+ // completed until the function returns.
+ // FIXME find a better way of handling the input dialogs that
+ // doesn't trigger this.
+ Timer {
+ id: asyncAction
+ interval: 50
+ repeat: false
+ running: false
+ onTriggered: ApplicationInterface.loadScriptURLDialog();
+ }
+
Button {
text: "from URL";
- onClicked: ApplicationInterface.loadScriptURLDialog();
+ onClicked: {
+ focus = false;
+ asyncAction.running = true;
+ }
}
Button {
text: "from Disk"
diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
new file mode 100644
index 0000000000..1c70f06efd
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
@@ -0,0 +1,124 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import Qt.labs.settings 1.0
+
+import "../../windows"
+import "attachments"
+
+Window {
+ id: root
+ title: "Edit Attachments"
+ objectName: "AttachmentsDialog"
+ width: 600
+ height: 600
+ resizable: true
+ // User must click OK or cancel to close the window
+ closable: false
+
+ readonly property var originalAttachments: MyAvatar.getAttachmentsVariant();
+ property var attachments: [];
+
+ property var settings: Settings {
+ category: "AttachmentsDialog"
+ property alias x: root.x
+ property alias y: root.y
+ property alias width: root.width
+ property alias height: root.height
+ }
+
+ Component.onCompleted: {
+ for (var i = 0; i < originalAttachments.length; ++i) {
+ var attachment = originalAttachments[i];
+ root.attachments.push(attachment);
+ listView.model.append({});
+ }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: 4
+
+ Rectangle {
+ id: attachmentsBackground
+ anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
+ color: "gray"
+ radius: 4
+
+ ScrollView{
+ id: scrollView
+ anchors.fill: parent
+ anchors.margins: 4
+ ListView {
+ id: listView
+ model: ListModel {}
+ delegate: Item {
+ implicitHeight: attachmentView.height + 8;
+ implicitWidth: attachmentView.width;
+ Attachment {
+ id: attachmentView
+ width: scrollView.width
+ attachment: root.attachments[index]
+ onDeleteAttachment: {
+ attachments.splice(index, 1);
+ listView.model.remove(index, 1);
+ }
+ onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments);
+ }
+ }
+ onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
+ }
+ }
+ }
+
+ Button {
+ id: newAttachmentButton
+ anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
+ text: "New Attachment"
+
+ onClicked: {
+ var template = {
+ modelUrl: "",
+ translation: { x: 0, y: 0, z: 0 },
+ rotation: { x: 0, y: 0, z: 0 },
+ scale: 1,
+ jointName: MyAvatar.jointNames[0],
+ soft: false
+ };
+ attachments.push(template);
+ listView.model.append({});
+ MyAvatar.setAttachmentsVariant(attachments);
+ }
+ }
+
+ Row {
+ id: buttonRow
+ spacing: 8
+ anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
+ Button { action: cancelAction }
+ Button { action: okAction }
+ }
+
+ Action {
+ id: cancelAction
+ text: "Cancel"
+ onTriggered: {
+ MyAvatar.setAttachmentsVariant(originalAttachments);
+ root.destroy()
+ }
+ }
+
+ Action {
+ id: okAction
+ text: "OK"
+ onTriggered: {
+ for (var i = 0; i < attachments.length; ++i) {
+ console.log("Attachment " + i + ": " + attachments[i]);
+ }
+
+ MyAvatar.setAttachmentsVariant(attachments);
+ root.destroy()
+ }
+ }
+ }
+}
+
diff --git a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
new file mode 100644
index 0000000000..252e4c629e
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
@@ -0,0 +1,128 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.XmlListModel 2.0
+
+import "../../windows"
+import "../../js/Utils.js" as Utils
+import "../models"
+
+ModalWindow {
+ id: root
+ resizable: true
+ width: 640
+ height: 480
+
+ property var result;
+
+ signal selected(var modelUrl);
+ signal canceled();
+
+ Rectangle {
+ anchors.fill: parent
+ color: "white"
+
+ Item {
+ anchors { fill: parent; margins: 8 }
+
+ TextField {
+ id: filterEdit
+ anchors { left: parent.left; right: parent.right; top: parent.top }
+ placeholderText: "filter"
+ onTextChanged: tableView.model.filter = text
+ }
+
+ TableView {
+ id: tableView
+ anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
+ model: S3Model{}
+ onCurrentRowChanged: {
+ if (currentRow == -1) {
+ root.result = null;
+ return;
+ }
+ result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
+ }
+ itemDelegate: Component {
+ Item {
+ clip: true
+ Text {
+ x: 3
+ anchors.verticalCenter: parent.verticalCenter
+ color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
+ elide: styleData.elideMode
+ text: getText()
+
+ function getText() {
+ switch(styleData.column) {
+ case 1:
+ return Utils.formatSize(styleData.value)
+ default:
+ return styleData.value;
+ }
+ }
+
+ }
+ }
+ }
+ TableViewColumn {
+ role: "name"
+ title: "Name"
+ width: 200
+ }
+ TableViewColumn {
+ role: "size"
+ title: "Size"
+ width: 100
+ }
+ TableViewColumn {
+ role: "modified"
+ title: "Last Modified"
+ width: 200
+ }
+ Rectangle {
+ anchors.fill: parent
+ visible: tableView.model.status !== XmlListModel.Ready
+ color: "#7fffffff"
+ BusyIndicator {
+ anchors.centerIn: parent
+ width: 48; height: 48
+ running: true
+ }
+ }
+ }
+
+ Row {
+ id: buttonRow
+ anchors { right: parent.right; bottom: parent.bottom }
+ Button { action: acceptAction }
+ Button { action: cancelAction }
+ }
+
+ Action {
+ id: acceptAction
+ text: qsTr("OK")
+ enabled: root.result ? true : false
+ shortcut: Qt.Key_Return
+ onTriggered: {
+ root.selected(root.result);
+ root.destroy();
+ }
+ }
+
+ Action {
+ id: cancelAction
+ text: qsTr("Cancel")
+ shortcut: Qt.Key_Escape
+ onTriggered: {
+ root.canceled();
+ root.destroy();
+ }
+ }
+ }
+
+ }
+}
+
+
+
+
diff --git a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
new file mode 100644
index 0000000000..252e4c629e
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
@@ -0,0 +1,128 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.XmlListModel 2.0
+
+import "../../windows"
+import "../../js/Utils.js" as Utils
+import "../models"
+
+ModalWindow {
+ id: root
+ resizable: true
+ width: 640
+ height: 480
+
+ property var result;
+
+ signal selected(var modelUrl);
+ signal canceled();
+
+ Rectangle {
+ anchors.fill: parent
+ color: "white"
+
+ Item {
+ anchors { fill: parent; margins: 8 }
+
+ TextField {
+ id: filterEdit
+ anchors { left: parent.left; right: parent.right; top: parent.top }
+ placeholderText: "filter"
+ onTextChanged: tableView.model.filter = text
+ }
+
+ TableView {
+ id: tableView
+ anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
+ model: S3Model{}
+ onCurrentRowChanged: {
+ if (currentRow == -1) {
+ root.result = null;
+ return;
+ }
+ result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
+ }
+ itemDelegate: Component {
+ Item {
+ clip: true
+ Text {
+ x: 3
+ anchors.verticalCenter: parent.verticalCenter
+ color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
+ elide: styleData.elideMode
+ text: getText()
+
+ function getText() {
+ switch(styleData.column) {
+ case 1:
+ return Utils.formatSize(styleData.value)
+ default:
+ return styleData.value;
+ }
+ }
+
+ }
+ }
+ }
+ TableViewColumn {
+ role: "name"
+ title: "Name"
+ width: 200
+ }
+ TableViewColumn {
+ role: "size"
+ title: "Size"
+ width: 100
+ }
+ TableViewColumn {
+ role: "modified"
+ title: "Last Modified"
+ width: 200
+ }
+ Rectangle {
+ anchors.fill: parent
+ visible: tableView.model.status !== XmlListModel.Ready
+ color: "#7fffffff"
+ BusyIndicator {
+ anchors.centerIn: parent
+ width: 48; height: 48
+ running: true
+ }
+ }
+ }
+
+ Row {
+ id: buttonRow
+ anchors { right: parent.right; bottom: parent.bottom }
+ Button { action: acceptAction }
+ Button { action: cancelAction }
+ }
+
+ Action {
+ id: acceptAction
+ text: qsTr("OK")
+ enabled: root.result ? true : false
+ shortcut: Qt.Key_Return
+ onTriggered: {
+ root.selected(root.result);
+ root.destroy();
+ }
+ }
+
+ Action {
+ id: cancelAction
+ text: qsTr("Cancel")
+ shortcut: Qt.Key_Escape
+ onTriggered: {
+ root.canceled();
+ root.destroy();
+ }
+ }
+ }
+
+ }
+}
+
+
+
+
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
new file mode 100644
index 0000000000..31a1895e58
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
@@ -0,0 +1,165 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+
+import "../../../windows"
+import "../../../controls" as VrControls
+import "."
+import ".."
+
+Item {
+ height: column.height + 2 * 8
+
+ property var attachment;
+
+ signal deleteAttachment(var attachment);
+ signal updateAttachment();
+ property bool completed: false;
+
+ Rectangle { color: "white"; anchors.fill: parent; radius: 4 }
+
+ Component.onCompleted: {
+ completed = true;
+ }
+
+ Column {
+ y: 8
+ id: column
+ anchors { left: parent.left; right: parent.right; margins: 8 }
+ spacing: 8
+
+ Item {
+ height: modelChooserButton.height
+ anchors { left: parent.left; right: parent.right; }
+ Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter }
+ TextField {
+ id: modelUrl;
+ height: jointChooser.height;
+ anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left }
+ text: attachment ? attachment.modelUrl : ""
+ onTextChanged: {
+ if (completed && attachment && attachment.modelUrl !== text) {
+ attachment.modelUrl = text;
+ updateAttachment();
+ }
+ }
+ }
+ Button {
+ id: modelChooserButton;
+ text: "Choose";
+ anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
+ Component {
+ id: modelBrowserBuiler;
+ ModelBrowserDialog {}
+ }
+
+ onClicked: {
+ var browser = modelBrowserBuiler.createObject(desktop);
+ browser.selected.connect(function(newModelUrl){
+ modelUrl.text = newModelUrl;
+ })
+ }
+ }
+ }
+
+ Item {
+ height: jointChooser.height
+ anchors { left: parent.left; right: parent.right; }
+ Text {
+ id: jointLabel;
+ width: 80;
+ text: "Joint:";
+ anchors.verticalCenter: jointChooser.verticalCenter;
+ }
+ VrControls.ComboBox {
+ id: jointChooser;
+ anchors { left: jointLabel.right; leftMargin: 8; right: parent.right }
+ model: MyAvatar.jointNames
+ currentIndex: attachment ? model.indexOf(attachment.jointName) : -1
+ onCurrentIndexChanged: {
+ if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) {
+ attachment.jointName = currentText;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Item {
+ height: translation.height
+ anchors { left: parent.left; right: parent.right; }
+ Text { id: translationLabel; width: 80; text: "Translation:"; anchors.verticalCenter: translation.verticalCenter; }
+ Translation {
+ id: translation;
+ anchors { left: translationLabel.right; leftMargin: 8; right: parent.right }
+ vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0};
+ onValueChanged: {
+ if (completed && attachment) {
+ attachment.translation = vector;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Item {
+ height: rotation.height
+ anchors { left: parent.left; right: parent.right; }
+ Text { id: rotationLabel; width: 80; text: "Rotation:"; anchors.verticalCenter: rotation.verticalCenter; }
+ Rotation {
+ id: rotation;
+ anchors { left: rotationLabel.right; leftMargin: 8; right: parent.right }
+ vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0};
+ onValueChanged: {
+ if (completed && attachment) {
+ attachment.rotation = vector;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Item {
+ height: scaleSpinner.height
+ anchors { left: parent.left; right: parent.right; }
+ Text { id: scaleLabel; width: 80; text: "Scale:"; anchors.verticalCenter: scale.verticalCenter; }
+ SpinBox {
+ id: scaleSpinner;
+ anchors { left: scaleLabel.right; leftMargin: 8; right: parent.right }
+ decimals: 1;
+ minimumValue: 0.1
+ maximumValue: 10
+ stepSize: 0.1;
+ value: attachment ? attachment.scale : 1.0
+ onValueChanged: {
+ if (completed && attachment && attachment.scale !== value) {
+ attachment.scale = value;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Item {
+ height: soft.height
+ anchors { left: parent.left; right: parent.right; }
+ Text { id: softLabel; width: 80; text: "Is soft:"; anchors.verticalCenter: soft.verticalCenter; }
+ CheckBox {
+ id: soft;
+ anchors { left: softLabel.right; leftMargin: 8; right: parent.right }
+ checked: attachment ? attachment.soft : false
+ onCheckedChanged: {
+ if (completed && attachment && attachment.soft !== checked) {
+ attachment.soft = checked;
+ updateAttachment();
+ }
+ }
+ }
+ }
+
+ Button {
+ anchors { left: parent.left; right: parent.right; }
+ text: "Delete"
+ onClicked: deleteAttachment(root.attachment);
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml
new file mode 100644
index 0000000000..6061efc4c8
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml
@@ -0,0 +1,9 @@
+import "."
+
+Vector3 {
+ decimals: 1;
+ stepSize: 1;
+ maximumValue: 180
+ minimumValue: -180
+}
+
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml
new file mode 100644
index 0000000000..f3a90cdf94
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml
@@ -0,0 +1,9 @@
+import "."
+
+Vector3 {
+ decimals: 2;
+ stepSize: 0.01;
+ maximumValue: 10
+ minimumValue: -10
+}
+
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
new file mode 100644
index 0000000000..02382749bd
--- /dev/null
+++ b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
@@ -0,0 +1,71 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+
+Item {
+ id: root
+ implicitHeight: xspinner.height
+ readonly property real spacing: 8
+ property real spinboxWidth: (width / 3) - spacing
+ property var vector;
+ property real decimals: 0
+ property real stepSize: 1
+ property real maximumValue: 99
+ property real minimumValue: 0
+
+ signal valueChanged();
+
+ SpinBox {
+ id: xspinner
+ width: root.spinboxWidth
+ anchors { left: parent.left }
+ value: root.vector.x
+
+ decimals: root.decimals
+ stepSize: root.stepSize
+ maximumValue: root.maximumValue
+ minimumValue: root.minimumValue
+ onValueChanged: {
+ if (value !== vector.x) {
+ vector.x = value
+ root.valueChanged();
+ }
+ }
+ }
+
+ SpinBox {
+ id: yspinner
+ width: root.spinboxWidth
+ anchors { horizontalCenter: parent.horizontalCenter }
+ value: root.vector.y
+
+ decimals: root.decimals
+ stepSize: root.stepSize
+ maximumValue: root.maximumValue
+ minimumValue: root.minimumValue
+ onValueChanged: {
+ if (value !== vector.y) {
+ vector.y = value
+ root.valueChanged();
+ }
+ }
+ }
+
+ SpinBox {
+ id: zspinner
+ width: root.spinboxWidth
+ anchors { right: parent.right; }
+ value: root.vector.z
+
+ decimals: root.decimals
+ stepSize: root.stepSize
+ maximumValue: root.maximumValue
+ minimumValue: root.minimumValue
+ onValueChanged: {
+ if (value !== vector.z) {
+ vector.z = value
+ root.valueChanged();
+ }
+ }
+ }
+}
+
diff --git a/interface/resources/qml/hifi/models/S3Model.qml b/interface/resources/qml/hifi/models/S3Model.qml
new file mode 100644
index 0000000000..565965c124
--- /dev/null
+++ b/interface/resources/qml/hifi/models/S3Model.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.0
+import QtQuick.XmlListModel 2.0
+
+//http://s3.amazonaws.com/hifi-public?prefix=models/attachments
+/*
+
+
+
+ models/attachments/guitar.fst
+ 2015-11-10T00:28:22.000Z
+ "236c00c4802ba9c2605cabd5601d138e"
+ 2992
+ STANDARD
+
+
+*/
+
+// FIXME how to deal with truncated results? Store the marker?
+XmlListModel {
+ id: xmlModel
+ property string prefix: "models/attachments/"
+ property string extension: "fst"
+ property string filter;
+
+ readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/")
+ readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension
+ readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')"
+ readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public"
+
+ // FIXME need to urlencode prefix?
+ source: baseUrl + "?prefix=" + realPrefix
+ query: "/ListBucketResult/Contents[matches(Key, '" + nameRegex + "')]"
+ namespaceDeclarations: "declare default element namespace 'http://s3.amazonaws.com/doc/2006-03-01/';"
+
+ XmlRole { name: "name"; query: nameQuery }
+ XmlRole { name: "size"; query: "Size/string()" }
+ XmlRole { name: "tag"; query: "ETag/string()" }
+ XmlRole { name: "modified"; query: "LastModified/string()" }
+ XmlRole { name: "key"; query: "Key/string()" }
+}
+
diff --git a/interface/resources/qml/js/Utils.js b/interface/resources/qml/js/Utils.js
index 417c8c1641..49f85fcc91 100644
--- a/interface/resources/qml/js/Utils.js
+++ b/interface/resources/qml/js/Utils.js
@@ -14,3 +14,17 @@ function randomPosition(min, max) {
Math.random() * (max.x - min.x),
Math.random() * (max.y - min.y));
}
+
+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];
+}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a295cd8a6e..27f9d81ab2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1186,6 +1186,7 @@ void Application::initializeUi() {
UpdateDialog::registerType();
qmlRegisterType("Hifi", 1, 0, "Preference");
+
auto offscreenUi = DependencyManager::get();
offscreenUi->create(_offscreenContext->getContext());
offscreenUi->setProxyWindow(_window->windowHandle());
@@ -1380,10 +1381,12 @@ void Application::paintGL() {
}
+ glm::vec3 boomOffset;
{
PerformanceTimer perfTimer("CameraUpdates");
auto myAvatar = getMyAvatar();
+ boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;
myAvatar->startCapture();
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
@@ -1411,18 +1414,16 @@ void Application::paintGL() {
if (isHMDMode()) {
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
_myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat)));
- auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f));
- _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset);
+ _myCamera.setPosition(extractTranslation(hmdWorldMat) +
+ myAvatar->getOrientation() * boomOffset);
} else {
_myCamera.setRotation(myAvatar->getHead()->getOrientation());
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
- + _myCamera.getRotation()
- * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
+ + _myCamera.getRotation() * boomOffset);
} else {
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
- + myAvatar->getOrientation()
- * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
+ + myAvatar->getOrientation() * boomOffset);
}
}
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
@@ -1488,6 +1489,7 @@ void Application::paintGL() {
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
PerformanceTimer perfTimer("mainRender");
+ renderArgs._boomOffset = boomOffset;
// Viewport is assigned to the size of the framebuffer
renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
if (displayPlugin->isStereo()) {
@@ -1841,6 +1843,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_X:
if (isShifted && isMeta) {
+ auto offscreenUi = DependencyManager::get();
+ offscreenUi->getRootContext()->engine()->clearComponentCache();
+ OffscreenUi::information("Debugging", "Component cache cleared");
// placeholder for dialogs being converted to QML.
}
break;
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 9cbb031a61..720d8993ab 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -70,8 +70,8 @@ Menu::Menu() {
}
// File > Update -- FIXME: needs implementation
- auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update");
- updateAction->setDisabled(true);
+ auto action = addActionToQMenuAndActionHash(fileMenu, "Update");
+ action->setDisabled(true);
// File > Help
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
@@ -166,8 +166,11 @@ Menu::Menu() {
QObject* avatar = avatarManager->getMyAvatar();
// Avatar > Attachments...
- addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0,
- dialogsManager.data(), SLOT(editAttachments()));
+ action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
+ connect(action, &QAction::triggered, [] {
+ DependencyManager::get()->show(QString("hifi/dialogs/AttachmentsDialog.qml"), "AttachmentsDialog");
+ });
+
// Avatar > Size
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
@@ -285,7 +288,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
// Settings > General...
- auto action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
+ action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
connect(action, &QAction::triggered, [] {
DependencyManager::get()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog");
});
@@ -479,6 +482,10 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
+ addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
+ avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
+ addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
+ avatar, SLOT(setEnableInverseKinematics(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 408ab15d5e..7b9cca2c63 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -208,6 +208,7 @@ namespace MenuOption {
const QString EchoServerAudio = "Echo Server Audio";
const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString EnableCharacterController = "Enable avatar collisions";
+ const QString EnableInverseKinematics = "Enable Inverse Kinematics";
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
@@ -302,6 +303,7 @@ namespace MenuOption {
const QString UploadAsset = "Upload File to Asset Server";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
+ const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
const QString VelocityFilter = "Velocity Filter";
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index e06dce2324..f5765f9259 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -49,6 +50,8 @@
#include "Util.h"
#include "InterfaceLogging.h"
#include "DebugDraw.h"
+#include "EntityEditPacketSender.h"
+#include "MovingEntitiesOperator.h"
using namespace std;
@@ -362,6 +365,37 @@ void MyAvatar::simulate(float deltaTime) {
// consider updating our billboard
maybeUpdateBillboard();
+
+ locationChanged();
+ // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server.
+ EntityTreeRenderer* entityTreeRenderer = qApp->getEntities();
+ EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
+ if (entityTree) {
+ EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
+ MovingEntitiesOperator moveOperator(entityTree);
+ forEachDescendant([&](SpatiallyNestablePointer object) {
+ // if the queryBox has changed, tell the entity-server
+ if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
+ EntityItemPointer entity = std::static_pointer_cast(object);
+ bool success;
+ AACube newCube = entity->getQueryAACube(success);
+ if (success) {
+ moveOperator.addEntityToMoveList(entity, newCube);
+ }
+ if (packetSender) {
+ EntityItemProperties properties = entity->getProperties();
+ properties.setQueryAACubeDirty();
+ packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);
+ entity->setLastBroadcast(usecTimestampNow());
+ }
+ }
+ });
+ // also update the position of children in our local octree
+ if (moveOperator.hasMovingEntities()) {
+ PerformanceTimer perfTimer("recurseTreeWithOperator");
+ entityTree->recurseTreeWithOperator(&moveOperator);
+ }
+ }
}
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
@@ -647,6 +681,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
_skeletonModel.setVisibleInScene(isEnabled, scene);
}
+void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
+ AnimClip::usePreAndPostPoseFromAnim = isEnabled;
+ reset(true);
+}
+
+void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
+ _rig->setEnableInverseKinematics(isEnabled);
+}
+
void MyAvatar::loadData() {
Settings settings;
settings.beginGroup("Avatar");
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 10ce962821..ed6c3cb883 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -260,6 +260,8 @@ public slots:
void setEnableDebugDrawPosition(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
void setEnableMeshVisible(bool isEnabled);
+ void setUseAnimPreAndPostRotations(bool isEnabled);
+ void setEnableInverseKinematics(bool isEnabled);
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
glm::vec3 getPositionForAudio();
diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp
deleted file mode 100644
index d718b52d6d..0000000000
--- a/interface/src/ui/AttachmentsDialog.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-//
-// AttachmentsDialog.cpp
-// interface/src/ui
-//
-// Created by Andrzej Kapolka on 5/4/14.
-// Copyright 2014 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include "AttachmentsDialog.h"
-#include "ModelsBrowser.h"
-
-AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
- QDialog(parent) {
-
- setWindowTitle("Edit Attachments");
- setAttribute(Qt::WA_DeleteOnClose);
-
- QVBoxLayout* layout = new QVBoxLayout();
- setLayout(layout);
-
- QScrollArea* area = new QScrollArea();
- layout->addWidget(area);
- area->setWidgetResizable(true);
- QWidget* container = new QWidget();
- container->setLayout(_attachments = new QVBoxLayout());
- container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
- area->setWidget(container);
- _attachments->addStretch(1);
-
- foreach (const AttachmentData& data, DependencyManager::get()->getMyAvatar()->getAttachmentData()) {
- addAttachment(data);
- }
-
- QPushButton* newAttachment = new QPushButton("New Attachment");
- connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
- layout->addWidget(newAttachment);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
- layout->addWidget(buttons);
- connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
- _ok = buttons->button(QDialogButtonBox::Ok);
-
- setMinimumSize(600, 600);
-}
-
-void AttachmentsDialog::setVisible(bool visible) {
- QDialog::setVisible(visible);
-
- // un-default the OK button
- if (visible) {
- _ok->setDefault(false);
- }
-}
-
-void AttachmentsDialog::updateAttachmentData() {
- QVector data;
- for (int i = 0; i < _attachments->count() - 1; i++) {
- data.append(static_cast(_attachments->itemAt(i)->widget())->getAttachmentData());
- }
- DependencyManager::get()->getMyAvatar()->setAttachmentData(data);
-}
-
-void AttachmentsDialog::addAttachment(const AttachmentData& data) {
- _attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data));
-}
-
-static QDoubleSpinBox* createTranslationBox(AttachmentPanel* panel, float value) {
- QDoubleSpinBox* box = new QDoubleSpinBox();
- box->setSingleStep(0.01);
- box->setMinimum(-FLT_MAX);
- box->setMaximum(FLT_MAX);
- box->setValue(value);
- panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
- return box;
-}
-
-static QDoubleSpinBox* createRotationBox(AttachmentPanel* panel, float value) {
- QDoubleSpinBox* box = new QDoubleSpinBox();
- box->setMinimum(-180.0);
- box->setMaximum(180.0);
- box->setWrapping(true);
- box->setValue(value);
- panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
- return box;
-}
-
-AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) :
- _dialog(dialog),
- _applying(false) {
- setFrameStyle(QFrame::StyledPanel);
-
- QFormLayout* layout = new QFormLayout();
- layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- setLayout(layout);
-
- QHBoxLayout* urlBox = new QHBoxLayout();
- layout->addRow("Model URL:", urlBox);
- urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
- _modelURL->setText(data.modelURL.toString());
- connect(_modelURL, SIGNAL(editingFinished()), SLOT(modelURLChanged()));
- QPushButton* chooseURL = new QPushButton("Choose");
- urlBox->addWidget(chooseURL);
- connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
-
- layout->addRow("Joint:", _jointName = new QComboBox());
- QSharedPointer geometry = DependencyManager::get()->getMyAvatar()->getSkeletonModel().getGeometry();
- if (geometry && geometry->isLoaded()) {
- foreach (const FBXJoint& joint, geometry->getFBXGeometry().joints) {
- _jointName->addItem(joint.name);
- }
- }
- _jointName->setCurrentText(data.jointName);
- connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
-
- QHBoxLayout* translationBox = new QHBoxLayout();
- translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
- translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
- translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
- layout->addRow("Translation:", translationBox);
-
- QHBoxLayout* rotationBox = new QHBoxLayout();
- glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
- rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
- rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
- rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
- layout->addRow("Rotation:", rotationBox);
-
- layout->addRow("Scale:", _scale = new QDoubleSpinBox());
- _scale->setSingleStep(0.01);
- _scale->setMaximum(FLT_MAX);
- _scale->setValue(data.scale);
- connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
-
- layout->addRow("Is Soft:", _isSoft = new QCheckBox());
- _isSoft->setChecked(data.isSoft);
- connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData()));
-
- QPushButton* remove = new QPushButton("Delete");
- layout->addRow(remove);
- connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
- dialog->connect(remove, SIGNAL(clicked(bool)), SLOT(updateAttachmentData()), Qt::QueuedConnection);
-}
-
-AttachmentData AttachmentPanel::getAttachmentData() const {
- AttachmentData data;
- data.modelURL = _modelURL->text();
- data.jointName = _jointName->currentText();
- data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
- data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
- data.scale = _scale->value();
- data.isSoft = _isSoft->isChecked();
- return data;
-}
-
-void AttachmentPanel::chooseModelURL() {
- ModelsBrowser modelBrowser(FSTReader::ATTACHMENT_MODEL, this);
- connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&)));
- modelBrowser.browse();
-}
-
-void AttachmentPanel::setModelURL(const QString& url) {
- _modelURL->setText(url);
- modelURLChanged();
-}
-
-void AttachmentPanel::modelURLChanged() {
- // check for saved attachment data
- if (_modelURL->text().isEmpty()) {
- _dialog->updateAttachmentData();
- return;
- }
- AttachmentData attachment = DependencyManager::get()->getMyAvatar()->loadAttachmentData(_modelURL->text());
- if (attachment.isValid()) {
- _applying = true;
- _jointName->setCurrentText(attachment.jointName);
- applyAttachmentData(attachment);
- }
- _dialog->updateAttachmentData();
-}
-
-void AttachmentPanel::jointNameChanged() {
- if (_applying) {
- return;
- }
- // check for saved attachment data specific to this joint
- if (_modelURL->text().isEmpty()) {
- _dialog->updateAttachmentData();
- return;
- }
- AttachmentData attachment = DependencyManager::get()->getMyAvatar()->loadAttachmentData(
- _modelURL->text(), _jointName->currentText());
- if (attachment.isValid()) {
- applyAttachmentData(attachment);
- }
- updateAttachmentData();
-}
-
-void AttachmentPanel::updateAttachmentData() {
- if (_applying) {
- return;
- }
- // save the attachment data under the model URL (if any)
- if (!_modelURL->text().isEmpty()) {
- DependencyManager::get()->getMyAvatar()->saveAttachmentData(getAttachmentData());
- }
- _dialog->updateAttachmentData();
-}
-
-void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
- _applying = true;
- _translationX->setValue(attachment.translation.x);
- _translationY->setValue(attachment.translation.y);
- _translationZ->setValue(attachment.translation.z);
- glm::vec3 eulers = glm::degrees(safeEulerAngles(attachment.rotation));
- _rotationX->setValue(eulers.x);
- _rotationY->setValue(eulers.y);
- _rotationZ->setValue(eulers.z);
- _scale->setValue(attachment.scale);
- _isSoft->setChecked(attachment.isSoft);
- _applying = false;
- _dialog->updateAttachmentData();
-}
diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h
deleted file mode 100644
index 43ba5f8f3e..0000000000
--- a/interface/src/ui/AttachmentsDialog.h
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// AttachmentsDialog.h
-// interface/src/ui
-//
-// Created by Andrzej Kapolka on 5/4/14.
-// Copyright 2014 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_AttachmentsDialog_h
-#define hifi_AttachmentsDialog_h
-
-#include
-#include
-
-#include
-
-class QComboBox;
-class QDoubleSpinner;
-class QLineEdit;
-class QVBoxLayout;
-
-/// Allows users to edit the avatar attachments.
-class AttachmentsDialog : public QDialog {
- Q_OBJECT
-
-public:
- AttachmentsDialog(QWidget* parent = nullptr);
-
- virtual void setVisible(bool visible);
-
-public slots:
-
- void updateAttachmentData();
-
-private slots:
-
- void addAttachment(const AttachmentData& data = AttachmentData());
-
-private:
-
- QVBoxLayout* _attachments;
- QPushButton* _ok;
-};
-
-/// A panel controlling a single attachment.
-class AttachmentPanel : public QFrame {
- Q_OBJECT
-
-public:
-
- AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
-
- AttachmentData getAttachmentData() const;
-
-private slots:
-
- void chooseModelURL();
- void setModelURL(const QString& url);
- void modelURLChanged();
- void jointNameChanged();
- void updateAttachmentData();
-
-private:
-
- void applyAttachmentData(const AttachmentData& attachment);
-
- AttachmentsDialog* _dialog;
- QLineEdit* _modelURL;
- QComboBox* _jointName;
- QDoubleSpinBox* _translationX;
- QDoubleSpinBox* _translationY;
- QDoubleSpinBox* _translationZ;
- QDoubleSpinBox* _rotationX;
- QDoubleSpinBox* _rotationY;
- QDoubleSpinBox* _rotationZ;
- QDoubleSpinBox* _scale;
- QCheckBox* _isSoft;
- bool _applying;
-};
-
-#endif // hifi_AttachmentsDialog_h
diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp
index ef0ec5792f..41d7a0eb13 100644
--- a/interface/src/ui/DialogsManager.cpp
+++ b/interface/src/ui/DialogsManager.cpp
@@ -19,7 +19,6 @@
#include
#include "AddressBarDialog.h"
-#include "AttachmentsDialog.h"
#include "BandwidthDialog.h"
#include "CachesSizeDialog.h"
#include "DiskCacheEditor.h"
@@ -91,15 +90,6 @@ void DialogsManager::cachesSizeDialog() {
_cachesSizeDialog->raise();
}
-void DialogsManager::editAttachments() {
- if (!_attachmentsDialog) {
- maybeCreateDialog(_attachmentsDialog);
- _attachmentsDialog->show();
- } else {
- _attachmentsDialog->close();
- }
-}
-
void DialogsManager::audioStatsDetails() {
if (! _audioStatsDialog) {
_audioStatsDialog = new AudioStatsDialog(qApp->getWindow());
diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h
index 72a24e032c..b8fa22ec83 100644
--- a/interface/src/ui/DialogsManager.h
+++ b/interface/src/ui/DialogsManager.h
@@ -48,7 +48,6 @@ public slots:
void showLoginDialog();
void octreeStatsDetails();
void cachesSizeDialog();
- void editAttachments();
void audioStatsDetails();
void bandwidthDetails();
void lodTools();
diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp
index 986bb0a30e..90cd85e727 100644
--- a/libraries/animation/src/AnimClip.cpp
+++ b/libraries/animation/src/AnimClip.cpp
@@ -13,6 +13,8 @@
#include "AnimationLogging.h"
#include "AnimUtil.h"
+bool AnimClip::usePreAndPostPoseFromAnim = false;
+
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
AnimNode(AnimNode::Type::Clip, id),
_startFrame(startFrame),
@@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() {
for (int frame = 0; frame < frameCount; frame++) {
+ const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
+
// init all joints in animation to default pose
// this will give us a resonable result for bones in the model skeleton but not in the animation.
_anim[frame].reserve(skeletonJointCount);
@@ -119,36 +123,41 @@ void AnimClip::copyFromNetworkAnim() {
for (int animJoint = 0; animJoint < animJointCount; animJoint++) {
int skeletonJoint = jointMap[animJoint];
+ const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint];
+ const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint];
+
// skip joints that are in the animation but not in the skeleton.
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
- const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
-#ifdef USE_PRE_ROT_FROM_ANIM
- // TODO: This is the correct way to apply the pre rotations from maya, however
- // the current animation set has incorrect preRotations for the left wrist and thumb
- // so it looks wrong if we enable this code.
- glm::quat preRotation = animSkeleton.getPreRotation(animJoint);
-#else
- // TODO: This is the legacy approach, this does not work when animations and models do not
- // have the same set of pre rotations. For example when mixing maya models with blender animations.
- glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot;
-#endif
- const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
+ AnimPose preRot, postRot;
+ if (usePreAndPostPoseFromAnim) {
+ preRot = animSkeleton.getPreRotationPose(animJoint);
+ postRot = animSkeleton.getPostRotationPose(animJoint);
+ } else {
+ // In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
+ preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3());
+ postRot = AnimPose::identity;
+ }
- // used to adjust translation offsets, so large translation animatons on the reference skeleton
+ // cancel out scale
+ preRot.scale = glm::vec3(1.0f);
+ postRot.scale = glm::vec3(1.0f);
+
+ AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3());
+
+ // adjust translation offsets, so large translation animatons on the reference skeleton
// will be adjusted when played on a skeleton with short limbs.
- float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans));
+ const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint];
+ const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint);
+ float boneLengthScale = 1.0f;
+ const float EPSILON = 0.0001f;
+ if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) {
+ boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans);
+ }
- AnimPose& pose = _anim[frame][skeletonJoint];
- const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame];
+ AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans));
- // rotation in fbxAnimationFrame is a delta from its preRotation.
- pose.rot = preRotation * fbxAnimFrame.rotations[animJoint];
-
- // translation in fbxAnimationFrame is not a delta.
- // convert it into a delta by subtracting from the first frame.
- const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint];
- pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans);
+ _anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
}
}
}
diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h
index 934f3f3ed8..7d58ae4f9a 100644
--- a/libraries/animation/src/AnimClip.h
+++ b/libraries/animation/src/AnimClip.h
@@ -25,6 +25,8 @@ class AnimClip : public AnimNode {
public:
friend class AnimTests;
+ static bool usePreAndPostPoseFromAnim;
+
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
virtual ~AnimClip() override;
diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp
index 415ee72d7b..e47a12960e 100644
--- a/libraries/animation/src/AnimInverseKinematics.cpp
+++ b/libraries/animation/src/AnimInverseKinematics.cpp
@@ -153,7 +153,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex);
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
@@ -165,12 +164,30 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorapply(tipRelativeRotation);
+ if (constrained) {
+ tipOrientation = glm::normalize(tipRelativeRotation * tipParentOrientation);
+ }
+ }
+ }
+
+ // cache tip absolute position
+ glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
// descend toward root, pivoting each joint to get tip closer to target
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
@@ -201,9 +218,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorapply(newRot);
if (constrained) {
- // the constraint will modify the movement of the tip so we have to compute the modified
- // model-frame deltaRotation
+ // the constraint will modify the local rotation of the tip so we must
+ // compute the corresponding model-frame deltaRotation
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
deltaRotation = absolutePoses[pivotsParentIndex].rot *
- newRot *
- glm::inverse(absolutePoses[pivotIndex].rot);
+ newRot * glm::inverse(absolutePoses[pivotIndex].rot);
}
}
@@ -264,8 +280,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(pivotIndex);
@@ -629,11 +645,11 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
- const float MAX_NECK_TWIST = PI / 4.0f;
+ const float MAX_NECK_TWIST = PI / 6.0f;
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
std::vector minDots;
- const float MAX_NECK_SWING = PI / 3.0f;
+ const float MAX_NECK_SWING = PI / 4.0f;
minDots.push_back(cosf(MAX_NECK_SWING));
stConstraint->setSwingLimits(minDots);
@@ -641,11 +657,11 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
- const float MAX_HEAD_TWIST = PI / 4.0f;
+ const float MAX_HEAD_TWIST = PI / 8.0f;
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
std::vector minDots;
- const float MAX_HEAD_SWING = PI / 4.0f;
+ const float MAX_HEAD_SWING = PI / 6.0f;
minDots.push_back(cosf(MAX_HEAD_SWING));
stConstraint->setSwingLimits(minDots);
diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp
index 8e3d716aac..8f45b785d1 100644
--- a/libraries/animation/src/AnimSkeleton.cpp
+++ b/libraries/animation/src/AnimSkeleton.cpp
@@ -59,8 +59,14 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const {
return _absoluteDefaultPoses[jointIndex];
}
-const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const {
- return _joints[jointIndex].preRotation;
+// get pre multiplied transform which should include FBX pre potations
+const AnimPose& AnimSkeleton::getPreRotationPose(int jointIndex) const {
+ return _relativePreRotationPoses[jointIndex];
+}
+
+// get post multiplied transform which might include FBX offset transformations
+const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
+ return _relativePostRotationPoses[jointIndex];
}
int AnimSkeleton::getParentIndex(int jointIndex) const {
@@ -99,13 +105,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints)
// build a chache of default poses
_absoluteDefaultPoses.reserve(joints.size());
_relativeDefaultPoses.reserve(joints.size());
+ _relativePreRotationPoses.reserve(joints.size());
+ _relativePostRotationPoses.reserve(joints.size());
// iterate over FBXJoints and extract the bind pose information.
for (int i = 0; i < (int)joints.size(); i++) {
+ // build pre and post transforms
+ glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation);
+ glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform;
+ _relativePreRotationPoses.push_back(AnimPose(preRotationTransform));
+ _relativePostRotationPoses.push_back(AnimPose(postRotationTransform));
+
// build relative and absolute default poses
- glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation);
- glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform;
+ glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform;
AnimPose relDefaultPose(relDefaultMat);
_relativeDefaultPoses.push_back(relDefaultPose);
int parentIndex = getParentIndex(i);
diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h
index 7312fea6b1..757f4e5c3e 100644
--- a/libraries/animation/src/AnimSkeleton.h
+++ b/libraries/animation/src/AnimSkeleton.h
@@ -42,8 +42,11 @@ public:
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
- // get pre-rotation aka Maya's joint orientation.
- const glm::quat getPreRotation(int jointIndex) const;
+ // get pre transform which should include FBX pre potations
+ const AnimPose& getPreRotationPose(int jointIndex) const;
+
+ // get post transform which might include FBX offset transformations
+ const AnimPose& getPostRotationPose(int jointIndex) const;
int getParentIndex(int jointIndex) const;
@@ -64,6 +67,8 @@ protected:
AnimPoseVec _relativeBindPoses;
AnimPoseVec _relativeDefaultPoses;
AnimPoseVec _absoluteDefaultPoses;
+ AnimPoseVec _relativePreRotationPoses;
+ AnimPoseVec _relativePostRotationPoses;
// no copies
AnimSkeleton(const AnimSkeleton&) = delete;
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index df92d7d912..4d72bfbaea 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
}
}
+void Rig::setEnableInverseKinematics(bool enable) {
+ _enableInverseKinematics = enable;
+}
+
AnimPose Rig::getAbsoluteDefaultPose(int index) const {
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
return _absoluteDefaultPoses[index];
@@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
}
t += deltaTime;
+
+ if (_enableInverseKinematics) {
+ _animVars.set("ikOverlayAlpha", 1.0f);
+ } else {
+ _animVars.set("ikOverlayAlpha", 0.0f);
+ }
}
_lastFront = front;
diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h
index d26be83d36..cf96c6dc16 100644
--- a/libraries/animation/src/Rig.h
+++ b/libraries/animation/src/Rig.h
@@ -208,6 +208,8 @@ public:
void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const;
+ void setEnableInverseKinematics(bool enable);
+
protected:
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
void updateAnimationStateHandlers();
@@ -290,6 +292,8 @@ public:
std::map _origRoleAnimations;
std::vector _prefetchedAnimations;
+ bool _enableInverseKinematics { true };
+
private:
QMap _stateHandlers;
int _nextStateHandlerId { 0 };
diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h
index b1c810710e..6c12c6aa15 100644
--- a/libraries/audio/src/AudioConstants.h
+++ b/libraries/audio/src/AudioConstants.h
@@ -29,7 +29,7 @@ namespace AudioConstants {
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
- const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f);
+ const int NETWORK_FRAME_USECS = static_cast(NETWORK_FRAME_MSECS * 1000.0f);
const int MIN_SAMPLE_VALUE = std::numeric_limits::min();
const int MAX_SAMPLE_VALUE = std::numeric_limits::max();
}
diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp
index 1f15007339..661509c3d2 100644
--- a/libraries/audio/src/AudioInjector.cpp
+++ b/libraries/audio/src/AudioInjector.cpp
@@ -36,7 +36,7 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO
_audioData(sound->getByteArray()),
_options(injectorOptions)
{
-
+
}
AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) :
@@ -49,15 +49,15 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt
void AudioInjector::finish() {
bool shouldDelete = (_state == State::NotFinishedWithPendingDelete);
_state = State::Finished;
-
+
emit finished();
-
+
if (_localBuffer) {
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = NULL;
}
-
+
if (shouldDelete) {
// we've been asked to delete after finishing, trigger a deleteLater here
deleteLater();
@@ -67,7 +67,7 @@ void AudioInjector::finish() {
void AudioInjector::setupInjection() {
if (!_hasSetup) {
_hasSetup = true;
-
+
// check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) {
@@ -87,31 +87,31 @@ void AudioInjector::setupInjection() {
void AudioInjector::restart() {
// grab the AudioInjectorManager
auto injectorManager = DependencyManager::get();
-
+
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "restart");
-
+
if (!_options.localOnly) {
// notify the AudioInjectorManager to wake up in case it's waiting for new injectors
injectorManager->notifyInjectorReadyCondition();
}
-
+
return;
}
-
+
// reset the current send offset to zero
_currentSendOffset = 0;
-
+
// check our state to decide if we need extra handling for the restart request
if (_state == State::Finished) {
// we finished playing, need to reset state so we can get going again
_hasSetup = false;
_shouldStop = false;
_state = State::NotFinished;
-
+
// call inject audio to start injection over again
setupInjection();
-
+
// if we're a local injector call inject locally to start injecting again
if (_options.localOnly) {
injectLocally();
@@ -153,155 +153,167 @@ bool AudioInjector::injectLocally() {
// we never started so we are finished, call our stop method
stop();
}
-
+
return success;
}
const uchar MAX_INJECTOR_VOLUME = 0xFF;
-static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0;
-static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1;
+static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
+static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
-uint64_t AudioInjector::injectNextFrame() {
-
+int64_t AudioInjector::injectNextFrame() {
if (_state == AudioInjector::State::Finished) {
qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning.";
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
-
+
// if we haven't setup the packet to send then do so now
static int positionOptionOffset = -1;
static int volumeOptionOffset = -1;
static int audioDataOffset = -1;
-
+
if (!_currentPacket) {
if (_currentSendOffset < 0 ||
_currentSendOffset >= _audioData.size()) {
_currentSendOffset = 0;
}
-
+
// make sure we actually have samples downloaded to inject
if (_audioData.size()) {
-
+
_outgoingSequenceNumber = 0;
_nextFrame = 0;
-
+
if (!_frameTimer) {
_frameTimer = std::unique_ptr(new QElapsedTimer);
}
-
+
_frameTimer->restart();
-
+
_currentPacket = NLPacket::create(PacketType::InjectAudio);
-
+
// setup the packet for injected audio
QDataStream audioPacketStream(_currentPacket.get());
-
+
// pack some placeholder sequence number for now
audioPacketStream << (quint16) 0;
-
+
// pack stream identifier (a generated UUID)
audioPacketStream << QUuid::createUuid();
-
+
// pack the stereo/mono type of the stream
audioPacketStream << _options.stereo;
-
+
// pack the flag for loopback
- uchar loopbackFlag = (uchar) true;
+ uchar loopbackFlag = (uchar)true;
audioPacketStream << loopbackFlag;
-
+
// pack the position for injected audio
positionOptionOffset = _currentPacket->pos();
audioPacketStream.writeRawData(reinterpret_cast(&_options.position),
sizeof(_options.position));
-
+
// pack our orientation for injected audio
audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation),
sizeof(_options.orientation));
-
+
// pack zero for radius
float radius = 0;
audioPacketStream << radius;
-
+
// pack 255 for attenuation byte
volumeOptionOffset = _currentPacket->pos();
quint8 volume = MAX_INJECTOR_VOLUME;
audioPacketStream << volume;
-
+
audioPacketStream << _options.ignorePenumbra;
-
+
audioDataOffset = _currentPacket->pos();
-
+
} else {
// no samples to inject, return immediately
qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning.";
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
}
-
- int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
- _audioData.size() - _currentSendOffset);
-
+
+ int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
+ if (!_options.loop) {
+ // If we aren't looping, let's make sure we don't read past the end
+ totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset);
+ }
+
// Measure the loudness of this frame
_loudness = 0.0f;
- for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
- _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) /
- (AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
+ for (int i = 0; i < totalBytesLeftToCopy; i += sizeof(int16_t)) {
+ _loudness += abs(*reinterpret_cast(_audioData.data() + ((_currentSendOffset + i) % _audioData.size()))) /
+ (AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
}
- _loudness /= (float)(bytesToCopy / sizeof(int16_t));
-
+ _loudness /= (float)(totalBytesLeftToCopy/ sizeof(int16_t));
+
_currentPacket->seek(0);
-
+
// pack the sequence number
_currentPacket->writePrimitive(_outgoingSequenceNumber);
-
+
_currentPacket->seek(positionOptionOffset);
_currentPacket->writePrimitive(_options.position);
_currentPacket->writePrimitive(_options.orientation);
-
+
quint8 volume = MAX_INJECTOR_VOLUME * _options.volume;
_currentPacket->seek(volumeOptionOffset);
_currentPacket->writePrimitive(volume);
-
+
_currentPacket->seek(audioDataOffset);
-
- // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
- _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
-
+
+ while (totalBytesLeftToCopy > 0) {
+ int bytesToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset);
+
+ _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy);
+ _currentSendOffset += bytesToCopy;
+ totalBytesLeftToCopy -= bytesToCopy;
+ if (_currentSendOffset >= _audioData.size()) {
+ _currentSendOffset = 0;
+ }
+ }
+
// set the correct size used for this packet
_currentPacket->setPayloadSize(_currentPacket->pos());
-
+
// grab our audio mixer from the NodeList, if it exists
auto nodeList = DependencyManager::get();
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
-
+
if (audioMixer) {
// send off this audio packet
nodeList->sendUnreliablePacket(*_currentPacket, *audioMixer);
_outgoingSequenceNumber++;
}
-
- _currentSendOffset += bytesToCopy;
-
- if (_currentSendOffset >= _audioData.size()) {
- // we're at the end of the audio data to send
- if (_options.loop) {
- // we were asked to loop, set our send offset to 0
- _currentSendOffset = 0;
- } else {
- // we weren't to loop, say that we're done now
- finish();
- return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
- }
+
+ if (_currentSendOffset >= _audioData.size() && !_options.loop) {
+ finish();
+ return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
-
- if (_currentSendOffset == bytesToCopy) {
+
+ if (!_hasSentFirstFrame) {
+ _hasSentFirstFrame = true;
// ask AudioInjectorManager to call us right away again to
// immediately send the first two frames so the mixer can start using the audio right away
return NEXT_FRAME_DELTA_IMMEDIATELY;
- } else {
- return (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000;
}
-
+
+ const int MAX_ALLOWED_FRAMES_TO_FALL_BEHIND = 7;
+ int64_t currentTime = _frameTimer->nsecsElapsed() / 1000;
+ auto currentFrameBasedOnElapsedTime = currentTime / AudioConstants::NETWORK_FRAME_USECS;
+ if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) {
+ // If we are falling behind by more frames than our threshold, let's skip the frames ahead
+ qDebug() << "AudioInjector::injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames";
+ _nextFrame = currentFrameBasedOnElapsedTime;
+ _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size();
+ }
+
+ int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS;
+ return std::max(INT64_C(0), playNextFrameAt - currentTime);
}
void AudioInjector::stop() {
@@ -315,7 +327,7 @@ void AudioInjector::triggerDeleteAfterFinish() {
QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection);
return;
}
-
+
if (_state == State::Finished) {
stopAndDeleteLater();
} else {
@@ -371,11 +383,11 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol
AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
AudioInjector* sound = playSound(buffer, options, localInterface);
-
+
if (sound) {
sound->_state = AudioInjector::State::NotFinishedWithPendingDelete;
}
-
+
return sound;
}
@@ -383,13 +395,13 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
AudioInjector* injector = new AudioInjector(buffer, options);
injector->setLocalAudioInterface(localInterface);
-
+
// grab the AudioInjectorManager
auto injectorManager = DependencyManager::get();
-
+
// setup parameters required for injection
injector->setupInjection();
-
+
if (options.localOnly) {
if (injector->injectLocally()) {
// local injection succeeded, return the pointer to injector
diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h
index f815b6fe3a..acfbc2b04a 100644
--- a/libraries/audio/src/AudioInjector.h
+++ b/libraries/audio/src/AudioInjector.h
@@ -84,16 +84,17 @@ private slots:
private:
void setupInjection();
- uint64_t injectNextFrame();
+ int64_t injectNextFrame();
bool injectLocally();
QByteArray _audioData;
AudioInjectorOptions _options;
State _state { State::NotFinished };
- bool _hasSetup = false;
- bool _shouldStop = false;
- float _loudness = 0.0f;
- int _currentSendOffset = 0;
+ bool _hasSentFirstFrame { false };
+ bool _hasSetup { false };
+ bool _shouldStop { false };
+ float _loudness { 0.0f };
+ int _currentSendOffset { 0 };
std::unique_ptr _currentPacket { nullptr };
AbstractAudioInterface* _localAudioInterface { nullptr };
AudioInjectorLocalBuffer* _localBuffer { nullptr };
diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp
index f504b31907..b91bddc553 100644
--- a/libraries/audio/src/AudioInjectorManager.cpp
+++ b/libraries/audio/src/AudioInjectorManager.cpp
@@ -88,7 +88,7 @@ void AudioInjectorManager::run() {
// this is an injector that's ready to go, have it send a frame now
auto nextCallDelta = injector->injectNextFrame();
- if (nextCallDelta > 0 && !injector->isFinished()) {
+ if (nextCallDelta >= 0 && !injector->isFinished()) {
// re-enqueue the injector with the correct timing
_injectors.emplace(usecTimestampNow() + nextCallDelta, injector);
}
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 5fd69128eb..3933d705fc 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -25,6 +25,7 @@
#include
#include
+#include
#include
#include
#include
@@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
assert(false);
return glm::vec3();
}
+
+QVariant AttachmentData::toVariant() const {
+ QVariantMap result;
+ result["modelUrl"] = modelURL;
+ result["jointName"] = jointName;
+ result["translation"] = glmToQMap(translation);
+ result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation)));
+ result["scale"] = scale;
+ result["soft"] = isSoft;
+ return result;
+}
+
+glm::vec3 variantToVec3(const QVariant& var) {
+ auto map = var.toMap();
+ glm::vec3 result;
+ result.x = map["x"].toFloat();
+ result.y = map["y"].toFloat();
+ result.z = map["z"].toFloat();
+ return result;
+}
+
+void AttachmentData::fromVariant(const QVariant& variant) {
+ auto map = variant.toMap();
+ if (map.contains("modelUrl")) {
+ auto urlString = map["modelUrl"].toString();
+ modelURL = urlString;
+ }
+ if (map.contains("jointName")) {
+ jointName = map["jointName"].toString();
+ }
+ if (map.contains("translation")) {
+ translation = variantToVec3(map["translation"]);
+ }
+ if (map.contains("rotation")) {
+ rotation = glm::quat(glm::radians(variantToVec3(map["rotation"])));
+ }
+ if (map.contains("scale")) {
+ scale = map["scale"].toFloat();
+ }
+ if (map.contains("soft")) {
+ isSoft = map["soft"].toBool();
+ }
+}
+
+QVariantList AvatarData::getAttachmentsVariant() const {
+ QVariantList result;
+ for (const auto& attachment : getAttachmentData()) {
+ result.append(attachment.toVariant());
+ }
+ return result;
+}
+
+void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
+ QVector newAttachments;
+ newAttachments.reserve(variant.size());
+ for (const auto& attachmentVar : variant) {
+ AttachmentData attachment;
+ attachment.fromVariant(attachmentVar);
+ newAttachments.append(attachment);
+ }
+ setAttachmentData(newAttachments);
+}
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 3b8214f226..eac1917533 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -277,6 +277,9 @@ public:
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
+ Q_INVOKABLE QVariantList getAttachmentsVariant() const;
+ Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
+
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
// key state
@@ -448,6 +451,9 @@ public:
QJsonObject toJson() const;
void fromJson(const QJsonObject& json);
+
+ QVariant toVariant() const;
+ void fromVariant(const QVariant& variant);
};
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index ff288fbe95..2cfef17c1b 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -252,6 +252,8 @@ public:
void setLinePointsDirty() {_linePointsChanged = true; }
+ void setQueryAACubeDirty() { _queryAACubeChanged = true; }
+
void setCreated(QDateTime& v);
bool hasTerseUpdateChanges() const;
diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
index 813386132c..3a72d98402 100644
--- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp
+++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
@@ -338,6 +338,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
_updateTimer.start();
_qmlComponent = new QQmlComponent(_qmlEngine);
+ _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
}
void OffscreenQmlSurface::resize(const QSize& newSize_) {
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 485100da0a..3ea3175390 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
QSharedMemory sharedMem(key);
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
- qWarning() << "Could not attach to shared memory at key" << key;
+ qCWarning(networking) << "Could not attach to shared memory at key" << key;
return false;
} else {
sharedMem.lock();
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 5cffd031d0..84d2282e7a 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -144,7 +144,12 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
return MOTION_TYPE_STATIC;
}
assert(entityTreeIsLocked());
+
if (_entity->getDynamic()) {
+ if (!_entity->getParentID().isNull()) {
+ // if something would have been dynamic but is a child of something else, force it to be kinematic, instead.
+ return MOTION_TYPE_KINEMATIC;
+ }
return MOTION_TYPE_DYNAMIC;
}
return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index 7240fba105..3fd8e022f5 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -146,9 +146,9 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
RenderArgs::RenderMode mode = args->_renderMode;
auto nearClip = viewFrustum->getNearClip();
- const int SHADOW_NEAR_DEPTH = -2;
+ float nearDepth = -args->_boomOffset.z;
const int SHADOW_FAR_DEPTH = 20;
- globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH);
+ globalLight->shadow.setKeylightFrustum(viewFrustum, nearDepth, nearClip + SHADOW_FAR_DEPTH);
// Set the keylight render args
args->_viewFrustum = globalLight->shadow.getFrustum().get();
diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h
index 7012c78c8f..f9fc6eb66b 100644
--- a/libraries/shared/src/RenderArgs.h
+++ b/libraries/shared/src/RenderArgs.h
@@ -105,7 +105,8 @@ public:
std::shared_ptr _pipeline = nullptr;
OctreeRenderer* _renderer = nullptr;
ViewFrustum* _viewFrustum = nullptr;
- glm::ivec4 _viewport{ 0, 0, 1, 1 };
+ glm::ivec4 _viewport{ 0.0f, 0.0f, 1.0f, 1.0f };
+ glm::vec3 _boomOffset{ 0.0f, 0.0f, 1.0f };
float _sizeScale = 1.0f;
int _boundaryLevelAdjust = 0;
RenderMode _renderMode = DEFAULT_RENDER_MODE;
diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp
index 8790c07f62..d7d28bef84 100644
--- a/libraries/ui/src/OffscreenUi.cpp
+++ b/libraries/ui/src/OffscreenUi.cpp
@@ -150,7 +150,9 @@ protected:
}
~ModalDialogListener() {
- disconnect(_dialog);
+ if (_dialog) {
+ disconnect(_dialog);
+ }
}
virtual QVariant waitForResult() {
@@ -164,10 +166,11 @@ protected slots:
void onDestroyed() {
_finished = true;
disconnect(_dialog);
+ _dialog = nullptr;
}
protected:
- QQuickItem* const _dialog;
+ QQuickItem* _dialog;
bool _finished { false };
QVariant _result;
};
@@ -372,6 +375,7 @@ void OffscreenUi::createDesktop(const QUrl& url) {
qDebug() << "Desktop already created";
return;
}
+
#ifdef DEBUG
getRootContext()->setContextProperty("DebugQML", QVariant(true));
#else
@@ -382,9 +386,6 @@ void OffscreenUi::createDesktop(const QUrl& url) {
Q_ASSERT(_desktop);
getRootContext()->setContextProperty("desktop", _desktop);
- // Enable focus debugging
- _desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow()));
-
_toolWindow = _desktop->findChild("ToolWindow");
new VrMenu(this);
diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp
index 103eb7660e..2006b22860 100644
--- a/libraries/ui/src/VrMenu.cpp
+++ b/libraries/ui/src/VrMenu.cpp
@@ -142,7 +142,6 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
Q_ASSERT(menuQml);
QQuickMenuItem* returnedValue { nullptr };
- qDebug() << menuQml;
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
Q_ARG(QString, action->text()));
diff --git a/tests/ui/qml/UI.js b/tests/ui/qml/UI.js
deleted file mode 100644
index 0286ac56a6..0000000000
--- a/tests/ui/qml/UI.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Quick Controls module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
-** of its contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-.pragma library
-
-var margin = 2
-var tabPosition = Qt.TopEdge
-var label = ""
diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml
index c970cd711b..d20b580b5a 100644
--- a/tests/ui/qml/main.qml
+++ b/tests/ui/qml/main.qml
@@ -7,6 +7,7 @@ import "../../../interface/resources/qml"
import "../../../interface/resources/qml/windows"
import "../../../interface/resources/qml/dialogs"
import "../../../interface/resources/qml/hifi"
+import "../../../interface/resources/qml/hifi/dialogs"
ApplicationWindow {
id: appWindow
@@ -196,4 +197,15 @@ ApplicationWindow {
}
*/
}
+
+ Action {
+ text: "Open Browser"
+ shortcut: "Ctrl+Shift+X"
+ onTriggered: {
+ builder.createObject(desktop);
+ }
+ property var builder: Component {
+ ModelBrowserDialog{}
+ }
+ }
}
diff --git a/tests/ui/qmlscratch.pro b/tests/ui/qmlscratch.pro
index b3c8be0f22..7a1b5ab652 100644
--- a/tests/ui/qmlscratch.pro
+++ b/tests/ui/qmlscratch.pro
@@ -7,108 +7,24 @@ CONFIG += c++11
SOURCES += src/main.cpp \
../../libraries/ui/src/FileDialogHelper.cpp
-# Additional import path used to resolve QML modules in Qt Creator's code model
-QML_IMPORT_PATH =
-
-DISTFILES += \
- qml/UI.js \
- qml/main.qml \
- qml/Palettes.qml \
- qml/StubMenu.qml \
- qml/Stubs.qml \
- qml/TestControllers.qml \
- qml/TestDialog.qml \
- qml/TestMenu.qml \
- qml/TestRoot.qml \
- qml/controlDemo/ButtonPage.qml \
- qml/controlDemo/InputPage.qml \
- qml/controlDemo/main.qml \
- qml/controlDemo/ProgressPage.qml \
- ../../interface/resources/qml/controller/hydra/HydraButtons.qml \
- ../../interface/resources/qml/controller/hydra/HydraStick.qml \
- ../../interface/resources/qml/controller/xbox/DPad.qml \
- ../../interface/resources/qml/controller/xbox/LeftAnalogStick.qml \
- ../../interface/resources/qml/controller/xbox/RightAnalogStick.qml \
- ../../interface/resources/qml/controller/xbox/XboxButtons.qml \
- ../../interface/resources/qml/controller/AnalogButton.qml \
- ../../interface/resources/qml/controller/AnalogStick.qml \
- ../../interface/resources/qml/controller/Hydra.qml \
- ../../interface/resources/qml/controller/Standard.qml \
- ../../interface/resources/qml/controller/ToggleButton.qml \
- ../../interface/resources/qml/controller/Xbox.qml \
- ../../interface/resources/qml/controls/Button.qml \
- ../../interface/resources/qml/controls/ButtonAwesome.qml \
- ../../interface/resources/qml/controls/CheckBox.qml \
- ../../interface/resources/qml/controls/FontAwesome.qml \
- ../../interface/resources/qml/controls/Player.qml \
- ../../interface/resources/qml/controls/Slider.qml \
- ../../interface/resources/qml/controls/Spacer.qml \
- ../../interface/resources/qml/controls/SpinBox.qml \
- ../../interface/resources/qml/controls/Text.qml \
- ../../interface/resources/qml/controls/TextAndSlider.qml \
- ../../interface/resources/qml/controls/TextAndSpinBox.qml \
- ../../interface/resources/qml/controls/TextArea.qml \
- ../../interface/resources/qml/controls/TextEdit.qml \
- ../../interface/resources/qml/controls/TextHeader.qml \
- ../../interface/resources/qml/controls/TextInput.qml \
- ../../interface/resources/qml/controls/TextInputAndButton.qml \
- ../../interface/resources/qml/controls/WebView.qml \
- ../../interface/resources/qml/dialogs/FileDialog.qml \
- ../../interface/resources/qml/dialogs/MessageDialog.qml \
- ../../interface/resources/qml/dialogs/RunningScripts.qml \
- ../../interface/resources/qml/hifi/MenuOption.qml \
- ../../interface/resources/qml/styles/Border.qml \
- ../../interface/resources/qml/styles/HifiConstants.qml \
- ../../interface/resources/qml/windows/DefaultFrame.qml \
- ../../interface/resources/qml/windows/Fadable.qml \
- ../../interface/resources/qml/windows/Frame.qml \
- ../../interface/resources/qml/windows/ModalFrame.qml \
- ../../interface/resources/qml/windows/Window.qml \
- ../../interface/resources/qml/AddressBarDialog.qml \
- ../../interface/resources/qml/AvatarInputs.qml \
- ../../interface/resources/qml/Browser.qml \
- ../../interface/resources/qml/InfoView.qml \
- ../../interface/resources/qml/LoginDialog.qml \
- ../../interface/resources/qml/QmlWebWindow.qml \
- ../../interface/resources/qml/QmlWindow.qml \
- ../../interface/resources/qml/Root.qml \
- ../../interface/resources/qml/ScrollingGraph.qml \
- ../../interface/resources/qml/Stats.qml \
- ../../interface/resources/qml/TextOverlayElement.qml \
- ../../interface/resources/qml/Tooltip.qml \
- ../../interface/resources/qml/ToolWindow.qml \
- ../../interface/resources/qml/UpdateDialog.qml \
- ../../interface/resources/qml/VrMenu.qml \
- ../../interface/resources/qml/VrMenuItem.qml \
- ../../interface/resources/qml/VrMenuView.qml \
- ../../interface/resources/qml/WebEntity.qml \
- ../../interface/resources/qml/desktop/Desktop.qml \
- ../../interface/resources/qml/hifi/Desktop.qml \
- ../../interface/resources/qml/menus/MenuMouseHandler.qml \
- ../../interface/resources/qml/menus/VrMenuItem.qml \
- ../../interface/resources/qml/menus/VrMenuView.qml \
- ../../interface/resources/qml/windows/ModalWindow.qml \
- ../../interface/resources/qml/desktop/FocusHack.qml \
- ../../interface/resources/qml/hifi/dialogs/PreferencesDialog.qml \
- ../../interface/resources/qml/hifi/dialogs/Section.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Browsable.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Section.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Editable.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Slider.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Preference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/CheckBox.qml \
- ../../interface/resources/qml/dialogs/fileDialog/FileTableView.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Avatar.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/AvatarBrowser.qml \
- ../../interface/resources/qml/dialogs/QueryDialog.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/Button.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/AvatarPreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/BrowsablePreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/ButtonPreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/CheckBoxPreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/EditablePreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/SliderPreference.qml \
- ../../interface/resources/qml/hifi/dialogs/preferences/SpinBoxPreference.qml
-
HEADERS += \
../../libraries/ui/src/FileDialogHelper.h
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH = ../../interface/resources/qml
+
+
+DISTFILES += \
+ qml/*.qml \
+ ../../interface/resources/qml/*.qml \
+ ../../interface/resources/qml/controls/*.qml \
+ ../../interface/resources/qml/dialogs/*.qml \
+ ../../interface/resources/qml/dialogs/fileDialog/*.qml \
+ ../../interface/resources/qml/desktop/*.qml \
+ ../../interface/resources/qml/menus/*.qml \
+ ../../interface/resources/qml/styles/*.qml \
+ ../../interface/resources/qml/windows/*.qml \
+ ../../interface/resources/qml/hifi/*.qml \
+ ../../interface/resources/qml/hifi/dialogs/*.qml \
+ ../../interface/resources/qml/hifi/dialogs/preferences/*.qml
+