Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 56 additions & 19 deletions Source/InputSourceSwitch.spoon/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
---
--- spoon.InputSourceSwitch:setApplications({
--- ["WeChat"] = "Pinyin - Simplified",
--- ["Mail"] = "ABC"
--- ["Mail"] = "ABC",
--- -- Use application watcher for apps that window filter can't capture
--- ["kitty-quick-access"] = { source = "ABC", watcher = "application" }
--- })
---
--- spoon.InputSourceSwitch:start()
Expand All @@ -21,7 +23,7 @@ obj.__index = obj

-- Metadata
obj.name = "InputSourceSwitch"
obj.version = "1.0"
obj.version = "1.1"
obj.author = "eks5115 <[email protected]>"
obj.homepage = "https://github.com/Hammerspoon/Spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"
Expand Down Expand Up @@ -52,43 +54,64 @@ local function isMethod(methodName)
return false
end

local function setAppInputSource(appName, sourceName, event)
local function switchInputSource(sourceName, appName)
local r = true

if (isLayout(sourceName)) then
r = hs.keycodes.setLayout(sourceName)
elseif isMethod(sourceName) then
r = hs.keycodes.setMethod(sourceName)
else
hs.alert.show(string.format('sourceName: %s is not layout or method', sourceName))
end

if (not r) then
hs.alert.show(string.format('set %s to %s failure', appName, sourceName))
end
end

local function setAppInputSourceWithWindowFilter(appName, sourceName, event)
event = event or hs.window.filter.windowFocused

hs.window.filter.new(appName):subscribe(event, function()
local r = true

if (isLayout(sourceName)) then
r = hs.keycodes.setLayout(sourceName)
elseif isMethod(sourceName) then
r = hs.keycodes.setMethod(sourceName)
else
hs.alert.show(string.format('sourceName: %s is not layout or method', sourceName))
end
switchInputSource(sourceName, appName)
end)
end

if (not r) then
hs.alert.show(string.format('set %s to %s failure', appName, sourceName))
local function setAppInputSourceWithAppWatcher(appName, sourceName)
local watcher = hs.application.watcher.new(function(name, eventType, appObj)
if eventType == hs.application.watcher.activated and name == appName then
switchInputSource(sourceName, appName)
end
end)
end)
watcher:start()
return watcher
end

--- InputSourceSwitch.applicationMap
--- Variable
--- Mapping the application name to the input source
obj.applicationsMap = {}

-- Store application watchers to prevent garbage collection
obj.appWatchers = {}

--- InputSourceSwitch:setApplications()
--- Method
--- Set that mapping the application name to the input source
---
--- Parameters:
--- * applications - A table containing that mapping the application name to the input source
--- key is the application name and value is the input source name
--- key is the application name
--- value can be:
--- - a string: the input source name (uses window filter, default)
--- - a table: { source = "input source name", watcher = "window" | "application" }
--- example:
--- ```
--- {
--- ["WeChat"] = "Pinyin - Simplified",
--- ["Mail"] = "ABC"
--- ["Mail"] = "ABC",
--- ["kitty-quick-access"] = { source = "ABC", watcher = "application" }
--- }
--- ```
function obj:setApplications(applications)
Expand All @@ -104,8 +127,22 @@ end
--- Parameters:
--- * None
function obj:start()
for k,v in pairs(self.applicationsMap) do
setAppInputSource(k, v)
for appName, config in pairs(self.applicationsMap) do
local sourceName, watcherType

if type(config) == "string" then
sourceName = config
watcherType = "window"
else
sourceName = config.source
watcherType = config.watcher or "window"
end

if watcherType == "application" then
self.appWatchers[appName] = setAppInputSourceWithAppWatcher(appName, sourceName)
else
setAppInputSourceWithWindowFilter(appName, sourceName)
end
end
return self
end
Expand Down