Ver código fonte

Reference layer wip

flabbet 4 anos atrás
pai
commit
8f45f54406

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Views.UserControls;
+using PixiEditor.Views.UserControls.Layers;
 using System;
 using System.Linq;
 using System.Windows.Input;

+ 6 - 6
PixiEditor/Views/MainWindow.xaml

@@ -13,7 +13,7 @@
         xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
-        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
+        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
         mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         WindowStartupLocation="CenterScreen" WindowState="Maximized">
@@ -299,20 +299,20 @@
                                     <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
                                                          CanClose="False" CanAutoHide="False"
                                                          CanDockAsTabbedDocument="True" CanFloat="True">
-                                        <usercontrols:LayersManager                                            
+                                        <layerUserControls:LayersManager                                            
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
                     Converter={StaticResource NotNullToBoolConverter}}">
-                                            <usercontrols:LayersManager.LayerTreeRoot>
+                                            <layerUserControls:LayersManager.LayerTreeRoot>
                                                 <MultiBinding Converter="{StaticResource LayersToStructuredLayersConverter}">
                                                     <Binding Path="BitmapManager.ActiveDocument.Layers" />
                                                     <Binding Path="BitmapManager.ActiveDocument.LayerStructure"/>
                                                 </MultiBinding>
-                                            </usercontrols:LayersManager.LayerTreeRoot>
-                                        </usercontrols:LayersManager>
+                                            </layerUserControls:LayersManager.LayerTreeRoot>
+                                        </layerUserControls:LayersManager>
                                     </LayoutAnchorable>
                                     <LayoutAnchorable x:Name="rawLayerAnchorable" ContentId="rawLayer" Title="Raw layers">
-                                        <usercontrols:RawLayersViewer Layers="{Binding BitmapManager.ActiveDocument.Layers}"
+                                        <layerUserControls:RawLayersViewer Layers="{Binding BitmapManager.ActiveDocument.Layers}"
                                                                       Structure="{Binding BitmapManager.ActiveDocument.LayerStructure}"/>
                                     </LayoutAnchorable>
                                 </LayoutAnchorablePane>

+ 1 - 1
PixiEditor/Views/UserControls/LayerGroupControl.xaml → PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayerGroupControl"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerGroupControl"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 34 - 34
PixiEditor/Views/UserControls/LayerGroupControl.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs

@@ -9,7 +9,7 @@ using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
     /// <summary>
     /// Interaction logic for LayerFolder.xaml.
@@ -53,9 +53,9 @@ namespace PixiEditor.Views.UserControls
         }
 
         public static readonly DependencyProperty GroupOpacityProperty =
-            DependencyProperty.Register("GroupOpacity", typeof(float), typeof(LayerGroupControl), new PropertyMetadata(1f));
-
-
+            DependencyProperty.Register("GroupOpacity", typeof(float), typeof(LayerGroupControl), new PropertyMetadata(1f));
+
+
         private static void LayersViewModelCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
             LayerGroupControl control = (LayerGroupControl)d;
@@ -129,9 +129,9 @@ namespace PixiEditor.Views.UserControls
             item.Background = LayerItem.HighlightColor;
         }
 
