فهرست منبع

Opacity slider

Equbuxu 3 سال پیش
والد
کامیت
c8171b7b2f

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Layer.cs

@@ -31,7 +31,6 @@ internal class Layer : StructureMember, IReadOnlyLayer
     /// <summary>
     /// Creates a clone of the layer, its image and its mask
     /// </summary>
-    /// <returns></returns>
     internal override Layer Clone()
     {
         return new Layer(LayerImage.CloneFromCommitted())

+ 121 - 0
src/PixiEditor/Helpers/Behaviours/SliderUpdateBehavior.cs

@@ -0,0 +1,121 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using Microsoft.Xaml.Behaviors;
+
+namespace PixiEditor.Helpers.Behaviours;
+#nullable enable
+internal class SliderUpdateBehavior : Behavior<Slider>
+{
+    public static DependencyProperty DragValueChangedProperty = DependencyProperty.Register(nameof(DragValueChanged), typeof(ICommand), typeof(SliderUpdateBehavior));
+    public ICommand DragValueChanged
+    {
+        get => (ICommand)GetValue(DragValueChangedProperty);
+        set => SetValue(DragValueChangedProperty, value);
+    }
+
+    public static DependencyProperty DragEndedProperty = DependencyProperty.Register(nameof(DragEnded), typeof(ICommand), typeof(SliderUpdateBehavior));
+    public ICommand DragEnded
+    {
+        get => (ICommand)GetValue(DragEndedProperty);
+        set => SetValue(DragEndedProperty, value);
+    }
+
+    public static readonly DependencyProperty DragStartedProperty =
+        DependencyProperty.Register(nameof(DragStarted), typeof(ICommand), typeof(SliderUpdateBehavior), new(null));
+
+    public ICommand DragStarted
+    {
+        get => (ICommand)GetValue(DragStartedProperty);
+        set => SetValue(DragStartedProperty, value);
+    }
+
+    public static DependencyProperty ValueFromSliderProperty =
+        DependencyProperty.Register(nameof(ValueFromSlider), typeof(double), typeof(SliderUpdateBehavior), new(OnSliderValuePropertyChange));
+    public double ValueFromSlider
+    {
+        get => (double)GetValue(ValueFromSliderProperty);
+        set => SetValue(ValueFromSliderProperty, value);
+    }
+
+    private bool attached = false;
+    private bool dragging = false;
+    private bool valueChangedWhileDragging = false;
+    protected override void OnAttached()
+    {
+        AssociatedObject.Loaded += AssociatedObject_Loaded;
+        AssociatedObject.Focusable = false;
+
+
+        if (AssociatedObject.IsLoaded)
+            AttachEvents();
+    }
+
+    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
+    {
+        AttachEvents();
+    }
+
+    private void AttachEvents()
+    {
+        if (attached)
+            return;
+
+        Thumb? thumb = GetThumb(AssociatedObject);
+        if (thumb is null)
+            return;
+
+        attached = true;
+
+        thumb.DragStarted += Thumb_DragStarted;
+        thumb.DragCompleted += Thumb_DragCompleted;
+    }
+
+    protected override void OnDetaching()
+    {
+        AssociatedObject.Loaded -= AssociatedObject_Loaded;
+        if (!attached)
+            return;
+        Thumb? thumb = GetThumb(AssociatedObject);
+        if (thumb is null)
+            return;
+
+        thumb.DragStarted -= Thumb_DragStarted;
+        thumb.DragCompleted -= Thumb_DragCompleted;
+    }
+
+    private static void OnSliderValuePropertyChange(DependencyObject slider, DependencyPropertyChangedEventArgs e)
+    {
+        SliderUpdateBehavior obj = (SliderUpdateBehavior)slider;
+        if (obj.dragging)
+        {
+            if (obj.DragValueChanged is not null && obj.DragValueChanged.CanExecute(e.NewValue))
+                obj.DragValueChanged.Execute(e.NewValue);
+            obj.valueChangedWhileDragging = true;
+        }
+    }
+
+    private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
+    {
+        dragging = false;
+        if (valueChangedWhileDragging && DragEnded is not null && DragEnded.CanExecute(null))
+            DragEnded.Execute(null);
+        valueChangedWhileDragging = false;
+    }
+
+    private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
+    {
+        dragging = true;
+        if (DragStarted is not null && DragStarted.CanExecute(null))
+            DragStarted.Execute(null);
+    }
+
+    private static Thumb? GetThumb(Slider slider)
+    {
+        Track? track = slider.Template.FindName("PART_Track", slider) as Track;
+        return track is null ? null : track.Thumb;
+    }
+
+
+}

+ 0 - 1
src/PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs

@@ -93,7 +93,6 @@ internal class TextBoxFocusBehavior : Behavior<TextBox>
         {
             AssociatedObject.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
         }
-
     }
 
     private void AssociatedObjectGotKeyboardFocus(

+ 36 - 0
src/PixiEditor/Helpers/Converters/MultiplyConverter.cs

@@ -0,0 +1,36 @@
+using System.Globalization;
+using System.Windows;
+
+namespace PixiEditor.Helpers.Converters;
+internal class MultiplyConverter : SingleInstanceConverter<MultiplyConverter>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        double? actuallyValue = NumberToDouble(value);
+        double? factor = NumberToDouble(parameter);
+        if (actuallyValue is null || factor is null)
+            return DependencyProperty.UnsetValue;
+        return actuallyValue * factor;
+    }
+
+    public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        return base.ConvertBack(value, targetType, parameter, culture);
+    }
+
+    private double? NumberToDouble(object number)
+    {
+        return number switch
+        {
+            int n => n,
+            uint n => n,
+            float n => n,
+            double n => n,
+            short n => n,
+            long n => n,
+            ulong n => n,
+            ushort n => n,
+            _ => null,
+        };
+    }
+}

