SukiBackground.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Rendering.Composition;
  4. using SukiUI.Enums;
  5. using SukiUI.Utilities.Effects;
  6. namespace SukiUI.Controls
  7. {
  8. public class SukiBackground : Control
  9. {
  10. public static readonly StyledProperty<SukiBackgroundStyle> StyleProperty =
  11. AvaloniaProperty.Register<SukiWindow, SukiBackgroundStyle>(nameof(Style),
  12. defaultValue: SukiBackgroundStyle.Gradient);
  13. /// <summary>
  14. /// Which of the default background styles to use - DEFAULT: Gradient
  15. /// </summary>
  16. public SukiBackgroundStyle Style
  17. {
  18. get => GetValue(StyleProperty);
  19. set => SetValue(StyleProperty, value);
  20. }
  21. public static readonly StyledProperty<string?> ShaderFileProperty =
  22. AvaloniaProperty.Register<SukiWindow, string?>(nameof(ShaderFile));
  23. /// <summary>
  24. /// Specify a filename of an EMBEDDED RESOURCE file of type `.SkSL` with or without extension and it will be loaded and displayed.
  25. /// This takes priority over the <see cref="ShaderCode"/> property, which in turns takes priority over <see cref="Style"/>.
  26. /// </summary>
  27. public string? ShaderFile
  28. {
  29. get => GetValue(ShaderFileProperty);
  30. set => SetValue(ShaderFileProperty, value);
  31. }
  32. public static readonly StyledProperty<string?> ShaderCodeProperty =
  33. AvaloniaProperty.Register<SukiWindow, string?>(nameof(ShaderCode));
  34. /// <summary>
  35. /// Specify the shader code to use directly, simpler if you don't want to create an .SkSL file or want to generate the shader effect at runtime in some way.
  36. /// This takes priority over the <see cref="Style"/> property, but is second in priority to <see cref="ShaderFile"/> if it is set.
  37. /// </summary>
  38. public string? ShaderCode
  39. {
  40. get => GetValue(ShaderCodeProperty);
  41. set => SetValue(ShaderCodeProperty, value);
  42. }
  43. public static readonly StyledProperty<bool> AnimationEnabledProperty =
  44. AvaloniaProperty.Register<SukiWindow, bool>(nameof(AnimationEnabled), defaultValue: false);
  45. /// <summary>
  46. /// [WARNING: This feature is experimental and has relatively high GPU utilisation] Enables/disables animations - DEFAULT: False
  47. /// </summary>
  48. public bool AnimationEnabled
  49. {
  50. get => GetValue(AnimationEnabledProperty);
  51. set => SetValue(AnimationEnabledProperty, value);
  52. }
  53. public static readonly StyledProperty<bool> TransitionsEnabledProperty =
  54. AvaloniaProperty.Register<SukiBackground, bool>(nameof(TransitionsEnabled), defaultValue: false);
  55. /// <summary>
  56. /// Enables/disables transition animations when switching backgrounds, Currently non-functional - DEFAULT: False
  57. /// </summary>
  58. public bool TransitionsEnabled
  59. {
  60. get => GetValue(TransitionsEnabledProperty);
  61. set => SetValue(TransitionsEnabledProperty, value);
  62. }
  63. public static readonly StyledProperty<double> TransitionTimeProperty =
  64. AvaloniaProperty.Register<SukiBackground, double>(nameof(TransitionTime), defaultValue: 1.0);
  65. /// <summary>
  66. /// The amount of time in seconds the background transition will take - DEFAULT: 1.0
  67. /// </summary>
  68. public double TransitionTime
  69. {
  70. get => GetValue(TransitionTimeProperty);
  71. set => SetValue(TransitionTimeProperty, value);
  72. }
  73. public static readonly StyledProperty<bool> ForceSoftwareRenderingProperty = AvaloniaProperty.Register<SukiBackground, bool>(nameof(ForceSoftwareRendering));
  74. public bool ForceSoftwareRendering
  75. {
  76. get => GetValue(ForceSoftwareRenderingProperty);
  77. set => SetValue(ForceSoftwareRenderingProperty, value);
  78. }
  79. private CompositionCustomVisual? _customVisual;
  80. public SukiBackground()
  81. {
  82. IsHitTestVisible = false;
  83. }
  84. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
  85. {
  86. base.OnAttachedToVisualTree(e);
  87. var comp = ElementComposition.GetElementVisual(this)?.Compositor;
  88. if (comp == null || _customVisual?.Compositor == comp) return;
  89. var visualHandler = new EffectBackgroundDraw();
  90. _customVisual = comp.CreateCustomVisual(visualHandler);
  91. ElementComposition.SetElementChildVisual(this, _customVisual);
  92. _customVisual.SendHandlerMessage(TransitionTime);
  93. HandleBackgroundStyleChanges();
  94. Update();
  95. }
  96. private void Update()
  97. {
  98. if (_customVisual == null) return;
  99. _customVisual.Size = new Vector(Bounds.Width, Bounds.Height);
  100. }
  101. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  102. {
  103. base.OnPropertyChanged(change);
  104. if (change.Property == BoundsProperty)
  105. Update();
  106. else if (change.Property == ForceSoftwareRenderingProperty && change.NewValue is bool forceSoftwareRendering)
  107. _customVisual?.SendHandlerMessage(forceSoftwareRendering
  108. ? EffectDrawBase.EnableForceSoftwareRendering
  109. : EffectDrawBase.DisableForceSoftwareRendering);
  110. else if(change.Property == TransitionsEnabledProperty && change.NewValue is bool transitionEnabled)
  111. _customVisual?.SendHandlerMessage(transitionEnabled
  112. ? EffectBackgroundDraw.EnableTransitions
  113. : EffectBackgroundDraw.DisableTransitions);
  114. else if(change.Property == TransitionTimeProperty && change.NewValue is double transitionTime)
  115. _customVisual?.SendHandlerMessage(transitionTime);
  116. else if (change.Property == AnimationEnabledProperty && change.NewValue is bool animationEnabled)
  117. _customVisual?.SendHandlerMessage(animationEnabled
  118. ? EffectDrawBase.StartAnimations
  119. : EffectDrawBase.StopAnimations);
  120. else if(change.Property == StyleProperty || change.Property == ShaderFileProperty || change.Property == ShaderCodeProperty)
  121. HandleBackgroundStyleChanges();
  122. }
  123. private void HandleBackgroundStyleChanges()
  124. {
  125. SukiEffect effect;
  126. if (ShaderFile is not null)
  127. effect = SukiEffect.FromEmbeddedResource(ShaderFile);
  128. else if (ShaderCode is not null)
  129. effect = SukiEffect.FromString(ShaderCode);
  130. else
  131. effect = SukiEffect.FromEmbeddedResource(Style.ToString());
  132. _customVisual?.SendHandlerMessage(effect);
  133. }
  134. }
  135. }