BakedCakes Posted September 16, 2022 Posted September 16, 2022 (edited) I want to make a window active, do some stuff to it, and then undo making it active. What I mean by undoing -- by making the window active, it gets brought up to the front, covering other windows, so when undoing that I want to put it back behind the other windows, just as it was before I have made it active, and the window that had the focus before I have made the other window active should have its focus restored too. I have end up with the following: expandcollapse popup#include <Constants.au3> #include <WinApi.au3> #include <WindowsConstants.au3> Opt("WinWaitDelay", 0) $initiallyActive = WinGetHandle("[ACTIVE]", "") $initialWindows = WinList() ; make Notepad the active window $notepad = WinGetHandle("[CLASS:Notepad]") WinActivate($notepad) WinWaitActive($notepad) Sleep(1000) ; Notice that Notepad is active ; undo making Notepad the active window RestoreOrderAfterWindow($notepad, $initialWindows) WinActivate($initiallyActive) WinWaitActive($initiallyActive) Func RestoreOrderAfterWindow($windowHandle, $initialWinList) For $i = 1 to $initialWinList[0][0] - 1 If $initialWinList[$i][1] == $windowHandle Then For $j = $i-1 to 1 Step -1 Local $state = WinGetState($initialWinList[$j][1]) If BitAND($state, $WIN_STATE_VISIBLE) And _ Not BitAND($state, $WIN_STATE_MINIMIZED) And _ Not BitAND(_WinAPI_GetWindowLong($initialWinList[$j][1], $GWL_EXSTYLE), $WS_EX_TOPMOST) Then WinActivate($initialWinList[$j][1]) WinWaitActive($initialWinList[$j][1]) EndIf Next Return EndIf Next EndFunc It assumes you have a Notepad open and that it's behind a few other windows. What it does is: Take note of the currently active window Take note of the current order of windows Make Notepad the active window Restore the things to how they were before step 3, i.e. bury the Notepad window back to where it was behind other windows (by making windows that were above it active, one by one) and restore the focus to the previously active window This works fairly well. However, when burying Notepad, the other windows that are being made active result in flicker, at least on my 165HZ monitor, which is annoying. You can see each window appearing. Ideally I'd want to skip having each window appearing and see only the final result. (Note that removing the WinWaitActive() call results in the windows being restored in the wrong order in some cases, so it doesn't seem like removing it is an option, and even without it you can still see the activated windows being drawn one by one resulting in the flicker). I wonder if there is a better way to what I'm trying to do here, one which results in no window "flicker"? Ideally I would make the Notepad window active without bringing it to the front, that way I wouldn't need to restore the window order at all, avoiding the cause of the flicker altogether, but that doesn't seem possible, as the MSDN entry for SetWindowPos() points out: Quote An application cannot activate an inactive window without also bringing it to the top of the Z order. One of the things I have tried is using _WinAPI_SetWindowPos() to change the Z-order of windows, but it turns out that once you say that a window X is above a window Y, then X is always above Y, i.e. click on the window Y in an attempt to bring it above X doesn't work, which is not what I want, which is why I went with using WinActivate() instead. Edited September 17, 2022 by BakedCakes
kurtykurtyboy Posted September 17, 2022 Posted September 17, 2022 This is actually a perfect use case for Windows' BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos which I just recently posted an example of. My example was for moving windows, but you can tweak it to set the z order instead. I'll try to give an example when I'm back at my computer if you haven't gotten it yet. BakedCakes 1
mikell Posted September 17, 2022 Posted September 17, 2022 ? #include <WinAPI.au3> #include <WindowsConstants.au3> ; get notepad Local $hWnd = WinGetHandle("[CLASS:notepad]") ; get active window $w = WinList() For $i = 1 To $w[0][0] If $w[$i][0] <> "" And BitAND(WinGetState($w[$i][1]), 2) Then $active = $w[$i][1] Exitloop EndIf Next ; activate notepad WinActivate($hWnd) Sleep(1000) ; send notepad to bottom and reactive the previous one _WinAPI_SetWindowPos($hWnd, $HWND_BOTTOM, 0, 0, 0, 0, BitOR($SWP_FRAMECHANGED, $SWP_NOMOVE, $SWP_NOSIZE)) WinActivate($active) pixelsearch and kurtykurtyboy 2
BakedCakes Posted September 17, 2022 Author Posted September 17, 2022 @mikell I want to send notepad back to where it was before it got activated, which might not be the very bottom, but your code always sends it to the very bottom. @kurtykurtyboy Thanks for the suggestion, that looks interesting. Should reduce the flicker of the RestoreOrderAfterWindow() function, since these Defer functions update windows in a single screen refresh cycle.
pixelsearch Posted September 18, 2022 Posted September 18, 2022 (edited) Hi everybody I got a correct result when using _WinAPI_SetWindowPos() which allows to force the Z-order of a window (handle as 1st parameter) to be placed immediately after another window (handle as 2nd parameter) So checking both handles before bringing NotePad to front allows to place it back exactly where it was when you're done. No need to activate any other window (no flicker) during the process : #include <AutoItConstants.au3> #include <WinAPISysWin.au3> $notepad = WinGetHandle("[CLASS:Notepad]") $initiallyActive = WinGetHandle("[ACTIVE]", "") $initialWindows = WinList() For $i = 1 To $initialWindows[0][0] If $initialWindows[$i][1] = $notepad Then $BeforeNotePad = $initialWindows[$i - 1][1] ExitLoop EndIf Next ; make Notepad the active window WinActivate($notepad) WinWaitActive($notepad) Sleep(5000) ; Notice that Notepad is active, change its size & position during 5 seconds _WinAPI_SetWindowPos($notepad, $BeforeNotePad, 0, 0, 0, 0, BitOr($SWP_NOMOVE, $SWP_NOSIZE)) WinActivate($initiallyActive) In this example, I had about 8 windows opened and NotePad was in the middle of them. Good luck Edit: half an hour after... it's only now that I notice @mikell also used _WinAPI_SetWindowPos() in his script above. Wish I noticed it earlier, it would have saved some time Edited September 18, 2022 by pixelsearch kurtykurtyboy 1
kurtykurtyboy Posted September 18, 2022 Posted September 18, 2022 @mikell nice solution, and @pixelsearch nice improvement with the 2nd parameter - I didn't realize that was the purpose of that parameter. You both saved me time with my super overkill solution I was about to work up.
mikell Posted September 18, 2022 Posted September 18, 2022 (edited) 11 hours ago, pixelsearch said: $initiallyActive = WinGetHandle("[ACTIVE]", "") Oooh yes, I tried this first... and got a mismatch, because the active window when I *click* the .au3 script file was the folder of the .au3 (either the desktop or any other directory) $initiallyActive = WinGetHandle("[ACTIVE]", "") Msgbox(0,"", Wingettitle($initiallyActive) ) Edit BTW I misunderstood the OP's requirements. Here is my last try #include <WinAPI.au3> #include <WindowsConstants.au3> ; get notepad Local $hWnd = WinGetHandle("[CLASS:notepad]") Local $before = _WinAPI_GetWindow($hWnd, $GW_HWNDNEXT) ; get active window $w = WinList() For $i = 1 To $w[0][0] If $w[$i][0] <> "" And BitAND(WinGetState($w[$i][1]), 2) Then $active = $w[$i][1] Exitloop EndIf Next ; activate notepad WinActivate($hWnd) Sleep(1000) ; send back notepad to where it was and reactive the previous active one _WinAPI_SetWindowPos($hWnd, $before, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE)) WinActivate($active) Edited September 18, 2022 by mikell
Solution BakedCakes Posted September 19, 2022 Author Solution Posted September 19, 2022 Thanks everyone for the suggestions. @mikell's last suggestion is very neat, didn't know about _WinAPI_GetWindow(). I ended up doing something entirely different though. On 9/16/2022 at 7:56 PM, BakedCakes said: Ideally I would make the Notepad window active without bringing it to the front, that way I wouldn't need to restore the window order at all, avoiding the cause of the flicker altogether, but that doesn't seem possible, as the MSDN entry for SetWindowPos() points out: Quote An application cannot activate an inactive window without also bringing it to the top of the Z order. Turns out I can in fact activate an inactive window without brining it up to the front. All I had to do is make the windows above the notepad temporarily always-on-top, i.e. set TOPMOST via DeferWindowPos() on them, then activate the notepad window and do stuff with it, and then set NOTOPMOST on the windows I have set TOPMOST on. That way the notepad window stays in the same Z-order while being activated, so there is no window flicker of any kind.
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