Merge branch 'master' of https://github.com/highfidelity/hifi into red

This commit is contained in:
samcake 2016-01-27 10:03:34 -08:00
commit 6d2d7c565a
35 changed files with 1223 additions and 1108 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
import "."
Vector3 {
decimals: 1;
stepSize: 1;
maximumValue: 180
minimumValue: -180
}

View file

@ -0,0 +1,9 @@
import "."
Vector3 {
decimals: 2;
stepSize: 0.01;
maximumValue: 10
minimumValue: -10
}

View file

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

View file

@ -0,0 +1,41 @@
import QtQuick 2.0
import QtQuick.XmlListModel 2.0
//http://s3.amazonaws.com/hifi-public?prefix=models/attachments
/*
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Contents>
<Key>models/attachments/guitar.fst</Key>
<LastModified>2015-11-10T00:28:22.000Z</LastModified>
<ETag>&quot;236c00c4802ba9c2605cabd5601d138e&quot;</ETag>
<Size>2992</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
*/
// 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()" }
}

View file

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

View file

@ -1186,6 +1186,7 @@ void Application::initializeUi() {
UpdateDialog::registerType();
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
auto offscreenUi = DependencyManager::get<OffscreenUi>();
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>();
offscreenUi->getRootContext()->engine()->clearComponentCache();
OffscreenUi::information("Debugging", "Component cache cleared");
// placeholder for dialogs being converted to QML.
}
break;

View file

@ -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<OffscreenUi>()->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<OffscreenUi>()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog");
});

View file

@ -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 <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QCheckBox>
#include <avatar/AvatarManager.h>
#include <avatar/MyAvatar.h>
#include <DependencyManager.h>
#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<AvatarManager>()->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<AttachmentData> data;
for (int i = 0; i < _attachments->count() - 1; i++) {
data.append(static_cast<AttachmentPanel*>(_attachments->itemAt(i)->widget())->getAttachmentData());
}
DependencyManager::get<AvatarManager>()->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<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->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<AvatarManager>()->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<AvatarManager>()->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<AvatarManager>()->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();
}

View file

@ -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 <QDialog>
#include <QFrame>
#include <AvatarData.h>
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

View file

@ -19,7 +19,6 @@
#include <PathUtils.h>
#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());

View file

@ -48,7 +48,6 @@ public slots:
void showLoginDialog();
void octreeStatsDetails();
void cachesSizeDialog();
void editAttachments();
void audioStatsDetails();
void bandwidthDetails();
void lodTools();

View file

