6 changed files with 212 additions and 1 deletions
@ -0,0 +1,89 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Disposables; |
||||
|
using System.Text; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.VisualTree; |
||||
|
|
||||
|
namespace Avalonia.Controls.Chrome |
||||
|
{ |
||||
|
public class CaptionButtons : TemplatedControl |
||||
|
{ |
||||
|
private CompositeDisposable _disposables; |
||||
|
private Window _hostWindow; |
||||
|
|
||||
|
public CaptionButtons(Window hostWindow) |
||||
|
{ |
||||
|
_hostWindow = hostWindow; |
||||
|
} |
||||
|
|
||||
|
public void Attach() |
||||
|
{ |
||||
|
if (_disposables == null) |
||||
|
{ |
||||
|
var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow); |
||||
|
|
||||
|
layer.Children.Add(this); |
||||
|
|
||||
|
_disposables = new CompositeDisposable |
||||
|
{ |
||||
|
_hostWindow.GetObservable(Window.WindowDecorationMarginsProperty) |
||||
|
.Subscribe(x => |
||||
|
{ |
||||
|
Height = x.Top; |
||||
|
}), |
||||
|
|
||||
|
|
||||
|
_hostWindow.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty) |
||||
|
.Subscribe(x => InvalidateSize()), |
||||
|
|
||||
|
_hostWindow.GetObservable(Window.OffScreenMarginProperty) |
||||
|
.Subscribe(x => InvalidateSize()), |
||||
|
|
||||
|
_hostWindow.GetObservable(Window.WindowStateProperty) |
||||
|
.Subscribe(x => |
||||
|
{ |
||||
|
PseudoClasses.Set(":minimized", x == WindowState.Minimized); |
||||
|
PseudoClasses.Set(":normal", x == WindowState.Normal); |
||||
|
PseudoClasses.Set(":maximized", x == WindowState.Maximized); |
||||
|
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen); |
||||
|
}) |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void InvalidateSize () |
||||
|
{ |
||||
|
Margin = new Thickness(1, _hostWindow.OffScreenMargin.Top, 1, 1); |
||||
|
Height = _hostWindow.WindowDecorationMargins.Top - _hostWindow.OffScreenMargin.Top; |
||||
|
} |
||||
|
|
||||
|
public void Detach() |
||||
|
{ |
||||
|
if (_disposables != null) |
||||
|
{ |
||||
|
var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow); |
||||
|
|
||||
|
layer.Children.Remove(this); |
||||
|
|
||||
|
_disposables.Dispose(); |
||||
|
_disposables = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) |
||||
|
{ |
||||
|
base.OnApplyTemplate(e); |
||||
|
|
||||
|
var closeButton = e.NameScope.Find<Panel>("PART_CloseButton"); |
||||
|
var restoreButton = e.NameScope.Find<Panel>("PART_RestoreButton"); |
||||
|
var minimiseButton = e.NameScope.Find<Panel>("PART_MinimiseButton"); |
||||
|
var fullScreenButton = e.NameScope.Find<Panel>("PART_FullScreenButton"); |
||||
|
|
||||
|
closeButton.PointerPressed += (sender, e) => _hostWindow.Close(); |
||||
|
restoreButton.PointerPressed += (sender, e) => _hostWindow.WindowState = _hostWindow.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; |
||||
|
minimiseButton.PointerPressed += (sender, e) => _hostWindow.WindowState = WindowState.Minimized; |
||||
|
fullScreenButton.PointerPressed += (sender, e) => _hostWindow.WindowState = _hostWindow.WindowState == WindowState.FullScreen ? WindowState.Normal : WindowState.FullScreen; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System.Linq; |
||||
|
using Avalonia.Rendering; |
||||
|
using Avalonia.VisualTree; |
||||
|
|
||||
|
namespace Avalonia.Controls.Primitives |
||||
|
{ |
||||
|
public class ChromeOverlayLayer : Canvas, ICustomSimpleHitTest |
||||
|
{ |
||||
|
public Size AvailableSize { get; private set; } |
||||
|
|
||||
|
public static ChromeOverlayLayer GetOverlayLayer(IVisual visual) |
||||
|
{ |
||||
|
foreach (var v in visual.GetVisualAncestors()) |
||||
|
if (v is VisualLayerManager vlm) |
||||
|
if (vlm.OverlayLayer != null) |
||||
|
return vlm.ChromeOverlayLayer; |
||||
|
|
||||
|
if (visual is TopLevel tl) |
||||
|
{ |
||||
|
var layers = tl.GetVisualDescendants().OfType<VisualLayerManager>().FirstOrDefault(); |
||||
|
return layers?.ChromeOverlayLayer; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public bool HitTest(Point point) => Children.HitTestCustom(point); |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
// We are saving it here since child controls might need to know the entire size of the overlay
|
||||
|
// and Bounds won't be updated in time
|
||||
|
AvailableSize = finalSize; |
||||
|
return base.ArrangeOverride(finalSize); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<Style Selector="CaptionButtons"> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/> |
||||
|
<Setter Property="Canvas.Right" Value="0" /> |
||||
|
<Setter Property="MaxHeight" Value="30" /> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<StackPanel Spacing="2" Margin="0 0 7 0" Background="#7F00FF00" VerticalAlignment="Stretch" TextBlock.FontSize="10" Orientation="Horizontal"> |
||||
|
<StackPanel.Styles> |
||||
|
<Style Selector="Panel"> |
||||
|
<Setter Property="Width" Value="45" /> |
||||
|
<Setter Property="Background" Value="Transparent" /> |
||||
|
</Style> |
||||
|
<Style Selector="Path"> |
||||
|
<Setter Property="Fill" Value="White" /> |
||||
|
</Style> |
||||
|
<Style Selector="Panel:pointerover"> |
||||
|
<Setter Property="Background" Value="#1FFFFFFF" /> |
||||
|
</Style> |
||||
|
<Style Selector="Panel#PART_CloseButton:pointerover"> |
||||
|
<Setter Property="Background" Value="#AFFF0000" /> |
||||
|
</Style> |
||||
|
<Style Selector="Viewbox"> |
||||
|
<Setter Property="Width" Value="11" /> |
||||
|
<Setter Property="Margin" Value="2" /> |
||||
|
</Style> |
||||
|
</StackPanel.Styles> |
||||
|
<Panel x:Name="PART_FullScreenButton"> |
||||
|
<Viewbox> |
||||
|
<Path Stretch="UniformToFill" /> |
||||
|
</Viewbox> |
||||
|
</Panel> |
||||
|
|
||||
|
<Panel x:Name="PART_MinimiseButton"> |
||||
|
<Viewbox> |
||||
|
<Path Stretch="UniformToFill" Data="M2048 1229v-205h-2048v205h2048z" /> |
||||
|
</Viewbox> |
||||
|
</Panel> |
||||
|
|
||||
|
<Panel x:Name="PART_RestoreButton"> |
||||
|
<Viewbox> |
||||
|
<Viewbox.RenderTransform> |
||||
|
<RotateTransform Angle="-90" /> |
||||
|
</Viewbox.RenderTransform> |
||||
|
<Path Stretch="UniformToFill"/> |
||||
|
</Viewbox> |
||||
|
</Panel> |
||||
|
|
||||
|
<Panel x:Name="PART_CloseButton"> |
||||
|
<Viewbox> |
||||
|
<Path Stretch="UniformToFill" Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z" /> |
||||
|
</Viewbox> |
||||
|
</Panel> |
||||
|
</StackPanel> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
<Style Selector="CaptionButtons Panel#PART_RestoreButton Path"> |
||||
|
<Setter Property="Data" Value="M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z" /> |
||||
|
</Style> |
||||
|
<Style Selector="CaptionButtons:maximized Panel#PART_RestoreButton Path"> |
||||
|
<Setter Property="Data" Value="M2048 410h-410v-410h-1638v1638h410v410h1638v-1638zM1434 1434h-1229v-1229h1229v1229zM1843 1843h-1229v-205h1024v-1024h205v1229z" /> |
||||
|
</Style> |
||||
|
<Style Selector="CaptionButtons Panel#PART_FullScreenButton Path"> |
||||
|
<Setter Property="Data" Value="M2048 2048v-819h-205v469l-1493 -1493h469v-205h-819v819h205v-469l1493 1493h-469v205h819z" /> |
||||
|
</Style> |
||||
|
<Style Selector="CaptionButtons:fullscreen Panel#PART_FullScreenButton Path"> |
||||
|
<Setter Property="Data" Value="M205 1024h819v-819h-205v469l-674 -674l-145 145l674 674h-469v205zM1374 1229h469v-205h-819v819h205v-469l674 674l145 -145z" /> |
||||
|
</Style> |
||||
|
</Styles> |
||||
Loading…
Reference in new issue