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

Fixed GIFs and dynamic properties. LayerEditor now animates GIFs and animations

This commit is contained in:
SpoinkyNL 2016-05-18 00:46:31 +02:00
parent c8c4143e88
commit 6e451c06cd
17 changed files with 155 additions and 67 deletions

View File

@ -2,6 +2,7 @@
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using Artemis.Utilities; using Artemis.Utilities;
using NLog;
using WpfExceptionViewer; using WpfExceptionViewer;
namespace Artemis namespace Artemis
@ -40,7 +41,6 @@ namespace Artemis
} }
} }
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
var ex = e.ExceptionObject as Exception; var ex = e.ExceptionObject as Exception;
@ -49,6 +49,8 @@ namespace Artemis
private static ExceptionViewer GetArtemisExceptionViewer(Exception e) private static ExceptionViewer GetArtemisExceptionViewer(Exception e)
{ {
var logger = LogManager.GetCurrentClassLogger();
logger.Fatal(e, "Unhandled exception, showing dialog and shutting down.");
return new ExceptionViewer("An unexpected error occurred in Artemis.", e) return new ExceptionViewer("An unexpected error occurred in Artemis.", e)
{ {
Title = "Artemis - Exception :c", Title = "Artemis - Exception :c",

View File

@ -310,6 +310,7 @@
<Compile Include="Models\Profiles\Properties\DynamicPropertiesModel.cs" /> <Compile Include="Models\Profiles\Properties\DynamicPropertiesModel.cs" />
<Compile Include="Models\Profiles\Properties\KeyboardPropertiesModel.cs" /> <Compile Include="Models\Profiles\Properties\KeyboardPropertiesModel.cs" />
<Compile Include="Models\Profiles\Properties\LayerPropertiesModel.cs" /> <Compile Include="Models\Profiles\Properties\LayerPropertiesModel.cs" />
<Compile Include="Models\Profiles\Properties\GenericPropertiesModel.cs" />
<Compile Include="Models\Profiles\Properties\MousePropertiesModel.cs" /> <Compile Include="Models\Profiles\Properties\MousePropertiesModel.cs" />
<Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectModel.cs" /> <Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectModel.cs" />
<Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectSettings.cs" /> <Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectSettings.cs" />

View File

@ -66,7 +66,7 @@ namespace Artemis.KeyboardProviders.Corsair
try try
{ {
if (CueSDK.ProtocolDetails == null) if (CueSDK.ProtocolDetails == null)
CueSDK.Initialize(); CueSDK.Initialize(true);
} }
catch (WrapperException) catch (WrapperException)
{ {

View File

@ -123,6 +123,12 @@ namespace Artemis.Managers
ActiveEffect = effectModel; ActiveEffect = effectModel;
ActiveEffect.Enable(); ActiveEffect.Enable();
if (!ActiveEffect.Initialized)
{
_logger.Debug("Cancelling effect change, couldn't initialize the effect ({0})", effectModel.Name);
ActiveEffect = null;
return;
}
} }
if (loopManager != null && !loopManager.Running) if (loopManager != null && !loopManager.Running)

View File

@ -25,13 +25,14 @@ namespace Artemis.Models.Profiles
public ChildItemCollection<LayerModel, LayerModel> Children { get; } public ChildItemCollection<LayerModel, LayerModel> Children { get; }
[XmlIgnore] [XmlIgnore]
public ImageSource LayerImage => GetThumbnail(); public ImageSource LayerImage => Drawer.DrawThumbnail(this);
[XmlIgnore] [XmlIgnore]
public LayerModel Parent { get; internal set; } public LayerModel Parent { get; internal set; }
[XmlIgnore] [XmlIgnore]
public ProfileModel Profile { get; internal set; } public ProfileModel Profile { get; internal set; }
[XmlIgnore] [XmlIgnore]
public GifImage GifImage { get; set; } public GifImage GifImage { get; set; }
@ -44,22 +45,26 @@ namespace Artemis.Models.Profiles
{ {
// Don't draw when the layer is disabled // Don't draw when the layer is disabled
if (!Enabled) if (!Enabled)
return; return;
// Preview simply shows the properties as they are. When not previewing they are applied // Preview simply shows the properties as they are. When not previewing they are applied
LayerPropertiesModel properties; LayerPropertiesModel appliedProperties;
if (!preview) if (!preview)
{ {
if (!ConditionsMet<T>(dataModel)) if (!ConditionsMet<T>(dataModel))
return; // Don't draw the layer when not previewing and the conditions arent met return; // Don't draw the layer when not previewing and the conditions arent met
properties = Properties.GetAppliedProperties(dataModel); appliedProperties = Properties.GetAppliedProperties(dataModel);
} }
else else
properties = GeneralHelpers.Clone(Properties); appliedProperties = GeneralHelpers.Clone(Properties);
// Update animations on layer types that support them // Update animations on layer types that support them
if (LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) if (LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif)
AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) properties); {
AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties);
((KeyboardPropertiesModel) appliedProperties).AnimationProgress =
((KeyboardPropertiesModel) Properties).AnimationProgress;
}
// Folders are drawn recursively // Folders are drawn recursively
if (LayerType == LayerType.Folder) if (LayerType == LayerType.Folder)
@ -69,19 +74,35 @@ namespace Artemis.Models.Profiles
} }
// All other types are handles by the Drawer helper // All other types are handles by the Drawer helper
else if (LayerType == LayerType.Keyboard) else if (LayerType == LayerType.Keyboard)
Drawer.Draw(c, (KeyboardPropertiesModel) properties); Drawer.Draw(c, (KeyboardPropertiesModel) appliedProperties);
else if (LayerType == LayerType.KeyboardGif) else if (LayerType == LayerType.KeyboardGif)
Drawer.DrawGif(c, (KeyboardPropertiesModel) properties, GifImage); GifImage = Drawer.DrawGif(c, (KeyboardPropertiesModel) appliedProperties, GifImage);
else if (LayerType == LayerType.Mouse) else if (LayerType == LayerType.Mouse)
Drawer.UpdateMouse(properties); Drawer.UpdateMouse(appliedProperties);
else if (LayerType == LayerType.Headset) else if (LayerType == LayerType.Headset)
Drawer.UpdateHeadset(properties); Drawer.UpdateHeadset(appliedProperties);
} }
private ImageSource GetThumbnail() public void SetupProperties()
{ {
// TODO if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) &&
return null; !(Properties is KeyboardPropertiesModel))
{
Properties = new KeyboardPropertiesModel
{
Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor()),
Animation = LayerAnimation.None,
Height = 1,
Width = 1,
X = 0,
Y = 0,
Opacity = 1
};
}
else if (LayerType == LayerType.Mouse && !(Properties is MousePropertiesModel))
Properties = new MousePropertiesModel();
else if (!(Properties is GenericPropertiesModel))
Properties = new GenericPropertiesModel();
} }
public void Reorder(LayerModel selectedLayer, bool moveUp) public void Reorder(LayerModel selectedLayer, bool moveUp)