-        private void Grid_CenterEnter(object sender, DragEventArgs e)
-        {
-            centerGrid.Background = LayerItem.HighlightColor;
+        private void Grid_CenterEnter(object sender, DragEventArgs e)
+        {
+            centerGrid.Background = LayerItem.HighlightColor;
         }
 
         private void Grid_DragLeave(object sender, DragEventArgs e)
@@ -205,10 +205,10 @@ namespace PixiEditor.Views.UserControls
             document.MoveGroupInStructure(group, tempLayer.LayerGuid, above);
             document.LayerStructure.AssignParent(tempLayer.LayerGuid, null);
             document.RemoveLayer(tempLayer, false);
-        }
-
-        private void HandleDropInside(IDataObject dataObj, Grid grid)
-        {
+        }
+
+        private void HandleDropInside(IDataObject dataObj, Grid grid)
+        {
             Guid referenceLayer = GroupData.EndLayerGuid;
             LayerItem.RemoveDragEffect(grid);
 
@@ -220,20 +220,20 @@ namespace PixiEditor.Views.UserControls
             if (dataObj.GetDataPresent(LayerGroupControlDataName))
             {
                 HandleGroupControlDrop(dataObj, referenceLayer, true, true);
-            }
+            }
         }
 
         private void Grid_Drop_Top(object sender, DragEventArgs e)
         {
             HandleDrop(e.Data, (Grid)sender, true);
-        }
-
-        private void Grid_Drop_Center(object sender, DragEventArgs e)
-        {
-            HandleDropInside(e.Data, (Grid)sender);
-            LayerItem.RemoveDragEffect(centerGrid);
-        }
-
+        }
+
+        private void Grid_Drop_Center(object sender, DragEventArgs e)
+        {
+            HandleDropInside(e.Data, (Grid)sender);
+            LayerItem.RemoveDragEffect(centerGrid);
+        }
+
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         {
             HandleDrop(e.Data, (Grid)sender, false);
@@ -243,9 +243,9 @@ namespace PixiEditor.Views.UserControls
         {
             var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
             var layer = doc.Layers.First(x => x.LayerGuid == GroupData.EndLayerGuid);
-            if (doc.ActiveLayerGuid != layer.LayerGuid)
-            {
-                doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
+            if (doc.ActiveLayerGuid != layer.LayerGuid)
+            {
+                doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
             }
         }
 
@@ -297,16 +297,16 @@ namespace PixiEditor.Views.UserControls
 
                 IsVisibleUndoTriggerable = value;
             }
-        }
-
-        private void GroupControl_DragEnter(object sender, DragEventArgs e)
-        {
-            middleDropGrid.Visibility = Visibility.Visible;
-        }
-
-        private void GroupControl_DragLeave(object sender, DragEventArgs e)
-        {
-            middleDropGrid.Visibility = Visibility.Collapsed;
-        }
+        }
+
+        private void GroupControl_DragEnter(object sender, DragEventArgs e)
+        {
+            middleDropGrid.Visibility = Visibility.Visible;
+        }
+
+        private void GroupControl_DragLeave(object sender, DragEventArgs e)
+        {
+            middleDropGrid.Visibility = Visibility.Collapsed;
+        }
     }
 }

+ 1 - 1
PixiEditor/Views/UserControls/LayerItem.xaml → PixiEditor/Views/UserControls/Layers/LayerItem.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.LayerItem"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerItem"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 11 - 11
PixiEditor/Views/UserControls/LayerItem.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerItem.xaml.cs

@@ -8,7 +8,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditor.Views
+namespace PixiEditor.Views.UserControls.Layers
 {
     /// <summary>
     /// Interaction logic for LayerItem.xaml.
@@ -163,10 +163,10 @@ namespace PixiEditor.Views
                 var doc = data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
 
                 doc.MoveLayerInStructure(layer, LayerGuid, above);
-                if (dropInParentFolder)
-                {
-                    Guid? groupGuid = doc.LayerStructure.GetGroupByLayer(layer)?.Parent?.GroupGuid;
-                    doc.LayerStructure.AssignParent(layer, groupGuid);
+                if (dropInParentFolder)
+                {
+                    Guid? groupGuid = doc.LayerStructure.GetGroupByLayer(layer)?.Parent?.GroupGuid;
+                    doc.LayerStructure.AssignParent(layer, groupGuid);
                 }
             }
 
@@ -196,11 +196,11 @@ namespace PixiEditor.Views
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         {
             HandleGridDrop(sender, e, false);
-        }
-
-        private void Grid_Drop_Below(object sender, DragEventArgs e)
-        {
-            HandleGridDrop(sender, e, false, true);
-        }
+        }
+
+        private void Grid_Drop_Below(object sender, DragEventArgs e)
+        {
+            HandleGridDrop(sender, e, false, true);
+        }
     }
 }

+ 2 - 2
PixiEditor/Views/UserControls/LayerStructureItemContainer.xaml → PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml

