在 Windows 10 中在同一应用程序的两个窗口之间切换的简便方法

在 Windows 10 中在同一应用程序的两个窗口之间切换的简便方法

我正在寻找一种方法来使用快捷键在同一个应用程序的两个打开的窗口之间切换,我发现了这个https://neosmart.net/EasySwitch/但它不是开源的,我甚至不相信它能用。有人有替代方案吗?这是 Unity 和 Mac OS 中的 OOTB,Windows 没有它真是太荒谬了。

不久前,一位微软员工制作了这个工具:https://switcher.en.softonic.com/不确定官方网站是什么,它很棒,但只适用于 Windows 7,不适用于 10,也不是开源的。

我发现最接近开源的东西是这个https://github.com/JochenBaier/fastwindowswitcher但不太理想。

还有其他办法吗?

答案1

您可以使用免费 Alt`自动热键

以下脚本将循环遍历活动进程的所有窗口:

!`::
WinGetClass, OldClass, A
WinGet, ActiveProcessName, ProcessName, A
WinGet, WinClassCount, Count, ahk_exe %ActiveProcessName%
IF WinClassCount = 1
    Return
loop, 2 {
  WinSet, Bottom,, A
  WinActivate, ahk_exe %ActiveProcessName%
  WinGetClass, NewClass, A
  if (OldClass <> "CabinetWClass" or NewClass = "CabinetWClass")
    break
}

安装 AutoHotKey 后,将上述文本放入一个.ahk文件中并双击进行测试。您可以通过右键单击托盘栏中的绿色 H 图标并选择退出来停止脚本。要让它在登录时运行,请将其放在 的启动组中
C:\Users\USER-NAME\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

有用的 AutoHotkey 文档:

答案2

有三种方法可以实现此目的:

  1. 您可以按住 Ctrl + 单击任务栏中的应用程序图标
  2. 使用Win+ 键将应用程序定位到任务栏上。例如,如果 Google Chrome 位于第三个位置,则使用Win+ 键3在 Chrome 窗口之间切换。请注意,这会循环切换不同的窗口,因此要打开第 4 个窗口,您必须按 3 键四次。
  3. 最快的,使用Win++ctrl可以n实现您想要的效果。(其中 n 是应用程序在任务栏上的位置)

请注意,在第二和第三个选项中,如果指定了 n,则该程序没有打开任何窗口,窗口将打开一个新窗口。此效果也可以通过按Win+ shift+来实现n,无论该应用程序的窗口是否已打开。

答案3

我已更新 harrymc 的脚本,使其可以在 AutoHotkey v2 中执行:

#Requires AutoHotkey v2.0

!`::{
  OldClass := WinGetClass("A")
  ActiveProcessName := WinGetProcessName("A")
  WinClassCount := WinGetCount("ahk_exe" ActiveProcessName)
  IF WinClassCount = 1
      Return
  loop 2 {
    WinMoveBottom("A")
    WinActivate("ahk_exe" ActiveProcessName)
    NewClass := WinGetClass("A")
    if (OldClass != "CabinetWClass" or NewClass = "CabinetWClass")
      break
  }
}

由此看来,转换 EugeneK 的脚本也应该很容易。

我也尝试在 v2 中实现 EugeneK 的脚本。我不确定它是否具有所有预期的功能,但它运行良好,我会使用它

#Requires AutoHotkey v2.0

!`::
{
    win_id := WinActive("A")
    win_class := WinGetClass("A")
    active_process_name := WinGetProcessName("A")
    ; We have to be extra careful about explorer.exe since that process is responsible for more than file explorer
    if (active_process_name = "explorer.exe")
        win_list := WinGetList("ahk_exe" active_process_name " ahk_class" win_class)
    else
        win_list := WinGetList("ahk_exe" active_process_name)
    
    ; Calculate index of next window. Since activating a window puts it at the top of the list, we have to take from the bottom.
    next_window_i := win_list.Length
    next_window_id := win_list[next_window_i]
    
    ; Activate the next window and send it to the top.
    WinMoveTop("ahk_id" next_window_id)
    WinActivate("ahk_id" next_window_id)
}

此外:我喜欢将此功能与键绑定在一起Alt+|。因此,如果您也更喜欢这个,请更改!'::!|::

答案4

我无法得到任何答案来满足我的愿望,所以我从中汲取灵感jenda 的解决方案并编写了自己的 AHK V2 脚本。我非常喜欢 Mac 处理此功能的方式,因此我尝试尽可能地模仿这种行为。

alt + ` 在窗口中向前循环

alt + shift + `在窗口间来回循环

我发现按下 alt 时 Windows 资源管理器会创建额外的进程和窗口,所以我添加了CatchWindowsExplorerErrors = true过滤掉这些进程和窗口的功能。

