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

Device properties - Implemented LED mappings tab

Device properties - Tweak design
This commit is contained in:
Robert 2022-05-15 20:14:57 +02:00
parent 78479c0fa2
commit a06bc68046
21 changed files with 295 additions and 129 deletions

View File

@ -19,12 +19,21 @@
<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" />
<entry key="Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/MainWindow.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/MainWindow.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Device/DevicePropertiesView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Device/DeviceSettingsView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Device/Tabs/DeviceInfoTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Device/Tabs/DevicePropertiesTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.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/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/FolderTreeItemView.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" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/LayerEffectRenameView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/LayerEffectRenameView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.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/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
@ -38,6 +47,7 @@
<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" />
<entry key="Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/VisualScripting/NodeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Artemis.UI/Screens/VisualScripting/NodeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Avalonia/Artemis.UI/Styles/Artemis.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" /> <entry key="Avalonia/Artemis.UI/Styles/Artemis.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />

View File

@ -142,7 +142,10 @@ namespace Artemis.UI.Shared
Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height); Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height);
DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.HitTest(scaledPosition)); DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.HitTest(scaledPosition));
if (deviceVisualizerLed != null) if (deviceVisualizerLed != null)
OnLedClicked(new LedClickedEventArgs(deviceVisualizerLed.Led.Device, deviceVisualizerLed.Led)); {
OnLedClicked(new LedClickedEventArgs(deviceVisualizerLed.Led.Device, deviceVisualizerLed.Led, e));
e.Handled = true;
}
} }
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e) private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)

View File

@ -1,27 +1,33 @@
using System; using System;
using Artemis.Core; using Artemis.Core;
using Avalonia.Input;
namespace Artemis.UI.Shared.Events;
namespace Artemis.UI.Shared.Events
{
/// <summary> /// <summary>
/// Provides data on LED click events raised by the device visualizer /// Provides data on LED click events raised by the device visualizer
/// </summary> /// </summary>
public class LedClickedEventArgs : EventArgs public class LedClickedEventArgs : EventArgs
{ {
internal LedClickedEventArgs(ArtemisDevice device, ArtemisLed led) internal LedClickedEventArgs(ArtemisDevice device, ArtemisLed led, PointerReleasedEventArgs pointerReleasedEventArgs)
{ {
Device = device; Device = device;
Led = led; Led = led;
PointerReleasedEventArgs = pointerReleasedEventArgs;
} }
/// <summary> /// <summary>
/// The device that was clicked /// Gets the device that was clicked.
/// </summary> /// </summary>
public ArtemisDevice Device { get; set; } public ArtemisDevice Device { get; set; }
/// <summary> /// <summary>
/// The LED that was clicked /// Gets the LED that was clicked.
/// </summary> /// </summary>
public ArtemisLed Led { get; set; } public ArtemisLed Led { get; set; }
}
/// <summary>
/// Gets the original pointer released event arguments.
/// </summary>
public PointerReleasedEventArgs PointerReleasedEventArgs { get; }
} }

View File

