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
4 changes: 4 additions & 0 deletions Input Source Pro.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
D60000622F20000000000062 /* AppURLAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60000612F20000000000061 /* AppURLAction.swift */; };
D60000722F20000000000072 /* AppURLActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60000712F20000000000071 /* AppURLActionTests.swift */; };
D60000822F20000000000082 /* URLActivationSuppressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60000812F20000000000081 /* URLActivationSuppressionTests.swift */; };
D60001922F20000000000192 /* ActivateEventInputSourceChangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60001912F20000000000191 /* ActivateEventInputSourceChangeTests.swift */; };
D60001022F20000000000102 /* AppKindComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60001012F20000000000101 /* AppKindComparisonTests.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -366,6 +367,7 @@
D60000612F20000000000061 /* AppURLAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppURLAction.swift; sourceTree = "<group>"; };
D60000712F20000000000071 /* AppURLActionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppURLActionTests.swift; sourceTree = "<group>"; };
D60000812F20000000000081 /* URLActivationSuppressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLActivationSuppressionTests.swift; sourceTree = "<group>"; };
D60001912F20000000000191 /* ActivateEventInputSourceChangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivateEventInputSourceChangeTests.swift; sourceTree = "<group>"; };
D60001012F20000000000101 /* AppKindComparisonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKindComparisonTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -746,6 +748,7 @@
D60000022F20000000000002 /* TestHarnessTests.swift */,
D60000712F20000000000071 /* AppURLActionTests.swift */,
D60000812F20000000000081 /* URLActivationSuppressionTests.swift */,
D60001912F20000000000191 /* ActivateEventInputSourceChangeTests.swift */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -1066,6 +1069,7 @@
D60000032F20000000000003 /* TestHarnessTests.swift in Sources */,
D60000722F20000000000072 /* AppURLActionTests.swift in Sources */,
D60000822F20000000000082 /* URLActivationSuppressionTests.swift in Sources */,
D60001922F20000000000192 /* ActivateEventInputSourceChangeTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ extension IndicatorWindowController {
let application = app.getApplication(preferencesVM: preferencesVM)

let needActivateAtFirstTime = {
// App switch that keeps the same input source: don't announce it,
// let focus detection decide whether the indicator should be visible.
if event.isAppChangesWithUnchangedInputSource {
return false
}

if preferencesVM.preferences.isActiveWhenSwitchApp {
return true
}
Expand Down
4 changes: 4 additions & 0 deletions Input Source Pro/Controllers/IndicatorWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class IndicatorWindowController: FloatWindowController {
return self.alwaysOnPublisher(event: event, inputSource: inputSource, appKind: appKind)
} else if preferencesVM.needDetectFocusedFieldChanges(app: app) {
return self.autoShowPublisher(event: event, inputSource: inputSource, appKind: appKind)
} else if event.isAppChangesWithUnchangedInputSource {
// App switch that keeps the same input source: nothing switched,
// so don't pop the indicator up.
return self.justHidePublisher()
} else {
return self.autoHidePublisher(event: event, inputSource: inputSource, appKind: appKind)
}
Expand Down
23 changes: 18 additions & 5 deletions Input Source Pro/Models/IndicatorVM+Triggers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ extension IndicatorVM {
enum ActivateEvent {
case justHide
case longMouseDown
case appChanges(current: AppKind?, prev: AppKind?)
case appChanges(current: AppKind?, prev: AppKind?, inputSourceDidChange: Bool)
case inputSourceChanges(InputSource, InputSourceChangeReason)
case functionKeyModeChanges(FKeyMode)

func isAppChangesWithSameAppOrWebsite() -> Bool {
switch self {
case let .appChanges(current, prev):
case let .appChanges(current, prev, _):
return current?.isSameAppOrWebsite(with: prev) == true
case .inputSourceChanges:
return false
Expand All @@ -27,6 +27,15 @@ extension IndicatorVM {
}
}

var isAppChangesWithUnchangedInputSource: Bool {
switch self {
case let .appChanges(_, _, inputSourceDidChange):
return !inputSourceDidChange
default:
return false
}
}

var isJustHide: Bool {
switch self {
case .justHide: return true
Expand Down Expand Up @@ -73,7 +82,11 @@ extension IndicatorVM {
.map { [weak self] previous, current -> ActivateEvent in
if let preferencesVM = self?.preferencesVM {
if previous?.appKind?.getId() != current.appKind?.getId() {
let event = ActivateEvent.appChanges(current: current.appKind, prev: previous?.appKind)
let event = ActivateEvent.appChanges(
current: current.appKind,
prev: previous?.appKind,
inputSourceDidChange: previous?.inputSource.persistentIdentifier != current.inputSource.persistentIdentifier
)

if preferencesVM.preferences.isActiveWhenSwitchApp || preferencesVM.preferences.isActiveWhenFocusedElementChangesEnabled {
if preferencesVM.preferences.isHideWhenSwitchAppWithForceKeyboard {
Expand Down Expand Up @@ -116,8 +129,8 @@ extension IndicatorVM {
extension IndicatorVM.ActivateEvent: @preconcurrency CustomStringConvertible {
var description: String {
switch self {
case let .appChanges(current, prev):
return "appChanges(\(String(describing: current)), \(String(describing: prev))"
case let .appChanges(current, prev, inputSourceDidChange):
return "appChanges(\(String(describing: current)), \(String(describing: prev)), inputSourceDidChange: \(inputSourceDidChange))"
case .inputSourceChanges:
return "inputSourceChanges"
case let .functionKeyModeChanges(mode):
Expand Down
11 changes: 11 additions & 0 deletions Input Source Pro/Models/IndicatorVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,17 @@ extension IndicatorVM {
return state
case let .appChanged(appKind):
if let status = preferencesVM.getAppAutoSwitchKeyboard(appKind) {
// The target keyboard is already active: skip the redundant
// TIS select (and CJKV fix) so nothing "switches", and mark
// the reason as .noChanges so the indicator won't announce it.
if status.inputSource.persistentIdentifier == state.inputSource.persistentIdentifier {
return updateState(
appKind: appKind,
inputSource: state.inputSource,
inputSourceChangeReason: .noChanges
)
}

inputSourceVM.select(inputSource: status.inputSource, app: appKind.getApp())

return updateState(
Expand Down
41 changes: 41 additions & 0 deletions Tests/ActivateEventInputSourceChangeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import AppKit
import XCTest
@testable import Input_Source_Pro

@MainActor
final class ActivateEventInputSourceChangeTests: XCTestCase {
private let app = NSRunningApplication.current

private func appKind() -> AppKind {
.normal(app: app, info: (focusedElement: nil, isFocusOnInputContainer: true))
}

func testAppChangesWithUnchangedInputSourceIsFlagged() {
let event = IndicatorVM.ActivateEvent.appChanges(
current: appKind(),
prev: appKind(),
inputSourceDidChange: false
)

XCTAssertTrue(event.isAppChangesWithUnchangedInputSource)
XCTAssertTrue(event.isAppChangesWithSameAppOrWebsite())
XCTAssertFalse(event.isJustHide)
}

func testAppChangesWithChangedInputSourceIsNotFlagged() {
let event = IndicatorVM.ActivateEvent.appChanges(
current: appKind(),
prev: appKind(),
inputSourceDidChange: true
)

XCTAssertFalse(event.isAppChangesWithUnchangedInputSource)
XCTAssertTrue(event.isAppChangesWithSameAppOrWebsite())
XCTAssertFalse(event.isJustHide)
}

func testNonAppChangeEventsAreNotFlagged() {
XCTAssertFalse(IndicatorVM.ActivateEvent.justHide.isAppChangesWithUnchangedInputSource)
XCTAssertFalse(IndicatorVM.ActivateEvent.longMouseDown.isAppChangesWithUnchangedInputSource)
}
}