+ 9 - 0
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -10,6 +10,7 @@ internal class ChangeExecutionController
     public MouseButtonState LeftMouseState { get; private set; }
     public VecI LastPixelPosition => lastPixelPos;
     public VecD LastPrecisePosition => lastPrecisePos;
+    public float LastOpacityValue = 1f;
     public bool IsChangeActive => currentSession is not null;
 
     private readonly DocumentViewModel document;
@@ -101,6 +102,14 @@ internal class ChangeExecutionController
         }
     }
 
+    public void OnOpacitySliderDragStarted() => currentSession?.OnOpacitySliderDragStarted();
+    public void OnOpacitySliderDragged(float newValue)
+    {
+        LastOpacityValue = newValue;
+        currentSession?.OnOpacitySliderDragged(newValue);
+    }
+    public void OnOpacitySliderDragEnded() => currentSession?.OnOpacitySliderDragEnded();
+
     public void OnLeftMouseButtonDown(VecD canvasPos)
     {
         //update internal state

+ 31 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/StructureMemberOpacityExecutor.cs

@@ -0,0 +1,31 @@
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+internal class StructureMemberOpacityExecutor : UpdateableChangeExecutor
+{
+    private Guid memberGuid;
+    public override OneOf<Success, Error> Start()
+    {
+        if (document.SelectedStructureMember is null)
+            return new Error();
+        memberGuid = document.SelectedStructureMember.GuidValue;
+        StructureMemberOpacity_Action action = new StructureMemberOpacity_Action(memberGuid, document.SelectedStructureMember.OpacityBindable);
+        helpers.ActionAccumulator.AddActions(action);
+        return new Success();
+    }
+
+    public override void OnOpacitySliderDragged(float newValue)
+    {
+        StructureMemberOpacity_Action action = new StructureMemberOpacity_Action(memberGuid, newValue);
+        helpers.ActionAccumulator.AddActions(action);
+    }
+
+    public override void OnOpacitySliderDragEnded()
+    {
+        helpers.ActionAccumulator.AddFinishedActions(new EndStructureMemberOpacity_Action());
+        onEnded?.Invoke(this);
+    }
+
+    public override void ForceStop()
+    {
+        helpers.ActionAccumulator.AddFinishedActions(new EndStructureMemberOpacity_Action());
+    }
+}

+ 3 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs

@@ -31,6 +31,9 @@ internal abstract class UpdateableChangeExecutor
     public virtual void OnPrecisePositionChange(VecD pos) { }
     public virtual void OnLeftMouseButtonDown(VecD pos) { }
     public virtual void OnLeftMouseButtonUp() { }
+    public virtual void OnOpacitySliderDragStarted() { }
+    public virtual void OnOpacitySliderDragged(float newValue) { }
+    public virtual void OnOpacitySliderDragEnded() { }
     public virtual void OnKeyDown(Key key) { }
     public virtual void OnKeyUp(Key key) { }
 }

