Done
This commit is contained in:
@@ -3,6 +3,11 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
|
xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
|
||||||
<ControlTheme x:Key="{x:Type ContentColorPicker}" TargetType="ContentColorPicker">
|
<ControlTheme x:Key="{x:Type ContentColorPicker}" TargetType="ContentColorPicker">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
|
<Setter Property="ClipToBounds" Value="False" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate>
|
<ControlTemplate>
|
||||||
<Border
|
<Border
|
||||||
@@ -13,17 +18,28 @@
|
|||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<ContentPresenter
|
<ZoomBorder
|
||||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
x:Name="ZoomBorder"
|
||||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
ClipToBounds="True"
|
||||||
Content="{TemplateBinding Content}"
|
PanButton="Left">
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
<ContentPresenter
|
||||||
<Canvas x:Name="Canvas" Background="Transparent">
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||||
|
</ZoomBorder>
|
||||||
|
<Canvas
|
||||||
|
x:Name="Canvas"
|
||||||
|
Width="{TemplateBinding Width}"
|
||||||
|
Height="{TemplateBinding Height}"
|
||||||
|
ClipToBounds="False">
|
||||||
<Border
|
<Border
|
||||||
x:Name="Preview"
|
x:Name="PeekBorder"
|
||||||
Width="100"
|
Width="100"
|
||||||
Height="100"
|
Height="100"
|
||||||
Background="Pink" />
|
Background="Pink"
|
||||||
|
ClipToBounds="False"
|
||||||
|
IsVisible="False" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -1,73 +1,163 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.PanAndZoom;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
|
||||||
namespace Toolkit.UI.Controls.Avalonia;
|
namespace Toolkit.UI.Controls.Avalonia;
|
||||||
|
|
||||||
public class ContentColorPicker : ContentControl
|
public class ContentColorPicker :
|
||||||
|
ContentControl
|
||||||
{
|
{
|
||||||
|
public static readonly StyledProperty<double> PeekOffsetProperty =
|
||||||
|
AvaloniaProperty.Register<ContentColorPicker, double>(nameof(PeekOffset), 20);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<int> PeekPixelsProperty =
|
||||||
|
AvaloniaProperty.Register<ContentColorPicker, int>(nameof(PeekPixels), 20);
|
||||||
|
|
||||||
|
private readonly Image image = new();
|
||||||
|
|
||||||
private Canvas? canvas;
|
private Canvas? canvas;
|
||||||
private Border? preview;
|
private (double X, double Y) lastPointerPosition;
|
||||||
|
private Border? peekBorder;
|
||||||
|
private ZoomBorder? zoomBorder;
|
||||||
|
|
||||||
|
public double PeekOffset
|
||||||
|
{
|
||||||
|
get => GetValue(PeekOffsetProperty);
|
||||||
|
set => SetValue(PeekOffsetProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PeekPixels
|
||||||
|
{
|
||||||
|
get => GetValue(PeekPixelsProperty);
|
||||||
|
set => SetValue(PeekPixelsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs args)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs args)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(args);
|
base.OnApplyTemplate(args);
|
||||||
|
|
||||||
|
PointerMoved -= OnPointerMoved;
|
||||||
|
PointerExited -= OnPointerExited;
|
||||||
|
PointerEntered -= OnPointerEntered;
|
||||||
|
|
||||||
|
PointerMoved += OnPointerMoved;
|
||||||
|
PointerExited += OnPointerExited;
|
||||||
|
PointerEntered += OnPointerEntered;
|
||||||
|
|
||||||
canvas = args.NameScope.Find<Canvas>("Canvas");
|
canvas = args.NameScope.Find<Canvas>("Canvas");
|
||||||
|
|
||||||
if (canvas is not null)
|
zoomBorder = args.NameScope.Find<ZoomBorder>("ZoomBorder");
|
||||||
|
if (zoomBorder is not null)
|
||||||
{
|
{
|
||||||
canvas.PointerMoved += OnPointerMoved;
|
zoomBorder.ZoomChanged += OnZoomChanged;
|
||||||
canvas.PointerExited += OnPointerExited;
|
|
||||||
canvas.PointerEntered += OnPointerEntered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preview = args.NameScope.Find<Border>("Preview");
|
peekBorder = args.NameScope.Find<Border>("PeekBorder");
|
||||||
|
if (peekBorder is not null)
|
||||||
|
{
|
||||||
|
peekBorder.Child = image;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPointerMoved(object? sender,
|
private void OnPointerEntered(object? sender,
|
||||||
PointerEventArgs args)
|
PointerEventArgs args)
|
||||||
{
|
{
|
||||||
if (canvas is null || preview is null)
|
if (peekBorder is not null)
|
||||||
{
|
{
|
||||||
return;
|
peekBorder.IsVisible = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerExited(object? sender,
|
||||||
|
PointerEventArgs args)
|
||||||
|
{
|
||||||
|
if (peekBorder is not null)
|
||||||
|
{
|
||||||
|
peekBorder.IsVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPointerMoved(object? sender,
|
||||||
|
PointerEventArgs args)
|
||||||
|
{
|
||||||
double relativeX = args.GetPosition(canvas).X;
|
double relativeX = args.GetPosition(canvas).X;
|
||||||
double relativeY = args.GetPosition(canvas).Y;
|
double relativeY = args.GetPosition(canvas).Y;
|
||||||
|
|
||||||
double newX = relativeX < 0 ? 0 : (relativeX > canvas.Bounds.Width ? canvas.Bounds.Width : relativeX);
|
lastPointerPosition = (relativeX, relativeY);
|
||||||
double newY = relativeY < 0 ? 0 : (relativeY > canvas.Bounds.Height ? canvas.Bounds.Height : relativeY);
|
|
||||||
|
|
||||||
if (newX < 0 || newX > canvas.Bounds.Width || newY < 0 || newY > canvas.Bounds.Height)
|
UpdatePeekPosition(relativeX, relativeY);
|
||||||
{
|
|
||||||
preview.IsVisible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Canvas.SetLeft(preview, newX);
|
|
||||||
Canvas.SetTop(preview, newY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPointerEntered(object? sender,
|
private void OnZoomChanged(object sender,
|
||||||
PointerEventArgs args)
|
ZoomChangedEventArgs args) => UpdatePeekPreview(lastPointerPosition.X, lastPointerPosition.Y);
|
||||||
|
|
||||||
|
private Bitmap RenderToBitmap(Visual visual,
|
||||||
|
double centreX,
|
||||||
|
double centreY)
|
||||||
{
|
{
|
||||||
if (preview is null)
|
int width = PeekPixels;
|
||||||
|
int height = PeekPixels;
|
||||||
|
|
||||||
|
double x = Math.Max(centreX - width / 2, 0);
|
||||||
|
double y = Math.Max(centreY - height / 2, 0);
|
||||||
|
|
||||||
|
x = Math.Min(x, visual.Bounds.Width - width);
|
||||||
|
y = Math.Min(y, visual.Bounds.Height - height);
|
||||||
|
|
||||||
|
PixelSize pixelSize = new(width, height);
|
||||||
|
RenderTargetBitmap renderTarget = new(pixelSize);
|
||||||
|
|
||||||
|
using (DrawingContext drawingContext = renderTarget.CreateDrawingContext())
|
||||||
{
|
{
|
||||||
return;
|
drawingContext.PushClip(new Rect(0, 0, width, height));
|
||||||
|
drawingContext.FillRectangle(new VisualBrush(visual), new Rect(-x, -y,
|
||||||
|
visual.Bounds.Width, visual.Bounds.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.IsVisible = true;
|
return renderTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPointerExited(object? sender,
|
private void UpdatePeekPosition(double relativeX,
|
||||||
PointerEventArgs args)
|
double relativeY)
|
||||||
{
|
{
|
||||||
if (preview is null)
|
if (canvas is null || peekBorder is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.IsVisible = false;
|
double peekOffset = PeekOffset;
|
||||||
|
|
||||||
|
double newX = relativeX + peekOffset;
|
||||||
|
double newY = relativeY + peekOffset;
|
||||||
|
|
||||||
|
newX = Math.Clamp(newX, -peekBorder.Bounds.Width, canvas.Bounds.Width);
|
||||||
|
newY = Math.Clamp(newY, -peekBorder.Bounds.Height, canvas.Bounds.Height);
|
||||||
|
|
||||||
|
Canvas.SetLeft(peekBorder, newX);
|
||||||
|
Canvas.SetTop(peekBorder, newY);
|
||||||
|
|
||||||
|
bool isPointerInside = relativeX >= -peekOffset && relativeX <= canvas.Bounds.Width + peekOffset &&
|
||||||
|
relativeY >= -peekOffset && relativeY <= canvas.Bounds.Height + peekOffset;
|
||||||
|
|
||||||
|
peekBorder.IsVisible = isPointerInside;
|
||||||
|
|
||||||
|
if (isPointerInside)
|
||||||
|
{
|
||||||
|
UpdatePeekPreview(relativeX, relativeY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void UpdatePeekPreview(double relativeX,
|
||||||
|
double relativeY)
|
||||||
|
{
|
||||||
|
if (zoomBorder is not null)
|
||||||
|
{
|
||||||
|
Bitmap bitmap = RenderToBitmap(zoomBorder, relativeX, relativeY);
|
||||||
|
image.Source = bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.2.0-rc1" />
|
<PackageReference Include="Avalonia" Version="11.2.0-rc1" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="11.1.0.1" />
|
||||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0.4" />
|
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0.4" />
|
||||||
<PackageReference Include="Avalonia.Labs.Controls" Version="11.1.0" />
|
<PackageReference Include="Avalonia.Labs.Controls" Version="11.1.0" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user