View File

@ -0,0 +1,13 @@
using Artemis.Models.Interfaces;
using Artemis.Utilities;
namespace Artemis.Models.Profiles.Properties
{
public class GenericPropertiesModel : LayerPropertiesModel
{
public override LayerPropertiesModel GetAppliedProperties(IGameDataModel dataModel)
{
return GeneralHelpers.Clone(this);
}
}
}

View File

@ -26,7 +26,7 @@ namespace Artemis.Models.Profiles.Properties
public List<DynamicPropertiesModel> DynamicProperties { get; set; } public List<DynamicPropertiesModel> DynamicProperties { get; set; }
[XmlIgnore] [XmlIgnore]
public int AnimationProgress { get; set; } public double AnimationProgress { get; set; }
public Rect GetRect(int scale = 4) public Rect GetRect(int scale = 4)
{ {

View File

@ -41,6 +41,9 @@ namespace Artemis.Modules.Games.RocketLeague
_pointer = JsonConvert.DeserializeObject<GamePointersCollection>(Offsets.Default.RocketLeague); _pointer = JsonConvert.DeserializeObject<GamePointersCollection>(Offsets.Default.RocketLeague);
var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessName); var tempProcess = MemoryHelpers.GetProcessIfRunning(ProcessName);
if (tempProcess == null)
return;
_memory = new Memory(tempProcess); _memory = new Memory(tempProcess);
Initialized = true; Initialized = true;

View File

@ -6,8 +6,15 @@
throwExceptions="false" throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log" > internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log" >
<targets> <targets>
<target name="file" xsi:type="File" maxArchiveFiles="7" fileName="${specialfolder:folder=MyDocuments}/Artemis/logs/${shortdate}.txt"/> <target name="file"
<target name="debugger" xsi:type="Debugger" layout="${logger:shortName=True} - ${uppercase:${level}}: ${message}"/> xsi:type="File"
maxArchiveFiles="7"
fileName="${specialfolder:folder=MyDocuments}/Artemis/logs/${shortdate}.txt"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"/>
<target name="debugger"
xsi:type="Debugger"
layout="${logger:shortName=True} - ${uppercase:${level}}: ${message}"/>
</targets> </targets>
<rules> <rules>

View File

@ -446,6 +446,11 @@
<nc:GradientStopAdder Height="20" Focusable="False" <nc:GradientStopAdder Height="20" Focusable="False"
IsTabStop="False" FocusVisualStyle="{x:Null}" IsTabStop="False" FocusVisualStyle="{x:Null}"
ColorBox="{Binding RelativeSource={RelativeSource TemplatedParent}}"> ColorBox="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<nc:GradientStopAdder.Background>
<LinearGradientBrush
GradientStops="{Binding Brush.GradientStops, RelativeSource={RelativeSource TemplatedParent}}"
StartPoint="0, .5" EndPoint="1, .5" />
</nc:GradientStopAdder.Background>
<Button.Style> <Button.Style>
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="Template"> <Setter Property="Template">

View File

@ -47,8 +47,8 @@ namespace Artemis.Utilities.GameState
} }
catch (HttpListenerException) catch (HttpListenerException)
{ {
MessageBox.Show( MessageBox.Show("Couldn't start the webserver. CS:GO/Dota2 effects won't work :c \n\n" +
"Couldn't start the webserver. CS:GO/Dota2 effects won't work :c \n\nTry changing the port in Settings and restart Artemis."); "Try changing the port in Settings and restart Artemis.");
} }
Running = true; Running = true;