#ErrorStdOut
!`::
{
    ;OutputDebug 'cmd+`` `n'
    changeActiveWindow("forward")
}

<+!`::
{
    ;OutputDebug 'cmd+shift+`` `n'
    changeActiveWindow("back")
}

changeActiveWindow(dir)
{
    static windowOrder := []
    static expectedOrder := []
    currentOrder := []
    CatchWindowsExplorerErrors := true ; Windows Explorer creates a number of invisible windows which breaks behavior when using the alt menu. Set this to false to let these errors through.
    debugging := false

    ; Get all windows the same as the active window
    OldClass := WinGetClass("A")
    ActiveProcessName := WinGetProcessName("A")
    WinClassCount := WinGetCount("ahk_exe " ActiveProcessName)
    ActiveId := WinGetID("A")
    OutputDebug 'Current Window:    ' ActiveId '/' OldClass '/' ActiveProcessName '/' WinGetTitle("ahk_id" ActiveId) '`n'
    
    ; If there's only one window, do nothing
    if (WinClassCount = 1)
        Return

    ; Get all windows of the same process
    ids := WinGetList("ahk_exe " ActiveProcessName)
    for SiblingID in ids {
        if (WinGetTitle(SiblingID) != ""){
            if (CatchWindowsExplorerErrors){
                if ( WinGetClass(SiblingID) != "KbxLabelClass" && WinGetTitle(SiblingID) != "Program Manager"){
                    currentOrder.Push(SiblingID)
                }
            } else {
                currentOrder.Push(SiblingID)
            }
        }
    }

    ; Check first run and populate
    if windowOrder.Length = 0 {
        resetWindows()
    }
    printDebugging()

    ; Check if current order and length match expected order and length
    ; If they don't, expected order has changed or a window has been removed or inserted
    if (currentOrder.Length = expectedOrder.Length) {
        Loop currentOrder.Length {
            if (currentOrder[A_Index] != expectedOrder[A_Index]){
                resetWindows()
                break
            }
        }
    } else {
        resetWindows()
    }

    windowOrder := moveToNextIndex(windowOrder, dir)
    changeActiveWindow()
    expectedOrder := updateExpectedOrder(expectedOrder, windowOrder)
    printDebugging()

    OutputDebug '`n'


    ; Functions
    ; Reset the windows to the current state - used if the order doesn't match the expected order e.g. a new window is introduced or the user has clicked and changed order
    resetWindows(){
        OutputDebug 'Restting memory`n'
        windowOrder := currentOrder.Clone()
        expectedOrder := currentOrder.Clone()
        return
    }

    ; Used to update the active window tracked by windowOrder
    changeActiveWindow(){
        WinActivate("ahk_id" windowOrder[1])
        try {
            OutputDebug 'Switching to:    ' WinGetTitle("ahk_id" windowOrder[1]) ' -- ' windowOrder[1] '`n'
        } catch Error as e {
            OutputDebug "An error was thrown!`nSpecifically: " e.Message
            Exit
        }
    }

    ; Print debugging information
    printDebugging(){
        if (debugging){
            OutputDebug '---------------------- currentOrder: ----------------------`n'
            for e in currentOrder {
                OutputDebug WinGetTitle("ahk_id" e) ' -- ' e '`n'
            }
            OutputDebug '---------------------- windowOrder: ----------------------`n'
            for e in windowOrder {
                OutputDebug WinGetTitle("ahk_id" e) ' -- ' e '`n'
            }
            OutputDebug '---------------------- expectedOrder: ----------------------`n'
            for e in expectedOrder {
                OutputDebug WinGetTitle("ahk_id" e) ' -- ' e '`n'
            }
        }
    }
}



getArrayValueIndex(arr, val){
    Loop arr.Length {
        if (arr[A_Index] == val)
            return A_Index
    }
}

moveToNextIndex(arr, dir){
    if (dir == "forward"){
        e := arr[1]
        arr.RemoveAt(1)
        arr.Push(e)
        return arr
    } else if (dir == "back"){
        e := arr[arr.Length]
        arr.RemoveAt(arr.Length)
        arr.InsertAt(1, e)
        return arr
    }
}

moveLastIndexToFirst(arr){
    e := arr[1]
    arr.RemoveAt(1)
    arr.Push(e)
    return arr
}

updateExpectedOrder(eo, wo){
    activeWindowIndex := getArrayValueIndex(eo, wo[1])
    activeWindow := eo[activeWindowIndex]
    eo.RemoveAt(activeWindowIndex)
    eo.InsertAt(1, activeWindow)
    return eo
}

如果您发现任何优化,我们很乐意接受 PR!

https://github.com/BdSteel/AHK-Scripts/blob/main/Window%20Switcher/Window%20Switcher.ahk

相关内容