@@ -1,9 +1,9 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayerStructureItemContainer"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerStructureItemContainer"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:vws="clr-namespace:PixiEditor.Views" xmlns:layers="clr-namespace:PixiEditor.Models.Layers" d:DataContext="{d:DesignInstance Type=layers:Layer}"
+             xmlns:vws="clr-namespace:PixiEditor.Views.UserControls.Layers" xmlns:layers="clr-namespace:PixiEditor.Models.Layers" d:DataContext="{d:DesignInstance Type=layers:Layer}"
              mc:Ignorable="d"
              d:DesignHeight="60" d:DesignWidth="250" Name="layerStructureContainer">
     <vws:LayerItem Tag="{Binding ElementName=layerStructureContainer}"

+ 1 - 1
PixiEditor/Views/UserControls/LayerStructureItemContainer.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml.cs

@@ -3,7 +3,7 @@ using System.Windows.Controls;
 using PixiEditor.Models.Layers;
 using PixiEditor.ViewModels.SubViewModels.Main;
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
     /// <summary>
     /// Interaction logic for LayerStructureItemContainer.xaml.

+ 6 - 5
PixiEditor/Views/UserControls/LayersManager.xaml → PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -1,13 +1,12 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayersManager"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayersManager"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
-             xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
              xmlns:vws="clr-namespace:PixiEditor.Views" 
              xmlns:layers="clr-namespace:PixiEditor.Models.Layers"
-             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" 
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="250" x:Name="layersManager">
     <UserControl.Resources>
@@ -18,6 +17,7 @@
             <RowDefinition Height="37.5"/>
             <RowDefinition Height="15"/>
             <RowDefinition Height="1*"/>
+            <RowDefinition Height="40"/>
         </Grid.RowDefinitions>
         <DockPanel Background="{StaticResource MainColor}" Grid.Row="0" HorizontalAlignment="Stretch">
             <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
@@ -70,7 +70,7 @@
                 </TreeView.ItemsPanel>
                 <TreeView.Resources>
                     <HierarchicalDataTemplate DataType="{x:Type layers:LayerGroup}" ItemsSource="{Binding Items}">
-                        <local:LayerGroupControl GroupName="{Binding Name}" MouseDown="SelectActiveItem"
+                        <layerUserControls:LayerGroupControl GroupName="{Binding Name}" MouseDown="SelectActiveItem"
                                              IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
                                              GroupOpacity="{Binding StructureData.Opacity}"
                                              LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
@@ -79,7 +79,7 @@
                                              MouseMove="LayerGroup_MouseMove"/>
                     </HierarchicalDataTemplate>
                     <DataTemplate DataType="{x:Type layers:Layer}">
-                        <local:LayerStructureItemContainer    
+                        <layerUserControls:LayerStructureItemContainer    
                             MouseDown="SelectActiveItem"
                             MouseMove="LayerStructureItemContainer_MouseMove" 
                             ContainerIndex="{Binding Converter={StaticResource IndexOfConverter}}"
@@ -89,5 +89,6 @@
             </TreeView>
             <Border Name="dropBorder" DragEnter="Grid_DragEnter" DragLeave="Grid_DragLeave" AllowDrop="True" Drop="Grid_Drop" Background="Transparent" BorderThickness="0, 5, 0, 0"></Border>
         </DockPanel>
+        <layerUserControls:ReferenceLayer Background="{StaticResource MainColor}" Grid.Row="3" VerticalAlignment="Bottom"/>
     </Grid>
 </UserControl>

+ 250 - 250
PixiEditor/Views/UserControls/LayersManager.xaml.cs → PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -1,5 +1,5 @@
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels.SubViewModels.Main;
@@ -7,25 +7,25 @@ using System;
 using System.Collections.ObjectModel;
 using System.Windows;
 using System.Windows.Controls;
-using System.Windows.Media;
+using System.Windows.Media;
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
     /// <summary>
     /// Interaction logic for LayersManager.xaml.
     /// </summary>
     public partial class LayersManager : UserControl
