From 145883caadc78ab1c4d55cc4aa2bcaec1262bd15 Mon Sep 17 00:00:00 2001 From: Dirk <93130991+DirkDoes@users.noreply.github.com> Date: Wed, 7 Oct 2026 21:11:48 +0200 Subject: [PATCH 1/3] feat: redesign mods UI and refine sidebar interactions --- .../ApplicationCompositionTests.cs | 25 +++ WheelWizard.UI.Test/ModBrowserPreviewTests.cs | 54 ++++- .../ToolTipBubbleBehaviorTests.cs | 103 +++++++++ WheelWizard.UI.Test/WindowCompositionTests.cs | 76 +++++++ WheelWizard/Views/App.axaml.cs | 4 +- .../Views/Behaviors/ToolTipBubbleBehavior.cs | 63 +++--- WheelWizard/Views/MainWindowService.cs | 3 +- .../ModManagement/ModPreviewViewModel.cs | 28 +++ WheelWizard/Views/Pages/ModsPage.axaml | 201 ++++++++++-------- WheelWizard/Views/Pages/ModsPage.axaml.cs | 20 +- WheelWizard/Views/Patterns/GridModPanel.axaml | 110 +++++----- .../Views/Patterns/GridModPanel.axaml.cs | 84 ++++++++ .../Views/Patterns/SidebarRadioButton.axaml | 28 +-- .../Patterns/SidebarRadioButton.axaml.cs | 9 +- WheelWizard/Views/Popups/Base/PopupContent.cs | 16 ++ .../Views/Popups/Base/PopupWindow.axaml | 3 +- .../Popups/Generic/ProgressWindow.axaml.cs | 16 +- .../Views/Styles/Resources/Icons.axaml | 6 +- 18 files changed, 632 insertions(+), 217 deletions(-) create mode 100644 WheelWizard.UI.Test/ToolTipBubbleBehaviorTests.cs diff --git a/WheelWizard.UI.Test/ApplicationCompositionTests.cs b/WheelWizard.UI.Test/ApplicationCompositionTests.cs index f6f12d54c..7e02bbf81 100644 --- a/WheelWizard.UI.Test/ApplicationCompositionTests.cs +++ b/WheelWizard.UI.Test/ApplicationCompositionTests.cs @@ -273,6 +273,31 @@ public async Task MainWindowAndPages_ConstructWithoutStaticServiceInitialization navigation.NavigateTo(page); original.UpdateLayout(); Assert.IsType(page, navigation.CurrentPage); + if (navigation.CurrentPage is ModsPage modsPage) + { + modsPage.HasMods = true; + original.UpdateLayout(); + var gridView = modsPage.FindControl("GridViewButton")!; + var listView = modsPage.FindControl("ListViewButton")!; + var list = modsPage.FindControl("ModsListBox")!; + var grid = modsPage.FindControl("ModsGridView")!; + gridView.IsChecked = true; + Assert.True(grid.IsVisible); + Assert.False(list.IsVisible); + listView.IsChecked = true; + Assert.True(list.IsVisible); + Assert.False(grid.IsVisible); + original.UpdateLayout(); + var viewToggle = modsPage.FindControl("ViewModeToggle")!; + Assert.True(viewToggle.TranslatePoint(new Point(), modsPage)!.Value.Y > list.TranslatePoint(new Point(), modsPage)!.Value.Y); + var enableAll = modsPage.FindControl("EnableAllCheckbox")!; + Assert.True(enableAll.TranslatePoint(new Point(), modsPage)!.Value.Y < list.TranslatePoint(new Point(), modsPage)!.Value.Y); + var presenter = list.GetVisualDescendants().OfType().Single(); + Assert.True(presenter.Margin.Bottom >= viewToggle.Bounds.Height + 12); + var icon = listView.GetVisualDescendants().OfType().Single(); + Assert.Equal(Avalonia.Media.Brushes.Transparent, icon.Fill); + Assert.NotNull(icon.Stroke); + } } var room = new WheelWizard.Models.RRInfo.RrRoom { diff --git a/WheelWizard.UI.Test/ModBrowserPreviewTests.cs b/WheelWizard.UI.Test/ModBrowserPreviewTests.cs index 55ef948b2..109aa476c 100644 --- a/WheelWizard.UI.Test/ModBrowserPreviewTests.cs +++ b/WheelWizard.UI.Test/ModBrowserPreviewTests.cs @@ -1,4 +1,8 @@ +using Avalonia; +using Avalonia.Headless; using Avalonia.Controls; +using WheelWizard.Models.Mods; +using WheelWizard.Views.Pages; using Avalonia.Headless.XUnit; using Avalonia.Threading; using Avalonia.VisualTree; @@ -18,6 +22,48 @@ namespace WheelWizard.UI.Test; public class ModBrowserPreviewTests { + [AvaloniaFact] + public void DisabledGridCard_RestoresColorOnWholeCardHover_AndTracksEnabledState() + { + using var preview = new ModPreviewViewModel(0, Substitute.For(), Substitute.For()); + var mod = new Mod { Title = "Test mod", IsEnabled = false, HasIncompatibleFiles = true }; + var card = new GridModPanel { DataContext = new ModListItem(mod, false, false, preview) }; + var window = new Window { Content = card, Width = 300, Height = 220 }; + try + { + window.Show(); + window.UpdateLayout(); + var grayImage = card.FindControl("DisabledThumbnailImage")!; + var originalSize = card.FindControl("RootCardBorder")!.Bounds.Size; + Assert.True(grayImage.IsVisible); + Assert.True(card.FindControl("CompatibilityWarning")!.IsVisible); + window.MouseMove(new Point(25, 130)); + Assert.False(grayImage.IsVisible); + window.MouseMove(card.FindControl("ThumbnailHeader")!.TranslatePoint(new Point(20, 40), window)!.Value); + Assert.False(grayImage.IsVisible); + window.MouseMove(new Point(25, 130)); + Assert.False(grayImage.IsVisible); + window.MouseDown(new Point(25, 130), Avalonia.Input.MouseButton.Left); + window.MouseUp(new Point(25, 130), Avalonia.Input.MouseButton.Left); + Assert.True(mod.IsEnabled); + window.UpdateLayout(); + Assert.Equal(originalSize, card.FindControl("RootCardBorder")!.Bounds.Size); + window.MouseDown(new Point(25, 130), Avalonia.Input.MouseButton.Left); + window.MouseUp(new Point(25, 130), Avalonia.Input.MouseButton.Left); + Assert.False(mod.IsEnabled); + window.MouseMove(new Point(1, 1)); + Assert.True(grayImage.IsVisible); + mod.IsEnabled = true; + Assert.False(grayImage.IsVisible); + mod.IsEnabled = false; + Assert.True(grayImage.IsVisible); + } + finally + { + window.Close(); + } + } + [AvaloniaFact] public async Task ClosedBrowser_DiscardsPendingSearchResults() { @@ -219,7 +265,10 @@ public async Task RecycledCard_LoadsAndBindsItsCurrentPreview() { var mods = Substitute.For(); var media = Substitute.For(); - var png = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jvX0AAAAASUVORK5CYII="); + using var pixels = new SkiaSharp.SKBitmap(2, 2); + pixels.Erase(SkiaSharp.SKColors.Red); + using var encoded = pixels.Encode(SkiaSharp.SKEncodedImageFormat.Png, 100); + var png = encoded.ToArray(); media.GetImageAsync(Arg.Any(), Arg.Any()).Returns((OperationResult)png); using var first = new ModPreviewViewModel(1, mods, media, "https://images.test/first.png"); using var second = new ModPreviewViewModel(2, mods, media, "https://images.test/second.png"); @@ -238,6 +287,9 @@ public async Task RecycledCard_LoadsAndBindsItsCurrentPreview() await first.LoadAsync(); var image = Assert.Single(card.GetVisualDescendants().OfType()); Assert.NotNull(first.Image); + Assert.NotNull(first.GrayscaleImage); + Assert.NotSame(first.Image, first.GrayscaleImage); + Assert.Same(first.GrayscaleImage, first.GrayscaleImage); Assert.Same(first.Image, image.Source); card.Preview = second; diff --git a/WheelWizard.UI.Test/ToolTipBubbleBehaviorTests.cs b/WheelWizard.UI.Test/ToolTipBubbleBehaviorTests.cs new file mode 100644 index 000000000..06e761146 --- /dev/null +++ b/WheelWizard.UI.Test/ToolTipBubbleBehaviorTests.cs @@ -0,0 +1,103 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Headless; +using Avalonia.Headless.XUnit; +using Avalonia.Threading; + +namespace WheelWizard.UI.Test; + +public class ToolTipBubbleBehaviorTests +{ + private static void SetActive(Window window, bool active) => + typeof(WindowBase) + .GetMethod( + active ? "HandleActivated" : "HandleDeactivated", + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic + )! + .Invoke(window, null); + + [AvaloniaFact] + public async Task Hover_OpensWithoutTimer_AndExitStartsExistingAnimationImmediately() + { + var target = new Border + { + Width = 100, + Height = 100, + Background = Avalonia.Media.Brushes.Transparent, + }; + ToolTip.SetTip(target, new ToolTip { Content = "Hover" }); + var window = new Window + { + Content = target, + Width = 300, + Height = 300, + }; + try + { + window.Show(); + // Headless windows do not receive native activation notifications. + SetActive(window, true); + window.UpdateLayout(); + window.MouseMove(target.TranslatePoint(new Point(50, 50), window)!.Value); + Dispatcher.UIThread.RunJobs(); + Assert.True(target.IsPointerOver); + Assert.True(window.IsActive, "Window should be active"); + Assert.False(ToolTip.GetServiceEnabled(target), "Custom tooltip service should be initialized"); + Assert.True(ToolTip.GetIsOpen(target)); + var tip = Assert.IsType(ToolTip.GetTip(target)); + Assert.Contains("BubbleAnimateIn", tip.Classes); + + window.MouseMove(new Point(1, 1)); + Dispatcher.UIThread.RunJobs(); + Assert.Contains("BubbleAnimateOut", tip.Classes); + await Task.Delay(80); + Dispatcher.UIThread.RunJobs(); + Assert.False(ToolTip.GetIsOpen(target)); + } + finally + { + window.Close(); + } + } + + [AvaloniaFact] + public void LosingFocus_ClosesTooltip_AndPreventsHoverReopeningInBackground() + { + var target = new Border + { + Width = 100, + Height = 100, + Background = Avalonia.Media.Brushes.Transparent, + }; + ToolTip.SetTip(target, "Hover"); + var window = new Window + { + Content = target, + Width = 300, + Height = 300, + }; + try + { + window.Show(); + // Headless windows do not receive native activation notifications. + SetActive(window, true); + window.UpdateLayout(); + window.MouseMove(target.TranslatePoint(new Point(50, 50), window)!.Value); + Dispatcher.UIThread.RunJobs(); + Assert.True(window.IsActive, "Window should be active"); + Assert.False(ToolTip.GetServiceEnabled(target), "Custom tooltip service should be initialized"); + Assert.True(ToolTip.GetIsOpen(target)); + + SetActive(window, false); + Assert.False(ToolTip.GetIsOpen(target)); + window.MouseMove(new Point(1, 1)); + window.MouseMove(target.TranslatePoint(new Point(50, 50), window)!.Value); + Dispatcher.UIThread.RunJobs(); + Assert.False(ToolTip.GetIsOpen(target)); + } + finally + { + window.Close(); + } + } +} diff --git a/WheelWizard.UI.Test/WindowCompositionTests.cs b/WheelWizard.UI.Test/WindowCompositionTests.cs index e239a2e11..e3fc298f1 100644 --- a/WheelWizard.UI.Test/WindowCompositionTests.cs +++ b/WheelWizard.UI.Test/WindowCompositionTests.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Headless; using Avalonia.Headless.XUnit; using Avalonia.Media; using Avalonia.Styling; @@ -13,6 +14,81 @@ namespace WheelWizard.UI.Test; public class WindowCompositionTests { + [AvaloniaTheory] + [InlineData(64.0)] + [InlineData(221.0)] + public void SidebarHover_MovesGlowWithoutChangingLayout(double width) + { + var button = new WheelWizard.Views.Patterns.SidebarRadioButton { Text = "Home" }; + var window = new Window { Width = width, Height = 46, Content = button }; + try + { + window.Show(); + window.UpdateLayout(); + var bounds = button.Bounds; + var desiredSize = button.DesiredSize; + var glow = button.GetVisualDescendants().OfType().Single(border => border.Name == "PART_HoverEffect"); + Assert.False(glow.IsHitTestVisible); + foreach (var x in new[] { 2.0, width / 2, width - 1 }) + { + window.MouseMove(new Point(x, 23)); + window.UpdateLayout(); + Assert.True(glow.IsVisible); + Assert.Equal(x - 23, Assert.IsType(glow.RenderTransform).X); + Assert.Equal(bounds, button.Bounds); + Assert.Equal(desiredSize, button.DesiredSize); + } + } + finally + { + window.Close(); + } + } + + [AvaloniaTheory] + [InlineData(400.0)] + [InlineData(500.0)] + public void ProgressContent_KeepsDefinedMeasuredWidth_ButFillsExtraAvailableWidth(double preferredWidth) + { + var content = new WheelWizard.Views.Popups.Generic.ProgressWindow("Download"); + Assert.IsType(content.Content).Width = preferredWidth; + content.SetGoal(new string('W', 150)); + content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + Assert.Equal(preferredWidth, content.DesiredSize.Width); + content.Arrange(new Rect(0, 0, 600, 200)); + Assert.Equal(600, Assert.IsType(content.Content).Bounds.Width); + Assert.Equal(600, content.FindControl("ProgressBar")!.Bounds.Width); + content.Close(); + } + + [AvaloniaTheory] + [InlineData(true, false)] + [InlineData(false, false)] + [InlineData(true, true)] + public void ProgressNativeClose_CancelsOnlyCancelableUserRequests(bool canCancel, bool programmatic) + { + using var cancellation = new CancellationTokenSource(); + var content = new WheelWizard.Views.Popups.Generic.ProgressWindow("Download"); + if (canCancel) + content.SetCancellationTokenSource(cancellation); + content.Show(); + var popup = Assert.IsType(TopLevel.GetTopLevel(content)); + try + { + var args = CreateClosingArgs(programmatic); + typeof(PopupWindow).GetMethod("OnClosing", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! + .Invoke(popup, [args]); + Assert.Equal(canCancel && !programmatic, cancellation.IsCancellationRequested); + Assert.Equal(canCancel && !programmatic, content.WasCancellationRequested); + Assert.Equal(!programmatic, args.Cancel); + } + finally + { + content.SetCancellationTokenSource(null); + content.Close(); + } + } + [AvaloniaFact] public void DesktopCaptionButtons_UseNavigationColorsAndMinimizeHoverBackground() { diff --git a/WheelWizard/Views/App.axaml.cs b/WheelWizard/Views/App.axaml.cs index cfd703a83..06eadde27 100644 --- a/WheelWizard/Views/App.axaml.cs +++ b/WheelWizard/Views/App.axaml.cs @@ -80,7 +80,8 @@ public override void OnFrameworkInitializationCompleted() if (_createStartup is not null && ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; - desktop.MainWindow = new SplashWindow(); + // Activate from the dispatcher after the native event loop has started. + desktop.MainWindow = new SplashWindow { ShowActivated = false }; desktop.MainWindow.Show(); var shutdown = new CancellationTokenSource(); var cancellationToken = shutdown.Token; @@ -94,6 +95,7 @@ public override void OnFrameworkInitializationCompleted() { try { + desktop.MainWindow?.Activate(); var startup = await _createStartup(); cancellationToken.ThrowIfCancellationRequested(); await startup.StartAsync(desktop, StartupOptions.Parse(desktop.Args ?? []), cancellationToken); diff --git a/WheelWizard/Views/Behaviors/ToolTipBubbleBehavior.cs b/WheelWizard/Views/Behaviors/ToolTipBubbleBehavior.cs index 87fcc26a7..c1231bd73 100644 --- a/WheelWizard/Views/Behaviors/ToolTipBubbleBehavior.cs +++ b/WheelWizard/Views/Behaviors/ToolTipBubbleBehavior.cs @@ -19,10 +19,9 @@ public static class ToolTipBubbleBehavior private const string BubbleAnimateOutClass = "BubbleAnimateOut"; private const double TailCenterOffsetFromSide = 22d; private const double TooltipVerticalOffset = -4d; - private static readonly TimeSpan HoverOpenDelay = TimeSpan.FromMilliseconds(200); - private static readonly TimeSpan MinimumVisibleTime = TimeSpan.FromSeconds(1); private static readonly TimeSpan CloseAnimationDuration = TimeSpan.FromMilliseconds(40); private static readonly ConditionalWeakTable ToolTipStates = new(); + private static readonly ConditionalWeakTable> OpenToolTips = new(); private static bool _isInitialized; public static void Initialize() @@ -35,6 +34,7 @@ public static void Initialize() ToolTip.ToolTipOpeningEvent.AddClassHandler(OnToolTipOpening); ToolTip.IsOpenProperty.Changed.AddClassHandler(OnIsOpenChanged); InputElement.IsPointerOverProperty.Changed.AddClassHandler(OnIsPointerOverChanged); + WindowBase.IsActiveProperty.Changed.AddClassHandler(OnWindowActiveChanged); } private static void OnTipChanged(Control control, AvaloniaPropertyChangedEventArgs args) @@ -83,6 +83,21 @@ private static void OnIsPointerOverChanged(Control control, AvaloniaPropertyChan OnPointerExited(control); } + private static void OnWindowActiveChanged(Window window, AvaloniaPropertyChangedEventArgs args) + { + if (args.GetNewValue() || !OpenToolTips.TryGetValue(window, out var controls)) + return; + + // Native tooltip popups must disappear immediately when their owner loses focus. + foreach (var control in controls.ToArray()) + { + var state = GetState(control); + CancelPendingOpen(state); + CancelPendingClose(state); + ToolTip.SetIsOpen(control, false); + } + } + private static void OnIsOpenChanged(Control control, AvaloniaPropertyChangedEventArgs args) { var wasOpen = args.GetOldValue(); @@ -94,7 +109,9 @@ private static void OnIsOpenChanged(Control control, AvaloniaPropertyChangedEven if (isOpen) { - state.OpenedAt = DateTimeOffset.UtcNow; + state.OwnerWindow = TopLevel.GetTopLevel(control) as Window; + if (state.OwnerWindow != null) + OpenToolTips.GetOrCreateValue(state.OwnerWindow).Add(control); CancelPendingOpen(state); CancelPendingClose(state); return; @@ -102,6 +119,9 @@ private static void OnIsOpenChanged(Control control, AvaloniaPropertyChangedEven CancelPendingOpen(state); CancelPendingClose(state); + if (state.OwnerWindow != null && OpenToolTips.TryGetValue(state.OwnerWindow, out var controls)) + controls.Remove(control); + state.OwnerWindow = null; ClearBubbleAnimationClasses(control); } @@ -124,7 +144,7 @@ private static void OnPointerEntered(Control control) var cts = new CancellationTokenSource(); state.PendingOpenCts = cts; - _ = DeferredOpenAsync(control, state, HoverOpenDelay, cts.Token); + _ = OpenAsync(control, state, cts.Token); } private static void OnPointerExited(Control control) @@ -135,15 +155,10 @@ private static void OnPointerExited(Control control) var state = GetState(control); CancelPendingOpen(state); - var elapsed = DateTimeOffset.UtcNow - state.OpenedAt; - var remaining = MinimumVisibleTime - elapsed; - if (remaining < TimeSpan.Zero) - remaining = TimeSpan.Zero; - CancelPendingClose(state); var cts = new CancellationTokenSource(); state.PendingCloseCts = cts; - _ = DeferredCloseAsync(control, state, remaining, cts.Token); + _ = CloseAsync(control, state, cts.Token); } private static void ApplyPointerClass(ToolTip toolTip, PlacementMode placement) @@ -244,17 +259,8 @@ private static void CancelPendingOpen(ToolTipState state) state.PendingOpenCts = null; } - private static async Task DeferredOpenAsync(Control control, ToolTipState state, TimeSpan delay, CancellationToken cancellationToken) + private static async Task OpenAsync(Control control, ToolTipState state, CancellationToken cancellationToken) { - try - { - await Task.Delay(delay, cancellationToken); - } - catch (TaskCanceledException) - { - return; - } - await Dispatcher.UIThread.InvokeAsync(() => { if (cancellationToken.IsCancellationRequested) @@ -263,6 +269,9 @@ await Dispatcher.UIThread.InvokeAsync(() => state.PendingOpenCts?.Dispose(); state.PendingOpenCts = null; + if (TopLevel.GetTopLevel(control) is Window { IsActive: false }) + return; + if (!control.IsPointerOver || ToolTip.GetIsOpen(control) || !HasToolTip(control)) return; @@ -291,18 +300,8 @@ private static void PrepareToolTipAfterOpen(Control control, CancellationToken c ); } - private static async Task DeferredCloseAsync(Control control, ToolTipState state, TimeSpan delay, CancellationToken cancellationToken) + private static async Task CloseAsync(Control control, ToolTipState state, CancellationToken cancellationToken) { - try - { - if (delay > TimeSpan.Zero) - await Task.Delay(delay, cancellationToken); - } - catch (TaskCanceledException) - { - return; - } - var shouldAnimateOut = false; await Dispatcher.UIThread.InvokeAsync(() => { @@ -378,7 +377,7 @@ private static PlacementMode NormalizePlacement(PlacementMode placement) => private sealed class ToolTipState { - public DateTimeOffset OpenedAt { get; set; } + public Window? OwnerWindow { get; set; } public CancellationTokenSource? PendingOpenCts { get; set; } public CancellationTokenSource? PendingCloseCts { get; set; } } diff --git a/WheelWizard/Views/MainWindowService.cs b/WheelWizard/Views/MainWindowService.cs index be05f2b03..6e16e31ab 100644 --- a/WheelWizard/Views/MainWindowService.cs +++ b/WheelWizard/Views/MainWindowService.cs @@ -48,9 +48,10 @@ public async Task ShowAsync(IClassicDesktopStyleApplicationLifetime desktop, Can window.Opacity = 1; window.ShowInTaskbar = true; window.IsHitTestVisible = true; - window.Activate(); + window.ShowActivated = true; desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; splash?.Close(); + window.Activate(); } public void Refresh() diff --git a/WheelWizard/Views/ModManagement/ModPreviewViewModel.cs b/WheelWizard/Views/ModManagement/ModPreviewViewModel.cs index 39a10c727..1165d6704 100644 --- a/WheelWizard/Views/ModManagement/ModPreviewViewModel.cs +++ b/WheelWizard/Views/ModManagement/ModPreviewViewModel.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using Avalonia.Media.Imaging; +using SkiaSharp; using WheelWizard.GameBanana; namespace WheelWizard.Views.ModManagement; @@ -15,6 +16,29 @@ public sealed class ModPreviewViewModel( private Task? _loading; private bool _disposed; public Bitmap? Image { get; private set; } + private Bitmap? _grayscaleImage; + private byte[]? _imageBytes; + public Bitmap? GrayscaleImage + { + get + { + if (Image is null || _grayscaleImage is not null) + return _grayscaleImage; + using var bitmap = SKBitmap.Decode(_imageBytes!); + if (bitmap == null) + return Image; + using var gray = new SKBitmap(bitmap.Width, bitmap.Height); + using var canvas = new SKCanvas(gray); + using var filter = SKColorFilter.CreateColorMatrix( + [0.2126f, 0.7152f, 0.0722f, 0, 0, 0.2126f, 0.7152f, 0.0722f, 0, 0, 0.2126f, 0.7152f, 0.0722f, 0, 0, 0, 0, 0, 1, 0] + ); + using var paint = new SKPaint { ColorFilter = filter }; + canvas.DrawBitmap(bitmap, 0, 0, paint); + using var encoded = gray.Encode(SKEncodedImageFormat.Png, 100); + using var stream = encoded.AsStream(); + return _grayscaleImage = new Bitmap(stream); + } + } public bool ShowPlaceholder => Image is null; public event PropertyChangedEventHandler? PropertyChanged; @@ -50,6 +74,7 @@ private async Task LoadCoreAsync() return; using var stream = new MemoryStream(result.Value); Image = new Bitmap(stream); + _imageBytes = result.Value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty)); } catch (OperationCanceledException) when (_disposed) { } @@ -66,6 +91,9 @@ public void Dispose() _disposed = true; _lifetime.Cancel(); _lifetime.Dispose(); + _grayscaleImage?.Dispose(); + _grayscaleImage = null; + _imageBytes = null; Image?.Dispose(); Image = null; } diff --git a/WheelWizard/Views/Pages/ModsPage.axaml b/WheelWizard/Views/Pages/ModsPage.axaml index 6a186c81a..8d03b3263 100644 --- a/WheelWizard/Views/Pages/ModsPage.axaml +++ b/WheelWizard/Views/Pages/ModsPage.axaml @@ -10,9 +10,26 @@ mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="876" x:Class="WheelWizard.Views.Pages.ModsPage" x:DataType="pages:ModsPage"> + + + + + - + @@ -21,20 +38,45 @@ - + + + + + + + + - - + - - + Click="BrowseMod_Click" /> + + + + + + + + + + + + + + @@ -43,59 +85,36 @@ - - + - - + Click="BrowseMod_Click" /> + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -117,6 +136,7 @@ + @@ -131,38 +151,15 @@ HorizontalAlignment="Center" VerticalAlignment="Center" /> + Grid.Column="2" VerticalAlignment="Center" Margin="10,0,8,0" + TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" /> - - - - - - - - - - - + + + + + + + + + + - + @@ -252,6 +265,22 @@ + + + + + + diff --git a/WheelWizard/Views/Pages/ModsPage.axaml.cs b/WheelWizard/Views/Pages/ModsPage.axaml.cs index d9ecffb16..77a9e1638 100644 --- a/WheelWizard/Views/Pages/ModsPage.axaml.cs +++ b/WheelWizard/Views/Pages/ModsPage.axaml.cs @@ -156,6 +156,12 @@ private void UpdateEnableAllCheckboxState() EnableAllCheckbox.IsChecked = !ModManager.Mods.Select(mod => mod.IsEnabled).Contains(false); } + private void ModActionsMenu_Click(object? sender, RoutedEventArgs e) + { + if (sender is Control { ContextMenu: { } menu } button) + menu.Open(button); + } + private void BrowseMod_Click(object sender, RoutedEventArgs e) { var modPopup = Popups.Create(); @@ -414,16 +420,20 @@ private async void ButtonDown_OnClick(object? sender, RoutedEventArgs e) MessageTranslationHelper.ShowMessage(priorityResult.Error); } - private void ToggleModsPageView_OnClick(object? sender, RoutedEventArgs e) + private void ModsView_OnCheckedChanged(object? sender, RoutedEventArgs e) { - var current = SettingsService.Get(SettingsService.PREFERS_MODS_ROW_VIEW); - SettingsService.Set(SettingsService.PREFERS_MODS_ROW_VIEW, !current); + if (sender is not RadioButton { IsChecked: true } button) + return; + var gridView = ReferenceEquals(button, GridViewButton); + if (SettingsService.Get(SettingsService.PREFERS_MODS_ROW_VIEW) == gridView) + return; + SettingsService.Set(SettingsService.PREFERS_MODS_ROW_VIEW, gridView); SetModsViewVariant(); } private void SetModsViewVariant() { - Control[] elementsToSwapClasses = [ToggleButton, ModsListBox]; + Control[] elementsToSwapClasses = [ModsListBox]; var asRows = SettingsService.Get(SettingsService.PREFERS_MODS_ROW_VIEW); foreach (var elementToSwapClass in elementsToSwapClasses) @@ -442,6 +452,8 @@ private void SetModsViewVariant() // Toggle between list view (Blocks/arrows mode) and grid view (Rows/priority text mode) ModsListBox.IsVisible = !asRows; ModsGridView.IsVisible = asRows; + GridViewButton.IsChecked = asRows; + ListViewButton.IsChecked = !asRows; } private void PriorityText_OnKeyDown(object? sender, KeyEventArgs e) diff --git a/WheelWizard/Views/Patterns/GridModPanel.axaml b/WheelWizard/Views/Patterns/GridModPanel.axaml index 1cccd71a1..052fd3f3a 100644 --- a/WheelWizard/Views/Patterns/GridModPanel.axaml +++ b/WheelWizard/Views/Patterns/GridModPanel.axaml @@ -7,45 +7,38 @@ x:Class="WheelWizard.Views.Patterns.GridModPanel" x:DataType="pages:ModListItem"> - - + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + + + - - - - + - + + + - - - - - - diff --git a/WheelWizard/Views/Patterns/GridModPanel.axaml.cs b/WheelWizard/Views/Patterns/GridModPanel.axaml.cs index 2ec53a9c5..9c1479668 100644 --- a/WheelWizard/Views/Patterns/GridModPanel.axaml.cs +++ b/WheelWizard/Views/Patterns/GridModPanel.axaml.cs @@ -1,24 +1,108 @@ +using System.ComponentModel; +using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.VisualTree; +using WheelWizard.Models.Mods; using WheelWizard.Views.Pages; namespace WheelWizard.Views.Patterns; public partial class GridModPanel : UserControl { + private Mod? _observedMod; + public GridModPanel() { InitializeComponent(); DataContextChanged += OnDataContextChanged; + RootCardBorder.PointerEntered += (_, _) => UpdateThumbnail(); + RootCardBorder.PointerExited += (_, _) => UpdateThumbnail(); + CardSurface.SizeChanged += (_, _) => CardSurface.Clip = new RectangleGeometry(new Rect(CardSurface.Bounds.Size), 7, 7); + RootCardBorder.PointerPressed += OnCardPointerPressed; + RootCardBorder.PointerReleased += OnCardPointerReleased; + Focusable = true; + KeyDown += OnCardKeyDown; + } + + private bool _cardPressed; + + private static bool IsPriorityInput(object? source) => + source is Visual visual && (visual is TextBox || visual.GetVisualAncestors().OfType().Any()); + + private void OnCardPointerPressed(object? sender, PointerPressedEventArgs e) + { + _cardPressed = e.GetCurrentPoint(RootCardBorder).Properties.IsLeftButtonPressed && !IsPriorityInput(e.Source); + if (_cardPressed) + Focus(); + } + + private void OnCardPointerReleased(object? sender, PointerReleasedEventArgs e) + { + var toggle = + _cardPressed && e.InitialPressMouseButton == MouseButton.Left && RootCardBorder.IsPointerOver && !IsPriorityInput(e.Source); + _cardPressed = false; + if (toggle && _observedMod != null) + { + _observedMod.IsEnabled = !_observedMod.IsEnabled; + e.Handled = true; + } + } + + private void OnCardKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Space && !IsPriorityInput(e.Source) && _observedMod != null) + { + _observedMod.IsEnabled = !_observedMod.IsEnabled; + e.Handled = true; + } } private async void OnDataContextChanged(object? sender, EventArgs e) { + if (_observedMod != null) + _observedMod.PropertyChanged -= OnModChanged; + _observedMod = (DataContext as ModListItem)?.Mod; + if (_observedMod != null) + _observedMod.PropertyChanged += OnModChanged; + UpdateThumbnail(); if (DataContext is ModListItem item) await item.Preview.LoadAsync(); } + private void OnModChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Mod.IsEnabled)) + UpdateThumbnail(); + } + + private void UpdateThumbnail() + { + var gray = _observedMod is { IsEnabled: false } && !RootCardBorder.IsPointerOver; + DisabledThumbnailImage.IsVisible = gray; + ThumbnailImage.IsVisible = !gray; + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + if (_observedMod != null) + _observedMod.PropertyChanged -= OnModChanged; + base.OnDetachedFromVisualTree(e); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + if (_observedMod != null) + { + _observedMod.PropertyChanged -= OnModChanged; + _observedMod.PropertyChanged += OnModChanged; + } + UpdateThumbnail(); + } + private void PriorityText_OnLostFocus(object? sender, RoutedEventArgs e) { if (DataContext is not ModListItem item || e.Source is not TextBox textBox) diff --git a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml index e4de476ba..26f04b909 100644 --- a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml +++ b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml @@ -50,26 +50,14 @@ HorizontalAlignment="Left" VerticalAlignment="Stretch" /> - - - - - + + + - + @@ -117,7 +105,7 @@ - - - diff --git a/WheelWizard/Views/Patterns/ModBrowserListItem.axaml.cs b/WheelWizard/Features/Mods/Views/ModBrowserListItem.axaml.cs similarity index 97% rename from WheelWizard/Views/Patterns/ModBrowserListItem.axaml.cs rename to WheelWizard/Features/Mods/Views/ModBrowserListItem.axaml.cs index 9741d2acf..fe9ab9378 100644 --- a/WheelWizard/Views/Patterns/ModBrowserListItem.axaml.cs +++ b/WheelWizard/Features/Mods/Views/ModBrowserListItem.axaml.cs @@ -1,9 +1,9 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; -using WheelWizard.Views.ModManagement; +using WheelWizard.Mods.ViewModels; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.Mods.Views; public class ModBrowserListItem : TemplatedControl { diff --git a/WheelWizard/Views/Pages/ModsPage.axaml b/WheelWizard/Features/Mods/Views/ModsPage.axaml similarity index 98% rename from WheelWizard/Views/Pages/ModsPage.axaml rename to WheelWizard/Features/Mods/Views/ModsPage.axaml index 8d03b3263..16a7c2628 100644 --- a/WheelWizard/Views/Pages/ModsPage.axaml +++ b/WheelWizard/Features/Mods/Views/ModsPage.axaml @@ -4,12 +4,11 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" - xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns" - xmlns:pages="clr-namespace:WheelWizard.Views.Pages" xmlns:patches="clr-namespace:WheelWizard.Features.Patches" mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="876" - x:Class="WheelWizard.Views.Pages.ModsPage" - x:DataType="pages:ModsPage"> + x:Class="WheelWizard.Mods.Views.ModsPage" + x:DataType="mods:ModsPage" + xmlns:mods="clr-namespace:WheelWizard.Mods.Views"> - - - diff --git a/WheelWizard/Views/Patterns/PlayerListItem.axaml.cs b/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml.cs similarity index 98% rename from WheelWizard/Views/Patterns/PlayerListItem.axaml.cs rename to WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml.cs index a159cbb10..1320ce4b1 100644 --- a/WheelWizard/Views/Patterns/PlayerListItem.axaml.cs +++ b/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml.cs @@ -2,10 +2,11 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; +using WheelWizard.Views.Components; using WheelWizard.WheelWizardData.Domain; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.RrRooms.Views; public class PlayerListItem : TemplatedControl { diff --git a/WheelWizard/Views/Pages/RoomDetailsPage.axaml b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml similarity index 95% rename from WheelWizard/Views/Pages/RoomDetailsPage.axaml rename to WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml index 51e84e79a..185a11bc2 100644 --- a/WheelWizard/Views/Pages/RoomDetailsPage.axaml +++ b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml @@ -5,10 +5,9 @@ mc:Ignorable="d" d:DesignHeight="876" d:DesignWidth="456" xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" - xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns" - xmlns:pages="clr-namespace:WheelWizard.Views.Pages" - x:Class="WheelWizard.Views.Pages.RoomDetailsPage" - x:DataType="pages:RoomDetailsPage"> + x:Class="WheelWizard.RrRooms.Views.RoomDetailsPage" + x:DataType="rooms:RoomDetailsPage" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views"> @@ -87,7 +86,7 @@ SelectionChanged="PlayerView_SelectionChanged"> - + x:Class="WheelWizard.RrRooms.Views.RoomsPage" + x:DataType="rooms:RoomsPage" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views"> @@ -140,7 +139,7 @@ SelectionChanged="PlayerView_SelectionChanged"> - + x:Class="WheelWizard.Settings.Views.AppInfo"> diff --git a/WheelWizard/Views/Pages/Settings/AppInfo.axaml.cs b/WheelWizard/Features/Settings/Views/AppInfo.axaml.cs similarity index 96% rename from WheelWizard/Views/Pages/Settings/AppInfo.axaml.cs rename to WheelWizard/Features/Settings/Views/AppInfo.axaml.cs index 36007b37f..6a1c497a2 100644 --- a/WheelWizard/Views/Pages/Settings/AppInfo.axaml.cs +++ b/WheelWizard/Features/Settings/Views/AppInfo.axaml.cs @@ -3,8 +3,9 @@ using WheelWizard.Branding; using WheelWizard.CustomDistributions; using WheelWizard.Shared; +using WheelWizard.Views.Shell; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; public partial class AppInfo : UserControl { diff --git a/WheelWizard/Views/Pages/Settings/OtherSettings.axaml b/WheelWizard/Features/Settings/Views/OtherSettings.axaml similarity index 99% rename from WheelWizard/Views/Pages/Settings/OtherSettings.axaml rename to WheelWizard/Features/Settings/Views/OtherSettings.axaml index 731e2c880..04cba764d 100644 --- a/WheelWizard/Views/Pages/Settings/OtherSettings.axaml +++ b/WheelWizard/Features/Settings/Views/OtherSettings.axaml @@ -5,7 +5,7 @@ mc:Ignorable="d" d:DesignWidth="444" d:DesignHeight="831" xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components1="clr-namespace:WheelWizard.Views.Components" - x:Class="WheelWizard.Views.Pages.Settings.OtherSettings"> + x:Class="WheelWizard.Settings.Views.OtherSettings"> diff --git a/WheelWizard/Views/Pages/Settings/OtherSettings.axaml.cs b/WheelWizard/Features/Settings/Views/OtherSettings.axaml.cs similarity index 96% rename from WheelWizard/Views/Pages/Settings/OtherSettings.axaml.cs rename to WheelWizard/Features/Settings/Views/OtherSettings.axaml.cs index d42a0cf47..a10332d4e 100644 --- a/WheelWizard/Views/Pages/Settings/OtherSettings.axaml.cs +++ b/WheelWizard/Features/Settings/Views/OtherSettings.axaml.cs @@ -1,14 +1,14 @@ using Avalonia.Controls; using Avalonia.Interactivity; using WheelWizard.CustomDistributions; +using WheelWizard.CustomDistributions.Presentation; using WheelWizard.Recomp; using WheelWizard.Settings; +using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.MessageTranslations; -using WheelWizard.Views.Distributions; -using WheelWizard.Views.Popups.Generic; -using WheelWizard.Views.Storage; +using WheelWizard.Views.Dialogs; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; public partial class OtherSettings : UserControl { diff --git a/WheelWizard/Views/Pages/Settings/RecompSettings.axaml b/WheelWizard/Features/Settings/Views/RecompSettings.axaml similarity index 99% rename from WheelWizard/Views/Pages/Settings/RecompSettings.axaml rename to WheelWizard/Features/Settings/Views/RecompSettings.axaml index d502eb800..52e36cdca 100644 --- a/WheelWizard/Views/Pages/Settings/RecompSettings.axaml +++ b/WheelWizard/Features/Settings/Views/RecompSettings.axaml @@ -5,7 +5,7 @@ xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" mc:Ignorable="d" d:DesignWidth="444" d:DesignHeight="716" - x:Class="WheelWizard.Views.Pages.Settings.RecompSettings"> + x:Class="WheelWizard.Settings.Views.RecompSettings"> diff --git a/WheelWizard/Views/Pages/Settings/RecompSettings.axaml.cs b/WheelWizard/Features/Settings/Views/RecompSettings.axaml.cs similarity index 98% rename from WheelWizard/Views/Pages/Settings/RecompSettings.axaml.cs rename to WheelWizard/Features/Settings/Views/RecompSettings.axaml.cs index cada035a6..e3bf7d082 100644 --- a/WheelWizard/Views/Pages/Settings/RecompSettings.axaml.cs +++ b/WheelWizard/Features/Settings/Views/RecompSettings.axaml.cs @@ -2,11 +2,13 @@ using Avalonia.Interactivity; using WheelWizard.Recomp; using WheelWizard.Settings; +using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.MessageTranslations; -using WheelWizard.Views.Popups.Generic; -using WheelWizard.Views.Storage; +using WheelWizard.Views.Components; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; public partial class RecompSettings : UserControl { diff --git a/WheelWizard/Views/Pages/Settings/SettingsEditing.cs b/WheelWizard/Features/Settings/Views/SettingsEditing.cs similarity index 92% rename from WheelWizard/Views/Pages/Settings/SettingsEditing.cs rename to WheelWizard/Features/Settings/Views/SettingsEditing.cs index 7d4243949..8ad9725b8 100644 --- a/WheelWizard/Views/Pages/Settings/SettingsEditing.cs +++ b/WheelWizard/Features/Settings/Views/SettingsEditing.cs @@ -2,7 +2,7 @@ using WheelWizard.Settings.Types; using WheelWizard.Shared.MessageTranslations; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; internal static class SettingsEditing { diff --git a/WheelWizard/Views/Pages/SettingsPage.axaml b/WheelWizard/Features/Settings/Views/SettingsPage.axaml similarity index 97% rename from WheelWizard/Views/Pages/SettingsPage.axaml rename to WheelWizard/Features/Settings/Views/SettingsPage.axaml index cda7ad5d9..33d4dc3e0 100644 --- a/WheelWizard/Views/Pages/SettingsPage.axaml +++ b/WheelWizard/Features/Settings/Views/SettingsPage.axaml @@ -5,7 +5,7 @@ xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" mc:Ignorable="d" d:DesignWidth="656" d:DesignHeight="876" - x:Class="WheelWizard.Views.Pages.SettingsPage"> + x:Class="WheelWizard.Settings.Views.SettingsPage"> diff --git a/WheelWizard/Views/Pages/SettingsPage.axaml.cs b/WheelWizard/Features/Settings/Views/SettingsPage.axaml.cs similarity index 95% rename from WheelWizard/Views/Pages/SettingsPage.axaml.cs rename to WheelWizard/Features/Settings/Views/SettingsPage.axaml.cs index 3191baa6a..941bb2986 100644 --- a/WheelWizard/Views/Pages/SettingsPage.axaml.cs +++ b/WheelWizard/Features/Settings/Views/SettingsPage.axaml.cs @@ -1,12 +1,13 @@ using Avalonia.Controls; using Avalonia.Interactivity; using WheelWizard.Settings; +using WheelWizard.Settings.Views; using WheelWizard.Shared; -using WheelWizard.Views.Navigation; -using WheelWizard.Views.Pages.Settings; -using WheelWizard.Views.Popups; +using WheelWizard.Views.DesignTime; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell.Navigation; -namespace WheelWizard.Views.Pages; +namespace WheelWizard.Settings.Views; public partial class SettingsPage : UserControl { diff --git a/WheelWizard/Views/Pages/Settings/VideoSettings.axaml b/WheelWizard/Features/Settings/Views/VideoSettings.axaml similarity index 98% rename from WheelWizard/Views/Pages/Settings/VideoSettings.axaml rename to WheelWizard/Features/Settings/Views/VideoSettings.axaml index 24d3da8b9..4e4d243e6 100644 --- a/WheelWizard/Views/Pages/Settings/VideoSettings.axaml +++ b/WheelWizard/Features/Settings/Views/VideoSettings.axaml @@ -3,10 +3,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="444" d:DesignHeight="716" - xmlns:settings="clr-namespace:WheelWizard.Views.Pages.Settings" xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components1="clr-namespace:WheelWizard.Views.Components" - x:Class="WheelWizard.Views.Pages.Settings.VideoSettings"> + x:Class="WheelWizard.Settings.Views.VideoSettings"> diff --git a/WheelWizard/Views/Pages/Settings/VideoSettings.axaml.cs b/WheelWizard/Features/Settings/Views/VideoSettings.axaml.cs similarity index 99% rename from WheelWizard/Views/Pages/Settings/VideoSettings.axaml.cs rename to WheelWizard/Features/Settings/Views/VideoSettings.axaml.cs index d7bee432d..c8711eac0 100644 --- a/WheelWizard/Views/Pages/Settings/VideoSettings.axaml.cs +++ b/WheelWizard/Features/Settings/Views/VideoSettings.axaml.cs @@ -4,7 +4,7 @@ using WheelWizard.Settings.Types; using WheelWizard.Shared.MessageTranslations; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; public partial class VideoSettings : UserControl { diff --git a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml b/WheelWizard/Features/Settings/Views/WhWzSettings.axaml similarity index 99% rename from WheelWizard/Views/Pages/Settings/WhWzSettings.axaml rename to WheelWizard/Features/Settings/Views/WhWzSettings.axaml index a3baeef07..e3d1cad61 100644 --- a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml +++ b/WheelWizard/Features/Settings/Views/WhWzSettings.axaml @@ -5,7 +5,7 @@ xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components1="clr-namespace:WheelWizard.Views.Components" mc:Ignorable="d" d:DesignWidth="444" d:DesignHeight="716" - x:Class="WheelWizard.Views.Pages.Settings.WhWzSettings"> + x:Class="WheelWizard.Settings.Views.WhWzSettings"> diff --git a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs b/WheelWizard/Features/Settings/Views/WhWzSettings.axaml.cs similarity index 99% rename from WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs rename to WheelWizard/Features/Settings/Views/WhWzSettings.axaml.cs index 2dffb65b2..6a342d8e8 100644 --- a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs +++ b/WheelWizard/Features/Settings/Views/WhWzSettings.axaml.cs @@ -9,14 +9,16 @@ using WheelWizard.Dolphin.Paths; using WheelWizard.Settings; using WheelWizard.Settings.Types; +using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.IO; using WheelWizard.Shared.MessageTranslations; using WheelWizard.Shared.Processes; -using WheelWizard.Views.Popups.Generic; -using WheelWizard.Views.Storage; +using WheelWizard.Views.Components; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; using SettingsButton = WheelWizard.Views.Components.Button; -namespace WheelWizard.Views.Pages.Settings; +namespace WheelWizard.Settings.Views; public partial class WhWzSettings : UserControl { diff --git a/WheelWizard/Views/Patterns/VrHistoryViewModel.cs b/WheelWizard/Features/WheelWizardData/ViewModels/VrHistoryViewModel.cs similarity index 98% rename from WheelWizard/Views/Patterns/VrHistoryViewModel.cs rename to WheelWizard/Features/WheelWizardData/ViewModels/VrHistoryViewModel.cs index 8c545d184..dfe11f18f 100644 --- a/WheelWizard/Views/Patterns/VrHistoryViewModel.cs +++ b/WheelWizard/Features/WheelWizardData/ViewModels/VrHistoryViewModel.cs @@ -2,7 +2,7 @@ using WheelWizard.RrRooms; using WheelWizard.Shared.Services; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.WheelWizardData.ViewModels; public sealed class VrHistoryViewModel(IApiCaller apiCaller) : INotifyPropertyChanged, IDisposable { diff --git a/WheelWizard/Views/Patterns/CurrentUserProfile.axaml b/WheelWizard/Features/WheelWizardData/Views/CurrentUserProfile.axaml similarity index 84% rename from WheelWizard/Views/Patterns/CurrentUserProfile.axaml rename to WheelWizard/Features/WheelWizardData/Views/CurrentUserProfile.axaml index 1517ad46e..4dea2682d 100644 --- a/WheelWizard/Views/Patterns/CurrentUserProfile.axaml +++ b/WheelWizard/Features/WheelWizardData/Views/CurrentUserProfile.axaml @@ -1,8 +1,8 @@ - + x:Class="WheelWizard.WheelWizardData.Views.CurrentUserProfile" + x:DataType="community:CurrentUserProfile" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views"> + x:Class="WheelWizard.WheelWizardData.Views.Dialogs.AddFriendConfirmationWindow" + x:DataType="communityDialogs:AddFriendConfirmationWindow" + mc:Ignorable="d" d:DesignWidth="420" d:DesignHeight="320" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base" + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views" + xmlns:communityDialogs="clr-namespace:WheelWizard.WheelWizardData.Views.Dialogs"> - - + diff --git a/WheelWizard/Views/Popups/AddFriendConfirmationWindow.axaml.cs b/WheelWizard/Features/WheelWizardData/Views/Dialogs/AddFriendConfirmationWindow.axaml.cs similarity index 93% rename from WheelWizard/Views/Popups/AddFriendConfirmationWindow.axaml.cs rename to WheelWizard/Features/WheelWizardData/Views/Dialogs/AddFriendConfirmationWindow.axaml.cs index ad5d6d716..a90bdf858 100644 --- a/WheelWizard/Views/Popups/AddFriendConfirmationWindow.axaml.cs +++ b/WheelWizard/Features/WheelWizardData/Views/Dialogs/AddFriendConfirmationWindow.axaml.cs @@ -1,9 +1,9 @@ using Avalonia.Interactivity; using WheelWizard.Models; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; -namespace WheelWizard.Views.Popups; +namespace WheelWizard.WheelWizardData.Views.Dialogs; public partial class AddFriendConfirmationWindow : PopupContent { diff --git a/WheelWizard/Views/Popups/PlayerProfileWindow.axaml b/WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml similarity index 90% rename from WheelWizard/Views/Popups/PlayerProfileWindow.axaml rename to WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml index d405b9a9f..b1cb81701 100644 --- a/WheelWizard/Views/Popups/PlayerProfileWindow.axaml +++ b/WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml @@ -1,17 +1,17 @@ - + x:Class="WheelWizard.WheelWizardData.Views.Dialogs.PlayerProfileWindow" + x:DataType="communityDialogs:PlayerProfileWindow" + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base" + xmlns:communityDialogs="clr-namespace:WheelWizard.WheelWizardData.Views.Dialogs"> @@ -33,7 +33,7 @@ - - + diff --git a/WheelWizard/Views/Popups/PlayerProfileWindow.axaml.cs b/WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml.cs similarity index 97% rename from WheelWizard/Views/Popups/PlayerProfileWindow.axaml.cs rename to WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml.cs index cf53da069..47b92b396 100644 --- a/WheelWizard/Views/Popups/PlayerProfileWindow.axaml.cs +++ b/WheelWizard/Features/WheelWizardData/Views/Dialogs/PlayerProfileWindow.axaml.cs @@ -5,12 +5,12 @@ using WheelWizard.Models; using WheelWizard.RrRooms; using WheelWizard.Shared.Services; -using WheelWizard.Views.Patterns; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; +using WheelWizard.WheelWizardData.Views; using WheelWizard.WiiManagement.MiiManagement; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; -namespace WheelWizard.Views.Popups; +namespace WheelWizard.WheelWizardData.Views.Dialogs; public partial class PlayerProfileWindow : PopupContent, INotifyPropertyChanged { diff --git a/WheelWizard/Views/Patterns/FriendsListItem.axaml b/WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml similarity index 91% rename from WheelWizard/Views/Patterns/FriendsListItem.axaml rename to WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml index 443738506..02b8a06e7 100644 --- a/WheelWizard/Views/Patterns/FriendsListItem.axaml +++ b/WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml @@ -4,39 +4,39 @@ xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" xmlns:miiVars="using:WheelWizard.MiiImages.Domain" - xmlns:behaviorComponent="clr-namespace:WheelWizard.Views.Patterns" - xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns"> + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views"> - - - - - - - - - diff --git a/WheelWizard/Views/Patterns/FriendsListItem.axaml.cs b/WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml.cs similarity index 98% rename from WheelWizard/Views/Patterns/FriendsListItem.axaml.cs rename to WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml.cs index a3a26a3b1..e799ab773 100644 --- a/WheelWizard/Views/Patterns/FriendsListItem.axaml.cs +++ b/WheelWizard/Features/WheelWizardData/Views/FriendsListItem.axaml.cs @@ -5,7 +5,7 @@ using WheelWizard.WheelWizardData.Domain; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.WheelWizardData.Views; public class FriendsListItem : TemplatedControl { diff --git a/WheelWizard/Views/Pages/FriendsPage.axaml b/WheelWizard/Features/WheelWizardData/Views/FriendsPage.axaml similarity index 96% rename from WheelWizard/Views/Pages/FriendsPage.axaml rename to WheelWizard/Features/WheelWizardData/Views/FriendsPage.axaml index 3e0850e35..72aa56852 100644 --- a/WheelWizard/Views/Pages/FriendsPage.axaml +++ b/WheelWizard/Features/WheelWizardData/Views/FriendsPage.axaml @@ -5,9 +5,9 @@ mc:Ignorable="d" d:DesignWidth="656" d:DesignHeight="876" xmlns:loc="clr-namespace:WheelWizard.Localization" xmlns:components="clr-namespace:WheelWizard.Views.Components" - xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns" xmlns:domain="clr-namespace:WheelWizard.WiiManagement.GameLicense.Domain" - x:Class="WheelWizard.Views.Pages.FriendsPage"> + x:Class="WheelWizard.WheelWizardData.Views.FriendsPage" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views"> @@ -75,7 +75,7 @@ - + x:Class="WheelWizard.WheelWizardData.Views.LeaderboardPage" + x:DataType="community:LeaderboardPage" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views"> - - - - @@ -174,7 +174,7 @@ RowDefinitions="Auto,16" VerticalAlignment="Center" ColumnDefinitions="*,1.1*,*"> - - + @@ -201,10 +201,10 @@ - - + + - - + @@ -232,10 +232,10 @@ - - + + - - + @@ -262,16 +262,16 @@ - - + + - - + - + @@ -296,8 +296,8 @@ - - + + diff --git a/WheelWizard/Views/Pages/LeaderboardPage.axaml.cs b/WheelWizard/Features/WheelWizardData/Views/LeaderboardPage.axaml.cs similarity index 98% rename from WheelWizard/Views/Pages/LeaderboardPage.axaml.cs rename to WheelWizard/Features/WheelWizardData/Views/LeaderboardPage.axaml.cs index 1b8cd5c36..8b0748e35 100644 --- a/WheelWizard/Views/Pages/LeaderboardPage.axaml.cs +++ b/WheelWizard/Features/WheelWizardData/Views/LeaderboardPage.axaml.cs @@ -7,18 +7,21 @@ using WheelWizard.Localization; using WheelWizard.Models; using WheelWizard.RrRooms; +using WheelWizard.RrRooms.Views; using WheelWizard.Settings; -using WheelWizard.Views.Navigation; -using WheelWizard.Views.Popups; -using WheelWizard.Views.Popups.MiiManagement; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; +using WheelWizard.Views.Shell.Navigation; using WheelWizard.WheelWizardData; using WheelWizard.WheelWizardData.Domain; +using WheelWizard.WheelWizardData.Views.Dialogs; using WheelWizard.WiiManagement.FriendCodes; using WheelWizard.WiiManagement.GameLicense; using WheelWizard.WiiManagement.MiiManagement; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; +using WheelWizard.WiiManagement.MiiManagement.Views.Dialogs; -namespace WheelWizard.Views.Pages; +namespace WheelWizard.WheelWizardData.Views; public sealed record LeaderboardPlayerItem { diff --git a/WheelWizard/Views/Patterns/LeaderboardPodiumCard.axaml b/WheelWizard/Features/WheelWizardData/Views/LeaderboardPodiumCard.axaml similarity index 92% rename from WheelWizard/Views/Patterns/LeaderboardPodiumCard.axaml rename to WheelWizard/Features/WheelWizardData/Views/LeaderboardPodiumCard.axaml index 9b462117f..b1050d0ce 100644 --- a/WheelWizard/Views/Patterns/LeaderboardPodiumCard.axaml +++ b/WheelWizard/Features/WheelWizardData/Views/LeaderboardPodiumCard.axaml @@ -1,13 +1,13 @@ + xmlns:miiVars="using:WheelWizard.MiiImages.Domain" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views" + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views"> - - - - - - - - - - - - - - - - - - - - - diff --git a/WheelWizard/Views/Patterns/MiiBlock.axaml.cs b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiBlock.axaml.cs similarity index 97% rename from WheelWizard/Views/Patterns/MiiBlock.axaml.cs rename to WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiBlock.axaml.cs index 110107ba9..6958112a9 100644 --- a/WheelWizard/Views/Patterns/MiiBlock.axaml.cs +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiBlock.axaml.cs @@ -5,11 +5,12 @@ using Avalonia.Interactivity; using WheelWizard.MiiImages; using WheelWizard.MiiImages.Domain; +using WheelWizard.MiiImages.Views; using WheelWizard.WiiManagement; using WheelWizard.WiiManagement.MiiManagement; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.WiiManagement.MiiManagement.Views; public class MiiBlock : RadioButton { diff --git a/WheelWizard/Views/Pages/MiiListPage.axaml b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml similarity index 94% rename from WheelWizard/Views/Pages/MiiListPage.axaml rename to WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml index 8d4ba3728..fb09f2d11 100644 --- a/WheelWizard/Views/Pages/MiiListPage.axaml +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml @@ -3,10 +3,10 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:components="clr-namespace:WheelWizard.Views.Components" - xmlns:behaviorComponent="clr-namespace:WheelWizard.Views.Patterns" xmlns:loc="clr-namespace:WheelWizard.Localization" mc:Ignorable="d" d:DesignWidth="456" d:DesignHeight="776" - x:Class="WheelWizard.Views.Pages.MiiListPage"> + x:Class="WheelWizard.WiiManagement.MiiManagement.Views.MiiListPage" + xmlns:miis="clr-namespace:WheelWizard.WiiManagement.MiiManagement.Views"> @@ -104,12 +104,12 @@ - - + - - + + diff --git a/WheelWizard/Views/Pages/MiiListPage.axaml.cs b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml.cs similarity index 98% rename from WheelWizard/Views/Pages/MiiListPage.axaml.cs rename to WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml.cs index 9a6f1fa47..530228b89 100644 --- a/WheelWizard/Views/Pages/MiiListPage.axaml.cs +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml.cs @@ -8,18 +8,18 @@ using Testably.Abstractions; using WheelWizard.CustomCharacters; using WheelWizard.Settings; +using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.MessageTranslations; -using WheelWizard.Views.Components; -using WheelWizard.Views.Patterns; -using WheelWizard.Views.Popups; -using WheelWizard.Views.Popups.Generic; -using WheelWizard.Views.Popups.MiiManagement; -using WheelWizard.Views.Storage; +using WheelWizard.Views.DesignTime; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; using WheelWizard.WiiManagement; using WheelWizard.WiiManagement.MiiManagement; using WheelWizard.WiiManagement.MiiManagement.Domain.Mii; +using WheelWizard.WiiManagement.MiiManagement.Views; +using WheelWizard.WiiManagement.MiiManagement.Views.Dialogs; -namespace WheelWizard.Views.Pages; +namespace WheelWizard.WiiManagement.MiiManagement.Views; public partial class MiiListPage : UserControl { diff --git a/WheelWizard/Program.cs b/WheelWizard/Program.cs index f95d5c08a..3da1392b7 100644 --- a/WheelWizard/Program.cs +++ b/WheelWizard/Program.cs @@ -5,12 +5,13 @@ using WheelWizard.ApplicationData; using WheelWizard.ApplicationIntegration; using WheelWizard.ApplicationLifecycle.Logging; +using WheelWizard.MiiImages.Views; using WheelWizard.Settings; using WheelWizard.Shared.Platform; using WheelWizard.Shared.Services; using WheelWizard.Views; -using WheelWizard.Views.Patterns; -using WheelWizard.Views.Startup; +using WheelWizard.Views.Shell; +using WheelWizard.Views.Shell.Startup; namespace WheelWizard; diff --git a/WheelWizard/SetupExtensions.cs b/WheelWizard/SetupExtensions.cs index 846ad0a05..44d92d0e5 100644 --- a/WheelWizard/SetupExtensions.cs +++ b/WheelWizard/SetupExtensions.cs @@ -17,7 +17,7 @@ using WheelWizard.Recomp; using WheelWizard.RrRooms; using WheelWizard.Settings; -using WheelWizard.Views; +using WheelWizard.Views.Shell; using WheelWizard.WheelWizardData; using WheelWizard.WiiManagement; diff --git a/WheelWizard/Views/Polling/AvaloniaPollingScheduler.cs b/WheelWizard/Shared/Desktop/Polling/AvaloniaPollingScheduler.cs similarity index 94% rename from WheelWizard/Views/Polling/AvaloniaPollingScheduler.cs rename to WheelWizard/Shared/Desktop/Polling/AvaloniaPollingScheduler.cs index 0bedef0e5..f99726dcd 100644 --- a/WheelWizard/Views/Polling/AvaloniaPollingScheduler.cs +++ b/WheelWizard/Shared/Desktop/Polling/AvaloniaPollingScheduler.cs @@ -1,7 +1,7 @@ using Avalonia.Threading; using WheelWizard.Shared.Polling; -namespace WheelWizard.Views.Polling; +namespace WheelWizard.Shared.Desktop.Polling; public sealed class AvaloniaPollingScheduler : IPollingScheduler { diff --git a/WheelWizard/Views/Storage/DesktopStorageProviderAccessor.cs b/WheelWizard/Shared/Desktop/Storage/DesktopStorageProviderAccessor.cs similarity index 92% rename from WheelWizard/Views/Storage/DesktopStorageProviderAccessor.cs rename to WheelWizard/Shared/Desktop/Storage/DesktopStorageProviderAccessor.cs index 021b2f6b5..fd55ad1ca 100644 --- a/WheelWizard/Views/Storage/DesktopStorageProviderAccessor.cs +++ b/WheelWizard/Shared/Desktop/Storage/DesktopStorageProviderAccessor.cs @@ -2,7 +2,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; -namespace WheelWizard.Views.Storage; +namespace WheelWizard.Shared.Desktop.Storage; public interface IStorageProviderAccessor { diff --git a/WheelWizard/Views/Storage/FilePickerFilters.cs b/WheelWizard/Shared/Desktop/Storage/FilePickerFilters.cs similarity index 92% rename from WheelWizard/Views/Storage/FilePickerFilters.cs rename to WheelWizard/Shared/Desktop/Storage/FilePickerFilters.cs index 8007da814..74c7e74c9 100644 --- a/WheelWizard/Views/Storage/FilePickerFilters.cs +++ b/WheelWizard/Shared/Desktop/Storage/FilePickerFilters.cs @@ -1,6 +1,6 @@ using Avalonia.Platform.Storage; -namespace WheelWizard.Views.Storage; +namespace WheelWizard.Shared.Desktop.Storage; public static class FilePickerFilters { diff --git a/WheelWizard/Views/Storage/FilePickerService.cs b/WheelWizard/Shared/Desktop/Storage/FilePickerService.cs similarity index 98% rename from WheelWizard/Views/Storage/FilePickerService.cs rename to WheelWizard/Shared/Desktop/Storage/FilePickerService.cs index cf4d4105c..6a062184a 100644 --- a/WheelWizard/Views/Storage/FilePickerService.cs +++ b/WheelWizard/Shared/Desktop/Storage/FilePickerService.cs @@ -3,7 +3,7 @@ using WheelWizard.Shared.Platform; using WheelWizard.Shared.Processes; -namespace WheelWizard.Views.Storage; +namespace WheelWizard.Shared.Desktop.Storage; public sealed class FilePickerService(IStorageProviderAccessor storage, IRuntimeEnvironment environment, IProcessLauncher processes) : IFilePickerService diff --git a/WheelWizard/Views/Storage/IFilePickerService.cs b/WheelWizard/Shared/Desktop/Storage/IFilePickerService.cs similarity index 93% rename from WheelWizard/Views/Storage/IFilePickerService.cs rename to WheelWizard/Shared/Desktop/Storage/IFilePickerService.cs index 61695efb1..4c095b3b7 100644 --- a/WheelWizard/Views/Storage/IFilePickerService.cs +++ b/WheelWizard/Shared/Desktop/Storage/IFilePickerService.cs @@ -1,6 +1,6 @@ using Avalonia.Platform.Storage; -namespace WheelWizard.Views.Storage; +namespace WheelWizard.Shared.Desktop.Storage; public interface IFilePickerService { diff --git a/WheelWizard/Views/Storage/StoragePaths.cs b/WheelWizard/Shared/Desktop/Storage/StoragePaths.cs similarity index 96% rename from WheelWizard/Views/Storage/StoragePaths.cs rename to WheelWizard/Shared/Desktop/Storage/StoragePaths.cs index d5d18f0e4..94b2dc235 100644 --- a/WheelWizard/Views/Storage/StoragePaths.cs +++ b/WheelWizard/Shared/Desktop/Storage/StoragePaths.cs @@ -1,6 +1,6 @@ using Avalonia.Platform.Storage; -namespace WheelWizard.Views.Storage; +namespace WheelWizard.Shared.Desktop.Storage; public static class StoragePaths { diff --git a/WheelWizard/Views/Downloads/DownloadPresentation.cs b/WheelWizard/Shared/Downloads/Presentation/DownloadPresentation.cs similarity index 96% rename from WheelWizard/Views/Downloads/DownloadPresentation.cs rename to WheelWizard/Shared/Downloads/Presentation/DownloadPresentation.cs index f246ce39f..89164e15b 100644 --- a/WheelWizard/Views/Downloads/DownloadPresentation.cs +++ b/WheelWizard/Shared/Downloads/Presentation/DownloadPresentation.cs @@ -1,7 +1,7 @@ using WheelWizard.Shared.Downloads; -using WheelWizard.Views.Popups.Generic; +using WheelWizard.Views.Dialogs; -namespace WheelWizard.Views.Downloads; +namespace WheelWizard.Shared.Downloads.Presentation; // Presentation adapts download progress and cancellation to the existing dialogs. // The download service itself has no dependency on Avalonia or global application state. diff --git a/WheelWizard/Shared/MessageTranslations/MessageTranslationHelper.cs b/WheelWizard/Shared/MessageTranslations/MessageTranslationHelper.cs index 84b54b8f0..f4a5eba3b 100644 --- a/WheelWizard/Shared/MessageTranslations/MessageTranslationHelper.cs +++ b/WheelWizard/Shared/MessageTranslations/MessageTranslationHelper.cs @@ -1,5 +1,5 @@ using Serilog; -using WheelWizard.Views.Popups.Generic; +using WheelWizard.Views.Dialogs; namespace WheelWizard.Shared.MessageTranslations; diff --git a/WheelWizard/Views/App.axaml b/WheelWizard/Views/App.axaml index 6335471c3..c5ad9ef41 100644 --- a/WheelWizard/Views/App.axaml +++ b/WheelWizard/Views/App.axaml @@ -105,7 +105,7 @@ Value="{OnPlatform Default={StaticResource DesktopWindowDecorations}, macOS={x:Null}}" /> - + @@ -133,12 +133,12 @@ - - - + + + - + - + diff --git a/WheelWizard/Views/App.axaml.cs b/WheelWizard/Views/App.axaml.cs index 06eadde27..b4d3a7ca4 100644 --- a/WheelWizard/Views/App.axaml.cs +++ b/WheelWizard/Views/App.axaml.cs @@ -7,7 +7,8 @@ using Serilog; using WheelWizard.ApplicationLifecycle; using WheelWizard.Views.Behaviors; -using WheelWizard.Views.Startup; +using WheelWizard.Views.Shell; +using WheelWizard.Views.Shell.Startup; namespace WheelWizard.Views; diff --git a/WheelWizard/Views/Components/PopupListButton.axaml b/WheelWizard/Views/Components/PopupListButton.axaml index de9accfdd..139a54572 100644 --- a/WheelWizard/Views/Components/PopupListButton.axaml +++ b/WheelWizard/Views/Components/PopupListButton.axaml @@ -1,22 +1,23 @@ + xmlns:components="clr-namespace:WheelWizard.Views.Components" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" + xmlns:shellViews="clr-namespace:WheelWizard.Views.Shell.Views"> + Type="{x:Type shellViews:HomePage}"/> + Type="{x:Type shellViews:HomePage}"/> + Type="{x:Type shellViews:HomePage}"/> + Type="{x:Type shellViews:HomePage}"/> - + Type="{x:Type rooms:RoomsPage}"/> + @@ -31,11 +32,11 @@ - + - + - + - + - + - \ No newline at end of file + \ No newline at end of file diff --git a/WheelWizard/Views/Popups/Generic/MessageBoxWindow.axaml.cs b/WheelWizard/Views/Dialogs/MessageBoxWindow.axaml.cs similarity index 94% rename from WheelWizard/Views/Popups/Generic/MessageBoxWindow.axaml.cs rename to WheelWizard/Views/Dialogs/MessageBoxWindow.axaml.cs index eac5bb27c..03ae3e25d 100644 --- a/WheelWizard/Views/Popups/Generic/MessageBoxWindow.axaml.cs +++ b/WheelWizard/Views/Dialogs/MessageBoxWindow.axaml.cs @@ -1,8 +1,10 @@ using Avalonia.Interactivity; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Components; +using WheelWizard.Views.Dialogs.Base; +using WheelWizard.Views.Shell; using Button = WheelWizard.Views.Components.Button; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class MessageBoxWindow : PopupContent { diff --git a/WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml b/WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml similarity index 93% rename from WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml rename to WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml index ce39fe888..801a43c89 100644 --- a/WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml +++ b/WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml @@ -1,11 +1,11 @@ - + x:Class="WheelWizard.Views.Dialogs.MiiRenderingSetupPopup" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base"> @@ -72,4 +72,4 @@ Click="DownloadButton_OnClick" /> - + diff --git a/WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml.cs b/WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml.cs similarity index 98% rename from WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml.cs rename to WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml.cs index 72e7f05f3..14566cbcb 100644 --- a/WheelWizard/Views/Popups/Generic/MiiRenderingSetupPopup.axaml.cs +++ b/WheelWizard/Views/Dialogs/MiiRenderingSetupPopup.axaml.cs @@ -1,9 +1,9 @@ using Avalonia.Interactivity; using Microsoft.Extensions.Logging; using WheelWizard.MiiRendering.Services; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class MiiRenderingSetupPopup : PopupContent { diff --git a/WheelWizard/Views/Popups/Generic/OptionsWindow.axaml b/WheelWizard/Views/Dialogs/OptionsWindow.axaml similarity index 75% rename from WheelWizard/Views/Popups/Generic/OptionsWindow.axaml rename to WheelWizard/Views/Dialogs/OptionsWindow.axaml index cb4e1456f..b5b6a5402 100644 --- a/WheelWizard/Views/Popups/Generic/OptionsWindow.axaml +++ b/WheelWizard/Views/Dialogs/OptionsWindow.axaml @@ -1,10 +1,10 @@ - + x:Class="WheelWizard.Views.Dialogs.OptionsWindow" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base"> @@ -13,4 +13,4 @@ - + diff --git a/WheelWizard/Views/Popups/Generic/OptionsWindow.axaml.cs b/WheelWizard/Views/Dialogs/OptionsWindow.axaml.cs similarity index 96% rename from WheelWizard/Views/Popups/Generic/OptionsWindow.axaml.cs rename to WheelWizard/Views/Dialogs/OptionsWindow.axaml.cs index 3a5d41e60..818f54ae5 100644 --- a/WheelWizard/Views/Popups/Generic/OptionsWindow.axaml.cs +++ b/WheelWizard/Views/Dialogs/OptionsWindow.axaml.cs @@ -3,9 +3,9 @@ using Avalonia.Media; using Avalonia.Threading; using WheelWizard.Views.Components; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class OptionsWindow : PopupContent { diff --git a/WheelWizard/Views/Popups/PopupFactory.cs b/WheelWizard/Views/Dialogs/PopupFactory.cs similarity index 85% rename from WheelWizard/Views/Popups/PopupFactory.cs rename to WheelWizard/Views/Dialogs/PopupFactory.cs index 8414bc18c..221e7ea7b 100644 --- a/WheelWizard/Views/Popups/PopupFactory.cs +++ b/WheelWizard/Views/Dialogs/PopupFactory.cs @@ -1,6 +1,6 @@ -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; -namespace WheelWizard.Views.Popups; +namespace WheelWizard.Views.Dialogs; public interface IPopupFactory { diff --git a/WheelWizard/Views/Popups/Generic/ProgressWindow.axaml b/WheelWizard/Views/Dialogs/ProgressWindow.axaml similarity index 91% rename from WheelWizard/Views/Popups/Generic/ProgressWindow.axaml rename to WheelWizard/Views/Dialogs/ProgressWindow.axaml index 4fc553d50..48754c645 100644 --- a/WheelWizard/Views/Popups/Generic/ProgressWindow.axaml +++ b/WheelWizard/Views/Dialogs/ProgressWindow.axaml @@ -1,14 +1,13 @@ - + mc:Ignorable="d" d:DesignHeight="170" d:DesignWidth="400" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base"> @@ -51,4 +50,4 @@ Click="CancelButton_OnClick" /> - + diff --git a/WheelWizard/Views/Popups/Generic/ProgressWindow.axaml.cs b/WheelWizard/Views/Dialogs/ProgressWindow.axaml.cs similarity index 98% rename from WheelWizard/Views/Popups/Generic/ProgressWindow.axaml.cs rename to WheelWizard/Views/Dialogs/ProgressWindow.axaml.cs index bb7f394bb..a1a4255ff 100644 --- a/WheelWizard/Views/Popups/Generic/ProgressWindow.axaml.cs +++ b/WheelWizard/Views/Dialogs/ProgressWindow.axaml.cs @@ -2,9 +2,9 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class ProgressWindow : PopupContent { diff --git a/WheelWizard/Views/Popups/Generic/TextInputWindow.axaml b/WheelWizard/Views/Dialogs/TextInputWindow.axaml similarity index 93% rename from WheelWizard/Views/Popups/Generic/TextInputWindow.axaml rename to WheelWizard/Views/Dialogs/TextInputWindow.axaml index 32f8560d4..600cb5066 100644 --- a/WheelWizard/Views/Popups/Generic/TextInputWindow.axaml +++ b/WheelWizard/Views/Dialogs/TextInputWindow.axaml @@ -1,4 +1,4 @@ - + x:Class="WheelWizard.Views.Dialogs.TextInputWindow" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base"> @@ -52,4 +52,4 @@ Click="SubmitButton_Click" Margin="5,0,0,0" /> - + diff --git a/WheelWizard/Views/Popups/Generic/TextInputWindow.axaml.cs b/WheelWizard/Views/Dialogs/TextInputWindow.axaml.cs similarity index 97% rename from WheelWizard/Views/Popups/Generic/TextInputWindow.axaml.cs rename to WheelWizard/Views/Dialogs/TextInputWindow.axaml.cs index 12a99ea5f..12d407f4a 100644 --- a/WheelWizard/Views/Popups/Generic/TextInputWindow.axaml.cs +++ b/WheelWizard/Views/Dialogs/TextInputWindow.axaml.cs @@ -1,10 +1,11 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Layout; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Components; +using WheelWizard.Views.Dialogs.Base; using Button = WheelWizard.Views.Components.Button; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class TextInputWindow : PopupContent { diff --git a/WheelWizard/Views/Popups/Generic/YesNoWindow.axaml b/WheelWizard/Views/Dialogs/YesNoWindow.axaml similarity index 89% rename from WheelWizard/Views/Popups/Generic/YesNoWindow.axaml rename to WheelWizard/Views/Dialogs/YesNoWindow.axaml index ba5af6b58..668f5b003 100644 --- a/WheelWizard/Views/Popups/Generic/YesNoWindow.axaml +++ b/WheelWizard/Views/Dialogs/YesNoWindow.axaml @@ -1,12 +1,12 @@ - + x:Class="WheelWizard.Views.Dialogs.YesNoWindow" + xmlns:dialogBase="clr-namespace:WheelWizard.Views.Dialogs.Base"> @@ -31,4 +31,4 @@ Click="yesButton_Click" Margin="5,0,0,0"/> - + diff --git a/WheelWizard/Views/Popups/Generic/YesNoWindow.axaml.cs b/WheelWizard/Views/Dialogs/YesNoWindow.axaml.cs similarity index 96% rename from WheelWizard/Views/Popups/Generic/YesNoWindow.axaml.cs rename to WheelWizard/Views/Dialogs/YesNoWindow.axaml.cs index 04460b9ba..20084f95b 100644 --- a/WheelWizard/Views/Popups/Generic/YesNoWindow.axaml.cs +++ b/WheelWizard/Views/Dialogs/YesNoWindow.axaml.cs @@ -2,9 +2,9 @@ using Avalonia.Layout; using Avalonia.Threading; using WheelWizard.Views.Components; -using WheelWizard.Views.Popups.Base; +using WheelWizard.Views.Dialogs.Base; -namespace WheelWizard.Views.Popups.Generic; +namespace WheelWizard.Views.Dialogs; public partial class YesNoWindow : PopupContent { diff --git a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml index 26f04b909..854c2dcab 100644 --- a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml +++ b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml @@ -1,31 +1,32 @@ + xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" + xmlns:shellViews="clr-namespace:WheelWizard.Views.Shell.Views"> + PageType="{x:Type shellViews:HomePage}"/> + PageType="{x:Type shellViews:HomePage}"/> + PageType="{x:Type shellViews:HomePage}"/> + PageType="{x:Type rooms:RoomsPage}"/> diff --git a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs index f8bdf936b..debbbd110 100644 --- a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs +++ b/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs @@ -3,7 +3,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Media; -using WheelWizard.Views.Pages; +using WheelWizard.Views.Shell.Views; namespace WheelWizard.Views.Patterns; diff --git a/WheelWizard/Views/BaseWindow.cs b/WheelWizard/Views/Shell/BaseWindow.cs similarity index 99% rename from WheelWizard/Views/BaseWindow.cs rename to WheelWizard/Views/Shell/BaseWindow.cs index 7a489b092..a5d7dc2f7 100644 --- a/WheelWizard/Views/BaseWindow.cs +++ b/WheelWizard/Views/Shell/BaseWindow.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public abstract class BaseWindow : Window { diff --git a/WheelWizard/Views/Layout.MacProfiles.cs b/WheelWizard/Views/Shell/Layout.MacProfiles.cs similarity index 96% rename from WheelWizard/Views/Layout.MacProfiles.cs rename to WheelWizard/Views/Shell/Layout.MacProfiles.cs index b5c4ca35b..e125a9b17 100644 --- a/WheelWizard/Views/Layout.MacProfiles.cs +++ b/WheelWizard/Views/Shell/Layout.MacProfiles.cs @@ -1,9 +1,9 @@ using Avalonia.Controls; using WheelWizard.Models.Enums; -using WheelWizard.Views.Pages; +using WheelWizard.WheelWizardData.Views; using WheelWizard.WiiManagement.GameLicense; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public partial class Layout { diff --git a/WheelWizard/Views/Layout.Sidebar.cs b/WheelWizard/Views/Shell/Layout.Sidebar.cs similarity index 99% rename from WheelWizard/Views/Layout.Sidebar.cs rename to WheelWizard/Views/Shell/Layout.Sidebar.cs index ff7ef97c1..67d7320de 100644 --- a/WheelWizard/Views/Layout.Sidebar.cs +++ b/WheelWizard/Views/Shell/Layout.Sidebar.cs @@ -9,7 +9,7 @@ using Avalonia.VisualTree; using WheelWizard.Views.Patterns; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public partial class Layout { diff --git a/WheelWizard/Views/Layout.axaml b/WheelWizard/Views/Shell/Layout.axaml similarity index 95% rename from WheelWizard/Views/Layout.axaml rename to WheelWizard/Views/Shell/Layout.axaml index c1ee3a61a..3cd47cdaa 100644 --- a/WheelWizard/Views/Layout.axaml +++ b/WheelWizard/Views/Shell/Layout.axaml @@ -3,9 +3,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="876" d:DesignWidth="656" - x:Class="WheelWizard.Views.Layout" + x:Class="WheelWizard.Views.Shell.Layout" xmlns:loc="clr-namespace:WheelWizard.Localization" - xmlns:pages="clr-namespace:WheelWizard.Views.Pages" xmlns:components="clr-namespace:WheelWizard.Views.Components" xmlns:patterns="clr-namespace:WheelWizard.Views.Patterns" xmlns:miiVars="using:WheelWizard.MiiImages.Domain" @@ -13,7 +12,14 @@ WindowDecorations="Full" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaTitleBarHeightHint="30" - CanResize='False' Background="Transparent"> + CanResize='False' Background="Transparent" + xmlns:mods="clr-namespace:WheelWizard.Mods.Views" + xmlns:miis="clr-namespace:WheelWizard.WiiManagement.MiiManagement.Views" + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" + xmlns:community="clr-namespace:WheelWizard.WheelWizardData.Views" + xmlns:shellViews="clr-namespace:WheelWizard.Views.Shell.Views" + xmlns:design="clr-namespace:WheelWizard.Views.DesignTime" + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views"> - - diff --git a/WheelWizard/Views/Layout.axaml.cs b/WheelWizard/Views/Shell/Layout.axaml.cs similarity index 98% rename from WheelWizard/Views/Layout.axaml.cs rename to WheelWizard/Views/Shell/Layout.axaml.cs index c67c89664..e6bf4da9b 100644 --- a/WheelWizard/Views/Layout.axaml.cs +++ b/WheelWizard/Views/Shell/Layout.axaml.cs @@ -10,22 +10,25 @@ using WheelWizard.AutoUpdating; using WheelWizard.Branding; using WheelWizard.Localization; +using WheelWizard.MiiImages.Views; using WheelWizard.Mods; using WheelWizard.RrRooms; +using WheelWizard.RrRooms.Views; using WheelWizard.Settings; using WheelWizard.Settings.Types; +using WheelWizard.Settings.Views; using WheelWizard.Shared.MessageTranslations; using WheelWizard.Shared.Polling; -using WheelWizard.Views.Navigation; -using WheelWizard.Views.Pages; -using WheelWizard.Views.Pages.Settings; +using WheelWizard.Views.Dialogs; using WheelWizard.Views.Patterns; -using WheelWizard.Views.Popups.Generic; +using WheelWizard.Views.Shell.Navigation; +using WheelWizard.Views.Shell.Views; using WheelWizard.WheelWizardData; using WheelWizard.WheelWizardData.Domain; +using WheelWizard.WheelWizardData.Views; using WheelWizard.WiiManagement.GameLicense; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public partial class Layout : BaseWindow, IPollingListener { diff --git a/WheelWizard/Views/MacWindowMenu.cs b/WheelWizard/Views/Shell/MacWindowMenu.cs similarity index 99% rename from WheelWizard/Views/MacWindowMenu.cs rename to WheelWizard/Views/Shell/MacWindowMenu.cs index c959423c7..4f856f5c0 100644 --- a/WheelWizard/Views/MacWindowMenu.cs +++ b/WheelWizard/Views/Shell/MacWindowMenu.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; /// Registers an AppKit Window menu so macOS supplies its standard window actions and icons. internal static class MacWindowMenu diff --git a/WheelWizard/Views/MainWindowService.cs b/WheelWizard/Views/Shell/MainWindowService.cs similarity index 97% rename from WheelWizard/Views/MainWindowService.cs rename to WheelWizard/Views/Shell/MainWindowService.cs index 6e16e31ab..d97f0c0bf 100644 --- a/WheelWizard/Views/MainWindowService.cs +++ b/WheelWizard/Views/Shell/MainWindowService.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform; +using WheelWizard.Views.Shell.Startup; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public interface IMainWindowService { diff --git a/WheelWizard/Views/Navigation/NavigationService.cs b/WheelWizard/Views/Shell/Navigation/NavigationService.cs similarity index 95% rename from WheelWizard/Views/Navigation/NavigationService.cs rename to WheelWizard/Views/Shell/Navigation/NavigationService.cs index 4541a713f..1e64650b7 100644 --- a/WheelWizard/Views/Navigation/NavigationService.cs +++ b/WheelWizard/Views/Shell/Navigation/NavigationService.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace WheelWizard.Views.Navigation; +namespace WheelWizard.Views.Shell.Navigation; public interface INavigationService { diff --git a/WheelWizard/Views/Navigation/PageFactory.cs b/WheelWizard/Views/Shell/Navigation/PageFactory.cs similarity index 94% rename from WheelWizard/Views/Navigation/PageFactory.cs rename to WheelWizard/Views/Shell/Navigation/PageFactory.cs index 59c82c938..c3dd6cdc8 100644 --- a/WheelWizard/Views/Navigation/PageFactory.cs +++ b/WheelWizard/Views/Shell/Navigation/PageFactory.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace WheelWizard.Views.Navigation; +namespace WheelWizard.Views.Shell.Navigation; public interface IPageFactory { diff --git a/WheelWizard/Views/Pages/HomePresentation.cs b/WheelWizard/Views/Shell/Presentation/HomePresentation.cs similarity index 75% rename from WheelWizard/Views/Pages/HomePresentation.cs rename to WheelWizard/Views/Shell/Presentation/HomePresentation.cs index 1380a2968..30b9ffd0c 100644 --- a/WheelWizard/Views/Pages/HomePresentation.cs +++ b/WheelWizard/Views/Shell/Presentation/HomePresentation.cs @@ -1,8 +1,11 @@ +using WheelWizard.Settings.Views; using WheelWizard.Shared.MessageTranslations; -using WheelWizard.Views.Navigation; -using WheelWizard.Views.Popups.Generic; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; +using WheelWizard.Views.Shell.Navigation; +using WheelWizard.Views.Shell.ViewModels; -namespace WheelWizard.Views.Pages; +namespace WheelWizard.Views.Shell.Presentation; public sealed class HomePresentation(INavigationService navigation) : IHomePresentation { diff --git a/WheelWizard/Views/PresentationExtensions.cs b/WheelWizard/Views/Shell/PresentationExtensions.cs similarity index 77% rename from WheelWizard/Views/PresentationExtensions.cs rename to WheelWizard/Views/Shell/PresentationExtensions.cs index 6789ceade..9605ed88d 100644 --- a/WheelWizard/Views/PresentationExtensions.cs +++ b/WheelWizard/Views/Shell/PresentationExtensions.cs @@ -1,28 +1,33 @@ using WheelWizard.AutoUpdating; +using WheelWizard.AutoUpdating.Presentation; using WheelWizard.CustomDistributions; +using WheelWizard.CustomDistributions.Presentation; using WheelWizard.GameBanana; using WheelWizard.GameBanana.InstallRequests; using WheelWizard.Launching; +using WheelWizard.Launching.Presentation; +using WheelWizard.MiiImages.Views; using WheelWizard.Mods; +using WheelWizard.Mods.Presentation; +using WheelWizard.Mods.ViewModels; +using WheelWizard.Mods.Views.Dialogs; using WheelWizard.Recomp; +using WheelWizard.Recomp.Presentation; +using WheelWizard.Shared.Desktop.Polling; +using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.Polling; using WheelWizard.Shared.Services; -using WheelWizard.Views.Diagnostics; -using WheelWizard.Views.Distributions; -using WheelWizard.Views.Launching; -using WheelWizard.Views.ModManagement; -using WheelWizard.Views.Navigation; -using WheelWizard.Views.Pages; -using WheelWizard.Views.Patterns; -using WheelWizard.Views.Polling; -using WheelWizard.Views.Popups; -using WheelWizard.Views.Popups.ModManagement; -using WheelWizard.Views.Recomp; -using WheelWizard.Views.Startup; -using WheelWizard.Views.Storage; -using WheelWizard.Views.Updating; +using WheelWizard.Views.DesignTime.Diagnostics; +using WheelWizard.Views.Dialogs; +using WheelWizard.Views.Shell; +using WheelWizard.Views.Shell.Navigation; +using WheelWizard.Views.Shell.Presentation; +using WheelWizard.Views.Shell.Startup; +using WheelWizard.Views.Shell.ViewModels; +using WheelWizard.WheelWizardData.ViewModels; +using WheelWizard.WheelWizardData.Views; -namespace WheelWizard.Views; +namespace WheelWizard.Views.Shell; public static class PresentationExtensions { diff --git a/WheelWizard/Views/Startup/DesktopStartup.cs b/WheelWizard/Views/Shell/Startup/DesktopStartup.cs similarity index 95% rename from WheelWizard/Views/Startup/DesktopStartup.cs rename to WheelWizard/Views/Shell/Startup/DesktopStartup.cs index a04772ec4..71d9c2904 100644 --- a/WheelWizard/Views/Startup/DesktopStartup.cs +++ b/WheelWizard/Views/Shell/Startup/DesktopStartup.cs @@ -4,11 +4,10 @@ using WheelWizard.ApplicationLifecycle; using WheelWizard.AutoUpdating; using WheelWizard.MiiRendering.Services; -using WheelWizard.Views.Popups; -using WheelWizard.Views.Popups.Generic; +using WheelWizard.Views.Dialogs; using WheelWizard.WiiManagement.GameLicense; -namespace WheelWizard.Views.Startup; +namespace WheelWizard.Views.Shell.Startup; public interface IDesktopStartup { diff --git a/WheelWizard/Views/Startup/SplashWindow.axaml b/WheelWizard/Views/Shell/Startup/SplashWindow.axaml similarity index 93% rename from WheelWizard/Views/Startup/SplashWindow.axaml rename to WheelWizard/Views/Shell/Startup/SplashWindow.axaml index 560457b51..3cb5a7f83 100644 --- a/WheelWizard/Views/Startup/SplashWindow.axaml +++ b/WheelWizard/Views/Shell/Startup/SplashWindow.axaml @@ -1,6 +1,6 @@ + x:DataType="shellViewModels:HomeViewModel" + x:Class="WheelWizard.Views.Shell.Views.HomePage" + xmlns:shellViewModels="clr-namespace:WheelWizard.Views.Shell.ViewModels"> - diff --git a/WheelWizard/Features/Mods/Views/ModsPage.axaml b/WheelWizard/Features/Mods/Views/ModsPage.axaml index 16a7c2628..84c9efc38 100644 --- a/WheelWizard/Features/Mods/Views/ModsPage.axaml +++ b/WheelWizard/Features/Mods/Views/ModsPage.axaml @@ -45,7 +45,7 @@ - @@ -59,18 +59,17 @@ - - + AutomationProperties.Name="{loc:T action.browse}"> + + - - + + - - + AutomationProperties.Name="{loc:T action.browse}"> + + - - + + @@ -223,7 +221,7 @@ - diff --git a/WheelWizard/Features/Mods/Views/ModsPage.axaml.cs b/WheelWizard/Features/Mods/Views/ModsPage.axaml.cs index 30facc64e..f445d4698 100644 --- a/WheelWizard/Features/Mods/Views/ModsPage.axaml.cs +++ b/WheelWizard/Features/Mods/Views/ModsPage.axaml.cs @@ -15,6 +15,7 @@ using WheelWizard.Settings; using WheelWizard.Shared.Desktop.Storage; using WheelWizard.Shared.MessageTranslations; +using WheelWizard.Views.Components; using WheelWizard.Views.Dialogs; using WheelWizard.Views.Shell; @@ -156,12 +157,6 @@ private void UpdateEnableAllCheckboxState() EnableAllCheckbox.IsChecked = !ModManager.Mods.Select(mod => mod.IsEnabled).Contains(false); } - private void ModActionsMenu_Click(object? sender, RoutedEventArgs e) - { - if (sender is Control { ContextMenu: { } menu } button) - menu.Open(button); - } - private void BrowseMod_Click(object sender, RoutedEventArgs e) { var modPopup = Popups.Create(); @@ -383,7 +378,7 @@ private void PriorityText_OnTextChanged(object? sender, TextChangedEventArgs e) if (mod == null || e.Source is not TextBox textBox) return; - // We intentionally don't use the FeedbackTextBox here since that component is a bit to big for this use case. + // We intentionally don't use the TextField here since that component is a bit to big for this use case. if (int.TryParse(textBox.Text, out _)) textBox.Classes.Remove("error"); else if (!textBox.Classes.Contains("error")) diff --git a/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml b/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml index 0debcf752..df80b6226 100644 --- a/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml +++ b/WheelWizard/Features/RrRooms/Views/PlayerListItem.axaml @@ -5,7 +5,8 @@ xmlns:components="using:WheelWizard.Views.Components" xmlns:miiVars="using:WheelWizard.MiiImages.Domain" xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" - xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views"> + xmlns:miiImages="clr-namespace:WheelWizard.MiiImages.Views" + xmlns:communityControls="clr-namespace:WheelWizard.WheelWizardData.Views"> - + diff --git a/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml index 185a11bc2..f38ef6c0a 100644 --- a/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml +++ b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml @@ -31,7 +31,7 @@ - - + @@ -72,7 +72,7 @@ - + diff --git a/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml.cs b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml.cs index 4c8b66df8..f341fa92c 100644 --- a/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml.cs +++ b/WheelWizard/Features/RrRooms/Views/RoomDetailsPage.axaml.cs @@ -116,7 +116,7 @@ public void OnUpdate(ObservablePollingService sender) } } - private void GoBackClick(object? sender, EventArgs eventArgs) => Navigation.NavigateTo(); + private void GoBackClick(object? sender, RoutedEventArgs eventArgs) => Navigation.NavigateTo(); private void CopyFriendCode_OnClick(object sender, RoutedEventArgs e) { diff --git a/WheelWizard/Features/RrRooms/Views/RoomsPage.axaml b/WheelWizard/Features/RrRooms/Views/RoomsPage.axaml index e22202c3b..941f3cdad 100644 --- a/WheelWizard/Features/RrRooms/Views/RoomsPage.axaml +++ b/WheelWizard/Features/RrRooms/Views/RoomsPage.axaml @@ -7,7 +7,8 @@ xmlns:components="clr-namespace:WheelWizard.Views.Components" x:Class="WheelWizard.RrRooms.Views.RoomsPage" x:DataType="rooms:RoomsPage" - xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views"> + xmlns:rooms="clr-namespace:WheelWizard.RrRooms.Views" + xmlns:communityControls="clr-namespace:WheelWizard.WheelWizardData.Views"> @@ -50,7 +51,7 @@ - - @@ -125,7 +126,7 @@ - + diff --git a/WheelWizard/Features/Settings/Views/AppInfo.axaml b/WheelWizard/Features/Settings/Views/AppInfo.axaml index 29c9ebc0f..3896c77cb 100644 --- a/WheelWizard/Features/Settings/Views/AppInfo.axaml +++ b/WheelWizard/Features/Settings/Views/AppInfo.axaml @@ -4,14 +4,15 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="444" d:DesignHeight="716" xmlns:components="clr-namespace:WheelWizard.Views.Components" - x:Class="WheelWizard.Settings.Views.AppInfo"> + x:Class="WheelWizard.Settings.Views.AppInfo" + xmlns:communityControls="clr-namespace:WheelWizard.WheelWizardData.Views"> - @@ -27,7 +28,7 @@ - @@ -42,7 +43,7 @@ - @@ -71,7 +72,7 @@ - - - - - + xmlns:controls="using:WheelWizard.Views.Components" + xmlns:communityControls="clr-namespace:WheelWizard.WheelWizardData.Views"> - + - + - + - + - + - + - + - + - + - + - + @@ -53,7 +54,7 @@ - LOOK OUT WITH USING UNIFORM STRETCH, IT WORKS BAD, DON'T TOUCH THE AWARDS, they are made with uniform stretch lol, and they work but they can fall apart easily --> - - - - - - - - - - - - - - - - - - + + @@ -48,26 +48,26 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/EditorStartPage.axaml.cs b/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/EditorStartPage.axaml.cs index 97cbd23b2..73181c424 100644 --- a/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/EditorStartPage.axaml.cs +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/EditorStartPage.axaml.cs @@ -21,10 +21,10 @@ public EditorStartPage(IRandomSystem random, MiiEditorWindow ew) private void PopupPageButton_OnClick(object? sender, RoutedEventArgs e) { - if (sender is not PopupListButton button) + if (sender is not ListActionButton button) return; - if (button.Type is not { } pageType) + if (button.CommandParameter is not Type pageType) return; Editor.SetEditorPage(pageType); @@ -48,7 +48,7 @@ private void RandomizeMii_OnClick(object? sender, RoutedEventArgs e) private void SaveButton_OnClick(object? sender, RoutedEventArgs e) => Editor.SignalSaveMii(); - private void FavoriteButton_OnClick(object? sender, EventArgs e) + private void FavoriteButton_OnClick(object? sender, RoutedEventArgs e) { Editor.Mii.IsFavorite = !Editor.Mii.IsFavorite; diff --git a/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/MiiEditorBaseControl.cs b/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/MiiEditorBaseControl.cs index 4fd540d35..598e14ae1 100644 --- a/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/MiiEditorBaseControl.cs +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/Dialogs/MiiEditor/MiiEditorBaseControl.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; using Avalonia.Media; using WheelWizard.Views.Components; using WheelWizard.Views.Shell; @@ -13,7 +14,7 @@ public class MiiEditorBaseControl : UserControl protected MiiEditorBaseControl(MiiEditorWindow editor) => Editor = editor; - protected void BackButton_OnClick(object? sender, EventArgs e) + protected void BackButton_OnClick(object? sender, RoutedEventArgs e) { BeforeBack(); Editor.SetEditorPage(typeof(EditorStartPage)); diff --git a/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml index fb09f2d11..793a7aa0e 100644 --- a/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml +++ b/WheelWizard/Features/WiiManagement/MiiManagement/Views/MiiListPage.axaml @@ -22,7 +22,7 @@ Spacing="8"> - diff --git a/WheelWizard/Views/App.axaml b/WheelWizard/Views/App.axaml index c5ad9ef41..43f3844e3 100644 --- a/WheelWizard/Views/App.axaml +++ b/WheelWizard/Views/App.axaml @@ -121,24 +121,25 @@ - + - - + + + - - - + + + - + - + diff --git a/WheelWizard/Views/Components/AspectGrid.axaml b/WheelWizard/Views/Components/AspectGrid.axaml deleted file mode 100644 index c5354c7c8..000000000 --- a/WheelWizard/Views/Components/AspectGrid.axaml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/WheelWizard/Views/Components/AspectGrid.axaml.cs b/WheelWizard/Views/Components/AspectGrid.axaml.cs index da51dc21a..7005bbabe 100644 --- a/WheelWizard/Views/Components/AspectGrid.axaml.cs +++ b/WheelWizard/Views/Components/AspectGrid.axaml.cs @@ -3,62 +3,37 @@ namespace WheelWizard.Views.Components; -public partial class AspectGrid : Grid +public class AspectGrid : Grid { - #region Properties - - /// - /// Defines the AspectRatio property. - /// public static readonly StyledProperty AspectRatioProperty = AvaloniaProperty.Register( nameof(AspectRatio), - 1.0 + 1, + validate: value => double.IsFinite(value) && value > 0 ); - /// - /// Gets or sets the aspect ratio. Default is 1.0 (square). - /// + static AspectGrid() => AffectsMeasure(AspectRatioProperty); + public double AspectRatio { get => GetValue(AspectRatioProperty); set => SetValue(AspectRatioProperty, value); } - /// - /// Defines the UseMaxDimension property. - /// - public static readonly StyledProperty UseMaxDimensionProperty = AvaloniaProperty.Register( - nameof(UseMaxDimension) - ); - - /// - /// Gets or sets whether to use the maximum dimension for sizing. - /// If true, uses the larger of width/height. If false, uses the smaller. - /// - public bool UseMaxDimension + protected override Size MeasureOverride(Size availableSize) { - get => GetValue(UseMaxDimensionProperty); - set => SetValue(UseMaxDimensionProperty, value); + var fitted = Fit(availableSize); + var desired = base.MeasureOverride(fitted); + if (double.IsFinite(fitted.Width) && double.IsFinite(fitted.Height)) + return fitted; + var height = Math.Max(desired.Width / AspectRatio, desired.Height); + return new Size(height * AspectRatio, height); } - #endregion + protected override Size ArrangeOverride(Size finalSize) => base.ArrangeOverride(Fit(finalSize)); - public AspectGrid() + private Size Fit(Size size) { - InitializeComponent(); - } - - private void OnSizeChanged(object sender, SizeChangedEventArgs e) - { - if (e.NewSize.Width <= 0 || e.NewSize.Height <= 0) - return; - - var heightSize = UseMaxDimension - ? Math.Max(e.NewSize.Width / AspectRatio, e.NewSize.Height) - : Math.Min(e.NewSize.Width / AspectRatio, e.NewSize.Height); - - // Set both width and height to the larger dimension to create a square - Width = heightSize * AspectRatio; - Height = heightSize; + var height = Math.Min(size.Width / AspectRatio, size.Height); + return new Size(height * AspectRatio, height); } } diff --git a/WheelWizard/Views/Components/Button.axaml b/WheelWizard/Views/Components/Button.axaml index 9e911d22d..732d692da 100644 --- a/WheelWizard/Views/Components/Button.axaml +++ b/WheelWizard/Views/Components/Button.axaml @@ -1,4 +1,4 @@ - @@ -6,10 +6,10 @@ - - + + - + - - - - - - - - - - - - - - - - - - - diff --git a/WheelWizard/Views/Components/Button.axaml.cs b/WheelWizard/Views/Components/Button.axaml.cs index 2d55134dd..f32206fc2 100644 --- a/WheelWizard/Views/Components/Button.axaml.cs +++ b/WheelWizard/Views/Components/Button.axaml.cs @@ -1,108 +1,76 @@ -using Avalonia; +using Avalonia; using Avalonia.Media; namespace WheelWizard.Views.Components; -public class Button : Avalonia.Controls.Button // Change to TemplatedControl +public enum ButtonVariant { - public static readonly StyledProperty VariantProperty = AvaloniaProperty.Register( - nameof(Variant), - ButtonsVariantType.Default - ); - - public static readonly StyledProperty ButtonSizeProperty = AvaloniaProperty.Register( - nameof(ButtonSize), - ButtonsSizeType.Regular - ); - - public static readonly StyledProperty IconDataProperty = AvaloniaProperty.Register(nameof(IconData)); - - public static readonly StyledProperty IconSizeProperty = AvaloniaProperty.Register(nameof(IconSize), 20.0); - - public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); - - public enum ButtonsVariantType - { - Primary, - Warning, - Default, - Danger, - UglyLight, - } + Default, + Primary, + Warning, + Danger, + Light, +} - public enum ButtonsSizeType - { - Regular, - Compact, - } +public enum ButtonSize +{ + Regular, + Compact, +} - // Constructor - public Button() - { - // No need for InitializeComponent() in code-behind for TemplatedControl - UpdateStyleClasses(Variant); - UpdateSizeClass(ButtonSize); - } +public class Button : Avalonia.Controls.Button +{ + public static readonly StyledProperty VariantProperty = AvaloniaProperty.Register( + nameof(Variant), + ButtonVariant.Default + ); - // Properties remain the same - public ButtonsVariantType Variant + public ButtonVariant Variant { get => GetValue(VariantProperty); set => SetValue(VariantProperty, value); } - public ButtonsSizeType ButtonSize + public static readonly StyledProperty SizeProperty = AvaloniaProperty.Register( + nameof(Size), + ButtonSize.Regular + ); + + public ButtonSize Size { - get => GetValue(ButtonSizeProperty); - set => SetValue(ButtonSizeProperty, value); + get => GetValue(SizeProperty); + set => SetValue(SizeProperty, value); } - public Geometry IconData + 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 string Text + public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); + + public string? Text { get => GetValue(TextProperty); set => SetValue(TextProperty, value); } - // UpdateStyleClasses remains the same - private void UpdateStyleClasses(ButtonsVariantType variant) - { - var types = Enum.GetValues(); - foreach (var enumType in types) - { - Classes.Remove(enumType.ToString()); - } - Classes.Add(variant.ToString()); - } + public static readonly StyledProperty IsIconLeftProperty = AvaloniaProperty.Register(nameof(IsIconLeft), true); - private void UpdateSizeClass(ButtonsSizeType size) + public bool IsIconLeft { - var sizes = Enum.GetValues(); - foreach (var enumSize in sizes) - { - Classes.Remove(enumSize.ToString()); - } - Classes.Add(size.ToString()); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == VariantProperty) - UpdateStyleClasses(change.GetNewValue()); - else if (change.Property == ButtonSizeProperty) - UpdateSizeClass(change.GetNewValue()); + get => GetValue(IsIconLeftProperty); + set => SetValue(IsIconLeftProperty, value); } } diff --git a/WheelWizard/Views/Components/FormFieldLabel.axaml b/WheelWizard/Views/Components/FormFieldLabel.axaml index 11afae4fd..eaff1055a 100644 --- a/WheelWizard/Views/Components/FormFieldLabel.axaml +++ b/WheelWizard/Views/Components/FormFieldLabel.axaml @@ -24,7 +24,7 @@ diff --git a/WheelWizard/Views/Components/FormFieldLabel.axaml.cs b/WheelWizard/Views/Components/FormFieldLabel.axaml.cs index 316d4d4ad..e53a7dc78 100644 --- a/WheelWizard/Views/Components/FormFieldLabel.axaml.cs +++ b/WheelWizard/Views/Components/FormFieldLabel.axaml.cs @@ -4,7 +4,7 @@ namespace WheelWizard.Views.Components; -public class FormFieldLabel : UserControl +public class FormFieldLabel : TemplatedControl { public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); @@ -32,14 +32,4 @@ public PlacementMode TipPlacement get => GetValue(TipPlacementProperty); set => SetValue(TipPlacementProperty, value); } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property != TipPlacementProperty) - return; - - ToolTip.SetPlacement(this, TipPlacement); - } } diff --git a/WheelWizard/Views/Components/HoverGlow.cs b/WheelWizard/Views/Components/HoverGlow.cs new file mode 100644 index 000000000..e29868ca7 --- /dev/null +++ b/WheelWizard/Views/Components/HoverGlow.cs @@ -0,0 +1,50 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.VisualTree; + +namespace WheelWizard.Views.Components; + +// Pointer-following decoration; render transforms never participate in layout. +public class HoverGlow : Border +{ + private readonly TranslateTransform _position = new(); + private Control? _owner; + + public HoverGlow() + { + IsHitTestVisible = false; + RenderTransform = _position; + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _owner = TemplatedParent as Control; + if (_owner is null) + return; + _owner.PointerMoved += UpdatePosition; + _owner.PointerEntered += UpdatePosition; + } + + private void UpdatePosition(object? sender, PointerEventArgs e) + { + if (this.GetVisualParent() is not { } parent) + return; + var position = e.GetPosition(parent); + _position.X = position.X - Width / 2; + _position.Y = position.Y - Height / 2; + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + if (_owner is not null) + { + _owner.PointerMoved -= UpdatePosition; + _owner.PointerEntered -= UpdatePosition; + _owner = null; + } + base.OnDetachedFromVisualTree(e); + } +} diff --git a/WheelWizard/Views/Components/IconLabel.axaml.cs b/WheelWizard/Views/Components/IconLabel.axaml.cs index c2bcd4198..1c7958faa 100644 --- a/WheelWizard/Views/Components/IconLabel.axaml.cs +++ b/WheelWizard/Views/Components/IconLabel.axaml.cs @@ -14,7 +14,7 @@ public Geometry IconData set => SetValue(IconDataProperty, value); } - public static readonly StyledProperty IconSizeProperty = AvaloniaProperty.Register(nameof(IconSize), 20.0); // Add a default value here + public static readonly StyledProperty IconSizeProperty = AvaloniaProperty.Register(nameof(IconSize), 20.0); public double IconSize { @@ -22,9 +22,9 @@ public double IconSize set => SetValue(IconSizeProperty, value); } - public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); + public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); - public object Text + public string? Text { get => GetValue(TextProperty); set => SetValue(TextProperty, value); diff --git a/WheelWizard/Views/Components/IconLabelButton.axaml.cs b/WheelWizard/Views/Components/IconLabelButton.axaml.cs deleted file mode 100644 index 95df85430..000000000 --- a/WheelWizard/Views/Components/IconLabelButton.axaml.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Avalonia; -using Avalonia.Input; -using Avalonia.Media; - -namespace WheelWizard.Views.Components; - -public class IconLabelButton : IconLabel -{ - public static readonly StyledProperty HoverForegroundProperty = AvaloniaProperty.Register( - nameof(HoverForeground) - ); - - public IBrush HoverForeground - { - get => GetValue(HoverForegroundProperty); - set => SetValue(HoverForegroundProperty, value); - } - - public event EventHandler? Click; - - protected override void OnPointerPressed(PointerPressedEventArgs e) - { - base.OnPointerPressed(e); - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - Click?.Invoke(this, EventArgs.Empty); - } -} diff --git a/WheelWizard/Views/Components/IconLabelButton.axaml b/WheelWizard/Views/Components/LinkButton.axaml similarity index 57% rename from WheelWizard/Views/Components/IconLabelButton.axaml rename to WheelWizard/Views/Components/LinkButton.axaml index 12aee26d3..09ba1417a 100644 --- a/WheelWizard/Views/Components/IconLabelButton.axaml +++ b/WheelWizard/Views/Components/LinkButton.axaml @@ -2,20 +2,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:components="clr-namespace:WheelWizard.Views.Components"> - + - @@ -33,4 +41,7 @@ - \ No newline at end of file + + diff --git a/WheelWizard/Views/Components/LinkButton.axaml.cs b/WheelWizard/Views/Components/LinkButton.axaml.cs new file mode 100644 index 000000000..4c0649da9 --- /dev/null +++ b/WheelWizard/Views/Components/LinkButton.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Media; + +namespace WheelWizard.Views.Components; + +public class LinkButton : Button +{ + public static readonly StyledProperty HoverForegroundProperty = AvaloniaProperty.Register( + nameof(HoverForeground) + ); + + public IBrush? HoverForeground + { + get => GetValue(HoverForegroundProperty); + set => SetValue(HoverForegroundProperty, value); + } + + public static readonly StyledProperty IsUnderlinedProperty = AvaloniaProperty.Register( + nameof(IsUnderlined), + false + ); + + public bool IsUnderlined + { + get => GetValue(IsUnderlinedProperty); + set => SetValue(IsUnderlinedProperty, value); + } +} diff --git a/WheelWizard/Views/Components/PopupListButton.axaml b/WheelWizard/Views/Components/ListActionButton.axaml similarity index 53% rename from WheelWizard/Views/Components/PopupListButton.axaml rename to WheelWizard/Views/Components/ListActionButton.axaml index 139a54572..d66cb3c33 100644 --- a/WheelWizard/Views/Components/PopupListButton.axaml +++ b/WheelWizard/Views/Components/ListActionButton.axaml @@ -7,22 +7,17 @@ - - - - - + + + + + - - - - - - + diff --git a/WheelWizard/Views/Components/ListActionButton.axaml.cs b/WheelWizard/Views/Components/ListActionButton.axaml.cs new file mode 100644 index 000000000..0e307250c --- /dev/null +++ b/WheelWizard/Views/Components/ListActionButton.axaml.cs @@ -0,0 +1,4 @@ +namespace WheelWizard.Views.Components; + +// A list-row appearance; navigation remains the caller's responsibility. +public class ListActionButton : Button { } diff --git a/WheelWizard/Views/Components/LoadingIcon.axaml b/WheelWizard/Views/Components/LoadingIcon.axaml index f9e589a45..239de42dd 100644 --- a/WheelWizard/Views/Components/LoadingIcon.axaml +++ b/WheelWizard/Views/Components/LoadingIcon.axaml @@ -1,7 +1,6 @@ + xmlns:components="clr-namespace:WheelWizard.Views.Components"> diff --git a/WheelWizard/Views/Components/MultiColoredIcon.axaml.cs b/WheelWizard/Views/Components/MultiColoredIcon.axaml.cs index 015503fb7..eced9f37e 100644 --- a/WheelWizard/Views/Components/MultiColoredIcon.axaml.cs +++ b/WheelWizard/Views/Components/MultiColoredIcon.axaml.cs @@ -199,12 +199,11 @@ private IBrush GetFinalBrush(IBrush originalBrush) continue; // From here we know that the color is indeed a template color - var newColorProperty = GetType().GetProperty($"Color{i}"); - var propertyValue = newColorProperty?.GetValue(this); + var propertyValue = GetValue(PaletteProperties[i - 1]); if (propertyValue == null) return UndefinedColorsTransparent ? Brushes.Transparent : originalBrush; - return (IBrush)propertyValue; + return propertyValue; } return originalBrush; } @@ -213,7 +212,27 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { base.OnPropertyChanged(change); - if (change.Property != ProcessedIconDataProperty) + if ( + change.Property == IconDataProperty + || change.Property == UndefinedColorsTransparentProperty + || PaletteProperties.Contains(change.Property) + ) UpdateDrawingColors(); } + + private static readonly StyledProperty[] PaletteProperties = + [ + Color1Property, + Color2Property, + Color3Property, + Color4Property, + Color5Property, + Color6Property, + Color7Property, + Color8Property, + Color9Property, + Color10Property, + Color11Property, + Color12Property, + ]; } diff --git a/WheelWizard/Views/Components/MultiIconRadioButton.axaml b/WheelWizard/Views/Components/MultiIconRadioButton.axaml index c902bb122..7ab923bba 100644 --- a/WheelWizard/Views/Components/MultiIconRadioButton.axaml +++ b/WheelWizard/Views/Components/MultiIconRadioButton.axaml @@ -43,6 +43,8 @@ - @@ -82,70 +84,70 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/WheelWizard/Views/Components/StatusBadge.axaml.cs b/WheelWizard/Views/Components/StatusBadge.axaml.cs new file mode 100644 index 000000000..c0984220d --- /dev/null +++ b/WheelWizard/Views/Components/StatusBadge.axaml.cs @@ -0,0 +1,72 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Media; + +namespace WheelWizard.Views.Components; + +public enum StatusVariant +{ + Default, + Dark, + Success, + Warning, + Danger, +} + +public class StatusBadge : ContentControl +{ + 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( + nameof(Variant), + StatusVariant.Default + ); + + public StatusVariant Variant + { + get => GetValue(VariantProperty); + set => SetValue(VariantProperty, value); + } + + public static readonly StyledProperty TipPlacementProperty = AvaloniaProperty.Register( + nameof(TipPlacement), + PlacementMode.Top + ); + + public PlacementMode TipPlacement + { + get => GetValue(TipPlacementProperty); + set => SetValue(TipPlacementProperty, value); + } +} diff --git a/WheelWizard/Views/Components/TextField.axaml b/WheelWizard/Views/Components/TextField.axaml new file mode 100644 index 000000000..593587c03 --- /dev/null +++ b/WheelWizard/Views/Components/TextField.axaml @@ -0,0 +1,20 @@ + + + diff --git a/WheelWizard/Views/Components/TextField.axaml.cs b/WheelWizard/Views/Components/TextField.axaml.cs new file mode 100644 index 000000000..b70977048 --- /dev/null +++ b/WheelWizard/Views/Components/TextField.axaml.cs @@ -0,0 +1,128 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Interactivity; + +namespace WheelWizard.Views.Components; + +public enum TextFieldVariant +{ + Default, + Dark, +} + +public class TextField : TemplatedControl +{ + private TextBox? _input; + private bool _hasError; + + public static readonly StyledProperty VariantProperty = AvaloniaProperty.Register( + nameof(Variant), + TextFieldVariant.Default + ); + + public TextFieldVariant Variant + { + get => GetValue(VariantProperty); + set => SetValue(VariantProperty, value); + } + + public static readonly StyledProperty ErrorTextProperty = AvaloniaProperty.Register(nameof(ErrorText)); + + public string? ErrorText + { + get => GetValue(ErrorTextProperty); + set => SetValue(ErrorTextProperty, value); + } + + public static readonly StyledProperty TextProperty = AvaloniaProperty.Register( + nameof(Text), + defaultBindingMode: BindingMode.TwoWay + ); + + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public static readonly StyledProperty PlaceholderTextProperty = AvaloniaProperty.Register( + nameof(PlaceholderText) + ); + + public string? PlaceholderText + { + get => GetValue(PlaceholderTextProperty); + set => SetValue(PlaceholderTextProperty, value); + } + + public static readonly StyledProperty LabelProperty = AvaloniaProperty.Register(nameof(Label)); + + public string? Label + { + get => GetValue(LabelProperty); + set => SetValue(LabelProperty, value); + } + + public static readonly StyledProperty TipTextProperty = AvaloniaProperty.Register(nameof(TipText)); + + public string? TipText + { + get => GetValue(TipTextProperty); + set => SetValue(TipTextProperty, value); + } + + public static readonly StyledProperty IsReadOnlyProperty = AvaloniaProperty.Register(nameof(IsReadOnly), false); + + public bool IsReadOnly + { + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); + } + + public static readonly DirectProperty HasErrorProperty = AvaloniaProperty.RegisterDirect( + nameof(HasError), + field => field.HasError + ); + public bool HasError => _hasError; + + public static readonly RoutedEvent TextChangedEvent = RoutedEvent.Register( + nameof(TextChanged), + RoutingStrategies.Bubble + ); + + public event EventHandler? TextChanged + { + add => AddHandler(TextChangedEvent, value); + remove => RemoveHandler(TextChangedEvent, value); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + if (_input is not null) + _input.TextChanged -= InputTextChanged; + base.OnApplyTemplate(e); + _input = e.NameScope.Find("PART_Input"); + if (_input is not null) + _input.TextChanged += InputTextChanged; + UpdateAppearance(); + } + + private void InputTextChanged(object? sender, TextChangedEventArgs e) => RaiseEvent(new TextChangedEventArgs(TextChangedEvent, this)); + + private void UpdateAppearance() + { + SetAndRaise(HasErrorProperty, ref _hasError, !string.IsNullOrWhiteSpace(ErrorText)); + PseudoClasses.Set(":error", HasError); + _input?.Classes.Set("error", HasError); + _input?.Classes.Set("dark", Variant == TextFieldVariant.Dark); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == ErrorTextProperty || change.Property == VariantProperty) + UpdateAppearance(); + } +} diff --git a/WheelWizard/Views/Components/OptionButton.axaml b/WheelWizard/Views/Components/TileButton.axaml similarity index 66% rename from WheelWizard/Views/Components/OptionButton.axaml rename to WheelWizard/Views/Components/TileButton.axaml index 9356747a8..f4ca3b9b0 100644 --- a/WheelWizard/Views/Components/OptionButton.axaml +++ b/WheelWizard/Views/Components/TileButton.axaml @@ -1,22 +1,25 @@ - + xmlns:components1="clr-namespace:WheelWizard.Views.Components"> - - - - - + + + + + - - - + + + + - - - - - - - - diff --git a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs b/WheelWizard/Views/Shell/Controls/SidebarRadioButton.axaml.cs similarity index 54% rename from WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs rename to WheelWizard/Views/Shell/Controls/SidebarRadioButton.axaml.cs index debbbd110..aaaa11abd 100644 --- a/WheelWizard/Views/Patterns/SidebarRadioButton.axaml.cs +++ b/WheelWizard/Views/Shell/Controls/SidebarRadioButton.axaml.cs @@ -1,16 +1,12 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Input; using Avalonia.Media; using WheelWizard.Views.Shell.Views; -namespace WheelWizard.Views.Patterns; +namespace WheelWizard.Views.Shell.Controls; -public partial class SidebarRadioButton : RadioButton +public class SidebarRadioButton : RadioButton { - private Border? _hoverEffect; - public static readonly StyledProperty IconDataProperty = AvaloniaProperty.Register( nameof(IconData) ); @@ -73,55 +69,11 @@ public double BoxIconSize set => SetValue(BoxIconSizeProperty, value); } - //todo: after patches is more stable, uncomment this - - // public static readonly StyledProperty WarningVisibleProperty = AvaloniaProperty.Register( - // nameof(WarningVisible) - // ); - // - // public bool WarningVisible - // { - // get => GetValue(WarningVisibleProperty); - // set => SetValue(WarningVisibleProperty, value); - // } - // - // public static readonly StyledProperty WarningTipProperty = AvaloniaProperty.Register( - // nameof(WarningTip) - // ); - // - // public string WarningTip - // { - // get => GetValue(WarningTipProperty); - // set => SetValue(WarningTipProperty, value); - // } - - protected override void OnPointerMoved(PointerEventArgs e) - { - base.OnPointerMoved(e); - if (_hoverEffect?.RenderTransform is not TranslateTransform transform) - return; - - var position = e.GetPosition(this); - transform.X = position.X - _hoverEffect.Width / 2; - transform.Y = position.Y - _hoverEffect.Height / 2; - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - _hoverEffect = e.NameScope.Find("PART_HoverEffect"); - } - public event EventHandler? NavigationRequested; - protected override void OnPointerPressed(PointerPressedEventArgs e) + protected override void OnClick() { - base.OnPointerPressed(e); - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - return; - - PageType ??= typeof(NotFoundPage); - - NavigationRequested?.Invoke(this, PageType); + base.OnClick(); + NavigationRequested?.Invoke(this, PageType ?? typeof(NotFoundPage)); } } diff --git a/WheelWizard/Views/Components/WheelTrail.axaml b/WheelWizard/Views/Shell/Controls/WheelTrail.axaml similarity index 85% rename from WheelWizard/Views/Components/WheelTrail.axaml rename to WheelWizard/Views/Shell/Controls/WheelTrail.axaml index 2225082a4..28beaf401 100644 --- a/WheelWizard/Views/Components/WheelTrail.axaml +++ b/WheelWizard/Views/Shell/Controls/WheelTrail.axaml @@ -1,7 +1,8 @@ + xmlns:controls="using:WheelWizard.Views.Components" + xmlns:shellControls="clr-namespace:WheelWizard.Views.Shell.Controls"> - - - - - - - - + + - - - - - + + - - - - - - - - - - - diff --git a/WheelWizard/WheelWizard.csproj b/WheelWizard/WheelWizard.csproj index 6f154dac4..781fb23bc 100644 --- a/WheelWizard/WheelWizard.csproj +++ b/WheelWizard/WheelWizard.csproj @@ -71,16 +71,16 @@ - - PopupListButton.axaml + + ListActionButton.axaml Code EditorEmpty.axaml Code - - MemeNumberState.axaml + + CommunityCountBadge.axaml