|
1 | | --- -- Old app switching config |
2 | | --- local favoriteApps = { |
3 | | --- { name = "goland"}, |
4 | | --- { name = "Firefox"}, |
5 | | --- { name = "alacritty"}, |
6 | | --- { name = "emacs"} |
7 | | --- } |
| 1 | +-- d = hs.application("Dock") |
| 2 | +-- for i=1,9 do |
| 3 | +-- hs.hotkey.bind({"alt"}, tostring(i), function() |
| 4 | +-- iconNumber = i |
| 5 | +-- icon = hs.axuielement.applicationElement(d)[1][iconNumber] |
| 6 | +-- if icon:attributeValue("AXRoleDescription") == "application dock item" then |
| 7 | +-- icon:performAction("AXPress") |
| 8 | +-- else |
| 9 | +-- print ("not an application") |
| 10 | +-- end |
| 11 | +-- end) |
| 12 | +-- end |
8 | 13 |
|
9 | | --- local obj = {} |
10 | | --- obj.currentWindows = {} |
| 14 | +local function launchOrFocusOrRotate(app) |
| 15 | + local focusedWindow = hs.window.focusedWindow() |
| 16 | + -- Output of the above is an hs.window object |
11 | 17 |
|
12 | | --- theWindows = hs.window.filter.new() |
13 | | --- theWindows:setDefaultFilter{} |
14 | | --- theWindows:setSortOrder(hs.window.filter.sortByFocusedLast) |
15 | | --- obj.currentWindows = {} |
16 | | --- obj.previousSelection = nil -- the idea is that one switches back and forth between two windows all the time |
| 18 | + -- I can get the application it belongs to via the :application() method |
| 19 | + -- See https://www.hammerspoon.org/docs/hs.window.html#application |
| 20 | + local focusedWindowApp = focusedWindow:application() |
| 21 | + -- This returns an hs.application object |
17 | 22 |
|
18 | | --- for i,v in ipairs(theWindows:getWindows()) do |
19 | | --- table.insert(obj.currentWindows, v) |
20 | | --- end |
| 23 | + -- Get the name of this application; this isn't really useful fof us as launchOrFocus needs the app name on disk |
| 24 | + -- I do use it below, further on... |
| 25 | + local focusedWindowAppName = focusedWindowApp:name() |
21 | 26 |
|
22 | | --- function obj:focus_by_app(appName) |
23 | | --- print(' [' .. appName ..']') |
24 | | --- for i,v in ipairs(obj.currentWindows) do |
25 | | --- print(' [' .. v:application():name() .. ']') |
26 | | --- if string.find(string.lower(v:application():name()), appName) then |
27 | | --- print("Focusing window" .. v:title()) |
28 | | --- v:focus() |
29 | | --- return v |
30 | | --- end |
31 | | --- end |
32 | | --- return nil |
33 | | --- end |
| 27 | + -- This gives the path - /Applications/<application>.app |
| 28 | + local focusedWindowPath = focusedWindowApp:path() |
34 | 29 |
|
| 30 | + -- I need to extract <application> from that |
| 31 | + local appNameOnDisk = string.gsub(focusedWindowPath, "/Applications/", "") |
| 32 | + local appNameOnDisk = string.gsub(appNameOnDisk, ".app", "") |
| 33 | + -- Finder has this as its path |
| 34 | + local appNameOnDisk = string.gsub(appNameOnDisk, "/System/Library/CoreServices/", "") |
35 | 35 |
|
36 | | --- -- Function to cycle between open Windows of the application |
37 | | --- function cycleWindows(appBundleID) |
38 | | --- local app = hs.application.get(appBundleID) |
39 | | --- -- print(app:name()) |
40 | | --- if app then |
41 | | --- local windows = hs.window.filter.new{app:name()}:getWindows() |
42 | | --- windows[#windows]:focus() |
43 | | --- end |
44 | | --- end |
| 36 | + -- If already focused, try to find the next window |
| 37 | + if focusedWindow and appNameOnDisk == app then |
| 38 | + -- hs.application.get needs the name as per hs.application:name() and not the name on disk |
| 39 | + -- It can also take pid or bundle, but that doesn't help here |
| 40 | + -- Since I have the name already from above, I can use that though |
| 41 | + local appWindows = hs.application.get(focusedWindowAppName):allWindows() |
45 | 42 |
|
46 | | --- -- Function to launch or focus an app |
47 | | --- function launchOrFocusApp(appBundleID) |
48 | | --- -- cycleWindows(appBundleID) |
49 | | --- obj:focus_by_app(appBundleID) |
50 | | --- end |
| 43 | + -- https://www.hammerspoon.org/docs/hs.application.html#allWindows |
| 44 | + -- A table of zero or more hs.window objects owned by the application. From the current space. |
51 | 45 |
|
52 | | --- Bind hotkeys for favorite apps |
53 | | --- for i, app in ipairs(favoriteApps) do |
54 | | --- hs.hotkey.bind({"alt"}, tostring(i), function() launchOrFocusApp(app.name) end) |
55 | | --- end |
| 46 | + if #appWindows > 0 then |
| 47 | + -- It seems that this list order changes after one window get focused, |
| 48 | + -- Let's directly bring the last one to focus every time |
| 49 | + -- https://www.hammerspoon.org/docs/hs.window.html#focus |
| 50 | + if app == "Finder" then |
| 51 | + -- If the app is Finder the window count returned is one more than the actual count, so I subtract |
| 52 | + appWindows[#appWindows - 1]:focus() |
| 53 | + else |
| 54 | + appWindows[#appWindows]:focus() |
| 55 | + end |
| 56 | + else |
| 57 | + -- this should not happen, but just in case |
| 58 | + hs.application.launchOrFocus(app) |
| 59 | + end |
| 60 | + else -- if not focused |
| 61 | + hs.application.launchOrFocus(app) |
| 62 | + end |
| 63 | +end |
| 64 | + |
| 65 | +local ctrlCmdShortcuts = { |
| 66 | + { "1", "IntelliJ IDEA" }, |
| 67 | + { "2", "Ghostty" }, |
| 68 | + { "3", "Vivaldi" }, |
| 69 | + { "4", "Obsidian" }, |
| 70 | + { "5", "Finder" }, |
| 71 | + { "s", "Slack" }, |
| 72 | + { "z", "zoom.us" }, |
| 73 | + { "x", "Postman" }, |
| 74 | + { "a", "Zotero" }, |
| 75 | +} |
56 | 76 |
|
57 | | -d = hs.application("Dock") |
58 | | -for i=1,9 do |
59 | | - hs.hotkey.bind({"alt"}, tostring(i), function() |
60 | | - iconNumber = i |
61 | | - icon = hs.axuielement.applicationElement(d)[1][iconNumber] |
62 | | - if icon:attributeValue("AXRoleDescription") == "application dock item" then |
63 | | - icon:performAction("AXPress") |
64 | | - else |
65 | | - print ("not an application") |
66 | | - end |
67 | | - end) |
| 77 | +for i, shortcut in ipairs(ctrlCmdShortcuts) do |
| 78 | + hs.hotkey.bind({ "alt" }, shortcut[1], function() |
| 79 | + -- hs.application.launchOrFocus(shortcut[2]) |
| 80 | + launchOrFocusOrRotate(shortcut[2]) |
| 81 | + end) |
68 | 82 | end |
69 | 83 |
|
70 | | --- New app switching config from |
71 | | -local hotswitchHs = require("hotswitch-hs/hotswitch-hs") |
72 | | -hotswitchHs.enableAutoUpdate() -- If you don't want to update automatically, remove this line. |
73 | | -hotswitchHs.enableAllSpaceWindows() |
74 | | -hs.hotkey.bind({"command"}, ".", hotswitchHs.openOrClose) -- Set a keybind you like to open HotSwitch-HS panel. |
| 84 | +-- New app switching config from |
| 85 | +-- local hotswitchHs = require("hotswitch-hs/hotswitch-hs") |
| 86 | +-- hotswitchHs.enableAutoUpdate() -- If you don't want to update automatically, remove this line. |
| 87 | +-- hotswitchHs.enableAllSpaceWindows() |
| 88 | +-- hs.hotkey.bind({ "command" }, ".", hotswitchHs.openOrClose) -- Set a keybind you like to open HotSwitch-HS panel. |
75 | 89 |
|
76 | 90 | -- hs.loadSpoon("FnMate") |
77 | 91 |
|
78 | 92 | -- j, k, h and l keys are returned as \n, \v, \b and \f when ctrl is pressed down so we have to modify the if else accordingly |
79 | 93 | -- Also, you have to provide input monitoring access to hammerspoon in macOS for this to work |
80 | 94 | function catcher(event) |
81 | | - if event:getFlags()['ctrl'] then |
82 | | - if event:getCharacters(true) == "h" then |
83 | | - return true, {hs.eventtap.event.newKeyEvent({}, "left", true)} |
84 | | - elseif event:getCharacters(true) == "l" then |
85 | | - return true, {hs.eventtap.event.newKeyEvent({}, "right", true)} |
86 | | - elseif event:getCharacters(true) == "j" then |
87 | | - return true, {hs.eventtap.event.newKeyEvent({}, "down", true)} |
88 | | - elseif event:getCharacters(true) == "k" then |
89 | | - return true, {hs.eventtap.event.newKeyEvent({}, "up", true)} |
90 | | - elseif event:getCharacters(true) == "[" then |
91 | | - return true, {hs.eventtap.event.newKeyEvent({}, "escape", true)} |
92 | | - end |
93 | | - end |
| 95 | + if event:getFlags()["ctrl"] then |
| 96 | + if event:getCharacters(true) == "h" then |
| 97 | + return true, { hs.eventtap.event.newKeyEvent({}, "left", true) } |
| 98 | + elseif event:getCharacters(true) == "l" then |
| 99 | + return true, { hs.eventtap.event.newKeyEvent({}, "right", true) } |
| 100 | + elseif event:getCharacters(true) == "j" then |
| 101 | + return true, { hs.eventtap.event.newKeyEvent({}, "down", true) } |
| 102 | + elseif event:getCharacters(true) == "k" then |
| 103 | + return true, { hs.eventtap.event.newKeyEvent({}, "up", true) } |
| 104 | + elseif event:getCharacters(true) == "[" then |
| 105 | + return true, { hs.eventtap.event.newKeyEvent({}, "escape", true) } |
| 106 | + end |
| 107 | + end |
94 | 108 | end |
95 | | -fn_tapper = hs.eventtap.new({hs.eventtap.event.types.keyDown}, catcher):start() |
| 109 | +fn_tapper = hs.eventtap.new({ hs.eventtap.event.types.keyDown }, catcher):start() |
0 commit comments