View File

@ -8,36 +8,36 @@ namespace Artemis.Utilities.Layers
public static void UpdateAnimation(KeyboardPropertiesModel properties) public static void UpdateAnimation(KeyboardPropertiesModel properties)
{ {
const int scale = 4; const int scale = 4;
var progress = properties.AnimationProgress;
// Horizontal sliding // Horizontal sliding
if (properties.Animation == LayerAnimation.SlideRight || properties.Animation == LayerAnimation.SlideLeft) if (properties.Animation == LayerAnimation.SlideRight || properties.Animation == LayerAnimation.SlideLeft)
{ {
if (properties.AnimationProgress > properties.Width*scale) if (progress > properties.Width*scale)
properties.AnimationProgress = 0; progress = 0;
} }
// Vertical sliding // Vertical sliding
if (properties.Animation == LayerAnimation.SlideDown || properties.Animation == LayerAnimation.SlideUp) if (properties.Animation == LayerAnimation.SlideDown || properties.Animation == LayerAnimation.SlideUp)
{ {
if (properties.AnimationProgress > properties.Height*scale) if (progress > properties.Height*scale)
properties.AnimationProgress = 0; progress = 0;
} }
// Pulse animation // Pulse animation
if (properties.Animation == LayerAnimation.Pulse) if (properties.Animation == LayerAnimation.Pulse)
{ {
var opac = (Math.Sin(properties.AnimationProgress*Math.PI) + 1)*(properties.Opacity/2); if (progress > 2)
properties.Opacity = opac; progress = 0;
if (properties.AnimationProgress > 2)
properties.AnimationProgress = 0;
properties.AnimationProgress = (int) (properties.AnimationProgress + properties.AnimationSpeed/2); progress = progress + properties.AnimationSpeed/2;
} }
else else
{ {
// ApplyProperties the animation progress progress = progress + properties.AnimationSpeed * 2;
properties.AnimationProgress = (int) (properties.AnimationProgress + properties.AnimationSpeed);
} }
properties.AnimationProgress = progress;
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System.Drawing; using System;
using System.Drawing;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
@ -52,8 +53,13 @@ namespace Artemis.Utilities.Layers
private static void DrawRectangle(DrawingContext c, KeyboardPropertiesModel properties, Rect rectangle, private static void DrawRectangle(DrawingContext c, KeyboardPropertiesModel properties, Rect rectangle,
Rect slide1, Rect slide2) Rect slide1, Rect slide2)
{ {
// Apply the pulse animation
var brush = properties.Brush.CloneCurrentValue(); var brush = properties.Brush.CloneCurrentValue();
brush.Opacity = properties.Opacity; brush.Opacity = properties.Opacity;
if (properties.Animation == LayerAnimation.Pulse)
brush.Opacity = (Math.Sin(properties.AnimationProgress * Math.PI) + 1) * (properties.Opacity / 2);
else
brush.Opacity = properties.Opacity;
// TODO: Implement clipping modes // TODO: Implement clipping modes
// Most animation types can be drawn regularly // Most animation types can be drawn regularly
@ -73,13 +79,13 @@ namespace Artemis.Utilities.Layers
} }
} }
public static void DrawGif(DrawingContext c, KeyboardPropertiesModel properties, GifImage gifImage, public static GifImage DrawGif(DrawingContext c, KeyboardPropertiesModel properties, GifImage gifImage,
bool update = true) bool update = true)
{ {
if (string.IsNullOrEmpty(properties.GifFile)) if (string.IsNullOrEmpty(properties.GifFile))
return; return null;
if (!File.Exists(properties.GifFile)) if (!File.Exists(properties.GifFile))
return; return null;
const int scale = 4; const int scale = 4;
@ -94,6 +100,7 @@ namespace Artemis.Utilities.Layers
var draw = update ? gifImage.GetNextFrame() : gifImage.GetFrame(0); var draw = update ? gifImage.GetNextFrame() : gifImage.GetFrame(0);
c.DrawImage(ImageUtilities.BitmapToBitmapImage(new Bitmap(draw)), gifRect); c.DrawImage(ImageUtilities.BitmapToBitmapImage(new Bitmap(draw)), gifRect);
return gifImage;
} }
public static void UpdateMouse(LayerPropertiesModel properties) public static void UpdateMouse(LayerPropertiesModel properties)
@ -104,7 +111,7 @@ namespace Artemis.Utilities.Layers
{ {
} }
public static DrawingImage GetThumbnail(LayerModel layerModel) public static ImageSource DrawThumbnail(LayerModel layerModel)
{ {
var thumbnailRect = new Rect(0, 0, 18, 18); var thumbnailRect = new Rect(0, 0, 18, 18);
var visual = new DrawingVisual(); var visual = new DrawingVisual();

View File

@ -99,6 +99,18 @@ namespace Artemis.ViewModels.Abstract
GameModel.Profile = ProfileEditor.SelectedProfile; GameModel.Profile = ProfileEditor.SelectedProfile;
ProfilePreviewModel.SelectedProfile = ProfileEditor.SelectedProfile; ProfilePreviewModel.SelectedProfile = ProfileEditor.SelectedProfile;
} }
protected override void OnActivate()
{
base.OnActivate();
ProfileEditor.PreviewTimer.Start();
}
protected override void OnDeactivate(bool close)
{
base.OnDeactivate(close);
ProfileEditor.PreviewTimer.Stop();
}
} }
public delegate void OnLayersUpdatedCallback(object sender); public delegate void OnLayersUpdatedCallback(object sender);

