Skip to content

Commit

Permalink
Merge pull request #223 from sirdoombox/main
Browse files Browse the repository at this point in the history
Complete `SukiBackground` rewrite.
  • Loading branch information
kikipoulet authored Jun 20, 2024
2 parents 0eb2774 + 87627cd commit e8b2ee6
Show file tree
Hide file tree
Showing 21 changed files with 904 additions and 672 deletions.
24 changes: 19 additions & 5 deletions SukiUI.Demo/Features/Theming/ThemingView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,39 @@
</controls:SettingsLayoutItem.Content>
</controls:SettingsLayoutItem>

<controls:SettingsLayoutItem Header="Other">
<controls:SettingsLayoutItem Header="Background">
<controls:SettingsLayoutItem.Content>
<controls:GlassCard Margin="15">
<StackPanel Spacing="25">
<DockPanel>
<ToggleButton VerticalAlignment="Top"
Classes="Switch"
DockPanel.Dock="Right"
IsChecked="{Binding IsBackgroundAnimated}" />
IsChecked="{Binding BackgroundAnimations}" />
<StackPanel HorizontalAlignment="Left">
<TextBlock FontSize="16"
FontWeight="DemiBold"
Text="Animated Background" />
<TextBlock Margin="0,12,70,0"
Foreground="{DynamicResource SukiLowText}"
Text="Enable a 'cloud' effect in the background. The background will be dynamic and the colors spaces will change over time."
Text="Enable/disable the animations for the background, which are driven by the currently active effect."
TextWrapping="Wrap" />
</StackPanel>
</DockPanel>
</controls:GlassCard>
<DockPanel>
<ComboBox DockPanel.Dock="Right"
ItemsSource="{Binding AvailableBackgroundStyles}"
SelectedItem="{Binding BackgroundStyle}" />
<StackPanel HorizontalAlignment="Left">
<TextBlock FontSize="16"
FontWeight="DemiBold"
Text="Background Style" />
<TextBlock Margin="0,12,70,0"
Foreground="{DynamicResource SukiLowText}"
Text="Select from the included background styles."
TextWrapping="Wrap" />
</StackPanel>
</DockPanel>
</StackPanel>
</controls:SettingsLayoutItem.Content>
</controls:SettingsLayoutItem>
</objectModel:ObservableCollection>
Expand Down
23 changes: 16 additions & 7 deletions SukiUI.Demo/Features/Theming/ThemingViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,52 @@
using Avalonia.Collections;
using System;
using Avalonia.Collections;
using Avalonia.Styling;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Material.Icons;
using SukiUI.Enums;
using SukiUI.Models;

namespace SukiUI.Demo.Features.Theming;

public partial class ThemingViewModel : DemoPageBase
{
public Action<SukiBackgroundStyle> BackgroundStyleChanged { get; set; }
public Action<bool> BackgroundAnimationsChanged { get; set; }

public IAvaloniaReadOnlyList<SukiColorTheme> AvailableColors { get; }
public IAvaloniaReadOnlyList<SukiBackgroundStyle> AvailableBackgroundStyles { get; }

private readonly SukiTheme _theme = SukiTheme.GetInstance();

[ObservableProperty] private bool _isBackgroundAnimated;
[ObservableProperty] private bool _isLightTheme;
[ObservableProperty] private SukiBackgroundStyle _backgroundStyle;
[ObservableProperty] private bool _backgroundAnimations;

public ThemingViewModel() : base("Theming", MaterialIconKind.PaletteOutline, -200)
{
AvailableBackgroundStyles = new AvaloniaList<SukiBackgroundStyle>(Enum.GetValues<SukiBackgroundStyle>());
AvailableColors = _theme.ColorThemes;
IsLightTheme = _theme.ActiveBaseTheme == ThemeVariant.Light;
IsBackgroundAnimated = _theme.IsBackgroundAnimated;
_theme.OnBaseThemeChanged += variant =>
IsLightTheme = variant == ThemeVariant.Light;
_theme.OnColorThemeChanged += theme =>
{
// TODO: Implement a way to make the correct, might need to wrap the thing in a VM, this isn't ideal.
};
_theme.OnBackgroundAnimationChanged += value =>
IsBackgroundAnimated = value;
}

partial void OnIsLightThemeChanged(bool value) =>
_theme.ChangeBaseTheme(value ? ThemeVariant.Light : ThemeVariant.Dark);

partial void OnIsBackgroundAnimatedChanged(bool value) =>
_theme.SetBackgroundAnimationsEnabled(value);

[RelayCommand]
public void SwitchToColorTheme(SukiColorTheme colorTheme) =>
_theme.ChangeColorTheme(colorTheme);

partial void OnBackgroundStyleChanged(SukiBackgroundStyle value) =>
BackgroundStyleChanged?.Invoke(value);

partial void OnBackgroundAnimationsChanged(bool value) =>
BackgroundAnimationsChanged?.Invoke(value);
}
19 changes: 13 additions & 6 deletions SukiUI.Demo/SukiUIDemoView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
d:DesignWidth="800"
x:DataType="demo:SukiUIDemoViewModel"
BackgroundAnimationEnabled="{Binding AnimationsEnabled}"
BackgroundStyle="{Binding BackgroundStyle}"
CanMinimize="{Binding !WindowLocked}"
CanMove="{Binding !WindowLocked}"
CanResize="{Binding !WindowLocked}"
Expand Down Expand Up @@ -41,11 +42,6 @@
<avalonia:MaterialIcon Kind="Lightbulb" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="{Binding ToggleAnimationsCommand}" Header="Animations">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="{Binding AnimationsEnabled, Converter={x:Static converters:BoolToIconConverters.Animation}}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="{Binding ToggleWindowLockCommand}"
Header="Window Lock"
ToolTip.Tip="Toggles minimizing and resizing.">
Expand All @@ -65,7 +61,7 @@
ToolTip.Tip="Makes the app fullscreen." />
</MenuItem>
<MenuItem Header="Theme">
<MenuItem Click="MenuItem_OnClick"
<MenuItem Click="ThemeMenuItem_OnClick"
Header="Switch To..."
ItemsSource="{Binding Themes}">
<MenuItem.DataTemplates>
Expand All @@ -77,6 +73,17 @@
<MenuItem Header="-" />
<MenuItem Command="{Binding CreateCustomThemeCommand}" Header="Create Custom" />
</MenuItem>
<MenuItem Header="Background">
<MenuItem Click="BackgroundMenuItem_OnClick"
Header="Switch To..."
ItemsSource="{Binding BackgroundStyles}" />
<MenuItem Header="-" />
<MenuItem Command="{Binding ToggleAnimationsCommand}" Header="Animations">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="{Binding AnimationsEnabled, Converter={x:Static converters:BoolToIconConverters.Animation}}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</suki:SukiWindow.MenuItems>
<suki:SukiSideMenu ItemsSource="{Binding DemoPages}" SelectedItem="{Binding ActivePage}">
<suki:SukiSideMenu.ItemTemplate>
Expand Down
11 changes: 10 additions & 1 deletion SukiUI.Demo/SukiUIDemoView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Avalonia.Input;
using Avalonia.Interactivity;
using SukiUI.Controls;
using SukiUI.Enums;
using SukiUI.Models;

