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

Merge remote-tracking branch 'origin/master'

This commit is contained in:
SpoinkyNL 2020-02-18 19:15:28 +01:00
commit 33756a228d
25 changed files with 390 additions and 81 deletions

View File

@ -99,7 +99,7 @@ namespace Artemis.Core.Services
{
try
{
if (!ModuleUpdatingDisabled)
if (!ModuleUpdatingDisabled && _modules != null)
{
lock (_modules)
{

View File

@ -97,7 +97,7 @@ namespace Artemis.UI.Behaviors
// Update state of all items starting with given, with optional recursion
private void UpdateTreeViewItem(TreeViewItem item, bool recurse)
{
if (SelectedItem == null) return;
// if (SelectedItem == null) return;
var model = item.DataContext;
// If the selected item is this model and is not yet selected - select and return

View File

@ -44,7 +44,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
PopulateProperties(_profileEditorService.SelectedProfileElement, null);
_profileEditorService.SelectedProfileElementChanged += (sender, args) => PopulateProperties(args.ProfileElement, args.PreviousProfileElement);
_profileEditorService.SelectedProfileChanged += (sender, args) => PopulateProperties(_profileEditorService.SelectedProfileElement, null);
_profileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
}
@ -112,6 +111,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
layer.LayerPropertyRegistered += LayerOnPropertyRegistered;
layer.LayerPropertyRemoved += LayerOnPropertyRemoved;
}
else
{
foreach (var layerPropertyViewModel in _layerPropertyViewModels.ToList())
RemovePropertyViewModel(layerPropertyViewModel);
}
}
private void LayerOnPropertyRegistered(object sender, LayerPropertyEventArgs e)

View File

@ -9,7 +9,7 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<ComboBox Width="132"
Margin="0 2"
Padding="0 -1"
@ -20,8 +20,7 @@
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=BrushInputValue}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}"
materialDesign:ComboBoxAssist.ClassicMode="True" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -59,6 +59,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
NotifyOfPropertyChange(() => BrushInputValue);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
protected override void OnInitialized()
{
UpdateEnumValues();

View File

@ -10,7 +10,7 @@
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:EnumPropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<ComboBox Width="132"
Margin="0 2"
Padding="0 -1"
@ -21,8 +21,7 @@
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=EnumInputValue}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}"
materialDesign:ComboBoxAssist.ClassicMode="True" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -27,6 +27,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
NotifyOfPropertyChange(() => EnumInputValue);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
protected override void OnInitialized()
{
EnumValues = EnumUtilities.GetAllValuesAndDescriptions(LayerPropertyViewModel.LayerProperty.Type);

View File

@ -10,14 +10,41 @@
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:FloatPropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<!-- Drag handle -->
<Border BorderThickness="0,0,0,1" Margin="0 2">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle StrokeDashArray="2 2" Stroke="{DynamicResource SecondaryAccentBrush}" StrokeThickness="1"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
<TextBlock Width="60"
Padding="2 0"
Margin="0 2 0 0"
Text="{Binding FloatInputValue}"
Cursor="/Resources/aero_drag_ew.cur"
Foreground="{DynamicResource SecondaryAccentBrush}"
Visibility="{Binding InputFieldEnabled, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
MouseDown="{s:Action InputMouseDown}"
MouseUp="{s:Action InputMouseUp}"
MouseMove="{s:Action InputMouseMove}" />
</Border>
<!-- Input -->
<TextBox Width="60"
Margin="0 2"
Padding="0 -1"
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
Text="{Binding FloatInputValue}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
Visibility="{Binding InputFieldEnabled, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"
LostFocus="{s:Action InputLostFocus}"
KeyDown="{s:Action InputKeyDown}"
IsVisibleChanged="{s:Action InputIsVisibleChanged}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -12,6 +12,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
public sealed override List<Type> CompatibleTypes { get; } = new List<Type> {typeof(float)};
public float FloatInputValue
{
get => (float?) InputValue ?? 0f;
@ -22,5 +23,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
{
NotifyOfPropertyChange(() => FloatInputValue);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
var floatStartValue = (float) startValue;
FloatInputValue = (float) (floatStartValue + dragDistance);
}
}
}

View File

