diff --git a/Source/InputSourceSwitch.spoon/init.lua b/Source/InputSourceSwitch.spoon/init.lua index d3814be4..9d44fa4a 100644 --- a/Source/InputSourceSwitch.spoon/init.lua +++ b/Source/InputSourceSwitch.spoon/init.lua @@ -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() @@ -21,7 +23,7 @@ obj.__index = obj -- Metadata obj.name = "InputSourceSwitch" -obj.version = "1.0" +obj.version = "1.1" obj.author = "eks5115 " obj.homepage = "https://github.com/Hammerspoon/Spoons" obj.license = "MIT - https://opensource.org/licenses/MIT" @@ -52,24 +54,38 @@ 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 @@ -77,18 +93,25 @@ end --- 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) @@ -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