namespace SukiUI.Demo;
Expand All @@ -13,14 +14,22 @@ public SukiUIDemoView()
InitializeComponent();
}

private void MenuItem_OnClick(object? sender, RoutedEventArgs e)
private void ThemeMenuItem_OnClick(object? sender, RoutedEventArgs e)
{
if (DataContext is not SukiUIDemoViewModel vm) return;
if (e.Source is not MenuItem mItem) return;
if (mItem.DataContext is not SukiColorTheme cTheme) return;
vm.ChangeTheme(cTheme);
}

private void BackgroundMenuItem_OnClick(object? sender, RoutedEventArgs e)
{
if (DataContext is not SukiUIDemoViewModel vm) return;
if (e.Source is not MenuItem mItem) return;
if (mItem.DataContext is not SukiBackgroundStyle cStyle) return;
vm.BackgroundStyle = cStyle;
}

private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
IsMenuVisible = !IsMenuVisible;
Expand Down
24 changes: 19 additions & 5 deletions SukiUI.Demo/SukiUIDemoViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using Avalonia.Collections;
using Avalonia.Styling;
using CommunityToolkit.Mvvm.ComponentModel;
Expand All @@ -11,6 +12,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SukiUI.Demo.Features.Theming;
using SukiUI.Enums;

namespace SukiUI.Demo;

Expand All @@ -19,18 +22,27 @@ public partial class SukiUIDemoViewModel : ObservableObject
public IAvaloniaReadOnlyList<DemoPageBase> DemoPages { get; }

public IAvaloniaReadOnlyList<SukiColorTheme> Themes { get; }

public IAvaloniaReadOnlyList<SukiBackgroundStyle> BackgroundStyles { get; }

[ObservableProperty] private ThemeVariant _baseTheme;
[ObservableProperty] private bool _animationsEnabled;
[ObservableProperty] private DemoPageBase? _activePage;
[ObservableProperty] private bool _windowLocked;
[ObservableProperty] private bool _titleBarVisible = true;
[ObservableProperty] private SukiBackgroundStyle _backgroundStyle;
[ObservableProperty] private bool _animationsEnabled;

private readonly SukiTheme _theme;
private readonly ThemingViewModel _theming;