@ -153,7 +153,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
continue;
}
// cache tip absolute transform
int tipIndex = target.getIndex();
int pivotIndex = _skeleton->getParentIndex(tipIndex);
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
@ -165,12 +164,30 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
continue;
}
glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
glm::quat tipRotation = absolutePoses[tipIndex].rot;
// cache tip's absolute orientation
glm::quat tipOrientation = absolutePoses[tipIndex].rot;
// cache tip's parent's absolute rotation so we can recompute the tip's parent-relative
// as we proceed walking down the joint chain
glm::quat tipParentRotation = absolutePoses[pivotIndex].rot;
// also cache tip's parent's absolute orientation so we can recompute
// the tip's parent-relative as we proceed up the chain
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot;
if (targetType == IKTarget::Type::HmdHead) {
// rotate tip directly to target orientation
tipOrientation = target.getRotation();
// enforce tip's constraint
RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) {
glm::quat tipRelativeRotation = glm::normalize(tipOrientation * glm::inverse(tipParentOrientation));
bool constrained = constraint->apply(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::vector<I
deltaRotation = glm::angleAxis(angle, axis);
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
// new rotation and its target. This is the final parent-relative rotation that the tip joint have
// make to achieve its target rotation.
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation();
// new orientation and its target. This is the final parent-relative orientation that the tip joint have
// make to achieve its target orientation.
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentOrientation) * target.getRotation();
// enforce tip's constraint
RotationConstraint* constraint = getConstraint(tipIndex);
@ -212,7 +229,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
if (constrained) {
// The tip's final parent-relative rotation would violate its constraint
// so we try to pre-twist this pivot to compensate.
glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation;
glm::quat constrainedTipRotation = deltaRotation * tipParentOrientation * tipRelativeRotation;
glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation);
glm::quat swingPart;
glm::quat twistPart;
@ -227,9 +244,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
} else if (targetType == IKTarget::Type::HmdHead) {
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
// deltas up the hierarchy. Its target position is enforced later by shifting the hips.
deltaRotation = target.getRotation() * glm::inverse(tipRotation);
deltaRotation = target.getRotation() * glm::inverse(tipOrientation);
float dotSign = copysignf(1.0f, deltaRotation.w);
const float ANGLE_DISTRIBUTION_FACTOR = 0.15f;
const float ANGLE_DISTRIBUTION_FACTOR = 0.35f;
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR));
}
@ -245,12 +262,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
if (constraint) {
bool constrained = constraint->apply(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::vector<I
// keep track of tip's new transform as we descend towards root
tipPosition = jointPosition + deltaRotation * leverArm;
tipRotation = glm::normalize(deltaRotation * tipRotation);
tipParentRotation = glm::normalize(deltaRotation * tipParentRotation);
tipOrientation = glm::normalize(deltaRotation * tipOrientation);
tipParentOrientation = glm::normalize(deltaRotation * tipParentOrientation);
pivotIndex = pivotsParentIndex;
pivotsParentIndex = _skeleton->getParentIndex(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<float> 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<float> 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);

View file

@ -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<AudioInjectorManager>();
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,7 +153,7 @@ bool AudioInjector::injectLocally() {
// we never started so we are finished, call our stop method
stop();
}
return success;
}
@ -162,85 +162,85 @@ static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0;
static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1;
uint64_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<QElapsedTimer>(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;
audioPacketStream << loopbackFlag;
// pack the position for injected audio
positionOptionOffset = _currentPacket->pos();
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.position),
sizeof(_options.position));
// pack our orientation for injected audio
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_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);
// Measure the loudness of this frame
_loudness = 0.0f;
for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
@ -248,40 +248,40 @@ uint64_t AudioInjector::injectNextFrame() {
(AudioConstants::MAX_SAMPLE_VALUE / 2.0f);
}
_loudness /= (float)(bytesToCopy / 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);
// 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<NodeList>();
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) {
@ -293,15 +293,20 @@ uint64_t AudioInjector::injectNextFrame() {
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
}
if (_currentSendOffset == bytesToCopy) {
// 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;
uint64_t untilNextFrame = (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000;
if (untilNextFrame >= 10 * AudioConstants::NETWORK_FRAME_USECS) {
qDebug() << "AudioInjector returning delta to next frame that is very large -" << untilNextFrame;
qDebug() << "Next Frame is " << _nextFrame << "frameTimer nsecsElapsed is" << _frameTimer->nsecsElapsed();
}
return untilNextFrame;
}
}
void AudioInjector::stop() {
@ -315,7 +320,7 @@ void AudioInjector::triggerDeleteAfterFinish() {
QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection);
return;
}
if (_state == State::Finished) {
stopAndDeleteLater();
} else {
@ -371,11 +376,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 +388,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<AudioInjectorManager>();
// setup parameters required for injection
injector->setupInjection();
if (options.localOnly) {
if (injector->injectLocally()) {
// local injection succeeded, return the pointer to injector

View file

@ -25,6 +25,7 @@
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QVariantGLM.h>
#include <Transform.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
@ -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<AttachmentData> newAttachments;
newAttachments.reserve(variant.size());
for (const auto& attachmentVar : variant) {
AttachmentData attachment;
attachment.fromVariant(attachmentVar);
newAttachments.append(attachment);
}
setAttachmentData(newAttachments);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -105,7 +105,8 @@ public:
std::shared_ptr<render::ShapePipeline> _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;

View file

@ -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<QQuickItem*>("ToolWindow");
new VrMenu(this);

View file

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

View file

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

View file

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

View file

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