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 @@ -35,4 +35,12 @@ final class AppSettingsContainer {
self.shortcutSettings.registerDefaults()
self.keyboardVisualizerSettings.registerDefaults()
}

func resetAllSettingsToDefaults() {
self.appSettings.resetToDefaults()
self.pointerRingSettings.resetToDefaults()
self.pointerIconSettings.resetToDefaults()
self.shortcutSettings.resetToDefaults()
self.keyboardVisualizerSettings.resetToDefaults()
}
}
25 changes: 5 additions & 20 deletions Apps/Keyty/Sources/Keyty/App/Composition/AppUIContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,15 @@ final class AppUIContainer {
self.permissionsOnboardingWindowController = PermissionsOnboardingWindowController(
permissionsService: services.permissionsService
)
let settingsWindowController = SettingsWindowController(
let settingsContext = SettingsContext(
settings: settings,
shortcutManager: services.shortcutManager,
appSettings: settings.appSettings,
pointerRingVisualizer: services.pointerVisualizersManager.ring,
pointerRingSettings: settings.pointerRingSettings,
pointerIconSettings: settings.pointerIconSettings,
keyboardVisualizerSettings: settings.keyboardVisualizerSettings,
startSettingKeyboardVisualizerPosition: { [weak keyboardVisualizerPlacementWindowController] onPlacementChanged in
keyboardVisualizerPlacementWindowController?.startSettingPosition(onPlacementChanged: onPlacementChanged)
},
stopSettingKeyboardVisualizerPosition: { [weak keyboardVisualizerPlacementWindowController] in
keyboardVisualizerPlacementWindowController?.stopSettingPosition()
},
permissionsService: services.permissionsService,
updater: updater
updater: updater,
placementCoordinator: keyboardVisualizerPlacementWindowController
)
settingsWindowController.onClose = { [weak keyboardVisualizerPlacementWindowController, keyboardVisualizerSettings = settings.keyboardVisualizerSettings] in
guard let placement = keyboardVisualizerPlacementWindowController?.stopSettingPosition() else { return }
keyboardVisualizerSettings.applyCustomPlacement(
screenID: placement.screenID,
normalizedX: placement.positionX,
normalizedY: placement.positionY
)
}
let settingsWindowController = SettingsWindowController(context: settingsContext)
self.settingsWindowController = settingsWindowController
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import SwiftUI
final class DisplaysSettingsPaneViewModel: ObservableObject {
private let screensService: any ScreenServiceProvider
private let keyboardVisualizerSettings: KeyboardVisualizerSettings
private let startSettingKeyboardVisualizerPosition: @MainActor (@escaping KeyboardVisualizerPlacementWindowController.PlacementChangeHandler) -> Void
private let stopSettingKeyboardVisualizerPosition: @MainActor () -> KeyboardVisualizerPlacementWindowController.Placement?
private let placementCoordinator: any KeyboardVisualizerPlacementCoordinating
private var cancellables = Set<AnyCancellable>()

let paddingRange: ClosedRange<Double> = Double(KeyboardVisualizerSettings.minWindowPadding)...Double(KeyboardVisualizerSettings.maxWindowPadding)
Expand Down Expand Up @@ -94,8 +93,7 @@ final class DisplaysSettingsPaneViewModel: ObservableObject {
init(
screensService: any ScreenServiceProvider = ScreensService.shared,
keyboardVisualizerSettings: KeyboardVisualizerSettings = KeyboardVisualizerSettings(),
startSettingKeyboardVisualizerPosition: @escaping @MainActor (@escaping KeyboardVisualizerPlacementWindowController.PlacementChangeHandler) -> Void = { _ in },
stopSettingKeyboardVisualizerPosition: @escaping @MainActor () -> KeyboardVisualizerPlacementWindowController.Placement? = { nil }
placementCoordinator: any KeyboardVisualizerPlacementCoordinating
) {
guard let selectedScreen = Self.initialSelectedScreen(
screensService: screensService,
Expand All @@ -106,8 +104,7 @@ final class DisplaysSettingsPaneViewModel: ObservableObject {

self.screensService = screensService
self.keyboardVisualizerSettings = keyboardVisualizerSettings
self.startSettingKeyboardVisualizerPosition = startSettingKeyboardVisualizerPosition
self.stopSettingKeyboardVisualizerPosition = stopSettingKeyboardVisualizerPosition
self.placementCoordinator = placementCoordinator
self.screens = screensService.screens
self.selectedScreen = selectedScreen
self.selectedAnchor = keyboardVisualizerSettings.anchor
Expand Down Expand Up @@ -143,7 +140,7 @@ final class DisplaysSettingsPaneViewModel: ObservableObject {
if self.isSettingCustomPosition {
self.stopCustomPositionSetting()
} else {
self.startSettingKeyboardVisualizerPosition { [weak self] placement in
self.placementCoordinator.startSettingPosition { [weak self] placement in
self?.applyPlacement(placement)
}
self.isSettingCustomPosition = true
Expand Down Expand Up @@ -176,7 +173,7 @@ extension DisplaysSettingsPaneViewModel {
private extension DisplaysSettingsPaneViewModel {
func stopCustomPositionSetting() {
guard self.isSettingCustomPosition else { return }
if let placement = self.stopSettingKeyboardVisualizerPosition() {
if let placement = self.placementCoordinator.stopSettingPosition() {
self.applyPlacement(placement)
}
self.isSettingCustomPosition = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import SwiftUI

struct GeneralSettingsPane: View {
@StateObject private var model: GeneralSettingsPaneViewModel
@State private var isShowingResetConfirmation = false

init(shortcutManager: ShortcutManager, appSettings: any AppSettingsProtocol) {
init(
shortcutManager: ShortcutManager,
appSettings: any AppSettingsProtocol,
onResetAllSettingsToDefaults: @escaping @MainActor () -> Void
) {
_model = StateObject(wrappedValue: GeneralSettingsPaneViewModel(
shortcutManager: shortcutManager,
appSettings: appSettings
appSettings: appSettings,
onResetAllSettingsToDefaults: onResetAllSettingsToDefaults
))
}

Expand Down Expand Up @@ -53,6 +59,29 @@ struct GeneralSettingsPane: View {
}
}
}

SettingsSectionView(title: L10n.General.settingsSectionTitle) {
SettingsControlRow(
title: L10n.General.resetAllSettingsTitle,
subtitle: L10n.General.resetAllSettingsSubtitle
) {
Button(L10n.General.resetAllSettingsButton) {
self.isShowingResetConfirmation = true
}
.buttonStyle(.bordered)
.controlSize(.regular)
}
}
}
.alert(isPresented: self.$isShowingResetConfirmation) {
Alert(
title: Text(L10n.General.resetAllSettingsConfirmationTitle),
message: Text(L10n.General.resetAllSettingsConfirmationMessage),
primaryButton: .destructive(Text(L10n.General.resetAllSettingsConfirmationButton)) {
self.model.resetAllSettingsToDefaults()
},
secondaryButton: .cancel(Text(L10n.General.resetAllSettingsCancelButton))
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,34 @@ final class GeneralSettingsPaneViewModel: ObservableObject {
let shortcutManager: ShortcutManager

private let appSettings: any AppSettingsProtocol
private let onResetAllSettingsToDefaults: @MainActor () -> Void

@Published var visibleAtLaunch: Bool {
didSet { self.appSettings.visibleAtLaunch = self.visibleAtLaunch }
}

@Published var shortcutValidationMessage: String?

init(shortcutManager: ShortcutManager, appSettings: any AppSettingsProtocol) {
init(
shortcutManager: ShortcutManager,
appSettings: any AppSettingsProtocol,
onResetAllSettingsToDefaults: @escaping @MainActor () -> Void
) {
self.shortcutManager = shortcutManager
self.appSettings = appSettings
self.onResetAllSettingsToDefaults = onResetAllSettingsToDefaults

self.visibleAtLaunch = self.appSettings.visibleAtLaunch
self.shortcutValidationMessage = self.shortcutManager.shortcutValidationMessage
self.shortcutManager.onShortcutValidationMessageChanged = { [weak self] message in
self?.shortcutValidationMessage = message
}
}

@MainActor
func resetAllSettingsToDefaults() {
self.onResetAllSettingsToDefaults()
self.visibleAtLaunch = self.appSettings.visibleAtLaunch
self.shortcutValidationMessage = self.shortcutManager.shortcutValidationMessage
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// KeyboardVisualizerPlacementCoordinating.swift
// Keyty
//
// SPDX-FileCopyrightText: 2026 Serhii Bykov
// SPDX-License-Identifier: BSD-3-Clause
//

import Foundation

@MainActor
protocol KeyboardVisualizerPlacementCoordinating: AnyObject {
func startSettingPosition(
onPlacementChanged: @escaping KeyboardVisualizerPlacementWindowController.PlacementChangeHandler
)

func stopSettingPosition() -> KeyboardVisualizerPlacementWindowController.Placement?
}
44 changes: 44 additions & 0 deletions Apps/Keyty/Sources/Keyty/Features/Settings/SettingsContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// SettingsContext.swift
// Keyty
//
// SPDX-FileCopyrightText: 2026 Serhii Bykov
// SPDX-License-Identifier: BSD-3-Clause
//

import Sparkle

@MainActor
final class SettingsContext {
let settings: AppSettingsContainer
let shortcutManager: ShortcutManager
let pointerRingVisualizer: PointerRingVisualizer
let permissionsService: any PermissionsService
let updater: SPUUpdater
let placementCoordinator: any KeyboardVisualizerPlacementCoordinating

var appSettings: any AppSettingsProtocol { self.settings.appSettings }
var pointerRingSettings: any PointerRingSettingsProtocol { self.settings.pointerRingSettings }
var pointerIconSettings: any PointerIconSettingsProtocol { self.settings.pointerIconSettings }
var keyboardVisualizerSettings: KeyboardVisualizerSettings { self.settings.keyboardVisualizerSettings }

init(
settings: AppSettingsContainer,
shortcutManager: ShortcutManager,
pointerRingVisualizer: PointerRingVisualizer,
permissionsService: any PermissionsService,
updater: SPUUpdater,
placementCoordinator: any KeyboardVisualizerPlacementCoordinating
) {
self.settings = settings
self.shortcutManager = shortcutManager
self.pointerRingVisualizer = pointerRingVisualizer
self.permissionsService = permissionsService
self.updater = updater
self.placementCoordinator = placementCoordinator
}

func resetAllSettingsToDefaults() {
self.settings.resetAllSettingsToDefaults()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,8 @@ struct SettingsPaneEntry: Identifiable {
@MainActor
struct SettingsPaneRegistry {
let entries: [SettingsPaneEntry]
private let displaysViewModel: DisplaysSettingsPaneViewModel

init(
shortcutManager: ShortcutManager,
appSettings: any AppSettingsProtocol,
pointerRingVisualizer: PointerRingVisualizer,
pointerRingSettings: any PointerRingSettingsProtocol,
pointerIconSettings: any PointerIconSettingsProtocol,
keyboardVisualizerSettings: KeyboardVisualizerSettings,
startSettingKeyboardVisualizerPosition: @escaping @MainActor (@escaping KeyboardVisualizerPlacementWindowController.PlacementChangeHandler) -> Void,
stopSettingKeyboardVisualizerPosition: @escaping @MainActor () -> KeyboardVisualizerPlacementWindowController.Placement?,
permissionsService: any PermissionsService,
updater: SPUUpdater
) {
let displaysViewModel = DisplaysSettingsPaneViewModel(
keyboardVisualizerSettings: keyboardVisualizerSettings,
startSettingKeyboardVisualizerPosition: startSettingKeyboardVisualizerPosition,
stopSettingKeyboardVisualizerPosition: stopSettingKeyboardVisualizerPosition
)
self.displaysViewModel = displaysViewModel

init(context: SettingsContext, displaysViewModel: DisplaysSettingsPaneViewModel) {
self.entries = [
SettingsPaneEntry(
id: .general,
Expand All @@ -48,8 +29,11 @@ struct SettingsPaneRegistry {
makeView: {
AnyView(
GeneralSettingsPane(
shortcutManager: shortcutManager,
appSettings: appSettings
shortcutManager: context.shortcutManager,
appSettings: context.appSettings,
onResetAllSettingsToDefaults: {
context.resetAllSettingsToDefaults()
}
)
)
}
Expand All @@ -59,7 +43,7 @@ struct SettingsPaneRegistry {
title: SettingsPaneIdentifier.keyboard.label,
systemImageName: SettingsPaneIdentifier.keyboard.sfSymbolName,
makeView: {
AnyView(KeyboardSettingsPane(settings: keyboardVisualizerSettings))
AnyView(KeyboardSettingsPane(settings: context.keyboardVisualizerSettings))
}
),
SettingsPaneEntry(
Expand All @@ -69,9 +53,9 @@ struct SettingsPaneRegistry {
makeView: {
AnyView(
MouseSettingsPane(
pointerRingVisualizer: pointerRingVisualizer,
pointerRingSettings: pointerRingSettings,
pointerIconSettings: pointerIconSettings
pointerRingVisualizer: context.pointerRingVisualizer,
pointerRingSettings: context.pointerRingSettings,
pointerIconSettings: context.pointerIconSettings
)
)
}
Expand All @@ -93,15 +77,15 @@ struct SettingsPaneRegistry {
title: SettingsPaneIdentifier.permissions.label,
systemImageName: SettingsPaneIdentifier.permissions.sfSymbolName,
makeView: {
AnyView(PermissionsSettingsPane(permissionsService: permissionsService))
AnyView(PermissionsSettingsPane(permissionsService: context.permissionsService))
}
),
SettingsPaneEntry(
id: .update,
title: SettingsPaneIdentifier.update.label,
systemImageName: SettingsPaneIdentifier.update.sfSymbolName,
makeView: {
AnyView(UpdateSettingsPane(updater: updater))
AnyView(UpdateSettingsPane(updater: context.updater))
}
),
SettingsPaneEntry(
Expand All @@ -118,8 +102,4 @@ struct SettingsPaneRegistry {
func entry(for identifier: SettingsPaneIdentifier) -> SettingsPaneEntry? {
self.entries.first { $0.id == identifier }
}

func finishTransientWork() {
self.displaysViewModel.finishCustomPositionSetting()
}
}
Loading
Loading