App Switcher Script Works on Everything Except Explorer

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
antoniopap
Posts: 3
Joined: 28 Nov 2022, 23:05
Contact:

App Switcher Script Works on Everything Except Explorer

Post by antoniopap » 29 Nov 2022, 16:11

I got this improved App Switch (supposed to work like macOS's Command + Tilde) from https://old.reddit.com/r/AutoHotkey/comments/jk11pn/alttilde_like_mac/#bottom-comments . It works how I would expect it to, except it will not shuffle windows from explorer. Could someone assist me in fixing this? Thanks!

Code: Select all

#SingleInstance force 
#Persistent 

; ****************************************************************************
; *                              Next App Window                             *
; ****************************************************************************

!`::
WinGet, currentwindow,, A
WinGet, activeprocess, ProcessName, A
WinGet, winids, List, ahk_exe %activeprocess%
arr := []
Loop, %winids%
{
    id := winids%A_Index%
    arr.Push(id)
}
SortArray(arr)
activeIndex := 0
for index, element in arr
{
    If (element = currentwindow)
        activeIndex := index
}
nextIndex := % Mod(activeIndex + 1, winids)
If (%nextIndex% = 0)
    nextIndex := winids
elem := arr[nextIndex]
WinSet, AlwaysOnTop, On, ahk_id %elem%
WinSet, AlwaysOnTop, Off, ahk_id %elem%
WinActivate, ahk_id %elem%
return

!+`::   ; Previous App Window
WinGet, currentwindow,, A
WinGet, activeprocess, ProcessName, A
WinGet, winids, List, ahk_exe %activeprocess%
arr := []
Loop, %winids%
{
    id := winids%A_Index%
    arr.Push(id)
}
SortArray(arr)
activeIndex := 0
for index, element in arr
{
    If (element = currentwindow)
        activeIndex := index
}
nextIndex := % Mod(activeIndex - 1, winids)
If (%nextIndex% = 0)
    nextIndex := winids
elem := arr[nextIndex]
WinSet, AlwaysOnTop, On, ahk_id %elem%
WinSet, AlwaysOnTop, Off, ahk_id %elem%
WinActivate, ahk_id %elem%
return




SortArray(Array, Order="A") {
    ;Order A: Ascending, D: Descending, R: Reverse
    MaxIndex := ObjMaxIndex(Array)
    If (Order = "R") {
        count := 0
        Loop, % MaxIndex
            ObjInsert(Array, ObjRemove(Array, MaxIndex - count++))
        Return
    }
    Partitions := "|" ObjMinIndex(Array) "," MaxIndex
    Loop {
        comma := InStr(this_partition := SubStr(Partitions, InStr(Partitions, "|", False, 0)+1), ",")
        spos := pivot := SubStr(this_partition, 1, comma-1) , epos := SubStr(this_partition, comma+1)    
        if (Order = "A") {    
            Loop, % epos - spos {
                if (Array[pivot] > Array[A_Index+spos])
                    ObjInsert(Array, pivot++, ObjRemove(Array, A_Index+spos))    
            }
        } else {
            Loop, % epos - spos {
                if (Array[pivot] < Array[A_Index+spos])
                    ObjInsert(Array, pivot++, ObjRemove(Array, A_Index+spos))    
            }
        }
        Partitions := SubStr(Partitions, 1, InStr(Partitions, "|", False, 0)-1)
        if (pivot - spos) > 1    ;if more than one elements
            Partitions .= "|" spos "," pivot-1        ;the left partition
        if (epos - pivot) > 1    ;if more than one elements
            Partitions .= "|" pivot+1 "," epos        ;the right partition
    } Until !Partitions
}


antoniopap
Posts: 3
Joined: 28 Nov 2022, 23:05
Contact:

Re: App Switcher Script Works on Everything Except Explorer

Post by antoniopap » 08 Aug 2023, 14:19

Do you think you could help me recreate this script to work with file explorer? I'm new to autohotkey

User avatar
mikeyww
Posts: 26437
Joined: 09 Sep 2014, 18:38

Re: App Switcher Script Works on Everything Except Explorer

Post by mikeyww » 08 Aug 2023, 15:08

No (it's a bit long), but some good news: I tried the script at the link that I cited, and it worked for both Notepad and File Explorer. You might find it useful.

You could try replacing the process name with the window class, but you'd have to test it.

Code: Select all

#Requires AutoHotkey v1.1.33

; Replace:
WinGet, activeprocess, ProcessName, A
WinGet, winids, List, ahk_exe %activeprocess%

; With:
WinGetClass activeClass, A
WinGet winids, List, ahk_class %activeClass%

; MsgBox % winids

eugenesv
Posts: 166
Joined: 21 Dec 2015, 10:11

Re: App Switcher Script Works on Everything Except Explorer

Post by eugenesv » 26 Aug 2023, 04:13

Check out this Autohotkey v2 test file, works in Explorer windows as well by adding a class condition as suggested by @ mikeyww

Code: Select all

#Requires AutoHotKey 2.0-beta.3
; ————— Window test
  ; Avoid GlobalWinKey by sending Self directly to window control (SubStr filter out $#)
  +#Tab::AppWindowSwitcher("Prev")	;⇧❖​ 	⭾ ⟶ Switch to the Previous Window of the same App
  #Tab:: AppWindowSwitcher("Next")	;  ❖​	⭾ ⟶ Switch to the Next     Window of the same App
  #vk4b::AppWindowSwitcher("←")	;  ❖​	k  ⟶ Switch to the Previous Window of the same App
  #vk4a::AppWindowSwitcher("→")	;  ❖​	j  ⟶ Switch to the Next     Window of the same App

AppWindowSwitcher(dir:="Next") {
  direction := 1 ; previous
  isNextStr := ["Next","→","1"]
  for isNext in isNextStr {
    if InStr(dir,isNext,0) {
      direction := -1
    }
  }
  winCur  	:= WinGetID(         "A")
  winProc 	:= WinGetProcessName("A")
  winClass	:= WinGetClass(      "A")
  if winProc = "explorer.exe" { ; Explorer needs an extra condition to only count actual File Explorer windows
    winTitle	:= "ahk_exe " winProc " ahk_class " winClass
  } else {
    winTitle	:= "ahk_exe " winProc
  }
  winIDs	:= WinGetList(winTitle)
  ; _test := "winProc=" . winProc
  ; _test .= "`nwinTitle=" . winTitle
  ; for this_id in winIDs {
  ;   _test .= "`n|id=" this_id
  ; }
  ; msgbox(_test)

  winIDsArr	:= Array()
  For v in winIDs {
    winIDsArr.Push(v)
  }
  arr := []
  winIDsCount := winIDsArr.Length
  Loop winIDsCount {
    id := winIDsArr[A_Index]
    arr.Push(id)
  }
  SortArray(arr)
  activeIndex := 0
  for index, element in arr {
    If (element = winCur)
      activeIndex := index
  }
  nextIndex := Mod(activeIndex + direction, winIDsCount)
  If (nextIndex = 0) {
    nextIndex := winIDsCount
  }
  elem := arr[nextIndex]
  WinSetAlwaysOnTop(1, "ahk_id " elem)
  WinSetAlwaysOnTop(0, "ahk_id " elem)
  WinActivate("ahk_id " elem)
  return
  }

SortArray(Array, Order:="A") { ; Order A: Ascending, D: Descending, R: Reverse
  MaxIndex := Array.Length
  If (Order = "R") {
    count := 0
    Loop MaxIndex {
      Array.Insert(Array.RemoveAt(MaxIndex - count++))
    }
    Return
  }
  MinIndex  	:= 1 ;Array.MinIndex()
  Partitions	:= "|" MinIndex "," MaxIndex
  Loop{
    this_partition := SubStr(Partitions
      , (InStr(Partitions,"|",False,-1)+1)<1
      ? (InStr(Partitions,"|",False,-1)+1)-1
      : (InStr(Partitions,"|",False,-1)+1)  )
    comma	:= InStr( this_partition,",")
    spos 	:= SubStr(this_partition,        1                   , comma-1)
    epos 	:= SubStr(this_partition, (comma+1)<1 ? (comma+1)-1 : (comma+1))
    pivot	:= spos
    if (Order = "A") {
      Loop epos - spos {
        if (Array[pivot] > Array[A_Index+spos])
          Array.InsertAt(pivot++, Array.RemoveAt(A_Index+spos))
      }
    } else {
      Loop epos - spos {
        if (Array[pivot] < Array[A_Index+spos])
          Array.InsertAt(pivot++, Array.RemoveAt(A_Index+spos))
      }
    }
    Partitions := SubStr(Partitions, 1, InStr(Partitions, "|", False, -1)-1)
    if (pivot - spos) > 1 { ; if more than one elements
      Partitions .= "|" spos "," pivot-1    ; ← partition
    }
    if (epos - pivot) > 1 { ; if more than one elements
      Partitions .= "|" pivot+1 "," epos    ; → partition
    }
  } Until !Partitions
  }

eugenesv
Posts: 166
Joined: 21 Dec 2015, 10:11

Re: App Switcher Script Works on Everything Except Explorer

Post by eugenesv » 26 Aug 2023, 11:25

though I didn't get the swap order of that version, doesn't seem to match what you'd expect with Alt-Tab/Alt-Shift-Tab, so I've updated it to match that, but only for a single app

Code: Select all

global ↑ := 2
  , ↓                           	:= -2
  , ←                           	:= -1
  , →                           	:=  1
+#Tab::AppWindowSwitcher(dir:=→)	;⇧❖​ 	⭾ ⟶ Switch to the Next     Window of the same App (↑ Z-order)
#Tab:: AppWindowSwitcher(dir:=←)	;  ❖​	⭾ ⟶ Switch to the Previous Window of the same App (↓ Z-order)
#vk4b::AppWindowSwitcher(dir:=→)	;  ❖​	k  ⟶ Switch to the Next     Window of the same App (↑ Z-order)
#vk4a::AppWindowSwitcher(dir:=←)	;  ❖​	j  ⟶ Switch to the Previous Window of the same App (↓ Z-order)
#vk49::SwapTwoAppWindows()      	;  ❖​	i  ⟶ Switch between the last 2 Windows of the same App

AppWindowSwitcher(dir:="Next") {
  isPrevStr	:= ["Prev","←",←] ; accepted arguments for directions
  isNextStr	:= ["Next","→",→]
  ;——————————————————————————————————————————————————
  isPrev	:= false
  isNext	:= false
  winI := 0
  for iPrev in isPrevStr {
    if (dir=iPrev) {
      isPrev	:= true
    }
  }
  if (isPrev = false) {
    for iNext in isNextStr {
      if (dir=iNext) {
        isNext	:= true
      }
    }
  }
  if (isPrev = false and isNext = false) {
    return
  }

  winA_id  	:= WinGetID(         "A")
  winA_proc	:= WinGetProcessName("A")
  winA_cls 	:= WinGetClass(      "A")
  if winA_proc = "explorer.exe" { ; Explorer needs an extra condition to only count actual File Explorer windows
    winTitleMatch	:= "ahk_exe " winA_proc " ahk_class " winA_cls
  } else {
    winTitleMatch	:= "ahk_exe " winA_proc
  }
  winIDs     	:= WinGetList(winTitleMatch)
  winID_count	:= winIDs.Length
  if        (winID_count = 1) {
  } else if (winID_count = 2) {
    winNext_id	:= winIDs[2]
    winTop(winNext_id)
  } else {
    if        isPrev {
      ; winI	:=  1 ; most recently switched away from window, down the Z-order
      WinMoveBottom("A")
      WinActivate(winTitleMatch)
    } else if isNext {
      winI    	:= -1 ; oldest switched away from window, at the bottom of the Z-order
      winTo_id	:= winIDs[winI]
      winTop(winTo_id)
      winA_title	:= WinGetTitle(winTo_id)
    }
  }
  return
  }
winTop(win_id) {
  ; WinMoveTop( "ahk_id " win_id) ; Bring window to the top of the stack without explicitly activating it
    ; ↑ may have no effect due to OS protection against applications that try to steal focus from the user (it may depend on factors such as what type of window is currently active and what the user is currently doing)
    ; ↓ work-around: make the window briefly always-on-top via WinSetAlwaysOnTop, then turn off always-on-top
  WinSetAlwaysOnTop(1, "ahk_id " win_id)
  WinSetAlwaysOnTop(0, "ahk_id " win_id)
  WinActivate(         "ahk_id " win_id)
}

SwapTwoAppWindows() { ; Instantly swap between the last 2 windows without showing any icons/thumnails
  static isNext	:= false ; switch to the next window
  if isNext {
    AppWindowSwitcher(dir:=→)
    isNext	:= false
  } else {
    AppWindowSwitcher(dir:=←)
    isNext	:= true
  }
}

Post Reply

Return to “Ask for Help (v1)”