@@ -40,6 +40,7 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
4040 private var detailHosting : NSHostingController < AnyView > !
4141 private var inspectorHosting : NSHostingController < AnyView > !
4242 private var hasMaterializedInspector = false
43+ private var baseWindowContentMinSize : NSSize ?
4344
4445 // MARK: - Toolbar
4546
@@ -169,6 +170,94 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
169170 inspectorHosting. rootView = AnyView ( buildInspectorView ( ) )
170171 }
171172
173+ internal struct PaneMinimum {
174+ internal let minimumThickness : CGFloat
175+ internal let isCollapsed : Bool
176+ }
177+
178+ internal static func resolvedContentMinSize(
179+ base: NSSize ,
180+ panes: [ PaneMinimum ] ,
181+ dividerThickness: CGFloat
182+ ) -> NSSize {
183+ let visiblePanes = panes. filter { !$0. isCollapsed }
184+ let paneWidth = visiblePanes. reduce ( CGFloat . zero) { partialResult, pane in
185+ partialResult + max( CGFloat . zero, pane. minimumThickness)
186+ }
187+ let dividerCount = max ( visiblePanes. count - 1 , 0 )
188+ let resolvedWidth = max ( base. width, paneWidth + ( CGFloat ( dividerCount) * dividerThickness) )
189+ return NSSize ( width: resolvedWidth, height: base. height)
190+ }
191+
192+ private func recomputeWindowMinimumSize(
193+ sidebarCollapsed: Bool ? = nil ,
194+ inspectorCollapsed: Bool ? = nil
195+ ) {
196+ guard let window = view. window else { return }
197+
198+ if baseWindowContentMinSize == nil {
199+ baseWindowContentMinSize = window. contentRect ( forFrameRect: NSRect ( origin: . zero, size: window. minSize) ) . size
200+ }
201+ guard let baseWindowContentMinSize else { return }
202+
203+ let resolvedMinSize = Self . resolvedContentMinSize (
204+ base: baseWindowContentMinSize,
205+ panes: [
206+ PaneMinimum (
207+ minimumThickness: sidebarSplitItem? . minimumThickness ?? . zero,
208+ isCollapsed: sidebarCollapsed ?? ( sidebarSplitItem? . isCollapsed ?? true )
209+ ) ,
210+ PaneMinimum (
211+ minimumThickness: detailSplitItem? . minimumThickness ?? . zero,
212+ isCollapsed: detailSplitItem? . isCollapsed ?? false
213+ ) ,
214+ PaneMinimum (
215+ minimumThickness: inspectorSplitItem? . minimumThickness ?? . zero,
216+ isCollapsed: inspectorCollapsed ?? ( inspectorSplitItem? . isCollapsed ?? true )
217+ )
218+ ] ,
219+ dividerThickness: splitView. dividerThickness
220+ )
221+
222+ if window. contentMinSize != resolvedMinSize {
223+ window. contentMinSize = resolvedMinSize
224+ }
225+
226+ let currentContentSize = window. contentRect ( forFrameRect: window. frame) . size
227+ guard currentContentSize. width < resolvedMinSize. width || currentContentSize. height < resolvedMinSize. height else { return }
228+ window. setContentSize ( NSSize (
229+ width: max ( currentContentSize. width, resolvedMinSize. width) ,
230+ height: max ( currentContentSize. height, resolvedMinSize. height)
231+ ) )
232+ }
233+
234+ private func setCollapsed(
235+ _ isCollapsed: Bool ,
236+ for splitItem: NSSplitViewItem ? ,
237+ prepareWindowMinimumSize: ( ( ) -> Void ) ? = nil
238+ ) {
239+ guard let splitItem else { return }
240+
241+ if splitItem. isCollapsed == isCollapsed {
242+ recomputeWindowMinimumSize ( )
243+ return
244+ }
245+
246+ prepareWindowMinimumSize ? ( )
247+
248+ guard view. window? . isVisible == true else {
249+ splitItem. isCollapsed = isCollapsed
250+ recomputeWindowMinimumSize ( )
251+ return
252+ }
253+
254+ NSAnimationContext . runAnimationGroup { _ in
255+ splitItem. animator ( ) . isCollapsed = isCollapsed
256+ } completionHandler: { [ weak self] in
257+ self ? . recomputeWindowMinimumSize ( )
258+ }
259+ }
260+
172261 override func viewWillAppear( ) {
173262 super. viewWillAppear ( )
174263 guard let window = view. window else { return }
@@ -192,6 +281,7 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
192281 }
193282
194283 installObservers ( )
284+ recomputeWindowMinimumSize ( )
195285 }
196286
197287 override func viewDidDisappear( ) {
@@ -257,11 +347,7 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
257347 sessionState = nil
258348 currentSession = nil
259349 sidebarContainer. updateSidebarState ( nil , windowState: nil )
260- if view. window? . isVisible == true {
261- sidebarSplitItem. animator ( ) . isCollapsed = true
262- } else {
263- sidebarSplitItem. isCollapsed = true
264- }
350+ setCollapsed ( true , for: sidebarSplitItem)
265351 }
266352 return
267353 }
@@ -289,10 +375,9 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
289375 }
290376
291377 let collapseSidebar = newSession. driver == nil
292- if view. window? . isVisible == true {
293- sidebarSplitItem. animator ( ) . isCollapsed = collapseSidebar
294- } else {
295- sidebarSplitItem. isCollapsed = collapseSidebar
378+ setCollapsed ( collapseSidebar, for: sidebarSplitItem) { [ weak self] in
379+ guard !collapseSidebar else { return }
380+ self ? . recomputeWindowMinimumSize ( sidebarCollapsed: false )
296381 }
297382 rebuildPanes ( )
298383 }
@@ -450,12 +535,14 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
450535
451536 func showInspector( ) {
452537 materializeInspectorIfNeeded ( )
453- inspectorSplitItem? . animator ( ) . isCollapsed = false
538+ setCollapsed ( false , for: inspectorSplitItem) { [ weak self] in
539+ self ? . recomputeWindowMinimumSize ( inspectorCollapsed: false )
540+ }
454541 UserDefaults . standard. set ( true , forKey: Self . inspectorPresentedKey)
455542 }
456543
457544 func hideInspector( ) {
458- inspectorSplitItem ? . animator ( ) . isCollapsed = true
545+ setCollapsed ( true , for : inspectorSplitItem )
459546 UserDefaults . standard. set ( false , forKey: Self . inspectorPresentedKey)
460547 }
461548
@@ -475,9 +562,11 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
475562
476563 if sidebarSplitItem? . isCollapsed == true {
477564 sidebarState. selectedSidebarTab = tab
478- sidebarSplitItem? . animator ( ) . isCollapsed = false
565+ setCollapsed ( false , for: sidebarSplitItem) { [ weak self] in
566+ self ? . recomputeWindowMinimumSize ( sidebarCollapsed: false )
567+ }
479568 } else if sidebarState. selectedSidebarTab == tab {
480- sidebarSplitItem ? . animator ( ) . isCollapsed = true
569+ setCollapsed ( true , for : sidebarSplitItem )
481570 } else {
482571 sidebarState. selectedSidebarTab = tab
483572 }
0 commit comments