完成 Empty 控件改造

This commit is contained in:
polarboy 2024-07-16 15:56:43 +08:00
parent b73611f5c4
commit f91f2e6b7f
9 changed files with 156 additions and 107 deletions

View File

@ -31,6 +31,23 @@ public static class BindUtils
return target.Bind(targetProperty, descriptor);
}
public static IDisposable RelayBind<TSource, TResult>(AvaloniaObject source, AvaloniaProperty<TSource> sourceProperty,
AvaloniaObject target, AvaloniaProperty<TResult> targetProperty,
Func<TSource, TResult> converter,
BindingPriority priority = BindingPriority.Template)
{
var registry = AvaloniaPropertyRegistry.Instance;
if (!registry.IsRegistered(source.GetType(), sourceProperty)) {
throw new ArgumentException($"Relay source property is not registered for: {source.GetType().Name}.");
}
if (!registry.IsRegistered(target.GetType(), targetProperty)) {
throw new ArgumentException($"Relay target property is not registered for: {target.GetType().Name}.");
}
return target.Bind(targetProperty, source.GetObservable(sourceProperty, converter), priority);
}
public static IDisposable CreateTokenBinding(AvaloniaObject target,
AvaloniaProperty targetProperty,
string resourceKey)

View File

@ -96,7 +96,6 @@ public class Alert : TemplatedControl, IControlCustomStyle
private readonly IControlCustomStyle _customStyle;
private readonly ControlTokenBinder _controlTokenBinder;
private bool _initialized = false;
private bool _scalingAwareConfigApplied = false;
@ -120,15 +119,6 @@ public class Alert : TemplatedControl, IControlCustomStyle
_customStyle.InitOnConstruct();
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
if (!_initialized) {
_customStyle.SetupUi();
_initialized = true;
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
@ -169,7 +159,7 @@ public class Alert : TemplatedControl, IControlCustomStyle
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
{
if (_initialized) {
if (VisualRoot is not null) {
if (e.Property == IsClosableProperty) {
SetupCloseButton();
}

View File

@ -31,7 +31,7 @@ internal class AlertTheme : ControlTheme
{
}
public override void BuildStyles()
protected override void BuildStyles()
{
BuildAlertTypeStyle();
BuildMessageLabelStyle();
@ -196,7 +196,7 @@ internal class AlertTheme : ControlTheme
}
}
public override void BuildControlTemplate()
protected override IControlTemplate BuildControlTemplate()
{
var controlTemplate = new FuncControlTemplate<Alert>((alert, scope) =>
{
@ -253,9 +253,8 @@ internal class AlertTheme : ControlTheme
return borderContainer;
});
var setter = new Setter(TemplatedControl.TemplateProperty, controlTemplate);
Add(setter);
return controlTemplate;
}
private Border CreateBorderContainer(Alert alert, INameScope scope)

View File

@ -12,7 +12,7 @@ public class CheckBoxTheme : ControlTheme
{
}
public override void BuildStyles()
protected override void BuildStyles()
{
Add(new Setter(CheckBox.CheckIndicatorSizeProperty, new DynamicResourceExtension(CheckBoxResourceKey.CheckIndicatorSize)));
Add(new Setter(CheckBox.PaddingInlineProperty, new DynamicResourceExtension(GlobalResourceKey.PaddingXS)));

View File

@ -1,12 +1,10 @@
using AtomUI.ColorSystem;
using AtomUI.Data;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
namespace AtomUI.Controls;
@ -17,7 +15,7 @@ public enum PresetEmptyImage
Default,
}
public partial class EmptyIndicator : Control,
public partial class EmptyIndicator : TemplatedControl,
IControlCustomStyle
{
@ -96,42 +94,7 @@ public partial class EmptyIndicator : Control,
_customStyle = this;
_controlTokenBinder = new ControlTokenBinder(this, EmptyIndicatorToken.ID);
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
if (!_initialized) {
_customStyle.SetupUi();
_initialized = true;
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (_initialized) {
if (change.Property == SizeTypeProperty) {
HandleSizeTypeChanged();
} else if (change.Property == DescriptionMarginProperty) {
_description!.Margin = new Thickness(0, _descriptionMargin, 0, 0);
}
}
}
private void HandleSizeTypeChanged()
{
_customStyle.ApplySizeTypeStyleConfig();
if (_svg is not null) {
if (SizeType == SizeType.Large) {
_svg.Height = _emptyImgHeightToken;
} else if (SizeType == SizeType.Middle) {
_svg.Height = _emptyImgHeightMDToken;
} else {
_svg.Height = _emptyImgHeightSMToken;
}
}
}
private void CheckImageSource()
{
var imageSettedCount = 0;
@ -163,18 +126,6 @@ public partial class EmptyIndicator : Control,
_controlTokenBinder.AddControlBinding(ColorFillTertiaryTokenProperty, GlobalResourceKey.ColorFillTertiary);
_controlTokenBinder.AddControlBinding(ColorFillQuaternaryTokenProperty, GlobalResourceKey.ColorFillQuaternary);
_controlTokenBinder.AddControlBinding(ColorBgContainerTokenProperty, GlobalResourceKey.ColorBgContainer);
_controlTokenBinder.AddControlBinding(_description!, TextElement.ForegroundProperty,
GlobalResourceKey.ColorTextDescription);
}
void IControlCustomStyle.ApplySizeTypeStyleConfig()
{
if (SizeType == SizeType.Small ||
SizeType == SizeType.Middle) {
_controlTokenBinder.AddControlBinding(DescriptionMarginProperty, GlobalResourceKey.MarginXS);
} else {
_controlTokenBinder.AddControlBinding(DescriptionMarginProperty, GlobalResourceKey.MarginSM);
}
}
void IControlCustomStyle.SetupUi()
@ -182,31 +133,6 @@ public partial class EmptyIndicator : Control,
HorizontalAlignment = HorizontalAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
CheckImageSource();
_layout = new StackPanel()
{
Orientation = Orientation.Vertical
};
LogicalChildren.Add(_layout);
VisualChildren.Add(_layout);
_svg = new Avalonia.Svg.Svg(new Uri("https://github.com/avaloniaui"));
_layout.Children.Add(_svg);
_description = new TextBlock()
{
HorizontalAlignment = HorizontalAlignment.Center,
TextWrapping = TextWrapping.Wrap,
Text = Description ?? "No data"
};
BindUtils.RelayBind(this, IsShowDescriptionProperty, _description, TextBlock.IsVisibleProperty);
_layout.Children.Add(_description);
_customStyle.ApplyFixedStyleConfig();
_customStyle.ApplySizeTypeStyleConfig();
_description.Margin = new Thickness(0, _descriptionMargin, 0, 0);
SetupImage();
HandleSizeTypeChanged();
}
private void SetupImage()
@ -234,6 +160,19 @@ public partial class EmptyIndicator : Control,
_svg.Path = ImagePath;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_customStyle.ApplyFixedStyleConfig();
_customStyle.HandleTemplateApplied(e.NameScope);
}
void IControlCustomStyle.HandleTemplateApplied(INameScope scope)
{
_svg = scope.Find<Avalonia.Svg.Svg>(EmptyIndicatorTheme.SvgImagePart);
SetupImage();
}
#endregion
}

View File

@ -5,12 +5,15 @@ namespace AtomUI.Controls;
public partial class EmptyIndicator
{
private double _descriptionMargin;
private static readonly DirectProperty<EmptyIndicator, double> DescriptionMarginProperty =
AvaloniaProperty.RegisterDirect<EmptyIndicator, double>(
nameof(_descriptionMargin),
o => o._descriptionMargin,
(o, v) => o._descriptionMargin = v);
internal static readonly StyledProperty<double> DescriptionMarginProperty =
AvaloniaProperty.Register<EmptyIndicator, double>(
nameof(DescriptionMargin));
internal double DescriptionMargin
{
get => GetValue(DescriptionMarginProperty);
set => SetValue(DescriptionMarginProperty, value);
}
#region Control token

View File

@ -0,0 +1,96 @@
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media;
using Avalonia.Styling;
namespace AtomUI.Controls;
[ControlThemeProvider]
public class EmptyIndicatorTheme : ControlTheme
{
public const string SvgImagePart = "PART_SvgImage";
public EmptyIndicatorTheme()
: base(typeof(EmptyIndicator))
{}
protected override IControlTemplate? BuildControlTemplate()
{
return new FuncControlTemplate<EmptyIndicator>((indicator, scope) =>
{
var layout = new StackPanel()
{
Orientation = Orientation.Vertical
};
var svg = new Avalonia.Svg.Svg(new Uri("https://github.com/avaloniaui"))
{
Name = SvgImagePart
};
layout.Children.Add(svg);
svg.RegisterInNameScope(scope);
var description = new TextBlock()
{
HorizontalAlignment = HorizontalAlignment.Center,
TextWrapping = TextWrapping.Wrap,
Text = indicator.Description ?? "No data"
};
BindUtils.CreateTokenBinding(description, TextBlock.ForegroundProperty, GlobalResourceKey.ColorTextDescription);
BindUtils.RelayBind(indicator, EmptyIndicator.DescriptionMarginProperty, description, TextBlock.MarginProperty,
d => new Thickness(0, d, 0, 0));
layout.Children.Add(description);
return layout;
});
}
protected override void BuildStyles()
{
// 设置本身样式
var sizeSmallAndMiddleStyle = new Style(selector => Selectors.Or(selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Middle),
selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Small)));
sizeSmallAndMiddleStyle.Setters.Add(new Setter(EmptyIndicator.DescriptionMarginProperty, new DynamicResourceExtension(GlobalResourceKey.MarginXS)));
Add(sizeSmallAndMiddleStyle);
var sizeLargeStyle = new Style(selector => selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Large));
sizeLargeStyle.Setters.Add(new Setter(EmptyIndicator.DescriptionMarginProperty, new DynamicResourceExtension(GlobalResourceKey.MarginSM)));
Add(sizeLargeStyle);
BuildSvgStyle();
}
private void BuildSvgStyle()
{
var svgSelector = default(Selector).Nesting().Template().OfType<Avalonia.Svg.Svg>();
{
var largeSizeStyle = new Style(selector => selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Large));
var svgStyle = new Style(selector => svgSelector);
svgStyle.Setters.Add(new Setter(Layoutable.HeightProperty, new DynamicResourceExtension(EmptyIndicatorResourceKey.EmptyImgHeight)));
largeSizeStyle.Add(svgStyle);
Add(largeSizeStyle);
}
{
var middleSizeStyle = new Style(selector => selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Middle));
var svgStyle = new Style(selector => svgSelector);
svgStyle.Setters.Add(new Setter(Layoutable.HeightProperty, new DynamicResourceExtension(EmptyIndicatorResourceKey.EmptyImgHeightMD)));
middleSizeStyle.Add(svgStyle);
Add(middleSizeStyle);
}
{
var smallSizeStyle = new Style(selector => selector.Nesting().PropertyEquals(EmptyIndicator.SizeTypeProperty, SizeType.Small));
var svgStyle = new Style(selector => svgSelector);
svgStyle.Setters.Add(new Setter(Layoutable.HeightProperty, new DynamicResourceExtension(EmptyIndicatorResourceKey.EmptyImgHeightSM)));
smallSizeStyle.Add(svgStyle);
Add(smallSizeStyle);
}
}
}

View File

@ -102,7 +102,6 @@ internal static class PopupInterceptorsRegister
{
public static void Register(Harmony harmony)
{
Console.WriteLine("Register");
RegisterPopupFlyoutBaseCreatePopup(harmony);
RegisterPopupUpdateHostPositionPrefix(harmony);
RegisterPopupUpdateHostPositionPostfix(harmony);

View File

@ -1,18 +1,24 @@
namespace AtomUI;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Styling;
namespace AtomUI;
using AvaloniaControlTheme = Avalonia.Styling.ControlTheme;
public class ControlTheme : AvaloniaControlTheme
{
public ControlTheme() { }
public ControlTheme(Type targetType) : base(targetType) {}
public void Build()
{
BuildStyles();
BuildControlTemplate();
var template = BuildControlTemplate();
if (template is not null) {
Add(new Setter(TemplatedControl.TemplateProperty, template));
}
}
public virtual void BuildControlTemplate() {}
public virtual void BuildStyles() {}
protected virtual IControlTemplate? BuildControlTemplate() { return default; }
protected virtual void BuildStyles() {}
}