From c51f1fe25ed9e092f250001401d468cfbd82a739 Mon Sep 17 00:00:00 2001
From: Patchzy <64382339+patchzyy@users.noreply.github.com>
Date: Sat, 27 Dec 2025 15:43:30 +0100
Subject: [PATCH] Add Mii image hover support to MiiBlock
---
.../MiiImages/MiiImageLoaderWithHover.axaml | 54 +++++
.../MiiImageLoaderWithHover.axaml.cs | 190 ++++++++++++++++++
.../Components/WhWzLibrary/MiiBlock.axaml | 17 +-
.../Components/WhWzLibrary/MiiBlock.axaml.cs | 69 +++++++
4 files changed, 324 insertions(+), 6 deletions(-)
create mode 100644 WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml
create mode 100644 WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml.cs
diff --git a/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml b/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml
new file mode 100644
index 00000000..808a2d62
--- /dev/null
+++ b/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml.cs b/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml.cs
new file mode 100644
index 00000000..16d2d4bd
--- /dev/null
+++ b/WheelWizard/Views/BehaviorComponent/MiiImages/MiiImageLoaderWithHover.axaml.cs
@@ -0,0 +1,190 @@
+using System.ComponentModel;
+using Avalonia;
+using Avalonia.Media;
+using WheelWizard.MiiImages;
+using WheelWizard.MiiImages.Domain;
+using WheelWizard.WiiManagement.MiiManagement.Domain.Mii;
+
+namespace WheelWizard.Views.BehaviorComponent;
+
+public partial class MiiImageLoaderWithHover : BaseMiiImage
+{
+ public static readonly StyledProperty IsHoveredProperty = AvaloniaProperty.Register(
+ nameof(IsHovered),
+ false
+ );
+
+ public bool IsHovered
+ {
+ get => GetValue(IsHoveredProperty);
+ set => SetValue(IsHoveredProperty, value);
+ }
+
+ public static readonly StyledProperty ShowNormalImageProperty = AvaloniaProperty.Register(
+ nameof(ShowNormalImage),
+ true
+ );
+
+ public bool ShowNormalImage
+ {
+ get => GetValue(ShowNormalImageProperty);
+ private set => SetValue(ShowNormalImageProperty, value);
+ }
+
+ public static readonly StyledProperty ShowHoverImageProperty = AvaloniaProperty.Register(
+ nameof(ShowHoverImage),
+ false
+ );
+
+ public bool ShowHoverImage
+ {
+ get => GetValue(ShowHoverImageProperty);
+ private set => SetValue(ShowHoverImageProperty, value);
+ }
+
+ private void UpdateImageVisibility()
+ {
+ var hasHoverImage = GeneratedImages.Count > 1 && GeneratedImages[1] != null;
+
+ if (IsHovered && hasHoverImage)
+ {
+ ShowNormalImage = false;
+ ShowHoverImage = true;
+ }
+ else
+ {
+ ShowNormalImage = true;
+ ShowHoverImage = false;
+ }
+ }
+
+ public static readonly StyledProperty LoadingColorProperty = AvaloniaProperty.Register(
+ nameof(LoadingColor),
+ new SolidColorBrush(ViewUtils.Colors.Neutral900)
+ );
+
+ public IBrush LoadingColor
+ {
+ get => GetValue(LoadingColorProperty);
+ set => SetValue(LoadingColorProperty, value);
+ }
+
+ public static readonly StyledProperty FallBackColorProperty = AvaloniaProperty.Register(
+ nameof(FallBackColor),
+ new SolidColorBrush(ViewUtils.Colors.Neutral700)
+ );
+
+ public IBrush FallBackColor
+ {
+ get => GetValue(FallBackColorProperty);
+ set => SetValue(FallBackColorProperty, value);
+ }
+
+ public static readonly StyledProperty ImageOnlyMarginProperty = AvaloniaProperty.Register<
+ MiiImageLoaderWithHover,
+ Thickness
+ >(nameof(ImageOnlyMargin), enableDataValidation: true);
+
+ public Thickness ImageOnlyMargin
+ {
+ get => GetValue(ImageOnlyMarginProperty);
+ set => SetValue(ImageOnlyMarginProperty, value);
+ }
+
+ public static readonly StyledProperty ImageVariantProperty = AvaloniaProperty.Register<
+ MiiImageLoaderWithHover,
+ MiiImageSpecifications
+ >(nameof(ImageVariant), MiiImageVariants.OnlinePlayerSmall, coerce: CoerceVariant);
+
+ public MiiImageSpecifications ImageVariant
+ {
+ get => GetValue(ImageVariantProperty);
+ set => SetValue(ImageVariantProperty, value);
+ }
+
+ public static readonly StyledProperty HoverVariantProperty = AvaloniaProperty.Register<
+ MiiImageLoaderWithHover,
+ MiiImageSpecifications?
+ >(nameof(HoverVariant), coerce: CoerceHoverVariant);
+
+ public MiiImageSpecifications? HoverVariant
+ {
+ get => GetValue(HoverVariantProperty);
+ set => SetValue(HoverVariantProperty, value);
+ }
+
+ private static MiiImageSpecifications? CoerceHoverVariant(AvaloniaObject o, MiiImageSpecifications? value)
+ {
+ var loader = (MiiImageLoaderWithHover)o;
+ // Reload both variants when hover variant changes (if Mii is already set)
+ // If Mii is not set yet, OnMiiChanged will handle the reload
+ if (loader.Mii != null && value != null)
+ {
+ loader.ReloadBothVariants();
+ }
+ return value;
+ }
+
+ private static MiiImageSpecifications CoerceVariant(AvaloniaObject o, MiiImageSpecifications value)
+ {
+ ((MiiImageLoaderWithHover)o).OnVariantChanged(value);
+ return value;
+ }
+
+ public MiiImageLoaderWithHover()
+ {
+ InitializeComponent();
+ PropertyChanged += MiiImageLoaderWithHover_PropertyChanged;
+ GeneratedImages.CollectionChanged += (s, e) => UpdateImageVisibility();
+ MiiImageLoaded += (s, e) => UpdateImageVisibility();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == IsHoveredProperty)
+ {
+ UpdateImageVisibility();
+ }
+ }
+
+ private void MiiImageLoaderWithHover_PropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(GeneratedImages))
+ {
+ UpdateImageVisibility();
+ }
+ }
+
+ private void OnVariantChanged(MiiImageSpecifications newSpecifications)
+ {
+ ReloadBothVariants();
+ }
+
+ protected override void OnMiiChanged(Mii? newMii)
+ {
+ // Always reload both variants when Mii changes
+ // This ensures both images are loaded even if hover variant is set later
+ ReloadBothVariants();
+ }
+
+ public void ReloadBothVariants()
+ {
+ if (Mii == null)
+ return;
+
+ var variants = new List();
+
+ // Always load the normal variant first
+ variants.Add(ImageVariant);
+
+ // If hover variant is set, load it as the second image
+ if (HoverVariant != null)
+ {
+ variants.Add(HoverVariant);
+ }
+
+ ReloadImages(Mii, variants);
+ }
+}
diff --git a/WheelWizard/Views/Components/WhWzLibrary/MiiBlock.axaml b/WheelWizard/Views/Components/WhWzLibrary/MiiBlock.axaml
index ba72c104..14b06bcd 100644
--- a/WheelWizard/Views/Components/WhWzLibrary/MiiBlock.axaml
+++ b/WheelWizard/Views/Components/WhWzLibrary/MiiBlock.axaml
@@ -44,12 +44,13 @@
IsVisible="{TemplateBinding Mii, Converter={x:Static ObjectConverters.IsNull} }"
Width="1000" Foreground="{StaticResource Neutral500}" />
-
+
+
+