11// Copyright (c) Wiesław Šoltés. All rights reserved.
22// Licensed under the MIT license. See LICENSE file in the project root for details.
33using System ;
4+ using System . Linq ;
45using Avalonia ;
56using Avalonia . Controls ;
67using Avalonia . Controls . Metadata ;
@@ -23,6 +24,8 @@ namespace Dock.Avalonia.Controls;
2324[ TemplatePart ( "PART_PinnedDockSplitter" , typeof ( GridSplitter ) ) ]
2425public class PinnedDockControl : TemplatedControl
2526{
27+ private const double BoundsEpsilon = 0.5 ;
28+
2629 /// <summary>
2730 /// Define the <see cref="PinnedDockAlignment"/> property.
2831 /// </summary>
@@ -42,6 +45,10 @@ public Alignment PinnedDockAlignment
4245 private GridSplitter ? _pinnedDockSplitter ;
4346 private PinnedDockWindow ? _window ;
4447 private Window ? _ownerWindow ;
48+ private IDockable ? _lastPinnedDockable ;
49+ private double _lastPinnedWidth = double . NaN ;
50+ private double _lastPinnedHeight = double . NaN ;
51+ private bool _isResizingPinnedDock ;
4552
4653 static PinnedDockControl ( )
4754 {
@@ -109,28 +116,35 @@ private void UpdateGrid()
109116 default :
110117 throw new ArgumentOutOfRangeException ( ) ;
111118 }
119+
120+ ApplyPinnedDockSize ( ) ;
112121 }
113122
114123 /// <inheritdoc/>
115124 protected override void OnApplyTemplate ( TemplateAppliedEventArgs e )
116125 {
117126 base . OnApplyTemplate ( e ) ;
127+ LayoutUpdated -= OnLayoutUpdated ;
128+ this . AttachedToVisualTree -= OnAttached ;
129+ this . DetachedFromVisualTree -= OnDetached ;
130+ DetachSplitterHandlers ( ) ;
118131 _pinnedDockGrid = e . NameScope . Get < Grid > ( "PART_PinnedDockGrid" ) ;
119132 _pinnedDock = e . NameScope . Get < ContentControl > ( "PART_PinnedDock" ) ;
120133 _pinnedDockSplitter = e . NameScope . Find < GridSplitter > ( "PART_PinnedDockSplitter" ) ;
134+ AttachSplitterHandlers ( ) ;
121135 UpdateGrid ( ) ;
122136
123- if ( DockSettings . UsePinnedDockWindow )
124- {
125- LayoutUpdated += OnLayoutUpdated ;
126- this . AttachedToVisualTree += OnAttached ;
127- this . DetachedFromVisualTree += OnDetached ;
128- }
137+ LayoutUpdated += OnLayoutUpdated ;
138+ this . AttachedToVisualTree += OnAttached ;
139+ this . DetachedFromVisualTree += OnDetached ;
129140 }
130141
131142 private void OnAttached ( object ? sender , VisualTreeAttachmentEventArgs e )
132143 {
133- UpdateWindow ( ) ;
144+ if ( DockSettings . UsePinnedDockWindow )
145+ {
146+ UpdateWindow ( ) ;
147+ }
134148 }
135149
136150 private void OnDetached ( object ? sender , VisualTreeAttachmentEventArgs e )
@@ -139,10 +153,13 @@ private void OnDetached(object? sender, VisualTreeAttachmentEventArgs e)
139153 LayoutUpdated -= OnLayoutUpdated ;
140154 this . AttachedToVisualTree -= OnAttached ;
141155 this . DetachedFromVisualTree -= OnDetached ;
156+ DetachSplitterHandlers ( ) ;
142157 }
143158
144159 private void OnLayoutUpdated ( object ? sender , EventArgs e )
145160 {
161+ ApplyPinnedDockSize ( ) ;
162+ UpdatePinnedDockableBounds ( ) ;
146163 UpdateWindow ( ) ;
147164 }
148165
@@ -227,6 +244,224 @@ private void CloseWindow()
227244
228245 }
229246
247+ private void ApplyPinnedDockSize ( )
248+ {
249+ if ( _isResizingPinnedDock )
250+ {
251+ return ;
252+ }
253+
254+ if ( _pinnedDockGrid is null || DataContext is not IRootDock rootDock )
255+ {
256+ return ;
257+ }
258+
259+ var dockable = GetPinnedDockable ( rootDock ) ;
260+ if ( dockable is null )
261+ {
262+ return ;
263+ }
264+
265+ dockable . GetPinnedBounds ( out _ , out _ , out var width , out var height ) ;
266+
267+ switch ( PinnedDockAlignment )
268+ {
269+ case Alignment . Unset :
270+ case Alignment . Left :
271+ if ( ! IsValidSize ( width ) )
272+ {
273+ return ;
274+ }
275+ if ( IsColumnSizeApplied ( _pinnedDockGrid . ColumnDefinitions , 0 , width ) )
276+ {
277+ return ;
278+ }
279+ SetColumnSize ( _pinnedDockGrid . ColumnDefinitions , 0 , width ) ;
280+ _lastPinnedDockable = dockable ;
281+ _lastPinnedWidth = width ;
282+ break ;
283+ case Alignment . Right :
284+ if ( ! IsValidSize ( width ) )
285+ {
286+ return ;
287+ }
288+ if ( IsColumnSizeApplied ( _pinnedDockGrid . ColumnDefinitions , 2 , width ) )
289+ {
290+ return ;
291+ }
292+ SetColumnSize ( _pinnedDockGrid . ColumnDefinitions , 2 , width ) ;
293+ _lastPinnedDockable = dockable ;
294+ _lastPinnedWidth = width ;
295+ break ;
296+ case Alignment . Top :
297+ if ( ! IsValidSize ( height ) )
298+ {
299+ return ;
300+ }
301+ if ( IsRowSizeApplied ( _pinnedDockGrid . RowDefinitions , 0 , height ) )
302+ {
303+ return ;
304+ }
305+ SetRowSize ( _pinnedDockGrid . RowDefinitions , 0 , height ) ;
306+ _lastPinnedDockable = dockable ;
307+ _lastPinnedHeight = height ;
308+ break ;
309+ case Alignment . Bottom :
310+ if ( ! IsValidSize ( height ) )
311+ {
312+ return ;
313+ }
314+ if ( IsRowSizeApplied ( _pinnedDockGrid . RowDefinitions , 2 , height ) )
315+ {
316+ return ;
317+ }
318+ SetRowSize ( _pinnedDockGrid . RowDefinitions , 2 , height ) ;
319+ _lastPinnedDockable = dockable ;
320+ _lastPinnedHeight = height ;
321+ break ;
322+ default :
323+ break ;
324+ }
325+ }
326+
327+ private void UpdatePinnedDockableBounds ( )
328+ {
329+ if ( _pinnedDock is null || DataContext is not IRootDock rootDock )
330+ {
331+ return ;
332+ }
333+
334+ var dockable = GetPinnedDockable ( rootDock ) ;
335+ if ( dockable is null )
336+ {
337+ return ;
338+ }
339+
340+ dockable . GetPinnedBounds ( out _ , out _ , out var storedWidth , out var storedHeight ) ;
341+
342+ var hasStoredBounds = IsValidSize ( storedWidth ) && IsValidSize ( storedHeight ) ;
343+ if ( ! _isResizingPinnedDock && hasStoredBounds )
344+ {
345+ return ;
346+ }
347+
348+ var width = _pinnedDock . Bounds . Width ;
349+ var height = _pinnedDock . Bounds . Height ;
350+
351+ if ( ! IsValidSize ( width ) || ! IsValidSize ( height ) )
352+ {
353+ return ;
354+ }
355+
356+ if ( AreClose ( width , storedWidth ) && AreClose ( height , storedHeight ) )
357+ {
358+ return ;
359+ }
360+
361+ dockable . SetPinnedBounds ( 0 , 0 , width , height ) ;
362+ _lastPinnedDockable = dockable ;
363+ _lastPinnedWidth = width ;
364+ _lastPinnedHeight = height ;
365+ }
366+
367+ private static IDockable ? GetPinnedDockable ( IRootDock rootDock )
368+ {
369+ return rootDock . PinnedDock ? . VisibleDockables ? . FirstOrDefault ( ) ;
370+ }
371+
372+ private static void SetColumnSize ( ColumnDefinitions definitions , int index , double size )
373+ {
374+ if ( index < 0 || index >= definitions . Count )
375+ {
376+ return ;
377+ }
378+
379+ definitions [ index ] . Width = new GridLength ( size , GridUnitType . Pixel ) ;
380+ }
381+
382+ private static void SetRowSize ( RowDefinitions definitions , int index , double size )
383+ {
384+ if ( index < 0 || index >= definitions . Count )
385+ {
386+ return ;
387+ }
388+
389+ definitions [ index ] . Height = new GridLength ( size , GridUnitType . Pixel ) ;
390+ }
391+
392+ private static bool IsColumnSizeApplied ( ColumnDefinitions definitions , int index , double size )
393+ {
394+ if ( index < 0 || index >= definitions . Count )
395+ {
396+ return false ;
397+ }
398+
399+ var length = definitions [ index ] . Width ;
400+ return length . IsAbsolute && AreClose ( length . Value , size ) ;
401+ }
402+
403+ private static bool IsRowSizeApplied ( RowDefinitions definitions , int index , double size )
404+ {
405+ if ( index < 0 || index >= definitions . Count )
406+ {
407+ return false ;
408+ }
409+
410+ var length = definitions [ index ] . Height ;
411+ return length . IsAbsolute && AreClose ( length . Value , size ) ;
412+ }
413+
414+ private static bool IsValidSize ( double size )
415+ {
416+ return ! double . IsNaN ( size ) && ! double . IsInfinity ( size ) && size > 0 ;
417+ }
418+
419+ private static bool AreClose ( double left , double right )
420+ {
421+ return Math . Abs ( left - right ) <= BoundsEpsilon ;
422+ }
423+
424+ private void AttachSplitterHandlers ( )
425+ {
426+ if ( _pinnedDockSplitter is null )
427+ {
428+ return ;
429+ }
430+
431+ _pinnedDockSplitter . DragStarted += OnPinnedDockSplitterDragStarted ;
432+ _pinnedDockSplitter . DragDelta += OnPinnedDockSplitterDragDelta ;
433+ _pinnedDockSplitter . DragCompleted += OnPinnedDockSplitterDragCompleted ;
434+ }
435+
436+ private void DetachSplitterHandlers ( )
437+ {
438+ if ( _pinnedDockSplitter is null )
439+ {
440+ return ;
441+ }
442+
443+ _pinnedDockSplitter . DragStarted -= OnPinnedDockSplitterDragStarted ;
444+ _pinnedDockSplitter . DragDelta -= OnPinnedDockSplitterDragDelta ;
445+ _pinnedDockSplitter . DragCompleted -= OnPinnedDockSplitterDragCompleted ;
446+ }
447+
448+ private void OnPinnedDockSplitterDragStarted ( object ? sender , VectorEventArgs e )
449+ {
450+ _isResizingPinnedDock = true ;
451+ UpdatePinnedDockableBounds ( ) ;
452+ }
453+
454+ private void OnPinnedDockSplitterDragDelta ( object ? sender , VectorEventArgs e )
455+ {
456+ UpdatePinnedDockableBounds ( ) ;
457+ }
458+
459+ private void OnPinnedDockSplitterDragCompleted ( object ? sender , VectorEventArgs e )
460+ {
461+ UpdatePinnedDockableBounds ( ) ;
462+ _isResizingPinnedDock = false ;
463+ }
464+
230465 private void OwnerWindow_PositionChanged ( object ? sender , PixelPointEventArgs e )
231466 {
232467 CloseWindow ( ) ;
0 commit comments