mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
Workshop - Layout info and images WIP
This commit is contained in:
parent
f4b9b67f1a
commit
c1e0dadce8
@ -148,6 +148,16 @@ public class ContentDialogBuilder
|
||||
_contentDialog.FullSizeDesired = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the dialog to be full screen.
|
||||
/// </summary>
|
||||
/// <returns>The builder that can be used to further build the dialog.</returns>
|
||||
public ContentDialogBuilder WithFullScreen()
|
||||
{
|
||||
_contentDialog.Classes.Add("fullscreen");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously shows the content dialog.
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Builders;
|
||||
|
||||
@ -37,6 +38,29 @@ public class FileDialogFilterBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all supported bitmap types to the filter.
|
||||
/// </summary>
|
||||
public FileDialogFilterBuilder WithBitmaps()
|
||||
{
|
||||
// Formats from SKEncodedImageFormat
|
||||
return WithExtension("astc")
|
||||
.WithExtension("avif")
|
||||
.WithExtension("bmp")
|
||||
.WithExtension("dng")
|
||||
.WithExtension("gif")
|
||||
.WithExtension("heif")
|
||||
.WithExtension("ico")
|
||||
.WithExtension("jpg")
|
||||
.WithExtension("jpeg")
|
||||
.WithExtension("ktx")
|
||||
.WithExtension("pkm")
|
||||
.WithExtension("png")
|
||||
.WithExtension("wbmp")
|
||||
.WithExtension("webp")
|
||||
.WithName("Bitmap image");
|
||||
}
|
||||
|
||||
internal FilePickerFileType Build()
|
||||
{
|
||||
return new FilePickerFileType(_name)
|
||||
|
||||
@ -10,13 +10,13 @@
|
||||
<StackPanel>
|
||||
<TextBlock TextWrapping="Wrap">Artemis couldn't automatically determine the logical layout of your</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.DeviceName, Mode=OneWay}" />
|
||||
|
||||
|
||||
<TextBlock Margin="0 10" TextWrapping="Wrap">
|
||||
While not as important as the physical layout, setting the correct logical layout will allow Artemis to show the right keycaps (if a matching layout file is present)
|
||||
</TextBlock>
|
||||
|
||||
<AutoCompleteBox HorizontalAlignment="Stretch"
|
||||
ItemsSource="{CompiledBinding Regions}"
|
||||
<AutoCompleteBox HorizontalAlignment="Stretch"
|
||||
ItemsSource="{CompiledBinding Regions}"
|
||||
SelectedItem="{CompiledBinding SelectedRegion}"
|
||||
ValueMemberBinding="{CompiledBinding EnglishName, DataType=globalization:RegionInfo}"
|
||||
Watermark="Enter keyboard country name"
|
||||
@ -26,12 +26,10 @@
|
||||
Name="RegionsAutoCompleteBox">
|
||||
<AutoCompleteBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type globalization:RegionInfo}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{CompiledBinding EnglishName}"></TextBlock>
|
||||
<TextBlock Text=" ("/>
|
||||
<TextBlock FontWeight="SemiBold" Text="{CompiledBinding TwoLetterISORegionName}"></TextBlock>
|
||||
<TextBlock Text=")"/>
|
||||
</StackPanel>
|
||||
<TextBlock>
|
||||
<Run Text="{CompiledBinding EnglishName}"></Run>
|
||||
<Run Text="(" /><Run FontWeight="SemiBold" Text="{CompiledBinding TwoLetterISORegionName}"></Run><Run Text=")" />
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</AutoCompleteBox.ItemTemplate>
|
||||
</AutoCompleteBox>
|
||||
|
||||
@ -181,7 +181,7 @@ public partial class ProfileConfigurationEditViewModel : DialogViewModelBase<Pro
|
||||
private async Task ExecuteBrowseBitmapFile()
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog()
|
||||
.HavingFilter(f => f.WithExtension("png").WithExtension("jpg").WithExtension("bmp").WithName("Bitmap image"))
|
||||
.HavingFilter(f => f.WithBitmaps())
|
||||
.ShowAsync();
|
||||
|
||||
if (result == null)
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
<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:image="clr-namespace:Artemis.UI.Screens.Workshop.Image"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Image.ImageSubmissionView"
|
||||
x:DataType="image:ImageSubmissionViewModel">
|
||||
<UserControl.Resources>
|
||||
<converters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<Border Classes="card" Padding="0" Width="300" ClipToBounds="True" Margin="5">
|
||||
<Grid RowDefinitions="230,*">
|
||||
<Rectangle Grid.Row="0" Fill="{DynamicResource CheckerboardBrush}"/>
|
||||
<Image Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
Source="{CompiledBinding Bitmap}"/>
|
||||
<StackPanel Grid.Row="1" Margin="12">
|
||||
<TextBlock Text="{CompiledBinding FileName, FallbackValue=Unnamed image}" TextTrimming="CharacterEllipsis" />
|
||||
<StackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding ImageDimensions, Mode=OneWay}" />
|
||||
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding FileSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Row="1"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="6"
|
||||
Classes="icon-button"
|
||||
Command="{CompiledBinding Remove}"
|
||||
ToolTip.Tip="Remove">
|
||||
<avalonia:MaterialIcon Kind="Trash" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@ -0,0 +1,11 @@
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Image;
|
||||
|
||||
public partial class ImageSubmissionView : ReactiveUserControl<ImageSubmissionViewModel>
|
||||
{
|
||||
public ImageSubmissionView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Image;
|
||||
|
||||
public partial class ImageSubmissionViewModel : ActivatableViewModelBase
|
||||
{
|
||||
[Notify(Setter.Private)] private Bitmap? _bitmap;
|
||||
[Notify(Setter.Private)] private string? _fileName;
|
||||
[Notify(Setter.Private)] private string? _imageDimensions;
|
||||
[Notify(Setter.Private)] private long _fileSize;
|
||||
[Notify] private ICommand? _remove;
|
||||
|
||||
public ImageSubmissionViewModel(Stream imageStream)
|
||||
{
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
imageStream.Seek(0, SeekOrigin.Begin);
|
||||
Bitmap = new Bitmap(imageStream);
|
||||
FileSize = imageStream.Length;
|
||||
ImageDimensions = Bitmap.Size.Width + "x" + Bitmap.Size.Height;
|
||||
|
||||
if (imageStream is FileStream fileStream)
|
||||
FileName = Path.GetFileName(fileStream.Name);
|
||||
else
|
||||
FileName = "Unnamed image";
|
||||
|
||||
Bitmap.DisposeWith(d);
|
||||
}, DispatcherPriority.Background);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
<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:dialogs="clr-namespace:Artemis.UI.Screens.Workshop.Layout.Dialogs"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:deviceProviders="clr-namespace:Artemis.Core.DeviceProviders;assembly=Artemis.Core"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Layout.Dialogs.DeviceProviderPickerDialogView"
|
||||
x:DataType="dialogs:DeviceProviderPickerDialogViewModel">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<ListBox Name="EffectDescriptorsList"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{CompiledBinding DeviceProviders}"
|
||||
IsVisible="{CompiledBinding DeviceProviders.Count}"
|
||||
Height="300">
|
||||
|
||||
<ListBox.DataTemplates>
|
||||
<DataTemplate DataType="{x:Type deviceProviders:DeviceProvider}">
|
||||
<Grid RowDefinitions="Auto,*"
|
||||
ColumnDefinitions="Auto,Auto"
|
||||
Background="Transparent"
|
||||
PointerReleased="InputElement_OnPointerReleased"
|
||||
Margin="0 4"
|
||||
VerticalAlignment="Center">
|
||||
<shared:ArtemisIcon Grid.Column="0"
|
||||
Grid.RowSpan="2"
|
||||
Icon="{CompiledBinding Plugin.Info.Icon}"
|
||||
Width="24"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 15 0" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Classes="BodyStrongTextBlockStyle"
|
||||
Text="{CompiledBinding Info.Name}"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="450"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Foreground="{DynamicResource TextFillColorSecondary}"
|
||||
Text="{CompiledBinding Plugin.Info.Name}"
|
||||
VerticalAlignment="Top"
|
||||
Width="450"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.DataTemplates>
|
||||
</ListBox>
|
||||
<Grid Grid.Row="1" Height="300">
|
||||
<StackPanel VerticalAlignment="Center"
|
||||
Spacing="20"
|
||||
IsVisible="{CompiledBinding !DeviceProviders.Count}">
|
||||
<avalonia:MaterialIcon Kind="CloseCircle" Width="32" Height="32" />
|
||||
<TextBlock Classes="h5" TextAlignment="Center">You do not have any device providers enabled</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,24 @@
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
||||
|
||||
public partial class DeviceProviderPickerDialogView : ReactiveUserControl<DeviceProviderPickerDialogViewModel>
|
||||
{
|
||||
public DeviceProviderPickerDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (sender is not IDataContextProvider {DataContext: DeviceProvider deviceProvider} || ViewModel == null)
|
||||
return;
|
||||
|
||||
ViewModel?.SelectDeviceProvider(deviceProvider);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
||||
|
||||
public class DeviceProviderPickerDialogViewModel : ContentDialogViewModelBase
|
||||
{
|
||||
public ObservableCollection<DeviceProvider> DeviceProviders { get; }
|
||||
|
||||
public DeviceProviderPickerDialogViewModel(IPluginManagementService pluginManagementService)
|
||||
{
|
||||
DeviceProviders = new ObservableCollection<DeviceProvider>(pluginManagementService.GetFeaturesOfType<DeviceProvider>());
|
||||
}
|
||||
|
||||
public DeviceProvider? DeviceProvider { get; set; }
|
||||
|
||||
public void SelectDeviceProvider(DeviceProvider deviceProvider)
|
||||
{
|
||||
DeviceProvider = deviceProvider;
|
||||
ContentDialog?.Hide();
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,55 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutInfoView"
|
||||
x:DataType="layout:LayoutInfoViewModel">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="*,*">
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Margin="0 0 4 0">
|
||||
<Label>Model</Label>
|
||||
<TextBox Text="{CompiledBinding Model}"></TextBox>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="1" Margin="4 0 0 4" ColumnDefinitions="*,*" RowDefinitions="*,*">
|
||||
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Bottom">Vendor</Label>
|
||||
<TextBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{CompiledBinding Vendor}"></TextBox>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
|
||||
<Label>Device provider ID</Label>
|
||||
<TextBox Text="{CompiledBinding DeviceProviderIdInput}"></TextBox>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Classes="AppBarButton"
|
||||
Command="{CompiledBinding BrowseDeviceProvider}"
|
||||
ToolTip.Tip="Browse">
|
||||
...
|
||||
</Button>
|
||||
|
||||
<TextBlock Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Classes="subtitle"
|
||||
Margin="0 2"
|
||||
Text="{CompiledBinding DeviceProviders}"
|
||||
VerticalAlignment="Top">
|
||||
</TextBlock>
|
||||
|
||||
<Button Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0 5"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Command="{CompiledBinding Remove}"
|
||||
Classes="icon-button">
|
||||
<avalonia:MaterialIcon Kind="Trash" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -1,10 +1,8 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Layout;
|
||||
|
||||
public partial class LayoutInfoView : UserControl
|
||||
public partial class LayoutInfoView : ReactiveUserControl<LayoutInfoViewModel>
|
||||
{
|
||||
public LayoutInfoView()
|
||||
{
|
||||
|
||||
@ -1,32 +1,68 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using RGB.NET.Core;
|
||||
using KeyboardLayoutType = Artemis.Core.KeyboardLayoutType;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Validation.Extensions;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Layout;
|
||||
|
||||
public partial class LayoutInfoViewModel : ViewModelBase
|
||||
public partial class LayoutInfoViewModel : ValidatableViewModelBase
|
||||
{
|
||||
[Notify] private Guid _deviceProvider;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly ObservableAsPropertyHelper<string?> _deviceProviders;
|
||||
[Notify] private string? _vendor;
|
||||
[Notify] private string? _model;
|
||||
[Notify] private KeyboardLayoutType? _physicalLayout;
|
||||
[Notify] private string? _logicalLayout;
|
||||
|
||||
/// <inheritdoc />
|
||||
public LayoutInfoViewModel(ArtemisLayout layout)
|
||||
[Notify] private Guid _deviceProviderId;
|
||||
[Notify] private string? _deviceProviderIdInput;
|
||||
[Notify] private ICommand? _remove;
|
||||
|
||||
public LayoutInfoViewModel(ArtemisLayout layout,
|
||||
IDeviceService deviceService,
|
||||
IWindowService windowService,
|
||||
IPluginManagementService pluginManagementService)
|
||||
{
|
||||
DisplayKeyboardLayout = layout.RgbLayout.Type == RGBDeviceType.Keyboard;
|
||||
_windowService = windowService;
|
||||
_vendor = layout.RgbLayout.Vendor;
|
||||
_model = layout.RgbLayout.Model;
|
||||
|
||||
DeviceProvider? deviceProvider = deviceService.Devices.FirstOrDefault(d => d.Layout == layout)?.DeviceProvider;
|
||||
if (deviceProvider != null)
|
||||
_deviceProviderId = deviceProvider.Plugin.Guid;
|
||||
|
||||
_deviceProviders = this.WhenAnyValue(vm => vm.DeviceProviderId)
|
||||
.Select(id => pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == id)?.Features.Select(f => f.Name))
|
||||
.Select(names => names != null ? string.Join(", ", names) : "")
|
||||
.ToProperty(this, vm => vm.DeviceProviders);
|
||||
|
||||
this.WhenAnyValue(vm => vm.DeviceProviderId).Subscribe(g => DeviceProviderIdInput = g.ToString());
|
||||
this.WhenAnyValue(vm => vm.DeviceProviderIdInput).Where(i => Guid.TryParse(i, out _)).Subscribe(i => DeviceProviderId = Guid.Parse(i!));
|
||||
|
||||
this.ValidationRule(vm => vm.Model, input => !string.IsNullOrWhiteSpace(input), "Device model is required");
|
||||
this.ValidationRule(vm => vm.Vendor, input => !string.IsNullOrWhiteSpace(input), "Device vendor is required");
|
||||
this.ValidationRule(vm => vm.DeviceProviderIdInput, input => Guid.TryParse(input, out _), "Must be a valid GUID formatted as: 00000000-0000-0000-0000-000000000000");
|
||||
this.ValidationRule(vm => vm.DeviceProviderIdInput, input => !string.IsNullOrWhiteSpace(input), "Device provider ID is required");
|
||||
}
|
||||
|
||||
public LayoutInfoViewModel(ArtemisLayout layout, LayoutInfo layoutInfo)
|
||||
{
|
||||
DisplayKeyboardLayout = layout.RgbLayout.Type == RGBDeviceType.Keyboard;
|
||||
|
||||
}
|
||||
public string? DeviceProviders => _deviceProviders.Value;
|
||||
|
||||
public bool DisplayKeyboardLayout { get; }
|
||||
public async Task BrowseDeviceProvider()
|
||||
{
|
||||
await _windowService.CreateContentDialog()
|
||||
.WithTitle("Select device provider")
|
||||
.WithViewModel(out DeviceProviderPickerDialogViewModel vm)
|
||||
.ShowAsync();
|
||||
|
||||
DeviceProvider? deviceProvider = vm.DeviceProvider;
|
||||
if (deviceProvider != null)
|
||||
DeviceProviderId = deviceProvider.Plugin.Guid;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using KeyboardLayoutType = Artemis.WebClient.Workshop.KeyboardLayoutType;
|
||||
using Artemis.UI.Screens.Workshop.Layout;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
|
||||
@ -14,15 +14,16 @@ public class LayoutEntrySource : IEntrySource
|
||||
}
|
||||
|
||||
public ArtemisLayout Layout { get; set; }
|
||||
public List<LayoutInfo> LayoutInfo { get; } = new();
|
||||
}
|
||||
public ObservableCollection<LayoutInfoViewModel> LayoutInfo { get; } = new();
|
||||
public KeyboardLayoutType PhysicalLayout { get; set; }
|
||||
|
||||
public class LayoutInfo
|
||||
{
|
||||
public Guid DeviceProvider { get; set; }
|
||||
public RGBDeviceType DeviceType { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string Vendor { get; set; }
|
||||
public string? LogicalLayout { get; set; }
|
||||
public KeyboardLayoutType? PhysicalLayout { get; set; }
|
||||
private List<LayoutCustomLedDataLogicalLayout> GetLogicalLayouts()
|
||||
{
|
||||
return Layout.Leds
|
||||
.Where(l => l.LayoutCustomLedData.LogicalLayouts != null)
|
||||
.SelectMany(l => l.LayoutCustomLedData.LogicalLayouts!)
|
||||
.Where(l => !string.IsNullOrWhiteSpace(l.Name))
|
||||
.DistinctBy(l => l.Name)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ using DryIoc;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
|
||||
public class SubmissionWizardState
|
||||
public class SubmissionWizardState : IDisposable
|
||||
{
|
||||
private readonly IContainer _container;
|
||||
private readonly IWindowService _windowService;
|
||||
@ -62,4 +62,11 @@ public class SubmissionWizardState
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Icon?.Dispose();
|
||||
foreach (Stream stream in Images)
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<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:steps="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.ImagesStepView"
|
||||
x:DataType="steps:ImagesStepViewModel">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}" TextWrapping="Wrap">Images</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Optionally provide some images of your submission.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" Margin="0 20 0 0">
|
||||
<ItemsControl ItemsSource="{CompiledBinding Images}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,14 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class ImagesStepView : ReactiveUserControl<ImagesStepViewModel>
|
||||
{
|
||||
public ImagesStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Screens.Workshop.Image;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class ImagesStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly SourceList<Stream> _imageStreams;
|
||||
|
||||
public ImagesStepViewModel(IWindowService windowService, Func<Stream, ImageSubmissionViewModel> imageSubmissionViewModel)
|
||||
{
|
||||
_windowService = windowService;
|
||||
|
||||
Continue = ReactiveCommand.Create(() => State.ChangeScreen<UploadStepViewModel>());
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<SpecificationsStepViewModel>());
|
||||
Secondary = ReactiveCommand.CreateFromTask(ExecuteAddImage);
|
||||
SecondaryText = "Add image";
|
||||
|
||||
_imageStreams = new SourceList<Stream>();
|
||||
_imageStreams.Connect()
|
||||
.Transform(p => CreateImageSubmissionViewModel(imageSubmissionViewModel, p))
|
||||
.Bind(out ReadOnlyObservableCollection<ImageSubmissionViewModel> images)
|
||||
.Subscribe();
|
||||
Images = images;
|
||||
|
||||
this.WhenActivated((CompositeDisposable d) =>
|
||||
{
|
||||
_imageStreams.Clear();
|
||||
_imageStreams.AddRange(State.Images);
|
||||
});
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollection<ImageSubmissionViewModel> Images { get; }
|
||||
|
||||
private ImageSubmissionViewModel CreateImageSubmissionViewModel(Func<Stream, ImageSubmissionViewModel> imageSubmissionViewModel, Stream stream)
|
||||
{
|
||||
ImageSubmissionViewModel viewModel = imageSubmissionViewModel(stream);
|
||||
viewModel.Remove = ReactiveCommand.Create(() => _imageStreams.Remove(stream));
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
private async Task ExecuteAddImage(CancellationToken arg)
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog().WithAllowMultiple().HavingFilter(f => f.WithBitmaps()).ShowAsync();
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
foreach (string path in result)
|
||||
{
|
||||
if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
|
||||
continue;
|
||||
|
||||
FileStream stream = new(path, FileMode.Open);
|
||||
_imageStreams.Add(stream);
|
||||
State.Images.Add(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,12 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout.LayoutInfoStepView"
|
||||
x:DataType="layout:LayoutInfoStepViewModel">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
@ -24,7 +26,25 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1"
|
||||
<StackPanel Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
IsVisible="{CompiledBinding IsKeyboardLayout}"
|
||||
Margin="0 20 0 0">
|
||||
<Label>Physical layout</Label>
|
||||
<shared:EnumComboBox Value="{CompiledBinding PhysicalLayout}"></shared:EnumComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<controls:HyperlinkButton Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
IsVisible="{CompiledBinding IsKeyboardLayout}"
|
||||
Margin="0 10 0 0"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
NavigateUri="https://wiki.artemis-rgb.com/en/guides/developer/layouts/keyboard-layouts?mtm_campaign=artemis&mtm_kwd=workshop-wizard">
|
||||
Learn about physical layouts
|
||||
</controls:HyperlinkButton>
|
||||
|
||||
<ScrollViewer Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="0 10 0 0"
|
||||
Classes="with-padding"
|
||||
@ -34,7 +54,10 @@
|
||||
<ItemsRepeater ItemsSource="{CompiledBinding LayoutInfo}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl Content="{CompiledBinding}" />
|
||||
<StackPanel>
|
||||
<Border Classes="card-separator" />
|
||||
<ContentControl Content="{CompiledBinding}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
|
||||
@ -1,25 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Screens.Workshop.Layout;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
using DynamicData;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Validation.Extensions;
|
||||
using RGB.NET.Core;
|
||||
using KeyboardLayoutType = Artemis.Core.KeyboardLayoutType;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
|
||||
public class LayoutInfoStepViewModel : SubmissionViewModel
|
||||
public partial class LayoutInfoStepViewModel : SubmissionViewModel
|
||||
{
|
||||
public LayoutInfoStepViewModel()
|
||||
private readonly Func<ArtemisLayout, LayoutInfoViewModel> _getLayoutInfoViewModel;
|
||||
private ArtemisLayout? _layout;
|
||||
[Notify(Setter.Private)] private bool _isKeyboardLayout;
|
||||
[Notify] private ObservableCollection<LayoutInfoViewModel> _layoutInfo = new();
|
||||
[Notify] private KeyboardLayoutType _physicalLayout;
|
||||
|
||||
public LayoutInfoStepViewModel(Func<ArtemisLayout, LayoutInfoViewModel> getLayoutInfoViewModel)
|
||||
{
|
||||
_getLayoutInfoViewModel = getLayoutInfoViewModel;
|
||||
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<LayoutSelectionStepViewModel>());
|
||||
this.WhenActivated((CompositeDisposable _) =>
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue, ValidationContext.Valid);
|
||||
Secondary = ReactiveCommand.Create(ExecuteAddLayoutInfo);
|
||||
SecondaryText = "Add layout info";
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
LayoutInfo.Clear();
|
||||
if (State.EntrySource is LayoutEntrySource layoutEntrySource)
|
||||
LayoutInfo.AddRange(layoutEntrySource.LayoutInfo.Select(i => new LayoutInfoViewModel(layoutEntrySource.Layout, i)));
|
||||
if (State.EntrySource is not LayoutEntrySource layoutEntrySource)
|
||||
return;
|
||||
|
||||
_layout = layoutEntrySource.Layout;
|
||||
IsKeyboardLayout = _layout.RgbLayout.Type == RGBDeviceType.Keyboard;
|
||||
PhysicalLayout = layoutEntrySource.PhysicalLayout;
|
||||
LayoutInfo = layoutEntrySource.LayoutInfo;
|
||||
|
||||
if (!LayoutInfo.Any())
|
||||
ExecuteAddLayoutInfo();
|
||||
|
||||
this.ValidationRule(
|
||||
vm => vm.PhysicalLayout,
|
||||
this.WhenAnyValue(vm => vm.IsKeyboardLayout, vm => vm.PhysicalLayout, (isKeyboard, layout) => !isKeyboard || layout != KeyboardLayoutType.Unknown),
|
||||
"A keyboard layout is required"
|
||||
).DisposeWith(d);
|
||||
this.ValidationRule(
|
||||
vm => vm.LayoutInfo,
|
||||
this.WhenAnyValue(vm => vm.LayoutInfo.Count).Select(c => c != 0),
|
||||
"At least one layout info is required"
|
||||
).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ObservableCollection<LayoutInfoViewModel> LayoutInfo { get; } = new();
|
||||
private void ExecuteAddLayoutInfo()
|
||||
{
|
||||
if (_layout == null)
|
||||
return;
|
||||
|
||||
LayoutInfoViewModel layoutInfo = _getLayoutInfoViewModel(_layout);
|
||||
layoutInfo.Remove = ReactiveCommand.Create(() => LayoutInfo.Remove(layoutInfo));
|
||||
LayoutInfo.Add(layoutInfo);
|
||||
}
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
if (State.EntrySource is not LayoutEntrySource layoutEntrySource)
|
||||
return;
|
||||
|
||||
layoutEntrySource.PhysicalLayout = PhysicalLayout;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(State.Name))
|
||||
State.Name = layoutEntrySource.Layout.RgbLayout.Name ?? "";
|
||||
if (string.IsNullOrWhiteSpace(State.Summary))
|
||||
{
|
||||
State.Summary = !string.IsNullOrWhiteSpace(layoutEntrySource.Layout.RgbLayout.Vendor)
|
||||
? $"{layoutEntrySource.Layout.RgbLayout.Vendor} {layoutEntrySource.Layout.RgbLayout.Type} device layout"
|
||||
: $"{layoutEntrySource.Layout.RgbLayout.Type} device layout";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(State.Description))
|
||||
{
|
||||
State.Description = $@"### Layout properties
|
||||
**Name**
|
||||
{layoutEntrySource.Layout.RgbLayout.Name ?? "N/A"}
|
||||
**Description**
|
||||
{layoutEntrySource.Layout.RgbLayout.Description ?? "N/A"}
|
||||
**Author**
|
||||
{layoutEntrySource.Layout.RgbLayout.Author ?? "N/A"}
|
||||
**Type**
|
||||
{layoutEntrySource.Layout.RgbLayout.Type}
|
||||
**Vendor**
|
||||
{layoutEntrySource.Layout.RgbLayout.Vendor ?? "N/A"}
|
||||
**Model**
|
||||
{layoutEntrySource.Layout.RgbLayout.Model ?? "N/A"}
|
||||
**Shape**
|
||||
{layoutEntrySource.Layout.RgbLayout.Shape}
|
||||
**Width**
|
||||
{layoutEntrySource.Layout.RgbLayout.Width}mm
|
||||
**Height**
|
||||
{layoutEntrySource.Layout.RgbLayout.Height}mm";
|
||||
}
|
||||
|
||||
State.Categories = new List<long> {8}; // Device category, yes this could change but why would it
|
||||
|
||||
if (State.EntryId == null)
|
||||
State.ChangeScreen<SpecificationsStepViewModel>();
|
||||
else
|
||||
State.ChangeScreen<UploadStepViewModel>();
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ using Artemis.Core.Services;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
@ -14,6 +13,7 @@ using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
@ -36,11 +36,11 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
);
|
||||
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<EntryTypeStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue, this.WhenAnyValue(vm => vm.Layout).Select(p => p != null));
|
||||
Continue = ReactiveCommand.CreateFromTask(ExecuteContinue, this.WhenAnyValue(vm => vm.Layout).Select(p => p != null));
|
||||
|
||||
this.WhenAnyValue(vm => vm.SelectedDevice).WhereNotNull().Subscribe(d => Layout = d.Layout);
|
||||
this.WhenAnyValue(vm => vm.Layout).Subscribe(CreatePreviewDevice);
|
||||
|
||||
|
||||
this.WhenActivated((CompositeDisposable _) =>
|
||||
{
|
||||
ShowGoBack = State.EntryId == null;
|
||||
@ -82,44 +82,64 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
Layout = layout;
|
||||
}
|
||||
|
||||
private void ExecuteContinue()
|
||||
private async Task ExecuteContinue()
|
||||
{
|
||||
if (Layout == null)
|
||||
return;
|
||||
|
||||
State.EntrySource = new LayoutEntrySource(Layout);
|
||||
State.Name = Layout.RgbLayout.Name ?? "";
|
||||
State.Summary = !string.IsNullOrWhiteSpace(Layout.RgbLayout.Vendor)
|
||||
? $"{Layout.RgbLayout.Vendor} {Layout.RgbLayout.Type} device layout"
|
||||
: $"{Layout.RgbLayout.Type} device layout";
|
||||
|
||||
State.Categories = new List<long> {8}; // Device category, yes this could change but why would it
|
||||
|
||||
State.Icon?.Dispose();
|
||||
State.Icon = GetDeviceIcon();
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(SetDeviceImages, DispatcherPriority.Background);
|
||||
State.ChangeScreen<LayoutInfoStepViewModel>();
|
||||
}
|
||||
|
||||
private Stream GetDeviceIcon()
|
||||
private void SetDeviceImages()
|
||||
{
|
||||
// Go through the hassle of resizing the image to 128x128 without losing aspect ratio, padding is added for this
|
||||
using RenderTargetBitmap image = Layout.RenderLayout(false);
|
||||
using MemoryStream stream = new();
|
||||
image.Save(stream);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (Layout == null)
|
||||
return;
|
||||
|
||||
MemoryStream deviceWithoutLeds = new();
|
||||
MemoryStream deviceWithLeds = new();
|
||||
|
||||
using (RenderTargetBitmap image = Layout.RenderLayout(false))
|
||||
{
|
||||
image.Save(deviceWithoutLeds);
|
||||
deviceWithoutLeds.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
using (RenderTargetBitmap image = Layout.RenderLayout(true))
|
||||
{
|
||||
image.Save(deviceWithLeds);
|
||||
deviceWithLeds.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
State.Icon?.Dispose();
|
||||
foreach (Stream stateImage in State.Images)
|
||||
stateImage.Dispose();
|
||||
State.Images.Clear();
|
||||
|
||||
// Go through the hassle of resizing the image to 128x128 without losing aspect ratio, padding is added for this
|
||||
State.Icon = ResizeImage(deviceWithoutLeds, 128);
|
||||
State.Images.Add(deviceWithoutLeds);
|
||||
State.Images.Add(deviceWithLeds);
|
||||
}
|
||||
|
||||
private Stream ResizeImage(Stream image, int size)
|
||||
{
|
||||
MemoryStream output = new();
|
||||
using SKBitmap? sourceBitmap = SKBitmap.Decode(stream);
|
||||
using MemoryStream input = new();
|
||||
|
||||
image.CopyTo(input);
|
||||
input.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using SKBitmap? sourceBitmap = SKBitmap.Decode(input);
|
||||
int sourceWidth = sourceBitmap.Width;
|
||||
int sourceHeight = sourceBitmap.Height;
|
||||
float scale = Math.Min((float) 128 / sourceWidth, (float) 128 / sourceHeight);
|
||||
float scale = Math.Min((float) size / sourceWidth, (float) size / sourceHeight);
|
||||
|
||||
SKSizeI scaledDimensions = new((int) Math.Floor(sourceWidth * scale), (int) Math.Floor(sourceHeight * scale));
|
||||
SKPointI offset = new((128 - scaledDimensions.Width) / 2, (128 - scaledDimensions.Height) / 2);
|
||||
SKPointI offset = new((size - scaledDimensions.Width) / 2, (size - scaledDimensions.Height) / 2);
|
||||
|
||||
using SKBitmap? scaleBitmap = sourceBitmap.Resize(scaledDimensions, SKFilterQuality.High);
|
||||
using SKBitmap targetBitmap = new(128, 128);
|
||||
using SKBitmap targetBitmap = new(size, size);
|
||||
using SKCanvas canvas = new(targetBitmap);
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
canvas.DrawBitmap(scaleBitmap, offset.X, offset.Y);
|
||||
|
||||
@ -57,7 +57,7 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
|
||||
return;
|
||||
|
||||
ApplyToState();
|
||||
State.ChangeScreen<SubmitStepViewModel>();
|
||||
State.ChangeScreen<ImagesStepViewModel>();
|
||||
}
|
||||
|
||||
private void ApplyFromState()
|
||||
|
||||
@ -8,9 +8,11 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
|
||||
|
||||
public abstract partial class SubmissionViewModel : ValidatableViewModelBase
|
||||
{
|
||||
[Notify] private ReactiveCommand<Unit, Unit>? _secondary;
|
||||
[Notify] private ReactiveCommand<Unit, Unit>? _continue;
|
||||
[Notify] private ReactiveCommand<Unit, Unit>? _goBack;
|
||||
[Notify] private string _continueText = "Continue";
|
||||
[Notify] private string? _secondaryText;
|
||||
[Notify] private bool _showFinish;
|
||||
[Notify] private bool _showGoBack = true;
|
||||
[Notify] private bool _showHeader = true;
|
||||
|
||||
@ -16,9 +16,10 @@
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="Auto,*,Auto" Margin="0 0 0 15">
|
||||
<ContentControl Grid.Column="0" Grid.RowSpan="2" Width="65" Height="65" VerticalAlignment="Center" Margin="0 0 20 0" Content="{CompiledBinding CurrentUserViewModel}"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="36" VerticalAlignment="Bottom" Text="{CompiledBinding CurrentUserViewModel.Name}" IsVisible="{CompiledBinding !CurrentUserViewModel.IsAnonymous}"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="36" VerticalAlignment="Bottom" Text="Not logged in" IsVisible="{CompiledBinding CurrentUserViewModel.IsAnonymous}"/>
|
||||
<ContentControl Grid.Column="0" Grid.RowSpan="2" Width="65" Height="65" VerticalAlignment="Center" Margin="0 0 20 0" Content="{CompiledBinding CurrentUserViewModel}" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="36" VerticalAlignment="Bottom" Text="{CompiledBinding CurrentUserViewModel.Name}"
|
||||
IsVisible="{CompiledBinding !CurrentUserViewModel.IsAnonymous}" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="36" VerticalAlignment="Bottom" Text="Not logged in" IsVisible="{CompiledBinding CurrentUserViewModel.IsAnonymous}" />
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal">
|
||||
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View Wiki" NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&mtm_kwd=workshop-wizard">
|
||||
@ -36,16 +37,23 @@
|
||||
<Border Classes="card" Grid.Row="1" Grid.Column="0">
|
||||
<controls:Frame Name="Frame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||
<controls:Frame.NavigationPageFactory>
|
||||
<ui:PageFactory/>
|
||||
<ui:PageFactory />
|
||||
</controls:Frame.NavigationPageFactory>
|
||||
</controls:Frame>
|
||||
</Border>
|
||||
|
||||
<Button Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
IsVisible="{CompiledBinding Screen.Secondary, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Margin="0 15 0 0"
|
||||
Content="{CompiledBinding Screen.SecondaryText}"
|
||||
Command="{CompiledBinding Screen.Secondary}"/>
|
||||
|
||||
<StackPanel Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" Orientation="Horizontal" Spacing="5" Margin="0 15 0 0">
|
||||
<Button Command="{CompiledBinding Screen.GoBack}" IsVisible="{CompiledBinding Screen.ShowGoBack}">
|
||||
Back
|
||||
</Button>
|
||||
<Button Command="{CompiledBinding Screen.Continue}" IsVisible="{CompiledBinding !Screen.ShowFinish}" Width="80" Content="{CompiledBinding Screen.ContinueText}"/>
|
||||
<Button Command="{CompiledBinding Screen.Continue}" IsVisible="{CompiledBinding !Screen.ShowFinish}" Width="80" Content="{CompiledBinding Screen.ContinueText}" />
|
||||
<Button Command="{CompiledBinding Screen.Continue}" IsVisible="{CompiledBinding Screen.ShowFinish}" Width="80">
|
||||
Finish
|
||||
</Button>
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
using Artemis.UI.Screens.Workshop.CurrentUser;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Screens.Workshop.CurrentUser;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using DryIoc;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
|
||||
|
||||
@ -26,6 +28,8 @@ public partial class SubmissionWizardViewModel : ActivatableViewModelBase, IWork
|
||||
WindowService = windowService;
|
||||
CurrentUserViewModel = currentUserViewModel;
|
||||
CurrentUserViewModel.AllowLogout = false;
|
||||
|
||||
this.WhenActivated(d => _state.DisposeWith(d));
|
||||
}
|
||||
|
||||
public IWindowService WindowService { get; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user