-    {
-        public object SelectedItem
-        {
-            get { return (object)GetValue(SelectedItemProperty); }
-            set { SetValue(SelectedItemProperty, value); }
-        }
-
-        public static readonly DependencyProperty SelectedItemProperty =
-            DependencyProperty.Register("SelectedItem", typeof(object), typeof(LayersManager), new PropertyMetadata(0));
-
-
+    {
+        public object SelectedItem
+        {
+            get { return (object)GetValue(SelectedItemProperty); }
+            set { SetValue(SelectedItemProperty, value); }
+        }
+
+        public static readonly DependencyProperty SelectedItemProperty =
+            DependencyProperty.Register("SelectedItem", typeof(object), typeof(LayersManager), new PropertyMetadata(0));
+
+
         public ObservableCollection<object> LayerTreeRoot
         {
             get { return (ObservableCollection<object>)GetValue(LayerTreeRootProperty); }
@@ -45,240 +45,240 @@ namespace PixiEditor.Views.UserControls
         }
 
         public static readonly DependencyProperty LayerCommandsViewModelProperty =
-            DependencyProperty.Register("LayerCommandsViewModel", typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
-
-        public bool OpacityInputEnabled
-        {
-            get { return (bool)GetValue(OpacityInputEnabledProperty); }
-            set { SetValue(OpacityInputEnabledProperty, value); }
-        }
-
-        public static readonly DependencyProperty OpacityInputEnabledProperty =
-            DependencyProperty.Register("OpacityInputEnabled", typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
-
-        public LayersManager()
-        {
-            InitializeComponent();
-        }
-
-        private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            if (e.NewValue is LayersViewModel vm)
-            {
-                LayersManager manager = (LayersManager)d;
-                vm.Owner.BitmapManager.AddPropertyChangedCallback(nameof(vm.Owner.BitmapManager.ActiveDocument), () =>
-                {
-                    var doc = vm.Owner.BitmapManager.ActiveDocument;
-                    if (doc != null)
-                    {
-                        if (doc.ActiveLayer != null)
-                        {
-                            manager.SetActiveLayerAsSelectedItem(doc);
-                        }
-                        doc.AddPropertyChangedCallback(nameof(doc.ActiveLayer), () =>
-                        {
-                            manager.SetActiveLayerAsSelectedItem(doc);
-                        });
-                    }
-                });
-            }
-        }
-
-        private void SetActiveLayerAsSelectedItem(Document doc)
-        {
-            SelectedItem = doc.ActiveLayer;
-            SetInputOpacity(SelectedItem);
-        }
-
-        private void SetInputOpacity(object item)
-        {
-            if (item is Layer layer)
-            {
-                numberInput.Value = layer.Opacity * 100f;
-            }
-            else if (item is LayerStructureItemContainer container)
-            {
-                numberInput.Value = container.Layer.Opacity * 100f;
-            }
-            else if (item is LayerGroup group)
-            {
-                numberInput.Value = group.StructureData.Opacity * 100f;
-            }
-            else if (item is LayerGroupControl groupControl)
-            {
-                numberInput.Value = groupControl.GroupData.Opacity * 100f;
-            }
-        }
-
-        private void LayerStructureItemContainer_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
-        {
-            if (sender is LayerStructureItemContainer container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
-            {
-                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
-            }
-        }
-
-        private void HandleGroupOpacityChange(GuidStructureItem group, float value)
-        {
-            if (LayerCommandsViewModel.Owner?.BitmapManager?.ActiveDocument != null)
-            {
-                var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-                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)
-        {
-            if (sender is LayerGroupControl container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
-            {
-                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
-            }
-        }
-
-        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);
-            }
-        }
-
-        private void HandleLayerOpacityChange(float val, Layer layer)
-        {
-            float oldOpacity = layer.Opacity;
-
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-            doc.RaisePropertyChange(nameof(doc.LayerStructure));
-
-            layer.OpacityUndoTriggerable = val;
-
-            doc.LayerStructure.ExpandParentGroups(layer.LayerGuid);
-
-            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);
-        }
-
-        private void Grid_Drop(object sender, DragEventArgs e)
-        {
-            dropBorder.BorderBrush = Brushes.Transparent;
-
-            if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
-            {
-                HandleLayerDrop(e.Data);
-            }
-
-            if (e.Data.GetDataPresent(LayerGroupControl.LayerGroupControlDataName))
-            {
-                HandleGroupControlDrop(e.Data);
-            }
-        }
-
-        private void HandleLayerDrop(IDataObject data)
-        {
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-            if (doc.Layers.Count == 0) return;
-
-            var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
-            var refLayer = doc.Layers[0].LayerGuid;
-            doc.MoveLayerInStructure(layerContainer.Layer.LayerGuid, refLayer);
-            doc.LayerStructure.AssignParent(layerContainer.Layer.LayerGuid, null);
-        }
-
-        private void HandleGroupControlDrop(IDataObject data)
-        {
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-            var groupContainer = (LayerGroupControl)data.GetData(LayerGroupControl.LayerGroupControlDataName);
-            doc.LayerStructure.MoveGroup(groupContainer.GroupGuid, 0);
-        }
-
-        private void Grid_DragEnter(object sender, DragEventArgs e)
-        {
-            ((Border)sender).BorderBrush = LayerItem.HighlightColor;
-        }
-
-        private void Grid_DragLeave(object sender, DragEventArgs e)
-        {
-            ((Border)sender).BorderBrush = Brushes.Transparent;
-        }
-
-        private void SelectActiveItem(object sender, System.Windows.Input.MouseButtonEventArgs e)
-        {
-            SelectedItem = sender;
-        }
+            DependencyProperty.Register("LayerCommandsViewModel", typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
+
+        public bool OpacityInputEnabled
+        {
+            get { return (bool)GetValue(OpacityInputEnabledProperty); }
+            set { SetValue(OpacityInputEnabledProperty, value); }
+        }
+
+        public static readonly DependencyProperty OpacityInputEnabledProperty =
+            DependencyProperty.Register("OpacityInputEnabled", typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
+
+        public LayersManager()
+        {
+            InitializeComponent();
+        }
+
+        private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            if (e.NewValue is LayersViewModel vm)
+            {
+                LayersManager manager = (LayersManager)d;
+                vm.Owner.BitmapManager.AddPropertyChangedCallback(nameof(vm.Owner.BitmapManager.ActiveDocument), () =>
+                {
+                    var doc = vm.Owner.BitmapManager.ActiveDocument;
+                    if (doc != null)
+                    {
+                        if (doc.ActiveLayer != null)
+                        {
+                            manager.SetActiveLayerAsSelectedItem(doc);
+                        }
+                        doc.AddPropertyChangedCallback(nameof(doc.ActiveLayer), () =>
+                        {
+                            manager.SetActiveLayerAsSelectedItem(doc);
+                        });
+                    }
+                });
+            }
+        }
+
+        private void SetActiveLayerAsSelectedItem(Document doc)
+        {
+            SelectedItem = doc.ActiveLayer;
+            SetInputOpacity(SelectedItem);
+        }
+
+        private void SetInputOpacity(object item)
+        {
+            if (item is Layer layer)
+            {
+                numberInput.Value = layer.Opacity * 100f;
+            }
+            else if (item is LayerStructureItemContainer container)
+            {
+                numberInput.Value = container.Layer.Opacity * 100f;
+            }
+            else if (item is LayerGroup group)
+            {
+                numberInput.Value = group.StructureData.Opacity * 100f;
+            }
+            else if (item is LayerGroupControl groupControl)
+            {
+                numberInput.Value = groupControl.GroupData.Opacity * 100f;
+            }
+        }
+
+        private void LayerStructureItemContainer_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
+        {
+            if (sender is LayerStructureItemContainer container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
+            {
+                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
+            }
+        }
+
+        private void HandleGroupOpacityChange(GuidStructureItem group, float value)
+        {
+            if (LayerCommandsViewModel.Owner?.BitmapManager?.ActiveDocument != null)
+            {
+                var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+
+                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)
+        {
+            if (sender is LayerGroupControl container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
+            {
+                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
+            }
+        }
+
+        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);
+            }
+        }
+
+        private void HandleLayerOpacityChange(float val, Layer layer)
+        {
+            float oldOpacity = layer.Opacity;
+
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+
+            doc.RaisePropertyChange(nameof(doc.LayerStructure));
+
+            layer.OpacityUndoTriggerable = val;
+
+            doc.LayerStructure.ExpandParentGroups(layer.LayerGuid);
+
+            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);
+        }
+
+        private void Grid_Drop(object sender, DragEventArgs e)
+        {
+            dropBorder.BorderBrush = Brushes.Transparent;
+
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
+            {
+                HandleLayerDrop(e.Data);
+            }
+
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerGroupControlDataName))
+            {
+                HandleGroupControlDrop(e.Data);
+            }
+        }
+
+        private void HandleLayerDrop(IDataObject data)
+        {
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+            if (doc.Layers.Count == 0) return;
+
+            var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
+            var refLayer = doc.Layers[0].LayerGuid;
+            doc.MoveLayerInStructure(layerContainer.Layer.LayerGuid, refLayer);
+            doc.LayerStructure.AssignParent(layerContainer.Layer.LayerGuid, null);
+        }
+
+        private void HandleGroupControlDrop(IDataObject data)
+        {
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+            var groupContainer = (LayerGroupControl)data.GetData(LayerGroupControl.LayerGroupControlDataName);
+            doc.LayerStructure.MoveGroup(groupContainer.GroupGuid, 0);
+        }
+
+        private void Grid_DragEnter(object sender, DragEventArgs e)
+        {
+            ((Border)sender).BorderBrush = LayerItem.HighlightColor;
+        }
+
+        private void Grid_DragLeave(object sender, DragEventArgs e)
+        {
+            ((Border)sender).BorderBrush = Brushes.Transparent;
+        }
+
+        private void SelectActiveItem(object sender, System.Windows.Input.MouseButtonEventArgs e)
+        {
+            SelectedItem = sender;
+        }
     }
 }

