Skip to content
Open
Show file tree
Hide file tree
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
39 changes: 35 additions & 4 deletions Input Source Pro/Models/ApplicationVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ final class ApplicationVM: ObservableObject {

let cancelBag = CancelBag()
let preferencesVM: PreferencesVM
private var lastResolvedBrowserAppKinds = [String: AppKind]()

lazy var windowAXNotificationPublisher = ApplicationVM
.createWindowAXNotificationPublisher(preferencesVM: preferencesVM)

init(preferencesVM: PreferencesVM) {
self.preferencesVM = preferencesVM
appKind = .from(NSWorkspace.shared.frontmostApplication, preferencesVM: preferencesVM)
appKind = resolveAppKind(for: NSWorkspace.shared.frontmostApplication)

activateAccessibilitiesForCurrentApp()
watchApplicationChange()
Expand Down Expand Up @@ -59,10 +60,14 @@ extension ApplicationVM {
else { return Empty().eraseToAnyPublisher() }

guard NSApplication.isBrowser(app)
else { return Just(.from(app, preferencesVM: preferencesVM)).eraseToAnyPublisher() }
else {
return Just(app)
.compactMap { [weak self] in self?.resolveAppKind(for: $0) }
.eraseToAnyPublisher()
}

return Timer
.interval(seconds: 1)
.interval(seconds: 0.05)
.prepend(Date())
.compactMap { _ in app.focusedUIElement(preferencesVM: preferencesVM) }
.first()
Expand All @@ -76,7 +81,7 @@ extension ApplicationVM {
.map { event in event.runningApp }
}
.prepend(app)
.compactMap { app -> AppKind? in .from(app, preferencesVM: preferencesVM) }
.compactMap { [weak self] in self?.resolveAppKind(for: $0) }
.eraseToAnyPublisher()
}
.removeDuplicates(by: { $0.isSameAppOrWebsite(with: $1, detectAddressBar: true) })
Expand All @@ -100,4 +105,30 @@ extension ApplicationVM {
.sink { $0.getApp().activateAccessibilities() }
.store(in: cancelBag)
}

private func resolveAppKind(for app: NSRunningApplication?) -> AppKind? {
guard let app else { return nil }

let resolved = AppKind.from(app, preferencesVM: preferencesVM)

if let browserInfo = resolved.getBrowserInfo(),
!browserInfo.isFocusedOnAddressBar,
browserInfo.url != .newtab,
let bundleIdentifier = app.bundleIdentifier
{
lastResolvedBrowserAppKinds[bundleIdentifier] = resolved
return resolved
}

guard preferencesVM.isBrowserAndEnabled(app),
let bundleIdentifier = app.bundleIdentifier,
case .normal = resolved,
let fallback = lastResolvedBrowserAppKinds[bundleIdentifier]
else {
return resolved
}

logger.debug { "Reusing cached browser context for \(bundleIdentifier) while waiting for the focused tab to resolve." }
return fallback
}
}
4 changes: 2 additions & 2 deletions Input Source Pro/Models/IndicatorVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ final class IndicatorVM: ObservableObject {
.compactMap { $0 }
.sink { [weak self] appKind in
guard let self = self else { return }

let app = appKind.getApp()
if self.punctuationService.shouldEnableForApp(app) {
self.logger.debug { "Enabling English punctuation for app: \(app.localizedName ?? app.bundleIdentifier ?? "Unknown")" }
Expand Down Expand Up @@ -253,7 +253,7 @@ extension IndicatorVM {

return updateState(appKind: state.appKind, inputSource: inputSource, inputSourceChangeReason: .system)
case let .switchInputSourceByShortcut(inputSource):
inputSourceVM.select(inputSource: inputSource)
inputSourceVM.select(inputSource: inputSource, allowShortcutFallback: false)

return updateState(appKind: state.appKind, inputSource: inputSource, inputSourceChangeReason: .shortcut)
}
Expand Down
43 changes: 35 additions & 8 deletions Input Source Pro/Models/InputSourceVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ import CombineExt

@MainActor
class InputSourceVM: ObservableObject {
private struct SelectionRequest {
let inputSource: InputSource
let allowShortcutFallback: Bool
}

let preferencesVM: PreferencesVM

private var cancelBag = CancelBag()

private let selectInputSourceSubject = PassthroughSubject<InputSource, Never>()
private let selectInputSourceSubject = PassthroughSubject<SelectionRequest, Never>()

private let inputSourceChangesSubject = PassthroughSubject<Void, Never>()

let inputSourceChangesPublisher: AnyPublisher<InputSource, Never>

init(preferencesVM: PreferencesVM) {
self.preferencesVM = preferencesVM

inputSourceChangesPublisher = inputSourceChangesSubject
.map { _ in InputSource.getCurrentInputSource() }
.removeDuplicates()
Expand All @@ -30,22 +35,44 @@ class InputSourceVM: ObservableObject {
selectInputSourceSubject
.tap { [weak self] in
if let self {
$0.select(useCJKVFix: self.preferencesVM.isUseCJKVFix())
$0.inputSource.select(
useCJKVFix: self.preferencesVM.isUseCJKVFix(),
allowShortcutFallback: $0.allowShortcutFallback
)
}
}
.flatMapLatest({ _ in
Timer
.interval(seconds: 1)
.eraseToAnyPublisher()
Publishers.MergeMany([
Just(())
.eraseToAnyPublisher(),
Timer
.delay(seconds: 0.05)
.mapToVoid()
.eraseToAnyPublisher(),
Timer
.delay(seconds: 0.15)
.mapToVoid()
.eraseToAnyPublisher(),
Timer
.delay(seconds: 0.3)
.mapToVoid()
.eraseToAnyPublisher()
])
.eraseToAnyPublisher()
})
.sink { [weak self] _ in
self?.inputSourceChangesSubject.send(())
}
.store(in: cancelBag)
}

func select(inputSource: InputSource) {
selectInputSourceSubject.send(inputSource)
func select(inputSource: InputSource, allowShortcutFallback: Bool = true) {
selectInputSourceSubject.send(
SelectionRequest(
inputSource: inputSource,
allowShortcutFallback: allowShortcutFallback
)
)
}

private func watchSystemNotification() {
Expand Down
5 changes: 4 additions & 1 deletion Input Source Pro/Utilities/AppKit/AppRuleMenuItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ class AppRuleMenuItem: NSMenuItem {
)
}

inputSource?.select(useCJKVFix: preferencesVM.isUseCJKVFix())
inputSource?.select(
useCJKVFix: preferencesVM.isUseCJKVFix(),
allowShortcutFallback: false
)

watchChanges()
}
Expand Down
5 changes: 4 additions & 1 deletion Input Source Pro/Utilities/AppKit/BrowserRuleMenuItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ class BrowserRuleMenuItem: NSMenuItem {
)
}

inputSource?.select(useCJKVFix: preferencesVM.isUseCJKVFix())
inputSource?.select(
useCJKVFix: preferencesVM.isUseCJKVFix(),
allowShortcutFallback: false
)

watchChanges()
}
Expand Down
8 changes: 6 additions & 2 deletions Input Source Pro/Utilities/InputSource/InputSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ class InputSource {
}()
}

func select(useCJKVFix: Bool) {
InputSourceSwitcher.switchToInputSource(self, useCJKVFix: useCJKVFix)
func select(useCJKVFix: Bool, allowShortcutFallback: Bool = true) {
InputSourceSwitcher.switchToInputSource(
self,
useCJKVFix: useCJKVFix,
allowShortcutFallback: allowShortcutFallback
)
}

private var normalizedInputModeID: String? {
Expand Down
Loading