@ -4,13 +4,17 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView" x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
x:DataType="device:DevicePropertiesViewModel"
Icon="/Assets/Images/Logo/application.ico" Icon="/Assets/Images/Logo/application.ico"
Title="Artemis | Device Properties" Title="Artemis | Device Properties"
Width="1250" Width="1250"
Height="900"> Height="900">
<controls1:CoreWindow.KeyBindings>
<KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelectedLeds}" />
</controls1:CoreWindow.KeyBindings>
<Grid ColumnDefinitions="*,0,1.5*"> <Grid ColumnDefinitions="*,0,1.5*">
<Grid.Background> <Grid.Background>
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25"> <VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25">
@ -24,38 +28,39 @@
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>
</Grid.Background> </Grid.Background>
<Grid Grid.Column="0" Name="DeviceDisplayGrid"> <Grid Grid.Column="0" Name="DeviceDisplayGrid" PointerReleased="DeviceDisplayGrid_OnPointerReleased">
<!-- No need to provide LEDs to highlight as LEDs are already physically highlighted --> <!-- No need to provide LEDs to highlight as LEDs are already physically highlighted -->
<shared:DeviceVisualizer Device="{Binding Device}" <shared:DeviceVisualizer Device="{CompiledBinding Device}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
ShowColors="True" ShowColors="True"
Margin="20" /> Margin="20"
LedClicked="DeviceVisualizer_OnLedClicked" />
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="15" Margin="15"
IsVisible="{Binding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"> IsVisible="{CompiledBinding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Classes="h5" Text="Device layout by " /> <TextBlock Classes="h5" Text="Device layout by " />
<TextBlock Classes="h5" FontWeight="Bold" Text="{Binding Device.Layout.RgbLayout.Author}" /> <TextBlock Classes="h5" FontWeight="Bold" Text="{CompiledBinding Device.Layout.RgbLayout.Author}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" />
<Border Grid.Column="2" Classes="card" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232"> <Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232">
<TabControl Items="{Binding Tabs}"> <TabControl Items="{CompiledBinding Tabs}">
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding DisplayName}" /> <TextBlock Text="{CompiledBinding DisplayName}" />
</DataTemplate> </DataTemplate>
</TabControl.ItemTemplate> </TabControl.ItemTemplate>
<TabControl.ContentTemplate> <TabControl.ContentTemplate>
<DataTemplate> <DataTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ContentControl Content="{Binding}" Margin="0 10 0 0" /> <ContentControl Content="{Binding}" Margin="0 12" />
</ScrollViewer> </ScrollViewer>
</DataTemplate> </DataTemplate>
</TabControl.ContentTemplate> </TabControl.ContentTemplate>

View File

@ -1,10 +1,13 @@
using System;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Events;
using Avalonia; using Avalonia;
using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device namespace Artemis.UI.Screens.Device;
{
public partial class DevicePropertiesView : ReactiveCoreWindow<DevicePropertiesViewModel> public class DevicePropertiesView : ReactiveCoreWindow<DevicePropertiesViewModel>
{ {
public DevicePropertiesView() public DevicePropertiesView()
{ {
@ -18,5 +21,20 @@ namespace Artemis.UI.Screens.Device
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void DeviceVisualizer_OnLedClicked(object? sender, LedClickedEventArgs e)
{
if (ViewModel == null)
return;
if (!e.PointerReleasedEventArgs.KeyModifiers.HasFlag(KeyModifiers.Shift))
ViewModel.SelectedLeds.Clear();
ViewModel.SelectedLeds.Add(e.Led);
}
private void DeviceDisplayGrid_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift))
ViewModel?.ClearSelectedLeds.Execute().Subscribe();
} }
} }

View File

@ -1,15 +1,22 @@
using System.Collections.ObjectModel; using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;
using SkiaSharp;
using ArtemisLed = Artemis.Core.ArtemisLed; using ArtemisLed = Artemis.Core.ArtemisLed;
namespace Artemis.UI.Screens.Device namespace Artemis.UI.Screens.Device
{ {
public class DevicePropertiesViewModel : DialogViewModelBase<object> public class DevicePropertiesViewModel : DialogViewModelBase<object>
{ {
public DevicePropertiesViewModel(ArtemisDevice device, IDeviceVmFactory deviceVmFactory) public DevicePropertiesViewModel(ArtemisDevice device, ICoreService coreService, IDeviceVmFactory deviceVmFactory)
{ {
Device = device; Device = device;
SelectedLeds = new ObservableCollection<ArtemisLed>(); SelectedLeds = new ObservableCollection<ArtemisLed>();
@ -20,10 +27,36 @@ namespace Artemis.UI.Screens.Device
if (Device.DeviceType == RGBDeviceType.Keyboard) if (Device.DeviceType == RGBDeviceType.Keyboard)
Tabs.Add(deviceVmFactory.InputMappingsTabViewModel(device, SelectedLeds)); Tabs.Add(deviceVmFactory.InputMappingsTabViewModel(device, SelectedLeds));
Tabs.Add(deviceVmFactory.DeviceLedsTabViewModel(device, SelectedLeds)); Tabs.Add(deviceVmFactory.DeviceLedsTabViewModel(device, SelectedLeds));
this.WhenActivated(d =>
{
coreService.FrameRendering += CoreServiceOnFrameRendering;
Disposable.Create(() => coreService.FrameRendering -= CoreServiceOnFrameRendering).DisposeWith(d);
});
ClearSelectedLeds = ReactiveCommand.Create(ExecuteClearSelectedLeds);
} }
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public ObservableCollection<ArtemisLed> SelectedLeds { get; } public ObservableCollection<ArtemisLed> SelectedLeds { get; }
public ObservableCollection<ActivatableViewModelBase> Tabs { get; } public ObservableCollection<ActivatableViewModelBase> Tabs { get; }
public ReactiveCommand<Unit, Unit> ClearSelectedLeds { get; }
private void ExecuteClearSelectedLeds()
{
SelectedLeds.Clear();
}
private void CoreServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
{
if (!SelectedLeds.Any())
return;
using SKPaint highlightPaint = new() {Color = SKColors.White};
using SKPaint dimPaint = new() {Color = new SKColor(0, 0, 0, 192)};
foreach (ArtemisLed artemisLed in Device.Leds)
e.Canvas.DrawRect(artemisLed.AbsoluteRectangle, SelectedLeds.Contains(artemisLed) ? highlightPaint : dimPaint);
}
} }
} }