+ 1 - 1
PixiEditor/Views/UserControls/RawLayersViewer.xaml → PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.RawLayersViewer"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.RawLayersViewer"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 1 - 1
PixiEditor/Views/UserControls/RawLayersViewer.xaml.cs → PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml.cs

@@ -5,7 +5,7 @@ using System.Windows;
 using System.Windows.Controls;
 using PixiEditor.Models.Layers;
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
     /// <summary>
     /// Interaction logic for RawLayersViewer.xaml.

+ 38 - 0
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml

@@ -0,0 +1,38 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.ReferenceLayer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PixiEditor.Views.UserControls.Layers" xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:local1="clr-namespace:PixiEditor.Views.UserControls"
+             mc:Ignorable="d" 
+             d:DesignHeight="60" d:DesignWidth="250" VerticalAlignment="Center" Name="uc">
+    <Border BorderBrush="{StaticResource DarkerAccentColor}" BorderThickness="0 2 0 0" MinWidth="60" Height="40" Focusable="True">
+        <i:Interaction.Behaviors>
+            <behaviors:ClearFocusOnClickBehavior/>
+        </i:Interaction.Behaviors>
+        <Grid Height="35">
+            <Grid Background="Transparent"/>
+        <Grid Grid.Row="1" Grid.RowSpan="3" VerticalAlignment="Center">
+                <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="30"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+                <Grid MouseDown="Grid_MouseDown" Visibility="{Binding ReferenceLayerSelected, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Cursor="Hand" Grid.ColumnSpan="2" Grid.RowSpan="2" Background="Transparent" Panel.ZIndex="5"/>
+                <CheckBox Visibility="{Binding ReferenceLayerSelected, ElementName=uc, Converter={BoolToVisibilityConverter}}" Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
+                      IsThreeState="False" HorizontalAlignment="Center" 
+                      IsChecked="{Binding Path=IsLayerVisible, Mode=TwoWay, ElementName=uc}" Grid.Column="0" Height="16" />
+                <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center">
+                    <Border HorizontalAlignment="Left" Visibility="{Binding ReferenceLayerSelected, ElementName=uc, Converter={BoolToVisibilityConverter}}" Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
+                           Margin="5, 0, 10, 0">
+                        <Image Source="{Binding PreviewImage}" Stretch="Uniform" Width="25" Height="25" 
+                       RenderOptions.BitmapScalingMode="NearestNeighbor"/>
+                    </Border>
+                    <Image Margin="0 0 5 0" Width="20" Source="/Images/Layer-add.png"  Visibility="{Binding ReferenceLayerSelected, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}"/>
+
+                    <local1:PrependTextBlock Prepend="Select " Foreground="White" HidePrepend="{Binding ReferenceLayerSelected, ElementName=uc}"
+                                             FontSize="15" VerticalAlignment="Center" Text="Reference Layer" />
+                </StackPanel>
+            </Grid>
+    </Grid>
+    </Border>
+</UserControl>

