From 6b613630bba7106a27468257e9dc3207d8856a01 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 16 Oct 2024 12:31:07 +0100 Subject: [PATCH 1/6] Rewrite internals of `EffectDrawBase` --- .../Features/Effects/ShaderToyRenderer.cs | 40 ++++++---- SukiUI/Controls/Loading.cs | 63 +++++++++++----- SukiUI/Controls/SukiBackground.cs | 73 ++++++++++--------- SukiUI/Controls/SukiWindow.axaml.cs | 1 + .../Utilities/Effects/EffectBackgroundDraw.cs | 16 ++-- SukiUI/Utilities/Effects/EffectDrawBase.cs | 52 +++++++++---- 6 files changed, 157 insertions(+), 88 deletions(-) diff --git a/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs b/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs index 677ed0c10..f915d64af 100644 --- a/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs +++ b/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs @@ -1,7 +1,7 @@ +using System; using Avalonia; using Avalonia.Controls; -using Avalonia.Media; -using Avalonia.Threading; +using Avalonia.Rendering.Composition; using SkiaSharp; using SukiUI.Utilities.Effects; @@ -9,28 +9,41 @@ namespace SukiUI.Demo.Features.Effects { public class ShaderToyRenderer : Control { - private readonly ShaderToyDraw _draw; + private CompositionCustomVisual? _customVisual; + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + var comp = ElementComposition.GetElementVisual(this)?.Compositor; + if (comp == null || _customVisual?.Compositor == comp) return; + var visualHandler = new ShaderToyDraw(); + _customVisual = comp.CreateCustomVisual(visualHandler); + ElementComposition.SetElementChildVisual(this, _customVisual); + _customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations); + Update(); + } - public ShaderToyRenderer() + private void Update() { - _draw = new ShaderToyDraw(Bounds); + if (_customVisual == null) return; + _customVisual.Size = new Vector(Bounds.Width, Bounds.Height); } - public override void Render(DrawingContext context) + public void SetEffect(SukiEffect effect) { - _draw.Bounds = Bounds; - context.Custom(_draw); + _customVisual?.SendHandlerMessage(effect); } - public void SetEffect(SukiEffect effect) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - _draw.Effect = effect; - Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); + base.OnPropertyChanged(change); + if(change.Property == BoundsProperty) + Update(); } private class ShaderToyDraw : EffectDrawBase { - public ShaderToyDraw(Rect bounds) : base(bounds) + public ShaderToyDraw() { AnimationEnabled = true; AnimationSpeedScale = 2f; @@ -38,8 +51,6 @@ public ShaderToyDraw(Rect bounds) : base(bounds) protected override void Render(SKCanvas canvas, SKRect rect) { - canvas.Scale(1,-1); - canvas.Translate(0, (float)-Bounds.Height); using var mainShaderPaint = new SKPaint(); if (Effect is not null) @@ -48,7 +59,6 @@ protected override void Render(SKCanvas canvas, SKRect rect) mainShaderPaint.Shader = shader; canvas.DrawRect(rect, mainShaderPaint); } - canvas.Restore(); } protected override void RenderSoftware(SKCanvas canvas, SKRect rect) diff --git a/SukiUI/Controls/Loading.cs b/SukiUI/Controls/Loading.cs index 089fc2d07..e252c1721 100644 --- a/SukiUI/Controls/Loading.cs +++ b/SukiUI/Controls/Loading.cs @@ -4,6 +4,7 @@ using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Media; using Avalonia.Media.Immutable; +using Avalonia.Rendering.Composition; using SkiaSharp; using SukiUI.Extensions; using SukiUI.Utilities.Effects; @@ -37,62 +38,77 @@ public IBrush? Foreground { LoadingStyle.Glow, SukiEffect.FromEmbeddedResource("glow") }, { LoadingStyle.Pellets, SukiEffect.FromEmbeddedResource("pellets") }, }; - - private readonly LoadingEffectDraw _draw; - + + private CompositionCustomVisual? _customVisual; + public Loading() { Width = 50; Height = 50; - _draw = new LoadingEffectDraw(Bounds); } - public override void Render(DrawingContext context) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - _draw.Bounds = Bounds; - _draw.Effect = Effects[LoadingStyle]; + base.OnAttachedToVisualTree(e); + var comp = ElementComposition.GetElementVisual(this)?.Compositor; + if (comp == null || _customVisual?.Compositor == comp) return; + var visualHandler = new LoadingEffectDraw(); + _customVisual = comp.CreateCustomVisual(visualHandler); + ElementComposition.SetElementChildVisual(this, _customVisual); + _customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations); if (Foreground is null) this[!ForegroundProperty] = new DynamicResourceExtension("SukiPrimaryColor"); if (Foreground is ImmutableSolidColorBrush brush) - brush.Color.ToFloatArrayNonAlloc(_draw.Color); - context.Custom(_draw); + brush.Color.ToFloatArrayNonAlloc(_color); + _customVisual.SendHandlerMessage(_color); + _customVisual.SendHandlerMessage(Effects[LoadingStyle]); + Update(); + } + + private void Update() + { + if (_customVisual == null) return; + _customVisual.Size = new Vector(Bounds.Width, Bounds.Height); } + private readonly float[] _color = new float[3]; + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); - if (change.Property != ForegroundProperty) return; - if (Foreground is ImmutableSolidColorBrush brush) - brush.Color.ToFloatArrayNonAlloc(_draw.Color); + if (change.Property == BoundsProperty) + Update(); + else if (change.Property == ForegroundProperty && Foreground is ImmutableSolidColorBrush brush) + { + brush.Color.ToFloatArrayNonAlloc(_color); + _customVisual?.SendHandlerMessage(_color); + } + else if (change.Property == LoadingStyleProperty) + _customVisual?.SendHandlerMessage(Effects[LoadingStyle]); } public class LoadingEffectDraw : EffectDrawBase { - public float[] Color { get; } = { 1.0f, 0f, 0f }; + private float[] _color = { 1.0f, 0f, 0f }; - public LoadingEffectDraw(Rect bounds) : base(bounds) + public LoadingEffectDraw() { - AnimationEnabled = true; AnimationSpeedScale = 2f; } protected override void Render(SKCanvas canvas, SKRect rect) { - canvas.Scale(1, -1); - canvas.Translate(0, (float)-Bounds.Height); using var mainShaderPaint = new SKPaint(); if (Effect is not null) { using var shader = EffectWithCustomUniforms(effect => new SKRuntimeEffectUniforms(effect) { - { "iForeground", Color } + { "iForeground", _color } }); mainShaderPaint.Shader = shader; canvas.DrawRect(rect, mainShaderPaint); } - - canvas.Restore(); } // I'm not really sure how to render this properly in software fallback scenarios. @@ -102,6 +118,13 @@ protected override void RenderSoftware(SKCanvas canvas, SKRect rect) { throw new System.NotImplementedException(); } + + public override void OnMessage(object message) + { + base.OnMessage(message); + if (message is float[] color) + _color = color; + } } } diff --git a/SukiUI/Controls/SukiBackground.cs b/SukiUI/Controls/SukiBackground.cs index 6a79f8d19..4f42c1d58 100644 --- a/SukiUI/Controls/SukiBackground.cs +++ b/SukiUI/Controls/SukiBackground.cs @@ -1,9 +1,6 @@ -using System; using Avalonia; using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.Media; -using Avalonia.Threading; +using Avalonia.Rendering.Composition; using SukiUI.Enums; using SukiUI.Utilities.Effects; @@ -54,7 +51,7 @@ public string? ShaderCode AvaloniaProperty.Register(nameof(AnimationEnabled), defaultValue: false); /// - /// Enables/disables animations - DEFAULT: False + /// [WARNING: This feature is experimental and has relatively high GPU utilisation] Enables/disables animations - DEFAULT: False /// public bool AnimationEnabled { @@ -65,8 +62,8 @@ public bool AnimationEnabled public static readonly StyledProperty TransitionsEnabledProperty = AvaloniaProperty.Register(nameof(TransitionsEnabled), defaultValue: false); - /// - /// Enables/disables transition animations when switching backgrounds - DEFAULT: False + /// + /// Enables/disables transition animations when switching backgrounds, Currently non-functional - DEFAULT: False /// public bool TransitionsEnabled { @@ -94,54 +91,64 @@ public bool ForceSoftwareRendering set => SetValue(ForceSoftwareRenderingProperty, value); } - private readonly EffectBackgroundDraw _draw; - + private CompositionCustomVisual? _customVisual; + public SukiBackground() { IsHitTestVisible = false; - _draw = new EffectBackgroundDraw(new Rect(0, 0, Bounds.Width, Bounds.Height)); } - protected override void OnLoaded(RoutedEventArgs e) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - base.OnLoaded(e); - _draw.ForceSoftwareRendering = ForceSoftwareRendering; - _draw.TransitionsEnabled = TransitionsEnabled; - _draw.TransitionTime = TransitionTime; - _draw.AnimationEnabled = AnimationEnabled; + base.OnAttachedToVisualTree(e); + var comp = ElementComposition.GetElementVisual(this)?.Compositor; + if (comp == null || _customVisual?.Compositor == comp) return; + var visualHandler = new EffectBackgroundDraw(); + _customVisual = comp.CreateCustomVisual(visualHandler); + ElementComposition.SetElementChildVisual(this, _customVisual); HandleBackgroundStyleChanges(); + Update(); + } + + private void Update() + { + if (_customVisual == null) return; + _customVisual.Size = new Vector(Bounds.Width, Bounds.Height); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); - if (change.Property == ForceSoftwareRenderingProperty && change.NewValue is bool forceSoftwareRendering) - _draw.ForceSoftwareRendering = forceSoftwareRendering; + if (change.Property == BoundsProperty) + Update(); + else if (change.Property == ForceSoftwareRenderingProperty && change.NewValue is bool forceSoftwareRendering) + _customVisual?.SendHandlerMessage(forceSoftwareRendering + ? EffectDrawBase.EnableForceSoftwareRendering + : EffectDrawBase.DisableForceSoftwareRendering); else if(change.Property == TransitionsEnabledProperty && change.NewValue is bool transitionEnabled) - _draw.TransitionsEnabled = transitionEnabled; + _customVisual?.SendHandlerMessage(transitionEnabled + ? EffectBackgroundDraw.EnableTransitions + : EffectBackgroundDraw.DisableTransitions); else if(change.Property == TransitionTimeProperty && change.NewValue is double transitionTime) - _draw.TransitionTime = transitionTime; - else if(change.Property == AnimationEnabledProperty && change.NewValue is bool animationEnabled) - _draw.AnimationEnabled = animationEnabled; + _customVisual?.SendHandlerMessage(transitionTime); + else if (change.Property == AnimationEnabledProperty && change.NewValue is bool animationEnabled) + _customVisual?.SendHandlerMessage(animationEnabled + ? EffectDrawBase.StartAnimations + : EffectDrawBase.StopAnimations); else if(change.Property == StyleProperty || change.Property == ShaderFileProperty || change.Property == ShaderCodeProperty) HandleBackgroundStyleChanges(); } - - public override void Render(DrawingContext context) - { - _draw.Bounds = Bounds; - context.Custom(_draw); - Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); - } - + private void HandleBackgroundStyleChanges() { + SukiEffect effect; if (ShaderFile is not null) - _draw.Effect = SukiEffect.FromEmbeddedResource(ShaderFile); + effect = SukiEffect.FromEmbeddedResource(ShaderFile); else if (ShaderCode is not null) - _draw.Effect = SukiEffect.FromString(ShaderCode); + effect = SukiEffect.FromString(ShaderCode); else - _draw.Effect = SukiEffect.FromEmbeddedResource(Style.ToString()); + effect = SukiEffect.FromEmbeddedResource(Style.ToString()); + _customVisual?.SendHandlerMessage(effect); } } } \ No newline at end of file diff --git a/SukiUI/Controls/SukiWindow.axaml.cs b/SukiUI/Controls/SukiWindow.axaml.cs index 151d7f3db..1bc9671fc 100644 --- a/SukiUI/Controls/SukiWindow.axaml.cs +++ b/SukiUI/Controls/SukiWindow.axaml.cs @@ -109,6 +109,7 @@ public bool CanMove public static readonly StyledProperty BackgroundAnimationEnabledProperty = AvaloniaProperty.Register(nameof(BackgroundAnimationEnabled), defaultValue: false); + /// public bool BackgroundAnimationEnabled { get => GetValue(BackgroundAnimationEnabledProperty); diff --git a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs index 03f1a5a3d..b45b0d5c7 100644 --- a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs +++ b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs @@ -9,6 +9,8 @@ namespace SukiUI.Utilities.Effects { internal class EffectBackgroundDraw : EffectDrawBase { + public static readonly object EnableTransitions = new(), DisableTransitions = new(); + internal bool TransitionsEnabled { get; set; } internal double TransitionTime { get; set; } @@ -20,10 +22,6 @@ internal class EffectBackgroundDraw : EffectDrawBase private float _transitionStartTime; private float _transitionEndTime; - public EffectBackgroundDraw(Rect bounds) : base(bounds) - { - } - protected override void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue) { if (!TransitionsEnabled) return; @@ -33,10 +31,15 @@ protected override void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue _transitionEndTime = TransitionSeconds + (float)Math.Max(0, TransitionTime); } - protected override void Render(SKCanvas canvas, SKRect rect) + public override void OnMessage(object message) { - canvas.Clear(SKColors.Transparent); + base.OnMessage(message); + if (message is float time) + TransitionTime = time; + } + protected override void Render(SKCanvas canvas, SKRect rect) + { if (Effect is not null) { using var paint = new SKPaint(); @@ -46,7 +49,6 @@ protected override void Render(SKCanvas canvas, SKRect rect) } if (_oldEffect is not null) { - using var paint = new SKPaint(); // TODO: Investigate how to blend the shaders better - currently the only problem with this system. // Blend modes effect the transition quite heavily, only these 3 seem to work in any reasonable way. diff --git a/SukiUI/Utilities/Effects/EffectDrawBase.cs b/SukiUI/Utilities/Effects/EffectDrawBase.cs index cfb4a79e1..4a35e5c78 100644 --- a/SukiUI/Utilities/Effects/EffectDrawBase.cs +++ b/SukiUI/Utilities/Effects/EffectDrawBase.cs @@ -3,6 +3,7 @@ using Avalonia; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using Avalonia.Rendering.SceneGraph; using Avalonia.Skia; using Avalonia.Styling; @@ -11,10 +12,11 @@ namespace SukiUI.Utilities.Effects { - public abstract class EffectDrawBase : ICustomDrawOperation + public abstract class EffectDrawBase : CompositionCustomVisualHandler { - public Rect Bounds { get; set; } - + public static readonly object StartAnimations = new(), StopAnimations = new(), + EnableForceSoftwareRendering = new(), DisableForceSoftwareRendering = new(); + private SukiEffect? _effect; public SukiEffect? Effect @@ -29,7 +31,7 @@ public SukiEffect? Effect } } - private bool _animationEnabled = true; + private bool _animationEnabled; public bool AnimationEnabled { get => _animationEnabled; @@ -51,30 +53,54 @@ public bool AnimationEnabled protected float AnimationSeconds => (float)_animationTick.Elapsed.TotalSeconds; - private readonly Stopwatch _animationTick = Stopwatch.StartNew(); + private readonly Stopwatch _animationTick = new(); - protected EffectDrawBase(Rect bounds) + protected EffectDrawBase() { - Bounds = bounds; var sTheme = SukiTheme.GetInstance(); sTheme.OnBaseThemeChanged += v => ActiveVariant = v; ActiveVariant = sTheme.ActiveBaseTheme; sTheme.OnColorThemeChanged += t => ActiveTheme = t; ActiveTheme = sTheme.ActiveColorTheme!; - } - public void Render(ImmediateDrawingContext context) + public override void OnRender(ImmediateDrawingContext context) { var leaseFeature = context.TryGetFeature(); if (leaseFeature is null) throw new InvalidOperationException("Unable to lease Skia API"); using var lease = leaseFeature.Lease(); - var rect = SKRect.Create((float)Bounds.Width, (float)Bounds.Height); - if(lease.GrContext is null || ForceSoftwareRendering) // GrContext is null whenever + var rect = SKRect.Create((float)EffectiveSize.X, (float)EffectiveSize.Y); + if(lease.GrContext is null || ForceSoftwareRendering) // GrContext is null whenever there is no hardware acceleration available RenderSoftware(lease.SkCanvas, rect); else Render(lease.SkCanvas, rect); } + + public override void OnMessage(object message) + { + if (message == StartAnimations) + { + AnimationEnabled = true; + RegisterForNextAnimationFrameUpdate(); + } + else if (message == StopAnimations) + { + AnimationEnabled = false; + } + else if (message is SukiEffect effect) + { + Effect = effect; + } + } + + public override void OnAnimationFrameUpdate() + { + if (!AnimationEnabled) return; + Invalidate(GetRenderBounds()); + RegisterForNextAnimationFrameUpdate(); + } + + //protected abstract void InvalidateInternal(); /// /// Called every frame to render content. @@ -90,13 +116,13 @@ public void Render(ImmediateDrawingContext context) EffectWithUniforms(Effect, alpha); protected SKShader? EffectWithUniforms(SukiEffect? effect, float alpha = 1f) => - effect?.ToShaderWithUniforms(AnimationSeconds, ActiveVariant, Bounds, AnimationSpeedScale, alpha); + effect?.ToShaderWithUniforms(AnimationSeconds, ActiveVariant, GetRenderBounds(), AnimationSpeedScale, alpha); protected SKShader? EffectWithCustomUniforms(Func uniformFactory, float alpha = 1f) => EffectWithCustomUniforms(Effect, uniformFactory, alpha); protected SKShader? EffectWithCustomUniforms(SukiEffect? effect, Func uniformFactory, float alpha = 1f) => - effect?.ToShaderWithCustomUniforms(uniformFactory, AnimationSeconds, Bounds, AnimationSpeedScale, alpha); + effect?.ToShaderWithCustomUniforms(uniformFactory, AnimationSeconds, GetRenderBounds(), AnimationSpeedScale, alpha); protected virtual void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue) { From 10ce6fea7416509bd461cbb66d69f620021d48e9 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 16 Oct 2024 14:38:04 +0100 Subject: [PATCH 2/6] Fix some outstanding issues with `SukiTransitioningContentControl` --- .../Features/Effects/ShaderToyRenderer.cs | 3 ++ .../SukiTransitioningContentControl.axaml.cs | 50 +++++++++++++------ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs b/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs index f915d64af..7dadb4d88 100644 --- a/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs +++ b/SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs @@ -10,6 +10,7 @@ namespace SukiUI.Demo.Features.Effects public class ShaderToyRenderer : Control { private CompositionCustomVisual? _customVisual; + private SukiEffect? _sukiEffect; protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { @@ -20,6 +21,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) _customVisual = comp.CreateCustomVisual(visualHandler); ElementComposition.SetElementChildVisual(this, _customVisual); _customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations); + if(_sukiEffect != null) _customVisual.SendHandlerMessage(_sukiEffect); Update(); } @@ -31,6 +33,7 @@ private void Update() public void SetEffect(SukiEffect effect) { + _sukiEffect = effect; _customVisual?.SendHandlerMessage(effect); } diff --git a/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs b/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs index 7715c6f78..f68092114 100644 --- a/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs +++ b/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; +using Avalonia.Media; using Avalonia.Styling; using Avalonia.Threading; @@ -44,14 +45,16 @@ public object? Content private bool _isFirstBufferActive; - private ContentPresenter _firstBuffer = null!; - private ContentPresenter _secondBuffer = null!; + private ContentPresenter? _firstBuffer = null; + private ContentPresenter? _secondBuffer = null; private static readonly Animation FadeIn; private static readonly Animation FadeOut; - private ContentPresenter To => _isFirstBufferActive ? _secondBuffer : _firstBuffer; - private ContentPresenter From => _isFirstBufferActive ? _firstBuffer : _secondBuffer; + private ContentPresenter? To => _isFirstBufferActive ? _firstBuffer : _secondBuffer; + private ContentPresenter? From => _isFirstBufferActive ? _secondBuffer : _firstBuffer; + + private object? _contentBeforeApplied; static SukiTransitioningContentControl() { @@ -123,6 +126,7 @@ static SukiTransitioningContentControl() } private CancellationTokenSource _animCancellationToken = new(); + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -138,34 +142,50 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) _firstBuffer = fBuff; if (e.NameScope.Get("PART_SecondBufferControl") is { } sBuff) _secondBuffer = sBuff; + if (_contentBeforeApplied != null) + { + PushContent(_contentBeforeApplied); + _contentBeforeApplied = null; + } } - public void PushContent(object? content) + private void PushContent(object? content) { - if (content is null) return; + if (To is null || From is null) + { + _contentBeforeApplied = content; + return; + } _animCancellationToken.Cancel(); _animCancellationToken.Dispose(); _animCancellationToken = new CancellationTokenSource(); - - FirstBuffer = null; - SecondBuffer = null; if (_isFirstBufferActive) SecondBuffer = content; else FirstBuffer = content; + _isFirstBufferActive = !_isFirstBufferActive; try { - FadeOut.RunAsync(From, _animCancellationToken.Token); - FadeIn.RunAsync(To, _animCancellationToken.Token); + FadeOut.RunAsync(From, _animCancellationToken.Token).ContinueWith(_ => + { + Dispatcher.UIThread.Invoke(() => + { + From.IsHitTestVisible = false; + if (_isFirstBufferActive) SecondBuffer = null; + else FirstBuffer = null; + }); + }); + FadeIn.RunAsync(To, _animCancellationToken.Token).ContinueWith(_ => + Dispatcher.UIThread.Invoke(() => + { + Console.WriteLine(To.Name); + return To.IsHitTestVisible = true; + })); } catch { // ignored } - - To.IsHitTestVisible = true; - From.IsHitTestVisible = false; - _isFirstBufferActive = !_isFirstBufferActive; } protected override void OnUnloaded(RoutedEventArgs e) From 75e3dbcddd1fef2039759a05e53f5552beac4f34 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 16 Oct 2024 14:49:40 +0100 Subject: [PATCH 3/6] Fix background transitions. --- SukiUI/Controls/SukiBackground.cs | 1 + SukiUI/Utilities/Effects/EffectBackgroundDraw.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/SukiUI/Controls/SukiBackground.cs b/SukiUI/Controls/SukiBackground.cs index 4f42c1d58..576e3e9f4 100644 --- a/SukiUI/Controls/SukiBackground.cs +++ b/SukiUI/Controls/SukiBackground.cs @@ -106,6 +106,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) var visualHandler = new EffectBackgroundDraw(); _customVisual = comp.CreateCustomVisual(visualHandler); ElementComposition.SetElementChildVisual(this, _customVisual); + _customVisual.SendHandlerMessage(TransitionTime); HandleBackgroundStyleChanges(); Update(); } diff --git a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs index b45b0d5c7..93ffd0e43 100644 --- a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs +++ b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs @@ -34,8 +34,9 @@ protected override void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue public override void OnMessage(object message) { base.OnMessage(message); - if (message is float time) - TransitionTime = time; + if (message == EnableTransitions) TransitionsEnabled = true; + else if (message == DisableTransitions) TransitionsEnabled = false; + if (message is double time) TransitionTime = time; } protected override void Render(SKCanvas canvas, SKRect rect) @@ -59,7 +60,10 @@ protected override void Render(SKCanvas canvas, SKRect rect) using var shader = EffectWithUniforms(_oldEffect, (float)(1 - lerped)); paint.Shader = shader; if (lerped < 1) + { canvas.DrawRect(rect, paint); + if(!AnimationEnabled) Invalidate(); + } else _oldEffect = null; } From 9893ca7210f6696db7423f74bf320ea5d3db9aba Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 16 Oct 2024 14:55:15 +0100 Subject: [PATCH 4/6] Missed a debug call... --- SukiUI/Controls/SukiTransitioningContentControl.axaml.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs b/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs index f68092114..d6a62ae2f 100644 --- a/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs +++ b/SukiUI/Controls/SukiTransitioningContentControl.axaml.cs @@ -176,11 +176,7 @@ private void PushContent(object? content) }); }); FadeIn.RunAsync(To, _animCancellationToken.Token).ContinueWith(_ => - Dispatcher.UIThread.Invoke(() => - { - Console.WriteLine(To.Name); - return To.IsHitTestVisible = true; - })); + Dispatcher.UIThread.Invoke(() => To.IsHitTestVisible = true)); } catch { From 94a0f8e1e07b52b6e7bb109336bc8d6d57283733 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 17 Oct 2024 08:11:42 +0100 Subject: [PATCH 5/6] Implement Maxkatz' suggestions --- SukiUI/Utilities/Effects/EffectBackgroundDraw.cs | 9 ++++++--- SukiUI/Utilities/Effects/EffectDrawBase.cs | 9 +++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs index 93ffd0e43..e0db76be9 100644 --- a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs +++ b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs @@ -14,14 +14,17 @@ internal class EffectBackgroundDraw : EffectDrawBase internal bool TransitionsEnabled { get; set; } internal double TransitionTime { get; set; } - private static readonly Stopwatch TransitionTick = Stopwatch.StartNew(); - - private static float TransitionSeconds => (float)TransitionTick.Elapsed.TotalSeconds; + private float TransitionSeconds => (float)CompositionNow.TotalSeconds; private SukiEffect? _oldEffect; private float _transitionStartTime; private float _transitionEndTime; + public EffectBackgroundDraw() : base(true) + { + + } + protected override void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue) { if (!TransitionsEnabled) return; diff --git a/SukiUI/Utilities/Effects/EffectDrawBase.cs b/SukiUI/Utilities/Effects/EffectDrawBase.cs index 4a35e5c78..268cfbb2c 100644 --- a/SukiUI/Utilities/Effects/EffectDrawBase.cs +++ b/SukiUI/Utilities/Effects/EffectDrawBase.cs @@ -54,9 +54,11 @@ public bool AnimationEnabled protected float AnimationSeconds => (float)_animationTick.Elapsed.TotalSeconds; private readonly Stopwatch _animationTick = new(); + private readonly bool _invalidateRect; - protected EffectDrawBase() + protected EffectDrawBase(bool invalidateRect = false) { + _invalidateRect = invalidateRect; var sTheme = SukiTheme.GetInstance(); sTheme.OnBaseThemeChanged += v => ActiveVariant = v; ActiveVariant = sTheme.ActiveBaseTheme; @@ -96,7 +98,10 @@ public override void OnMessage(object message) public override void OnAnimationFrameUpdate() { if (!AnimationEnabled) return; - Invalidate(GetRenderBounds()); + if(_invalidateRect) + Invalidate(GetRenderBounds()); + else + Invalidate(); RegisterForNextAnimationFrameUpdate(); } From e8139a43b97744a01e4898c7a2255a22c4fa4f85 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 17 Oct 2024 09:04:28 +0100 Subject: [PATCH 6/6] Woops, wrong way around... --- SukiUI/Utilities/Effects/EffectBackgroundDraw.cs | 2 +- SukiUI/Utilities/Effects/EffectDrawBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs index e0db76be9..a9cc73e5e 100644 --- a/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs +++ b/SukiUI/Utilities/Effects/EffectBackgroundDraw.cs @@ -20,7 +20,7 @@ internal class EffectBackgroundDraw : EffectDrawBase private float _transitionStartTime; private float _transitionEndTime; - public EffectBackgroundDraw() : base(true) + public EffectBackgroundDraw() : base(false) { } diff --git a/SukiUI/Utilities/Effects/EffectDrawBase.cs b/SukiUI/Utilities/Effects/EffectDrawBase.cs index 268cfbb2c..695f256c3 100644 --- a/SukiUI/Utilities/Effects/EffectDrawBase.cs +++ b/SukiUI/Utilities/Effects/EffectDrawBase.cs @@ -56,7 +56,7 @@ public bool AnimationEnabled private readonly Stopwatch _animationTick = new(); private readonly bool _invalidateRect; - protected EffectDrawBase(bool invalidateRect = false) + protected EffectDrawBase(bool invalidateRect = true) { _invalidateRect = invalidateRect; var sTheme = SukiTheme.GetInstance();