From 4667ea21f923759d5755f6301f25a4edad405683 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Wed, 18 May 2016 18:33:01 +0200 Subject: [PATCH] Implemented clipping modes, hopefully fixed UI freezes on tab switch --- Artemis/Artemis/Managers/LoopManager.cs | 86 +++++------ Artemis/Artemis/Models/Profiles/LayerModel.cs | 22 ++- .../Artemis/Models/Profiles/ProfileModel.cs | 4 +- .../Properties/KeyboardPropertiesModel.cs | 2 - .../ProfilePreview/ProfilePreviewModel.cs | 2 +- .../Games/CounterStrike/CounterStrikeModel.cs | 2 +- .../Artemis/Modules/Games/Dota2/Dota2Model.cs | 2 +- .../Games/RocketLeague/RocketLeagueModel.cs | 2 +- .../Modules/Games/Witcher3/Witcher3Model.cs | 2 +- .../Utilities/Layers/AnimationUpdater.cs | 26 ++-- Artemis/Artemis/Utilities/Layers/Drawer.cs | 42 +++--- .../LayerEditor/LayerEditorViewModel.cs | 16 +- .../ViewModels/ProfileEditorViewModel.cs | 139 ++++++++++-------- 13 files changed, 185 insertions(+), 162 deletions(-) diff --git a/Artemis/Artemis/Managers/LoopManager.cs b/Artemis/Artemis/Managers/LoopManager.cs index f0e892a64..b3c3e78c3 100644 --- a/Artemis/Artemis/Managers/LoopManager.cs +++ b/Artemis/Artemis/Managers/LoopManager.cs @@ -1,9 +1,11 @@ using System; using System.Drawing; +using System.Threading; using System.Timers; using Artemis.Events; using Caliburn.Micro; using Ninject.Extensions.Logging; +using Timer = System.Timers.Timer; namespace Artemis.Managers { @@ -108,49 +110,51 @@ namespace Artemis.Managers return; } - lock (_keyboardManager.ActiveKeyboard) + if (!Monitor.TryEnter(_keyboardManager.ActiveKeyboard)) + return; + + // Skip frame if effect is still initializing + if (renderEffect.Initialized == false) + return; + + // ApplyProperties the current effect + if (renderEffect.Initialized) + renderEffect.Update(); + + // Get ActiveEffect's bitmap + var bitmap = renderEffect.Initialized + ? renderEffect.GenerateBitmap() + : null; + + // Draw enabled overlays on top + foreach (var overlayModel in _effectManager.EnabledOverlays) { - // Skip frame if effect is still initializing - if (renderEffect.Initialized == false) - return; - - // ApplyProperties the current effect - if (renderEffect.Initialized) - renderEffect.Update(); - - // Get ActiveEffect's bitmap - var bitmap = renderEffect.Initialized - ? renderEffect.GenerateBitmap() - : null; - - // Draw enabled overlays on top - foreach (var overlayModel in _effectManager.EnabledOverlays) - { - overlayModel.Update(); - bitmap = bitmap != null - ? overlayModel.GenerateBitmap(bitmap) - : overlayModel.GenerateBitmap(); - } - - if (bitmap == null) - return; - - // Fill the bitmap's background with black to avoid trailing colors on some keyboards - var fixedBmp = new Bitmap(bitmap.Width, bitmap.Height); - using (var g = Graphics.FromImage(fixedBmp)) - { - g.Clear(Color.Black); - g.DrawImage(bitmap, 0, 0); - } - - bitmap = fixedBmp; - - // If it exists, send bitmap to the device - _keyboardManager.ActiveKeyboard?.DrawBitmap(bitmap); - - // debugging TODO: Disable when window isn't shown (in Debug VM, or get rid of it, w/e) - _events.PublishOnUIThread(new ChangeBitmap(bitmap)); + overlayModel.Update(); + bitmap = bitmap != null + ? overlayModel.GenerateBitmap(bitmap) + : overlayModel.GenerateBitmap(); } + + if (bitmap == null) + return; + + // Fill the bitmap's background with black to avoid trailing colors on some keyboards + var fixedBmp = new Bitmap(bitmap.Width, bitmap.Height); + using (var g = Graphics.FromImage(fixedBmp)) + { + g.Clear(Color.Black); + g.DrawImage(bitmap, 0, 0); + } + + bitmap = fixedBmp; + + // If it exists, send bitmap to the device + _keyboardManager.ActiveKeyboard?.DrawBitmap(bitmap); + + // debugging TODO: Disable when window isn't shown (in Debug VM, or get rid of it, w/e) + _events.PublishOnUIThread(new ChangeBitmap(bitmap)); + + Monitor.Exit(_keyboardManager.ActiveKeyboard); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/LayerModel.cs b/Artemis/Artemis/Models/Profiles/LayerModel.cs index 350560b0b..61aaf2143 100644 --- a/Artemis/Artemis/Models/Profiles/LayerModel.cs +++ b/Artemis/Artemis/Models/Profiles/LayerModel.cs @@ -41,7 +41,7 @@ namespace Artemis.Models.Profiles return Enabled && Properties.Conditions.All(cm => cm.ConditionMet(dataModel)); } - public void Draw(IGameDataModel dataModel, DrawingContext c, bool preview = false) + public void Draw(IGameDataModel dataModel, DrawingContext c, bool preview, bool updateAnimations) { // Don't draw when the layer is disabled if (!Enabled) @@ -59,22 +59,21 @@ namespace Artemis.Models.Profiles appliedProperties = GeneralHelpers.Clone(Properties); // Update animations on layer types that support them - if (LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) + if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) && updateAnimations) { - AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties); - ((KeyboardPropertiesModel) appliedProperties).AnimationProgress = - ((KeyboardPropertiesModel) Properties).AnimationProgress; + AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties, + (KeyboardPropertiesModel) appliedProperties); } // Folders are drawn recursively if (LayerType == LayerType.Folder) { foreach (var layerModel in Children.OrderByDescending(l => l.Order)) - layerModel.Draw(dataModel, c); + layerModel.Draw(dataModel, c, preview, updateAnimations); } // All other types are handles by the Drawer helper else if (LayerType == LayerType.Keyboard) - Drawer.Draw(c, (KeyboardPropertiesModel) appliedProperties); + Drawer.Draw(c, (KeyboardPropertiesModel) Properties, (KeyboardPropertiesModel) appliedProperties); else if (LayerType == LayerType.KeyboardGif) GifImage = Drawer.DrawGif(c, (KeyboardPropertiesModel) appliedProperties, GifImage); else if (LayerType == LayerType.Mouse) @@ -131,6 +130,15 @@ namespace Artemis.Models.Profiles Children[i].Order = i; } + /// + /// Returns whether the layer meets the requirements to be drawn + /// + /// + public bool MustDraw() + { + return Enabled && (LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif); + } + #region IChildItem Members LayerModel IChildItem.Parent diff --git a/Artemis/Artemis/Models/Profiles/ProfileModel.cs b/Artemis/Artemis/Models/Profiles/ProfileModel.cs index 03cb2bea2..8a6b88fc1 100644 --- a/Artemis/Artemis/Models/Profiles/ProfileModel.cs +++ b/Artemis/Artemis/Models/Profiles/ProfileModel.cs @@ -110,7 +110,7 @@ namespace Artemis.Models.Profiles Layers[i].Order = i; } - public Bitmap GenerateBitmap(Rect keyboardRect, IGameDataModel gameDataModel, bool preview = false) + public Bitmap GenerateBitmap(Rect keyboardRect, IGameDataModel gameDataModel, bool preview, bool updateAnimations) { Bitmap bitmap = null; DrawingVisual.Dispatcher.Invoke(delegate @@ -124,7 +124,7 @@ namespace Artemis.Models.Profiles // Draw the layers foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) - layerModel.Draw(gameDataModel, c, preview); + layerModel.Draw(gameDataModel, c, preview, updateAnimations); // Remove the clip c.Pop(); diff --git a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs index 3f02b5950..50398a3ad 100644 --- a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs +++ b/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs @@ -24,8 +24,6 @@ namespace Artemis.Models.Profiles.Properties public double AnimationSpeed { get; set; } public string GifFile { get; set; } public List DynamicProperties { get; set; } - - [XmlIgnore] public double AnimationProgress { get; set; } public Rect GetRect(int scale = 4) diff --git a/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs b/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs index 06a35dabe..a4b29ea0d 100644 --- a/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs +++ b/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs @@ -41,7 +41,7 @@ namespace Artemis.Modules.Effects.ProfilePreview return bitmap; var keyboardRect = MainManager.KeyboardManager.ActiveKeyboard.KeyboardRectangle(4); - var image = SelectedProfile.GenerateBitmap(keyboardRect, _previewDataModel, true); + var image = SelectedProfile.GenerateBitmap(keyboardRect, _previewDataModel, true, true); // Draw on top of everything else using (var g = Graphics.FromImage(bitmap)) diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs index 5fe39ca59..bb3daccf9 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs @@ -48,7 +48,7 @@ namespace Artemis.Modules.Games.CounterStrike return null; var keyboardRect = MainManager.KeyboardManager.ActiveKeyboard.KeyboardRectangle(Scale); - return Profile.GenerateBitmap(keyboardRect, GameDataModel); + return Profile.GenerateBitmap(keyboardRect, GameDataModel, false, true); } public void HandleGameData(object sender, GameDataReceivedEventArgs e) diff --git a/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs b/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs index 127aeb731..0b1ed01d9 100644 --- a/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs +++ b/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs @@ -62,7 +62,7 @@ namespace Artemis.Modules.Games.Dota2 return null; var keyboardRect = MainManager.KeyboardManager.ActiveKeyboard.KeyboardRectangle(Scale); - return Profile.GenerateBitmap(keyboardRect, GameDataModel); + return Profile.GenerateBitmap(keyboardRect, GameDataModel, false, true); } diff --git a/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs b/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs index 32e93b9f1..9c7c88704 100644 --- a/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs +++ b/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs @@ -73,7 +73,7 @@ namespace Artemis.Modules.Games.RocketLeague return null; var keyboardRect = MainManager.KeyboardManager.ActiveKeyboard.KeyboardRectangle(Scale); - return Profile.GenerateBitmap(keyboardRect, GameDataModel); + return Profile.GenerateBitmap(keyboardRect, GameDataModel, false, true); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs b/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs index ab96dc427..f37836a28 100644 --- a/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs +++ b/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs @@ -110,7 +110,7 @@ namespace Artemis.Modules.Games.Witcher3 return null; var keyboardRect = MainManager.KeyboardManager.ActiveKeyboard.KeyboardRectangle(Scale); - return Profile.GenerateBitmap(keyboardRect, GameDataModel); + return Profile.GenerateBitmap(keyboardRect, GameDataModel, false, true); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs b/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs index a29b30a26..0f745ca77 100644 --- a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs +++ b/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs @@ -1,42 +1,46 @@ -using System; -using Artemis.Models.Profiles.Properties; +using Artemis.Models.Profiles.Properties; namespace Artemis.Utilities.Layers { public static class AnimationUpdater { - public static void UpdateAnimation(KeyboardPropertiesModel properties) + public static void UpdateAnimation(KeyboardPropertiesModel properties, KeyboardPropertiesModel appliedProperties) { const int scale = 4; - var progress = properties.AnimationProgress; + var animateProperties = properties.Contain ? appliedProperties : properties; + var progress = appliedProperties.AnimationProgress; // Horizontal sliding - if (properties.Animation == LayerAnimation.SlideRight || properties.Animation == LayerAnimation.SlideLeft) + if (animateProperties.Animation == LayerAnimation.SlideRight || + animateProperties.Animation == LayerAnimation.SlideLeft) { - if (progress > properties.Width*scale) + if (progress > animateProperties.Width*scale) progress = 0; } // Vertical sliding - if (properties.Animation == LayerAnimation.SlideDown || properties.Animation == LayerAnimation.SlideUp) + if (animateProperties.Animation == LayerAnimation.SlideDown || + animateProperties.Animation == LayerAnimation.SlideUp) { - if (progress > properties.Height*scale) + if (progress > animateProperties.Height*scale) progress = 0; } // Pulse animation - if (properties.Animation == LayerAnimation.Pulse) + if (animateProperties.Animation == LayerAnimation.Pulse) { if (progress > 2) progress = 0; - progress = progress + properties.AnimationSpeed/2; + progress = progress + animateProperties.AnimationSpeed/2; } else { - progress = progress + properties.AnimationSpeed * 2; + progress = progress + animateProperties.AnimationSpeed*2; } + appliedProperties.AnimationProgress = progress; + // Store the animation progress in the actual model for the next frame properties.AnimationProgress = progress; } } diff --git a/Artemis/Artemis/Utilities/Layers/Drawer.cs b/Artemis/Artemis/Utilities/Layers/Drawer.cs index b088bf8ce..9438ba821 100644 --- a/Artemis/Artemis/Utilities/Layers/Drawer.cs +++ b/Artemis/Artemis/Utilities/Layers/Drawer.cs @@ -14,54 +14,60 @@ namespace Artemis.Utilities.Layers { public static class Drawer { - public static void Draw(DrawingContext c, KeyboardPropertiesModel props) + public static void Draw(DrawingContext c, KeyboardPropertiesModel props, KeyboardPropertiesModel applied) { - if (props.Brush == null) + if (applied.Brush == null) return; const int scale = 4; - // Set up variables for this frame - var rect = new Rect(props.X*scale, props.Y*scale, props.Width*scale, props.Height*scale); + var rect = props.Contain + ? new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale) + : new Rect(props.X*scale, props.Y*scale, props.Width*scale, props.Height*scale); + var s1 = new Rect(); var s2 = new Rect(); - if (props.Animation == LayerAnimation.SlideRight) + if (applied.Animation == LayerAnimation.SlideRight) { - s1 = new Rect(new Point(rect.X + props.AnimationProgress, rect.Y), new Size(rect.Width, rect.Height)); + s1 = new Rect(new Point(rect.X + applied.AnimationProgress, rect.Y), new Size(rect.Width, rect.Height)); s2 = new Rect(new Point(s1.X - rect.Width, rect.Y), new Size(rect.Width + 1, rect.Height)); } - if (props.Animation == LayerAnimation.SlideLeft) + if (applied.Animation == LayerAnimation.SlideLeft) { - s1 = new Rect(new Point(rect.X - props.AnimationProgress, rect.Y), new Size(rect.Width + 1, rect.Height)); + s1 = new Rect(new Point(rect.X - applied.AnimationProgress, rect.Y), + new Size(rect.Width + 1, rect.Height)); s2 = new Rect(new Point(s1.X + rect.Width, rect.Y), new Size(rect.Width, rect.Height)); } - if (props.Animation == LayerAnimation.SlideDown) + if (applied.Animation == LayerAnimation.SlideDown) { - s1 = new Rect(new Point(rect.X, rect.Y + props.AnimationProgress), new Size(rect.Width, rect.Height)); + s1 = new Rect(new Point(rect.X, rect.Y + applied.AnimationProgress), new Size(rect.Width, rect.Height)); s2 = new Rect(new Point(s1.X, s1.Y - rect.Height), new Size(rect.Width, rect.Height)); } - if (props.Animation == LayerAnimation.SlideUp) + if (applied.Animation == LayerAnimation.SlideUp) { - s1 = new Rect(new Point(rect.X, rect.Y - props.AnimationProgress), new Size(rect.Width, rect.Height)); + s1 = new Rect(new Point(rect.X, rect.Y - applied.AnimationProgress), new Size(rect.Width, rect.Height)); s2 = new Rect(new Point(s1.X, s1.Y + rect.Height), new Size(rect.Width, rect.Height)); } - props.Brush.Dispatcher.Invoke(() => DrawRectangle(c, props, rect, s1, s2)); + var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + applied.Brush.Dispatcher.Invoke(() => DrawRectangle(c, applied, clip, rect, s1, s2)); } - private static void DrawRectangle(DrawingContext c, KeyboardPropertiesModel properties, Rect rectangle, + private static void DrawRectangle(DrawingContext c, KeyboardPropertiesModel properties, Rect clip, + Rect rectangle, Rect slide1, Rect slide2) { // Apply the pulse animation var brush = properties.Brush.CloneCurrentValue(); brush.Opacity = properties.Opacity; if (properties.Animation == LayerAnimation.Pulse) - brush.Opacity = (Math.Sin(properties.AnimationProgress * Math.PI) + 1) * (properties.Opacity / 2); + brush.Opacity = (Math.Sin(properties.AnimationProgress*Math.PI) + 1)*(properties.Opacity/2); else brush.Opacity = properties.Opacity; - // TODO: Implement clipping modes + c.PushClip(new RectangleGeometry(clip)); // Most animation types can be drawn regularly if (properties.Animation == LayerAnimation.None || properties.Animation == LayerAnimation.Grow || @@ -72,11 +78,11 @@ namespace Artemis.Utilities.Layers // Sliding animations however, require offsetting two rects else { - c.PushClip(new RectangleGeometry(rectangle)); + c.PushClip(new RectangleGeometry(clip)); c.DrawRectangle(brush, null, slide1); c.DrawRectangle(brush, null, slide2); - c.Pop(); } + c.Pop(); } public static GifImage DrawGif(DrawingContext c, KeyboardPropertiesModel properties, GifImage gifImage, diff --git a/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs b/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs index 7d960f1cc..28a455bf1 100644 --- a/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs @@ -1,8 +1,6 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.IO; using System.Linq; -using System.Windows.Forms; using Artemis.Models.Interfaces; using Artemis.Models.Profiles; using Artemis.Models.Profiles.Properties; @@ -10,14 +8,12 @@ using Artemis.Services; using Artemis.Utilities; using Caliburn.Micro; using Ninject; -using Screen = Caliburn.Micro.Screen; namespace Artemis.ViewModels.LayerEditor { public sealed class LayerEditorViewModel : Screen { private readonly IGameDataModel _gameDataModel; - private readonly bool _wasEnabled; private LayerModel _layer; private LayerPropertiesViewModel _layerPropertiesViewModel; private LayerType _layerType; @@ -27,10 +23,8 @@ namespace Artemis.ViewModels.LayerEditor public LayerEditorViewModel(IGameDataModel gameDataModel, LayerModel layer) { _gameDataModel = gameDataModel; - _wasEnabled = layer.Enabled; Layer = layer; - Layer.Enabled = false; if (Layer.Properties == null) Layer.SetupProperties(); @@ -39,7 +33,7 @@ namespace Artemis.ViewModels.LayerEditor DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap(gameDataModel)); LayerConditionVms = new BindableCollection(layer.Properties.Conditions .Select(c => new LayerConditionViewModel(this, c, DataModelProps))); - + PropertyChanged += PropertiesViewModelHandler; PreSelect(); } @@ -172,11 +166,5 @@ namespace Artemis.ViewModels.LayerEditor LayerConditionVms.Remove(layerConditionViewModel); Layer.Properties.Conditions.Remove(layerConditionModel); } - - public override void CanClose(Action callback) - { - _layer.Enabled = _wasEnabled; - base.CanClose(callback); - } } } \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs index 1ac7355de..247f37b0f 100644 --- a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Threading; using Artemis.DAL; using Artemis.Events; using Artemis.KeyboardProviders; @@ -31,6 +32,7 @@ namespace Artemis.ViewModels private LayerModel _draggingLayer; private Point? _draggingLayerOffset; private LayerEditorViewModel _editorVm; + private ImageSource _keyboardPreview; private Cursor _keyboardPreviewCursor; private BindableCollection _layers; private BindableCollection _profiles; @@ -45,10 +47,13 @@ namespace Artemis.ViewModels Profiles = new BindableCollection(); Layers = new BindableCollection(); + ActiveKeyboard = _mainManager.KeyboardManager.ActiveKeyboard; + events.Subscribe(this); PreviewTimer = new Timer(40); - PreviewTimer.Elapsed += (sender, args) => NotifyOfPropertyChange(() => KeyboardPreview); + PreviewTimer.Elapsed += InvokeUpdateKeyboardPreview; + PropertyChanged += PropertyChangeHandler; LoadProfiles(); @@ -124,59 +129,12 @@ namespace Artemis.ViewModels public ImageSource KeyboardPreview { - get + get { return _keyboardPreview; } + set { - if (_selectedProfile == null || ActiveKeyboard == null) - return null; - - var keyboardRect = ActiveKeyboard.KeyboardRectangle(4); - var visual = new DrawingVisual(); - using (var drawingContext = visual.RenderOpen()) - { - // Setup the DrawingVisual's size - drawingContext.PushClip(new RectangleGeometry(keyboardRect)); - drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); - - // Draw the layers - foreach (var layerModel in _selectedProfile.Layers - .OrderByDescending(l => l.Order) - .Where(l => l.LayerType == LayerType.Keyboard || - l.LayerType == LayerType.KeyboardGif)) - { - layerModel.Draw(null, drawingContext, true); - } - - // Get the selection color - var color = (Color) ThemeManager.DetectAppStyle(Application.Current).Item2.Resources["AccentColor"]; - var pen = new Pen(new SolidColorBrush(color), 0.4); - - // Draw the selection outline and resize indicator - if (SelectedLayer != null && ShouldDrawLayer(SelectedLayer)) - { - var layerRect = ((KeyboardPropertiesModel) SelectedLayer.Properties).GetRect(); - // Deflate the rect so that the border is drawn on the inside - layerRect.Inflate(-0.2, -0.2); - - // Draw an outline around the selected layer - drawingContext.DrawRectangle(null, pen, layerRect); - // Draw a resize indicator in the bottom-right - drawingContext.DrawLine(pen, - new Point(layerRect.BottomRight.X - 1, layerRect.BottomRight.Y - 0.5), - new Point(layerRect.BottomRight.X - 1.2, layerRect.BottomRight.Y - 0.7)); - drawingContext.DrawLine(pen, - new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 1), - new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 1.2)); - drawingContext.DrawLine(pen, - new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 0.5), - new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 0.7)); - } - - // Remove the clip - drawingContext.Pop(); - } - var image = new DrawingImage(visual.Drawing); - - return image; + if (Equals(value, _keyboardPreview)) return; + _keyboardPreview = value; + NotifyOfPropertyChange(() => KeyboardPreview); } } @@ -187,7 +145,7 @@ namespace Artemis.ViewModels public bool CanAddLayer => _selectedProfile != null; public bool CanRemoveLayer => _selectedProfile != null && _selectedLayer != null; - private KeyboardProvider ActiveKeyboard => _mainManager.KeyboardManager.ActiveKeyboard; + private KeyboardProvider ActiveKeyboard { get; set; } /// /// Handles chaning the active keyboard, updating the preview image and profiles collection @@ -195,6 +153,7 @@ namespace Artemis.ViewModels /// public void Handle(ActiveKeyboardChanged message) { + ActiveKeyboard = _mainManager.KeyboardManager.ActiveKeyboard; NotifyOfPropertyChange(() => KeyboardImage); NotifyOfPropertyChange(() => PreviewSettings); LoadProfiles(); @@ -401,8 +360,13 @@ namespace Artemis.ViewModels var x = pos.X/((double) ActiveKeyboard.PreviewSettings.Width/ActiveKeyboard.Width); var y = pos.Y/((double) ActiveKeyboard.PreviewSettings.Height/ActiveKeyboard.Height); - var hoverLayer = SelectedProfile.Layers.OrderBy(l => l.Order).Where(ShouldDrawLayer) - .FirstOrDefault(l => ((KeyboardPropertiesModel) l.Properties).GetRect(1).Contains(x, y)); + var hoverLayer = SelectedProfile.Layers + .OrderBy(l => l.Order) + .Where(l => l.MustDraw()) + .FirstOrDefault(l => ((KeyboardPropertiesModel) l.Properties) + .GetRect(1) + .Contains(x, y)); + SelectedLayer = hoverLayer; } @@ -415,7 +379,7 @@ namespace Artemis.ViewModels var pos = e.GetPosition((Image) e.OriginalSource); var x = pos.X/((double) ActiveKeyboard.PreviewSettings.Width/ActiveKeyboard.Width); var y = pos.Y/((double) ActiveKeyboard.PreviewSettings.Height/ActiveKeyboard.Height); - var hoverLayer = SelectedProfile.Layers.OrderBy(l => l.Order).Where(ShouldDrawLayer) + var hoverLayer = SelectedProfile.Layers.OrderBy(l => l.Order).Where(l => l.MustDraw()) .FirstOrDefault(l => ((KeyboardPropertiesModel) l.Properties).GetRect(1).Contains(x, y)); HandleDragging(e, x, y, hoverLayer); @@ -440,6 +404,62 @@ namespace Artemis.ViewModels KeyboardPreviewCursor = Cursors.Hand; } + private void InvokeUpdateKeyboardPreview(object sender, ElapsedEventArgs e) + { + Application.Current.Dispatcher.Invoke(UpdateKeyboardPreview, DispatcherPriority.ContextIdle); + } + + /// + /// Generates a new image for the keyboard preview + /// + public void UpdateKeyboardPreview() + { + if (_selectedProfile == null || ActiveKeyboard == null) + return; + + var keyboardRect = ActiveKeyboard.KeyboardRectangle(4); + var visual = new DrawingVisual(); + using (var drawingContext = visual.RenderOpen()) + { + // Setup the DrawingVisual's size + drawingContext.PushClip(new RectangleGeometry(keyboardRect)); + drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); + + // Draw the layers + foreach (var layer in _selectedProfile.Layers.OrderByDescending(l => l.Order).Where(l => l.MustDraw())) + layer.Draw(null, drawingContext, true, false); + + // Get the selection color + var color = (Color) ThemeManager.DetectAppStyle(Application.Current).Item2.Resources["AccentColor"]; + var pen = new Pen(new SolidColorBrush(color), 0.4); + + // Draw the selection outline and resize indicator + if (SelectedLayer != null && SelectedLayer.MustDraw()) + { + var layerRect = ((KeyboardPropertiesModel) SelectedLayer.Properties).GetRect(); + // Deflate the rect so that the border is drawn on the inside + layerRect.Inflate(-0.2, -0.2); + + // Draw an outline around the selected layer + drawingContext.DrawRectangle(null, pen, layerRect); + // Draw a resize indicator in the bottom-right + drawingContext.DrawLine(pen, + new Point(layerRect.BottomRight.X - 1, layerRect.BottomRight.Y - 0.5), + new Point(layerRect.BottomRight.X - 1.2, layerRect.BottomRight.Y - 0.7)); + drawingContext.DrawLine(pen, + new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 1), + new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 1.2)); + drawingContext.DrawLine(pen, + new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 0.5), + new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 0.7)); + } + + // Remove the clip + drawingContext.Pop(); + } + KeyboardPreview = new DrawingImage(visual.Drawing); + } + /// /// Handles dragging the given layer /// @@ -496,10 +516,5 @@ namespace Artemis.ViewModels draggingProps.Y = (int) Math.Round(y - _draggingLayerOffset.Value.Y); } } - - private bool ShouldDrawLayer(LayerModel layer) - { - return layer.Enabled && (layer.LayerType == LayerType.Keyboard || layer.LayerType == LayerType.KeyboardGif); - } } } \ No newline at end of file