Browse Source

Rectangle selection works

flabbet 1 year ago
parent
commit
717b61e41b

+ 3 - 3
src/PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml

@@ -128,7 +128,7 @@
                                             CommandParameter="{x:Null}" />
                                             CommandParameter="{x:Null}" />
                                     </EventTriggerBehavior>
                                     </EventTriggerBehavior>
                                 </Interaction.Behaviors>
                                 </Interaction.Behaviors>
-                                <ItemsControl ClipToBounds="False"
+                                <ItemsControl ClipToBounds="False" Name="PART_KeyFramesHost"
                                               Margin="0, 35, 0, 0"
                                               Margin="0, 35, 0, 0"
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     <ItemsControl.DataTemplates>
                                     <ItemsControl.DataTemplates>
@@ -194,8 +194,8 @@
                                 </ItemsControl>
                                 </ItemsControl>
                                 
                                 
                                 <Rectangle Name="PART_SelectionRectangle" HorizontalAlignment="Left" VerticalAlignment="Top"
                                 <Rectangle Name="PART_SelectionRectangle" HorizontalAlignment="Left" VerticalAlignment="Top"
-                                           IsVisible="False" ZIndex="100" Stroke="{DynamicResource ThemeAccent2Brush}" StrokeThickness="1"
-                                           Fill="{DynamicResource ThemeControlHighlightBrush}" Opacity="0.25"/>
+                                           IsVisible="False" ZIndex="100"
+                                           Fill="{DynamicResource SelectionFillBrush}" Opacity="1"/>
                             </Grid>
                             </Grid>
                         </ScrollViewer>
                         </ScrollViewer>
                     </Grid>
                     </Grid>

+ 1 - 22
src/PixiEditor.AvaloniaUI/Views/Animations/KeyFrame.cs

@@ -45,8 +45,6 @@ internal class KeyFrame : TemplatedControl
     
     
     private InputElement _resizePanelRight;
     private InputElement _resizePanelRight;
     private InputElement _resizePanelLeft;
     private InputElement _resizePanelLeft;
-
-    private int clickFrameOffset;
     
     
     static KeyFrame()
     static KeyFrame()
     {
     {
@@ -68,10 +66,6 @@ internal class KeyFrame : TemplatedControl
         _resizePanelLeft.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelLeft.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelRight.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelRight.PointerCaptureLost += UpdateKeyFrame;
 
 
-        /*PointerPressed += CapturePointer;
-        PointerMoved += DragOnPointerMoved;
-        PointerCaptureLost += UpdateKeyFrame;*/
-
         if (Item is not KeyFrameGroupViewModel)
         if (Item is not KeyFrameGroupViewModel)
         {
         {
             MultiBinding marginBinding = new MultiBinding
             MultiBinding marginBinding = new MultiBinding
@@ -97,7 +91,6 @@ internal class KeyFrame : TemplatedControl
         }
         }
         
         
         e.Pointer.Capture(sender as IInputElement);
         e.Pointer.Capture(sender as IInputElement);
-        clickFrameOffset = Item.StartFrameBindable - (int)Math.Floor(e.GetPosition(this.FindAncestorOfType<Border>()).X / Scale);
         e.Handled = true;
         e.Handled = true;
     }
     }
 
 
@@ -138,20 +131,6 @@ internal class KeyFrame : TemplatedControl
         
         
         e.Handled = true;
         e.Handled = true;
     }
     }
