Layer adapter hints - Ported UI

This commit is contained in:
Robert committed 2022-05-24 23:24:49 +02:00
1 parent 850346ccd2
commit 720bebaf35
23 files changed
+606 -22

No files matched your search

@@ -0,0 +1,22 @@
using System.Reactive;
using Artemis.Core;
using Artemis.UI.Shared;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public abstract class AdaptionHintViewModelBase : ViewModelBase
{
protected AdaptionHintViewModelBase(IAdaptionHint adaptionHint)
{
AdaptionHint = adaptionHint;
Remove = ReactiveCommand.Create(ExecuteRemove);
}
public ReactiveCommand<Unit, Unit> Remove { get; }
public IAdaptionHint AdaptionHint { get; }
private void ExecuteRemove()
{
}
}
@@ -0,0 +1,52 @@
<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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:adaptionHints="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints"
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.ProfileEditor.ProfileTree.Dialogs.AdaptionHints.CategoryAdaptionHintView"
x:DataType="adaptionHints:CategoryAdaptionHintViewModel">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
<StackPanel Grid.ColumnSpan="2" Margin="0 0 0 5">
<TextBlock Classes="h5">Category hint</TextBlock>
<TextBlock Classes="subtitle">Applies the layer to devices of a certain category</TextBlock>
</StackPanel>
<Button Grid.Row="0"
Grid.Column="1"
Classes="icon-button"
VerticalAlignment="Top"
HorizontalAlignment="Right"
ToolTip.Tip="Remove hint"
Command="{CompiledBinding Remove}">
<avalonia:MaterialIcon Kind="Delete" />
</Button>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Spacing="5">
<shared:EnumComboBox Value="{CompiledBinding CategoryAdaptionHint.Category}" Width="130" Margin="0 0 10 0" />
<TextBlock VerticalAlignment="Center">Skip</TextBlock>
<controls:NumberBox HorizontalAlignment="Stretch"
Value="{CompiledBinding CategoryAdaptionHint.Skip}"
Minimum="0" />
<TextBlock VerticalAlignment="Center">device(s)</TextBlock>
<TextBlock VerticalAlignment="Center">Take</TextBlock>
<controls:NumberBox IsEnabled="{CompiledBinding CategoryAdaptionHint.LimitAmount}"
HorizontalAlignment="Stretch"
Value="{CompiledBinding CategoryAdaptionHint.Amount}"
Minimum="1" />
<TextBlock VerticalAlignment="Center">device(s)</TextBlock>
</StackPanel>
<CheckBox Grid.Row="1"
Grid.Column="1"
Content="Take all devices"
VerticalAlignment="Center"
HorizontalAlignment="Right"
IsChecked="{CompiledBinding !CategoryAdaptionHint.LimitAmount}" />
</Grid>
</UserControl>
@@ -0,0 +1,18 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public partial class CategoryAdaptionHintView : UserControl
{
public CategoryAdaptionHintView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public class CategoryAdaptionHintViewModel : AdaptionHintViewModelBase
{
public CategoryAdaptionHintViewModel(CategoryAdaptionHint adaptionHint) : base(adaptionHint)
{
CategoryAdaptionHint = adaptionHint;
}
public CategoryAdaptionHint CategoryAdaptionHint { get; }
}
@@ -0,0 +1,52 @@
<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:adaptionHints="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
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.ProfileEditor.ProfileTree.Dialogs.AdaptionHints.DeviceAdaptionHintView"
x:DataType="adaptionHints:DeviceAdaptionHintViewModel">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
<StackPanel Grid.ColumnSpan="2" Margin="0 0 0 5">
<TextBlock Classes="h5">Device type hint</TextBlock>
<TextBlock Classes="subtitle">Applies the layer to devices of a certain type</TextBlock>
</StackPanel>
<Button Grid.Row="0"
Grid.Column="1"
Classes="icon-button"
VerticalAlignment="Top"
HorizontalAlignment="Right"
ToolTip.Tip="Remove hint"
Command="{CompiledBinding Remove}">
<avalonia:MaterialIcon Kind="Delete" />
</Button>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Spacing="5">
<shared:EnumComboBox Value="{CompiledBinding DeviceAdaptionHint.DeviceType}" Width="130" Margin="0 0 10 0" />
<TextBlock VerticalAlignment="Center">Skip</TextBlock>
<controls:NumberBox HorizontalAlignment="Stretch"
Value="{CompiledBinding DeviceAdaptionHint.Skip}"
Minimum="0" />
<TextBlock VerticalAlignment="Center">device(s)</TextBlock>
<TextBlock VerticalAlignment="Center">Take</TextBlock>
<controls:NumberBox IsEnabled="{CompiledBinding DeviceAdaptionHint.LimitAmount}"
HorizontalAlignment="Stretch"
Value="{CompiledBinding DeviceAdaptionHint.Amount}"
Minimum="1" />
<TextBlock VerticalAlignment="Center">device(s)</TextBlock>
</StackPanel>
<CheckBox Grid.Row="1"
Grid.Column="1"
Content="Take all devices"
VerticalAlignment="Center"
HorizontalAlignment="Right"
IsChecked="{CompiledBinding !DeviceAdaptionHint.LimitAmount}" />
</Grid>
</UserControl>
@@ -0,0 +1,18 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public partial class DeviceAdaptionHintView : UserControl
{
public DeviceAdaptionHintView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public class DeviceAdaptionHintViewModel : AdaptionHintViewModelBase
{
public DeviceAdaptionHintViewModel(DeviceAdaptionHint adaptionHint) : base(adaptionHint)
{
DeviceAdaptionHint = adaptionHint;
}
public DeviceAdaptionHint DeviceAdaptionHint { get; }
}
@@ -0,0 +1,32 @@
<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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:adaptionHints="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints.KeyboardSectionAdaptionHintView"
x:DataType="adaptionHints:KeyboardSectionAdaptionHintViewModel">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
<StackPanel Grid.ColumnSpan="2" Margin="0 0 0 5">
<TextBlock Classes="h5">Keyboard section hint</TextBlock>
<TextBlock Classes="subtitle">Applies the layer to a section of all keyboards</TextBlock>
</StackPanel>
<Button Grid.Row="0"
Grid.Column="1"
Classes="icon-button"
VerticalAlignment="Top"
HorizontalAlignment="Right"
ToolTip.Tip="Remove hint"
Command="{CompiledBinding Remove}">
<avalonia:MaterialIcon Kind="Delete" />
</Button>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Spacing="5">
<shared:EnumComboBox Value="{CompiledBinding KeyboardSectionAdaptionHint.Section}" Width="130" Margin="0 0 10 0" />
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,18 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public partial class KeyboardSectionAdaptionHintView : UserControl
{
public KeyboardSectionAdaptionHintView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -0,0 +1,13 @@
using Artemis.Core;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
public class KeyboardSectionAdaptionHintViewModel : AdaptionHintViewModelBase
{
public KeyboardSectionAdaptionHintViewModel(KeyboardSectionAdaptionHint adaptionHint) : base(adaptionHint)
{
KeyboardSectionAdaptionHint = adaptionHint;
}
public KeyboardSectionAdaptionHint KeyboardSectionAdaptionHint { get; }
}
@@ -0,0 +1,81 @@
<controls:CoreWindow 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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:dialogs="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.LayerHintsDialogView"
x:DataType="dialogs:LayerHintsDialogViewModel"
Title="Artemis | Adaption hints"
Width="750"
Height="800">
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
<StackPanel Grid.Row="0" Orientation="Horizontal">
<avalonia:MaterialIcon Kind="{CompiledBinding Layer.LayerBrush.Descriptor.Icon}" Width="24" Height="24" Margin="0 0 5 10"/>
<TextBlock Classes="h4" Text="{CompiledBinding Layer.Name}" TextWrapping="Wrap"/>
</StackPanel>
<controls:HyperlinkButton Grid.Row="0"
Grid.Column="1"
VerticalAlignment="Top"
NavigateUri="https://wiki.artemis-rgb.com/guides/user/profiles/layers/adaption-hints">
Learn more about adaption hints
</controls:HyperlinkButton>
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Classes="subtitle" TextWrapping="Wrap">
Add hints below to help decide where to place this layer when the profile is imported.
</TextBlock>
</Grid>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" Margin="0 15">
<ItemsControl Items="{CompiledBinding AdaptionHints}" Classes="adaption-hints">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Classes="card-condensed">
<ContentControl Content="{Binding}"></ContentControl>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Grid Grid.Row="2" ColumnDefinitions="*,Auto">
<Button Grid.Row="0" Grid.Column="0" Command="{Binding AutoDetermineHints}">Auto-determine hints</Button>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Spacing="5">
<controls:DropDownButton>
<controls:DropDownButton.Flyout>
<MenuFlyout Placement="Top">
<MenuItem Header="Category hint" Command="{Binding AddCategoryHint}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Desk" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Device type hint" Command="{Binding AddDeviceHint}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Devices" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Keyboard-section hint" Command="{Binding AddKeyboardSectionHint}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Keyboard" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</controls:DropDownButton.Flyout>
Add hint
</controls:DropDownButton>
<Button Command="{Binding Finish}">Close</Button>
</StackPanel>
</Grid>
</Grid>
</controls:CoreWindow>
@@ -0,0 +1,22 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
public partial class LayerHintsDialogView : ReactiveCoreWindow<LayerHintsDialogViewModel>
{
public LayerHintsDialogView()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
using Artemis.UI.Shared;
using Avalonia.Controls.Mixins;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
public class LayerHintsDialogViewModel : DialogViewModelBase<bool>
{
private readonly IRgbService _rgbService;
private readonly ILayerHintVmFactory _vmFactory;
public LayerHintsDialogViewModel(Layer layer, IRgbService rgbService, ILayerHintVmFactory vmFactory)
{
_rgbService = rgbService;
_vmFactory = vmFactory;
Layer = layer;
AdaptionHints = new ObservableCollection<AdaptionHintViewModelBase>();
this.WhenActivated(d =>
{
Observable.FromEventPattern<LayerAdapterHintEventArgs>(x => layer.Adapter.AdapterHintAdded += x, x => layer.Adapter.AdapterHintAdded -= x)
.Subscribe(c => AdaptionHints.Add(CreateHintViewModel(c.EventArgs.AdaptionHint)))
.DisposeWith(d);
Observable.FromEventPattern<LayerAdapterHintEventArgs>(x => layer.Adapter.AdapterHintRemoved += x, x => layer.Adapter.AdapterHintRemoved -= x)
.Subscribe(c => AdaptionHints.Remove(AdaptionHints.FirstOrDefault(h => h.AdaptionHint == c.EventArgs.AdaptionHint)!))
.DisposeWith(d);
AdaptionHints.AddRange(Layer.Adapter.AdaptionHints.Select(CreateHintViewModel));
});
}
public Layer Layer { get; }
public ObservableCollection<AdaptionHintViewModelBase> AdaptionHints { get; }
public void Finish()
{
Close(true);
}
public void AutoDetermineHints()
{
Layer.Adapter.DetermineHints(_rgbService.EnabledDevices);
}
public void AddCategoryHint()
{
Layer.Adapter.Add(new CategoryAdaptionHint());
}
public void AddDeviceHint()
{
Layer.Adapter.Add(new DeviceAdaptionHint());
}
public void AddKeyboardSectionHint()
{
Layer.Adapter.Add(new KeyboardSectionAdaptionHint());
}
public void RemoveAdaptionHint(IAdaptionHint hint)
{
Layer.Adapter.Remove(hint);
}
private AdaptionHintViewModelBase CreateHintViewModel(IAdaptionHint hint)
{
return hint switch
{
CategoryAdaptionHint categoryAdaptionHint => _vmFactory.CategoryAdaptionHintViewModel(categoryAdaptionHint),
DeviceAdaptionHint deviceAdaptionHint => _vmFactory.DeviceAdaptionHintViewModel(deviceAdaptionHint),
KeyboardSectionAdaptionHint keyboardSectionAdaptionHint => _vmFactory.KeyboardSectionAdaptionHintViewModel(keyboardSectionAdaptionHint),
_ => throw new ArgumentOutOfRangeException(nameof(hint))
};
}
}
@@ -112,6 +112,11 @@
<avalonia:MaterialIcon Kind="LayersPlus" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Open adaption hints" Command="{CompiledBinding OpenAdaptionHints}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Magic" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="-" />
<MenuItem Header="Duplicate" Command="{CompiledBinding Duplicate}" InputGesture="Ctrl+D">
<MenuItem.Icon>
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
@@ -46,6 +47,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
AddLayer = ReactiveCommand.Create(ExecuteAddLayer);
AddFolder = ReactiveCommand.Create(ExecuteAddFolder);
OpenAdaptionHints = ReactiveCommand.CreateFromTask(ExecuteOpenAdaptionHints, this.WhenAnyValue(vm => vm.ProfileElement).Select(p => p is Layer));
Rename = ReactiveCommand.Create(ExecuteRename);
Delete = ReactiveCommand.Create(ExecuteDelete);
Duplicate = ReactiveCommand.CreateFromTask(ExecuteDuplicate);
@@ -97,6 +99,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
public ReactiveCommand<Unit, Unit> AddLayer { get; }
public ReactiveCommand<Unit, Unit> AddFolder { get; }
public ReactiveCommand<Unit, Unit> OpenAdaptionHints { get; }
public ReactiveCommand<Unit, Unit> Rename { get; }
public ReactiveCommand<Unit, Unit> Duplicate { get; }
public ReactiveCommand<Unit, Unit> Copy { get; }
@@ -247,6 +250,14 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
if (ProfileElement != null)
ProfileEditorService.CreateAndAddLayer(ProfileElement);
}
private async Task ExecuteOpenAdaptionHints()
{
if (ProfileElement is not Layer layer)
return;
await _windowService.ShowDialogAsync<LayerHintsDialogViewModel, bool>(("layer", layer));
}
private async void UpdateCanPaste(bool isFlyoutOpen)
{