1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Workshop - Implemented image gallery dialog

This commit is contained in:
RobertBeekman 2023-12-13 21:55:27 +01:00
parent e304d67035
commit 9393bf2b68
8 changed files with 135 additions and 9 deletions

View File

@ -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;
}

View File

@ -0,0 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"
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: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.EntryImagesDialogView"
x:DataType="details:EntryImagesDialogViewModel"
Margin="-25 -63 -25 -25">
<Image asyncImageLoader:ImageLoader.Source="{CompiledBinding CurrentImage.Url}" Stretch="None"/>
</UserControl>

View File

@ -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<EntryImagesDialogViewModel>
{
public EntryImagesDialogView()
{
InitializeComponent();
}
}

View File

@ -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<EntryImageViewModel> 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<EntryImageViewModel> Images { get; }
public ReactiveCommand<Unit, EntryImageViewModel> Previous { get; }
public ReactiveCommand<Unit, EntryImageViewModel> Next { get; }
}

View File

@ -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">
<ScrollViewer Classes="with-padding" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{CompiledBinding Images}" Width="300">
<ItemsControl ItemsSource="{CompiledBinding Images}">
<ItemsControl.Styles>
<Styles>
<Style Selector="ItemsControl > ContentPresenter">
@ -20,6 +18,11 @@
</Style>
</Styles>
</ItemsControl.Styles>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{CompiledBinding}" Cursor="Hand" PointerPressed="InputElement_OnPointerPressed"></ContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />

View File

@ -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<EntryImagesViewModel>
{
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);
}
}

View File

@ -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<EntryImageViewModel> Images { get; }
public EntryImagesViewModel(IEntryDetails entryDetails)
public EntryImagesViewModel(IEntryDetails entryDetails, IWindowService windowService)
{
_windowService = windowService;
Images = new ObservableCollection<EntryImageViewModel>(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();
}
}

View File

@ -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">
<!-- Third party styles -->
<styling:FluentAvaloniaTheme PreferSystemTheme="False" PreferUserAccentColor="True"/>
<avalonia:MaterialIconStyles />
@ -24,6 +25,17 @@
<Setter Property="SelectionForeground" Value="{DynamicResource TextOnAccentFillColorSelectedTextBrush}" />
</Style>
<Style Selector="controls|ContentDialog.fullscreen controls|FABorder#BackgroundElement">
<Setter Property="MaxWidth" Value="99999"></Setter>
<Setter Property="MaxHeight" Value="99999"></Setter>
<Setter Property="Margin" Value="100"></Setter>
</Style>
<Style Selector="controls|ContentDialog.fullscreen ScrollViewer#ContentScrollViewer">
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter>
<Setter Property="VerticalScrollBarVisibility" Value="Disabled"></Setter>
</Style>
<Styles.Resources>
<ResourceDictionary>
<FontFamily x:Key="RobotoMono">avares://Artemis.UI/Assets/Fonts#Roboto Mono</FontFamily>