View File

@ -1,9 +1,10 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device namespace Artemis.UI.Screens.Device
{ {
public partial class DeviceInfoTabView : UserControl public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewModel>
{ {
public DeviceInfoTabView() public DeviceInfoTabView()
{ {

View File

@ -1,9 +1,10 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device namespace Artemis.UI.Screens.Device
{ {
public partial class DeviceLedsTabView : UserControl public partial class DeviceLedsTabView : ReactiveUserControl<DeviceLedsTabViewModel>
{ {
public DeviceLedsTabView() public DeviceLedsTabView()
{ {

View File

@ -138,7 +138,12 @@
ClipValueToMinMax="True" /> ClipValueToMinMax="True" />
<CheckBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{Binding DisplayOnDevices}" Content="Show preview" VerticalAlignment="Center" /> <CheckBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{Binding DisplayOnDevices}" Content="Show preview" VerticalAlignment="Center" />
<controls:ColorPickerButton Grid.Row="3" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Right" Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}" /> <controls:ColorPickerButton Grid.Row="3"
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
ShowAcceptDismissButtons="False" />
</Grid> </Grid>
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -181,16 +186,13 @@
<!-- Buttons --> <!-- Buttons -->
<Grid Grid.Row="1" ColumnDefinitions="Auto,*"> <Grid Grid.Row="1" ColumnDefinitions="Auto,*">
<Button Grid.Column="0" <Button Grid.Column="0" Command="{Binding SelectPhysicalLayout}" ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout">
Classes="AppBarButton"
Command="{Binding SelectPhysicalLayout}"
ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout">
Restart setup Restart setup
</Button> </Button>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Classes="AppBarButton" IsCancel="True" Command="{Binding Reset}" Margin="0 0 5 0">Reset</Button> <Button IsCancel="True" Command="{Binding Reset}" Margin="0 0 5 0">Reset</Button>
<Button IsDefault="True" Command="{Binding Apply}">Apply</Button> <Button Classes="accent" IsDefault="True" Command="{Binding Apply}">Apply</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -240,9 +240,9 @@ namespace Artemis.UI.Screens.Device
HasRoomCategory = Device.Categories.Contains(DeviceCategory.Room); HasRoomCategory = Device.Categories.Contains(DeviceCategory.Room);
HasPeripheralsCategory = Device.Categories.Contains(DeviceCategory.Peripherals); HasPeripheralsCategory = Device.Categories.Contains(DeviceCategory.Peripherals);
Device.RedScale = _initialRedScale; RedScale = _initialRedScale * 100;
Device.GreenScale = _initialGreenScale; GreenScale = _initialGreenScale * 100;
Device.BlueScale = _initialBlueScale; BlueScale = _initialBlueScale * 100;
} }
private bool GetCategory(DeviceCategory category) private bool GetCategory(DeviceCategory category)
@ -254,7 +254,7 @@ namespace Artemis.UI.Screens.Device
{ {
if (value && !_categories.Contains(category)) if (value && !_categories.Contains(category))
_categories.Add(category); _categories.Add(category);
else else if (!value)
_categories.Remove(category); _categories.Remove(category);
this.RaisePropertyChanged($"Has{category}Category"); this.RaisePropertyChanged($"Has{category}Category");

View File

@ -2,7 +2,73 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.InputMappingsTabView"> x:Class="Artemis.UI.Screens.Device.InputMappingsTabView"
Welcome to Avalonia! x:DataType="device:InputMappingsTabViewModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Classes="h4">
Introduction
</TextBlock>
<TextBlock TextWrapping="Wrap">
In some cases you may want Artemis to map key presses to different LEDs.
</TextBlock>
<TextBlock TextWrapping="Wrap" Margin="0 13 0 0">
This is useful when your logical layout swaps keys around (like Hungarian layouts where the Z and Y keys are swapped). In this tab you can set up these custom input mappings, simply click on a LED and press a key, Artemis will from then on consider that LED pressed whenever you press the same key again.
</TextBlock>
</StackPanel>
<TextBlock Grid.Row="1"
Margin="0 20"
Classes="h4"
TextAlignment="Center"
IsVisible="{CompiledBinding SelectedLed, Converter={x:Static ObjectConverters.IsNull}}">
Select a LED in the preview on the left side to get started...
</TextBlock>
<StackPanel Grid.Row="1" Margin="0 20" IsVisible="{CompiledBinding SelectedLed, Converter={x:Static ObjectConverters.IsNotNull}}">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Spacing="6">
<TextBlock Classes="h4" TextAlignment="Center">Current target LED: </TextBlock>
<TextBlock Classes="h4" TextAlignment="Center" Text="{CompiledBinding SelectedLed.RgbLed.Id, Mode=OneWay, FallbackValue='none'}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Spacing="5">
<TextBlock TextAlignment="Center">Press the key you want to remap</TextBlock>
<TextBlock TextAlignment="Center" FontWeight="Bold" Text="{CompiledBinding SelectedLed.RgbLed.Id, Mode=OneWay, FallbackValue='none'}"/>
<TextBlock TextAlignment="Center">to ...</TextBlock>
</StackPanel>
</StackPanel>
<DataGrid Grid.Row="2"
Items="{CompiledBinding InputMappings}"
CanUserSortColumns="True"
IsReadOnly="True"
AutoGenerateColumns="False"
Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding [0].RgbLed.Id}" Header="Original LED ID" Width="*" />
<DataGridTextColumn Binding="{Binding [1].RgbLed.Id}" Header="Remapped LED ID" Width="*" />
<DataGridTemplateColumn Width="Auto" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding $parent[UserControl].DataContext.DeleteMapping}"
CommandParameter="{Binding}"
Classes="icon-button"
ToolTip.Tip="Delete mapping"
HorizontalAlignment="Center">
<avalonia:MaterialIcon Kind="Delete" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl> </UserControl>

View File

@ -1,9 +1,10 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device namespace Artemis.UI.Screens.Device
{ {
public partial class InputMappingsTabView : UserControl public partial class InputMappingsTabView : ReactiveUserControl<InputMappingsTabViewModel>
{ {
public InputMappingsTabView() public InputMappingsTabView()
{ {

View File

@ -2,6 +2,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
@ -30,8 +31,23 @@ namespace Artemis.UI.Screens.Device
Device = device; Device = device;
DisplayName = "Input Mappings"; DisplayName = "Input Mappings";
InputMappings = new ObservableCollection<(ArtemisLed, ArtemisLed)>(); InputMappings = new ObservableCollection<(ArtemisLed, ArtemisLed)>();
this.WhenActivated(d =>
{
_selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
UpdateInputMappings();
Disposable.Create(() =>
{
_selectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
InputMappings.Clear();
}).DisposeWith(d);
});
} }
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public ArtemisLed? SelectedLed public ArtemisLed? SelectedLed
@ -42,13 +58,13 @@ namespace Artemis.UI.Screens.Device
public ObservableCollection<(ArtemisLed, ArtemisLed)> InputMappings { get; } public ObservableCollection<(ArtemisLed, ArtemisLed)> InputMappings { get; }
public void DeleteMapping(Tuple<ArtemisLed, ArtemisLed> inputMapping) public void DeleteMapping((ArtemisLed, ArtemisLed) inputMapping)
{ {
Device.InputMappings.Remove(inputMapping.Item1); Device.InputMappings.Remove(inputMapping.Item1);
UpdateInputMappings(); UpdateInputMappings();
} }
private void InputServiceOnKeyboardKeyUp(object sender, ArtemisKeyboardKeyEventArgs e) private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
{ {
if (SelectedLed == null || e.Led == null) if (SelectedLed == null || e.Led == null)
return; return;
@ -64,7 +80,7 @@ namespace Artemis.UI.Screens.Device
// Apply the new LED mapping // Apply the new LED mapping
Device.InputMappings[SelectedLed] = artemisLed; Device.InputMappings[SelectedLed] = artemisLed;
_rgbService.SaveDevice(Device); _rgbService.SaveDevice(Device);
// ((DeviceDialogViewModel) Parent).SelectedLeds.Clear(); _selectedLeds.Clear();
UpdateInputMappings(); UpdateInputMappings();
} }
@ -74,35 +90,13 @@ namespace Artemis.UI.Screens.Device
if (InputMappings.Any()) if (InputMappings.Any())
InputMappings.Clear(); InputMappings.Clear();
// InputMappings.AddRange(Device.InputMappings.Select(m => new Tuple<ArtemisLed, ArtemisLed>(m.Key, m.Value))); foreach ((ArtemisLed, ArtemisLed) tuple in Device.InputMappings.Select(m => (m.Key, m.Value)))
InputMappings.Add(tuple);
} }
private void SelectedLedsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void SelectedLedsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
// SelectedLed = ((DeviceDialogViewModel) Parent).SelectedLeds.FirstOrDefault(); SelectedLed = _selectedLeds.FirstOrDefault();
} }
//
// #region Overrides of Screen
//
// /// <inheritdoc />
// protected override void OnActivate()
// {
// UpdateInputMappings();
// _inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
// ((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
//
// base.OnActivate();
// }
//
// /// <inheritdoc />
// protected override void OnDeactivate()
// {
// InputMappings.Clear();
// _inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
// ((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
// base.OnDeactivate();
// }
//
// #endregion
} }
} }

View File

@ -35,7 +35,7 @@
<avalonia:MaterialIcon Kind="BookEdit" /> <avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Adapt Profile" Command="{Binding AdaptProfile}"> <MenuItem Header="Adapt Profile" Command="{CompiledBinding AdaptProfile}">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Magic" /> <avalonia:MaterialIcon Kind="Magic" />
</MenuItem.Icon> </MenuItem.Icon>

View File

@ -55,6 +55,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
AddFolder = ReactiveCommand.Create(ExecuteAddFolder); AddFolder = ReactiveCommand.Create(ExecuteAddFolder);
AddLayer = ReactiveCommand.Create(ExecuteAddLayer); AddLayer = ReactiveCommand.Create(ExecuteAddLayer);
ViewProperties = ReactiveCommand.CreateFromTask(ExecuteViewProperties, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); ViewProperties = ReactiveCommand.CreateFromTask(ExecuteViewProperties, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null));
AdaptProfile = ReactiveCommand.CreateFromTask(ExecuteAdaptProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null));
ToggleSuspended = ReactiveCommand.Create(ExecuteToggleSuspended, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); ToggleSuspended = ReactiveCommand.Create(ExecuteToggleSuspended, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null));
DeleteProfile = ReactiveCommand.CreateFromTask(ExecuteDeleteProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); DeleteProfile = ReactiveCommand.CreateFromTask(ExecuteDeleteProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null));
ExportProfile = ReactiveCommand.CreateFromTask(ExecuteExportProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null)); ExportProfile = ReactiveCommand.CreateFromTask(ExecuteExportProfile, this.WhenAnyValue(vm => vm.ProfileConfiguration).Select(c => c != null));
@ -68,6 +69,7 @@ public class MenuBarViewModel : ActivatableViewModelBase
public ReactiveCommand<Unit, Unit> AddLayer { get; } public ReactiveCommand<Unit, Unit> AddLayer { get; }
public ReactiveCommand<Unit, Unit> ToggleSuspended { get; } public ReactiveCommand<Unit, Unit> ToggleSuspended { get; }
public ReactiveCommand<Unit, Unit> ViewProperties { get; } public ReactiveCommand<Unit, Unit> ViewProperties { get; }
public ReactiveCommand<Unit,Unit> AdaptProfile { get; }
public ReactiveCommand<Unit, Unit> DeleteProfile { get; } public ReactiveCommand<Unit, Unit> DeleteProfile { get; }
public ReactiveCommand<Unit, Unit> ExportProfile { get; } public ReactiveCommand<Unit, Unit> ExportProfile { get; }
public ReactiveCommand<Unit, Unit> DuplicateProfile { get; } public ReactiveCommand<Unit, Unit> DuplicateProfile { get; }
@ -121,6 +123,16 @@ public class MenuBarViewModel : ActivatableViewModelBase
("profileConfiguration", ProfileConfiguration) ("profileConfiguration", ProfileConfiguration)
); );
} }
private async Task ExecuteAdaptProfile()
{
if (ProfileConfiguration?.Profile == null)
return;
if (!await _windowService.ShowConfirmContentDialog("Adapt profile", "Are you sure you want to adapt the profile to your current surface? Layer assignments may change."))
return;
_profileService.AdaptProfile(ProfileConfiguration.Profile);
}
private void ExecuteToggleSuspended() private void ExecuteToggleSuspended()
{ {

View File

@ -56,6 +56,19 @@ public class FolderTreeItemViewModel : TreeItemViewModel
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Folder.Order - 1)); ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Folder.Order - 1));
} }
private async Task ExecutePasteInto()
{
RenderProfileElement? pasted = await Folder.PasteChildFromClipboard();
if (pasted == null)
return;
// If the target contains an element with the same name, affix with " - copy"
if (Folder.Children.Any(c => c.Name == pasted.Name))
pasted.Name = Folder.GetNewFolderName(pasted.Name + " - copy");
ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, Folder, 0));
}
/// <inheritdoc /> /// <inheritdoc />
public override bool SupportsChildren => true; public override bool SupportsChildren => true;

