diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index 33e0b522d..67a903f4e 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -148,6 +148,10 @@
..\packages\CUE.NET.1.0.2.2\lib\net45\CUE.NET.dllTrue
+
+ ..\packages\gong-wpf-dragdrop.0.1.4.3\lib\net40\GongSolutions.Wpf.DragDrop.dll
+ True
+ ..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net451\Hardcodet.Wpf.TaskbarNotification.dllTrue
@@ -418,6 +422,8 @@
TrueOffsets.settings
+
+
diff --git a/Artemis/Artemis/Models/Profiles/LayerModel.cs b/Artemis/Artemis/Models/Profiles/LayerModel.cs
index 2952d7e62..149dd7819 100644
--- a/Artemis/Artemis/Models/Profiles/LayerModel.cs
+++ b/Artemis/Artemis/Models/Profiles/LayerModel.cs
@@ -149,91 +149,7 @@ namespace Artemis.Models.Profiles
Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor())
};
}
-
- public void Reorder(LayerModel selectedLayer, bool moveUp)
- {
- // Fix the sorting just in case
- FixOrder();
-
- // Possibly remove selectedLayer from a folder
- if (selectedLayer.Parent?.LayerType == LayerType.Folder)
- {
- var parent = selectedLayer.Parent;
- var siblings = parent.Children;
- if ((selectedLayer == siblings.FirstOrDefault() && moveUp) ||
- (selectedLayer == siblings.LastOrDefault() && !moveUp))
- {
- // If selectedLayer is on the edge of a folder and moved off of it, remove it from the folder
- parent.Children.Remove(selectedLayer);
- if (parent.Parent != null)
- parent.Parent.Children.Add(selectedLayer);
- else
- parent.Profile.Layers.Add(selectedLayer);
-
- if (moveUp)
- selectedLayer.Order = parent.Order - 1;
- else
- selectedLayer.Order = parent.Order + 1;
-
- return;
- }
- }
-
- int newOrder;
- if (moveUp)
- newOrder = selectedLayer.Order - 1;
- else
- newOrder = selectedLayer.Order + 1;
-
- var target = Children.FirstOrDefault(l => l.Order == newOrder);
- if (target == null)
- return;
-
- ApplyReorder(selectedLayer, target, newOrder, moveUp);
- }
-
-
- public static void ApplyReorder(LayerModel selectedLayer, LayerModel target, int newOrder, bool moveUp)
- {
- if (target.LayerType == LayerType.Folder)
- {
- if (selectedLayer.Parent == null)
- selectedLayer.Profile.Layers.Remove(selectedLayer);
- else
- selectedLayer.Parent.Children.Remove(selectedLayer);
-
- target.Children.Add(selectedLayer);
- selectedLayer.Parent = target;
-
- if (moveUp)
- {
- var parentTarget = target.Children.OrderBy(c => c.Order).LastOrDefault();
- if (parentTarget != null)
- {
- parentTarget.Order--;
- selectedLayer.Order = parentTarget.Order + 1;
- }
- else
- selectedLayer.Order = 1;
- }
- else
- {
- var parentTarget = target.Children.OrderBy(c => c.Order).FirstOrDefault();
- if (parentTarget != null)
- {
- parentTarget.Order++;
- selectedLayer.Order = parentTarget.Order - 1;
- }
- else
- selectedLayer.Order = 1;
- }
- target.FixOrder();
- return;
- }
- target.Order = selectedLayer.Order;
- selectedLayer.Order = newOrder;
- }
-
+
public void FixOrder()
{
Children.Sort(l => l.Order);
diff --git a/Artemis/Artemis/Models/Profiles/ProfileModel.cs b/Artemis/Artemis/Models/Profiles/ProfileModel.cs
index 2537c3d8e..7e4325c94 100644
--- a/Artemis/Artemis/Models/Profiles/ProfileModel.cs
+++ b/Artemis/Artemis/Models/Profiles/ProfileModel.cs
@@ -86,23 +86,6 @@ namespace Artemis.Models.Profiles
return layer;
}
- public void Reorder(LayerModel selectedLayer, bool moveUp)
- {
- FixOrder();
-
- int newOrder;
- if (moveUp)
- newOrder = selectedLayer.Order - 1;
- else
- newOrder = selectedLayer.Order + 1;
-
- var target = Layers.FirstOrDefault(l => l.Order == newOrder);
- if (target == null)
- return;
-
- LayerModel.ApplyReorder(selectedLayer, target, newOrder, moveUp);
- }
-
public void FixOrder()
{
Layers.Sort(l => l.Order);
diff --git a/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroHighlightAdorner.cs b/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroHighlightAdorner.cs
new file mode 100644
index 000000000..2b5c7086d
--- /dev/null
+++ b/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroHighlightAdorner.cs
@@ -0,0 +1,75 @@
+// Direct copy-paste from GongSolutions.WPF.DragDrop - https://github.com/punker76/gong-wpf-dragdrop
+// All credit goes to their awesome library, I merely copied this in order to change the highlight color.
+
+// BSD 3-Clause License
+//
+// Copyright (c) 2015, Jan Karger (Steven Kirk)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of gong-wpf-dragdrop nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using GongSolutions.Wpf.DragDrop;
+using MahApps.Metro;
+
+namespace Artemis.Styles.DropTargetAdorners
+{
+ public class DropTargetMetroHighlightAdorner : DropTargetAdorner
+ {
+ public DropTargetMetroHighlightAdorner(UIElement adornedElement)
+ : base(adornedElement)
+ {
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ var visualTargetItem = DropInfo.VisualTargetItem;
+ if (visualTargetItem != null)
+ {
+ var rect = Rect.Empty;
+
+ var tvItem = visualTargetItem as TreeViewItem;
+ if (tvItem != null && VisualTreeHelper.GetChildrenCount(tvItem) > 0)
+ {
+ var descendant = VisualTreeHelper.GetDescendantBounds(tvItem);
+ var translatePoint = tvItem.TranslatePoint(new Point(), AdornedElement);
+ var itemRect = new Rect(translatePoint, tvItem.RenderSize);
+ descendant.Union(itemRect);
+ rect = new Rect(translatePoint, new Size(descendant.Width - translatePoint.X, tvItem.ActualHeight));
+ }
+ if (rect.IsEmpty)
+ {
+ rect = new Rect(visualTargetItem.TranslatePoint(new Point(), AdornedElement),
+ VisualTreeHelper.GetDescendantBounds(visualTargetItem).Size);
+ }
+ var color = (Color) ThemeManager.DetectAppStyle(Application.Current).Item2.Resources["AccentColor"];
+ drawingContext.DrawRoundedRectangle(null, new Pen(new SolidColorBrush(color), 2), rect, 2, 2);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroInsertionAdorner.cs b/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroInsertionAdorner.cs
new file mode 100644
index 000000000..f70c8cac1
--- /dev/null
+++ b/Artemis/Artemis/Styles/DropTargetAdorners/DropTargetMetroInsertionAdorner.cs
@@ -0,0 +1,169 @@
+// Direct copy-paste from GongSolutions.WPF.DragDrop - https://github.com/punker76/gong-wpf-dragdrop
+// All credit goes to their awesome library, I merely copied this in order to change the highlight color.
+
+// BSD 3-Clause License
+//
+// Copyright (c) 2015, Jan Karger (Steven Kirk)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of gong-wpf-dragdrop nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using GongSolutions.Wpf.DragDrop;
+using MahApps.Metro;
+
+namespace Artemis.Styles.DropTargetAdorners
+{
+ public class DropTargetMetroInsertionAdorner : DropTargetAdorner
+ {
+ private static readonly Pen m_Pen;
+ private static readonly PathGeometry m_Triangle;
+
+ static DropTargetMetroInsertionAdorner()
+ {
+ // Create the pen and triangle in a static constructor and freeze them to improve performance.
+ const int triangleSize = 5;
+
+ var color = (Color)ThemeManager.DetectAppStyle(Application.Current).Item2.Resources["AccentColor"];
+ m_Pen = new Pen(new SolidColorBrush(color), 2);
+ m_Pen.Freeze();
+
+ var firstLine = new LineSegment(new Point(0, -triangleSize), false);
+ firstLine.Freeze();
+ var secondLine = new LineSegment(new Point(0, triangleSize), false);
+ secondLine.Freeze();
+
+ var figure = new PathFigure { StartPoint = new Point(triangleSize, 0) };
+ figure.Segments.Add(firstLine);
+ figure.Segments.Add(secondLine);
+ figure.Freeze();
+
+ m_Triangle = new PathGeometry();
+ m_Triangle.Figures.Add(figure);
+ m_Triangle.Freeze();
+ }
+
+ public DropTargetMetroInsertionAdorner(UIElement adornedElement)
+ : base(adornedElement)
+ {
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ var itemsControl = DropInfo.VisualTarget as ItemsControl;
+
+ if (itemsControl != null)
+ {
+ // Get the position of the item at the insertion index. If the insertion point is
+ // to be after the last item, then get the position of the last item and add an
+ // offset later to draw it at the end of the list.
+ ItemsControl itemParent;
+
+ if (DropInfo.VisualTargetItem != null)
+ {
+ itemParent = ItemsControl.ItemsControlFromItemContainer(DropInfo.VisualTargetItem);
+ }
+ else
+ {
+ itemParent = itemsControl;
+ }
+
+ var index = Math.Min(DropInfo.InsertIndex, itemParent.Items.Count - 1);
+
+ var lastItemInGroup = false;
+ var targetGroup = DropInfo.TargetGroup;
+ if (targetGroup != null && targetGroup.IsBottomLevel &&
+ DropInfo.InsertPosition.HasFlag(RelativeInsertPosition.AfterTargetItem))
+ {
+ var indexOf = targetGroup.Items.IndexOf(DropInfo.TargetItem);
+ lastItemInGroup = indexOf == targetGroup.ItemCount - 1;
+ if (lastItemInGroup && DropInfo.InsertIndex != itemParent.Items.Count)
+ {
+ index--;
+ }
+ }
+
+ var itemContainer = (UIElement)itemParent.ItemContainerGenerator.ContainerFromIndex(index);
+
+ if (itemContainer != null)
+ {
+ var itemRect = new Rect(itemContainer.TranslatePoint(new Point(), AdornedElement),
+ itemContainer.RenderSize);
+ Point point1, point2;
+ double rotation = 0;
+
+ if (DropInfo.VisualTargetOrientation == Orientation.Vertical)
+ {
+ if (DropInfo.InsertIndex == itemParent.Items.Count || lastItemInGroup)
+ {
+ itemRect.Y += itemContainer.RenderSize.Height;
+ }
+
+ point1 = new Point(itemRect.X, itemRect.Y);
+ point2 = new Point(itemRect.Right, itemRect.Y);
+ }
+ else
+ {
+ var itemRectX = itemRect.X;
+
+ if (DropInfo.VisualTargetFlowDirection == FlowDirection.LeftToRight &&
+ DropInfo.InsertIndex == itemParent.Items.Count)
+ {
+ itemRectX += itemContainer.RenderSize.Width;
+ }
+ else if (DropInfo.VisualTargetFlowDirection == FlowDirection.RightToLeft &&
+ DropInfo.InsertIndex != itemParent.Items.Count)
+ {
+ itemRectX += itemContainer.RenderSize.Width;
+ }
+
+ point1 = new Point(itemRectX, itemRect.Y);
+ point2 = new Point(itemRectX, itemRect.Bottom);
+ rotation = 90;
+ }
+
+ drawingContext.DrawLine(m_Pen, point1, point2);
+ DrawTriangle(drawingContext, point1, rotation);
+ DrawTriangle(drawingContext, point2, 180 + rotation);
+ }
+ }
+ }
+
+ private void DrawTriangle(DrawingContext drawingContext, Point origin, double rotation)
+ {
+ drawingContext.PushTransform(new TranslateTransform(origin.X, origin.Y));
+ drawingContext.PushTransform(new RotateTransform(rotation));
+
+ drawingContext.DrawGeometry(m_Pen.Brush, null, m_Triangle);
+
+ drawingContext.Pop();
+ drawingContext.Pop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
index 925b224e6..6b4068740 100644
--- a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
+++ b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
@@ -18,16 +18,18 @@ using Artemis.Models;
using Artemis.Models.Profiles;
using Artemis.Models.Profiles.Properties;
using Artemis.Services;
+using Artemis.Styles.DropTargetAdorners;
using Artemis.Utilities;
using Artemis.ViewModels.LayerEditor;
using Caliburn.Micro;
+using GongSolutions.Wpf.DragDrop;
using MahApps.Metro;
using Ninject;
using Timer = System.Timers.Timer;
namespace Artemis.ViewModels
{
- public sealed class ProfileEditorViewModel : Screen, IHandle
+ public sealed class ProfileEditorViewModel : Screen, IHandle, IDropTarget
{
private readonly GameModel _gameModel;
private readonly MainManager _mainManager;
@@ -150,6 +152,66 @@ namespace Artemis.ViewModels
private KeyboardProvider ActiveKeyboard { get; set; }
+ public void DragOver(IDropInfo dropInfo)
+ {
+ var sourceItem = dropInfo.Data as LayerModel;
+ var targetItem = dropInfo.TargetItem as LayerModel;
+ if (sourceItem == null || targetItem == null)
+ return;
+
+ if (dropInfo.InsertPosition == RelativeInsertPosition.TargetItemCenter &&
+ targetItem.LayerType == LayerType.Folder)
+ {
+ dropInfo.DropTargetAdorner = typeof(DropTargetMetroHighlightAdorner);
+ dropInfo.Effects = DragDropEffects.Copy;
+ }
+ else
+ {
+ dropInfo.DropTargetAdorner = typeof(DropTargetMetroInsertionAdorner);
+ dropInfo.Effects = DragDropEffects.Move;
+ }
+ }
+
+ public void Drop(IDropInfo dropInfo)
+ {
+ var sourceItem = dropInfo.Data as LayerModel;
+ var targetItem = dropInfo.TargetItem as LayerModel;
+ if (sourceItem == null || targetItem == null || sourceItem == targetItem)
+ return;
+
+ if (dropInfo.InsertPosition == RelativeInsertPosition.TargetItemCenter &&
+ targetItem.LayerType == LayerType.Folder)
+ {
+ // Insert into folder
+
+ return;
+ }
+
+ // Remove the source from it's old profile/parent
+ if (sourceItem.Parent == null)
+ sourceItem.Profile.Layers.Remove(sourceItem);
+ else
+ sourceItem.Parent.Children.Remove(sourceItem);
+
+ // Insert the source into it's new profile/parent and update the order
+ if (dropInfo.InsertPosition == RelativeInsertPosition.AfterTargetItem)
+ sourceItem.Order = targetItem.Order + 1;
+ else
+ sourceItem.Order = targetItem.Order - 1;
+ if (targetItem.Parent == null)
+ {
+ targetItem.Profile.Layers.Add(sourceItem);
+ targetItem.Profile.FixOrder();
+ }
+ else
+ {
+ targetItem.Parent.Children.Add(sourceItem);
+ targetItem.Parent.FixOrder();
+ }
+
+ UpdateLayerList(sourceItem);
+ }
+
///
/// Handles chaning the active keyboard, updating the preview image and profiles collection
///
@@ -318,41 +380,6 @@ namespace Artemis.ViewModels
SelectedProfile.FixOrder();
}
- ///
- /// Moves the currently selected layer up in the profile's layer tree
- ///
- public void LayerUp()
- {
- MoveLayer(true);
- }
-
- ///
- /// Moves the currently selected layer down in the profile's layer tree
- ///
- public void LayerDown()
- {
- MoveLayer(false);
- }
-
- ///
- /// Moves the currently selected layer up or down in the profile's layer tree
- ///
- ///
- private void MoveLayer(bool moveUp)
- {
- if (SelectedLayer == null)
- return;
-
- var reorderLayer = SelectedLayer;
-
- if (SelectedLayer.Parent != null)
- SelectedLayer.Parent.Reorder(SelectedLayer, moveUp);
- else
- SelectedLayer.Profile.Reorder(SelectedLayer, moveUp);
-
- UpdateLayerList(reorderLayer);
- }
-
private void UpdateLayerList(LayerModel selectModel)
{
// Update the UI
@@ -421,7 +448,7 @@ namespace Artemis.ViewModels
var hoverLayer = SelectedProfile.GetEnabledLayers()
.Where(l => l.MustDraw())
.FirstOrDefault(l => ((KeyboardPropertiesModel) l.Properties)
- .GetRect(1).Contains(x, y));
+ .GetRect(1).Contains(x, y));
HandleDragging(e, x, y, hoverLayer);
diff --git a/Artemis/Artemis/Views/ProfileEditorView.xaml b/Artemis/Artemis/Views/ProfileEditorView.xaml
index 8718fb8e6..9e5a51472 100644
--- a/Artemis/Artemis/Views/ProfileEditorView.xaml
+++ b/Artemis/Artemis/Views/ProfileEditorView.xaml
@@ -7,6 +7,7 @@
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:itemBehaviours="clr-namespace:Artemis.ItemBehaviours"
xmlns:utilities="clr-namespace:Artemis.Utilities"
+ xmlns:dragDrop="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
mc:Ignorable="d"
d:DesignHeight="473" Width="1055">
@@ -16,7 +17,6 @@
-
@@ -27,7 +27,7 @@
+ BorderThickness="3" Width="800" Height="400">
@@ -64,6 +64,9 @@
@@ -113,29 +116,6 @@
-
-
-
+ Margin="10,0,0,0"/>
\ No newline at end of file
diff --git a/Artemis/Artemis/packages.config b/Artemis/Artemis/packages.config
index c355321fe..8b6697134 100644
--- a/Artemis/Artemis/packages.config
+++ b/Artemis/Artemis/packages.config
@@ -6,6 +6,7 @@
+