+ 16 - 3
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -207,6 +207,15 @@ internal class DocumentViewModel : NotifyableObject
 
     #endregion
 
+    public void SetMemberOpacity(Guid memberGuid, float value)
+    {
+        if (Helpers.ChangeController.IsChangeActive || value is > 1 or < 0)
+            return;
+        Helpers.ActionAccumulator.AddFinishedActions(
+            new StructureMemberOpacity_Action(memberGuid, value),
+            new EndStructureMemberOpacity_Action());
+    }
+
     public void AddOrUpdateViewport(ViewportInfo info) => Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
 
     public void RemoveViewport(Guid viewportGuid) => Helpers.ActionAccumulator.AddActions(new RemoveViewport_PassthroughAction(viewportGuid));
@@ -219,16 +228,20 @@ internal class DocumentViewModel : NotifyableObject
 
     public void ClearSoftSelectedMembers() => Helpers.ActionAccumulator.AddActions(new ClearSoftSelectedMembers_PassthroughAction());
 
+    public void UseOpacitySlider() => Helpers.ChangeController.TryStartUpdateableChange<StructureMemberOpacityExecutor>();
+
     public void UsePenTool() => Helpers.ChangeController.TryStartUpdateableChange<LineBasedPenExecutor>();
 
 
     public void OnKeyDown(Key args) => Helpers.ChangeController.OnKeyDown(args);
-
     public void OnKeyUp(Key args) => Helpers.ChangeController.OnKeyUp(args);
 
     public void OnCanvasLeftMouseButtonDown(VecD pos) => Helpers.ChangeController.OnLeftMouseButtonDown(pos);
-
     public void OnCanvasMouseMove(VecD newPos) => Helpers.ChangeController.OnMouseMove(newPos);
-
     public void OnCanvasLeftMouseButtonUp() => Helpers.ChangeController.OnLeftMouseButtonUp();
+
+    public void OnOpacitySliderDragStarted() => Helpers.ChangeController.OnOpacitySliderDragStarted();
+    public void OnOpacitySliderDragged(float newValue) => Helpers.ChangeController.OnOpacitySliderDragged(newValue);
+    public void OnOpacitySliderDragEnded() => Helpers.ChangeController.OnOpacitySliderDragEnded();
+
 }

+ 7 - 3
src/PixiEditor/ViewModels/SubViewModels/Document/StructureMemberViewModel.cs

@@ -2,12 +2,10 @@
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Enums;
-using SkiaSharp;
 
 namespace PixiEditor.ViewModels.SubViewModels.Document;
 #nullable enable
@@ -105,6 +103,12 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
     public float OpacityBindable
     {
         get => opacity;
+        // this is stupid. This setter shouldn't actually exist, but it has to because the NumberInput control is badly designed.
+        // You can't bind it's value using a OneWay binding, because it gets overwritten by NumberInput internally when it assigns something to Value
+        // This doesn't happen with TwoWay bindings because they behave differently and forward the value you set instead of getting replaced
+        // So really I just need a OneWay binding, but I'm forced to use a TwoWay binding with a setter
+        // The binding is in LayersManager's opacity field btw.
+        set { }
     }
 
     public StructureMemberSelectionType Selection { get; set; }
@@ -147,7 +151,7 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
         EndOpacityUpdateCommand = new(EndOpacityUpdate);
 
         this.guidValue = guidValue;
-        var previewSize = CalculatePreviewSize(doc.SizeBindable);
+        VecI previewSize = CalculatePreviewSize(doc.SizeBindable);
         PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
         PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
     }

+ 21 - 1
src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -2,7 +2,6 @@
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Commands.Attributes.Commands;
-using PixiEditor.Models.Commands.Attributes.Evaluators;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main;
 #nullable enable