View File

@ -112,7 +112,7 @@
<avalonia:MaterialIcon Kind="LayersPlus" /> <avalonia:MaterialIcon Kind="LayersPlus" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <MenuItem Header="-" />
<MenuItem Header="Duplicate" Command="{CompiledBinding Duplicate}" InputGesture="Ctrl+D"> <MenuItem Header="Duplicate" Command="{CompiledBinding Duplicate}" InputGesture="Ctrl+D">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" /> <avalonia:MaterialIcon Kind="ContentDuplicate" />
@ -128,7 +128,7 @@
<avalonia:MaterialIcon Kind="ContentPaste" /> <avalonia:MaterialIcon Kind="ContentPaste" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <MenuItem Header="-" />
<MenuItem Header="Rename" Command="{CompiledBinding Rename}" InputGesture="F2"> <MenuItem Header="Rename" Command="{CompiledBinding Rename}" InputGesture="F2">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="RenameBox" /> <avalonia:MaterialIcon Kind="RenameBox" />

View File

@ -6,8 +6,7 @@
xmlns:timeline="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline" xmlns:timeline="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes.TimelineKeyframeView" x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes.TimelineKeyframeView"
ClipToBounds="False" ClipToBounds="False">
Height="{DynamicResource RailsHeight}">
<Ellipse Fill="{DynamicResource SystemAccentColorLight2}" <Ellipse Fill="{DynamicResource SystemAccentColorLight2}"
Stroke="White" Stroke="White"
Width="10" Width="10"
@ -52,7 +51,7 @@
<avalonia:MaterialIcon Kind="Creation" /> <avalonia:MaterialIcon Kind="Creation" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <MenuItem Header="-" />
<MenuItem Header="Duplicate" Command="{Binding $parent[timeline:TimelineView].DataContext.DuplicateKeyframes}" CommandParameter="{Binding}" InputGesture="Ctrl+D"> <MenuItem Header="Duplicate" Command="{Binding $parent[timeline:TimelineView].DataContext.DuplicateKeyframes}" CommandParameter="{Binding}" InputGesture="Ctrl+D">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="ContentDuplicate" /> <avalonia:MaterialIcon Kind="ContentDuplicate" />
@ -68,7 +67,7 @@
<avalonia:MaterialIcon Kind="ContentPaste" /> <avalonia:MaterialIcon Kind="ContentPaste" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <MenuItem Header="-" />
<MenuItem Header="Delete" Command="{Binding $parent[timeline:TimelineView].DataContext.DeleteKeyframes}" CommandParameter="{Binding}" InputGesture="Delete"> <MenuItem Header="Delete" Command="{Binding $parent[timeline:TimelineView].DataContext.DeleteKeyframes}" CommandParameter="{Binding}" InputGesture="Delete">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Delete" /> <avalonia:MaterialIcon Kind="Delete" />

