Files
dotfiles/hammerspoon/app_toggle.lua

159 lines
4.2 KiB
Lua

--- === app_toggle ===
---
--- A Hammerspoon module for toggling between specified applications using
--- hotkeys.
---
--- This module allows you to bind a hotkey to switch focus between specific
--- applications and show/hide them.
local obj = {}
local function findRunningApp(name, path)
for _, app in ipairs(hs.application.runningApplications()) do
if app:name() == name and (path == nil or path == app:path()) then
return app
end
end
end
local focusTimes = {}
local function focusWatcher(_, eventType, appObject)
if eventType == hs.application.watcher.activated then
focusTimes[appObject:bundleID()] = hs.timer.secondsSinceEpoch()
end
end
local appWatcher = hs.application.watcher.new(focusWatcher)
obj.started = false
function obj:start()
if obj.started then
return
end
appWatcher:start()
obj.started = true
end
function obj:stop()
if not obj.started then
return
end
appWatcher:stop()
obj.started = false
end
--- app_toggle:bind(mods, key, ...)
--- Method
--- Binds a hotkey to toggle between the specified applications.
---
--- Parameters:
--- * mods - A table with the modifiers for the hotkey
--- * key - A string with the key for the hotkey
--- * ... - A list of tables, each containing an application name and an
--- optional path
function obj:bind(mods, key, ...)
local apps = { ... }
if #apps > 1 then
self:start()
end
hs.hotkey.bind(mods, key, self:toggleFn(apps))
end
--- app_toggle:toggleFn(apps)
--- Method
--- Creates and returns a function that toggles between the specified
--- applications via app_toggle:toggle() when called.
---
--- Parameters:
--- * apps - A table containing application configurations. Each configuration
--- is a table with an application name at the first index and an
--- optional path at the second index.
---
--- Returns:
--- * A function that, when called, toggles between the specified applications.
---
--- Example:
--- local toggleApps = obj:toggleFn({{"Firefox"}, {"Safari"}})
--- hs.hotkey.bind({"cmd", "ctrl"}, "b", toggleApps)
---
--- Notes:
--- * The returned function can be used as a callback for hotkey bindings or
--- other event-driven scenarios.
function obj:toggleFn(apps)
return function()
self:toggle(apps)
end
end
--- app_toggle:toggle(apps)
--- Method
--- Toggles focus/visibility specified applications.
---
--- Parameters:
--- * apps - A table containing application configurations. Each configuration
--- is a table with an application name at the first index and an
--- optional path at the second index.
---
--- Notes:
--- * If none of the specified applications are running, the function attempts
--- to launch the first application in the list.
--- * If the most recently focused application in the list is the current
--- frontmost application, it will be hidden. Otherwise, the most recently
--- focused application will be brought to the front.
function obj:toggle(apps)
local runningApps = {}
local mostRecentApp = nil
local mostRecentTime = -1
for _, appInfo in ipairs(apps) do
local name, path = appInfo[1], appInfo[2]
local app = findRunningApp(name, path)
if app then
table.insert(runningApps, app)
local focusTime = focusTimes[app:bundleID()] or 0
if focusTime > mostRecentTime then
mostRecentTime = focusTime
mostRecentApp = app
end
end
end
if #runningApps == 0 then
local app = apps[1]
local status, err = pcall(hs.application.open, app[2] or app[1])
if not status then
hs.alert.show('Failed to open ' .. (app[2] or app[1]) .. ': ' .. err)
end
return
end
if not mostRecentApp then
mostRecentApp = runningApps[1]
end
local frontMostApp = hs.application.frontmostApplication()
if frontMostApp and mostRecentApp == frontMostApp then
return mostRecentApp:hide()
end
return mostRecentApp:activate()
end
--- app_toggle:showAppInfo()
--- Method
--- Shows an alert with information about the frontmost application.
function obj:showAppInfo()
local app = hs.application.frontmostApplication()
hs.alert.show(app:name() .. " (" .. app:bundleID() .. ")")
hs.alert.show(app:path())
hs.alert.show("PID: " .. app:pid())
end
-- the end
return obj