From 83827b6d241290c1f7c2b658480ecd61e1c8a3a7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 14:57:32 -0800 Subject: [PATCH 01/32] add startup shortcut in current users context --- cmake/templates/NSIS.template.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0b37cd5853..b398c2af7d 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -993,6 +993,12 @@ 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@" !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES From 6934594bb96bb95c7c9ac413f70ba801a4914a70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:20:51 -0800 Subject: [PATCH 02/32] always use current context, ignore CPack InstallOptions --- cmake/templates/NSIS.template.in | 121 +------------------------------ 1 file changed, 2 insertions(+), 119 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index b398c2af7d..24f1e0000f 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 @@ -678,7 +672,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@" ;-------------------------------- @@ -768,7 +761,6 @@ 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 @@ -799,27 +791,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 @@ -835,14 +810,6 @@ Section "-Add to path" 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" @@ -1012,8 +979,6 @@ Function HandlePostInstallOptions ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState ${If} $CopyFromProductionState == ${BST_CHECKED} - SetShellVarContext current - StrCpy $0 "$APPDATA\@BUILD_ORGANIZATION@" ; we need to copy whatever is in the data folder for production build to the data folder for this build @@ -1115,26 +1080,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: --- @@ -1195,16 +1140,6 @@ Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "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@ @@ -1294,16 +1229,6 @@ Section "Uninstall" 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... - Function .onInit !ifdef INNER @@ -1348,48 +1273,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 user for context of data/startup folders + SetShellVarContext current FunctionEnd From f393c5eeda0f098aa04f13ce473a11bcd35c9fc8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:25:35 -0800 Subject: [PATCH 03/32] remove CMake templated code for path changes --- cmake/templates/NSIS.template.in | 279 ------------------------------- 1 file changed, 279 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 24f1e0000f..0bd1dfd214 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -199,277 +199,10 @@ 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 @@ -802,14 +535,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 - ; Make sure nsDialogs is included before we use it !include "nsdialogs.nsh" @@ -1222,10 +947,6 @@ Section "Uninstall" DeleteRegKey /ifempty SHCTX "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 From aa2d19a61148a618c0aa2754351b3016d4f08f1f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:32:23 -0800 Subject: [PATCH 04/32] remove two unused utility functions --- cmake/templates/NSIS.template.in | 64 -------------------------------- 1 file changed, 64 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0bd1dfd214..09aa92ed47 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -203,70 +203,6 @@ Var AR_RegFlags # Utility Functions # ########################################### -; 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 From 150a8d388ca879ea71aaf532bec43037bf020ce7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:37:11 -0800 Subject: [PATCH 05/32] allow user to close running exec and continue --- cmake/templates/NSIS.template.in | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 09aa92ed47..801b773844 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -682,14 +682,25 @@ 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 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 From aeb097664ee44781e4e9fe24c0c919c5b01825ed Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:38:57 -0800 Subject: [PATCH 06/32] remove InstallOptionsPage from list of pages --- cmake/templates/NSIS.template.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 801b773844..a620347e0d 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -261,7 +261,6 @@ 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 From eabafa800cebf913e063deefa954413014361fde Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:51:47 -0800 Subject: [PATCH 07/32] clarify application close behaviour --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index a620347e0d..9c29e91e81 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -691,7 +691,7 @@ FunctionEnd ; 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 to continue." \ + "${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 From 9d4b5292236d16ac8a83c0fd26309a55ea46070b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:57:42 -0800 Subject: [PATCH 08/32] remove any old admin desktop/sm shortcuts --- cmake/templates/NSIS.template.in | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 9c29e91e81..0505e44c43 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -432,12 +432,21 @@ Section "-Core installation" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts + + ; in case the user has any shortcuts from prior to when we were using the user context, remove those now + SetShellVarContext all + RMDir /r "$SMPROGRAMS\$STARTMENU_FOLDER" + + ; go back to current user context before setting up the startmenu folder + SetShellVarContext current + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -592,6 +601,15 @@ Var LaunchNowState Var CopyFromProductionState Function HandlePostInstallOptions + ; If the user created shortcuts from a time prior to when we using current user context, pull them now + SetShellVarContext all + + Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" + Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" + + ; return the context to the current user so the shortcuts are correct + SetShellVarContext current + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState From c7228ce02073a3680e9aebc65ecb5d353c540f45 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:59:17 -0800 Subject: [PATCH 09/32] ensure current user context for uninstaller --- cmake/templates/NSIS.template.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0505e44c43..f8d7dfe75f 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -826,6 +826,9 @@ FunctionEnd !ifdef INNER Section "Uninstall" + ; use user for context of data/startup folders + SetShellVarContext current + ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" From 57f9d82efabca1ff53a3409908539acb22647d8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 16:13:39 -0800 Subject: [PATCH 10/32] remove shortcuts from custom start menu location --- cmake/templates/NSIS.template.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index f8d7dfe75f..fa424ea632 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -897,6 +897,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 From f4ece26271742587f0c4ea264c36dc46e3097347 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 16:16:29 -0800 Subject: [PATCH 11/32] use install dir written to registry as potential default --- cmake/templates/NSIS.template.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index fa424ea632..2303ddee35 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -378,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 @@ -919,6 +919,8 @@ Section "Uninstall" SectionEnd !endif +InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" + Function .onInit !ifdef INNER From e4110ea401976b1749ffa015dd11a62b9caa7adf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 17:21:54 -0800 Subject: [PATCH 12/32] install for all users, EXCEPT the startup menu item --- cmake/templates/NSIS.template.in | 40 +++++++++++--------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 2303ddee35..b698d6072a 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -207,7 +207,7 @@ 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'" @@ -264,7 +264,7 @@ FunctionEnd !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 @@ -433,20 +433,12 @@ Section "-Core installation" ;Create shortcuts - ; in case the user has any shortcuts from prior to when we were using the user context, remove those now - SetShellVarContext all - RMDir /r "$SMPROGRAMS\$STARTMENU_FOLDER" - - ; go back to current user context before setting up the startmenu folder - SetShellVarContext current - CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} - CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -601,15 +593,6 @@ Var LaunchNowState Var CopyFromProductionState Function HandlePostInstallOptions - ; If the user created shortcuts from a time prior to when we using current user context, pull them now - SetShellVarContext all - - Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" - Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" - - ; return the context to the current user so the shortcuts are correct - SetShellVarContext current - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState @@ -646,6 +629,9 @@ Function HandlePostInstallOptions 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 @@ -826,10 +812,10 @@ FunctionEnd !ifdef INNER Section "Uninstall" - ; use user for context of data/startup folders - SetShellVarContext current + ; use all users context for data/startup folders + SetShellVarContext all - ReadRegStr $START_MENU SHCTX \ + ReadRegStr $START_MENU HKLM \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" @@ -850,13 +836,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" @@ -914,7 +900,7 @@ 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@" SectionEnd !endif @@ -965,6 +951,6 @@ inst: ; Reads components status for registry !insertmacro SectionList "InitSection" - ; use user for context of data/startup folders - SetShellVarContext current + ; use all users for context of data/startup folders + SetShellVarContext all FunctionEnd From 2728290ffbeb6e95ed9db0343a80a42a69d755f8 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Tue, 26 Jan 2016 09:37:33 -0800 Subject: [PATCH 13/32] fix bug in IK and tune head/neck constraints --- .../animation/src/AnimInverseKinematics.cpp | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) 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); From 79a2b7f2135fed3cb1622a784a70e3c1b3495a12 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:17:54 -0800 Subject: [PATCH 14/32] make sure shell var context is correct for AppData --- cmake/templates/NSIS.template.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index b698d6072a..42743196b5 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -639,10 +639,13 @@ 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 ${If} $CopyFromProductionState == ${BST_CHECKED} + SetShellVarContext current + StrCpy $0 "$APPDATA\@BUILD_ORGANIZATION@" ; we need to copy whatever is in the data folder for production build to the data folder for this build @@ -660,6 +663,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} From 068de5203c3aa8797dc7dba1af006295e51bdb9a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:40:50 -0800 Subject: [PATCH 15/32] use specific shortcut names for PR builds --- cmake/macros/SetPackagingParameters.cmake | 12 ++++++++---- cmake/templates/CPackProperties.cmake.in | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index b0f093977a..de5a87e4d6 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 (NOT PR_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@") From 4115d52d916251311597fc683a8851aa62fc07ec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:46:37 -0800 Subject: [PATCH 16/32] default shortcut options off for PR builds --- cmake/templates/NSIS.template.in | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 42743196b5..ecf703f121 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -536,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@} @@ -569,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 From 6daeefef6882a781fc2d4fba1a22bba8666528e0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 12:08:26 -0800 Subject: [PATCH 17/32] remove the correct startup shortcut during uninstall --- cmake/templates/NSIS.template.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index ecf703f121..070c12192c 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -868,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@ From 0a413904a098bab3c7a4dc7b88fc5a6e0bb2e7e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 12:09:45 -0800 Subject: [PATCH 18/32] use non PR-specific names for close prompt --- cmake/templates/NSIS.template.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 070c12192c..b5699cb3b3 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -721,8 +721,8 @@ FunctionEnd !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 From 2da2bc60db47584f1e07d2a705f87ffdab49b9a2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Jan 2016 12:35:32 -0800 Subject: [PATCH 19/32] Tie shadow frustum to boom offset --- interface/src/Application.cpp | 13 +++++++------ libraries/render-utils/src/RenderShadowTask.cpp | 4 ++-- libraries/shared/src/RenderArgs.h | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a295cd8a6e..4629a8c08c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1380,10 +1380,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 +1413,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 +1488,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()) { 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; From b6f478e08759497969a5a9743166407a54b7f4f0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 13:10:02 -0800 Subject: [PATCH 20/32] add some debug for audio injector wrap case --- libraries/audio/src/AudioInjector.cpp | 115 ++++++++++++++------------ 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1f15007339..d22e0ff2f6 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,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(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(&_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); - + // 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(); 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(); - + // setup parameters required for injection injector->setupInjection(); - + if (options.localOnly) { if (injector->injectLocally()) { // local injection succeeded, return the pointer to injector From 8ba0e3bc6ee40975832ce3012b3221db3d682d2f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 16:21:18 -0800 Subject: [PATCH 21/32] give dev shortcuts a specific name too --- cmake/macros/SetPackagingParameters.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index de5a87e4d6..92e3273f67 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -83,7 +83,7 @@ macro(SET_PACKAGING_PARAMETERS) set(AC_EXEC_NAME "assignment-client.exe") # shortcut names - if (NOT PR_BUILD) + if (PRODUCTION_BUILD) set(INTERFACE_SHORTCUT_NAME "High Fidelity") set(CONSOLE_SHORTCUT_NAME "Server Console") else () From 8dc066fc00332ac2e8352768f624bd6226a811b0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:48:25 -0800 Subject: [PATCH 22/32] Fix the recursive launching of load script from URL --- interface/resources/qml/desktop/Desktop.qml | 2 +- .../resources/qml/dialogs/RunningScripts.qml | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 96c84b49c0..f246f79be9 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -167,7 +167,7 @@ FocusScope { } if (setFocus) { - focus = true; + targetWindow.focus = true; } reposition(targetWindow); 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" From cea57a111bc6a5923f5250de164286fd2de96489 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:56:33 -0800 Subject: [PATCH 23/32] Desktop cleanup --- interface/resources/qml/desktop/Desktop.qml | 72 ++++++++++----------- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 + 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index f246f79be9..14687f387e 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,31 @@ 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); +// 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) { @@ -190,24 +214,16 @@ FocusScope { var newPosition; if (targetWindow.x === -1 && targetWindow.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); + newPosition = Qt.vector2d(desktop.width / 2 - windowRect.width / 2, + desktop.height / 2 - windowRect.height / 2); } + + newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), 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 +268,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/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_) { From 5f38c5265c5dfb226b270884bc936fc9b8309ecc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:58:32 -0800 Subject: [PATCH 24/32] Fixing potential crash in dialog popups --- libraries/ui/src/OffscreenUi.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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); From 940b0950139ecfe8cb34d0aa0c0fc57e86d582e7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:59:37 -0800 Subject: [PATCH 25/32] Removing menu logspam --- libraries/ui/src/VrMenu.cpp | 1 - 1 file changed, 1 deletion(-) 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())); From fee920723a04e675e91dca4a098f56b78f41da93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 17:25:20 -0800 Subject: [PATCH 26/32] Cleaning up test project --- tests/ui/qml/UI.js | 45 --------------- tests/ui/qmlscratch.pro | 122 +++++++--------------------------------- 2 files changed, 19 insertions(+), 148 deletions(-) delete mode 100644 tests/ui/qml/UI.js 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/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 + From 9a8d56da77ea79a232f16017cfa75d4301c03068 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 17:25:36 -0800 Subject: [PATCH 27/32] Center new windows without a defined position --- interface/resources/qml/desktop/Desktop.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 14687f387e..e87862aa56 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -211,15 +211,16 @@ 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); + 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(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition); + newPosition = Utils.clampVector(newPosition, minPosition, maxPosition); targetWindow.x = newPosition.x; targetWindow.y = newPosition.y; } From fbee221966a7dd2dc7f21dd558cc161a33d8ad59 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 18:15:41 -0800 Subject: [PATCH 28/32] Fix tool window breaking when visible while edit.js reloads --- interface/resources/qml/ToolWindow.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 } From 0a6be80578a1abd00ec23eac423443baab2061e1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 18:50:55 -0800 Subject: [PATCH 29/32] Added some disabled modal focus protection code --- interface/resources/qml/desktop/Desktop.qml | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index e87862aa56..8c4f73c200 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -154,6 +154,28 @@ FocusScope { 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); From 664100b9b14e13eeea2ff2aae71be21c95dbd571 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 23 Jan 2016 17:14:30 -0800 Subject: [PATCH 30/32] Attachment dialog --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 123 +++++++++++++ .../hifi/dialogs/AvatarAttachmentsDialog.qml | 128 ++++++++++++++ .../qml/hifi/dialogs/ModelBrowserDialog.qml | 128 ++++++++++++++ .../hifi/dialogs/attachments/Attachment.qml | 164 ++++++++++++++++++ .../qml/hifi/dialogs/attachments/Rotation.qml | 9 + .../hifi/dialogs/attachments/Translation.qml | 9 + .../qml/hifi/dialogs/attachments/Vector3.qml | 71 ++++++++ .../resources/qml/hifi/models/S3Model.qml | 41 +++++ interface/resources/qml/js/Utils.js | 14 ++ interface/src/Application.cpp | 5 + libraries/avatars/src/AvatarData.cpp | 63 +++++++ libraries/avatars/src/AvatarData.h | 6 + libraries/networking/src/LimitedNodeList.cpp | 2 +- tests/ui/qml/main.qml | 12 ++ 14 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Attachment.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Rotation.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Translation.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Vector3.qml create mode 100644 interface/resources/qml/hifi/models/S3Model.qml diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml new file mode 100644 index 0000000000..77771f65c4 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml @@ -0,0 +1,123 @@ +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" + 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..b45e10d755 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -0,0 +1,164 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import "../../../windows" +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; + } + 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 4629a8c08c..b3142702bb 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()); @@ -1842,6 +1843,10 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_X: if (isShifted && isMeta) { + auto offscreenUi = DependencyManager::get(); + offscreenUi->getRootContext()->engine()->clearComponentCache(); + offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml"); + // OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } break; 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/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/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{} + } + } } From 2956557c5f50f8e5dd9b81ee4ed42629a54b8892 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 14:41:22 -0800 Subject: [PATCH 31/32] Adding overlay compatible combo-box --- interface/resources/qml/controls/ComboBox.qml | 148 ++++++++++++++++++ .../hifi/dialogs/attachments/Attachment.qml | 3 +- 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/controls/ComboBox.qml 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/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index b45e10d755..31a1895e58 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -2,6 +2,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import "../../../windows" +import "../../../controls" as VrControls import "." import ".." @@ -69,7 +70,7 @@ Item { text: "Joint:"; anchors.verticalCenter: jointChooser.verticalCenter; } - ComboBox { + VrControls.ComboBox { id: jointChooser; anchors { left: jointLabel.right; leftMargin: 8; right: parent.right } model: MyAvatar.jointNames From f53aaa1d64b3c92a39bfc24e885270232287f166 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 15:54:03 -0800 Subject: [PATCH 32/32] Removing old attachment dialog, updating menu code --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 1 + interface/src/Application.cpp | 3 +- interface/src/Menu.cpp | 13 +- interface/src/ui/AttachmentsDialog.cpp | 239 ------------------ interface/src/ui/AttachmentsDialog.h | 84 ------ interface/src/ui/DialogsManager.cpp | 10 - interface/src/ui/DialogsManager.h | 1 - 7 files changed, 10 insertions(+), 341 deletions(-) delete mode 100644 interface/src/ui/AttachmentsDialog.cpp delete mode 100644 interface/src/ui/AttachmentsDialog.h diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml index 77771f65c4..1c70f06efd 100644 --- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml @@ -8,6 +8,7 @@ import "attachments" Window { id: root title: "Edit Attachments" + objectName: "AttachmentsDialog" width: 600 height: 600 resizable: true diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b3142702bb..27f9d81ab2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1845,8 +1845,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted && isMeta) { auto offscreenUi = DependencyManager::get(); offscreenUi->getRootContext()->engine()->clearComponentCache(); - offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml"); - // OffscreenUi::information("Debugging", "Component cache cleared"); + 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..14c91dbda8 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"); }); 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();