ApplicationSwitcher for v2

Post your working scripts, libraries and tools.
vysmaty
Posts: 11
Joined: 26 Mar 2024, 12:13

ApplicationSwitcher for v2

12 Apr 2024, 14:24

Hello everyone,
I've modified ApplicationSwitcher for v2.


The script enables quick switching to/between specified applications, cycling through windows of application, cycling through tabs, and minimizing.
The script is customizable, allowing you to set your modifier keys (e.g. L[Mouse]Button or Shift+Win for Switching Windows) for each function (Switching Windows, Switching Tabs, Minimalize) and define last key of hotkey (e.g. F1-F12) for each application.

I’ve included the script below for those interested. Feel free to modify it to suit your needs, and don’t hesitate to share your own modifications.

Default Help menu to remind you of your set shortcuts is under: "Shift+Win+NumpadSub" OR "Left Mouse Button + NumpadSub"

Image

Code: Select all

; ApplicationSwitcher_v2.ahk
; by vysmaty
; version 20240414

; This is a modified script for v2.
; From Taran Van Hemert and … and … fill in your name. I had it too many years and I don't know exact source. It's rewriten.
; https://www.youtube.com/watch?v=OqyQABySV8k

; Icons by https://icons8.com/

; Script for quick switching between applications, application windows, application tabs and application minimization.
; You have to set your MODIFIER KEYS and APPS & KEYS below.

#Requires AutoHotkey v2.0
#SingleInstance force
#WinActivateForce
SendMode("Input")

; MARK: SET your MODIFIER KEYS
;{-----------------------------------------------

; Show Gui With Help
HelpHotkey := ["+#NumpadSub", "~LButton & NumpadSub"] ; Eg. ( Shift+Win OR Left Mouse Button ) + Numpad Minus key

; Switch Between aka Cycle Through Windows
SwitchBetweenKey := ["+#", "~LButton & "] ; Eg. ( Shift+Win OR Left Mouse Button ) + Key set for app
; Switch to Window and cycle through tabs
SwitchToKey := ["^#"] ; Eg. ( Ctrl+Win) + Key set for app; Or USE Tabs On Wheels ;)
; Minimize Window
MinimalizeKey := ["!#", "~MButton & "] ; Eg. (Alt+Win) + Key set for app


