Done
This commit is contained in:
@@ -3,6 +3,11 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
|
||||
<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">
|
||||
<ControlTemplate>
|
||||
<Border
|
||||
@@ -13,17 +18,28 @@
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Grid>
|
||||
<ZoomBorder
|
||||
x:Name="ZoomBorder"
|
||||
ClipToBounds="True"
|
||||
PanButton="Left">
|
||||
<ContentPresenter
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||
<Canvas x:Name="Canvas" Background="Transparent">
|
||||
</ZoomBorder>
|
||||
<Canvas
|
||||
x:Name="Canvas"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
ClipToBounds="False">
|
||||
<Border
|
||||
x:Name="Preview"
|
||||
x:Name="PeekBorder"
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="Pink" />
|
||||
Background="Pink"
|
||||
ClipToBounds="False"
|
||||
IsVisible="False" />
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -1,73 +1,163 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.PanAndZoom;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
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 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)
|
||||
{
|
||||
base.OnApplyTemplate(args);
|
||||
|
||||
PointerMoved -= OnPointerMoved;
|
||||
PointerExited -= OnPointerExited;
|
||||
PointerEntered -= OnPointerEntered;
|
||||
|
||||
PointerMoved += OnPointerMoved;
|
||||
PointerExited += OnPointerExited;
|
||||
PointerEntered += OnPointerEntered;
|
||||
|
||||
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;
|
||||
canvas.PointerExited += OnPointerExited;
|
||||
canvas.PointerEntered += OnPointerEntered;
|
||||
zoomBorder.ZoomChanged += OnZoomChanged;
|
||||
}
|
||||
|
||||
preview = args.NameScope.Find<Border>("Preview");
|
||||
}
|
||||
|
||||
private void OnPointerMoved(object? sender,
|
||||
PointerEventArgs args)
|
||||
peekBorder = args.NameScope.Find<Border>("PeekBorder");
|
||||
if (peekBorder is not null)
|
||||
{
|
||||
if (canvas is null || preview is null)
|
||||
{
|
||||
return;
|
||||
peekBorder.Child = image;
|
||||
}
|
||||
|
||||
double relativeX = args.GetPosition(canvas).X;
|
||||
double relativeY = args.GetPosition(canvas).Y;
|
||||
|
||||
double newX = relativeX < 0 ? 0 : (relativeX > canvas.Bounds.Width ? canvas.Bounds.Width : relativeX);
|
||||
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)
|
||||
{
|
||||
preview.IsVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Canvas.SetLeft(preview, newX);
|
||||
Canvas.SetTop(preview, newY);
|
||||
}
|
||||
|
||||
private void OnPointerEntered(object? sender,
|
||||
PointerEventArgs args)
|
||||
{
|
||||
if (preview is null)
|
||||
if (peekBorder is not null)
|
||||
{
|
||||
return;
|
||||
peekBorder.IsVisible = true;
|
||||
}
|
||||
|
||||
preview.IsVisible = true;
|
||||
}
|
||||
|
||||
private void OnPointerExited(object? sender,
|
||||
PointerEventArgs args)
|
||||
{
|
||||
if (preview is null)
|
||||
if (peekBorder is not null)
|
||||
{
|
||||
peekBorder.IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerMoved(object? sender,
|
||||
PointerEventArgs args)
|
||||
{
|
||||
double relativeX = args.GetPosition(canvas).X;
|
||||
double relativeY = args.GetPosition(canvas).Y;
|
||||
|
||||
lastPointerPosition = (relativeX, relativeY);
|
||||
|
||||
UpdatePeekPosition(relativeX, relativeY);
|
||||
}
|
||||
|
||||
private void OnZoomChanged(object sender,
|
||||
ZoomChangedEventArgs args) => UpdatePeekPreview(lastPointerPosition.X, lastPointerPosition.Y);
|
||||
|
||||
private Bitmap RenderToBitmap(Visual visual,
|
||||
double centreX,
|
||||
double centreY)
|
||||
{
|
||||
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())
|
||||
{
|
||||
drawingContext.PushClip(new Rect(0, 0, width, height));
|
||||
drawingContext.FillRectangle(new VisualBrush(visual), new Rect(-x, -y,
|
||||
visual.Bounds.Width, visual.Bounds.Height));
|
||||
}
|
||||
|
||||
return renderTarget;
|
||||
}
|
||||
|
||||
private void UpdatePeekPosition(double relativeX,
|
||||
double relativeY)
|
||||
{
|
||||
if (canvas is null || peekBorder is null)
|
||||
{
|
||||
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>
|
||||
<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.Labs.Controls" Version="11.1.0" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
||||
|
||||
Reference in New Issue
Block a user