@@ -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
@@ -186,6 +187,94 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
186187 inspectorHosting. rootView = AnyView ( buildInspectorView ( ) )
187188 }
188189
190+ internal struct PaneMinimum {
191+ internal let minimumThickness : CGFloat
192+ internal let isCollapsed : Bool
193+ }
194+
195+ internal static func resolvedContentMinSize(
196+ base: NSSize ,
197+ panes: [ PaneMinimum ] ,
198+ dividerThickness: CGFloat
199+ ) -> NSSize {
200+ let visiblePanes = panes. filter { !$0. isCollapsed }
201+ let paneWidth = visiblePanes. reduce ( CGFloat . zero) { partialResult, pane in
202+ partialResult + max( CGFloat . zero, pane. minimumThickness)
203+ }
204+ let dividerCount = max ( visiblePanes. count - 1 , 0 )
205+ let resolvedWidth = max ( base. width, paneWidth + ( CGFloat ( dividerCount) * dividerThickness) )
206+ return NSSize ( width: resolvedWidth, height: base. height)
207+ }
208+
209+ private func recomputeWindowMinimumSize(
210+ sidebarCollapsed: Bool ? = nil ,
211+ inspectorCollapsed: Bool ? = nil
212+ ) {
213+ guard let window = view. window else { return }
214+
215+ if baseWindowContentMinSize == nil {
216+ baseWindowContentMinSize = window. contentRect ( forFrameRect: NSRect ( origin: . zero, size: window. minSize) ) . size
217+ }
218+ guard let baseWindowContentMinSize else { return }
219+
220+ let resolvedMinSize = Self . resolvedContentMinSize (
221+ base: baseWindowContentMinSize,
222+ panes: [
223+ PaneMinimum (
224+ minimumThickness: sidebarSplitItem? . minimumThickness ?? . zero,
225+ isCollapsed: sidebarCollapsed ?? ( sidebarSplitItem? . isCollapsed ?? true )
226+ ) ,
227+ PaneMinimum (
228+ minimumThickness: detailSplitItem? . minimumThickness ?? . zero,
229+ isCollapsed: detailSplitItem? . isCollapsed ?? false
230+ ) ,
231+ PaneMinimum (
232+ minimumThickness: inspectorSplitItem? . minimumThickness ?? . zero,
233+ isCollapsed: inspectorCollapsed ?? ( inspectorSplitItem? . isCollapsed ?? true )
234+ )
235+ ] ,
236+ dividerThickness: splitView. dividerThickness
237+ )
238+
239+ if window. contentMinSize != resolvedMinSize {
240+ window. contentMinSize = resolvedMinSize
241+ }
242+
243+ let currentContentSize = window. contentRect ( forFrameRect: window. frame) . size
244+ guard currentContentSize. width < resolvedMinSize. width || currentContentSize. height < resolvedMinSize. height else { return }
245+ window. setContentSize ( NSSize (
246+ width: max ( currentContentSize. width, resolvedMinSize. width) ,
247+ height: max ( currentContentSize. height, resolvedMinSize. height)
248+ ) )
249+ }
250+
251+ private func setCollapsed(
252+ _ isCollapsed: Bool ,
253+ for splitItem: NSSplitViewItem ? ,
254+ prepareWindowMinimumSize: ( ( ) -> Void ) ? = nil
255+ ) {
256+ guard let splitItem else { return }
257+
258+ if splitItem. isCollapsed == isCollapsed {
259+ recomputeWindowMinimumSize ( )
260+ return
261+ }
262+
263+ prepareWindowMinimumSize ? ( )
264+
265+ guard view. window? . isVisible == true else {
266+ splitItem. isCollapsed = isCollapsed
267+ recomputeWindowMinimumSize ( )
268+ return
269+ }
270+
271+ NSAnimationContext . runAnimationGroup { _ in
272+ splitItem. animator ( ) . isCollapsed = isCollapsed
273+ } completionHandler: { [ weak self] in
274+ self ? . recomputeWindowMinimumSize ( )
275+ }
276+ }
277+
189278 override func viewWillAppear( ) {
190279 super. viewWillAppear ( )
191280 guard let window = view. window else { return }
@@ -209,6 +298,7 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
209298 }
210299
211300 installObservers ( )
301+ recomputeWindowMinimumSize ( )
212302 }
213303
214304 override func viewDidDisappear( ) {
@@ -274,11 +364,7 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
274364 sessionState = nil
275365 currentSession = nil
276366 sidebarContainer. updateSidebarState ( nil , windowState: nil )
277- if view. window? . isVisible == true {
278- sidebarSplitItem. animator ( ) . isCollapsed = true
279- } else {
280- sidebarSplitItem. isCollapsed = true
281- }
367+ setCollapsed ( true , for: sidebarSplitItem)
282368 }
283369 return
284370 }
@@ -306,10 +392,9 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
306392 }
307393
308394 let collapseSidebar = newSession. driver == nil
309- if view. window? . isVisible == true {
310- sidebarSplitItem. animator ( ) . isCollapsed = collapseSidebar
311- } else {
312- sidebarSplitItem. isCollapsed = collapseSidebar
395+ setCollapsed ( collapseSidebar, for: sidebarSplitItem) { [ weak self] in
396+ guard !collapseSidebar else { return }
397+ self ? . recomputeWindowMinimumSize ( sidebarCollapsed: false )
313398 }
314399 rebuildPanes ( )
315400 }
@@ -467,12 +552,14 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
467552
468553 func showInspector( ) {
469554 materializeInspectorIfNeeded ( )
470- inspectorSplitItem? . animator ( ) . isCollapsed = false
555+ setCollapsed ( false , for: inspectorSplitItem) { [ weak self] in
556+ self ? . recomputeWindowMinimumSize ( inspectorCollapsed: false )
557+ }
471558 UserDefaults . standard. set ( true , forKey: Self . inspectorPresentedKey)
472559 }
473560
474561 func hideInspector( ) {
475- inspectorSplitItem ? . animator ( ) . isCollapsed = true
562+ setCollapsed ( true , for : inspectorSplitItem )
476563 UserDefaults . standard. set ( false , forKey: Self . inspectorPresentedKey)
477564 }
478565
@@ -492,9 +579,11 @@ internal final class MainSplitViewController: NSSplitViewController, InspectorVi
492579
493580 if sidebarSplitItem? . isCollapsed == true {
494581 sidebarState. selectedSidebarTab = tab
495- sidebarSplitItem? . animator ( ) . isCollapsed = false
582+ setCollapsed ( false , for: sidebarSplitItem) { [ weak self] in
583+ self ? . recomputeWindowMinimumSize ( sidebarCollapsed: false )
584+ }
496585 } else if sidebarState. selectedSidebarTab == tab {
497- sidebarSplitItem ? . animator ( ) . isCollapsed = true
586+ setCollapsed ( true , for : sidebarSplitItem )
498587 } else {
499588 sidebarState. selectedSidebarTab = tab
500589 }
0 commit comments