1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Layer adapter hints - Ported UI

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

View File

@ -14,6 +14,7 @@
<entry key="Artemis.UI.Avalonia/Screens/Sidebar/Views/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" /> <entry key="Artemis.UI.Avalonia/Screens/Sidebar/Views/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
<entry key="Artemis.UI.Avalonia/Screens/SidebarView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" /> <entry key="Artemis.UI.Avalonia/Screens/SidebarView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
<entry key="Artemis.UI.Avalonia/Views/MainWindow.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" /> <entry key="Artemis.UI.Avalonia/Views/MainWindow.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
<entry key="Artemis.UI.Shared/Controls/EnumComboBox.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI.Shared/Styles/Border.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI.Shared/Styles/Border.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI.Windows/App.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI.Windows/App.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
@ -27,7 +28,12 @@
<entry key="Artemis.UI/Screens/Plugins/PluginFeatureView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Plugins/PluginFeatureView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Plugins/PluginSettingsView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Plugins/PluginSettingsView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/Dialogs/AdaptionHints/CategoryAdaptionHintView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/Dialogs/AdaptionHints/DeviceAdaptionHintView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/Dialogs/AdaptionHints/KeyboardSectionAdaptionHintView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/Dialogs/LayerHintsDialogView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
@ -40,6 +46,7 @@
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Windows/EffectConfigurationWindowView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Windows/EffectConfigurationWindowView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
@ -47,6 +54,7 @@
<entry key="Artemis.UI/Screens/Root/SplashView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Root/SplashView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/SidebarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/Sidebar/SidebarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />

View File

@ -0,0 +1,19 @@
using System;
namespace Artemis.Core;
/// <summary>
/// Provides data for layer adapter hint events.
/// </summary>
public class LayerAdapterHintEventArgs : EventArgs
{
internal LayerAdapterHintEventArgs(IAdaptionHint adaptionHint)
{
AdaptionHint = adaptionHint;
}
/// <summary>
/// Gets the layer adaption hint this event is related to
/// </summary>
public IAdaptionHint AdaptionHint { get; }
}

View File