+ 67 - 0
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace PixiEditor.Views.UserControls.Layers
+{
+    /// <summary>
+    /// Interaction logic for ReferenceLayer.xaml
+    /// </summary>
+    public partial class ReferenceLayer : UserControl
+    {
+        public WriteableBitmap PreviewImage
+        {
+            get { return (WriteableBitmap)GetValue(PreviewImageProperty); }
+            set { SetValue(PreviewImageProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for PreviewImage.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty PreviewImageProperty =
+            DependencyProperty.Register("PreviewImage", typeof(WriteableBitmap), typeof(ReferenceLayer), new PropertyMetadata(default(WriteableBitmap)));
+
+
+        public bool IsLayerVisible
+        {
+            get { return (bool)GetValue(IsLayerVisibleProperty); }
+            set { SetValue(IsLayerVisibleProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for IsLayerVisible.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty IsLayerVisibleProperty =
+            DependencyProperty.Register("IsLayerVisible", typeof(bool), typeof(ReferenceLayer), new PropertyMetadata(true));
+
+
+        public bool ReferenceLayerSelected
+        {
+            get { return (bool)GetValue(ReferenceLayerSelectedProperty); }
+            set { SetValue(ReferenceLayerSelectedProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for ReferenceLayerSelected.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ReferenceLayerSelectedProperty =
+            DependencyProperty.Register("ReferenceLayerSelected", typeof(bool), typeof(ReferenceLayer), new PropertyMetadata(false));
+
+
+
+        public ReferenceLayer()
+        {
+            InitializeComponent();
+        }
+
+        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+    }
+}

+ 2 - 2
PixiEditor/Views/UserControls/PrependTextBlock.xaml

@@ -8,7 +8,7 @@
              d:DesignHeight="450" d:DesignWidth="800"
              x:Name="uc">
     <StackPanel Orientation="Horizontal">
-        <TextBlock Text="{Binding Prepend, ElementName=uc}"/>
+        <TextBlock Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Text="{Binding Prepend, ElementName=uc}"/>
         
         <TextBlock>
             <TextBlock.Text>
@@ -19,6 +19,6 @@
             </TextBlock.Text>
         </TextBlock>
 
-        <TextBlock Text="{Binding Append, ElementName=uc}"/>
+        <TextBlock Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Text="{Binding Append, ElementName=uc}"/>
     </StackPanel>
 </UserControl>

+ 12 - 0
PixiEditor/Views/UserControls/PrependTextBlock.xaml.cs

@@ -65,6 +65,18 @@ namespace PixiEditor.Views.UserControls
             set => SetValue(AppendColorProperty, value);
         }
 
+        public bool HidePrepend
+        {
+            get { return (bool)GetValue(HidePrependProperty); }
+            set { SetValue(HidePrependProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for HidePrepend.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty HidePrependProperty =
+            DependencyProperty.Register("HidePrepend", typeof(bool), typeof(PrependTextBlock), new PropertyMetadata(false));
+
+
+
         public PrependTextBlock()
         {
             InitializeComponent();