mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-29 18:38:16 +08:00
* 调整AtomLayer,对Adorner提供Bounds;
This commit is contained in:
parent
1d70e4535b
commit
0e379e0d3c
22
docs/Code Style.md
Normal file
22
docs/Code Style.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Code Styles
|
||||||
|
|
||||||
|
## C# Naming Style
|
||||||
|
- Type, NameSpace, Method, Property, Event: **UpperCamelCase**
|
||||||
|
- Interface: **IUpperCamelCase**
|
||||||
|
- Type parameter: **TUpperCamelCase**
|
||||||
|
- Local variable, Parameter: **lowerCamelCase**
|
||||||
|
- Public Field (基本不用): **UpperCamelCase**
|
||||||
|
- Private Field: **_lowerCamelCase**
|
||||||
|
|
||||||
|
- Control Template Part Name: **PART_UpperCamelCase**
|
||||||
|
- Control Pseudo Class Name: **STATE_UpperCamelCase**
|
||||||
|
|
||||||
|
## 其他
|
||||||
|
- 使用Rider配置,尽量和Rider的建议保持一致即可。
|
||||||
|
- 不要在一行中声明多个变量;
|
||||||
|
|
||||||
|
## Xaml
|
||||||
|
- 使用 XamlStyler插件,添加配置。
|
||||||
|
- 不超过两个属性的共用一行。
|
||||||
|
- Name:UpperCamelCase。
|
||||||
|
- 结构复杂增加注释。
|
@ -1,76 +1,60 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Presenters;
|
using Avalonia.Threading;
|
||||||
using Avalonia.Layout;
|
|
||||||
using Avalonia.VisualTree;
|
|
||||||
|
|
||||||
// ReSharper disable SuggestBaseTypeForParameter
|
// ReSharper disable SuggestBaseTypeForParameter
|
||||||
|
|
||||||
namespace AtomUI.Controls.Primitives
|
namespace AtomUI.Controls.Primitives
|
||||||
{
|
{
|
||||||
public class AtomLayer : Panel
|
public class AtomLayer : Canvas
|
||||||
{
|
{
|
||||||
public static Control? GetTarget(Visual element)
|
#region Static
|
||||||
{
|
|
||||||
return element.GetValue(TargetProperty);
|
|
||||||
}
|
|
||||||
public static void SetTarget(Visual element, Control? value)
|
|
||||||
{
|
|
||||||
element.SetValue(TargetProperty, value);
|
|
||||||
}
|
|
||||||
public static readonly AttachedProperty<Control?> TargetProperty = AvaloniaProperty
|
|
||||||
.RegisterAttached<AtomLayer, Visual, Control?>("Target");
|
|
||||||
|
|
||||||
|
public static AtomLayer? GetLayer(Visual target)
|
||||||
|
{
|
||||||
|
return target.GetLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Visual GetTarget(Control adorner)
|
||||||
|
{
|
||||||
|
return adorner.GetValue(TargetProperty);
|
||||||
|
}
|
||||||
|
private static void SetTarget(Control adorner, Visual value)
|
||||||
|
{
|
||||||
|
adorner.SetValue(TargetProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly AttachedProperty<Visual> TargetProperty = AvaloniaProperty
|
||||||
|
.RegisterAttached<AtomLayer, Control, Visual>("Target");
|
||||||
|
|
||||||
|
public static Rect GetTargetRect(Control adorner)
|
||||||
|
{
|
||||||
|
return adorner.GetValue(TargetRectProperty);
|
||||||
|
}
|
||||||
|
private static void SetTargetRect(Control adorner, Rect value)
|
||||||
|
{
|
||||||
|
adorner.SetValue(TargetRectProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly AttachedProperty<Rect> TargetRectProperty = AvaloniaProperty
|
||||||
|
.RegisterAttached<AtomLayer, Control, Rect>("TargetRect");
|
||||||
|
|
||||||
public static AtomLayer? GetLayer(Visual visual)
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private readonly IList<WeakReference<Control>> _detachedAdorners = new List<WeakReference<Control>>();
|
||||||
|
|
||||||
|
internal Visual? ParentHost { get; set; }
|
||||||
|
|
||||||
|
internal AtomLayer() { }
|
||||||
|
|
||||||
|
public T? GetAdorner<T>(Visual target) where T : Control
|
||||||
{
|
{
|
||||||
var host = visual.FindAncestorOfType<ScrollContentPresenter>(true)?.Content as Control
|
return this.Children.OfType<T>().FirstOrDefault(a => GetTarget(a) == target);
|
||||||
?? TopLevel.GetTopLevel(visual);
|
|
||||||
|
|
||||||
if (host == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var layer = host.GetVisualChildren().FirstOrDefault(c => c is AtomLayer) as AtomLayer;
|
|
||||||
layer ??= TryInject(host);
|
|
||||||
return layer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AtomLayer TryInject(Control control)
|
public IEnumerable<Control> GetAdorners(Visual target)
|
||||||
{
|
{
|
||||||
var layer = new AtomLayer();
|
return this.Children.Where(v => GetTarget(v) == target).ToList();
|
||||||
|
|
||||||
InjectCore(control, layer);
|
|
||||||
|
|
||||||
return layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void InjectCore(Control control, AtomLayer layer)
|
|
||||||
{
|
|
||||||
if (control.GetVisualChildren() is not IList<Visual> visualChildren)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (visualChildren.Any(c => c is AtomLayer))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.HorizontalAlignment = HorizontalAlignment.Stretch;
|
|
||||||
layer.VerticalAlignment = VerticalAlignment.Stretch;
|
|
||||||
layer.InheritanceParent = control;
|
|
||||||
|
|
||||||
visualChildren.Add(layer);
|
|
||||||
control.InvalidateMeasure();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private AtomLayer()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAdorner(Visual target, Control adorner)
|
public void AddAdorner(Visual target, Control adorner)
|
||||||
@ -80,58 +64,117 @@ namespace AtomUI.Controls.Primitives
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTarget(target, adorner);
|
target.PropertyChanged -= TargetOnPropertyChanged;
|
||||||
Children.Add(adorner);
|
target.PropertyChanged += TargetOnPropertyChanged;
|
||||||
|
|
||||||
target.AttachedToVisualTree -= OnTargetOnAttachedToVisualTree;
|
target.AttachedToVisualTree -= OnTargetOnAttachedToVisualTree;
|
||||||
target.DetachedFromVisualTree -= OnTargetOnDetachedFromVisualTree;
|
target.DetachedFromVisualTree -= OnTargetOnDetachedFromVisualTree;
|
||||||
target.AttachedToVisualTree += OnTargetOnAttachedToVisualTree;
|
target.AttachedToVisualTree += OnTargetOnAttachedToVisualTree;
|
||||||
target.DetachedFromVisualTree += OnTargetOnDetachedFromVisualTree;
|
target.DetachedFromVisualTree += OnTargetOnDetachedFromVisualTree;
|
||||||
|
|
||||||
if (target is Control c)
|
SetTarget(adorner, target);
|
||||||
{
|
Children.Add(adorner);
|
||||||
c.SizeChanged -= OnTargetOnSizeChanged;
|
Locate(target, adorner);
|
||||||
c.SizeChanged += OnTargetOnSizeChanged;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTargetOnSizeChanged(object? sender, SizeChangedEventArgs e)
|
private void TargetOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is not Visual visual)
|
if (e.Property != BoundsProperty)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach (var adorner in GetAdorners(visual))
|
|
||||||
|
if (sender is not Visual target)
|
||||||
{
|
{
|
||||||
adorner.InvalidateMeasure();
|
return;
|
||||||
adorner.InvalidateVisual();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Child element's bounds will be updated first.
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
foreach (var adorner in GetAdorners(target))
|
||||||
|
{
|
||||||
|
Locate(target, adorner);
|
||||||
|
}
|
||||||
|
}, DispatcherPriority.Send);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Locate(Visual target, Control adorner)
|
||||||
|
{
|
||||||
|
if (this.ParentHost is Control { IsLoaded: false })
|
||||||
|
{
|
||||||
|
this.ParentHost.PropertyChanged -= ParentHostOnPropertyChanged;
|
||||||
|
this.ParentHost.PropertyChanged += ParentHostOnPropertyChanged;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LocateCore(target, adorner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
void ParentHostOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
this.ParentHost.PropertyChanged -= ParentHostOnPropertyChanged;
|
||||||
|
LocateCore(target, adorner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LocateCore(Visual target, Control adorner)
|
||||||
|
{
|
||||||
|
var matrix = target.TransformToVisual(this)!;
|
||||||
|
var x = matrix.Value.M31;
|
||||||
|
var y = matrix.Value.M32;
|
||||||
|
var rect = new Rect(x, y, target.Bounds.Width, target.Bounds.Height);
|
||||||
|
|
||||||
|
SetTargetRect(adorner, rect);
|
||||||
|
SetLeft(adorner, x);
|
||||||
|
SetTop(adorner, y);
|
||||||
|
adorner.Width = target.Bounds.Width;
|
||||||
|
adorner.Height = target.Bounds.Height;
|
||||||
|
|
||||||
|
adorner.Measure(target.Bounds.Size);
|
||||||
|
adorner.Arrange(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTargetOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
private void OnTargetOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
||||||
{
|
{
|
||||||
if (Children.Contains(this))
|
if (sender is not Visual target)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Children.Add(this);
|
var adorners = new List<Control>();
|
||||||
|
for (var i = 0; i < _detachedAdorners.Count; i++)
|
||||||
|
{
|
||||||
|
var w = _detachedAdorners[i];
|
||||||
|
if (w.TryGetTarget(out var t) && Children.Contains(t) == false && GetTarget(t) == target)
|
||||||
|
{
|
||||||
|
adorners.Add(t);
|
||||||
|
_detachedAdorners.RemoveAt(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adorners = adorners.Where(a => Children.Contains(a) == false && GetTarget(a) == target).ToList();
|
||||||
|
foreach (var adorner in adorners)
|
||||||
|
{
|
||||||
|
Children.Add(adorner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTargetOnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
private void OnTargetOnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
||||||
{
|
{
|
||||||
Children.Remove(this);
|
if (sender is not Visual target)
|
||||||
}
|
{
|
||||||
|
return;
|
||||||
public Control? GetAdorner<T>(Visual target) where T : Control, IAtomAdorner
|
}
|
||||||
{
|
|
||||||
var adorner = this.Children.OfType<T>().FirstOrDefault(a => a.Target == target);
|
var adorners = target.GetAdorners();
|
||||||
return adorner;
|
foreach (var adorner in adorners)
|
||||||
}
|
{
|
||||||
|
Children.Remove(adorner);
|
||||||
public IEnumerable<Control> GetAdorners(Visual target)
|
_detachedAdorners.Add(new WeakReference<Control>(adorner));
|
||||||
{
|
}
|
||||||
return this.Children.OfType<IAtomAdorner>().Where(c => c.Target == target).OfType<Control>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Presenters;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
namespace AtomUI.Controls.Primitives;
|
namespace AtomUI.Controls.Primitives;
|
||||||
|
|
||||||
@ -6,6 +10,67 @@ public static class AtomLayerExtension
|
|||||||
{
|
{
|
||||||
public static AtomLayer? GetLayer(this Visual? visual)
|
public static AtomLayer? GetLayer(this Visual? visual)
|
||||||
{
|
{
|
||||||
return visual == null ? null : AtomLayer.GetLayer(visual);
|
if (visual == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var host = visual.FindAncestorOfType<ScrollContentPresenter>(true)?.Content as Visual
|
||||||
|
?? TopLevel.GetTopLevel(visual);
|
||||||
|
|
||||||
|
if (host == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var layer = host.GetVisualChildren().FirstOrDefault(c => c is AtomLayer) as AtomLayer
|
||||||
|
?? TryInject(host);
|
||||||
|
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? GetAdorner<T>(this Visual target) where T : Control
|
||||||
|
{
|
||||||
|
return target.GetLayer()?.GetAdorner<T>(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Control> GetAdorners(this Visual target)
|
||||||
|
{
|
||||||
|
return target.GetLayer()?.GetAdorners(target) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddAdorner(this Visual target, Control adorner)
|
||||||
|
{
|
||||||
|
target.GetLayer()?.AddAdorner(target, adorner);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static AtomLayer? TryInject(Visual host)
|
||||||
|
{
|
||||||
|
var layer = new AtomLayer
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
|
VerticalAlignment = VerticalAlignment.Stretch
|
||||||
|
};
|
||||||
|
|
||||||
|
return InjectCore(host, layer) ? layer : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool InjectCore(Visual host, AtomLayer layer)
|
||||||
|
{
|
||||||
|
if (host.GetVisualChildren() is not IList<Visual> visualChildren)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (visualChildren.Any(c => c is AtomLayer))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visualChildren.Add(layer);
|
||||||
|
layer.ParentHost = host;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
|
|
||||||
namespace AtomUI.Controls.Primitives;
|
|
||||||
|
|
||||||
public interface IAtomAdorner
|
|
||||||
{
|
|
||||||
public Visual Target { get; }
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ using Avalonia.VisualTree;
|
|||||||
|
|
||||||
namespace AtomUI.Controls;
|
namespace AtomUI.Controls;
|
||||||
|
|
||||||
public sealed class Watermark : Control, IAtomAdorner
|
public sealed class Watermark : Control
|
||||||
{
|
{
|
||||||
public static WatermarkGlyph? GetGlyph(Visual element)
|
public static WatermarkGlyph? GetGlyph(Visual element)
|
||||||
{
|
{
|
||||||
@ -27,8 +27,6 @@ public sealed class Watermark : Control, IAtomAdorner
|
|||||||
public Visual Target { get; }
|
public Visual Target { get; }
|
||||||
|
|
||||||
private WatermarkGlyph? Glyph { get; }
|
private WatermarkGlyph? Glyph { get; }
|
||||||
|
|
||||||
private AtomLayer Layer { get; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -38,11 +36,10 @@ public sealed class Watermark : Control, IAtomAdorner
|
|||||||
GlyphProperty.Changed.AddClassHandler<Visual>(OnGlyphChanged);
|
GlyphProperty.Changed.AddClassHandler<Visual>(OnGlyphChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Watermark(AtomLayer layer, Visual target, WatermarkGlyph? glyph)
|
private Watermark(Visual target, WatermarkGlyph? glyph)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
Glyph = glyph;
|
Glyph = glyph;
|
||||||
Layer = layer;
|
|
||||||
|
|
||||||
if (glyph != null)
|
if (glyph != null)
|
||||||
{
|
{
|
||||||
@ -90,7 +87,7 @@ public sealed class Watermark : Control, IAtomAdorner
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
watermark = new Watermark(layer, target, GetGlyph(target));
|
watermark = new Watermark(target, GetGlyph(target));
|
||||||
layer.AddAdorner(target ,watermark);
|
layer.AddAdorner(target ,watermark);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,24 +111,12 @@ public sealed class Watermark : Control, IAtomAdorner
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CheckLayer(Target, out var layer) == false)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var matrix = Target.TransformToVisual(layer);
|
|
||||||
if (matrix == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var size = Glyph.GetDesiredSize();
|
var size = Glyph.GetDesiredSize();
|
||||||
if (size.Width == 0 || size.Height == 0)
|
if (size.Width == 0 || size.Height == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (context.PushTransform(matrix.Value))
|
|
||||||
using (context.PushClip(new Rect(Target.Bounds.Size)))
|
using (context.PushClip(new Rect(Target.Bounds.Size)))
|
||||||
using (context.PushOpacity(Glyph.Opacity))
|
using (context.PushOpacity(Glyph.Opacity))
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user