Loading.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using System.Collections.Generic;
  2. using Avalonia;
  3. using Avalonia.Controls;
  4. using Avalonia.Markup.Xaml.MarkupExtensions;
  5. using Avalonia.Media;
  6. using Avalonia.Media.Immutable;
  7. using Avalonia.Rendering.Composition;
  8. using SkiaSharp;
  9. using SukiUI.Extensions;
  10. using SukiUI.Utilities.Effects;
  11. namespace SukiUI.Controls
  12. {
  13. public class Loading : Control
  14. {
  15. public static readonly StyledProperty<LoadingStyle> LoadingStyleProperty =
  16. AvaloniaProperty.Register<Loading, LoadingStyle>(nameof(LoadingStyle), defaultValue: LoadingStyle.Simple);
  17. public LoadingStyle LoadingStyle
  18. {
  19. get => GetValue(LoadingStyleProperty);
  20. set => SetValue(LoadingStyleProperty, value);
  21. }
  22. public static readonly StyledProperty<IBrush?> ForegroundProperty =
  23. AvaloniaProperty.Register<Loading, IBrush?>(nameof(Foreground));
  24. public IBrush? Foreground
  25. {
  26. get => GetValue(ForegroundProperty);
  27. set => SetValue(ForegroundProperty, value);
  28. }
  29. private static readonly IReadOnlyDictionary<LoadingStyle, SukiEffect> Effects =
  30. new Dictionary<LoadingStyle, SukiEffect>()
  31. {
  32. { LoadingStyle.Simple, SukiEffect.FromEmbeddedResource("simple") },
  33. { LoadingStyle.Glow, SukiEffect.FromEmbeddedResource("glow") },
  34. { LoadingStyle.Pellets, SukiEffect.FromEmbeddedResource("pellets") },
  35. };
  36. private CompositionCustomVisual? _customVisual;
  37. public Loading()
  38. {
  39. Width = 50;
  40. Height = 50;
  41. }
  42. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  43. {
  44. base.OnAttachedToVisualTree(e);
  45. var comp = ElementComposition.GetElementVisual(this)?.Compositor;
  46. if (comp == null || _customVisual?.Compositor == comp) return;
  47. var visualHandler = new LoadingEffectDraw();
  48. _customVisual = comp.CreateCustomVisual(visualHandler);
  49. ElementComposition.SetElementChildVisual(this, _customVisual);
  50. _customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations);
  51. if (Foreground is null)
  52. this[!ForegroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  53. if (Foreground is ImmutableSolidColorBrush brush)
  54. brush.Color.ToFloatArrayNonAlloc(_color);
  55. _customVisual.SendHandlerMessage(_color);
  56. _customVisual.SendHandlerMessage(Effects[LoadingStyle]);
  57. Update();
  58. }
  59. private void Update()
  60. {
  61. if (_customVisual == null) return;
  62. _customVisual.Size = new Vector(Bounds.Width, Bounds.Height);
  63. }
  64. private readonly float[] _color = new float[3];
  65. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  66. {
  67. base.OnPropertyChanged(change);
  68. if (change.Property == BoundsProperty)
  69. Update();
  70. else if (change.Property == ForegroundProperty && Foreground is ImmutableSolidColorBrush brush)
  71. {
  72. brush.Color.ToFloatArrayNonAlloc(_color);
  73. _customVisual?.SendHandlerMessage(_color);
  74. }
  75. else if (change.Property == LoadingStyleProperty)
  76. _customVisual?.SendHandlerMessage(Effects[LoadingStyle]);
  77. }
  78. public class LoadingEffectDraw : EffectDrawBase
  79. {
  80. private float[] _color = { 1.0f, 0f, 0f };
  81. public LoadingEffectDraw()
  82. {
  83. AnimationSpeedScale = 2f;
  84. }
  85. protected override void Render(SKCanvas canvas, SKRect rect)
  86. {
  87. using var mainShaderPaint = new SKPaint();
  88. if (Effect is not null)
  89. {
  90. using var shader = EffectWithCustomUniforms(effect => new SKRuntimeEffectUniforms(effect)
  91. {
  92. { "iForeground", _color }
  93. });
  94. mainShaderPaint.Shader = shader;
  95. canvas.DrawRect(rect, mainShaderPaint);
  96. }
  97. }
  98. // I'm not really sure how to render this properly in software fallback scenarios.
  99. // This is likely to cause issues with the previewer.
  100. // Might be worth just drawing a circle or something...
  101. protected override void RenderSoftware(SKCanvas canvas, SKRect rect)
  102. {
  103. throw new System.NotImplementedException();
  104. }
  105. public override void OnMessage(object message)
  106. {
  107. base.OnMessage(message);
  108. if (message is float[] color)
  109. _color = color;
  110. }
  111. }
  112. }
  113. public enum LoadingStyle
  114. {
  115. Simple,
  116. Glow,
  117. Pellets
  118. }
  119. }