public SukiUIDemoViewModel(IEnumerable<DemoPageBase> demoPages, PageNavigationService pageNavigationService)
{
DemoPages = new AvaloniaList<DemoPageBase>(demoPages.OrderBy(x => x.Index).ThenBy(x => x.DisplayName));
_theming = (ThemingViewModel)DemoPages.First(x => x is ThemingViewModel);
_theming.BackgroundStyleChanged += style => BackgroundStyle = style;
_theming.BackgroundAnimationsChanged += enabled => AnimationsEnabled = enabled;

BackgroundStyles = new AvaloniaList<SukiBackgroundStyle>(Enum.GetValues<SukiBackgroundStyle>());
_theme = SukiTheme.GetInstance();

// Subscribe to the navigation service (when a page navigation is requested)
Expand All @@ -54,10 +66,6 @@ public SukiUIDemoViewModel(IEnumerable<DemoPageBase> demoPages, PageNavigationSe
// Subscribe to the color theme changed events
_theme.OnColorThemeChanged += theme =>
SukiHost.ShowToast("Successfully Changed Color", $"Changed Color To {theme.DisplayName}.");

// Subscribe to the background animation changed events
_theme.OnBackgroundAnimationChanged +=
value => AnimationsEnabled = value;
}

[RelayCommand]
Expand Down Expand Up @@ -102,4 +110,10 @@ private void ToggleTitleBar()

[RelayCommand]
private static void OpenUrl(string url) => UrlUtilities.OpenUrl(url);

partial void OnBackgroundStyleChanged(SukiBackgroundStyle value) =>
_theming.BackgroundStyle = value;

partial void OnAnimationsEnabledChanged(bool value) =>
_theming.BackgroundAnimations = value;
}
44 changes: 44 additions & 0 deletions SukiUI/Content/Shaders/cells.sksl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
vec3 blendOverlay(vec3 base, vec3 blend) {
return vec3(
base.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)),
base.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)),
base.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b))
);
}

vec2 ran(vec2 uv) {
uv *= vec2(dot(uv, vec2(127.1, 311.7)), dot(uv, vec2(227.1, 521.7)));
return 1.0 - fract(tan(cos(uv) * 123.6) * 3533.3) * fract(tan(cos(uv) * 123.6) * 3533.3);
}
vec2 pt(vec2 id) {
return sin(iTime * 0.5 * (ran(id + .5) - 0.5) + ran(id - 20.1) * 8.0) * 0.5;
}

vec4 main(vec2 fragCoord)
{
float SIZE = 10.;
vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.x;
vec2 off = iTime / vec2(200., 120.);
uv += off;
uv *= SIZE;

vec2 gv = fract(uv) - .5;
vec2 id = floor(uv);

float mindist = 1e9;
vec2 vorv = vec2(0);
for (float i = -1.;i <= 1.; i++) {
for (float j = -1.;j <= 1.; j++) {
vec2 offv = vec2(i, j);
float dist = length(gv + pt(id + offv) - offv);
if (dist < mindist) {
mindist = dist;
vorv = (id + pt(id + offv) + offv) / SIZE - off;
}
}
}

vec3 col = mix(iPrimary, iAccent, clamp(vorv.x * 2.2 + vorv.y, -1., 1.) * 0.5 + 0.5);
vec3 comp = blendOverlay(iBase, col);
return vec4(comp, 1.);
}
3 changes: 3 additions & 0 deletions SukiUI/Content/Shaders/flat.sksl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vec4 main(vec2 fragCoord) {
return vec4(iBase, 1.0);
}
63 changes: 63 additions & 0 deletions SukiUI/Content/Shaders/gradient.sksl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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);
}

vec3 blendOverlay(vec3 base, vec3 blend) {
return vec3(
base.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)),
base.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)),
base.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b))
);
}

mat2 Rot(float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c);
}

vec2 hash(vec2 p) {
p = vec2(dot(p, vec2(2127.1, 81.17)), dot(p, vec2(1269.5, 283.37)));
return fract(sin(p) * 43758.5453);
}

float noise(in vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);

vec2 u = f * f * (3.0 - 2.0 * f);

float n = mix(mix(dot(-1.0 + 2.0 * hash(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(-1.0 + 2.0 * hash(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(-1.0 + 2.0 * hash(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(-1.0 + 2.0 * hash(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
return 0.5 + 0.5 * n;
}

vec4 main(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
float ratio = iResolution.x / iResolution.y;

vec2 tuv = uv;
tuv -= .5;

float degree = noise(vec2(iTime * .1, tuv.x * tuv.y));

tuv.y *= 1. / ratio;
tuv *= Rot(radians((degree - .5) * 720. + 180.));
tuv.y *= ratio;

float frequency = 10.;
float amplitude = 15.;
float speed = iTime * 0.1;
tuv.x += sin(tuv.y * frequency + speed) / amplitude;
tuv.y += sin(tuv.x * frequency * 1.5 + speed) / (amplitude * .5);

vec3 layer1 = mix(iAccent, iPrimary * 0.85, smoothstep(-.3, .2, (tuv * Rot(radians(-5.))).x));
vec3 layer2 = mix(iPrimary, iAccent * 0.65, smoothstep(-.3, .2, (tuv * Rot(radians(-5.))).x));
vec3 finalComp = mix(layer1, layer2, smoothstep(.5, -.3, tuv.y));
vec3 col = blendOverlay(iBase, finalComp);

return vec4(col, 1.0);
}
Loading

0 comments on commit e8b2ee6

Please sign in to comment.