@@ -150,6 +149,27 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         return Owner.DocumentManagerSubViewModel.ActiveDocument is not null;
     }
 
+    [Command.Internal("PixiEditor.Layer.OpacitySliderDragStarted")]
+    public void OpacitySliderDragStarted(object paramenter)
+    {
+        Owner.DocumentManagerSubViewModel.ActiveDocument?.UseOpacitySlider();
+        Owner.DocumentManagerSubViewModel.ActiveDocument?.OnOpacitySliderDragStarted();
+    }
+
+    [Command.Internal("PixiEditor.Layer.OpacitySliderDragged")]
+    public void OpacitySliderDragged(object paramenter)
+    {
+        if (paramenter is not double value)
+            return;
+        Owner.DocumentManagerSubViewModel.ActiveDocument?.OnOpacitySliderDragged((float)value);
+    }
+
+    [Command.Internal("PixiEditor.Layer.OpacitySliderDragEnded")]
+    public void OpacitySliderDragEnded(object paramenter)
+    {
+        Owner.DocumentManagerSubViewModel.ActiveDocument?.OnOpacitySliderDragEnded();
+    }
+
     public void SetActiveLayer(object parameter)
     {
         //int index = (int)parameter;

+ 1 - 4
src/PixiEditor/Views/MainWindow.xaml

@@ -665,10 +665,7 @@
                                             CanDockAsTabbedDocument="True"
                                             CanFloat="True">
                                             <layerUserControls:LayersManager
-                                                StructureRoot="{Binding DocumentManagerSubViewModel.ActiveDocument.StructureRoot}"
-                                                OpacityInputEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument, 
-                    Converter={converters:NotNullToBoolConverter}}">
-                                            </layerUserControls:LayersManager>
+                                                ActiveDocument="{Binding DocumentManagerSubViewModel.ActiveDocument}"/>
                                         </LayoutAnchorable>
                                     </LayoutAnchorablePane>
                                     <LayoutAnchorablePane>

+ 43 - 16
src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -6,7 +6,10 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:docVm="clr-namespace:PixiEditor.ViewModels.SubViewModels.Document"
              xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
+             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:beh="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:vws="clr-namespace:PixiEditor.Views" 
+             xmlns:sys="clr-namespace:System;assembly=mscorlib" 
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" 
              xmlns:commands="clr-namespace:PixiEditor.Models.Commands.XAML" 
              xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers"
@@ -23,7 +26,6 @@
             <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
                 <Button Command="{commands:Command PixiEditor.Layer.NewLayer}" 
                         Height="24" Width="24" Cursor="Hand" ToolTip="New Layer"
-                        CommandParameter="{Binding Path=SelectedItem, ElementName=layersManager}"
                                                 HorizontalAlignment="Stretch" Margin="5"
                                                 Style="{StaticResource ToolButtonStyle}">
                     <Button.Background>
@@ -31,7 +33,6 @@
                     </Button.Background>
                 </Button>
                 <Button Command="{commands:Command PixiEditor.Layer.NewFolder}" 
-                        CommandParameter="{Binding Path=SelectedItem, ElementName=layersManager}"
                         Height="24" Width="24" ToolTip="New Group" Cursor="Hand"
                                                 HorizontalAlignment="Stretch" Margin="5"
                                                 Style="{StaticResource ToolButtonStyle}">
@@ -39,8 +40,7 @@
                         <ImageBrush ImageSource="/Images/Folder-add.png"/>
                     </Button.Background>
                 </Button>
-                <Button Command="{commands:Command PixiEditor.Layer.DeleteSelected}"
-                        CommandParameter="{Binding ElementName=layersManager, Path=SelectedItem}" Height="24" Width="24" ToolTip="Delete selected" Cursor="Hand"
+                <Button Command="{commands:Command PixiEditor.Layer.DeleteSelected}" Height="24" Width="24" ToolTip="Delete selected" Cursor="Hand"
                                                 HorizontalAlignment="Stretch" Margin="5"
                                                 Style="{StaticResource ToolButtonStyle}">
                     <Button.Background>
@@ -49,14 +49,44 @@
                 </Button>
             </StackPanel>
             <StackPanel Orientation="Horizontal" DockPanel.Dock="Right" Margin="0,0,10,0" HorizontalAlignment="Right" Focusable="True">
