TimRude Posted November 28, 2022 Share Posted November 28, 2022 (edited) Jump to the last post in this thread for the finished code. ============================================================================ Using the various Control... functions (such as Enable/Disable/SetText/Hide/Show), I can modify the Edit control on a Folder Browse dialog box generated using the _WinAPI_BrowseForFolderDlg function (which is actually using the windows SHBrowseForFolder function from shell32.dll). Is there a way to make this Edit control be ReadOnly? I could simply disable it, but then the user can't scroll left/right to see the entirety of a long string in the control. I want to keep the user from typing into the control, but still be able to scroll the contents left/right. I'm using the older style of this dialog (because I like it) and the MSDN help notes that the function returns the hWnd to the dialog specifically to allow you "to modify the layout or contents of the dialog box. Because it is not resizable, modifying the older style dialog box is relatively straightforward." So that's what I'm trying to do - modify the older style dialog box a little. For example, I disable the OK button whenever a non-folder selection is highlighted in the Tree. And yes, I've tried AutoIt's built-in FileSelectFolder function but I like the old dialog better for this application. Much less busy and single-purposed without a ton of bells and whistles that complicate it. Here's my code (based largely on the example script in the help file for _WinAPI_BrowseForFolderDlg). Currently the test routine is uncommented so it's run-able. expandcollapse popup#include-once #include <WinAPIDlg.au3> #include <WinAPISysWin.au3> ; These includes were in the help sample but appear to be already included via other includes - no harm in leaving them #include <APIDlgConstants.au3> #include <MsgBoxConstants.au3> #include <SendMessage.au3> #include <WinAPIMem.au3> #include <WinAPIMisc.au3> #include <WinAPIShellEx.au3> #include <WinAPIShPath.au3> ;#cs ; Test routine Opt('MustDeclareVars' = 1) ConsoleWrite('Returned Path = ' & _BrowseForFolder(@ScriptDir, 'Custom Title', 'Custom Prompt') & @TAB & '@extended = ' & @extended & @CRLF) Exit ;#ce Func _BrowseForFolder($sInitDir, $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $s_BFF_Dialog_Title = $sTitle Local $hBrowseProc = DllCallbackRegister('_BrowseProc', 'int', 'hwnd;uint;lparam;ptr') Local $pBrowseProc = DllCallbackGetPtr($hBrowseProc) Local $pText = _WinAPI_CreateString($sInitDir) Local $sPath = _WinAPI_BrowseForFolderDlg('', $sPrompt, BitOR($BIF_RETURNONLYFSDIRS, $BIF_EDITBOX, $BIF_VALIDATE), $pBrowseProc, $pText, $hParent_hWnd) _WinAPI_FreeMemory($pText) DllCallbackFree($hBrowseProc) If $sPath = '' Then Return SetExtended(-1, '') Else Return $sPath EndIf EndFunc ;==>_BrowseForFolder Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) ControlDisable($hWnd, '', 'Edit1') ; <-- WOULD PREFER READONLY INSTEAD OF DISABLED Case $BFFM_SELCHANGED $sPath = _WinAPI_ShellGetPathFromIDList($wParam) If @error Or ($sPath = '') Then ControlDisable($hWnd, '', 'Button1') ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update Edit control to show full path ControlEnable($hWnd, '', 'Button1') ; enable OK button EndIf Case $BFFM_VALIDATEFAILED MsgBox(($MB_ICONERROR + $MB_SYSTEMMODAL), 'Error', _WinAPI_GetString($wParam) & ' is invalid.', 0, $hWnd) Return 1 EndSwitch Return 0 EndFunc ;==>_BrowseProc Here's a screenshot of it showing an example of selecting a long folder path where you can't see the whole thing in the Edit control: Edited December 18, 2022 by TimRude Changed title, and linked to finished product Link to comment Share on other sites More sharing options...
Solution pixelsearch Posted November 28, 2022 Solution Share Posted November 28, 2022 Hi TimRude . Just replace this line... ControlDisable($hWnd, '', 'Edit1') ; <-- WOULD PREFER READONLY INSTEAD OF DISABLED ...with that line _SendMessage(ControlGetHandle($hWnd, "", "Edit1"), 0x00CF, 1, 0) ; $EM_SETREADONLY = 0x00CF Zedna and TimRude 1 1 Link to comment Share on other sites More sharing options...
TimRude Posted November 29, 2022 Author Share Posted November 29, 2022 I thought there had to be an easy way to do it! Thanks @pixelsearch! Link to comment Share on other sites More sharing options...
pixelsearch Posted December 1, 2022 Share Posted December 1, 2022 (edited) @TimRude I wondered if there was a way to display a tooltip while hovering over the Edit control. This tooltip should constantly be updated and display the complete text of the Edit Control (e.g. 1 line) without having the user scroll right or left inside the Edit control. The problem is that the dialog box displayed by _WinAPI_BrowseForFolderDlg() isn't created by the user, nor the treeview + edit control + 2 buttons + label found in this dialog box, so I didn't know exactly how to create the tooltip in this script where there is no While... WEnd loop. Then I thought of Kafu's interesting script and the use of _WinAPI_SetTimer() helped me to achieve this. As there is no GUI created by the user in your script (TimRude) then I choosed the callback function associated to _WinAPI_SetTimer() rather than the impossible WM_TIMER registration when there is no GUI created by the user (if I'm not mistaken) expandcollapse popup#include <WinAPIDlg.au3> #include <WinAPISysWin.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> Opt('MustDeclareVars', 1) Global $g_hEdit = 0, $g_aSize[2] ; width & height of edit control (client area) Local $hTimerProc = DllCallbackRegister('_TimerProc', 'none', 'hwnd;uint;uint_ptr;dword') Local $iTimerID = _WinAPI_SetTimer(0, 0, 200, DllCallbackGetPtr($hTimerProc)) ; 200ms ConsoleWrite('Returned Path = ' & _BrowseForFolder(@ScriptDir, 'Custom Title', 'Custom Prompt') & @TAB & '@extended = ' & @extended & @CRLF) _WinAPI_KillTimer(0, $iTimerID) DllCallbackFree($hTimerProc) ;=================================================================================================================================== Func _BrowseForFolder($sInitDir, $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $s_BFF_Dialog_Title = $sTitle Local $hBrowseProc = DllCallbackRegister('_BrowseProc', 'int', 'hwnd;uint;lparam;ptr') Local $pBrowseProc = DllCallbackGetPtr($hBrowseProc) Local $pText = _WinAPI_CreateString($sInitDir) Local $sPath = _WinAPI_BrowseForFolderDlg('', $sPrompt, BitOR($BIF_RETURNONLYFSDIRS, $BIF_EDITBOX, $BIF_VALIDATE), $pBrowseProc, $pText, $hParent_hWnd) _WinAPI_FreeMemory($pText) DllCallbackFree($hBrowseProc) If $sPath = '' Then Return SetExtended(-1, '') Else Return $sPath EndIf EndFunc ;==>_BrowseForFolder ;=============================================== Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) $g_hEdit = ControlGetHandle($hWnd, "", "Edit1") _SendMessage($g_hEdit, 0x00CF, 1, 0) ; $EM_SETREADONLY = 0x00CF $g_aSize = WinGetClientSize($g_hEdit) Case $BFFM_SELCHANGED $sPath = _WinAPI_ShellGetPathFromIDList($wParam) If @error Or ($sPath = '') Then ControlDisable($hWnd, '', 'Button1') ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update Edit control to show full path ControlEnable($hWnd, '', 'Button1') ; enable OK button EndIf Case $BFFM_VALIDATEFAILED MsgBox(($MB_ICONERROR + $MB_SYSTEMMODAL), 'Error', _WinAPI_GetString($wParam) & ' is invalid.', 0, $hWnd) Return 1 EndSwitch Return 0 EndFunc ;==>_BrowseProc ;=============================================== Func _TimerProc($hWnd, $iMsg, $iTimerID, $iTime) #forceref $hWnd, $iMsg, $iTimerID, $iTime Local Static $tPoint = DllStructCreate("int X;int Y"), $bToolTip = False Local $aMPos = MouseGetPos() DllStructSetData($tPoint, "X", $aMPos[0]) DllStructSetData($tPoint, "Y", $aMPos[1]) _WinAPI_ScreenToClient($g_hEdit, $tPoint) If $tPoint.X > - 1 And $tPoint.X < $g_aSize[0] And $tPoint.Y > -1 And $tPoint.Y < $g_aSize[1] Then ;~ If Not $bToolTip Then ; keep this test commented out for an accurate tooltip display ToolTip(_WinAPI_GetWindowText($g_hEdit)) $bToolTip = True ;~ EndIf Else If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndIf EndFunc ;==>_TimerProc If anyone thinks of an easier way to achieve the tooltip part, please indicate it here, thanks Edited December 1, 2022 by pixelsearch Changed variable name $g_aPos[] to a more precise $g_aSize[] Zedna 1 Link to comment Share on other sites More sharing options...
TimRude Posted December 1, 2022 Author Share Posted December 1, 2022 1 minute ago, pixelsearch said: I wondered if there was a way to display a tooltip while hovering over the Edit control. I had the same thought but couldn't find a way to do it. So instead I just used the $EM_SETSEL message to make the Edit control always shows the very end of the line of text. So if there's a path that's too long to fit, you're at least seeing the most important part of it at all times. And since the Edit control is now read-only instead of disabled, the user can always scroll around in or select and copy the entire path from the control if desired. So my _BrowseProc function now looks like this: Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) ; set the title _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) ; preselect specified folder Local $hEdit = ControlGetHandle($hWnd, '', 'Edit1') _SendMessage($hEdit, $EM_SETREADONLY, 1, 0) ; make edit control readonly ; turn off tab-stop for edit control and give focus to treeview at startup Local $Style = _WinAPI_GetWindowLong($hEdit, $GWL_STYLE) _WinAPI_SetWindowLong($hEdit, $GWL_STYLE, BitAND($Style, BitNOT($WS_TABSTOP))) Case $BFFM_SELCHANGED $sPath = _WinAPI_ShellGetPathFromIDList($wParam) ; get the selected path as a string If @error Or ($sPath = '') Then _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 0) ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update edit control to show full path ; make sure end of path is visible in edit control _SendMessage(ControlGetHandle($hWnd, '', 'Edit1'), $EM_SETSEL, StringLen($sPath), StringLen($sPath)) _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 1) ; enable OK button EndIf EndSwitch Return 0 EndFunc ;==>_BrowseProc In this version I also turned off the tab-stop for the Edit control so the dialog starts with focus on the TreeView. If the user wants to interact with the Edit control to scroll or copy text, they'll need to mouse it. And since the user can't type anything into the Edit control now, the $BFFM_VALIDATEFAILED message will never fire so I removed that branch of the Switch conditional. Your approach with the tooltip is very interesting, but I'm wanting to end up with an include file that I can drop into any project and just call the _BrowseForFolder function without having to do anything else. So you'd need to roll the tooltip display and cleanup code into that _BrowseForFolder function so that one call does it all. Another thing that could make the tooltips look more "normal" is to not have them move or blink if the mouse cursor is moving but is still within the rectangle of the Edit control. In other words, when the mouse cursor moves into the Edit control, display the tooltip but then leave it displayed where it first appeared until the mouse cursor leaves the Edit control. Link to comment Share on other sites More sharing options...
pixelsearch Posted December 1, 2022 Share Posted December 1, 2022 (edited) All your concerns should be easy to solve. Edit : the following code should do it all. If you wanna keep the tooltip part, then its separate callback function is required and after all, if you intend to place 2 functions in your include file (_BrowseForFolder and _BrowseProc) then there should be room for 3, by adding the timer callback function _TimerProc in the include file, no harm done. 9 hours ago, TimRude said: just call the _BrowseForFolder function without having to do anything else. Solved. The timer part has been moved inside _BrowseForFolder, leaving only 1 line in main to call _BrowseForFolder 9 hours ago, TimRude said: In other words, when the mouse cursor moves into the Edit control, display the tooltip but then leave it displayed where it first appeared until the mouse cursor leaves the Edit control. Solved. The tooltip appears now at a fixed place just above the Edit control. In fact, the user shouldn't need too often to scroll right or left inside the edit control if a tooltip displays an accurate content of the edit control. I updated the Func _BrowseProc function to take care of the changes you made in it expandcollapse popup#include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <WinAPIDlg.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> Opt('MustDeclareVars', 1) ConsoleWrite('Returned Path = ' & _BrowseForFolder(@ScriptDir, 'Custom Title', 'Custom Prompt') & @TAB & '@extended = ' & @extended & @CRLF) ;=================================================================================================================================== Func _BrowseForFolder($sInitDir, $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $g_hEdit = 0 Local $hTimerProc = DllCallbackRegister('_TimerProc', 'none', 'hwnd;uint;uint_ptr;dword') Local $iTimerID = _WinAPI_SetTimer(0, 0, 200, DllCallbackGetPtr($hTimerProc)) ; 200ms Global $s_BFF_Dialog_Title = $sTitle Local $hBrowseProc = DllCallbackRegister('_BrowseProc', 'int', 'hwnd;uint;lparam;ptr') Local $pBrowseProc = DllCallbackGetPtr($hBrowseProc) Local $pText = _WinAPI_CreateString($sInitDir) Local $sPath = _WinAPI_BrowseForFolderDlg('', $sPrompt, BitOR($BIF_RETURNONLYFSDIRS, $BIF_EDITBOX, $BIF_VALIDATE), $pBrowseProc, $pText, $hParent_hWnd) _WinAPI_FreeMemory($pText) DllCallbackFree($hBrowseProc) _WinAPI_KillTimer(0, $iTimerID) DllCallbackFree($hTimerProc) If $sPath = '' Then Return SetExtended(-1, '') Else Return $sPath EndIf EndFunc ;==>_BrowseForFolder ;=============================================== Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) ; set the title _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) ; preselect specified folder $g_hEdit = ControlGetHandle($hWnd, '', 'Edit1') _SendMessage($g_hEdit, $EM_SETREADONLY, 1, 0) ; make edit control readonly ; turn off tab-stop for edit control and give focus to treeview at startup Local $Style = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE) _WinAPI_SetWindowLong($g_hEdit, $GWL_STYLE, BitAND($Style, BitNOT($WS_TABSTOP))) Case $BFFM_SELCHANGED $sPath = _WinAPI_ShellGetPathFromIDList($wParam) ; get the selected path as a string If @error Or ($sPath = '') Then _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 0) ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update edit control to show full path ; make sure end of path is visible in edit control _SendMessage(ControlGetHandle($hWnd, '', 'Edit1'), $EM_SETSEL, StringLen($sPath), StringLen($sPath)) _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 1) ; enable OK button EndIf EndSwitch Return 0 EndFunc ;==>_BrowseProc ;=============================================== Func _TimerProc($hWnd, $iMsg, $iTimerID, $iTime) #forceref $hWnd, $iMsg, $iTimerID, $iTime Local Static $bToolTip = False Local $aEditPos = WinGetPos($g_hEdit) If @error Then Return ; Edit window not found Local $aMPos = MouseGetPos() If ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1] - 25) $bToolTip = True Else If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndIf EndFunc ;==>_TimerProc Good luck, whatever way you'll choose Edited December 1, 2022 by pixelsearch Link to comment Share on other sites More sharing options...
TimRude Posted December 2, 2022 Author Share Posted December 2, 2022 @pixelsearch I like It! I did make a couple more little tweaks, however. In the _BrowseForFolder function I added another Global variable, for the hWnd of the dialog so it could be used in the _TimeProc function. And then I stored the hWnd of the dialog in the _BrowserProc function. Func _BrowseForFolder($sInitDir, $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $g_hBrowseDlg = 0, $g_hEdit = 0 ... Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) $g_hBrowseDlg = $hWnd Then I changed the If conditional in the _TimerProc function from this: If ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1] - 25) $bToolTip = True Else If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndIf To this: If WinActive($g_hBrowseDlg) _ And ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then If Not $bToolTip Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) $bToolTip = True Else If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndIf These little tweaks accomplish 3 things: 1) The tooltip gets dismissed if the dialog stops being the active window. Otherwise the tooltip bleeds through to other windows if you happen to have the mouse positioned where the Edit control is, even if the dialog is hidden below the other windows. 2) If the tooltip is already displayed it doesn't try to display it again. It just leaves it alone. That eliminates flickering. 3) Instead of positioning the tooltip higher than the Edit control, I positioned it directly covering the Edit control. This is more consistent with Microsoft UI design guidelines. The article here states: Quote The difference between ordinary and in-place tooltips is positioning. By default, when the mouse pointer hovers over a region that has a tooltip associated with it, the tooltip is displayed adjacent to the region. However, tooltips are windows, and they can be positioned anywhere you choose by calling SetWindowPos. Creating an in-place tooltip is a matter of positioning the tooltip window so that it overlays the text string. In fact, if you happen to have (or create) a folder name that's too long to display fully in the TreeView portion of the dialog, the dialog already displays an in-place tooltip for it within the TreeView control. So doing the same for the Edit control is more consistent with the over dialog design. So here's the complete listing with my tweaks added: expandcollapse popup#include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <WinAPIDlg.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> Opt('MustDeclareVars', 1) ConsoleWrite('Returned Path = ' & _BrowseForFolder(@ScriptDir, 'Custom Title', 'Custom Prompt') & @TAB & '@extended = ' & @extended & @CRLF) ;=================================================================================================================================== Func _BrowseForFolder($sInitDir, $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $g_hBrowseDlg = 0, $g_hEdit = 0 Local $hTimerProc = DllCallbackRegister('_TimerProc', 'none', 'hwnd;uint;uint_ptr;dword') Local $iTimerID = _WinAPI_SetTimer(0, 0, 200, DllCallbackGetPtr($hTimerProc)) ; 200ms Global $s_BFF_Dialog_Title = $sTitle Local $hBrowseProc = DllCallbackRegister('_BrowseProc', 'int', 'hwnd;uint;lparam;ptr') Local $pBrowseProc = DllCallbackGetPtr($hBrowseProc) Local $pText = _WinAPI_CreateString($sInitDir) Local $sPath = _WinAPI_BrowseForFolderDlg('', $sPrompt, BitOR($BIF_RETURNONLYFSDIRS, $BIF_EDITBOX, $BIF_VALIDATE), $pBrowseProc, $pText, $hParent_hWnd) _WinAPI_FreeMemory($pText) DllCallbackFree($hBrowseProc) _WinAPI_KillTimer(0, $iTimerID) DllCallbackFree($hTimerProc) If $sPath = '' Then Return SetExtended(-1, '') Else Return $sPath EndIf EndFunc ;==>_BrowseForFolder ;=============================================== Func _BrowseProc($hWnd, $iMsg, $wParam, $lParam) $g_hBrowseDlg = $hWnd Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) ; set the title _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) ; preselect specified folder $g_hEdit = ControlGetHandle($hWnd, '', 'Edit1') _SendMessage($g_hEdit, $EM_SETREADONLY, 1, 0) ; make edit control readonly ; turn off tab-stop for edit control and give focus to treeview at startup Local $Style = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE) _WinAPI_SetWindowLong($g_hEdit, $GWL_STYLE, BitAND($Style, BitNOT($WS_TABSTOP))) Case $BFFM_SELCHANGED $sPath = _WinAPI_ShellGetPathFromIDList($wParam) ; get the selected path as a string If @error Or ($sPath = '') Then _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 0) ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update edit control to show full path ; make sure end of path is visible in edit control _SendMessage(ControlGetHandle($hWnd, '', 'Edit1'), $EM_SETSEL, StringLen($sPath), StringLen($sPath)) _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 1) ; enable OK button EndIf EndSwitch Return 0 EndFunc ;==>_BrowseProc ;=============================================== Func _TimerProc($hWnd, $iMsg, $iTimerID, $iTime) #forceref $hWnd, $iMsg, $iTimerID, $iTime Local Static $bToolTip = False Local $aEditPos = WinGetPos($g_hEdit) If @error Then Return ; Edit window not found Local $aMPos = MouseGetPos() If WinActive($g_hBrowseDlg) _ And ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then If Not $bToolTip Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) $bToolTip = True Else If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndIf EndFunc ;==>_TimerProc Thanks again for your invaluable assistance with this. It's ended up being a pretty slick little include, and works exactly as I'd hoped. pixelsearch 1 Link to comment Share on other sites More sharing options...
pixelsearch Posted December 2, 2022 Share Posted December 2, 2022 Glad you made it ! As I'm having a busy day today, then I'll read your last post carefully later in the evening. One thing I quickly noticed concerns the following line : If Not $bToolTip Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) It will display a wrong "static" tooltip if the user does the following : * leave the mouse cursor over the edit control * move through items in the treeview using the keyboard * the tooltip won't display the correct path while the user uses keyup/keydown in the treeview You'll decide if it's acceptable or not to have a tooltip not reflecting in real time the changes of path, when the keyboard is used to move through items in the treeview, while the mouse pointer is waiting patiently over the edit control. See you later Link to comment Share on other sites More sharing options...
TimRude Posted December 2, 2022 Author Share Posted December 2, 2022 Good catch! The fix: Add another Global variable to _BrowseForFolder function to track if the path in the Treeview has changed Global $g_hBrowseDlg = 0, $g_hEdit = 0, $b_BFF_PathChanged = False In the _BrowseProc function, set that variable to True in the $BFFM_SELCHANGED event Case $BFFM_SELCHANGED $b_BFF_PathChanged = True In the _TimerProc function, include that variable in the decision whether to update the Tooltip, and then reset it back to False again If (Not $bToolTip) Or $b_BFF_PathChanged Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) $b_BFF_PathChanged = False $bToolTip = True Now you can leave the mouse over the Edit control to display the Tooltip and scroll through the TreeView with the keyboard and see the Tooltip update immediately. Link to comment Share on other sites More sharing options...
pixelsearch Posted December 2, 2022 Share Posted December 2, 2022 Hi TimRude, You did great improvements, by covering the Edit control with the ToolTip, also using $g_hBrowseDlg to test if the dialog window is active or not. As the test on WinActive($g_hBrowseDlg) becomes important in the function _TimerProc() then I think there is no point anymore on testing in all cases the position of the edit control and the mouse position during the function _TimerProc() . Reordering its lines like the following seem a bit more efficient, especially during the phases when the dialog window isn't active. Also we'll get rid of the (now) completely useless line "If @error Then Return ; Edit window not found" Func _TimerProc($hWnd, $iMsg, $iTimerID, $iTime) #forceref $hWnd, $iMsg, $iTimerID, $iTime Local Static $bToolTip = False If WinActive($g_hBrowseDlg) Then Local $aEditPos = WinGetPos($g_hEdit) Local $aMPos = MouseGetPos() If ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then If (Not $bToolTip) Or $b_BFF_PathChanged Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) $b_BFF_PathChanged = False $bToolTip = True Return EndIf EndIf If $bToolTip Then ToolTip("") $bToolTip = False EndIf EndFunc ;==>_TimerProc In case you don't have any new modification, maybe you could post the final amended script for users that might be interested. Well... it was an interesting journey, it's time for new AutoIt adventures Link to comment Share on other sites More sharing options...
TimRude Posted December 3, 2022 Author Share Posted December 3, 2022 (edited) Finished product. The only other final modifications were cosmetic, mainly changing the function names _BrowseProc and _TimerProc to _BFF_BrowseProc and _BFF_TimerProc to help avoid any conflicts with potentially existing function names in code that this include might be used with, and adding a UDF header. expandcollapse popup#include-once #include <WinAPIDlg.au3> #include <WinAPISysWin.au3> #include <EditConstants.au3> #include <WindowsConstants.au3> ; #FUNCTION# ==================================================================================================================== ; Name ..........: _BrowseForFolder ; Description ...: Display older style Browse for Folder dialog with some enhancements ; Syntax ........: _BrowseForFolder([$sInitDir = '', [$sTitle = 'Browse For Folder'[, $sPrompt = 'Select a folder from the list below.'[, $hParent_hWnd = 0]]]]) ; Parameters ....: $sInitDir - [optional] a string value. Initial folder to display in dialog. If blank, starts at 'This PC'. ; $sTitle - [optional] a string value. Title of dialog box. Default is 'Browse For Folder'. ; $sPrompt - [optional] a string value. Prompt text for dialog box. Default is 'Select a folder from the list below.'. ; $hParent_hWnd - [optional] a handle value. Handle of parent window. Default is 0. ; Return values .: Folder path selected. If Cancel or an invalid selection is made, returns nothing and sets @extended to -1. ; Author ........: TimRude, pixelsearch ; Modified ......: ; Remarks .......: Initially based on code in the help for _WinAPI_BrowseForFolderDlg function. ; Related .......: _WinAPI_BrowseForFolderDlg by Yashied, jpm ; =============================================================================================================================== Func _BrowseForFolder($sInitDir = '', $sTitle = 'Browse For Folder', $sPrompt = 'Select a folder from the list below.', $hParent_hWnd = 0) Global $g_hBrowseDlg = 0, $g_hEdit = 0, $b_BFF_PathChanged = False Local $hTimerProc = DllCallbackRegister('_BFF_TimerProc', 'none', 'hwnd;uint;uint_ptr;dword') Local $iTimerID = _WinAPI_SetTimer(0, 0, 200, DllCallbackGetPtr($hTimerProc)) ; 200ms Global $s_BFF_Dialog_Title = $sTitle Local $hBrowseProc = DllCallbackRegister('_BFF_BrowseProc', 'int', 'hwnd;uint;lparam;ptr') Local $pBrowseProc = DllCallbackGetPtr($hBrowseProc) Local $pText = _WinAPI_CreateString($sInitDir) Local $sPath = _WinAPI_BrowseForFolderDlg('', $sPrompt, BitOR($BIF_RETURNONLYFSDIRS, $BIF_EDITBOX), $pBrowseProc, $pText, $hParent_hWnd) _WinAPI_FreeMemory($pText) DllCallbackFree($hBrowseProc) _WinAPI_KillTimer(0, $iTimerID) ToolTip('') ; ensure tooltip is dismissed when dialog is dismissed DllCallbackFree($hTimerProc) If $sPath = '' Then Return SetExtended(-1, '') Else Return $sPath EndIf EndFunc ;==>_BrowseForFolder Func _BFF_BrowseProc($hWnd, $iMsg, $wParam, $lParam) ; Browse dialog callback $g_hBrowseDlg = $hWnd Local $sPath Switch $iMsg Case $BFFM_INITIALIZED _WinAPI_SetWindowText($hWnd, $s_BFF_Dialog_Title) ; set the title _SendMessage($hWnd, $BFFM_SETSELECTIONW, 1, $lParam) ; preselect specified folder $g_hEdit = ControlGetHandle($hWnd, '', 'Edit1') _SendMessage($g_hEdit, $EM_SETREADONLY, 1, 0) ; make edit control readonly ; turn off tab-stop for edit control and give focus to treeview at startup Local $Style = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE) _WinAPI_SetWindowLong($g_hEdit, $GWL_STYLE, BitAND($Style, BitNOT($WS_TABSTOP))) Case $BFFM_SELCHANGED $b_BFF_PathChanged = True $sPath = _WinAPI_ShellGetPathFromIDList($wParam) ; get the selected path as a string If @error Or ($sPath = '') Then _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 0) ; disable OK button for non-folder selections Else ControlSetText($hWnd, '', 'Edit1', $sPath) ; update edit control to show full path ; make sure end of path is visible in edit control _SendMessage(ControlGetHandle($hWnd, '', 'Edit1'), $EM_SETSEL, StringLen($sPath), StringLen($sPath)) _SendMessage($hWnd, $BFFM_ENABLEOK, 0, 1) ; enable OK button EndIf EndSwitch Return 0 EndFunc ;==>_BFF_BrowseProc Func _BFF_TimerProc($hWnd, $iMsg, $iTimerID, $iTime) #forceref $hWnd, $iMsg, $iTimerID, $iTime Local Static $bToolTip = False If WinActive($g_hBrowseDlg) Then Local $aEditPos = WinGetPos($g_hEdit) Local $aMPos = MouseGetPos() If ($aMPos[0] > $aEditPos[0]) And ($aMPos[0] < $aEditPos[0] + $aEditPos[2]) _ And ($aMPos[1] > $aEditPos[1]) And ($aMPos[1] < $aEditPos[1] + $aEditPos[3]) Then If (Not $bToolTip) Or $b_BFF_PathChanged Then ToolTip(_WinAPI_GetWindowText($g_hEdit), $aEditPos[0], $aEditPos[1]) $b_BFF_PathChanged = False $bToolTip = True Return EndIf EndIf ; If we get to here, either the dialog isn't active or the mouse isn't over the Edit control, so turn off the tooltip If $bToolTip Then ToolTip('') $bToolTip = False EndIf EndFunc ;==>_BFF_TimerProc Edited December 18, 2022 by TimRude Add command to ensure tooltip is dismissed when dialog closes Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now