Skip to content
Closed
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
24 changes: 23 additions & 1 deletion TablePro/Views/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ import SwiftUI
/// Settings tab identifiers for programmatic navigation
enum SettingsTab: String {
case general, appearance, editor, dataGrid, keyboard, history, ai, plugins, sync, license

static let fixedWidth: CGFloat = 720

var preferredHeight: CGFloat {
switch self {
case .general: 380
case .appearance: 500
case .editor: 300
case .dataGrid: 380
case .keyboard: 500
case .history: 320
case .ai: 520
case .plugins: 500
case .sync: 420
case .license: 280
}
}
}

/// Main settings view with tab-based navigation (macOS Settings style)
Expand All @@ -18,6 +35,10 @@ struct SettingsView: View {
@Environment(UpdaterBridge.self) var updaterBridge
@AppStorage("selectedSettingsTab") private var selectedTab: String = SettingsTab.general.rawValue

private var currentTab: SettingsTab {
SettingsTab(rawValue: selectedTab) ?? .general
}

var body: some View {
TabView(selection: $selectedTab) {
GeneralSettingsView(settings: $settingsManager.general, updaterBridge: updaterBridge)
Expand Down Expand Up @@ -81,7 +102,8 @@ struct SettingsView: View {
}
.tag(SettingsTab.license.rawValue)
}
.frame(width: 720, height: 500)
.frame(width: SettingsTab.fixedWidth, height: currentTab.preferredHeight)
.background(SettingsWindowResizer(size: CGSize(width: SettingsTab.fixedWidth, height: currentTab.preferredHeight)))
}
}

Expand Down
39 changes: 39 additions & 0 deletions TablePro/Views/Settings/SettingsWindowResizer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SettingsWindowResizer.swift
// TablePro
//

import AppKit
import SwiftUI

/// Resizes the Settings window height to match the selected tab's preferred size.
/// Width stays fixed to keep all tab items visible. Uses AppKit's
/// `NSWindow.setFrame(_:display:animate:)` for smooth height transitions,
/// keeping the top-left corner pinned (standard macOS preferences behavior).
struct SettingsWindowResizer: NSViewRepresentable {
var size: CGSize

func makeNSView(context: Context) -> NSView {
_SettingsWindowSizeView()
}

func updateNSView(_ nsView: NSView, context: Context) {
guard let window = nsView.window else { return }
let newFrameSize = window.frameRect(forContentRect: NSRect(origin: .zero, size: size)).size
guard window.frame.size != newFrameSize else { return }
// Defer to next run loop tick to avoid reentrant layout during SwiftUI rendering
DispatchQueue.main.async {
var frame = window.frame
frame.origin.y += frame.size.height - newFrameSize.height
frame.size = newFrameSize
window.setFrame(frame, display: true, animate: window.isVisible)
}
}
}

private final class _SettingsWindowSizeView: NSView {
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
needsLayout = true
}
}
Loading