Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class AppServiceContainer {
pointerRingSettings: settings.pointerRingSettings,
pointerIconSettings: settings.pointerIconSettings
)
let keyboardVisualizer = KeyboardVisualizer(store: settings.store)
let keyboardVisualizer = KeyboardVisualizer(settings: settings.keyboardVisualizerSettings)
keyboardVisualizer.activate()
let permissionsService = SystemPermissionsService()
let captureController = CaptureController(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,29 @@ struct KeyboardSettingsPane: View {

SettingsStack {
SettingsSectionView(
title: L10n.KeyboardVisualizer.styleSectionTitle,
subtitle: L10n.KeyboardVisualizer.styleSectionSubtitle
title: L10n.KeyboardVisualizer.generalSectionTitle,
subtitle: L10n.KeyboardVisualizer.generalSectionSubtitle
) {
SettingsControlRow(
title: L10n.KeyboardVisualizer.enabledLabel,
subtitle: L10n.KeyboardVisualizer.enabledSubtitle
) {
Toggle("", isOn: self.$model.isEnabled)
.labelsHidden()
.accessibilityLabel(L10n.KeyboardVisualizer.enabledLabel)
.toggleStyle(.switch)
.controlSize(.small)
}

Divider()

self.stylePicker
.disabled(!self.model.isEnabled)

Divider()

self.baseThemePicker
.disabled(!self.model.isEnabled)
}

if self.model.usesCustomThemePalette {
Expand Down Expand Up @@ -91,6 +106,7 @@ struct KeyboardSettingsPane: View {
selection: self.$model.mouseTheme
)
}
.disabled(!self.model.isEnabled)
}

SettingsSectionView(
Expand All @@ -105,6 +121,7 @@ struct KeyboardSettingsPane: View {
.labelsHidden()
.accessibilityLabel(L10n.KeyboardVisualizer.onlyShowModifiedKeystrokesLabel)
.toggleStyle(.switch)
.controlSize(.small)
}

Divider()
Expand All @@ -117,6 +134,7 @@ struct KeyboardSettingsPane: View {
.labelsHidden()
.accessibilityLabel(L10n.KeyboardVisualizer.showSpecialKeysLabel)
.toggleStyle(.switch)
.controlSize(.small)
}

Divider()
Expand All @@ -129,6 +147,7 @@ struct KeyboardSettingsPane: View {
.labelsHidden()
.accessibilityLabel(L10n.KeyboardVisualizer.showMediaKeyButtonsLabel)
.toggleStyle(.switch)
.controlSize(.small)
}

Divider()
Expand All @@ -141,8 +160,10 @@ struct KeyboardSettingsPane: View {
.labelsHidden()
.accessibilityLabel(L10n.KeyboardVisualizer.showMouseEventsLabel)
.toggleStyle(.switch)
.controlSize(.small)
}
}
.disabled(!self.model.isEnabled)

SettingsSectionView(
title: L10n.KeyboardVisualizer.layoutSectionTitle,
Expand All @@ -166,6 +187,7 @@ struct KeyboardSettingsPane: View {
)
}
}
.disabled(!self.model.isEnabled)

SettingsSectionView(
title: L10n.KeyboardVisualizer.historySectionTitle,
Expand All @@ -190,6 +212,7 @@ struct KeyboardSettingsPane: View {
self.axisPicker
}
}
.disabled(!self.model.isEnabled)

SettingsSectionView(
title: L10n.KeyboardVisualizer.timingSectionTitle,
Expand Down Expand Up @@ -223,6 +246,7 @@ struct KeyboardSettingsPane: View {
)
}
}
.disabled(!self.model.isEnabled)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ final class KeyboardSettingsPaneViewModel: ObservableObject {
let scaleRange: ClosedRange<Double> = 0.5...2.0
let scaleStep: Double = 0.1

@Published var isEnabled: Bool {
didSet { self.settings.isEnabled = self.isEnabled }
}

@Published var stackAxis: KeyboardVisualizerStackAxis {
didSet { self.settings.stackAxis = self.stackAxis }
}
Expand Down Expand Up @@ -118,6 +122,7 @@ final class KeyboardSettingsPaneViewModel: ObservableObject {

init(settings: KeyboardVisualizerSettings) {
self.settings = settings
self.isEnabled = settings.isEnabled
self.stackAxis = settings.stackAxis
self.anchor = settings.anchor
self.maxCount = settings.maxCount
Expand All @@ -143,6 +148,7 @@ final class KeyboardSettingsPaneViewModel: ObservableObject {
var previewIdentity: String {
[
"\(self.style.rawValue)",
"\(self.isEnabled)",
"\(self.theme.rawValue)",
self.legendColorMode.rawValue,
self.customLegendColor.hexString,
Expand All @@ -166,6 +172,7 @@ final class KeyboardSettingsPaneViewModel: ObservableObject {
var previewRenderSettings: KeyboardVisualizerSettings {
let settings = KeyboardVisualizerSettings(store: InMemoryKeyValueStore())
settings.registerDefaults()
settings.isEnabled = self.isEnabled
settings.style = self.style
settings.theme = self.theme
settings.legendColorMode = self.legendColorMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
//

import AppKit
import Combine

final class KeyboardVisualizer {
private static let trackedModifierFlags: NSEvent.ModifierFlags = [.command, .shift, .option, .control, .function]

private let visualizerSettings: KeyboardVisualizerSettings
private let visualizerWindow: KeyboardVisualizerWindow
private let eventCoordinator = KeycapEventCoordinator<KeyboardVisualizerGroupView, KeycapItem>()
private var cancellables = Set<AnyCancellable>()
private var currentModifierFlags: NSEvent.ModifierFlags = []
private var lastModifierFlags: NSEvent.ModifierFlags = []
private var hasPendingGroupBreak = false
Expand All @@ -22,20 +24,34 @@ final class KeyboardVisualizer {
self.init(store: UserDefaultsStore())
}

init(store: KeyValueStore) {
let settings = KeyboardVisualizerSettings(store: store)
convenience init(store: KeyValueStore) {
self.init(settings: KeyboardVisualizerSettings(store: store))
}

init(settings: KeyboardVisualizerSettings) {
self.visualizerSettings = settings
self.visualizerWindow = KeyboardVisualizerWindow(settings: settings)
self.visualizerWindow.onGroupRemoved = { [weak self] group in
self?.eventCoordinator.removeGroup(group)
}
settings.isEnabledChanges
.filter { !$0 }
.sink { [weak self] _ in
self?.clearDisplayState()
}
.store(in: &self.cancellables)
}

func activate() {
self.visualizerWindow.orderFront(nil)
}

func display(_ item: DisplayItem) {
guard self.visualizerSettings.isEnabled else {
self.clearDisplayState()
return
}

if item.kind == .flagsChanged {
self.currentModifierFlags = item.modifierFlags
self.displayModifierPreview(item.modifierFlags)
Expand Down Expand Up @@ -186,6 +202,14 @@ final class KeyboardVisualizer {
self.hasPendingGroupBreak = false
}

private func clearDisplayState() {
self.eventCoordinator.reset()
self.currentModifierFlags = []
self.lastModifierFlags = []
self.hasPendingGroupBreak = false
self.visualizerWindow.removeAllGroups()
}

private var currentTrackedFlags: NSEvent.ModifierFlags {
self.currentModifierFlags.intersection(Self.trackedModifierFlags)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AppKit
import Combine

protocol KeyboardVisualizerSettingsProtocol: AnyObject {
var isEnabled: Bool { get set }
var stackAxis: KeyboardVisualizerStackAxis { get set }
var maxCount: Int { get set }
var fadeDelay: CGFloat { get set }
Expand Down Expand Up @@ -41,8 +42,13 @@ final class KeyboardVisualizerSettings: KeyboardVisualizerSettingsProtocol, HasS
static let maxWindowPadding: CGFloat = Spacing.grid(40)

let store: KeyValueStore
private let isEnabledChangesSubject = PassthroughSubject<Bool, Never>()
private let placementChangesSubject = PassthroughSubject<Void, Never>()

var isEnabledChanges: AnyPublisher<Bool, Never> {
self.isEnabledChangesSubject.eraseToAnyPublisher()
}

var placementChanges: AnyPublisher<Void, Never> {
self.placementChangesSubject.eraseToAnyPublisher()
}
Expand All @@ -55,6 +61,19 @@ final class KeyboardVisualizerSettings: KeyboardVisualizerSettingsProtocol, HasS
self.registerStoredDefaults()
}

/// Whether the keyboard overlay window should render input events.
@Stored(.bool(KeyboardVisualizerSettingsKeys.isEnabled, default: true))
private var storedIsEnabled: Bool

var isEnabled: Bool {
get { self.storedIsEnabled }
set {
guard newValue != self.storedIsEnabled else { return }
self.storedIsEnabled = newValue
self.isEnabledChangesSubject.send(newValue)
}
}

@Stored(.custom(
key: KeyboardVisualizerSettingsKeys.axis,
default: .vertical,
Expand Down Expand Up @@ -265,4 +284,5 @@ final class KeyboardVisualizerSettings: KeyboardVisualizerSettingsProtocol, HasS
return self.customLegendColor
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum KeyboardVisualizerSettingsKeys {
static let defaultMaxCount = 1
static let minMaxCount = 1

static let isEnabled = "keyboard_visualizer.isEnabled"
static let axis = "keyboard_visualizer.direction"
static let maxCount = "keyboard_visualizer.maxCount"
static let fadeDelay = "keyboard_visualizer.fadeDelay"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,20 @@ final class KeyboardVisualizerWindow: NSWindow {
self.scheduleFadeOut(for: groupView)
}

func removeAllGroups() {
let removedGroups = self.groupViews
self.groupViews.removeAll()
removedGroups.forEach { groupView in
groupView.removeFromSuperview()
self.onGroupRemoved?(groupView)
}
self.layoutGroups()
}

private func scheduleFadeOut(for groupView: KeyboardVisualizerGroupView) {
groupView.scheduleFadeOut { [weak self, weak groupView] in
guard let self, let groupView else { return }
guard self.groupViews.contains(where: { $0 === groupView }) else { return }
self.groupViews.removeAll { $0 === groupView }
groupView.removeFromSuperview()
self.onGroupRemoved?(groupView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
/* Keyboard visualizer settings pane */
"keyboard_visualizer.live_overlay_section_title" = "Vorschau";
"keyboard_visualizer.live_overlay_section_subtitle" = "Diese Vorschau verwendet die aktuellen Einstellungen der Tastaturvisualisierung.";
"keyboard_visualizer.style_section_title" = "Stil";
"keyboard_visualizer.style_section_subtitle" = "Wähle die allgemeine Form und Oberfläche der Tastenkappen.";
"keyboard_visualizer.general_section_title" = "Allgemein";
"keyboard_visualizer.general_section_subtitle" = "Steuere Sichtbarkeit und Darstellung der Tastatureinblendung.";
"keyboard_visualizer.theme_section_title" = "Thema";
"keyboard_visualizer.theme_section_subtitle" = "Weise jedem Tastentyp eine eigene Farbpalette zu.";
"keyboard_visualizer.layout_section_title" = "Layout";
Expand All @@ -79,6 +79,8 @@
"keyboard_visualizer.timing_section_subtitle" = "Lege fest, wie lange Tasten sichtbar bleiben und wie schnell sie ausgeblendet werden.";
"keyboard_visualizer.filter_section_title" = "Filter";
"keyboard_visualizer.filter_section_subtitle" = "Wähle aus, welche Tastaturereignisse in der Einblendung erscheinen.";
"keyboard_visualizer.enabled_label" = "Aktiviert";
"keyboard_visualizer.enabled_subtitle" = "Sichtbarkeit der Tastatureinblendung.";
"keyboard_visualizer.axis_label" = "Achse";
"keyboard_visualizer.axis_subtitle" = "Richtung, in der aufeinanderfolgende Tastengruppen gestapelt werden.";
"keyboard_visualizer.anchor_label" = "Position";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
/* Keyboard visualizer settings pane */
"keyboard_visualizer.live_overlay_section_title" = "Live Overlay";
"keyboard_visualizer.live_overlay_section_subtitle" = "This is a real renderer-backed sample using the current keyboard visualizer settings.";
"keyboard_visualizer.style_section_title" = "Style";
"keyboard_visualizer.style_section_subtitle" = "Choose the overall keycap construction and finish.";
"keyboard_visualizer.general_section_title" = "General";
"keyboard_visualizer.general_section_subtitle" = "Control keyboard overlay visibility and appearance.";
"keyboard_visualizer.theme_section_title" = "Theme";
"keyboard_visualizer.theme_section_subtitle" = "Give each key type its own color palette.";
"keyboard_visualizer.layout_section_title" = "Layout";
Expand All @@ -79,6 +79,8 @@
"keyboard_visualizer.timing_section_subtitle" = "Balance readability and responsiveness by controlling how long keys remain visible and how quickly they fade.";
"keyboard_visualizer.filter_section_title" = "Filter";
"keyboard_visualizer.filter_section_subtitle" = "Choose which keyboard events appear in the overlay.";
"keyboard_visualizer.enabled_label" = "Enabled";
"keyboard_visualizer.enabled_subtitle" = "Keyboard overlay visibility.";
"keyboard_visualizer.axis_label" = "Axis";
"keyboard_visualizer.axis_subtitle" = "Direction used to stack consecutive key groups.";
"keyboard_visualizer.anchor_label" = "Position";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
/* Keyboard visualizer settings pane */
"keyboard_visualizer.live_overlay_section_title" = "Vista previa";
"keyboard_visualizer.live_overlay_section_subtitle" = "Esta muestra utiliza el renderizador y los ajustes actuales del visualizador del teclado.";
"keyboard_visualizer.style_section_title" = "Estilo";
"keyboard_visualizer.style_section_subtitle" = "Elige el diseño y el acabado general de las teclas.";
"keyboard_visualizer.general_section_title" = "General";
"keyboard_visualizer.general_section_subtitle" = "Controla la visibilidad y la apariencia de la superposición del teclado.";
"keyboard_visualizer.theme_section_title" = "Tema";
"keyboard_visualizer.theme_section_subtitle" = "Asigna una paleta de colores distinta a cada tipo de tecla.";
"keyboard_visualizer.layout_section_title" = "Disposición";
Expand All @@ -79,6 +79,8 @@
"keyboard_visualizer.timing_section_subtitle" = "Ajusta cuánto tiempo permanecen visibles las teclas y la velocidad con la que se desvanecen.";
"keyboard_visualizer.filter_section_title" = "Filtro";
"keyboard_visualizer.filter_section_subtitle" = "Elige qué eventos del teclado aparecen en la superposición.";
"keyboard_visualizer.enabled_label" = "Activado";
"keyboard_visualizer.enabled_subtitle" = "Visibilidad de la superposición del teclado.";
"keyboard_visualizer.axis_label" = "Eje";
"keyboard_visualizer.axis_subtitle" = "Dirección en la que se apilan los grupos de teclas consecutivos.";
"keyboard_visualizer.anchor_label" = "Posición";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
/* Keyboard visualizer settings pane */
"keyboard_visualizer.live_overlay_section_title" = "Aperçu en direct";
"keyboard_visualizer.live_overlay_section_subtitle" = "Cet aperçu utilise le moteur de rendu et les réglages actuels de visualisation du clavier.";
"keyboard_visualizer.style_section_title" = "Style";
"keyboard_visualizer.style_section_subtitle" = "Choisissez la forme et la finition générales des touches.";
"keyboard_visualizer.general_section_title" = "Général";
"keyboard_visualizer.general_section_subtitle" = "Contrôlez la visibilité et l’apparence de la superposition du clavier.";
"keyboard_visualizer.theme_section_title" = "Thème";
"keyboard_visualizer.theme_section_subtitle" = "Attribuez une palette de couleurs différente à chaque type de touche.";
"keyboard_visualizer.layout_section_title" = "Disposition";
Expand All @@ -79,6 +79,8 @@
"keyboard_visualizer.timing_section_subtitle" = "Réglez la durée d’affichage des touches et la vitesse de leur disparition.";
"keyboard_visualizer.filter_section_title" = "Filtre";
"keyboard_visualizer.filter_section_subtitle" = "Choisissez les événements du clavier à afficher dans la superposition.";
"keyboard_visualizer.enabled_label" = "Activé";
"keyboard_visualizer.enabled_subtitle" = "Visibilité de la superposition du clavier.";
"keyboard_visualizer.axis_label" = "Axe";
"keyboard_visualizer.axis_subtitle" = "Direction d’empilement des groupes de touches successifs.";
"keyboard_visualizer.anchor_label" = "Position";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
/* Keyboard visualizer settings pane */
"keyboard_visualizer.live_overlay_section_title" = "ライブプレビュー";
"keyboard_visualizer.live_overlay_section_subtitle" = "現在のキーボード表示設定を使用した実際の表示例です。";
"keyboard_visualizer.style_section_title" = "スタイル";
"keyboard_visualizer.style_section_subtitle" = "キーの全体的な形状と仕上げを選択します。";
"keyboard_visualizer.general_section_title" = "一般";
"keyboard_visualizer.general_section_subtitle" = "キーボードオーバーレイの表示と外観を設定します。";
"keyboard_visualizer.theme_section_title" = "テーマ";
"keyboard_visualizer.theme_section_subtitle" = "キーの種類ごとにカラーパレットを設定します。";
"keyboard_visualizer.layout_section_title" = "レイアウト";
Expand All @@ -79,6 +79,8 @@
"keyboard_visualizer.timing_section_subtitle" = "キーを表示する時間とフェードアウトの速さを設定します。";
"keyboard_visualizer.filter_section_title" = "フィルタ";
"keyboard_visualizer.filter_section_subtitle" = "オーバーレイに表示するキーボードイベントを選択します。";
"keyboard_visualizer.enabled_label" = "有効";
"keyboard_visualizer.enabled_subtitle" = "キーボードオーバーレイの表示。";
"keyboard_visualizer.axis_label" = "方向";
"keyboard_visualizer.axis_subtitle" = "連続するキーグループを並べる方向です。";
"keyboard_visualizer.anchor_label" = "位置";
Expand Down
Loading
Loading