-                <Label Content="Opacity" Foreground="White" VerticalAlignment="Center"/>
+                <Slider
+                    Width="50"
+                    Minimum="0"
+                    Maximum="1"
+                    SmallChange="0.01"
+                    LargeChange="0.1"
+                    IsSnapToTickEnabled="False"
+                    x:Name="opacitySlider"
+                    VerticalAlignment="Center"
+                    HorizontalAlignment="Stretch"
+                    Value="{Binding ElementName=layersManager, Path=ActiveDocument.SelectedStructureMember.OpacityBindable, Mode=OneWay}">
+                    <i:Interaction.Behaviors>
+                        <beh:SliderUpdateBehavior
+                            DragStarted="{commands:Command PixiEditor.Layer.OpacitySliderDragStarted}"
+                            DragValueChanged="{commands:Command PixiEditor.Layer.OpacitySliderDragged, UseProvided=True}"
+                            DragEnded="{commands:Command PixiEditor.Layer.OpacitySliderDragEnded}"
+                            ValueFromSlider="{Binding ElementName=opacitySlider, Path=Value}" />
+                    </i:Interaction.Behaviors>
+                </Slider>
                 <userControls:NumberInput
-                        Min="0" Max="100"
-                        x:Name="numberInput"
-                        IsEnabled="{Binding Path=OpacityInputEnabled, ElementName=layersManager}" 
-                        Width="40" Height="20"
-                        VerticalAlignment="Center"
-                        LostFocus="NumberInput_LostFocus"/>
+                    Min="0" Max="100"
+                    x:Name="numberInput"
+                    IsEnabled="{Binding Path=ActiveDocument, ElementName=layersManager, Converter={converters:NotNullToVisibilityConverter}}" 
+                    Width="40" Height="20"
+                    VerticalAlignment="Center"
+                    LostFocus="NumberInput_LostFocus">
+                    <userControls:NumberInput.Value>
+                        <Binding
+                            Mode="TwoWay"
+                            ElementName="layersManager"
+                            Path="ActiveDocument.SelectedStructureMember.OpacityBindable"
+                            Converter="{converters:MultiplyConverter}">
+                            <Binding.ConverterParameter>
+                                <sys:Double>100</sys:Double>
+                            </Binding.ConverterParameter>
+                        </Binding>
+                    </userControls:NumberInput.Value>
+                </userControls:NumberInput>
                 <Label Content="%" Foreground="White" VerticalAlignment="Center"/>
             </StackPanel>
         </DockPanel>
@@ -64,11 +94,10 @@
         <DockPanel LastChildFill="True" Grid.Row="2" Margin="0, -12, 0, 0">
             <layerUserControls:ReferenceLayer
                 DockPanel.Dock="Bottom"
-                Visibility="{Binding Path=OpacityInputEnabled, ElementName=layersManager, 
-                Converter={BoolToVisibilityConverter}}" 
+                Visibility="{Binding Path=ActiveDocument, ElementName=layersManager, Converter={converters:NotNullToVisibilityConverter}}" 
                 Background="{StaticResource MainColor}" 
                 Grid.Row="3" VerticalAlignment="Bottom"/>
-            <TreeView DockPanel.Dock="Top" Name="treeView" ItemsSource="{Binding StructureRoot.Children, ElementName=layersManager}"  SelectedItemChanged="TreeView_SelectedItemChanged">
+            <TreeView DockPanel.Dock="Top" Name="treeView" ItemsSource="{Binding ActiveDocument.StructureRoot.Children, ElementName=layersManager}">
                 <TreeView.ItemsPanel>
                     <ItemsPanelTemplate>
                         <ui:ReversedOrderStackPanel/>
@@ -78,13 +107,11 @@
                     <HierarchicalDataTemplate DataType="{x:Type docVm:FolderViewModel}" ItemsSource="{Binding Children}">
                         <layerUserControls:FolderControl
                             Folder="{Binding}"
-                            MouseDown="SelectActiveItem"
                             MouseMove="LayerGroup_MouseMove"/>
                     </HierarchicalDataTemplate>
                     <DataTemplate DataType="{x:Type docVm:LayerViewModel}">
                         <layerUserControls:LayerControl
                             Layer="{Binding}"