View File

@ -9,7 +9,7 @@
x:DataType="timeline:TimelineGroupViewModel"> x:DataType="timeline:TimelineGroupViewModel">
<Grid RowDefinitions="28,1,Auto"> <Grid RowDefinitions="28,1,Auto">
<ItemsControl Grid.Row="0" <ItemsControl Grid.Row="0"
Height="{DynamicResource RailsHeight}" Height="28"
IsVisible="{CompiledBinding !PropertyGroupViewModel.IsExpanded}" IsVisible="{CompiledBinding !PropertyGroupViewModel.IsExpanded}"
Items="{CompiledBinding KeyframePositions}" Items="{CompiledBinding KeyframePositions}"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
@ -21,16 +21,17 @@
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ContentPresenter"> <Style Selector="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding}" /> <Setter Property="Canvas.Left" Value="{Binding}" />
<Setter Property="Canvas.Top" Value="9" />
</Style> </Style>
</ItemsControl.Styles> </ItemsControl.Styles>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Ellipse Fill="{DynamicResource ControlFillColorDisabledBrush}" <Ellipse Fill="{DynamicResource ControlSolidFillColorDefaultBrush}"
Stroke="White" Stroke="White"
StrokeThickness="0" StrokeThickness="0"
Width="10" Width="10"
Height="10" Height="10"
Margin="-5,6,0,0" /> Margin="-5 0 0 0" />
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>

