From abf42c1bcc1199fae2692eb0c85b99e6280802f0 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 5 Oct 2024 10:22:07 +0100 Subject: [PATCH 1/5] Switch old loading style with shader-based approach. --- .../ControlsLibrary/ProgressView.axaml | 21 ++-- SukiUI.Demo/SukiUI.Demo.csproj | 6 +- .../Content/Shaders/Loading/glow_accent.sksl | 0 .../Content/Shaders/Loading/glow_primary.sksl | 69 ++++++++++++ SukiUI/Content/Shaders/Loading/glow_text.sksl | 69 ++++++++++++ .../Shaders/Loading/pellets_accent.sksl | 0 .../Shaders/Loading/pellets_primary.sksl | 28 +++++ .../Content/Shaders/Loading/pellets_text.sksl | 28 +++++ SukiUI/Controls/Loading.axaml | 102 ------------------ SukiUI/Controls/Loading.axaml.cs | 32 ------ .../Controls/Loading.cs | 25 +++-- SukiUI/SukiUI.csproj | 6 ++ SukiUI/Theme/Button.axaml | 14 +-- 13 files changed, 238 insertions(+), 162 deletions(-) rename SukiUI.Demo/Assets/glow.sksl => SukiUI/Content/Shaders/Loading/glow_accent.sksl (100%) create mode 100644 SukiUI/Content/Shaders/Loading/glow_primary.sksl create mode 100644 SukiUI/Content/Shaders/Loading/glow_text.sksl rename SukiUI.Demo/Assets/pellets.sksl => SukiUI/Content/Shaders/Loading/pellets_accent.sksl (100%) create mode 100644 SukiUI/Content/Shaders/Loading/pellets_primary.sksl create mode 100644 SukiUI/Content/Shaders/Loading/pellets_text.sksl delete mode 100644 SukiUI/Controls/Loading.axaml delete mode 100644 SukiUI/Controls/Loading.axaml.cs rename SukiUI.Demo/Controls/LoadingTest.cs => SukiUI/Controls/Loading.cs (69%) diff --git a/SukiUI.Demo/Features/ControlsLibrary/ProgressView.axaml b/SukiUI.Demo/Features/ControlsLibrary/ProgressView.axaml index fc017e6ae..51f5cd5fb 100644 --- a/SukiUI.Demo/Features/ControlsLibrary/ProgressView.axaml +++ b/SukiUI.Demo/Features/ControlsLibrary/ProgressView.axaml @@ -2,7 +2,6 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:suki="https://github.com/kikipoulet/SukiUI" - xmlns:controls1="clr-namespace:SukiUI.Demo.Controls" xmlns:controlsLibrary="clr-namespace:SukiUI.Demo.Features.ControlsLibrary" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -140,21 +139,19 @@ - + - - - - - - - - - + - + + + + + + + diff --git a/SukiUI.Demo/SukiUI.Demo.csproj b/SukiUI.Demo/SukiUI.Demo.csproj index 103581312..fc8eee1bf 100644 --- a/SukiUI.Demo/SukiUI.Demo.csproj +++ b/SukiUI.Demo/SukiUI.Demo.csproj @@ -58,8 +58,6 @@ - - @@ -72,4 +70,8 @@ + + + + diff --git a/SukiUI.Demo/Assets/glow.sksl b/SukiUI/Content/Shaders/Loading/glow_accent.sksl similarity index 100% rename from SukiUI.Demo/Assets/glow.sksl rename to SukiUI/Content/Shaders/Loading/glow_accent.sksl diff --git a/SukiUI/Content/Shaders/Loading/glow_primary.sksl b/SukiUI/Content/Shaders/Loading/glow_primary.sksl new file mode 100644 index 000000000..0af7e7fa8 --- /dev/null +++ b/SukiUI/Content/Shaders/Loading/glow_primary.sksl @@ -0,0 +1,69 @@ +const float pi = 3.14159265358979323846; + +float smoothstep(float a, float b, float x) { + float t = clamp((x - a) / (b - a), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +float atan(float y, float x) { + // Constants for the series expansion + const float pi_2 = pi / 2.0; + + // Handle special cases + if (x == 0.0) { + if (y > 0.0) return pi_2; + if (y < 0.0) return -pi_2; + return 0.0; // Undefined, but return 0 + } + + float abs_y = abs(y) + 1e-10; // Avoid division by zero + + // Compute the arctangent of y/x + float angle; + if (abs(x) > abs_y) { + float z = y / x; + float zz = z * z; + angle = z * (0.999866 + zz * (-0.3302995 + zz * (0.180141 + zz * (-0.085133 + zz * 0.020835)))); + if (x < 0.0) { + if (y < 0.0) { + angle -= pi; + } else { + angle += pi; + } + } + } else { + float z = x / y; + float zz = z * z; + angle = pi_2 - z * (0.999866 + zz * (-0.3302995 + zz * (0.180141 + zz * (-0.085133 + zz * 0.020835)))); + if (y < 0.0) { + angle -= pi; + } + } + + return angle; +} + +vec4 main(vec2 fragCoord) { + float radius = 0.3; + float lineWidth = 2.0; // in pixels + float glowSize = 3.0; // in pixels + + float pixelSize = 1.0 / min(iResolution.x, iResolution.y); + lineWidth *= pixelSize; + glowSize *= pixelSize; + glowSize *= 2.0; + + vec2 uv = (fragCoord.xy / iResolution.xy) - 0.5; + uv.x *= iResolution.x / iResolution.y; + + float len = length(uv); + float angle = atan(uv.y, uv.x); + + float fallOff = fract(-0.5 * (angle / pi) - iTime * 0.5); + + lineWidth = (lineWidth - pixelSize) * 0.5 * fallOff; + float color = smoothstep(pixelSize, 0.0, abs(radius - len) - lineWidth) * fallOff; + color += smoothstep(glowSize * fallOff, 0.0, abs(radius - len) - lineWidth) * fallOff * 0.5; + + return vec4(color) * vec4(iPrimary, iAlpha); +} \ No newline at end of file diff --git a/SukiUI/Content/Shaders/Loading/glow_text.sksl b/SukiUI/Content/Shaders/Loading/glow_text.sksl new file mode 100644 index 000000000..a4e990821 --- /dev/null +++ b/SukiUI/Content/Shaders/Loading/glow_text.sksl @@ -0,0 +1,69 @@ +const float pi = 3.14159265358979323846; + +float smoothstep(float a, float b, float x) { + float t = clamp((x - a) / (b - a), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +float atan(float y, float x) { + // Constants for the series expansion + const float pi_2 = pi / 2.0; + + // Handle special cases + if (x == 0.0) { + if (y > 0.0) return pi_2; + if (y < 0.0) return -pi_2; + return 0.0; // Undefined, but return 0 + } + + float abs_y = abs(y) + 1e-10; // Avoid division by zero + + // Compute the arctangent of y/x + float angle; + if (abs(x) > abs_y) { + float z = y / x; + float zz = z * z; + angle = z * (0.999866 + zz * (-0.3302995 + zz * (0.180141 + zz * (-0.085133 + zz * 0.020835)))); + if (x < 0.0) { + if (y < 0.0) { + angle -= pi; + } else { + angle += pi; + } + } + } else { + float z = x / y; + float zz = z * z; + angle = pi_2 - z * (0.999866 + zz * (-0.3302995 + zz * (0.180141 + zz * (-0.085133 + zz * 0.020835)))); + if (y < 0.0) { + angle -= pi; + } + } + + return angle; +} + +vec4 main(vec2 fragCoord) { + float radius = 0.3; + float lineWidth = 2.0; // in pixels + float glowSize = 3.0; // in pixels + + float pixelSize = 1.0 / min(iResolution.x, iResolution.y); + lineWidth *= pixelSize; + glowSize *= pixelSize; + glowSize *= 2.0; + + vec2 uv = (fragCoord.xy / iResolution.xy) - 0.5; + uv.x *= iResolution.x / iResolution.y; + + float len = length(uv); + float angle = atan(uv.y, uv.x); + + float fallOff = fract(-0.5 * (angle / pi) - iTime * 0.5); + + lineWidth = (lineWidth - pixelSize) * 0.5 * fallOff; + float color = smoothstep(pixelSize, 0.0, abs(radius - len) - lineWidth) * fallOff; + color += smoothstep(glowSize * fallOff, 0.0, abs(radius - len) - lineWidth) * fallOff * 0.5; + + return vec4(color) * vec4(iDark, iDark, iDark, iAlpha); +} \ No newline at end of file diff --git a/SukiUI.Demo/Assets/pellets.sksl b/SukiUI/Content/Shaders/Loading/pellets_accent.sksl similarity index 100% rename from SukiUI.Demo/Assets/pellets.sksl rename to SukiUI/Content/Shaders/Loading/pellets_accent.sksl diff --git a/SukiUI/Content/Shaders/Loading/pellets_primary.sksl b/SukiUI/Content/Shaders/Loading/pellets_primary.sksl new file mode 100644 index 000000000..afd3e1547 --- /dev/null +++ b/SukiUI/Content/Shaders/Loading/pellets_primary.sksl @@ -0,0 +1,28 @@ +float smoothstep(float a, float b, float x) { + float t = clamp((x - a) / (b - a), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +float A(vec2 p, float a) { + a *= 3.14159;\ + vec2 s = vec2(sin(a), cos(a)); + p.x = abs(p.x); + return ((s.y * p.x > s.x * p.y) ? length(p - s * .7) : + abs(length(p) - .7)) - .13; +} + +mat2 D(float a) { + a *= 3.14159;\ + vec2 s = vec2(sin(a), cos(a)); + return mat2(s.y, -s.x, s.x, s.y); +} + +vec4 main(vec2 fragCoord) { + vec2 r = iResolution.xy, p = (2. * fragCoord - r) / r.y; + float T = iTime * 1., + d = A(p * D(1. - .125 * floor(T)), .4375), // distance to longest arc + i; + for (i = 0.; i < 1.; i += .5) + d = min(A(p * D(mix(-.5, .625, fract(T / 2. + i)) - .125 * T), .0625), d); // distance to shorter arcs ("pellets") + return vec4(smoothstep(.01, .0, d)) * vec4(iPrimary, iAlpha); +} \ No newline at end of file diff --git a/SukiUI/Content/Shaders/Loading/pellets_text.sksl b/SukiUI/Content/Shaders/Loading/pellets_text.sksl new file mode 100644 index 000000000..586c27152 --- /dev/null +++ b/SukiUI/Content/Shaders/Loading/pellets_text.sksl @@ -0,0 +1,28 @@ +float smoothstep(float a, float b, float x) { + float t = clamp((x - a) / (b - a), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +float A(vec2 p, float a) { + a *= 3.14159;\ + vec2 s = vec2(sin(a), cos(a)); + p.x = abs(p.x); + return ((s.y * p.x > s.x * p.y) ? length(p - s * .7) : + abs(length(p) - .7)) - .13; +} + +mat2 D(float a) { + a *= 3.14159;\ + vec2 s = vec2(sin(a), cos(a)); + return mat2(s.y, -s.x, s.x, s.y); +} + +vec4 main(vec2 fragCoord) { + vec2 r = iResolution.xy, p = (2. * fragCoord - r) / r.y; + float T = iTime * 1., + d = A(p * D(1. - .125 * floor(T)), .4375), // distance to longest arc + i; + for (i = 0.; i < 1.; i += .5) + d = min(A(p * D(mix(-.5, .625, fract(T / 2. + i)) - .125 * T), .0625), d); // distance to shorter arcs ("pellets") + return vec4(smoothstep(.01, .0, d)) * vec4(iDark, iDark, iDark, iAlpha); +} \ No newline at end of file diff --git a/SukiUI/Controls/Loading.axaml b/SukiUI/Controls/Loading.axaml deleted file mode 100644 index 25949939c..000000000 --- a/SukiUI/Controls/Loading.axaml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - diff --git a/SukiUI/Controls/Loading.axaml.cs b/SukiUI/Controls/Loading.axaml.cs deleted file mode 100644 index bc2c2d2dc..000000000 --- a/SukiUI/Controls/Loading.axaml.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.MarkupExtensions; -using Avalonia.Media; - -namespace SukiUI.Controls -{ - public partial class Loading : UserControl - { - public Loading() - { - InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - - public static readonly StyledProperty ForegroundProperty = - AvaloniaProperty.Register(nameof(Foreground), defaultValue: Brushes.Aqua); - - public IBrush Foreground - { - get => GetValue(ForegroundProperty); - set => SetValue(ForegroundProperty, value); - } - - } -} \ No newline at end of file diff --git a/SukiUI.Demo/Controls/LoadingTest.cs b/SukiUI/Controls/Loading.cs similarity index 69% rename from SukiUI.Demo/Controls/LoadingTest.cs rename to SukiUI/Controls/Loading.cs index 296f6d0e8..270a2a0ce 100644 --- a/SukiUI.Demo/Controls/LoadingTest.cs +++ b/SukiUI/Controls/Loading.cs @@ -5,12 +5,12 @@ using SkiaSharp; using SukiUI.Utilities.Effects; -namespace SukiUI.Demo.Controls +namespace SukiUI.Controls { - public class LoadingTest : Control + public class Loading : Control { public static readonly StyledProperty LoadingStyleProperty = - AvaloniaProperty.Register(nameof(LoadingStyle)); + AvaloniaProperty.Register(nameof(LoadingStyle), defaultValue: LoadingStyle.Glow); public LoadingStyle LoadingStyle { @@ -21,13 +21,17 @@ public LoadingStyle LoadingStyle private static readonly IReadOnlyDictionary Effects = new Dictionary() { - { LoadingStyle.Glow, SukiEffect.FromEmbeddedResource("glow") }, - { LoadingStyle.Pellets, SukiEffect.FromEmbeddedResource("pellets") } + { LoadingStyle.Glow, SukiEffect.FromEmbeddedResource("glow_primary") }, + { LoadingStyle.Pellets, SukiEffect.FromEmbeddedResource("pellets_primary") }, + { LoadingStyle.GlowAccent, SukiEffect.FromEmbeddedResource("glow_accent") }, + { LoadingStyle.PelletsAccent, SukiEffect.FromEmbeddedResource("pellets_accent") }, + { LoadingStyle.GlowText, SukiEffect.FromEmbeddedResource("glow_text") }, + { LoadingStyle.PelletsText, SukiEffect.FromEmbeddedResource("pellets_text") } }; private readonly LoadingEffectDraw _draw; - public LoadingTest() + public Loading() { Width = 50; Height = 50; @@ -64,6 +68,9 @@ protected override void Render(SKCanvas canvas, SKRect rect) canvas.Restore(); } + // I'm not really sure how to render this properly in software fallback scenarios. + // This is likely to cause issues with the previewer. + // Might be worth just drawing a circle or something... protected override void RenderSoftware(SKCanvas canvas, SKRect rect) { throw new System.NotImplementedException(); @@ -74,6 +81,10 @@ protected override void RenderSoftware(SKCanvas canvas, SKRect rect) public enum LoadingStyle { Glow, - Pellets + GlowAccent, + GlowText, + Pellets, + PelletsAccent, + PelletsText } } \ No newline at end of file diff --git a/SukiUI/SukiUI.csproj b/SukiUI/SukiUI.csproj index 580617e19..4608f3771 100644 --- a/SukiUI/SukiUI.csproj +++ b/SukiUI/SukiUI.csproj @@ -97,6 +97,12 @@ + + + + + + diff --git a/SukiUI/Theme/Button.axaml b/SukiUI/Theme/Button.axaml index 66fca62e2..5d05c8f01 100644 --- a/SukiUI/Theme/Button.axaml +++ b/SukiUI/Theme/Button.axaml @@ -31,7 +31,7 @@ - + @@ -191,7 +191,7 @@ @@ -191,7 +191,7 @@