mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
Merge pull request #29 from awizemann/claude/issue-26-sidebar-width
Persist sidebar width across launches (#26)
This commit is contained in:
@@ -14,6 +14,7 @@ struct ContentView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationSplitView {
|
NavigationSplitView {
|
||||||
SidebarView()
|
SidebarView()
|
||||||
|
.navigationSplitViewColumnWidth(min: 180, ideal: 240, max: 360)
|
||||||
} detail: {
|
} detail: {
|
||||||
detailView
|
detailView
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|||||||
@@ -59,5 +59,6 @@ struct SidebarView: View {
|
|||||||
}
|
}
|
||||||
.listStyle(.sidebar)
|
.listStyle(.sidebar)
|
||||||
.navigationTitle("Scarf")
|
.navigationTitle("Scarf")
|
||||||
|
.splitViewAutosaveName("ScarfMainSidebar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// Makes the enclosing `NSSplitView` remember its divider positions across
|
||||||
|
/// app launches. `NavigationSplitView` is backed by `NSSplitViewController`,
|
||||||
|
/// whose split view honours `autosaveName` — AppKit writes the divider
|
||||||
|
/// offsets to `UserDefaults` on drag and restores them on the next launch.
|
||||||
|
///
|
||||||
|
/// Usage: attach `.splitViewAutosaveName("…")` to a child of the split view
|
||||||
|
/// (the sidebar is a good choice). The modifier installs an invisible helper
|
||||||
|
/// that walks up the view hierarchy on first layout, finds the `NSSplitView`,
|
||||||
|
/// and assigns its autosave name. Subsequent launches restore the divider
|
||||||
|
/// positions before the window appears.
|
||||||
|
///
|
||||||
|
/// The name is also used to key the entry in `UserDefaults` (AppKit stores
|
||||||
|
/// it as `NSSplitView Subview Frames <name>`), so changing the name resets
|
||||||
|
/// the remembered width. Pick a stable string and leave it alone.
|
||||||
|
struct SplitViewAutosaveFinder: NSViewRepresentable {
|
||||||
|
let autosaveName: String
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> NSView {
|
||||||
|
let view = NSView()
|
||||||
|
// Defer the hierarchy walk until after SwiftUI has attached this
|
||||||
|
// view to its host window — at makeNSView time the view has no
|
||||||
|
// superview yet, so we can't find the split view above us.
|
||||||
|
DispatchQueue.main.async { [weak view] in
|
||||||
|
guard let view else { return }
|
||||||
|
SplitViewAutosaveFinder.apply(autosaveName, startingFrom: view)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSView(_ nsView: NSView, context: Context) {}
|
||||||
|
|
||||||
|
private static func apply(_ name: String, startingFrom view: NSView) {
|
||||||
|
var current: NSView? = view
|
||||||
|
while let node = current {
|
||||||
|
if let split = node as? NSSplitView {
|
||||||
|
// Only set once — reassigning clobbers AppKit's restore path.
|
||||||
|
if split.autosaveName != NSSplitView.AutosaveName(name) {
|
||||||
|
split.autosaveName = NSSplitView.AutosaveName(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current = node.superview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Persist the enclosing `NavigationSplitView` / `NSSplitView` divider
|
||||||
|
/// positions to `UserDefaults` under `autosaveName`. Attach to any child
|
||||||
|
/// of the split view (the sidebar works well).
|
||||||
|
func splitViewAutosaveName(_ autosaveName: String) -> some View {
|
||||||
|
background(SplitViewAutosaveFinder(autosaveName: autosaveName))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user