diff --git a/src/Artemis.UI.Shared/Services/Builders/ContentDialogBuilder.cs b/src/Artemis.UI.Shared/Services/Builders/ContentDialogBuilder.cs index c5a9f3e34..da40fa9d2 100644 --- a/src/Artemis.UI.Shared/Services/Builders/ContentDialogBuilder.cs +++ b/src/Artemis.UI.Shared/Services/Builders/ContentDialogBuilder.cs @@ -107,7 +107,7 @@ public class ContentDialogBuilder _contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter); builder.Command.CanExecuteChanged += (_, _) => _contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter); } - + return this; } diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml new file mode 100644 index 000000000..76860968f --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml @@ -0,0 +1,12 @@ + + + diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml.cs new file mode 100644 index 000000000..5da316d40 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Entries.Details; + +public partial class EntryImagesDialogView : ReactiveUserControl +{ + public EntryImagesDialogView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogViewModel.cs new file mode 100644 index 000000000..4fd125a8a --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesDialogViewModel.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.ObjectModel; +using System.Reactive; +using System.Reactive.Disposables; +using Artemis.Core.Services; +using Artemis.UI.Shared; +using FluentAvalonia.UI.Controls; +using PropertyChanged.SourceGenerator; +using ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Entries.Details; + +public partial class EntryImagesDialogViewModel : ContentDialogViewModelBase +{ + [Notify] private EntryImageViewModel _currentImage; + private readonly IInputService _inputService; + + public EntryImagesDialogViewModel(ObservableCollection images, EntryImageViewModel startImage, IInputService inputService) + { + _currentImage = startImage; + _inputService = inputService; + + Images = images; + Previous = ReactiveCommand.Create(() => CurrentImage = Images[(Images.IndexOf(CurrentImage) - 1 + Images.Count) % Images.Count]); + Next = ReactiveCommand.Create(() => CurrentImage = Images[(Images.IndexOf(CurrentImage) + 1) % Images.Count]); + + this.WhenActivated(d => + { + if (ContentDialog == null) + return; + + _inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown; + ContentDialog.Closing += ContentDialogOnClosing; + Disposable.Create(() => + { + _inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown; + ContentDialog.Closing -= ContentDialogOnClosing; + }).DisposeWith(d); + }); + } + + private void InputServiceOnKeyboardKeyDown(object? sender, ArtemisKeyboardKeyEventArgs e) + { + // Leveraging InputService to avoid issues with which control has focus + if (e.Key == KeyboardKey.ArrowRight) + Next.Execute().Subscribe(); + else if (e.Key == KeyboardKey.ArrowLeft) + Previous.Execute().Subscribe(); + else if (e.Key == KeyboardKey.Escape) + ContentDialog?.Hide(ContentDialogResult.None); + } + + private void ContentDialogOnClosing(ContentDialog sender, ContentDialogClosingEventArgs args) + { + args.Cancel = args.Result != ContentDialogResult.None; + } + + public ObservableCollection Images { get; } + public ReactiveCommand Previous { get; } + public ReactiveCommand Next { get; } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml index e4af87004..6c3bc886c 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml @@ -2,14 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:system="clr-namespace:System;assembly=System.Runtime" - xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" xmlns:details="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Details" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryImagesView" x:DataType="details:EntryImagesViewModel"> - + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs index 0d0672f94..38013a59a 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs @@ -1,13 +1,23 @@ using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Input; +using Avalonia.ReactiveUI; namespace Artemis.UI.Screens.Workshop.Entries.Details; -public partial class EntryImagesView : UserControl +public partial class EntryImagesView : ReactiveUserControl { public EntryImagesView() { InitializeComponent(); } + + private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + if (sender is not IDataContextProvider contextProvider) + return; + if (contextProvider.DataContext is not EntryImageViewModel entryImageViewModel) + return; + + ViewModel?.ShowImages(entryImageViewModel); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs index c38cbb744..0f8cced22 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs @@ -1,16 +1,30 @@ using System.Collections.ObjectModel; using System.Linq; +using System.Threading.Tasks; using Artemis.UI.Shared; +using Artemis.UI.Shared.Services; using Artemis.WebClient.Workshop; namespace Artemis.UI.Screens.Workshop.Entries.Details; public class EntryImagesViewModel : ViewModelBase { + private readonly IWindowService _windowService; public ObservableCollection Images { get; } - public EntryImagesViewModel(IEntryDetails entryDetails) + public EntryImagesViewModel(IEntryDetails entryDetails, IWindowService windowService) { + _windowService = windowService; Images = new ObservableCollection(entryDetails.Images.Select(i => new EntryImageViewModel(i))); } + + public async Task ShowImages(EntryImageViewModel image) + { + await _windowService.CreateContentDialog() + .WithViewModel(out EntryImagesDialogViewModel vm, Images, image) + .HavingPrimaryButton(b => b.WithText("Previous").WithCommand(vm.Previous)) + .HavingSecondaryButton(b => b.WithText("Next").WithCommand(vm.Next)) + .WithFullScreen() + .ShowAsync(); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Styles/Artemis.axaml b/src/Artemis.UI/Styles/Artemis.axaml index 55109b2d3..997c34ab6 100644 --- a/src/Artemis.UI/Styles/Artemis.axaml +++ b/src/Artemis.UI/Styles/Artemis.axaml @@ -3,7 +3,8 @@ xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:aedit="using:AvaloniaEdit" - xmlns:aedit2="using:AvaloniaEdit.Editing"> + xmlns:aedit2="using:AvaloniaEdit.Editing" + xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"> @@ -24,6 +25,17 @@ + + + + avares://Artemis.UI/Assets/Fonts#Roboto Mono