View File

@ -11,7 +11,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
public class TimelineGroupViewModel : ActivatableViewModelBase public class TimelineGroupViewModel : ActivatableViewModelBase
{ {
private ObservableCollection<double>? _keyframePositions; private ObservableCollection<double>? _keyframePositions;
private int _pixelsPerSecond; private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
public TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel, IProfileEditorService profileEditorService) public TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel, IProfileEditorService profileEditorService)
{ {
@ -19,16 +19,16 @@ public class TimelineGroupViewModel : ActivatableViewModelBase
this.WhenActivated(d => this.WhenActivated(d =>
{ {
profileEditorService.PixelsPerSecond.Subscribe(p => _pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond).DisposeWith(d);
{ profileEditorService.PixelsPerSecond.Subscribe(p => UpdateKeyframePositions()).DisposeWith(d);
_pixelsPerSecond = p;
UpdateKeyframePositions();
}).DisposeWith(d);
this.WhenAnyValue(vm => vm.PropertyGroupViewModel.IsExpanded).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d);
PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => this.RaisePropertyChanged(nameof(Children))).DisposeWith(d); PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => this.RaisePropertyChanged(nameof(Children))).DisposeWith(d);
PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d);
this.WhenAnyValue(vm => vm.PixelsPerSecond).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d);
UpdateKeyframePositions();
}); });
} }
public int PixelsPerSecond => _pixelsPerSecond?.Value ?? 0;
public PropertyGroupViewModel PropertyGroupViewModel { get; } public PropertyGroupViewModel PropertyGroupViewModel { get; }
public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
@ -43,6 +43,6 @@ public class TimelineGroupViewModel : ActivatableViewModelBase
{ {
KeyframePositions = new ObservableCollection<double>(PropertyGroupViewModel KeyframePositions = new ObservableCollection<double>(PropertyGroupViewModel
.GetAllKeyframeViewModels(false) .GetAllKeyframeViewModels(false)
.Select(p => p.Position.TotalSeconds * _pixelsPerSecond)); .Select(p => p.Position.TotalSeconds * PixelsPerSecond));
} }
} }

View File

@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelinePropertyView"> x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelinePropertyView">
<Border Height="{DynamicResource RailsBorderHeight}" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource ButtonBorderBrush}"> <Border Height="29" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource ButtonBorderBrush}">
<ItemsControl Items="{Binding KeyframeViewModels}"> <ItemsControl Items="{Binding KeyframeViewModels}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@ -14,6 +14,7 @@
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ItemsControl > ContentPresenter"> <Style Selector="ItemsControl > ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X, TargetNullValue=0}" /> <Setter Property="Canvas.Left" Value="{Binding X, TargetNullValue=0}" />
<Setter Property="Canvas.Top" Value="9" />
</Style> </Style>
</ItemsControl.Styles> </ItemsControl.Styles>
</ItemsControl> </ItemsControl>