@ -10,14 +10,13 @@
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:IntPropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBox Width="60"
Margin="0 2"
Padding="0 -1"
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
Text="{Binding IntInputValue}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
Text="{Binding IntInputValue}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -22,5 +22,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
{
NotifyOfPropertyChange(() => IntInputValue);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Artemis.UI.Exceptions;
using Artemis.UI.Services.Interfaces;
using Stylet;
@ -15,11 +17,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
}
protected IProfileEditorService ProfileEditorService { get; set; }
public abstract List<Type> CompatibleTypes { get; }
public bool Initialized { get; private set; }
public abstract List<Type> CompatibleTypes { get; }
public LayerPropertyViewModel LayerPropertyViewModel { get; private set; }
public bool InputFieldEnabled { get; set; }
protected object InputValue
{
@ -46,18 +48,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
OnInitialized();
}
/// <summary>
/// Called by the view, prevents scrolling into view when scrubbing through the timeline
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
e.Handled = true;
}
public abstract void Update();
public abstract void ApplyInputDrag(object startValue, double dragDistance);
protected virtual void OnInitialized()
{
}
@ -70,5 +64,70 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
ProfileEditorService.UpdateSelectedProfileElement();
}
#region Mouse-based mutations
private Point _mouseDragStartPoint;
private object _startValue;
// ReSharper disable once UnusedMember.Global - Called from view
public void InputMouseDown(object sender, MouseButtonEventArgs e)
{
_startValue = InputValue;
((IInputElement) sender).CaptureMouse();
_mouseDragStartPoint = e.GetPosition((IInputElement) sender);
}
// ReSharper disable once UnusedMember.Global - Called from view
public void InputMouseUp(object sender, MouseEventArgs e)
{
var position = e.GetPosition((IInputElement) sender);
if (position == _mouseDragStartPoint)
InputFieldEnabled = true;
((IInputElement) sender).ReleaseMouseCapture();
}
// ReSharper disable once UnusedMember.Global - Called from view
public void InputMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var position = e.GetPosition((IInputElement) sender);
ApplyInputDrag(_startValue, position.X - _mouseDragStartPoint.X);
}
}
// ReSharper disable once UnusedMember.Global - Called from view
public void InputIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (InputFieldEnabled)
{
((UIElement) sender).Focus();
if (sender is TextBox textBox)
textBox.SelectAll();
}
}
// ReSharper disable once UnusedMember.Global - Called from view
public void InputLostFocus(object sender, RoutedEventArgs e)
{
InputFieldEnabled = false;
}
// ReSharper disable once UnusedMember.Global - Called from view
public void InputKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
InputFieldEnabled = false;
else if (e.Key == Key.Escape)
{
if (sender is TextBox textBox)
textBox.Text = _startValue.ToString();
InputFieldEnabled = false;
}
}
#endregion
}
}

View File

@ -15,11 +15,11 @@
<converters:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<artemis:ColorPicker Width="132"
Margin="0 2"
Padding="0 -1"
Color="{Binding SKColorInputValue, Converter={StaticResource SKColorToColorConverter}}" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -23,5 +23,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
{
NotifyOfPropertyChange(() => SKColorInputValue);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
}
}

View File

@ -10,15 +10,14 @@
d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:SKPointPropertyInputViewModel}">
<StackPanel Orientation="Horizontal" KeyboardNavigation.IsTabStop="True">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBox Width="60"
Margin="0 2"
Padding="0 -1"
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
ToolTip="X-coordinate (horizontal)"
Text="{Binding X}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
Text="{Binding X}" />
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
<TextBox Width="60"
Margin="0 2"
@ -26,8 +25,7 @@
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
ToolTip="Y-coordinate (vertical)"
Text="{Binding Y}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
Text="{Binding Y}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -34,5 +34,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
NotifyOfPropertyChange(() => X);
NotifyOfPropertyChange(() => Y);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
}
}

View File

@ -10,15 +10,14 @@
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<TextBox Width="60"
Margin="0 2"
Padding="0 -1"
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
ToolTip="Height"
Text="{Binding Height}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
Text="{Binding Height}" />
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
<TextBox Width="60"
Margin="0 2"
@ -26,8 +25,7 @@
materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left"
ToolTip="Width"
Text="{Binding Width}"
RequestBringIntoView="{s:Action OnRequestBringIntoView}" />
<TextBlock Margin="5 0 0 0" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
Text="{Binding Width}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -34,5 +34,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
NotifyOfPropertyChange(() => Width);
NotifyOfPropertyChange(() => Height);
}
public override void ApplyInputDrag(object startValue, double dragDistance)
{
throw new NotImplementedException();
}
}
}

View File