;{-----------------------------------------------
; Close Window
for Modifier in SwitchBetweenKey
{
  Hotkey(Modifier "Esc", CloseWindow.Bind()) ; Eg. ( Shift+Win OR Left Mouse Button ) + Esc to Close Window
}
CloseWindow(this_hotkey) {
  Send "{LButton up}^w"
  ;WinClose("A")
}
;{-----------------------------------------------
; Show Help
for Modifier in HelpHotkey
{
  Hotkey(Modifier, Show.Bind())
}

; Optional For hiding Modifiers from Help Menu
enable_help_for_modifiers := true

; Create base object.
;{-----------------------------------------------
app := Array()
;{-----------------------------------------------

/*
; TEMPLATEs
;{-----------------------------------------------
app.Push(
  ; App Template
  Map(
    "Key", "F13", ; <- Set ME  | OPTIONAL: IF EMPTY APP WILL BE IGNORED. Base Hotkey Key for App Switching.
    "Name", "Template", ; Name
    "EXE", "template.exe", ; Path to Executable (Full Path or Short from ENV)
    "ID", "ahk_class templates", ; Window Identification (ahk_exe, ahk_class)
    "TabKey", "^{tab}", ; OPTIONAL: Keystroke whitch is Switching Tabs
    "icon", "pathtoicon", ; OPTIONAL: IF EMPTY It try to get it from exe. Icon is used at help menu.
    "ComputerName", "COMPUTERNAME" ;OPTIONAL: IF NOT EMPTY, it will work only on a computer with this name. Must match AHK variable: A_ComputerName. For using same script in sync on multiple machines with different apps/versions.
*/

; MARK: SET Apps and KEYS
;{-----------------------------------------------
; Set the desired last key of shortcut below for each desired app!

; FILE MANAGERs
;{-----------------------------------------------
app.Push(
  ; Windows Explorer
  Map(
    "Key", "F2", ; <- Set ME
    "Name", "Explorer",
    "EXE", "explorer.exe",
    "ID", "ahk_class CabinetWClass",
    "TabKey", "^{tab}"
  )
)

app.Push(
  ; One Commander
  Map(
    "Key", "F1", ; <- Set ME
    "Name", "OneCommander",
    "EXE", "onecommander.exe",
    "ID", "ahk_exe OneCommander.exe",
    "TabKey", "^{tab}",
    "ComputerName", "BEAST"
  )
)


; WEB BROWSERS
;{-----------------------------------------------
app.Push(
  ; Vivaldi
  Map(
    "Key", "F3", ; <- Set ME
    "Name", "Vivaldi",
    "EXE", "vivaldi.exe",
    "ID", "ahk_exe vivaldi.exe",
    "icon", ".\icons\Vivaldi.png",
    "TabKey", "^{PgDn}"
  )
)

; NOTES
;{-----------------------------------------------
app.Push(
  ; Notion
  Map(
    "Key", "F10", ; <- Set ME
    "Name", "Notion",
    "EXE", "C:\Users\" A_UserName "\AppData\Local\Programs\Notion\Notion.exe",
    "ID", "ahk_exe Notion.exe",
    "TabKey", "^{tab}"
  )
)

; OFFICE
;{-----------------------------------------------
; Microsoft Office
app.Push(
  Map(
    "Key", "F11", ; <- Set ME
    "Name", "MicrosoftWord",
    "EXE", "C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE",
    "ID", "ahk_class OpusApp",
    "icon", ".\icons\Word.png",
    "TabKey", ""
  )
)

; PDF
;{-----------------------------------------------
app.Push(
  ; Foxit PDF Editor
  Map(
    "Key", "F4", ; <- Set ME
    "Name", "FoxitPDFEditor",
    "EXE", "C:\Program Files (x86)\Foxit Software\Foxit PDF Editor\FoxitPDFEditor.exe",
    "ID", "ahk_class classFoxitPhantom",
    "TabKey", "",
    "ComputerName", "BEAST"
  )
)

app.Push(
  ; Foxit PDF Editor
  Map(
    "Key", "F4", ; <- Set ME
    "Name", "FoxitPDFReader",
    "EXE", "C:\Program Files (x86)\Foxit Software\Foxit Reader\C:\Program Files (x86)\Foxit Software\Foxit PDF Reader\FoxitPDFReader.exe.exe",
    "ID", "ahk_class classFoxitReader",
    "TabKey", "",
    "ComputerName", "AALTO"
  )
)


; CAD
;{-----------------------------------------------
; BricsCAD
app.Push(
  Map(
    "Key", "F5", ; <- Set ME
    "Name", "BricsCAD",
    "EXE", "C:\Program Files\Bricsys\BricsCAD V21 en_US\bricscad.exe",
    "ID", "ahk_exe bricscad.exe",
    "TabKey", "^{tab}"
  )
)


; 3D
;{-----------------------------------------------
app.Push(
  Map(
    "Key", "F6", ; <- Set ME
    "Name", "SketchUp",
    "EXE", "C:\Program Files\SketchUp\SketchUp 2020\SketchUp.exe",
    "ID", "ahk_exe SketchUp.exe",
  )
)


; GRAPHICS
;{-----------------------------------------------

; Reference
app.Push(
  ; PureRef
  Map(
    "Key", "F9", ; <- Set ME
    "Name", "PureRef",
    "EXE", "C:\Program Files\PureRef\PureRef.exe",
    "ID", "ahk_exe PureRef.exe"
  )
)

; AFFINITY
app.Push(
  ; Photo
  Map(
    "Key", "F7", ; <- Set ME
    "Name", "AffinityPhoto",
    "EXE", "C:\Users\" A_UserName "\AppData\Local\Microsoft\WindowsApps\AffinityPhoto2.exe",
    "ID", "ahk_exe Photo.exe",
    "icon", ".\icons\AffinityPhoto2.png",
    "TabKey", "^{tab}"
  )
)


app.Push(
  ; Publisher
  Map(
    "Key", "F8", ; <- Set ME
    "Name", "AffinityPublisher",
    "EXE", "C:\Users\" A_UserName "\AppData\Local\Microsoft\WindowsApps\AffinityPublisher2.exe",
    "ID", "ahk_exe Publisher.exe",
    "icon", ".\icons\AffinityPublisher2.png",
    "TabKey", "^{tab}"
  )
)

; COMUNICATION
;{-----------------------------------------------
; Discord
app.Push(
  Map(
    "Key", "", ; <- Set ME
    "Name", "Discord",
    "EXE", "C:\Users\" A_UserName "\Desktop\Discord.lnk",
    "ID", "ahk_exe Discord.exe",
    "TabKey", "^{PgDn}"
  )
)

; CODE
;{-----------------------------------------------
; VSCode
app.Push(
  Map(
    "Key", "F12", ; <- Set ME
    "Name", "VSCode",
    "EXE", "C:\Users\" A_UserName "\AppData\Local\Programs\Microsoft VS Code\Code.exe",
    "ID", "ahk_exe Code.exe",
    "TabKey", "^{PgDn}"
  )
)

; SORT OBJECT NATURALY
; https://en.wikipedia.org/wiki/Natural_sort_order
; Concept of "natural sorting" aka or "human sorting"
; https://ux.stackexchange.com/questions/95431/how-should-sorting-work-when-numeric-is-mixed-with-alpha-numeric


; Sorting Array with Sorted string (by mikeyww)
; https://www.autohotkey.com/boards/viewtopic.php?p=567448&sid=b137a94cd3939b9c6a0648b298b7297b#p567448

app2 := [], sorted := '', regex := '\d+$'
For n, arr in app
  sorted .= (sorted = '' ? '' : '`n') Format('{:-10}{}', arr["Key"], n)
; N at the End is for index from app array.
For line in StrSplit(logical_sort(sorted), '`n')
  ; Sort(sorted) replaced with logical_sort(sorted)
  ; For natural sorting is used external function StrCmpLogicalW
  ; Sort: If option parameter is blank or omitted, String will be sorted in ascending alphabetical order (case-insensitive), using a linefeed (`n) as separator.
  If RegExMatch(line, regex, &m) ; Matching index at the End of line
    app2.Push(app[m[]])

; Output Sorted array
app := app2
app2 := ""

; NATURAL SORTING
; StrCmpLogicalW function
; https://www.autohotkey.com/boards/viewtopic.php?p=320555#p320555

logical_sort(str) {
  return sort(str, , logical_cmp)
  logical_cmp(a, b, o) => dllcall('Shlwapi.dll\StrCmpLogicalW', 'wstr', a, 'wstr', b, 'int')
}

; Help with set keys - popup menu
helpMenu := Menu()
if (enable_help_for_modifiers = true)
{
  for Modifier in SwitchBetweenKey {
    item_name := "Cycle Through Windows  :  [" Modifier "]  +"
    helpMenu.Add(item_name, DefaultRun.Bind("notepad.exe " A_ScriptFullPath))
    try {
      helpMenu.SetIcon(item_name, ".\icons\icons8-windows-views-96.png", "1")
    }
  }
  helpMenu.Add()
  for Modifier in SwitchToKey {
    item_name := "Cycle Through Tabs  :  [" Modifier "]  +"
    helpMenu.Add(item_name, DefaultRun.Bind("notepad.exe " A_ScriptFullPath))
    try {
      helpMenu.SetIcon(item_name, ".\icons\icons8-switching-between-tabs-96.png", "1")
    }
  }
  helpMenu.Add()
  for Modifier in MinimalizeKey {
    item_name := "Minimize  :  [" Modifier "]  +"
    helpMenu.Add(item_name, DefaultRun.Bind("notepad.exe " A_ScriptFullPath))
    try {
      helpMenu.SetIcon(item_name, ".\icons\icons8-minimize-96.png", "1")
    }
  }
  helpMenu.Add()
}

DefaultRun(command,*)
{
  Run(command)
}

;{-----------------------------------------------
; MARK: Loop Though Apps
;{-----------------------------------------------

; Asign HOTKEYS for All Application and Functions
for k, v in app
{
  if not (v.get("ComputerName", A_ComputerName) = A_ComputerName) {
    continue
  }
  if (v.get("Key", "") = "") {
    continue
  }
  for Modifier in SwitchBetweenKey
  {
    Hotkey(Modifier v["Key"], SwitchBetweenWindows.Bind(, v["ID"], v["Name"], v["EXE"]))
  }
  for Modifier in SwitchToKey
  {
    Hotkey(Modifier v["Key"], SwitchToWindow.Bind(, v["ID"], v["EXE"], v.get("TabKey", "")))
  }
  for Modifier in MinimalizeKey
  {
    Hotkey(Modifier v["Key"], MinimizeWindow.Bind(, v["ID"]))
  }
  item_name := "[" v["Key"] "]   :   " v["Name"]
  helpMenu.Add(item_name, SwitchBetweenWindows.Bind("NONE", v["ID"], v["Name"], v["EXE"]))
  if (v.get("icon", "") != "")
    try {
      helpMenu.SetIcon(item_name, GetFullPathName(v["icon"]), "1")
      continue
    }
  try {
    helpMenu.SetIcon(item_name, v["EXE"], "1")
  }
}

; MARK: Help with generated LIST of Apps and KEYs.
;{-----------------------------------------------
; Show Help
Show(this_hotkey) {
  ;Refresh DarkMode state when menu Called.
  LightTheme := RegRead("HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme")
  if !LightTheme {
    SetDarkMode()
  }
  helpMenu.Show()
  return
}

; DARKMODE
SetDarkMode() {
  uxtheme := DllCall("GetModuleHandle", "str", "uxtheme", "ptr")
  SetPreferredAppMode := DllCall("GetProcAddress", "ptr", uxtheme, "ptr", 135, "ptr")
  FlushMenuThemes := DllCall("GetProcAddress", "ptr", uxtheme, "ptr", 136, "ptr")
  DllCall(SetPreferredAppMode, "int", 1) ; Dark
  DllCall(FlushMenuThemes)
}

; MARK: Functions
;{-----------------------------------------------
; ShellRun runs the given program with restricted privileges as the AutoHotkey
; https://www.autohotkey.com/boards/viewtopic.php?p=404418#p404418
ShellRun(prms*)
{
  shellWindows := ComObject("Shell.Application").Windows
  desktop := shellWindows.FindWindowSW(0, 0, 8, 0, 1) ; SWC_DESKTOP, SWFO_NEEDDISPATCH

  ; Retrieve top-level browser object.
  tlb := ComObjQuery(desktop,
    "{4C96BE40-915C-11CF-99D3-00AA004AE837}", ; SID_STopLevelBrowser
    "{000214E2-0000-0000-C000-000000000046}") ; IID_IShellBrowser

  ; IShellBrowser.QueryActiveShellView -> IShellView
  ComCall(15, tlb, "ptr*", sv := ComValue(13, 0)) ; VT_UNKNOWN

  ; Define IID_IDispatch.
  NumPut("int64", 0x20400, "int64", 0x46000000000000C0, IID_IDispatch := Buffer(16))

  ; IShellView.GetItemObject -> IDispatch (object which implements IShellFolderViewDual)
  ComCall(15, sv, "uint", 0, "ptr", IID_IDispatch, "ptr*", sfvd := ComValue(9, 0)) ; VT_DISPATCH

  ; Get Shell object.
  shell := sfvd.Application

  ; IShellDispatch2.ShellExecute
  shell.ShellExecute(prms*)
}

GetFullPathName(path) {
  ;The GetFullPathName function returns the full path to the file based on the specified relative path. The root against which the relative path is interpreted depends on the current working directory in which the script or program is running.
  cc := DllCall("GetFullPathNameW", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint")
  buf := Buffer(cc * 2)
  DllCall("GetFullPathNameW", "str", path, "uint", cc, "ptr", buf, "ptr", 0, "uint")
  return StrGet(buf)
}

; SwitchToWindow switches to the last active window matching the given selector
; or starts the program if it is not yet running. If a matching window is
; already active, the given keystroke is sent.
SwitchToWindow(this_hotkey, Selector, FilePath, KeystrokeIfActive := "")
{
  ; Check if a matching window is open
  if !WinExist(Selector)
  {
    ; If not, run the program
    if (FilePath != "")
      ShellRun(FilePath) ; Run the program as a limited user and not as an administrator
    return
  }

  ; Optionally send a keystroke if the window is alredy in focus
  if WinActive(Selector)
  {
    if (KeystrokeIfActive != "`"`"")
      Send(KeystrokeIfActive)
    return
  }

  ; If a window is currently open but not focussed, activate it
  WinActivate()
}

; SwitchBetweenWindows cycles between all windows matching the given selector.
SwitchBetweenWindows(this_hotkey, Selector, GroupIdentifier, FilePath := "", *)
{
  ; Check if a matching window is open
  if WinExist(Selector)
  {
    GroupName := "MacroWindows_" . GroupIdentifier

    ; Add all matching windows to the same group to allow switching between them
    GroupAdd(GroupName, Selector)

    ; Check if a matching window is in already focus
    if WinActive(Selector)
    {
      ; Switch to the next matching window
      GroupActivate(GroupName, "R")
      return
    }

    ; If a window is currently open but not focused, activate it
    WinActivate(Selector)
    return
  }
  else
  {
    ; If not, run the program
    if (FilePath != "")
      ShellRun(FilePath) ; Run the program as a limited user and not as an administrator
    return
  }
  return
}

; MinimizeWindow minimizes the first window matching the given selector.
MinimizeWindow(this_hotkey, Selector)
{
  WinMinimize(Selector)
}
I hope you find this script as useful as I have. ;)
Last edited by vysmaty on 15 Apr 2024, 02:59, edited 3 times in total.
pgeugene
Posts: 34
Joined: 27 Jun 2019, 04:36

Re: ApplicationSwitcher for v2

14 Apr 2024, 02:32

I tried your script but it is totally not working
Below is what I tried for Notepad and VSCode.But both are not working at all
I opened Notepad and switch to other program.Then I pressed Hotkey(^9) but nothing happened

Code: Select all

app.Push(
  Map(
    "Hotkey", "^9", ; <- Set ME
    "Name", "Notepad",
    "EXE", "C:\Windows\System32\notepad.exe",
    "ID", "ahk_exe Notepad.exe",
    "TabKey", "^{PgDn}"
  )
)

Code: Select all

app.Push(
  Map(
    "Hotkey", "^F12", ; <- Set ME
    "Name", "VSCode",
    "EXE", "C:\Users\hp\Downloads\AutoHotKey\2024\Feb\VSCode-win32-x64-1.86.2\Code.exe",
    "ID", "ahk_exe Code.exe",
    "TabKey", "^{PgDn}"
  )
)

vysmaty
Posts: 11
Joined: 26 Mar 2024, 12:13

Re: ApplicationSwitcher for v2

14 Apr 2024, 07:35

Hello. The script has two parts. In the first one, you set a 3x modifier key (you can choose multiple) for 3 functions (Switch Windows, Switch Tabs, Minimize). In the second part (applications settings, the second key of shortcut (for each application).

That is "^" should be set at "SwitchBetweenKey". And the application should have just 9, F9, F12...

In addition to those 3 functions, there is also an function to display a menu
and an function to close tabs.

I am not now close to the keyboard.
But your problem lies in the modifier settings. By default there is LButton of mouse or Shift+Win... Ctrl+Win, Alt+Win and so on. From this, the script sets hotkeys for each key combination (for each function and each application).

Maybe it could be explained better in the skript comments. If you later can describe it better in English, we would appreciate it.
vysmaty
Posts: 11
Joined: 26 Mar 2024, 12:13

Re: ApplicationSwitcher for v2

14 Apr 2024, 07:47

And when I'm not such a newbie here, and I will be able to edit a post here, I'll rename "Hotkey" for individual applications to "Key" so that it's not so misleading. And I will update the script.
pgeugene
Posts: 34
Joined: 27 Jun 2019, 04:36

Re: ApplicationSwitcher for v2

16 Apr 2024, 03:39

vysmaty wrote:
14 Apr 2024, 07:47
And when I'm not such a newbie here, and I will be able to edit a post here, I'll rename "Hotkey" for individual applications to "Key" so that it's not so misleading. And I will update the script.
Thank you for your clarification.
I am able to use the script now
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

Re: ApplicationSwitcher for v2

18 Apr 2024, 07:58

This is really cool -- Thanks for sharing it!
ste(phen|ve) kunkel
vysmaty
Posts: 11
Joined: 26 Mar 2024, 12:13

Re: ApplicationSwitcher for v2

Yesterday, 10:21

kunkel321 wrote:
18 Apr 2024, 07:58
This is really cool -- Thanks for sharing it!
I'm glad to hear that. Thanks for the response. :)

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 49 guests