-                            MouseDown="SelectActiveItem"
                             MouseMove="LayerStructureItemContainer_MouseMove"/>
                     </DataTemplate>
                 </TreeView.Resources>

+ 9 - 137
src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -8,35 +8,15 @@ namespace PixiEditor.Views.UserControls.Layers;
 
 internal partial class LayersManager : UserControl
 {
+    public static readonly DependencyProperty ActiveDocumentProperty =
+        DependencyProperty.Register(nameof(ActiveDocument), typeof(DocumentViewModel), typeof(LayersManager), new(null));
 
-    public static readonly DependencyProperty SelectedItemProperty =
-        DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(LayersManager), new PropertyMetadata(0));
-    public object SelectedItem
+    public DocumentViewModel ActiveDocument
     {
-        get { return (object)GetValue(SelectedItemProperty); }
-        set { SetValue(SelectedItemProperty, value); }
+        get => (DocumentViewModel)GetValue(ActiveDocumentProperty);
+        set => SetValue(ActiveDocumentProperty, value);
     }
 
-
-    public static readonly DependencyProperty OpacityInputEnabledProperty =
-        DependencyProperty.Register(nameof(OpacityInputEnabled), typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
-
-    public bool OpacityInputEnabled
-    {
-        get { return (bool)GetValue(OpacityInputEnabledProperty); }
-        set { SetValue(OpacityInputEnabledProperty, value); }
-    }
-
-    public static readonly DependencyProperty StructureRootProperty =
-        DependencyProperty.Register(nameof(StructureRoot), typeof(FolderViewModel), typeof(LayersManager), new(null));
-
-    public FolderViewModel StructureRoot
-    {
-        get => (FolderViewModel)GetValue(StructureRootProperty);
-        set => SetValue(StructureRootProperty, value);
-    }
-
-
     public LayersManager()
     {
         InitializeComponent();
@@ -53,45 +33,6 @@ internal partial class LayersManager : UserControl
         }
         */
     }
