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
10 changes: 10 additions & 0 deletions TablePro/Core/Events/AppEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ final class AppEvents {

let connectionStatusChanged = PassthroughSubject<ConnectionStatusChange, Never>()

let editorSettingsChanged = PassthroughSubject<Void, Never>()

let dataGridSettingsChanged = PassthroughSubject<Void, Never>()

let aiSettingsChanged = PassthroughSubject<Void, Never>()

let terminalSettingsChanged = PassthroughSubject<Void, Never>()

let accessibilityTextSizeChanged = PassthroughSubject<Void, Never>()

private init() {}
}

Expand Down
31 changes: 0 additions & 31 deletions TablePro/Core/Services/Infrastructure/SettingsNotifications.swift

This file was deleted.

15 changes: 6 additions & 9 deletions TablePro/Core/Storage/AppSettingsManager.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AppKit
import Combine
import Foundation
import Observation
import os
Expand Down Expand Up @@ -44,7 +45,7 @@ final class AppSettingsManager {

wordWrap: editor.wordWrap
)
notifyChange(.editorSettingsDidChange)
AppEvents.shared.editorSettingsChanged.send(())
SyncChangeTracker.shared.markDirty(.settings, id: "editor")
}
}
Expand All @@ -64,7 +65,7 @@ final class AppSettingsManager {

storage.saveDataGrid(validated)
DateFormattingService.shared.updateFormat(validated.dateFormat)
notifyChange(.dataGridSettingsDidChange)
AppEvents.shared.dataGridSettingsChanged.send(())
SyncChangeTracker.shared.markDirty(.settings, id: "dataGrid")
}
}
Expand Down Expand Up @@ -106,7 +107,7 @@ final class AppSettingsManager {
didSet {
storage.saveAI(ai)
SyncChangeTracker.shared.markDirty(.settings, id: "ai")
notifyChange(.aiSettingsDidChange)
AppEvents.shared.aiSettingsChanged.send(())
let hadCopilot = oldValue.providers.contains(where: { $0.type == .copilot })
let hasCopilot = ai.providers.contains(where: { $0.type == .copilot })
if hasCopilot != hadCopilot {
Expand All @@ -131,7 +132,7 @@ final class AppSettingsManager {
var terminal: TerminalSettings {
didSet {
storage.saveTerminal(terminal)
notifyChange(.terminalSettingsDidChange)
AppEvents.shared.terminalSettingsChanged.send(())
SyncChangeTracker.shared.markDirty(.settings, id: "terminal")
}
}
Expand Down Expand Up @@ -207,10 +208,6 @@ final class AppSettingsManager {
}
}

private func notifyChange(_ notification: Notification.Name) {
NotificationCenter.default.post(name: notification, object: self)
}

/// Auto-pick the first configured provider as active when nothing is selected.
/// Avoids a "AI suddenly stopped working" upgrade UX when older settings JSON
/// (with multiple providers and no activeProviderID concept) is loaded.
Expand Down Expand Up @@ -240,7 +237,7 @@ final class AppSettingsManager {
lastAccessibilityScale = newScale
Self.logger.debug("Accessibility text size changed, scale: \(newScale, format: .fixed(precision: 2))")
ThemeEngine.shared.reloadFontCaches()
NotificationCenter.default.post(name: .accessibilityTextSizeDidChange, object: self)
AppEvents.shared.accessibilityTextSizeChanged.send(())
}
}
}
Expand Down
18 changes: 7 additions & 11 deletions TablePro/Core/Terminal/TerminalSessionState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// TablePro
//

import Combine
import Foundation
import GhosttyTerminal
import GhosttyTheme
Expand All @@ -24,7 +25,7 @@ final class TerminalSessionState: Identifiable {
var exitCode: Int32 = 0
var error: String?

@ObservationIgnored private var settingsObserver: NSObjectProtocol?
@ObservationIgnored private var settingsCancellable: AnyCancellable?

init(connectionId: UUID, databaseType: DatabaseType) {
self.id = UUID()
Expand All @@ -36,9 +37,6 @@ final class TerminalSessionState: Identifiable {
}

deinit {
if let settingsObserver {
NotificationCenter.default.removeObserver(settingsObserver)
}
// TerminalProcessManager.deinit handles source cancellation, fd close, and child kill
// via nonisolated(unsafe) fields (see Issue 5 fix). Releasing our strong reference
// here triggers that cleanup if no other references remain.
Expand Down Expand Up @@ -151,13 +149,11 @@ final class TerminalSessionState: Identifiable {
}

private func observeSettingsChanges() {
settingsObserver = NotificationCenter.default.addObserver(
forName: .terminalSettingsDidChange,
object: nil,
queue: .main
) { [weak self] _ in
self?.applySettingsToTerminal()
}
settingsCancellable = AppEvents.shared.terminalSettingsChanged
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.applySettingsToTerminal()
}
}

// MARK: - Private
Expand Down
2 changes: 1 addition & 1 deletion TablePro/Theme/ThemeEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ internal final class ThemeEngine {
lastAccessibilityScale = newScale
Self.logger.debug("Accessibility text size changed, scale: \(newScale, format: .fixed(precision: 2))")
reloadFontCaches()
NotificationCenter.default.post(name: .accessibilityTextSizeDidChange, object: self)
AppEvents.shared.accessibilityTextSizeChanged.send(())
}
}
}
Expand Down
51 changes: 18 additions & 33 deletions TablePro/Views/Editor/SQLEditorCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import AppKit
import CodeEditSourceEditor
import CodeEditTextView
import Combine
import Observation
import os

Expand All @@ -30,8 +31,8 @@ final class SQLEditorCoordinator: TextViewCoordinator, TextViewDelegate {
@ObservationIgnored private var aiChatInlineSource: AIChatInlineSource?
@ObservationIgnored private var copilotDocumentSync: CopilotDocumentSync?
@ObservationIgnored private var copilotInlineSource: CopilotInlineSource?
@ObservationIgnored private var editorSettingsObserver: NSObjectProtocol?
@ObservationIgnored private var aiSettingsObserver: NSObjectProtocol?
@ObservationIgnored private var editorSettingsCancellable: AnyCancellable?
@ObservationIgnored private var aiSettingsCancellable: AnyCancellable?
@ObservationIgnored private var windowKeyObserver: NSObjectProtocol?
@ObservationIgnored private var lastInlineSourceKind: InlineSourceKind = .off
/// Debounce work item for frame-change notification to avoid
Expand Down Expand Up @@ -71,27 +72,15 @@ final class SQLEditorCoordinator: TextViewCoordinator, TextViewDelegate {
}

deinit {
if let observer = editorSettingsObserver {
NotificationCenter.default.removeObserver(observer)
}
if let observer = aiSettingsObserver {
NotificationCenter.default.removeObserver(observer)
}
if let observer = windowKeyObserver {
NotificationCenter.default.removeObserver(observer)
}
frameChangeTask?.cancel()
}

private func cleanupMonitors() {
if let observer = editorSettingsObserver {
NotificationCenter.default.removeObserver(observer)
editorSettingsObserver = nil
}
if let observer = aiSettingsObserver {
NotificationCenter.default.removeObserver(observer)
aiSettingsObserver = nil
}
editorSettingsCancellable = nil
aiSettingsCancellable = nil
if let observer = windowKeyObserver {
NotificationCenter.default.removeObserver(observer)
windowKeyObserver = nil
Expand Down Expand Up @@ -450,23 +439,19 @@ final class SQLEditorCoordinator: TextViewCoordinator, TextViewDelegate {
// MARK: - Editor Settings Observer

private func installEditorSettingsObserver(controller: TextViewController) {
editorSettingsObserver = NotificationCenter.default.addObserver(
forName: .editorSettingsDidChange,
object: nil,
queue: .main
) { [weak self, weak controller] _ in
guard let self, let controller else { return }
self.handleVimSettingsChange(controller: controller)
self.handleInlineProviderChange()
self.vimCursorManager?.updatePosition()
}
aiSettingsObserver = NotificationCenter.default.addObserver(
forName: .aiSettingsDidChange,
object: nil,
queue: .main
) { [weak self] _ in
self?.handleInlineProviderChange()
}
editorSettingsCancellable = AppEvents.shared.editorSettingsChanged
.receive(on: RunLoop.main)
.sink { [weak self, weak controller] _ in
guard let self, let controller else { return }
self.handleVimSettingsChange(controller: controller)
self.handleInlineProviderChange()
self.vimCursorManager?.updatePosition()
}
aiSettingsCancellable = AppEvents.shared.aiSettingsChanged
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.handleInlineProviderChange()
}
}

private func handleInlineProviderChange() {
Expand Down
5 changes: 4 additions & 1 deletion TablePro/Views/Editor/SQLEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ struct SQLEditorView: View {
.onChange(of: AppSettingsManager.shared.editor) {
editorConfiguration = Self.makeConfiguration()
}
.onReceive(NotificationCenter.default.publisher(for: .accessibilityTextSizeDidChange)) { _ in
.onReceive(AppEvents.shared.accessibilityTextSizeChanged) { _ in
editorConfiguration = Self.makeConfiguration()
}
.onReceive(AppEvents.shared.themeChanged) { _ in
editorConfiguration = Self.makeConfiguration()
}
.onAppear {
Expand Down
19 changes: 5 additions & 14 deletions TablePro/Views/Results/Cells/DataGridCellRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

import AppKit
import Combine
import Foundation

@MainActor
Expand All @@ -12,27 +13,17 @@ final class DataGridCellRegistry {
weak var textFieldDelegate: NSTextFieldDelegate?

private(set) var nullDisplayString: String
private var settingsObserver: NSObjectProtocol?
private var settingsCancellable: AnyCancellable?

private let rowNumberCellIdentifier = NSUserInterfaceItemIdentifier("RowNumberCellView")

init() {
nullDisplayString = AppSettingsManager.shared.dataGrid.nullDisplay
settingsObserver = NotificationCenter.default.addObserver(
forName: .dataGridSettingsDidChange,
object: nil,
queue: .main
) { [weak self] _ in
Task { @MainActor [weak self] in
settingsCancellable = AppEvents.shared.dataGridSettingsChanged
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.nullDisplayString = AppSettingsManager.shared.dataGrid.nullDisplay
}
}
}

deinit {
if let observer = settingsObserver {
NotificationCenter.default.removeObserver(observer)
}
}

func resolveKind(
Expand Down
17 changes: 4 additions & 13 deletions TablePro/Views/Results/DataGridCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData
let tableRowsController = TableRowsController()
var overlayEditor: CellOverlayEditor?

var settingsObserver: NSObjectProtocol?
var settingsCancellable: AnyCancellable?
var themeCancellable: AnyCancellable?
private var lastDataGridSettings: DataGridSettings

Expand Down Expand Up @@ -136,14 +136,9 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData

observeThemeChanges()

settingsObserver = NotificationCenter.default.addObserver(
forName: .dataGridSettingsDidChange,
object: nil,
queue: .main
) { [weak self] _ in
guard let self else { return }

Task { @MainActor [weak self] in
settingsCancellable = AppEvents.shared.dataGridSettingsChanged
.receive(on: RunLoop.main)
.sink { [weak self] _ in
guard let self, let tableView = self.tableView else { return }
let settings = AppSettingsManager.shared.dataGrid
let prev = self.lastDataGridSettings
Expand Down Expand Up @@ -176,7 +171,6 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData
}
}
}
}
}

func observeThemeChanges() {
Expand Down Expand Up @@ -224,9 +218,6 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData
private(set) var teardownObserver: NSObjectProtocol?

deinit {
if let observer = settingsObserver {
NotificationCenter.default.removeObserver(observer)
}
if let observer = teardownObserver {
NotificationCenter.default.removeObserver(observer)
}
Expand Down
5 changes: 1 addition & 4 deletions TablePro/Views/Results/DataGridView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,7 @@ struct DataGridView: NSViewRepresentable {
static func dismantleNSView(_ nsView: NSScrollView, coordinator: TableViewCoordinator) {
coordinator.overlayEditor?.dismiss(commit: false)
coordinator.persistColumnLayoutToStorage()
if let observer = coordinator.settingsObserver {
NotificationCenter.default.removeObserver(observer)
coordinator.settingsObserver = nil
}
coordinator.settingsCancellable = nil
coordinator.themeCancellable = nil
coordinator.tableRowsController.detach()
}
Expand Down
Loading