Jump to content

Making a window temporarily active


BakedCakes
 Share

Go to solution Solved by BakedCakes,

Recommended Posts

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:

#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:

  1. Take note of the currently active window
  2. Take note of the current order of windows
  3. Make Notepad the active window
  4. 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 by BakedCakes
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

?

#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)

 

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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 :D

Edited by pixelsearch
Link to comment
Share on other sites

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) :D

$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 by mikell
Link to comment
Share on other sites

  • Solution

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.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...