-    /*
-    private void HandleGroupOpacityChange(GuidStructureItem group, float value)
-    {
-        if (LayerCommandsViewModel.Owner?.BitmapManager?.ActiveDocument != null)
-        {
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-            if (group.Opacity == value)
-                return;
-
-            var processArgs = new object[] { group.GroupGuid, value };
-            var reverseProcessArgs = new object[] { group.GroupGuid, group.Opacity };
-
-            ChangeGroupOpacityProcess(processArgs);
-
-            LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure.ExpandParentGroups(group);
-
-            doc.UndoManager.AddUndoChange(
-                new Change(
-                    ChangeGroupOpacityProcess,
-                    reverseProcessArgs,
-                    ChangeGroupOpacityProcess,
-                    processArgs,
-                    $"Change {group.Name} opacity"), false);
-        }
-    }
-
-    private void ChangeGroupOpacityProcess(object[] processArgs)
-    {
-        if (processArgs.Length > 0 && processArgs[0] is Guid groupGuid && processArgs[1] is float opacity)
-        {
-            var structure = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure;
-            var group = structure.GetGroupByGuid(groupGuid);
-            group.Opacity = opacity;
-            var layers = structure.GetGroupLayers(group);
-            layers.ForEach(x => x.Opacity = x.Opacity); // This might seems stupid, but it raises property changed, without setting any value. This is used to trigger converters that use group opacity
-            numberInput.Value = opacity * 100;
-        }
-    }*/
 
     private void LayerGroup_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
     {
@@ -106,76 +47,12 @@ internal partial class LayersManager : UserControl
 
     private void NumberInput_LostFocus(object sender, RoutedEventArgs e)
     {
-        float val = numberInput.Value / 100f;
-
-        object item = SelectedItem;
-        /*
-        if (item is Layer || item is LayerStructureItemContainer)
-        {
-            Layer layer = null;
-
-            if (item is Layer lr)
-            {
-                layer = lr;
-            }
-            else if (item is LayerStructureItemContainer container)
-            {
-                layer = container.Layer;
-            }
-
-            HandleLayerOpacityChange(val, layer);
-        }
-        else if (item is LayerGroup group)
-        {
-            HandleGroupOpacityChange(group.StructureData, val);
-        }
-        else if (item is LayerGroupControl groupControl)
-        {
-            HandleGroupOpacityChange(groupControl.GroupData, val);
-        }
-        */
-        ShortcutController.UnblockShortcutExecutionAll();
-    }
-    /*
-    private void HandleLayerOpacityChange(float val, Layer layer)
-    {
-        float oldOpacity = layer.Opacity;
-        if (oldOpacity == val)
+        if (ActiveDocument?.SelectedStructureMember is null)
             return;
+        ActiveDocument.SetMemberOpacity(ActiveDocument.SelectedStructureMember.GuidValue, numberInput.Value / 100f);
 
-        var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-        doc.RaisePropertyChange(nameof(doc.LayerStructure));
-
-        layer.OpacityUndoTriggerable = val;
-
-        doc.LayerStructure.ExpandParentGroups(layer.GuidValue);
-
-        doc.RaisePropertyChange(nameof(doc.LayerStructure));
-
-        UndoManager undoManager = doc.UndoManager;
-
-
-        undoManager.AddUndoChange(
-            new Change(
-                UpdateNumberInputLayerOpacityProcess,
-                new object[] { oldOpacity },
-                UpdateNumberInputLayerOpacityProcess,
-                new object[] { val }));
-        undoManager.SquashUndoChanges(2);
-    }
-
-    private void UpdateNumberInputLayerOpacityProcess(object[] args)
-    {
-        if (args.Length > 0 && args[0] is float opacity)
-        {
-            numberInput.Value = opacity * 100;
-        }
-    }
-    */
-    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
-    {
-        //SetInputOpacity(SelectedItem);
+        // does anyone know why this is here?
+        ShortcutController.UnblockShortcutExecutionAll();
     }
 
     private void Grid_Drop(object sender, DragEventArgs e)
@@ -222,9 +99,4 @@ internal partial class LayersManager : UserControl
     {
         ((Border)sender).BorderBrush = Brushes.Transparent;
     }
-
-    private void SelectActiveItem(object sender, System.Windows.Input.MouseButtonEventArgs e)
-    {
-        SelectedItem = sender;
-    }
 }

+ 6 - 6
src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs

@@ -1,8 +1,8 @@
-using Microsoft.Xaml.Behaviors;
-using System.Windows;
+using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls.Primitives;
 using System.Windows.Input;
+using Microsoft.Xaml.Behaviors;
 
 namespace PixiEditorPrototype.Behaviors;
 
@@ -52,7 +52,7 @@ internal class SliderUpdateBehavior : Behavior<Slider>
         if (attached)
             return;
         attached = true;
-        var thumb = GetThumb(AssociatedObject);
+        Thumb? thumb = GetThumb(AssociatedObject);
         if (thumb is null)
             return;
 
@@ -65,7 +65,7 @@ internal class SliderUpdateBehavior : Behavior<Slider>
         AssociatedObject.Loaded -= AssociatedObject_Loaded;
         if (!attached)
             return;
-        var thumb = GetThumb(AssociatedObject);
+        Thumb? thumb = GetThumb(AssociatedObject);
         if (thumb is null)
             return;
 
@@ -75,7 +75,7 @@ internal class SliderUpdateBehavior : Behavior<Slider>
 
     private static void OnSliderValuePropertyChange(DependencyObject slider, DependencyPropertyChangedEventArgs e)
     {
-        var obj = (SliderUpdateBehavior)slider;
+        SliderUpdateBehavior? obj = (SliderUpdateBehavior)slider;
         if (obj.dragging)
         {
             if (obj.DragValueChanged is not null && obj.DragValueChanged.CanExecute(e.NewValue))
@@ -99,7 +99,7 @@ internal class SliderUpdateBehavior : Behavior<Slider>
 
     private static Thumb? GetThumb(Slider slider)
     {
-        var track = slider.Template.FindName("PART_Track", slider) as Track;
+        Track? track = slider.Template.FindName("PART_Track", slider) as Track;
         return track is null ? null : track.Thumb;
     }