@ -9,15 +9,47 @@
d:DesignHeight="25"
d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:PropertyTimelineViewModel}">
<ItemsControl ItemsSource="{Binding PropertyTrackViewModels}"
Width="{Binding Width}"
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollViewer}}"
Background="{DynamicResource MaterialDesignToolBarBackground}"
HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Canvas Background="{DynamicResource MaterialDesignToolBarBackground}"
MouseDown="{s:Action TimelineCanvasMouseDown}"
MouseUp="{s:Action TimelineCanvasMouseUp}"
MouseMove="{s:Action TimelineCanvasMouseMove}">
<Canvas.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="MultiSelectionPath" Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeftButtonUp">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="MultiSelectionPath" Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
<ItemsControl ItemsSource="{Binding PropertyTrackViewModels}"
Width="{Binding Width}"
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollViewer}}"
HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Multi-selection rectangle -->
<Path Data="{Binding SelectionRectangle}" Opacity="0"
Stroke="{DynamicResource PrimaryHueLightBrush}"
StrokeThickness="1"
x:Name="MultiSelectionPath"
IsHitTestVisible="False">
<Path.Fill>
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
</Path.Fill>
</Path>
</Canvas>
</UserControl>

View File

@ -1,7 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Artemis.Core.Models.Surface;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.SurfaceEditor.Visualization;
using Artemis.UI.Services.Interfaces;
using Stylet;
@ -24,12 +32,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
_profileEditorService.SelectedProfileElementUpdated += (sender, args) => Update();
LayerPropertiesViewModel.PixelsPerSecondChanged += (sender, args) => UpdateKeyframePositions();
Execute.PostToUIThread(() => SelectionRectangle = new RectangleGeometry());
}
public LayerPropertiesViewModel LayerPropertiesViewModel { get; }
public double Width { get; set; }
public BindableCollection<PropertyTrackViewModel> PropertyTrackViewModels { get; set; }
public RectangleGeometry SelectionRectangle { get; set; }
public void UpdateEndTime()
{
@ -88,6 +99,119 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
UpdateEndTime();
}
#region Keyframe selection
private Point _mouseDragStartPoint;
// ReSharper disable once UnusedMember.Global - Called from view
public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e)
{
((IInputElement) sender).CaptureMouse();
SelectionRectangle.Rect = new Rect();
_mouseDragStartPoint = e.GetPosition((IInputElement) sender);
}
// ReSharper disable once UnusedMember.Global - Called from view
public void TimelineCanvasMouseUp(object sender, MouseEventArgs e)
{
var position = e.GetPosition((IInputElement)sender);
var selectedRect = new Rect(_mouseDragStartPoint, position);
SelectionRectangle.Rect = selectedRect;
// Find all keyframes in the rectangle
var selectedKeyframes = new List<PropertyTrackKeyframeViewModel>();
var hitTestParams = new GeometryHitTestParameters(SelectionRectangle);
var resultCallback = new HitTestResultCallback(result => HitTestResultBehavior.Continue);
var filterCallback = new HitTestFilterCallback(element =>
{
if (element is Ellipse ellipse)
selectedKeyframes.Add((PropertyTrackKeyframeViewModel) ellipse.DataContext);
return HitTestFilterBehavior.Continue;
});
VisualTreeHelper.HitTest((Visual) sender, filterCallback, resultCallback, hitTestParams);
var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList();
foreach (var keyframeViewModel in keyframeViewModels)
keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel);
((IInputElement) sender).ReleaseMouseCapture();
}
public void TimelineCanvasMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var position = e.GetPosition((IInputElement) sender);
var selectedRect = new Rect(_mouseDragStartPoint, position);
SelectionRectangle.Rect = selectedRect;
}
}
public void SelectKeyframe(PropertyTrackKeyframeViewModel clicked, bool selectBetween, bool toggle)
{
var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList();
if (selectBetween)
{
var selectedIndex = keyframeViewModels.FindIndex(k => k.IsSelected);
// If nothing is selected, select only the clicked
if (selectedIndex == -1)
{
clicked.IsSelected = true;
return;
}
foreach (var keyframeViewModel in keyframeViewModels)
keyframeViewModel.IsSelected = false;
var clickedIndex = keyframeViewModels.IndexOf(clicked);
if (clickedIndex < selectedIndex)
{
foreach (var keyframeViewModel in keyframeViewModels.Skip(clickedIndex).Take(selectedIndex - clickedIndex + 1))
keyframeViewModel.IsSelected = true;
}
else
{
foreach (var keyframeViewModel in keyframeViewModels.Skip(selectedIndex).Take(clickedIndex - selectedIndex + 1))
keyframeViewModel.IsSelected = true;
}
}
else if (toggle)
{
// Toggle only the clicked keyframe, leave others alone
clicked.IsSelected = !clicked.IsSelected;
}
else
{
// Only select the clicked keyframe
foreach (var keyframeViewModel in keyframeViewModels)
keyframeViewModel.IsSelected = false;
clicked.IsSelected = true;
}
}
#endregion
#region Keyframe movement
public void MoveSelectedKeyframes(TimeSpan offset)
{
var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList();
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
{
// TODO: Not ideal as this stacks them all if they get to 0, oh well
if (keyframeViewModel.Keyframe.Position + offset > TimeSpan.Zero)
{
keyframeViewModel.Keyframe.Position += offset;
keyframeViewModel.Update(LayerPropertiesViewModel.PixelsPerSecond);
}
}
_profileEditorService.UpdateProfilePreview();
}
#endregion
private void CreateViewModels(LayerPropertyViewModel property)
{
PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property));