@ -7,8 +7,13 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Represents a hint that adapts layers to a certain category of devices /// Represents a hint that adapts layers to a certain category of devices
/// </summary> /// </summary>
public class CategoryAdaptionHint : IAdaptionHint public class CategoryAdaptionHint : CorePropertyChanged, IAdaptionHint
{ {
private DeviceCategory _category;
private int _skip;
private bool _limitAmount;
private int _amount;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="CategoryAdaptionHint" /> class /// Creates a new instance of the <see cref="CategoryAdaptionHint" /> class
/// </summary> /// </summary>
@ -27,22 +32,38 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets or sets the category of devices LEDs will be applied to /// Gets or sets the category of devices LEDs will be applied to
/// </summary> /// </summary>
public DeviceCategory Category { get; set; } public DeviceCategory Category
{
get => _category;
set => SetAndNotify(ref _category, value);
}
/// <summary> /// <summary>
/// Gets or sets the amount of devices to skip /// Gets or sets the amount of devices to skip
/// </summary> /// </summary>
public int Skip { get; set; } public int Skip
{
get => _skip;
set => SetAndNotify(ref _skip, value);
}
/// <summary> /// <summary>
/// Gets or sets a boolean indicating whether a limited amount of devices should be used /// Gets or sets a boolean indicating whether a limited amount of devices should be used
/// </summary> /// </summary>
public bool LimitAmount { get; set; } public bool LimitAmount
{
get => _limitAmount;
set => SetAndNotify(ref _limitAmount, value);
}
/// <summary> /// <summary>
/// Gets or sets the amount of devices to limit to if <see cref="LimitAmount" /> is <see langword="true" /> /// Gets or sets the amount of devices to limit to if <see cref="LimitAmount" /> is <see langword="true" />
/// </summary> /// </summary>
public int Amount { get; set; } public int Amount
{
get => _amount;
set => SetAndNotify(ref _amount, value);
}
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()

View File

@ -8,8 +8,13 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Represents a hint that adapts layers to a certain type of devices /// Represents a hint that adapts layers to a certain type of devices
/// </summary> /// </summary>
public class DeviceAdaptionHint : IAdaptionHint public class DeviceAdaptionHint : CorePropertyChanged, IAdaptionHint
{ {
private RGBDeviceType _deviceType;
private int _skip;
private bool _limitAmount;
private int _amount;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="DeviceAdaptionHint" /> class /// Creates a new instance of the <see cref="DeviceAdaptionHint" /> class
/// </summary> /// </summary>
@ -28,22 +33,38 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets or sets the type of devices LEDs will be applied to /// Gets or sets the type of devices LEDs will be applied to
/// </summary> /// </summary>
public RGBDeviceType DeviceType { get; set; } public RGBDeviceType DeviceType
{
get => _deviceType;
set => SetAndNotify(ref _deviceType, value);
}
/// <summary> /// <summary>
/// Gets or sets the amount of devices to skip /// Gets or sets the amount of devices to skip
/// </summary> /// </summary>
public int Skip { get; set; } public int Skip
{
get => _skip;
set => SetAndNotify(ref _skip, value);
}
/// <summary> /// <summary>
/// Gets or sets a boolean indicating whether a limited amount of devices should be used /// Gets or sets a boolean indicating whether a limited amount of devices should be used
/// </summary> /// </summary>
public bool LimitAmount { get; set; } public bool LimitAmount
{
get => _limitAmount;
set => SetAndNotify(ref _limitAmount, value);
}
/// <summary> /// <summary>
/// Gets or sets the amount of devices to limit to if <see cref="LimitAmount" /> is <see langword="true" /> /// Gets or sets the amount of devices to limit to if <see cref="LimitAmount" /> is <see langword="true" />
/// </summary> /// </summary>
public int Amount { get; set; } public int Amount
{
get => _amount;
set => SetAndNotify(ref _amount, value);
}
#region Implementation of IAdaptionHint #region Implementation of IAdaptionHint

View File

@ -9,7 +9,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Represents a hint that adapts layers to a certain region of keyboards /// Represents a hint that adapts layers to a certain region of keyboards
/// </summary> /// </summary>
public class KeyboardSectionAdaptionHint : IAdaptionHint public class KeyboardSectionAdaptionHint : CorePropertyChanged, IAdaptionHint
{ {
private static readonly Dictionary<KeyboardSection, List<LedId>> RegionLedIds = new() private static readonly Dictionary<KeyboardSection, List<LedId>> RegionLedIds = new()
{ {
@ -18,6 +18,8 @@ namespace Artemis.Core
{KeyboardSection.Extra, Enum.GetValues<LedId>().Where(l => l >= LedId.Keyboard_Custom1 && l <= LedId.Keyboard_Custom64).ToList()} {KeyboardSection.Extra, Enum.GetValues<LedId>().Where(l => l >= LedId.Keyboard_Custom1 && l <= LedId.Keyboard_Custom64).ToList()}
}; };
private KeyboardSection _section;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="KeyboardSectionAdaptionHint" /> class /// Creates a new instance of the <see cref="KeyboardSectionAdaptionHint" /> class
/// </summary> /// </summary>
@ -33,7 +35,11 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets or sets the section this hint will apply LEDs to /// Gets or sets the section this hint will apply LEDs to
/// </summary> /// </summary>
public KeyboardSection Section { get; set; } public KeyboardSection Section
{
get => _section;
set => SetAndNotify(ref _section, value);
}
#region Implementation of IAdaptionHint #region Implementation of IAdaptionHint

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.AdaptionHints; using Artemis.Storage.Entities.Profile.AdaptionHints;
@ -12,10 +13,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class LayerAdapter : IStorageModel public class LayerAdapter : IStorageModel
{ {
private readonly List<IAdaptionHint> _adaptionHints;
internal LayerAdapter(Layer layer) internal LayerAdapter(Layer layer)
{ {
_adaptionHints = new List<IAdaptionHint>();
Layer = layer; Layer = layer;
AdaptionHints = new List<IAdaptionHint>(); AdaptionHints = new ReadOnlyCollection<IAdaptionHint>(_adaptionHints);
} }
/// <summary> /// <summary>
@ -26,7 +30,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets or sets a list containing the adaption hints used by this adapter /// Gets or sets a list containing the adaption hints used by this adapter
/// </summary> /// </summary>
public List<IAdaptionHint> AdaptionHints { get; set; } public ReadOnlyCollection<IAdaptionHint> AdaptionHints { get; set; }
/// <summary> /// <summary>
/// Modifies the layer, adapting it to the provided <paramref name="devices" /> /// Modifies the layer, adapting it to the provided <paramref name="devices" />
@ -73,7 +77,7 @@ namespace Artemis.Core
if (devices.All(DoesLayerCoverDevice)) if (devices.All(DoesLayerCoverDevice))
{ {
DeviceAdaptionHint hint = new() {DeviceType = RGBDeviceType.All}; DeviceAdaptionHint hint = new() {DeviceType = RGBDeviceType.All};
AdaptionHints.Add(hint); Add(hint);
newHints.Add(hint); newHints.Add(hint);
} }
else else
@ -88,7 +92,7 @@ namespace Artemis.Core
if (DoesLayerCoverDevice(device)) if (DoesLayerCoverDevice(device))
{ {
DeviceAdaptionHint hint = new() {DeviceType = device.DeviceType}; DeviceAdaptionHint hint = new() {DeviceType = device.DeviceType};
AdaptionHints.Add(hint); Add(hint);
newHints.Add(hint); newHints.Add(hint);
} }
} }
@ -103,7 +107,7 @@ namespace Artemis.Core
if (categoryDevices.Any() && categoryDevices.All(DoesLayerCoverDevice)) if (categoryDevices.Any() && categoryDevices.All(DoesLayerCoverDevice))
{ {
CategoryAdaptionHint hint = new() {Category = deviceCategory}; CategoryAdaptionHint hint = new() {Category = deviceCategory};
AdaptionHints.Add(hint); Add(hint);
newHints.Add(hint); newHints.Add(hint);
} }
} }
@ -117,25 +121,57 @@ namespace Artemis.Core
return device.Leds.All(l => Layer.Leds.Contains(l)); return device.Leds.All(l => Layer.Leds.Contains(l));
} }
/// <summary>
/// Adds an adaption hint to the adapter.
/// </summary>
/// <param name="adaptionHint">The adaption hint to add.</param>
public void Add(IAdaptionHint adaptionHint)
{
if (_adaptionHints.Contains(adaptionHint))
return;
_adaptionHints.Add(adaptionHint);
AdapterHintAdded?.Invoke(this, new LayerAdapterHintEventArgs(adaptionHint));
}
/// <summary>
/// Removes the first occurrence of a specific adaption hint from the adapter.
/// </summary>
/// <param name="adaptionHint">The adaption hint to remove.</param>
public void Remove(IAdaptionHint adaptionHint)
{
if (_adaptionHints.Remove(adaptionHint))
AdapterHintRemoved?.Invoke(this, new LayerAdapterHintEventArgs(adaptionHint));
}
/// <summary>
/// Removes all adaption hints from the adapter.
/// </summary>
public void Clear()
{
while (_adaptionHints.Any())
Remove(_adaptionHints.First());
}
#region Implementation of IStorageModel #region Implementation of IStorageModel
/// <inheritdoc /> /// <inheritdoc />
public void Load() public void Load()
{ {
AdaptionHints.Clear(); _adaptionHints.Clear();
// Kind of meh. // Kind of meh.
// This leaves the adapter responsible for finding the right hint for the right entity, but it's gotta be done somewhere.. // This leaves the adapter responsible for finding the right hint for the right entity, but it's gotta be done somewhere..
foreach (IAdaptionHintEntity hintEntity in Layer.LayerEntity.AdaptionHints) foreach (IAdaptionHintEntity hintEntity in Layer.LayerEntity.AdaptionHints)
switch (hintEntity) switch (hintEntity)
{ {
case DeviceAdaptionHintEntity entity: case DeviceAdaptionHintEntity entity:
AdaptionHints.Add(new DeviceAdaptionHint(entity)); Add(new DeviceAdaptionHint(entity));
break; break;
case CategoryAdaptionHintEntity entity: case CategoryAdaptionHintEntity entity:
AdaptionHints.Add(new CategoryAdaptionHint(entity)); Add(new CategoryAdaptionHint(entity));
break; break;
case KeyboardSectionAdaptionHintEntity entity: case KeyboardSectionAdaptionHintEntity entity:
AdaptionHints.Add(new KeyboardSectionAdaptionHint(entity)); Add(new KeyboardSectionAdaptionHint(entity));
break; break;
} }
} }
@ -149,5 +185,15 @@ namespace Artemis.Core
} }
#endregion #endregion
/// <summary>
/// Occurs whenever a new adapter hint is added to the adapter.
/// </summary>
public event EventHandler<LayerAdapterHintEventArgs>? AdapterHintAdded;
/// <summary>
/// Occurs whenever an adapter hint is removed from the adapter.
/// </summary>
public event EventHandler<LayerAdapterHintEventArgs>? AdapterHintRemoved;
} }
} }