-    
-    private void DragOnPointerMoved(object? sender, PointerEventArgs e)
-    {
-        /*if (Item is null)
-        {
-            return;
-        }
-        
-        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-        {
-            var frame = MousePosToFrame(e, false);
-            Item.ChangeFrameLength(frame + clickFrameOffset, Item.DurationBindable);
-        }*/
-    }
 
 
     private int MousePosToFrame(PointerEventArgs e, bool round = true)
     private int MousePosToFrame(PointerEventArgs e, bool round = true)
     {
     {
@@ -172,7 +151,7 @@ internal class KeyFrame : TemplatedControl
     
     
     private void UpdateKeyFrame(object? sender, PointerCaptureLostEventArgs e)
     private void UpdateKeyFrame(object? sender, PointerCaptureLostEventArgs e)
     {
     {
-        if (Item is null || e.Source is not KeyFrame)
+        if (Item is null)
         {
         {
             return;
             return;
         }
         }

+ 95 - 64
src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs

@@ -9,6 +9,7 @@ using Avalonia.Controls.Shapes;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Threading;
 using Avalonia.Threading;
+using Avalonia.VisualTree;
 using CommunityToolkit.Mvvm.Input;
 using CommunityToolkit.Mvvm.Input;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
@@ -22,6 +23,7 @@ namespace PixiEditor.AvaloniaUI.Views.Animations;
 [TemplatePart("PART_TimelineKeyFramesScroll", typeof(ScrollViewer))]
 [TemplatePart("PART_TimelineKeyFramesScroll", typeof(ScrollViewer))]
 [TemplatePart("PART_TimelineHeaderScroll", typeof(ScrollViewer))]
 [TemplatePart("PART_TimelineHeaderScroll", typeof(ScrollViewer))]
 [TemplatePart("PART_SelectionRectangle", typeof(Rectangle))]
 [TemplatePart("PART_SelectionRectangle", typeof(Rectangle))]
+[TemplatePart("PART_KeyFramesHost", typeof(ItemsControl))]
 internal class Timeline : TemplatedControl, INotifyPropertyChanged
 internal class Timeline : TemplatedControl, INotifyPropertyChanged
 {
 {
     private const float MarginMultiplier = 1.5f;
     private const float MarginMultiplier = 1.5f;
@@ -145,6 +147,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     private ScrollViewer? _timelineHeaderScroll;
     private ScrollViewer? _timelineHeaderScroll;
     private Control? extendingElement;
     private Control? extendingElement;
     private Rectangle _selectionRectangle;
     private Rectangle _selectionRectangle;
+    private ItemsControl? _keyFramesHost;
     
     
     private Vector clickPos;
     private Vector clickPos;
     
     
@@ -167,63 +170,12 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         _playTimer =
         _playTimer =
             new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
             new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
         _playTimer.Tick += PlayTimerOnTick;
         _playTimer.Tick += PlayTimerOnTick;
-        PressedKeyFrameCommand = new RelayCommand<PointerPressedEventArgs>((e) =>
-        {
-            shouldClearNextSelection = !e.KeyModifiers.HasFlag(KeyModifiers.Control);
-            KeyFrame target = null;
-            if (e.Source is Control obj)
-            {
-                if(obj is KeyFrame frame) target = frame;
-                else if (obj.TemplatedParent is KeyFrame keyFrame) target = keyFrame;
-            }
-            
-            e.Pointer.Capture(target);
-            clickedKeyFrame = target.Item;
-            dragStartFrame = MousePosToFrame(e);
-            e.Handled = true;
-        });
-        ClearSelectedKeyFramesCommand = new RelayCommand<KeyFrameViewModel>((keyFrame) =>
-        {
-            ClearSelectedKeyFrames();
-        });
-        DraggedKeyFrameCommand = new RelayCommand<PointerEventArgs>((e) =>
-        {
-            if(clickedKeyFrame == null) return;
-            
-            int frameUnderMouse = MousePosToFrame(e);
-            int delta = frameUnderMouse - dragStartFrame;
-
-            if (delta != 0)
-            {
-                if (!clickedKeyFrame.IsSelected)
-                {
-                    SelectKeyFrame(clickedKeyFrame);
-                }
-                
-                dragged = true;
-                if (DragAllSelectedKeyFrames(delta))
-                {
-                    dragStartFrame += delta;
-                }
-            }
-        });
-        ReleasedKeyFrameCommand = new RelayCommand<KeyFrameViewModel>((e) =>
-        {
-            if (!dragged)
-            {
-                SelectKeyFrame(e, shouldClearNextSelection);
-                shouldClearNextSelection = true;
-            }
-            else
-            {
-                EndDragging();
-            }
-            
-            dragged = false;
-            clickedKeyFrame = null;
-        });
+        PressedKeyFrameCommand = new RelayCommand<PointerPressedEventArgs>(KeyFramePressed);
+        ClearSelectedKeyFramesCommand = new RelayCommand<KeyFrameViewModel>(ClearSelectedKeyFrames);
+        DraggedKeyFrameCommand = new RelayCommand<PointerEventArgs>(KeyFramesDragged);
+        ReleasedKeyFrameCommand = new RelayCommand<KeyFrameViewModel>(KeyFramesReleased);
     }
     }
-
+    
     public void SelectKeyFrame(KeyFrameViewModel? keyFrame, bool clearSelection = true)
     public void SelectKeyFrame(KeyFrameViewModel? keyFrame, bool clearSelection = true)
     {
     {
         if (clearSelection)
         if (clearSelection)
@@ -234,14 +186,6 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         keyFrame?.Document.AnimationDataViewModel.AddSelectedKeyFrame(keyFrame.Id);
         keyFrame?.Document.AnimationDataViewModel.AddSelectedKeyFrame(keyFrame.Id);
     }
     }
 
 
-    private void ClearSelectedKeyFrames()
-    {
-        foreach (var keyFrame in SelectedKeyFrames)
-        {
-            keyFrame.Document.AnimationDataViewModel.RemoveSelectedKeyFrame(keyFrame.Id);
-        }
-    }
-
     public bool DragAllSelectedKeyFrames(int delta)
     public bool DragAllSelectedKeyFrames(int delta)
     {
     {
         bool canDrag = SelectedKeyFrames.All(x => x.StartFrameBindable + delta >= 0);
         bool canDrag = SelectedKeyFrames.All(x => x.StartFrameBindable + delta >= 0);
@@ -293,6 +237,76 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         extendingElement = new Control();
         extendingElement = new Control();
         extendingElement.SetValue(MarginProperty, new Thickness(0, 0, 0, 0));
         extendingElement.SetValue(MarginProperty, new Thickness(0, 0, 0, 0));
         _contentGrid.Children.Add(extendingElement);
         _contentGrid.Children.Add(extendingElement);
+        
+        _keyFramesHost = e.NameScope.Find<ItemsControl>("PART_KeyFramesHost");
+    }
+    
+    private void KeyFramesReleased(KeyFrameViewModel? e)
+    {
+        if (!dragged)
+        {
+            SelectKeyFrame(e, shouldClearNextSelection);
+            shouldClearNextSelection = true;
+        }
+        else
+        {
+            EndDragging();
+        }
+
+        dragged = false;
+        clickedKeyFrame = null;
+    }
+
+    private void KeyFramesDragged(PointerEventArgs? e)
+    {
+        if (clickedKeyFrame == null) return;
+
+        int frameUnderMouse = MousePosToFrame(e);
+        int delta = frameUnderMouse - dragStartFrame;
+
+        if (delta != 0)
+        {
+            if (!clickedKeyFrame.IsSelected)
+            {
+                SelectKeyFrame(clickedKeyFrame);
+            }
+
+            dragged = true;
+            if (DragAllSelectedKeyFrames(delta))
+            {
+                dragStartFrame += delta;
+            }
+        }
+    }
+
+    private void ClearSelectedKeyFrames(KeyFrameViewModel? keyFrame)
+    {
+        ClearSelectedKeyFrames();
+    }
+
+    private void KeyFramePressed(PointerPressedEventArgs? e)
+    {
+        shouldClearNextSelection = !e.KeyModifiers.HasFlag(KeyModifiers.Control);
+        KeyFrame target = null;
+        if (e.Source is Control obj)
+        {
+            if (obj is KeyFrame frame)
+                target = frame;
+            else if (obj.TemplatedParent is KeyFrame keyFrame) target = keyFrame;
+        }
+
+        e.Pointer.Capture(target);
+        clickedKeyFrame = target.Item;
+        dragStartFrame = MousePosToFrame(e);
+        e.Handled = true;
+    }
+
+    private void ClearSelectedKeyFrames()
+    {
+        foreach (var keyFrame in SelectedKeyFrames)
+        {
+            keyFrame.Document.AnimationDataViewModel.RemoveSelectedKeyFrame(keyFrame.Id);
+        }
     }
     }
 
 
     private void TimelineKeyFramesScrollOnScrollChanged(object? sender, ScrollChangedEventArgs e)
     private void TimelineKeyFramesScrollOnScrollChanged(object? sender, ScrollChangedEventArgs e)
@@ -454,6 +468,23 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         _selectionRectangle.Height = Math.Abs(height);
         _selectionRectangle.Height = Math.Abs(height);
         Thickness margin = new Thickness(Math.Min(clickPos.X, x), Math.Min(clickPos.Y, y), 0, 0);
         Thickness margin = new Thickness(Math.Min(clickPos.X, x), Math.Min(clickPos.Y, y), 0, 0);
         _selectionRectangle.Margin = margin;
         _selectionRectangle.Margin = margin;
+        ClearSelectedKeyFrames();
+
+        SelectAllWithinBounds(_selectionRectangle.Bounds);
+    }
+
+    private void SelectAllWithinBounds(Rect bounds)
+    {
+        var frames = _keyFramesHost.ItemsPanelRoot.GetVisualDescendants().OfType<KeyFrame>();
+        foreach (var frame in frames)
+        {
+            var translated = frame.TranslatePoint(new Point(0, 0), _contentGrid);
+            Rect frameBounds = new Rect(translated.Value.X, translated.Value.Y, frame.Bounds.Width, frame.Bounds.Height);
+            if (bounds.Contains(frameBounds))
+            {
+                SelectKeyFrame(frame.Item, false);
+            }
+        }
     }
     }
 
 
     private void ContentOnPointerLost(object? sender, PointerCaptureLostEventArgs e)
     private void ContentOnPointerLost(object? sender, PointerCaptureLostEventArgs e)

+ 5 - 0
src/PixiEditor.AvaloniaUI/Views/Overlays/SelectionOverlay/SelectionOverlay.cs

@@ -57,6 +57,11 @@ internal class SelectionOverlay : Overlay
     {
     {
         IsHitTestVisible = false;
         IsHitTestVisible = false;
 
 
+        if (Application.Current.Styles.TryGetResource("SelectionFillBrush", Application.Current.ActualThemeVariant, out var fillBrush))
+        {
+            this.fillBrush = (Brush)fillBrush;
+        }
+
         animation = new Avalonia.Animation.Animation()
         animation = new Avalonia.Animation.Animation()
         {
         {
             Duration = new TimeSpan(0, 0, 0, 2, 0),
             Duration = new TimeSpan(0, 0, 0, 2, 0),

+ 4 - 0
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -33,6 +33,8 @@
             <Color x:Key="GlyphColor">#444</Color>
             <Color x:Key="GlyphColor">#444</Color>
             <Color x:Key="GlyphBackground">White</Color>
             <Color x:Key="GlyphBackground">White</Color>
             <Color x:Key="ThumbColor">#606060</Color>
             <Color x:Key="ThumbColor">#606060</Color>
+            
+            <Color x:Key="SelectionFillColor">#510051ff</Color>
 
 
             <Color x:Key="NotificationCardBackgroundColor">#303030</Color>
             <Color x:Key="NotificationCardBackgroundColor">#303030</Color>
 
 
@@ -67,6 +69,8 @@
             <SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}" />
             <SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}" />
             <SolidColorBrush x:Key="GlyphBrush" Color="{StaticResource GlyphColor}"/>
             <SolidColorBrush x:Key="GlyphBrush" Color="{StaticResource GlyphColor}"/>
             <SolidColorBrush x:Key="ThumbBrush" Color="{StaticResource ThumbColor}"/>
             <SolidColorBrush x:Key="ThumbBrush" Color="{StaticResource ThumbColor}"/>
+            
+            <SolidColorBrush x:Key="SelectionFillBrush" Color="{StaticResource SelectionFillColor}"/>
 
 
             <CornerRadius x:Key="ControlCornerRadius">5</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadius">5</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadiusTop">5, 5, 0, 0</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadiusTop">5, 5, 0, 0</CornerRadius>