View File

@ -1,4 +1,6 @@
using Artemis.Models.Interfaces; using System.Windows.Forms;
using Artemis.Models.Interfaces;
using Artemis.Models.Profiles;
using Artemis.Models.Profiles.Properties; using Artemis.Models.Profiles.Properties;
using Artemis.Utilities; using Artemis.Utilities;
using Caliburn.Micro; using Caliburn.Micro;
@ -24,6 +26,17 @@ namespace Artemis.ViewModels.LayerEditor
OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", DataModelProps, keyboardProperties); OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", DataModelProps, keyboardProperties);
} }
public KeyboardPropertiesModel ProposedProperties
{
get { return _proposedProperties; }
set
{
if (Equals(value, _proposedProperties)) return;
_proposedProperties = value;
NotifyOfPropertyChange(() => ProposedProperties);
}
}
public bool IsGif public bool IsGif
{ {
get { return _isGif; } get { return _isGif; }
@ -36,23 +49,26 @@ namespace Artemis.ViewModels.LayerEditor
} }
public bool ShowGif => IsGif; public bool ShowGif => IsGif;
public bool ShowBrush => !IsGif; public bool ShowBrush => !IsGif;
public BindableCollection<GeneralHelpers.PropertyCollection> DataModelProps { get; set; } public BindableCollection<GeneralHelpers.PropertyCollection> DataModelProps { get; set; }
public LayerDynamicPropertiesViewModel HeightProperties { get; set; } public LayerDynamicPropertiesViewModel HeightProperties { get; set; }
public LayerDynamicPropertiesViewModel WidthProperties { get; set; } public LayerDynamicPropertiesViewModel WidthProperties { get; set; }
public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } public LayerDynamicPropertiesViewModel OpacityProperties { get; set; }
public KeyboardPropertiesModel ProposedProperties public void BrowseGif()
{ {
get { return _proposedProperties; } var dialog = new OpenFileDialog { Filter = "Animated image file (*.gif)|*.gif" };
set var result = dialog.ShowDialog();
{ if (result != DialogResult.OK)
if (Equals(value, _proposedProperties)) return; return;
_proposedProperties = value;
NotifyOfPropertyChange(() => ProposedProperties); ProposedProperties.GifFile = dialog.FileName;
} NotifyOfPropertyChange(() => ProposedProperties);
} }
public override LayerPropertiesModel GetAppliedProperties() public override LayerPropertiesModel GetAppliedProperties()