View File

@ -7,7 +7,7 @@
<ComboBox x:Name="EnumComboBox" HorizontalAlignment="Stretch"> <ComboBox x:Name="EnumComboBox" HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding [0]}" /> <TextBlock Text="{Binding [1]}" />
</DataTemplate> </DataTemplate>
</ComboBox.ItemTemplate> </ComboBox.ItemTemplate>
</ComboBox> </ComboBox>

View File

@ -7,6 +7,7 @@ using Artemis.UI.Screens.Plugins;
using Artemis.UI.Screens.ProfileEditor; using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes; using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
using Artemis.UI.Screens.ProfileEditor.ProfileTree; using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs.AdaptionHints;
using Artemis.UI.Screens.ProfileEditor.Properties; using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding; using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
@ -114,4 +115,11 @@ public interface IConditionVmFactory : IVmFactory
PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition); PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition);
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition); StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition); EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
}
public interface ILayerHintVmFactory : IVmFactory
{
CategoryAdaptionHintViewModel CategoryAdaptionHintViewModel(CategoryAdaptionHint adaptionHint);
DeviceAdaptionHintViewModel DeviceAdaptionHintViewModel(DeviceAdaptionHint adaptionHint);
KeyboardSectionAdaptionHintViewModel KeyboardSectionAdaptionHintViewModel(KeyboardSectionAdaptionHint adaptionHint);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -112,6 +112,11 @@
<avalonia:MaterialIcon Kind="LayersPlus" /> <avalonia:MaterialIcon Kind="LayersPlus" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Open adaption hints" Command="{CompiledBinding OpenAdaptionHints}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Magic" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="-" /> <MenuItem Header="-" />
<MenuItem Header="Duplicate" Command="{CompiledBinding Duplicate}" InputGesture="Ctrl+D"> <MenuItem Header="Duplicate" Command="{CompiledBinding Duplicate}" InputGesture="Ctrl+D">
<MenuItem.Icon> <MenuItem.Icon>

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor; using Artemis.UI.Shared.Services.ProfileEditor;
@ -46,6 +47,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
AddLayer = ReactiveCommand.Create(ExecuteAddLayer); AddLayer = ReactiveCommand.Create(ExecuteAddLayer);
AddFolder = ReactiveCommand.Create(ExecuteAddFolder); AddFolder = ReactiveCommand.Create(ExecuteAddFolder);
OpenAdaptionHints = ReactiveCommand.CreateFromTask(ExecuteOpenAdaptionHints, this.WhenAnyValue(vm => vm.ProfileElement).Select(p => p is Layer));
Rename = ReactiveCommand.Create(ExecuteRename); Rename = ReactiveCommand.Create(ExecuteRename);
Delete = ReactiveCommand.Create(ExecuteDelete); Delete = ReactiveCommand.Create(ExecuteDelete);
Duplicate = ReactiveCommand.CreateFromTask(ExecuteDuplicate); Duplicate = ReactiveCommand.CreateFromTask(ExecuteDuplicate);
@ -97,6 +99,7 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
public ReactiveCommand<Unit, Unit> AddLayer { get; } public ReactiveCommand<Unit, Unit> AddLayer { get; }
public ReactiveCommand<Unit, Unit> AddFolder { get; } public ReactiveCommand<Unit, Unit> AddFolder { get; }
public ReactiveCommand<Unit, Unit> OpenAdaptionHints { get; }
public ReactiveCommand<Unit, Unit> Rename { get; } public ReactiveCommand<Unit, Unit> Rename { get; }
public ReactiveCommand<Unit, Unit> Duplicate { get; } public ReactiveCommand<Unit, Unit> Duplicate { get; }
public ReactiveCommand<Unit, Unit> Copy { get; } public ReactiveCommand<Unit, Unit> Copy { get; }
@ -247,6 +250,14 @@ public abstract class TreeItemViewModel : ActivatableViewModelBase
if (ProfileElement != null) if (ProfileElement != null)
ProfileEditorService.CreateAndAddLayer(ProfileElement); 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) private async void UpdateCanPaste(bool isFlyoutOpen)
{ {