From 9ddc790264b5e2ab4b4dcec1a6a545f5c703041b Mon Sep 17 00:00:00 2001
From: WantToBeeMe <93130991+WantToBeeMe@users.noreply.github.com>
Date: Sat, 31 Jan 2026 17:01:11 +0100
Subject: [PATCH 1/3] add meme number state
---
WheelWizard/Views/App.axaml | 1 +
.../MemeNumberState.axaml.cs | 165 ++++++++++++++++++
.../StandardLibrary/MemeNumberState.axaml | 88 ++++++++++
.../StandardLibrary/MemeNumberState.axaml.cs | 121 +++++++++++++
.../Components/StandardLibrary/StateBox.axaml | 16 +-
.../StandardLibrary/StateBox.axaml.cs | 8 +
WheelWizard/Views/Pages/RoomsPage.axaml | 8 +-
WheelWizard/Views/Pages/RoomsPage.axaml.cs | 2 -
WheelWizard/WheelWizard.csproj | 3 +
9 files changed, 401 insertions(+), 11 deletions(-)
create mode 100644 WheelWizard/Views/Components/BehaviorComponents/MemeNumberState.axaml.cs
create mode 100644 WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml
create mode 100644 WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml.cs
diff --git a/WheelWizard/Views/App.axaml b/WheelWizard/Views/App.axaml
index c8b10f02..65f76ef6 100644
--- a/WheelWizard/Views/App.axaml
+++ b/WheelWizard/Views/App.axaml
@@ -61,6 +61,7 @@
+
diff --git a/WheelWizard/Views/Components/BehaviorComponents/MemeNumberState.axaml.cs b/WheelWizard/Views/Components/BehaviorComponents/MemeNumberState.axaml.cs
new file mode 100644
index 00000000..b1fc0c8a
--- /dev/null
+++ b/WheelWizard/Views/Components/BehaviorComponents/MemeNumberState.axaml.cs
@@ -0,0 +1,165 @@
+using Avalonia;
+using Avalonia.Animation;
+using Avalonia.Animation.Easings;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
+using Avalonia.Styling;
+
+namespace WheelWizard.Views.Components.BehaviorComponents;
+
+public class MemeNumberState : TemplatedControl
+{
+ private StateBox? _stateBox;
+ private FormFieldLabel? _niceLabel;
+
+ public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), "0");
+
+ public string Value
+ {
+ get => GetValue(ValueProperty);
+ set => SetValue(ValueProperty, value);
+ }
+
+ public static readonly StyledProperty IconDataProperty = AvaloniaProperty.Register(
+ nameof(IconData)
+ );
+
+ public Geometry IconData
+ {
+ get => GetValue(IconDataProperty);
+ set => SetValue(IconDataProperty, value);
+ }
+
+ public static readonly StyledProperty IconSizeProperty = AvaloniaProperty.Register(
+ nameof(IconSize),
+ 20
+ );
+
+ public double IconSize
+ {
+ get => GetValue(IconSizeProperty);
+ set => SetValue(IconSizeProperty, value);
+ }
+
+ public static readonly StyledProperty TipTextProperty = AvaloniaProperty.Register(nameof(TipText));
+
+ public string TipText
+ {
+ get => GetValue(TipTextProperty);
+ set => SetValue(TipTextProperty, value);
+ }
+
+ public static readonly StyledProperty VariantProperty = AvaloniaProperty.Register<
+ MemeNumberState,
+ StateBox.StateBoxVariantType
+ >(nameof(Variant), StateBox.StateBoxVariantType.Default);
+
+ public StateBox.StateBoxVariantType Variant
+ {
+ get => GetValue(VariantProperty);
+ set => SetValue(VariantProperty, value);
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _stateBox = e.NameScope.Find("PART_StateBox");
+ _niceLabel = e.NameScope.Find("PART_NiceLabel");
+
+ UpdateState();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (change.Property == ValueProperty)
+ {
+ UpdateState();
+ }
+ }
+
+ private void UpdateState()
+ {
+ if (_stateBox == null || _niceLabel == null)
+ return;
+
+ var val = Value;
+
+ // Reset defaults
+ _niceLabel.IsVisible = false;
+ _stateBox.Content = null;
+ _stateBox.Text = val; // Always ensure text is set for normal cases
+
+ if (val == "69")
+ {
+ _niceLabel.IsVisible = true;
+ }
+ else if (val == "67")
+ {
+ _stateBox.Content = CreateBobbingBadge();
+ }
+ }
+
+ private Control CreateBobbingBadge()
+ {
+ // 6 7 bobbing animation
+ var grid = new Grid { ColumnDefinitions = new ColumnDefinitions("Auto, Auto") };
+
+ var t6 = new TextBlock
+ {
+ Text = "6",
+ Foreground = Brushes.LightGoldenrodYellow,
+ FontWeight = FontWeight.Bold,
+ FontSize = 14, // Match StateBox default or bind it
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ };
+
+ var t7 = new TextBlock
+ {
+ Text = "7",
+ Foreground = Brushes.LightGoldenrodYellow,
+ FontWeight = FontWeight.Bold,
+ FontSize = 14,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ };
+
+ // Add to grid
+ Grid.SetColumn(t6, 0);
+ Grid.SetColumn(t7, 1);
+ grid.Children.Add(t6);
+ grid.Children.Add(t7);
+
+ // Animations
+ var anim6 = CreateBobAnimation(0);
+ var anim7 = CreateBobAnimation(0.5); // Phase shift
+
+ anim6.RunAsync(t6);
+ anim7.RunAsync(t7);
+
+ return grid;
+ }
+
+ private Animation CreateBobAnimation(double delayRatio)
+ {
+ var animation = new Animation
+ {
+ Duration = TimeSpan.FromSeconds(1),
+ IterationCount = IterationCount.Infinite,
+ PlaybackDirection = PlaybackDirection.Alternate,
+ Delay = TimeSpan.FromSeconds(delayRatio), // Just initial offset, might not be perfect out of phase
+ };
+
+ // Keyframes: 0% -> 0, 100% -> -5 (up)
+ // Since it's Alternate, it will go 0 -> -5 -> 0 -> -5...
+
+ var kf1 = new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(TranslateTransform.YProperty, 0d) } };
+
+ var kf2 = new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(TranslateTransform.YProperty, -4d) } };
+
+ animation.Children.Add(kf1);
+ animation.Children.Add(kf2);
+
+ return animation;
+ }
+}
diff --git a/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml b/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml
new file mode 100644
index 00000000..cd9f56e2
--- /dev/null
+++ b/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml.cs b/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml.cs
new file mode 100644
index 00000000..4dc62a87
--- /dev/null
+++ b/WheelWizard/Views/Components/StandardLibrary/MemeNumberState.axaml.cs
@@ -0,0 +1,121 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
+
+namespace WheelWizard.Views.Components;
+
+public class MemeNumberState : TemplatedControl
+{
+ private StateBox? _stateBox;
+ private StateBox? _specialBadge;
+ private FormFieldLabel? _niceLabel;
+
+ public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text), "0");
+
+ public string Text
+ {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly StyledProperty IconDataProperty = AvaloniaProperty.Register(
+ nameof(IconData)
+ );
+
+ public Geometry IconData
+ {
+ get => GetValue(IconDataProperty);
+ set => SetValue(IconDataProperty, value);
+ }
+
+ public static readonly StyledProperty IconSizeProperty = AvaloniaProperty.Register(
+ nameof(IconSize),
+ 20
+ );
+
+ public double IconSize
+ {
+ get => GetValue(IconSizeProperty);
+ set => SetValue(IconSizeProperty, value);
+ }
+
+ public static readonly StyledProperty TipTextProperty = AvaloniaProperty.Register(nameof(TipText));
+
+ public string TipText
+ {
+ get => GetValue(TipTextProperty);
+ set => SetValue(TipTextProperty, value);
+ }
+
+ public static readonly StyledProperty VariantProperty = AvaloniaProperty.Register<
+ MemeNumberState,
+ StateBox.StateBoxVariantType
+ >(nameof(Variant), StateBox.StateBoxVariantType.Default);
+
+ public StateBox.StateBoxVariantType Variant
+ {
+ get => GetValue(VariantProperty);
+ set => SetValue(VariantProperty, value);
+ }
+
+ public static readonly StyledProperty Enable67Property = AvaloniaProperty.Register(nameof(Enable67), true);
+
+ public bool Enable67
+ {
+ get => GetValue(Enable67Property);
+ set => SetValue(Enable67Property, value);
+ }
+
+ public static readonly StyledProperty Enable69Property = AvaloniaProperty.Register(nameof(Enable69), true);
+
+ public bool Enable69
+ {
+ get => GetValue(Enable69Property);
+ set => SetValue(Enable69Property, value);
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _stateBox = e.NameScope.Find("PART_StateBox");
+ _specialBadge = e.NameScope.Find("PART_SpecialBadge");
+ _niceLabel = e.NameScope.Find("PART_NiceLabel");
+
+ UpdateState();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (change.Property == TextProperty)
+ {
+ UpdateState();
+ }
+ }
+
+ private void UpdateState()
+ {
+ if (_stateBox == null || _niceLabel == null || _specialBadge == null)
+ return;
+
+ var val = Text;
+
+ // Reset defaults
+ _niceLabel.IsVisible = false;
+ _stateBox.IsVisible = true;
+ _specialBadge.IsVisible = false;
+
+ _stateBox.Text = val; // Always ensure text is set for normal cases
+
+ if (val == "69" && Enable69)
+ {
+ _niceLabel.IsVisible = true;
+ }
+ else if (val == "67" && Enable67)
+ {
+ _stateBox.IsVisible = false;
+ _specialBadge.IsVisible = true;
+ }
+ }
+}
diff --git a/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml b/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml
index 2e1b3e67..89a43018 100644
--- a/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml
+++ b/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml
@@ -30,7 +30,8 @@
ToolTip.Tip="{TemplateBinding TipText}"
ToolTip.Placement="{TemplateBinding TipPlacement}"
ToolTip.ShowDelay="20">
-
+
+
@@ -38,15 +39,24 @@
Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+
-
+
+ FontWeight="Medium" VerticalAlignment="Center"
+ IsVisible="{TemplateBinding Content, Converter={x:Static ObjectConverters.IsNull}}"/>
+
+
+
diff --git a/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml.cs b/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml.cs
index 8c3c2039..632ef21b 100644
--- a/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml.cs
+++ b/WheelWizard/Views/Components/StandardLibrary/StateBox.axaml.cs
@@ -41,6 +41,14 @@ public Geometry IconData
set => SetValue(IconDataProperty, value);
}
+ public static readonly StyledProperty