View File

@ -32,12 +32,14 @@ namespace Artemis.ViewModels.LayerEditor
Layer = layer; Layer = layer;
Layer.Enabled = false; Layer.Enabled = false;
if (Layer.Properties == null)
Layer.SetupProperties();
DataModelProps = new BindableCollection<GeneralHelpers.PropertyCollection>(); DataModelProps = new BindableCollection<GeneralHelpers.PropertyCollection>();
DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap(gameDataModel)); DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap(gameDataModel));
LayerConditionVms = new BindableCollection<LayerConditionViewModel>(layer.Properties.Conditions LayerConditionVms = new BindableCollection<LayerConditionViewModel>(layer.Properties.Conditions
.Select(c => new LayerConditionViewModel(this, c, DataModelProps))); .Select(c => new LayerConditionViewModel(this, c, DataModelProps)));
PropertyChanged += PropertiesViewModelHandler; PropertyChanged += PropertiesViewModelHandler;
PreSelect(); PreSelect();
} }
@ -120,7 +122,12 @@ namespace Artemis.ViewModels.LayerEditor
if (e.PropertyName != "LayerType") if (e.PropertyName != "LayerType")
return; return;
Layer.LayerType = LayerType; // Update the model
if (Layer.LayerType != LayerType)
{
Layer.LayerType = LayerType;
Layer.SetupProperties();
}
// Update the KeyboardPropertiesViewModel if it's being used // Update the KeyboardPropertiesViewModel if it's being used
var model = LayerPropertiesViewModel as KeyboardPropertiesViewModel; var model = LayerPropertiesViewModel as KeyboardPropertiesViewModel;
@ -165,17 +172,7 @@ namespace Artemis.ViewModels.LayerEditor
LayerConditionVms.Remove(layerConditionViewModel); LayerConditionVms.Remove(layerConditionViewModel);
Layer.Properties.Conditions.Remove(layerConditionModel); Layer.Properties.Conditions.Remove(layerConditionModel);
} }
public void BrowseGif()
{
if (Layer.LayerType != LayerType.KeyboardGif)
return;
var dialog = new OpenFileDialog {Filter = "Animated image file (*.gif)|*.gif"};
var result = dialog.ShowDialog();
if (result == DialogResult.OK)
((KeyboardPropertiesModel) Layer.Properties).GifFile = dialog.FileName;
}
public override void CanClose(Action<bool> callback) public override void CanClose(Action<bool> callback)
{ {
_layer.Enabled = _wasEnabled; _layer.Enabled = _wasEnabled;

View File

@ -2,6 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Dynamic; using System.Dynamic;
using System.Linq; using System.Linq;
using System.Timers;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@ -46,6 +47,9 @@ namespace Artemis.ViewModels
Layers = new BindableCollection<LayerModel>(); Layers = new BindableCollection<LayerModel>();
events.Subscribe(this); events.Subscribe(this);
PreviewTimer = new Timer(40);
PreviewTimer.Elapsed += (sender, args) => NotifyOfPropertyChange(() => KeyboardPreview);
PropertyChanged += PropertyChangeHandler; PropertyChanged += PropertyChangeHandler;
LoadProfiles(); LoadProfiles();
} }
@ -53,6 +57,8 @@ namespace Artemis.ViewModels
[Inject] [Inject]
public MetroDialogService DialogService { get; set; } public MetroDialogService DialogService { get; set; }
public Timer PreviewTimer { get; set; }
public BindableCollection<ProfileModel> Profiles public BindableCollection<ProfileModel> Profiles
{ {
get { return _profiles; } get { return _profiles; }
@ -204,8 +210,6 @@ namespace Artemis.ViewModels
if (e.PropertyName == "KeyboardPreview") if (e.PropertyName == "KeyboardPreview")
return; return;
NotifyOfPropertyChange(() => KeyboardPreview);
if (SelectedProfile != null) if (SelectedProfile != null)
ProfileProvider.AddOrUpdate(SelectedProfile); ProfileProvider.AddOrUpdate(SelectedProfile);
} }
@ -262,11 +266,6 @@ namespace Artemis.ViewModels
SelectedProfile = profile; SelectedProfile = profile;
} }
public void ToggleEnabled(LayerModel layer)
{
NotifyOfPropertyChange(() => KeyboardPreview);
}
/// <summary> /// <summary>
/// Opens a new LayerEditorView for the given layer /// Opens a new LayerEditorView for the given layer
/// </summary> /// </summary>
@ -496,7 +495,6 @@ namespace Artemis.ViewModels
draggingProps.X = (int) Math.Round(x - _draggingLayerOffset.Value.X); draggingProps.X = (int) Math.Round(x - _draggingLayerOffset.Value.X);
draggingProps.Y = (int) Math.Round(y - _draggingLayerOffset.Value.Y); draggingProps.Y = (int) Math.Round(y - _draggingLayerOffset.Value.Y);
} }
NotifyOfPropertyChange(() => KeyboardPreview);
} }
private bool ShouldDrawLayer(LayerModel layer) private bool ShouldDrawLayer(LayerModel layer)