mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Workshop - Layout submission WIP
This commit is contained in:
1 parent
190d797f1a
commit
c77d51fb58
9 files changed
+364
-58
No files matched your search
+68
@@ -0,0 +1,68 @@
|
||||
<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:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout"
|
||||
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.SubmissionWizard.Steps.Layout.LayoutSelectionStepView"
|
||||
x:DataType="layout:LayoutSelectionStepViewModel">
|
||||
<UserControl.Resources>
|
||||
<VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,20,20">
|
||||
<VisualBrush.Visual>
|
||||
<Canvas Width="20" Height="20">
|
||||
<Rectangle Width="10" Height="10" Fill="Black" Opacity="0.15" />
|
||||
<Rectangle Width="10" Height="10" Canvas.Left="10" />
|
||||
<Rectangle Width="10" Height="10" Canvas.Top="10" />
|
||||
<Rectangle Width="10" Height="10" Canvas.Left="10" Canvas.Top="10" Fill="Black" Opacity="0.15" />
|
||||
</Canvas>
|
||||
</VisualBrush.Visual>
|
||||
</VisualBrush>
|
||||
</UserControl.Resources>
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}" TextWrapping="Wrap">
|
||||
Layout selection
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Please select the layout you want to share by either selecting a device or browsing for the layout file
|
||||
</TextBlock>
|
||||
|
||||
<ComboBox ItemsSource="{CompiledBinding Devices}" SelectedItem="{CompiledBinding SelectedDevice}"
|
||||
Width="460"
|
||||
VerticalContentAlignment="Center"
|
||||
Height="50"
|
||||
Margin="0 15"
|
||||
PlaceholderText="Select the layout of a device">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="core:ArtemisDevice">
|
||||
<Grid RowDefinitions="Auto,*" ColumnDefinitions="Auto,*">
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{CompiledBinding RgbDevice.DeviceInfo.Model}" TextTrimming="CharacterEllipsis"></TextBlock>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{CompiledBinding RgbDevice.DeviceInfo.Manufacturer}" TextTrimming="CharacterEllipsis" Classes="subtitle"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Button Command="{CompiledBinding BrowseLayout}">Browse layout</Button>
|
||||
|
||||
<TextBlock Text="{CompiledBinding Layout.FilePath, FallbackValue=''}" Margin="0 10 0 5" ></TextBlock>
|
||||
</StackPanel>
|
||||
<Border Grid.Row="1"
|
||||
Classes="card"
|
||||
Padding="0"
|
||||
ClipToBounds="True"
|
||||
IsVisible="{CompiledBinding LayoutImage, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Background="{DynamicResource CheckerboardBrush}">
|
||||
<Image Source="{CompiledBinding LayoutImage}" Margin="25"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
|
||||
public partial class LayoutSelectionStepView : ReactiveUserControl<LayoutSelectionStepViewModel>
|
||||
{
|
||||
public LayoutSelectionStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Material.Icons;
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
|
||||
public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
[Notify] private ArtemisDevice? _selectedDevice;
|
||||
[Notify] private ArtemisLayout? _layout;
|
||||
[Notify] private Bitmap? _layoutImage;
|
||||
|
||||
public LayoutSelectionStepViewModel(IDeviceService deviceService, IWindowService windowService)
|
||||
{
|
||||
_windowService = windowService;
|
||||
Devices = new ObservableCollection<ArtemisDevice>(
|
||||
deviceService.Devices
|
||||
.Where(d => d.Layout != null && d.Layout.IsValid)
|
||||
.DistinctBy(d => d.Layout?.FilePath)
|
||||
.OrderBy(d => d.RgbDevice.DeviceInfo.Model)
|
||||
);
|
||||
|
||||
Continue = ReactiveCommand.Create(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);
|
||||
}
|
||||
|
||||
public ObservableCollection<ArtemisDevice> Devices { get; }
|
||||
|
||||
public async Task BrowseLayout()
|
||||
{
|
||||
string[]? selected = await _windowService.CreateOpenFileDialog().HavingFilter(f => f.WithExtension("xml").WithName("Artemis Layout")).ShowAsync();
|
||||
if (selected == null || selected.Length != 1)
|
||||
return;
|
||||
|
||||
ArtemisLayout layout = new(selected[0], LayoutSource.User);
|
||||
if (!layout.IsValid)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Invalid layout file", "The selected file does not appear to be a valid RGB.NET layout file", cancel: null);
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedDevice = null;
|
||||
Layout = layout;
|
||||
}
|
||||
|
||||
private void CreatePreviewDevice(ArtemisLayout? layout)
|
||||
{
|
||||
if (layout == null)
|
||||
{
|
||||
LayoutImage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
LayoutImage = layout.RenderLayout(true);
|
||||
Layout = layout;
|
||||
}
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
if (Layout == null)
|
||||
return;
|
||||
|
||||
State.EntrySource = 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";
|
||||
|
||||
// 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);
|
||||
|
||||
MemoryStream output = new();
|
||||
using SKBitmap? sourceBitmap = SKBitmap.Decode(stream);
|
||||
int sourceWidth = sourceBitmap.Width;
|
||||
int sourceHeight = sourceBitmap.Height;
|
||||
float scale = Math.Min((float) 128 / sourceWidth, (float) 128 / 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);
|
||||
|
||||
using (SKBitmap? scaleBitmap = sourceBitmap.Resize(scaledDimensions, SKFilterQuality.High))
|
||||
using (SKBitmap targetBitmap = new(128, 128))
|
||||
using (SKCanvas canvas = new(targetBitmap))
|
||||
{
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
canvas.DrawBitmap(scaleBitmap, offset.X, offset.Y);
|
||||
targetBitmap.Encode(output, SKEncodedImageFormat.Png, 100);
|
||||
output.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
State.Icon?.Dispose();
|
||||
State.Icon = output;
|
||||
State.ChangeScreen<SpecificationsStepViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Screens.Workshop.Entries;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using DynamicData;
|
||||
@@ -38,6 +39,7 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
|
||||
switch (State.EntryType)
|
||||
{
|
||||
case EntryType.Layout:
|
||||
State.ChangeScreen<LayoutSelectionStepViewModel>();
|
||||
break;
|
||||
case EntryType.Plugin:
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
@@ -56,6 +57,8 @@ public class SubmissionWizardState
|
||||
{
|
||||
if (EntryType == EntryType.Profile)
|
||||
ChangeScreen<ProfileSelectionStepViewModel>();
|
||||
else if (EntryType == EntryType.Layout)
|
||||
ChangeScreen<LayoutSelectionStepViewModel>();
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user