View File

@ -46,13 +46,26 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public void KeyframeMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
return;
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected)
PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, true, false);
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, false, true);
else if (!IsSelected)
PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, false, false);
((IInputElement) sender).CaptureMouse();
e.Handled = true;
}
public void KeyframeMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
return;
((IInputElement) sender).ReleaseMouseCapture();
_profileEditorService.UpdateSelectedProfileElement();
e.Handled = true;
}
public void KeyframeMouseMove(object sender, MouseEventArgs e)
@ -71,26 +84,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
else
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds));
if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
{
Keyframe.Position = newTime;
Update(_pixelsPerSecond);
_profileEditorService.UpdateProfilePreview();
return;
}
// If shift is held, snap to the current time
// Take a tolerance of 5 pixels (half a keyframe width)
var tolerance = 1000f / _pixelsPerSecond * 5;
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - newTime.TotalMilliseconds) < tolerance)
Keyframe.Position = _profileEditorService.CurrentTime;
else
Keyframe.Position = newTime;
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
var tolerance = 1000f / _pixelsPerSecond * 5;
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - newTime.TotalMilliseconds) < tolerance)
newTime = _profileEditorService.CurrentTime;
}
Update(_pixelsPerSecond);
_profileEditorService.UpdateProfilePreview();
PropertyTrackViewModel.PropertyTimelineViewModel.MoveSelectedKeyframes(newTime - Keyframe.Position);
}
e.Handled = true;
}
#endregion

View File

@ -29,6 +29,8 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Fill="{StaticResource PrimaryHueMidBrush}"
Stroke="White"
StrokeThickness="0"
Width="10"
Height="10"
Margin="-5,6,0,0"
@ -37,6 +39,28 @@
MouseDown="{s:Action KeyframeMouseDown}"
MouseUp="{s:Action KeyframeMouseUp}"
MouseMove="{s:Action KeyframeMouseMove}">
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="StrokeThickness" To="1" Duration="0:0:0.25"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="StrokeThickness" To="0" Duration="0:0:0.25"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
<Ellipse.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command="{s:Action Copy}">

View File

@ -136,9 +136,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private void ChangeSelectedProfile(Profile profile)
{
if (profile == Module.ActiveProfile)
return;
var oldProfile = Module.ActiveProfile;
_profileService.ActivateProfile(Module, profile);
@ -147,7 +144,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
if (profile != null)
_profileService.UpdateProfile(profile, false);
_profileEditorService.ChangeSelectedProfile(profile);
if (_profileEditorService.SelectedProfile != profile)
_profileEditorService.ChangeSelectedProfile(profile);
}
private void ModuleOnActiveProfileChanged(object sender, EventArgs e)
@ -188,11 +186,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
// Populate the UI collection
Execute.PostToUIThread(() =>
{
Profiles.Clear();
Profiles.AddRange(profiles.OrderBy(p => p.Name));
SelectedProfile = activeProfile;
Profiles.AddRange(profiles.Except(Profiles).ToList());
Profiles.RemoveRange(Profiles.Except(profiles).ToList());
var index = 0;
foreach (var profile in Profiles.OrderBy(p => p.Name).ToList())
{
Profiles.Move(Profiles.IndexOf(profile), index);
index++;
}
_profileEditorService.ChangeSelectedProfile(SelectedProfile);
SelectedProfile = activeProfile;
if (_profileEditorService.SelectedProfile != activeProfile)
_profileEditorService.ChangeSelectedProfile(activeProfile);
if (!activeProfile.IsActivated)
_profileService.ActivateProfile(Module, activeProfile);
});

View File

@ -16,10 +16,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
private TreeItemViewModel _selectedTreeItem;
private bool _updatingTree;
public ProfileTreeViewModel(IProfileEditorService profileEditorService,
IFolderVmFactory folderVmFactory,
ILayerVmFactory layerVmFactory)
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderVmFactory folderVmFactory)
{
_profileEditorService = profileEditorService;
_folderVmFactory = folderVmFactory;

View File

@ -40,6 +40,8 @@ namespace Artemis.UI.Services
public void ChangeSelectedProfile(Profile profile)
{
ChangeSelectedProfileElement(null);
var profileElementEvent = new ProfileElementEventArgs(profile, SelectedProfile);
SelectedProfile = profile;
UpdateProfilePreview();