diff --git a/Artemis/Artemis/App.config b/Artemis/Artemis/App.config index 6ad4f906d..7f7bc23d9 100644 --- a/Artemis/Artemis/App.config +++ b/Artemis/Artemis/App.config @@ -52,38 +52,11 @@ True - - - - - True - - - True - - - True - - - True - - - True - - - #FFFF0000 - - - #FF0000FF - - + Default - - #FF00FF00 - - - #FF6A5ACD + + @@ -145,33 +118,12 @@ True - - - - - True - - - #FFFF2900 - - - #FF26F600 - - - True - - - True - - - True - - - True - Default + + + @@ -344,6 +296,10 @@ + + + + diff --git a/Artemis/Artemis/App.xaml b/Artemis/Artemis/App.xaml index 7acc3be4c..e13bd017f 100644 --- a/Artemis/Artemis/App.xaml +++ b/Artemis/Artemis/App.xaml @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:artemis="clr-namespace:Artemis" + xmlns:converters="clr-namespace:Artemis.Utilities.Converters" DispatcherUnhandledException="Application_DispatcherUnhandledException" ShutdownMode="OnExplicitShutdown"> @@ -21,6 +22,7 @@ + \ No newline at end of file diff --git a/Artemis/Artemis/App.xaml.cs b/Artemis/Artemis/App.xaml.cs index 5ca4b1c47..008f21c59 100644 --- a/Artemis/Artemis/App.xaml.cs +++ b/Artemis/Artemis/App.xaml.cs @@ -2,6 +2,7 @@ using System.Security.Principal; using System.Windows; using System.Windows.Threading; +using Artemis.Utilities; using NLog; using WpfExceptionViewer; @@ -14,8 +15,8 @@ namespace Artemis { public App() { - //if (!IsRunAsAdministrator()) - // GeneralHelpers.RunAsAdministrator(); + if (!IsRunAsAdministrator()) + GeneralHelpers.RunAsAdministrator(); InitializeComponent(); } diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index 83e85ee8a..909e2e886 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -38,8 +38,8 @@ https://github.com/SpoinkyNL/Artemis/wiki/Frequently-Asked-Questions-%28FAQ%29 Artemis Artemis - 2 - 1.1.3.2 + 3 + 1.1.3.3 false true true @@ -276,9 +276,9 @@ + - @@ -301,15 +301,14 @@ - - - - - - - - - + + + + + + + + AudioVisualization.settings True @@ -420,6 +419,26 @@ + + + + + + + + + + + + + + + + + + + + True @@ -442,6 +461,8 @@ + + @@ -450,8 +471,6 @@ - - @@ -467,11 +486,11 @@ - + @@ -482,21 +501,25 @@ + - + - - - - + + + + + + DebugView.xaml + EffectsView.xaml @@ -524,13 +547,16 @@ Witcher3View.xaml - + + EventPropertiesView.xaml + + FolderPropertiesView.xaml - + HeadsetPropertiesView.xaml - + KeyboardPropertiesView.xaml @@ -542,7 +568,7 @@ LayerEditorView.xaml - + MousePropertiesView.xaml @@ -660,7 +686,9 @@ - + + Designer + @@ -695,6 +723,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -731,15 +763,19 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile - + Designer MSBuild:Compile - + + Designer + MSBuild:Compile + + Designer MSBuild:Compile @@ -755,7 +791,7 @@ MSBuild:Compile Designer - + Designer MSBuild:Compile @@ -802,7 +838,9 @@ false - + + + diff --git a/Artemis/Artemis/ArtemisBootstrapper.cs b/Artemis/Artemis/ArtemisBootstrapper.cs index f4137ab37..b4376a51d 100644 --- a/Artemis/Artemis/ArtemisBootstrapper.cs +++ b/Artemis/Artemis/ArtemisBootstrapper.cs @@ -5,9 +5,11 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Forms; using Artemis.InjectionModules; +using Artemis.Settings; using Artemis.Utilities; using Artemis.ViewModels; using Caliburn.Micro; +using Newtonsoft.Json; using Ninject; using Application = System.Windows.Application; using MessageBox = System.Windows.Forms.MessageBox; @@ -22,7 +24,7 @@ namespace Artemis public ArtemisBootstrapper() { // Start logging before anything else - Logging.SetupLogging(Settings.General.Default.LogLevel); + Logging.SetupLogging(General.Default.LogLevel); CheckDuplicateInstances(); Initialize(); @@ -77,6 +79,7 @@ namespace Artemis protected override void Configure() { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}; _kernel = new StandardKernel(new BaseModules(), new ArtemisModules(), new ManagerModules()); _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); @@ -114,7 +117,7 @@ namespace Artemis private void CheckDuplicateInstances() { bool aIsNewInstance; - Mutex = new Mutex(true, "ArtemisMutex", out aIsNewInstance); + Mutex = new Mutex(true, "ArtemisMutex2", out aIsNewInstance); if (aIsNewInstance) return; diff --git a/Artemis/Artemis/DAL/ProfileProvider.cs b/Artemis/Artemis/DAL/ProfileProvider.cs index 0c764b95e..3babec07f 100644 --- a/Artemis/Artemis/DAL/ProfileProvider.cs +++ b/Artemis/Artemis/DAL/ProfileProvider.cs @@ -4,13 +4,13 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; -using System.Xml.Serialization; using Artemis.DeviceProviders; using Artemis.Models; -using Artemis.Models.Profiles; -using Artemis.Models.Profiles.Properties; +using Artemis.Profiles; +using Artemis.Profiles.Layers.Types.Keyboard; using Artemis.Properties; using Artemis.Utilities; +using Newtonsoft.Json; using NLog; namespace Artemis.DAL @@ -65,14 +65,8 @@ namespace Artemis.DAL if (!Directory.Exists(path)) Directory.CreateDirectory(path); - var serializer = new XmlSerializer(typeof(ProfileModel)); - - // Could use a StreamWriter but should serializing fail this method doesn't ruin the existing XML file - using (var xml = new StringWriter()) - { - serializer.Serialize(xml, prof); - File.WriteAllText(path + $@"\{prof.Name}.xml", xml.ToString()); - } + var json = JsonConvert.SerializeObject(prof, Formatting.Indented); + File.WriteAllText(path + $@"\{prof.Name}.json", json); } private static List ReadProfiles() @@ -82,22 +76,18 @@ namespace Artemis.DAL var profiles = new List(); // Create the directory structure - var profilePaths = Directory.GetFiles(ProfileFolder, "*.xml", SearchOption.AllDirectories); + var profilePaths = Directory.GetFiles(ProfileFolder, "*.json", SearchOption.AllDirectories); // Parse the JSON files into objects and add them if they are valid - var deserializer = new XmlSerializer(typeof(ProfileModel)); foreach (var path in profilePaths) { try { - using (var file = new StreamReader(path)) - { - var prof = (ProfileModel) deserializer.Deserialize(file); - if (prof.GameName?.Length > 1 && prof.KeyboardSlug?.Length > 1 && prof.Name?.Length > 1) - profiles.Add(prof); - } + var prof = LoadProfileIfValid(path); + if (prof != null) + profiles.Add(prof); } - catch (InvalidOperationException e) + catch (Exception e) { Logger.Error("Failed to load profile: {0} - {1}", path, e.InnerException.Message); } @@ -144,7 +134,6 @@ namespace Artemis.DAL ((KeyboardPropertiesModel) gifLayer.Properties).GifFile = gifPath; AddOrUpdate(demoProfile); } - } /// @@ -166,18 +155,17 @@ namespace Artemis.DAL /// The loaded profile, or null if invalid public static ProfileModel LoadProfileIfValid(string path) { + // TODO: What exception on load failure? try { - var deserializer = new XmlSerializer(typeof(ProfileModel)); - using (var file = new StreamReader(path)) - { - var prof = (ProfileModel) deserializer.Deserialize(file); - if (!(prof.GameName?.Length > 1) || !(prof.KeyboardSlug?.Length > 1) || !(prof.Name?.Length > 1)) - return null; - return prof; - } + var prof = JsonConvert.DeserializeObject(File.ReadAllText(path)); + if (prof == null) + return null; + if (prof.GameName.Length < 1 || prof.KeyboardSlug.Length < 1 || prof.Name.Length < 1) + return null; + return prof; } - catch (InvalidOperationException) + catch (Exception) { return null; } @@ -186,15 +174,12 @@ namespace Artemis.DAL /// /// Exports the given profile to the provided path in XML /// - /// The profile to export + /// The profile to export /// The path to save the profile to - public static void ExportProfile(ProfileModel selectedProfile, string path) + public static void ExportProfile(ProfileModel prof, string path) { - var serializer = new XmlSerializer(typeof(ProfileModel)); - using (var file = new StreamWriter(path)) - { - serializer.Serialize(file, selectedProfile); - } + var json = JsonConvert.SerializeObject(prof); + File.WriteAllText(path, json); } /// @@ -208,7 +193,7 @@ namespace Artemis.DAL return; // Remove the old file - var path = ProfileFolder + $@"\{profile.KeyboardSlug}\{profile.GameName}\{profile.Name}.xml"; + var path = ProfileFolder + $@"\{profile.KeyboardSlug}\{profile.GameName}\{profile.Name}.json"; if (File.Exists(path)) File.Delete(path); @@ -220,7 +205,7 @@ namespace Artemis.DAL public static void DeleteProfile(ProfileModel profile) { // Remove the file - var path = ProfileFolder + $@"\{profile.KeyboardSlug}\{profile.GameName}\{profile.Name}.xml"; + var path = ProfileFolder + $@"\{profile.KeyboardSlug}\{profile.GameName}\{profile.Name}.json"; if (File.Exists(path)) File.Delete(path); } diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs index fdedd3c49..db45cd5ab 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairHeadsets.cs @@ -1,8 +1,7 @@ -using System.Linq; +using System; +using System.Drawing; +using System.Linq; using System.Threading; -using System.Windows; -using System.Windows.Media; -using Artemis.Utilities; using CUE.NET; using CUE.NET.Devices.Generic.Enums; using Ninject.Extensions.Logging; @@ -35,33 +34,30 @@ namespace Artemis.DeviceProviders.Corsair CueSDK.Reinitialize(); } - public override void UpdateDevice(Brush brush) + public override void UpdateDevice(Bitmap bitmap) { - if (!CanUse || brush == null) + if (!CanUse || bitmap == null) return; + if (bitmap.Width != bitmap.Height) + throw new ArgumentException("Bitmap must be a perfect square"); var leds = CueSDK.HeadsetSDK.Leds.Count(); - var rect = new Rect(new Size(leds*20, leds*20)); - - var visual = new DrawingVisual(); - using (var c = visual.RenderOpen()) - c.DrawRectangle(brush, null, rect); - - using (var img = ImageUtilities.DrawinVisualToBitmap(visual, rect)) + var step = (double) bitmap.Width/leds; + using (bitmap) { - var ledIndex = 0; // Color each LED according to one of the pixels foreach (var corsairLed in CueSDK.HeadsetSDK.Leds) { - corsairLed.Color = ledIndex == 0 - ? img.GetPixel(0, 0) - : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); + if (ledIndex == 0) + corsairLed.Color = bitmap.GetPixel(0, 0); + else + corsairLed.Color = bitmap.GetPixel((int) ((ledIndex + 1)*step - 1), + (int) ((ledIndex + 1)*step - 1)); ledIndex++; } } - // Flush is required for headset to work reliably on CUE2 for some reason - CueSDK.HeadsetSDK.Update(true); + CueSDK.HeadsetSDK.Update(); } private static bool CanInitializeSdk() diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs index 3e08f941c..4fc70ce84 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairMice.cs @@ -1,8 +1,7 @@ -using System.Linq; +using System; +using System.Drawing; +using System.Linq; using System.Threading; -using System.Windows; -using System.Windows.Media; -using Artemis.Utilities; using CUE.NET; using CUE.NET.Devices.Generic.Enums; using Ninject.Extensions.Logging; @@ -35,27 +34,26 @@ namespace Artemis.DeviceProviders.Corsair CueSDK.Reinitialize(); } - public override void UpdateDevice(Brush brush) + public override void UpdateDevice(Bitmap bitmap) { - if (!CanUse || brush == null) + if (!CanUse || bitmap == null) return; + if (bitmap.Width != bitmap.Height) + throw new ArgumentException("Bitmap must be a perfect square"); var leds = CueSDK.MouseSDK.Leds.Count(); - var rect = new Rect(new Size(leds*20, leds*20)); - - var visual = new DrawingVisual(); - using (var c = visual.RenderOpen()) - c.DrawRectangle(brush, null, rect); - - using (var img = ImageUtilities.DrawinVisualToBitmap(visual, rect)) + var step = (double) bitmap.Width/leds; + using (bitmap) { var ledIndex = 0; // Color each LED according to one of the pixels foreach (var corsairLed in CueSDK.MouseSDK.Leds) { - corsairLed.Color = ledIndex == 0 - ? img.GetPixel(0, 0) - : img.GetPixel((ledIndex + 1)*20 - 1, (ledIndex + 1)*20 - 1); + if (ledIndex == 0) + corsairLed.Color = bitmap.GetPixel(0, 0); + else + corsairLed.Color = bitmap.GetPixel((int) ((ledIndex + 1)*step - 1), + (int) ((ledIndex + 1)*step - 1)); ledIndex++; } } diff --git a/Artemis/Artemis/DeviceProviders/DeviceProvider.cs b/Artemis/Artemis/DeviceProviders/DeviceProvider.cs index c244eb73a..f68bc3dbf 100644 --- a/Artemis/Artemis/DeviceProviders/DeviceProvider.cs +++ b/Artemis/Artemis/DeviceProviders/DeviceProvider.cs @@ -1,5 +1,5 @@ -using System.Threading.Tasks; -using System.Windows.Media; +using System.Drawing; +using System.Threading.Tasks; namespace Artemis.DeviceProviders { @@ -16,10 +16,10 @@ namespace Artemis.DeviceProviders public bool CanUse { get; set; } /// - /// Updates a non-keyboard to take the colours of the provided brush + /// Updates a non-keyboard to take the colours found in the provided bitmap /// - /// - public abstract void UpdateDevice(Brush brush); + /// + public abstract void UpdateDevice(Bitmap bitmap); /// /// Tries to enable the device and updates CanUse accordingly @@ -32,7 +32,7 @@ namespace Artemis.DeviceProviders public abstract void Disable(); /// - /// Tries to enable the device and updates CanUse accordingly asynchronously + /// Tries to enable the device and updates CanUse accordingly asynchronously /// /// public Task TryEnableAsync() diff --git a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs index ab0984904..887f4be74 100644 --- a/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs +++ b/Artemis/Artemis/DeviceProviders/KeyboardProvider.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using MahApps.Metro.Controls.Dialogs; -using Brush = System.Windows.Media.Brush; using Size = System.Windows.Size; namespace Artemis.DeviceProviders @@ -87,7 +86,7 @@ namespace Artemis.DeviceProviders return Task.Run(() => Enable()); } - public override void UpdateDevice(Brush brush) + public override void UpdateDevice(Bitmap bitmap) { throw new NotImplementedException("KeyboardProvider doesn't implement UpdateDevice, use DrawBitmap instead."); } diff --git a/Artemis/Artemis/Events/ChangeBitmap.cs b/Artemis/Artemis/Events/ChangeBitmap.cs deleted file mode 100644 index 4f72e82b8..000000000 --- a/Artemis/Artemis/Events/ChangeBitmap.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Drawing; - -namespace Artemis.Events -{ - public class ChangeBitmap - { - public ChangeBitmap(Bitmap bitmap) - { - Bitmap = bitmap; - } - - public Bitmap Bitmap { get; private set; } - - public void ChangeTextMessage(Bitmap bitmap) - { - Bitmap = bitmap; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Events/RazerColorArrayChanged.cs b/Artemis/Artemis/Events/RazerColorArrayChanged.cs new file mode 100644 index 000000000..23d1ae192 --- /dev/null +++ b/Artemis/Artemis/Events/RazerColorArrayChanged.cs @@ -0,0 +1,14 @@ +using System.Windows.Media; + +namespace Artemis.Events +{ + public class RazerColorArrayChanged + { + public RazerColorArrayChanged(Color[,] colors) + { + Colors = colors; + } + + public Color[,] Colors { get; set; } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/InjectionFactories/ILayerEditorVmFactory.cs b/Artemis/Artemis/InjectionFactories/ILayerEditorVmFactory.cs index a33a3416b..0f3e0609f 100644 --- a/Artemis/Artemis/InjectionFactories/ILayerEditorVmFactory.cs +++ b/Artemis/Artemis/InjectionFactories/ILayerEditorVmFactory.cs @@ -1,5 +1,5 @@ using Artemis.Models.Interfaces; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.ViewModels.Profiles; namespace Artemis.InjectionFactories diff --git a/Artemis/Artemis/InjectionModules/ArtemisModules.cs b/Artemis/Artemis/InjectionModules/ArtemisModules.cs index 2ec4602f1..4aa41678d 100644 --- a/Artemis/Artemis/InjectionModules/ArtemisModules.cs +++ b/Artemis/Artemis/InjectionModules/ArtemisModules.cs @@ -13,6 +13,14 @@ using Artemis.Modules.Games.RocketLeague; using Artemis.Modules.Games.TheDivision; using Artemis.Modules.Games.Witcher3; using Artemis.Modules.Overlays.VolumeDisplay; +using Artemis.Profiles.Layers.Animations; +using Artemis.Profiles.Layers.Conditions; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Types.Folder; +using Artemis.Profiles.Layers.Types.Headset; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Profiles.Layers.Types.KeyboardGif; +using Artemis.Profiles.Layers.Types.Mouse; using Artemis.ViewModels.Abstract; using Ninject.Modules; @@ -55,6 +63,28 @@ namespace Artemis.InjectionModules Bind().To().InSingletonScope(); #endregion + + #region Layers + + // Animations + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + // Conditions + Bind().To(); + Bind().To(); + // Types + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + Bind().To(); + + #endregion } } } \ No newline at end of file diff --git a/Artemis/Artemis/InjectionModules/BaseModules.cs b/Artemis/Artemis/InjectionModules/BaseModules.cs index 05805579a..0c5a73936 100644 --- a/Artemis/Artemis/InjectionModules/BaseModules.cs +++ b/Artemis/Artemis/InjectionModules/BaseModules.cs @@ -19,6 +19,7 @@ namespace Artemis.InjectionModules Bind().ToFactory(); Bind().ToFactory(); Bind().ToSelf(); + Bind().ToSelf().InSingletonScope(); Bind().To().InSingletonScope(); Bind().To().InSingletonScope(); diff --git a/Artemis/Artemis/Managers/LoopManager.cs b/Artemis/Artemis/Managers/LoopManager.cs index 73aa62ab3..32abd3fba 100644 --- a/Artemis/Artemis/Managers/LoopManager.cs +++ b/Artemis/Artemis/Managers/LoopManager.cs @@ -6,7 +6,6 @@ using System.Timers; using Artemis.Events; using Caliburn.Micro; using Ninject.Extensions.Logging; -using Brush = System.Windows.Media.Brush; namespace Artemis.Managers { @@ -21,7 +20,8 @@ namespace Artemis.Managers private readonly Timer _loopTimer; private Bitmap _keyboardBitmap; - public LoopManager(IEventAggregator events, ILogger logger, EffectManager effectManager, DeviceManager deviceManager) + public LoopManager(IEventAggregator events, ILogger logger, EffectManager effectManager, + DeviceManager deviceManager) { events.Subscribe(this); _logger = logger; @@ -48,6 +48,18 @@ namespace Artemis.Managers _keyboardBitmap?.Dispose(); } + public void Handle(ActiveEffectChanged message) + { + if (_deviceManager.ActiveKeyboard != null && _effectManager.ActiveEffect != null) + _keyboardBitmap = _deviceManager.ActiveKeyboard.KeyboardBitmap(_effectManager.ActiveEffect.KeyboardScale); + } + + public void Handle(ActiveKeyboardChanged message) + { + if (_deviceManager.ActiveKeyboard != null && _effectManager.ActiveEffect != null) + _keyboardBitmap = _deviceManager.ActiveKeyboard.KeyboardBitmap(_effectManager.ActiveEffect.KeyboardScale); + } + public Task StartAsync() { return Task.Run(() => Start()); @@ -132,50 +144,31 @@ namespace Artemis.Managers renderEffect.Update(); // Get ActiveEffect's bitmap - Brush mouseBrush = null; - Brush headsetBrush = null; + Bitmap mouseBitmap = null; + Bitmap headsetBitmap = null; var mice = _deviceManager.MiceProviders.Where(m => m.CanUse).ToList(); var headsets = _deviceManager.HeadsetProviders.Where(m => m.CanUse).ToList(); - using (Graphics keyboardGraphics = Graphics.FromImage(_keyboardBitmap)) + if (renderEffect.Initialized) + renderEffect.Render(_keyboardBitmap, out mouseBitmap, out headsetBitmap, mice.Any(), headsets.Any()); + + // Draw enabled overlays on top of the renderEffect + foreach (var overlayModel in _effectManager.EnabledOverlays) { - // Fill the bitmap's background with black to avoid trailing colors on some keyboards - keyboardGraphics.Clear(Color.Black); - - if (renderEffect.Initialized) - renderEffect.Render(keyboardGraphics, out mouseBrush, out headsetBrush, mice.Any(), - headsets.Any()); - - // Draw enabled overlays on top of the renderEffect - foreach (var overlayModel in _effectManager.EnabledOverlays) - { - overlayModel.Update(); - overlayModel.RenderOverlay(keyboardGraphics, ref mouseBrush, ref headsetBrush, mice.Any(), - headsets.Any()); - } - - // Update mice and headsets - foreach (var mouse in mice) - mouse.UpdateDevice(mouseBrush); - foreach (var headset in headsets) - headset.UpdateDevice(headsetBrush); + overlayModel.Update(); + overlayModel.RenderOverlay(_keyboardBitmap, ref mouseBitmap, ref headsetBitmap, mice.Any(), + headsets.Any()); } + // Update mice and headsets + foreach (var mouse in mice) + mouse.UpdateDevice(mouseBitmap); + foreach (var headset in headsets) + headset.UpdateDevice(headsetBitmap); + // Update the keyboard _deviceManager.ActiveKeyboard?.DrawBitmap(_keyboardBitmap); } } - - public void Handle(ActiveKeyboardChanged message) - { - if (_deviceManager.ActiveKeyboard != null &&_effectManager.ActiveEffect != null) - _keyboardBitmap = _deviceManager.ActiveKeyboard.KeyboardBitmap(_effectManager.ActiveEffect.KeyboardScale); - } - - public void Handle(ActiveEffectChanged message) - { - if (_deviceManager.ActiveKeyboard != null && _effectManager.ActiveEffect != null) - _keyboardBitmap = _deviceManager.ActiveKeyboard.KeyboardBitmap(_effectManager.ActiveEffect.KeyboardScale); - } } } \ No newline at end of file diff --git a/Artemis/Artemis/Models/EffectModel.cs b/Artemis/Artemis/Models/EffectModel.cs index 962d4cf90..67423ef6d 100644 --- a/Artemis/Artemis/Models/EffectModel.cs +++ b/Artemis/Artemis/Models/EffectModel.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Windows; using Artemis.Managers; using Artemis.Models.Interfaces; -using Artemis.Models.Profiles; +using Artemis.Profiles; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Headset; +using Artemis.Profiles.Layers.Types.Mouse; using Newtonsoft.Json; -using NLog; -using Brush = System.Windows.Media.Brush; namespace Artemis.Models { @@ -15,12 +17,7 @@ namespace Artemis.Models { public delegate void SettingsUpdateHandler(EffectSettings settings); - public bool Initialized { get; set; } - public MainManager MainManager { get; set; } - public string Name { get; set; } - public int KeyboardScale { get; set; } = 4; - - private DateTime _lastTrace; + protected DateTime LastTrace; protected EffectModel(MainManager mainManager, IDataModel dataModel) { @@ -28,6 +25,11 @@ namespace Artemis.Models DataModel = dataModel; } + public bool Initialized { get; set; } + public MainManager MainManager { get; set; } + public string Name { get; set; } + public int KeyboardScale { get; set; } = 4; + // Used by profile system public IDataModel DataModel { get; set; } public ProfileModel Profile { get; set; } @@ -41,7 +43,8 @@ namespace Artemis.Models public abstract void Update(); // Called after every update - public virtual void Render(Graphics keyboard, out Brush mouse, out Brush headset, bool renderMice, bool renderHeadsets) + public virtual void Render(Bitmap keyboard, out Bitmap mouse, out Bitmap headset, bool renderMice, + bool renderHeadsets) { mouse = null; headset = null; @@ -52,24 +55,41 @@ namespace Artemis.Models // Get all enabled layers who's conditions are met var renderLayers = GetRenderLayers(renderMice, renderHeadsets); - // Trace debugging - if (DateTime.Now.AddSeconds(-2) > _lastTrace) + // Render the keyboard layer-by-layer + var keyboardRect = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); + using (var g = Graphics.FromImage(keyboard)) { - _lastTrace = DateTime.Now; - MainManager.Logger.Trace("Effect datamodel as JSON: \r\n{0}", - JsonConvert.SerializeObject(DataModel, Formatting.Indented)); - MainManager.Logger.Trace("Effect {0} has to render {1} layers", Name, renderLayers.Count); - foreach (var renderLayer in renderLayers) - MainManager.Logger.Trace(" Layer name: {0}, layer type: {1}", renderLayer.Name, - renderLayer.LayerType); + // Fill the bitmap's background with black to avoid trailing colors on some keyboards + g.Clear(Color.Black); + Profile.DrawLayers(g, renderLayers.Where(rl => rl.MustDraw()), DataModel, keyboardRect, false, true); } - // Render the keyboard layer-by-layer - Profile.DrawProfile(keyboard, renderLayers, DataModel, MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale), false, true); - // Render the first enabled mouse (will default to null if renderMice was false) - mouse = Profile.GenerateBrush(renderLayers.LastOrDefault(l => l.LayerType == LayerType.Mouse), DataModel); - // Render the first enabled headset (will default to null if renderHeadsets was false) - headset = Profile.GenerateBrush(renderLayers.LastOrDefault(l => l.LayerType == LayerType.Headset), DataModel); + // Render the mouse layer-by-layer + var smallRect = new Rect(0, 0, 40, 40); + mouse = new Bitmap(40, 40); + using (var g = Graphics.FromImage(mouse)) + { + Profile.DrawLayers(g, renderLayers.Where(rl => rl.LayerType is MouseType), DataModel, smallRect, + false, true); + } + + // Render the headset layer-by-layer + headset = new Bitmap(40, 40); + using (var g = Graphics.FromImage(headset)) + { + Profile.DrawLayers(g, renderLayers.Where(rl => rl.LayerType is HeadsetType), DataModel, smallRect, + false, true); + } + + // Trace debugging + if (DateTime.Now.AddSeconds(-2) <= LastTrace) + return; + LastTrace = DateTime.Now; + MainManager.Logger.Trace("Effect datamodel as JSON: \r\n{0}", + JsonConvert.SerializeObject(DataModel, Formatting.Indented)); + MainManager.Logger.Trace("Effect {0} has to render {1} layers", Name, renderLayers.Count); + foreach (var renderLayer in renderLayers) + MainManager.Logger.Trace("- Layer name: {0}, layer type: {1}", renderLayer.Name, renderLayer.LayerType); } public abstract List GetRenderLayers(bool renderMice, bool renderHeadsets); diff --git a/Artemis/Artemis/Models/OverlayModel.cs b/Artemis/Artemis/Models/OverlayModel.cs index 5dac1f475..8d4ac2335 100644 --- a/Artemis/Artemis/Models/OverlayModel.cs +++ b/Artemis/Artemis/Models/OverlayModel.cs @@ -1,6 +1,5 @@ using System.Drawing; using Artemis.Managers; -using Brush = System.Windows.Media.Brush; namespace Artemis.Models { @@ -29,7 +28,7 @@ namespace Artemis.Models } } - public abstract void RenderOverlay(Graphics keyboard, ref Brush mouse, ref Brush headset, bool renderMice, + public abstract void RenderOverlay(Bitmap keyboard, ref Bitmap mouse, ref Bitmap headset, bool renderMice, bool renderHeadsets); } } \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/FolderPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/FolderPropertiesModel.cs deleted file mode 100644 index dc36491c4..000000000 --- a/Artemis/Artemis/Models/Profiles/Properties/FolderPropertiesModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Artemis.Models.Interfaces; - -namespace Artemis.Models.Profiles.Properties -{ - public class FolderPropertiesModel : LayerPropertiesModel - { - public override AppliedProperties GetAppliedProperties(IDataModel dataModel, bool ignoreDynamic = false) - { - return new AppliedProperties(); - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/HeadsetPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/HeadsetPropertiesModel.cs deleted file mode 100644 index c44226e99..000000000 --- a/Artemis/Artemis/Models/Profiles/Properties/HeadsetPropertiesModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Artemis.Models.Interfaces; - -namespace Artemis.Models.Profiles.Properties -{ - public class HeadsetPropertiesModel : LayerPropertiesModel - { - public override AppliedProperties GetAppliedProperties(IDataModel dataModel, bool ignoreDynamic = false) - { - return new AppliedProperties {Brush = Brush}; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs deleted file mode 100644 index 197fdccae..000000000 --- a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Windows; -using System.Xml.Serialization; -using Artemis.Models.Interfaces; - -namespace Artemis.Models.Profiles.Properties -{ - public class KeyboardPropertiesModel : LayerPropertiesModel - { - public KeyboardPropertiesModel() - { - DynamicProperties = new List(); - } - - public double X { get; set; } - public double Y { get; set; } - public double Width { get; set; } - public double Height { get; set; } - public double Opacity { get; set; } - public bool Contain { get; set; } - public LayerAnimation Animation { get; set; } - 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) - { - return new Rect(X*scale, Y*scale, Width*scale, Height*scale); - } - - public override AppliedProperties GetAppliedProperties(IDataModel dataModel, bool ignoreDynamic = false) - { - var applied = new AppliedProperties - { - X = X, - Y = Y, - Width = Width, - Height = Height, - Opacity = Opacity, - Brush = Brush.CloneCurrentValue() - }; - - if (ignoreDynamic) - return applied; - - foreach (var dynamicProperty in DynamicProperties) - dynamicProperty.ApplyProperty(dataModel, ref applied); - - if (Math.Abs(applied.Opacity - 1) > 0.001) - { - applied.Brush = Brush.CloneCurrentValue(); - applied.Brush.Opacity = applied.Opacity; - } - - return applied; - } - } - - public enum LayerAnimation - { - [Description("None")] None, - [Description("Slide left")] SlideLeft, - [Description("Slide right")] SlideRight, - [Description("Slide up")] SlideUp, - [Description("Slide down")] SlideDown, - [Description("Grow")] Grow, - [Description("Pulse")] Pulse - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/LayerPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/LayerPropertiesModel.cs deleted file mode 100644 index 0cd18e38c..000000000 --- a/Artemis/Artemis/Models/Profiles/Properties/LayerPropertiesModel.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System.Windows.Media; -using System.Xml.Serialization; -using Artemis.Models.Interfaces; - -namespace Artemis.Models.Profiles.Properties -{ - [XmlInclude(typeof(SolidColorBrush))] - [XmlInclude(typeof(LinearGradientBrush))] - [XmlInclude(typeof(RadialGradientBrush))] - [XmlInclude(typeof(MatrixTransform))] - [XmlInclude(typeof(KeyboardPropertiesModel))] - [XmlInclude(typeof(MousePropertiesModel))] - [XmlInclude(typeof(HeadsetPropertiesModel))] - [XmlInclude(typeof(FolderPropertiesModel))] - public abstract class LayerPropertiesModel - { - private Brush _brush; - - protected LayerPropertiesModel() - { - Conditions = new List(); - } - - public List Conditions { get; set; } - - public Brush Brush - { - get { return _brush; } - set - { - if (value == null) - { - _brush = null; - return; - } - - if (value.IsFrozen) - { - _brush = value; - return; - } - - // Clone the brush off of the UI thread and freeze it - var cloned = value.Dispatcher.Invoke(value.CloneCurrentValue); - cloned.Freeze(); - _brush = cloned; - } - } - - public abstract AppliedProperties GetAppliedProperties(IDataModel dataModel, bool ignoreDynamic = false); - } - - public struct AppliedProperties - { - public double X { get; set; } - public double Y { get; set; } - public double Width { get; set; } - public double Height { get; set; } - public double Opacity { get; set; } - public Brush Brush { get; set; } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/MousePropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/MousePropertiesModel.cs deleted file mode 100644 index 2bebe3c5b..000000000 --- a/Artemis/Artemis/Models/Profiles/Properties/MousePropertiesModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Artemis.Models.Interfaces; - -namespace Artemis.Models.Profiles.Properties -{ - public class MousePropertiesModel : LayerPropertiesModel - { - public override AppliedProperties GetAppliedProperties(IDataModel dataModel, bool ignoreDynamic = false) - { - return new AppliedProperties {Brush = Brush}; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualization.cs b/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualization.cs new file mode 100644 index 000000000..a3db59c01 --- /dev/null +++ b/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualization.cs @@ -0,0 +1,28 @@ +namespace Artemis.Modules.Effects.AudioVisualizer { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class AudioVisualization { + + public AudioVisualization() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualizerModel.cs b/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualizerModel.cs index dc215cfe4..aa103501f 100644 --- a/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualizerModel.cs +++ b/Artemis/Artemis/Modules/Effects/AudioVisualizer/AudioVisualizerModel.cs @@ -5,13 +5,12 @@ using System.Drawing.Drawing2D; using System.Linq; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; using Artemis.Modules.Effects.AudioVisualizer.Utilities; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; using Artemis.Utilities.Keyboard; using NAudio.CoreAudioApi; using NAudio.Wave; -using Brush = System.Windows.Media.Brush; namespace Artemis.Modules.Effects.AudioVisualizer { @@ -78,7 +77,7 @@ namespace Artemis.Modules.Effects.AudioVisualizer ColorHelpers.ToDrawingColor(Settings.BottomColor) }, LinearGradientMode.Vertical) - { ContainedBrush = false, Height = 0 }); + {ContainedBrush = false, Height = 0}); } _sensitivity = Settings.Sensitivity; _fromBottom = Settings.FromBottom; @@ -118,22 +117,22 @@ namespace Artemis.Modules.Effects.AudioVisualizer if (SpectrumData.Count - 1 < i || SpectrumData[i] == 0) height = 0; else - height = (int)Math.Round(SpectrumData[i] / 2.55); + height = (int) Math.Round(SpectrumData[i]/2.55); // Apply Sensitivity setting - height = height * _sensitivity; + height = height*_sensitivity; var keyboardHeight = - (int)Math.Round(MainManager.DeviceManager.ActiveKeyboard.Height / 100.00 * height * KeyboardScale); + (int) Math.Round(MainManager.DeviceManager.ActiveKeyboard.Height/100.00*height*KeyboardScale); if (keyboardHeight > SoundRectangles[i].Height) SoundRectangles[i].Height = keyboardHeight; else SoundRectangles[i].Height = SoundRectangles[i].Height - Settings.FadeSpeed; // Apply Bars setting - SoundRectangles[i].X = i * KeyboardScale; + SoundRectangles[i].X = i*KeyboardScale; SoundRectangles[i].Width = KeyboardScale; if (_fromBottom) - SoundRectangles[i].Y = MainManager.DeviceManager.ActiveKeyboard.Height * KeyboardScale - + SoundRectangles[i].Y = MainManager.DeviceManager.ActiveKeyboard.Height*KeyboardScale - SoundRectangles[i].Height; } _generating = false; @@ -164,7 +163,7 @@ namespace Artemis.Modules.Effects.AudioVisualizer for (x = 0; x < Lines; x++) { float peak = 0; - var b1 = (int)Math.Pow(2, x * 10.0 / (Lines - 1)); + var b1 = (int) Math.Pow(2, x*10.0/(Lines - 1)); if (b1 > 2047) b1 = 2047; if (b1 <= b0) @@ -174,12 +173,12 @@ namespace Artemis.Modules.Effects.AudioVisualizer if (peak < e.Result[1 + b0].X) peak = e.Result[1 + b0].X; } - var y = (int)(Math.Sqrt(peak) * 3 * 255 - 4); + var y = (int) (Math.Sqrt(peak)*3*255 - 4); if (y > 255) y = 255; if (y < 0) y = 0; - SpectrumData.Add((byte)y); + SpectrumData.Add((byte) y); } } @@ -188,7 +187,7 @@ namespace Artemis.Modules.Effects.AudioVisualizer return null; } - public override void Render(Graphics keyboard, out Brush mouse, out Brush headset, bool renderMice, + public override void Render(Bitmap keyboard, out Bitmap mouse, out Bitmap headset, bool renderMice, bool renderHeadsets) { mouse = null; @@ -200,8 +199,11 @@ namespace Artemis.Modules.Effects.AudioVisualizer // Lock the _spectrumData array while busy with it _generating = true; - foreach (var soundRectangle in SoundRectangles) - soundRectangle.Draw(keyboard); + using (var g = Graphics.FromImage(keyboard)) + { + foreach (var soundRectangle in SoundRectangles) + soundRectangle.Draw(g); + } _generating = false; } diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/Bubble.cs b/Artemis/Artemis/Modules/Effects/Bubbles/Bubble.cs index 535e03428..3e9755968 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/Bubble.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/Bubble.cs @@ -6,11 +6,24 @@ namespace Artemis.Modules.Effects.Bubbles { public class Bubble { + #region Constructors + + public Bubble(Color color, int radius, Point position, Vector direction) + { + Color = color; + Radius = radius; + Position = position; + Direction = direction; + } + + #endregion + #region Properties & Fields private Brush _brush; private Color _color; + public Color Color { get { return _color; } @@ -27,27 +40,15 @@ namespace Artemis.Modules.Effects.Bubbles #endregion - #region Constructors - - public Bubble(Color color, int radius, Point position, Vector direction) - { - this.Color = color; - this.Radius = radius; - this.Position = position; - this.Direction = direction; - } - - #endregion - #region Methods public void CheckCollision(Rect border) { if (Position.X - Radius < border.X || Position.X + Radius > border.X + border.Width) - Direction = new Vector(Direction.X * -1, Direction.Y); + Direction = new Vector(Direction.X*-1, Direction.Y); if (Position.Y - Radius < border.Y || Position.Y + Radius > border.Y + border.Height) - Direction = new Vector(Direction.X, Direction.Y * -1); + Direction = new Vector(Direction.X, Direction.Y*-1); } public void Move() @@ -57,9 +58,9 @@ namespace Artemis.Modules.Effects.Bubbles public void Draw(Graphics g) { - g.FillEllipse(_brush, (float)Position.X - Radius, (float)Position.Y - Radius, Radius * 2, Radius * 2); + g.FillEllipse(_brush, (float) Position.X - Radius, (float) Position.Y - Radius, Radius*2, Radius*2); } #endregion } -} +} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.cs b/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.cs new file mode 100644 index 000000000..bf183261a --- /dev/null +++ b/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.cs @@ -0,0 +1,28 @@ +namespace Artemis.Modules.Effects.Bubbles { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Bubbles { + + public Bubbles() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.settings b/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.settings index 98fa04416..748dada51 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.settings +++ b/Artemis/Artemis/Modules/Effects/Bubbles/Bubbles.settings @@ -1,5 +1,7 @@  - + + diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs index 66ecd0ea2..0ec3ecb48 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesModel.cs @@ -4,24 +4,14 @@ using System.Drawing; using System.Windows; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; -using Brush = System.Windows.Media.Brush; +using Point = System.Windows.Point; namespace Artemis.Modules.Effects.Bubbles { public class BubblesModel : EffectModel { - #region Properties & Fields - - private static readonly Random _random = new Random(); - - private readonly List _bubbles = new List(); - - public BubblesSettings Settings { get; } - - #endregion - #region Constructors public BubblesModel(MainManager mainManager, BubblesSettings settings) @@ -34,27 +24,43 @@ namespace Artemis.Modules.Effects.Bubbles #endregion + #region Properties & Fields + + private static readonly Random _random = new Random(); + + private readonly List _bubbles = new List(); + + public BubblesSettings Settings { get; } + + #endregion + #region Methods public override void Enable() { KeyboardScale = Settings.Smoothness; - Rect rect = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); + var rect = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); - double scaleFactor = Settings.Smoothness / 25.0; + var scaleFactor = Settings.Smoothness/25.0; - for (int i = 0; i < Settings.BubbleCount; i++) + for (var i = 0; i < Settings.BubbleCount; i++) { - Color color = Settings.IsRandomColors ? ColorHelpers.GetRandomRainbowColor() : ColorHelpers.ToDrawingColor(Settings.BubbleColor); + var color = Settings.IsRandomColors + ? ColorHelpers.GetRandomRainbowColor() + : ColorHelpers.ToDrawingColor(Settings.BubbleColor); // -Settings.MoveSpeed because we want to spawn at least one move away from borders - double initialPositionX = ((rect.Width - (Settings.BubbleSize * scaleFactor * 2) - Settings.MoveSpeed * scaleFactor) * _random.NextDouble()) + Settings.BubbleSize * scaleFactor; - double initialPositionY = ((rect.Height - (Settings.BubbleSize * scaleFactor * 2) - Settings.MoveSpeed * scaleFactor) * _random.NextDouble()) + Settings.BubbleSize * scaleFactor; - double initialDirectionX = (Settings.MoveSpeed * scaleFactor * _random.NextDouble()) * (_random.Next(1) == 0 ? -1 : 1); - double initialDirectionY = (Settings.MoveSpeed * scaleFactor - Math.Abs(initialDirectionX)) * (_random.Next(1) == 0 ? -1 : 1); + var initialPositionX = (rect.Width - Settings.BubbleSize*scaleFactor*2 - Settings.MoveSpeed*scaleFactor)* + _random.NextDouble() + Settings.BubbleSize*scaleFactor; + var initialPositionY = (rect.Height - Settings.BubbleSize*scaleFactor*2 - Settings.MoveSpeed*scaleFactor)* + _random.NextDouble() + Settings.BubbleSize*scaleFactor; + var initialDirectionX = Settings.MoveSpeed*scaleFactor*_random.NextDouble()* + (_random.Next(1) == 0 ? -1 : 1); + var initialDirectionY = (Settings.MoveSpeed*scaleFactor - Math.Abs(initialDirectionX))* + (_random.Next(1) == 0 ? -1 : 1); - _bubbles.Add(new Bubble(color, (int)Math.Round(Settings.BubbleSize * scaleFactor), - new System.Windows.Point(initialPositionX, initialPositionY), new Vector(initialDirectionX, initialDirectionY))); + _bubbles.Add(new Bubble(color, (int) Math.Round(Settings.BubbleSize*scaleFactor), + new Point(initialPositionX, initialPositionY), new Vector(initialDirectionX, initialDirectionY))); } Initialized = true; @@ -68,24 +74,31 @@ namespace Artemis.Modules.Effects.Bubbles public override void Update() { - Rect keyboardRectangle = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); - foreach (Bubble bubble in _bubbles) + var keyboardRectangle = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); + foreach (var bubble in _bubbles) { if (Settings.IsShiftColors) - bubble.Color = ColorHelpers.ShiftColor(bubble.Color, Settings.IsRandomColors ? (int)Math.Round(Settings.ShiftColorSpeed * _random.NextDouble()) : Settings.ShiftColorSpeed); + bubble.Color = ColorHelpers.ShiftColor(bubble.Color, + Settings.IsRandomColors + ? (int) Math.Round(Settings.ShiftColorSpeed*_random.NextDouble()) + : Settings.ShiftColorSpeed); bubble.CheckCollision(keyboardRectangle); bubble.Move(); } } - public override void Render(Graphics keyboard, out Brush mouse, out Brush headset, bool renderMice, bool renderHeadsets) + public override void Render(Bitmap keyboard, out Bitmap mouse, out Bitmap headset, bool renderMice, + bool renderHeadsets) { mouse = null; headset = null; - foreach (Bubble bubble in _bubbles) - bubble.Draw(keyboard); + using (var g = Graphics.FromImage(keyboard)) + { + foreach (var bubble in _bubbles) + bubble.Draw(g); + } } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) @@ -95,4 +108,4 @@ namespace Artemis.Modules.Effects.Bubbles #endregion } -} +} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesSettings.cs b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesSettings.cs index 149daa6d1..a6f264c67 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesSettings.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesSettings.cs @@ -57,4 +57,4 @@ namespace Artemis.Modules.Effects.Bubbles Smoothness = 25; } } -} +} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml index 9c40b7792..cb79d8462 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml @@ -120,7 +120,7 @@ HorizontalAlignment="Right" Width="110" TickPlacement="None" TickFrequency="1" Value="{Binding Path=EffectSettings.MoveSpeed, Mode=TwoWay}" Minimum="1" Maximum="15" SmallChange="10" IsSnapToTickEnabled="True" /> - + diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml.cs b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml.cs index 2392befb1..3a9bf3cca 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesView.xaml.cs @@ -9,4 +9,4 @@ namespace Artemis.Modules.Effects.Bubbles InitializeComponent(); } } -} +} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesViewModel.cs b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesViewModel.cs index c24f95272..9c2803a0e 100644 --- a/Artemis/Artemis/Modules/Effects/Bubbles/BubblesViewModel.cs +++ b/Artemis/Artemis/Modules/Effects/Bubbles/BubblesViewModel.cs @@ -14,7 +14,7 @@ namespace Artemis.Modules.Effects.Bubbles events.Subscribe(this); MainManager.EffectManager.EffectModels.Add(EffectModel); - EffectSettings = ((BubblesModel)EffectModel).Settings; + EffectSettings = ((BubblesModel) EffectModel).Settings; } public void Handle(ActiveEffectChanged message) @@ -22,4 +22,4 @@ namespace Artemis.Modules.Effects.Bubbles NotifyOfPropertyChange(() => EffectEnabled); } } -} +} \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs b/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs index aa0819ab9..f66a8b7ad 100644 --- a/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs +++ b/Artemis/Artemis/Modules/Effects/ProfilePreview/ProfilePreviewModel.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Windows; using Artemis.Managers; using Artemis.Models; using Artemis.Models.Interfaces; -using Artemis.Models.Profiles; -using Brush = System.Windows.Media.Brush; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Headset; +using Artemis.Profiles.Layers.Types.Mouse; namespace Artemis.Modules.Effects.ProfilePreview { @@ -32,10 +34,10 @@ namespace Artemis.Modules.Effects.ProfilePreview public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, true); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, true); } - public override void Render(Graphics keyboard, out Brush mouse, out Brush headset, bool renderMice, + public override void Render(Bitmap keyboard, out Bitmap mouse, out Bitmap headset, bool renderMice, bool renderHeadsets) { mouse = null; @@ -48,12 +50,30 @@ namespace Artemis.Modules.Effects.ProfilePreview var renderLayers = GetRenderLayers(renderMice, renderHeadsets); // Render the keyboard layer-by-layer - Profile?.DrawProfile(keyboard, renderLayers, DataModel, MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale), true, true); - // Render the first enabled mouse (will default to null if renderMice was false) - mouse = Profile?.GenerateBrush(renderLayers.LastOrDefault(l => l.LayerType == LayerType.Mouse), DataModel); - // Render the first enabled headset (will default to null if renderHeadsets was false) - headset = Profile?.GenerateBrush(renderLayers.LastOrDefault(l => l.LayerType == LayerType.Headset), - DataModel); + var keyboardRect = MainManager.DeviceManager.ActiveKeyboard.KeyboardRectangle(KeyboardScale); + using (var g = Graphics.FromImage(keyboard)) + { + // Fill the bitmap's background with black to avoid trailing colors on some keyboards + g.Clear(Color.Black); + Profile.DrawLayers(g, renderLayers.Where(rl => rl.MustDraw()), DataModel, keyboardRect, true, true); + } + + // Render the mouse layer-by-layer + var smallRect = new Rect(0, 0, 40, 40); + mouse = new Bitmap(40, 40); + using (var g = Graphics.FromImage(mouse)) + { + Profile.DrawLayers(g, renderLayers.Where(rl => rl.LayerType is MouseType), DataModel, smallRect, + true, true); + } + + // Render the headset layer-by-layer + headset = new Bitmap(40, 40); + using (var g = Graphics.FromImage(headset)) + { + Profile.DrawLayers(g, renderLayers.Where(rl => rl.LayerType is HeadsetType), DataModel, smallRect, + true, true); + } } } diff --git a/Artemis/Artemis/Modules/Effects/TypeWave/TypeWaveModel.cs b/Artemis/Artemis/Modules/Effects/TypeWave/TypeWaveModel.cs index 366012c8f..8020ac68d 100644 --- a/Artemis/Artemis/Modules/Effects/TypeWave/TypeWaveModel.cs +++ b/Artemis/Artemis/Modules/Effects/TypeWave/TypeWaveModel.cs @@ -7,9 +7,8 @@ using Artemis.DeviceProviders.Corsair; using Artemis.DeviceProviders.Logitech.Utilities; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; -using Brush = System.Windows.Media.Brush; namespace Artemis.Modules.Effects.TypeWave { @@ -46,8 +45,8 @@ namespace Artemis.Modules.Effects.TypeWave return; _waves.Add(Settings.IsRandomColors - ? new Wave(new Point(keyMatch.PosX * KeyboardScale, keyMatch.PosY * KeyboardScale), 0, _randomColor) - : new Wave(new Point(keyMatch.PosX * KeyboardScale, keyMatch.PosY * KeyboardScale), 0, + ? new Wave(new Point(keyMatch.PosX*KeyboardScale, keyMatch.PosY*KeyboardScale), 0, _randomColor) + : new Wave(new Point(keyMatch.PosX*KeyboardScale, keyMatch.PosY*KeyboardScale), 0, ColorHelpers.ToDrawingColor(Settings.WaveColor))); } @@ -71,12 +70,12 @@ namespace Artemis.Modules.Effects.TypeWave // TODO: Get from settings var fps = 25; - _waves[i].Size += Settings.SpreadSpeed * KeyboardScale; + _waves[i].Size += Settings.SpreadSpeed*KeyboardScale; if (Settings.IsShiftColors) _waves[i].Color = ColorHelpers.ShiftColor(_waves[i].Color, Settings.ShiftColorSpeed); - var decreaseAmount = 255 / (Settings.TimeToLive / fps); + var decreaseAmount = 255/(Settings.TimeToLive/fps); _waves[i].Color = Color.FromArgb( _waves[i].Color.A - decreaseAmount, _waves[i].Color.R, _waves[i].Color.G, @@ -95,7 +94,7 @@ namespace Artemis.Modules.Effects.TypeWave return null; } - public override void Render(Graphics keyboard, out Brush mouse, out Brush headset, bool renderMice, + public override void Render(Bitmap keyboard, out Bitmap mouse, out Bitmap headset, bool renderMice, bool renderHeadsets) { mouse = null; @@ -111,7 +110,7 @@ namespace Artemis.Modules.Effects.TypeWave if (_waves[i].Size == 0) continue; var path = new GraphicsPath(); - path.AddEllipse(_waves[i].Point.X - _waves[i].Size / 2, _waves[i].Point.Y - _waves[i].Size / 2, + path.AddEllipse(_waves[i].Point.X - _waves[i].Size/2, _waves[i].Point.Y - _waves[i].Size/2, _waves[i].Size, _waves[i].Size); Color fillColor; @@ -122,16 +121,19 @@ namespace Artemis.Modules.Effects.TypeWave var pthGrBrush = new PathGradientBrush(path) { - SurroundColors = new[] { _waves[i].Color }, + SurroundColors = new[] {_waves[i].Color}, CenterColor = fillColor }; - keyboard.FillPath(pthGrBrush, path); - pthGrBrush.FocusScales = new PointF(0.3f, 0.8f); + using (var g = Graphics.FromImage(keyboard)) + { + g.FillPath(pthGrBrush, path); + pthGrBrush.FocusScales = new PointF(0.3f, 0.8f); - keyboard.FillPath(pthGrBrush, path); - keyboard.DrawEllipse(new Pen(pthGrBrush, 1), _waves[i].Point.X - _waves[i].Size / 2, - _waves[i].Point.Y - _waves[i].Size / 2, _waves[i].Size, _waves[i].Size); + g.FillPath(pthGrBrush, path); + g.DrawEllipse(new Pen(pthGrBrush, 1), _waves[i].Point.X - _waves[i].Size/2, + _waves[i].Point.Y - _waves[i].Size/2, _waves[i].Size, _waves[i].Size); + } } } } diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs index be27cad22..ca28bc496 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileDataModel.cs @@ -14,6 +14,16 @@ namespace Artemis.Modules.Effects.WindowsProfile public CpuDataModel Cpu { get; set; } public PerformanceDataModel Performance { get; set; } public Spotify Spotify { get; set; } + public CurrentTime CurrentTime { get; set; } + } + + class CurrentTime + { + public int Hours24 { get; set; } + public int Hours12 { get; set; } + public int Minutes { get; set; } + public int Seconds { get; set; } + } public class CpuDataModel diff --git a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs index 03d1843ee..1ce50203f 100644 --- a/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs +++ b/Artemis/Artemis/Modules/Effects/WindowsProfile/WindowsProfileModel.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Ninject.Extensions.Logging; using SpotifyAPI.Local; @@ -16,7 +16,8 @@ namespace Artemis.Modules.Effects.WindowsProfile { [DllImport("psapi.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetPerformanceInfo([Out] out PerformanceInformation performanceInformation, [In] int size); + public static extern bool GetPerformanceInfo([Out] out PerformanceInformation performanceInformation, + [In] int size); public static long GetPhysicalAvailableMemoryInMiB() { @@ -58,6 +59,8 @@ namespace Artemis.Modules.Effects.WindowsProfile } } + + public class WindowsProfileModel : EffectModel { private readonly ILogger _logger; @@ -95,6 +98,7 @@ namespace Artemis.Modules.Effects.WindowsProfile var dataModel = (WindowsProfileDataModel) DataModel; UpdateCpu(dataModel); UpdateSpotify(dataModel); + UpdateDay(dataModel); } #region CPU @@ -161,7 +165,7 @@ namespace Artemis.Modules.Effects.WindowsProfile public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, false); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets, false); } public static PerformanceCounter GetOverallPerformanceCounter() @@ -190,6 +194,19 @@ namespace Artemis.Modules.Effects.WindowsProfile #endregion + #region Current Time + private void UpdateDay(WindowsProfileDataModel dataModel) + { + + var now = DateTime.Now; + dataModel.CurrentTime.Hours24 = int.Parse(now.ToString("HH")); + dataModel.CurrentTime.Hours12 = int.Parse(now.ToString("hh")); + dataModel.CurrentTime.Minutes = int.Parse(now.ToString("mm")); + dataModel.CurrentTime.Seconds = int.Parse(now.ToString("ss")); + + } + #endregion + #region Spotify public void SetupSpotify() diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.Designer.cs b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.Designer.cs index e9ab9d8e4..85a966af8 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.Designer.cs +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.Designer.cs @@ -35,102 +35,6 @@ namespace Artemis.Modules.Games.CounterStrike { } } - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string GameDirectory { - get { - return ((string)(this["GameDirectory"])); - } - set { - this["GameDirectory"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool AmmoEnabled { - get { - return ((bool)(this["AmmoEnabled"])); - } - set { - this["AmmoEnabled"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FFFF2900")] - public global::System.Windows.Media.Color AmmoMainColor { - get { - return ((global::System.Windows.Media.Color)(this["AmmoMainColor"])); - } - set { - this["AmmoMainColor"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FF26F600")] - public global::System.Windows.Media.Color AmmoSecondaryColor { - get { - return ((global::System.Windows.Media.Color)(this["AmmoSecondaryColor"])); - } - set { - this["AmmoSecondaryColor"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool TeamColorEnabled { - get { - return ((bool)(this["TeamColorEnabled"])); - } - set { - this["TeamColorEnabled"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool FlashEnabled { - get { - return ((bool)(this["FlashEnabled"])); - } - set { - this["FlashEnabled"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool SmokeEnabled { - get { - return ((bool)(this["SmokeEnabled"])); - } - set { - this["SmokeEnabled"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool LowHpEnabled { - get { - return ((bool)(this["LowHpEnabled"])); - } - set { - this["LowHpEnabled"] = value; - } - } - [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("Default")] @@ -142,5 +46,17 @@ namespace Artemis.Modules.Games.CounterStrike { this["LastProfile"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string GameDirectory { + get { + return ((string)(this["GameDirectory"])); + } + set { + this["GameDirectory"] = value; + } + } } } diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.settings b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.settings index e5b8b302b..cfa6c587e 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.settings +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrike.settings @@ -1,7 +1,5 @@  - - + @@ -13,26 +11,5 @@ - - True - - - #FFFF2900 - - - #FF26F600 - - - True - - - True - - - True - - - True - \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs index 35f69761b..567c79bca 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities.GameState; using Newtonsoft.Json; using Ninject.Extensions.Logging; @@ -66,7 +66,7 @@ namespace Artemis.Modules.Games.CounterStrike public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeSettings.cs b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeSettings.cs index 96141065c..24b5699f1 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeSettings.cs +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeSettings.cs @@ -1,5 +1,4 @@ -using System.Windows.Media; -using Artemis.Models; +using Artemis.Models; namespace Artemis.Modules.Games.CounterStrike { @@ -12,29 +11,11 @@ namespace Artemis.Modules.Games.CounterStrike public string GameDirectory { get; set; } - public bool AmmoEnabled { get; set; } - public Color AmmoMainColor { get; set; } - public Color AmmoSecondaryColor { get; set; } - - public bool TeamColorEnabled { get; set; } - public bool FlashEnabled { get; set; } - public bool SmokeEnabled { get; set; } - public bool LowHpEnabled { get; set; } - public sealed override void Load() { Enabled = CounterStrike.Default.Enabled; LastProfile = CounterStrike.Default.LastProfile; GameDirectory = CounterStrike.Default.GameDirectory; - - AmmoEnabled = CounterStrike.Default.AmmoEnabled; - AmmoMainColor = CounterStrike.Default.AmmoMainColor; - AmmoSecondaryColor = CounterStrike.Default.AmmoSecondaryColor; - - TeamColorEnabled = CounterStrike.Default.TeamColorEnabled; - FlashEnabled = CounterStrike.Default.FlashEnabled; - SmokeEnabled = CounterStrike.Default.SmokeEnabled; - LowHpEnabled = CounterStrike.Default.LowHpEnabled; } public sealed override void Save() @@ -42,15 +23,6 @@ namespace Artemis.Modules.Games.CounterStrike CounterStrike.Default.Enabled = Enabled; CounterStrike.Default.GameDirectory = GameDirectory; - CounterStrike.Default.AmmoEnabled = AmmoEnabled; - CounterStrike.Default.AmmoMainColor = AmmoMainColor; - CounterStrike.Default.AmmoSecondaryColor = AmmoSecondaryColor; - - CounterStrike.Default.TeamColorEnabled = TeamColorEnabled; - CounterStrike.Default.FlashEnabled = FlashEnabled; - CounterStrike.Default.SmokeEnabled = SmokeEnabled; - CounterStrike.Default.LowHpEnabled = LowHpEnabled; - CounterStrike.Default.Save(); } @@ -58,15 +30,6 @@ namespace Artemis.Modules.Games.CounterStrike { Enabled = true; GameDirectory = string.Empty; - - AmmoEnabled = true; - AmmoMainColor = Color.FromArgb(255, 38, 246, 0); - AmmoSecondaryColor = Color.FromArgb(255, 255, 41, 0); - - TeamColorEnabled = true; - FlashEnabled = true; - SmokeEnabled = true; - LowHpEnabled = true; } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/Dota2/Dota2.Designer.cs b/Artemis/Artemis/Modules/Games/Dota2/Dota2.Designer.cs index 8e1cd92e2..1f692a740 100644 --- a/Artemis/Artemis/Modules/Games/Dota2/Dota2.Designer.cs +++ b/Artemis/Artemis/Modules/Games/Dota2/Dota2.Designer.cs @@ -58,125 +58,5 @@ namespace Artemis.Modules.Games.Dota2 { this["GameDirectory"] = value; } } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool CanCastAbility { - get { - return ((bool)(this["CanCastAbility"])); - } - set { - this["CanCastAbility"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowHealth { - get { - return ((bool)(this["ShowHealth"])); - } - set { - this["ShowHealth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowDayCycle { - get { - return ((bool)(this["ShowDayCycle"])); - } - set { - this["ShowDayCycle"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowMana { - get { - return ((bool)(this["ShowMana"])); - } - set { - this["ShowMana"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowEvents { - get { - return ((bool)(this["ShowEvents"])); - } - set { - this["ShowEvents"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FFFF0000")] - public global::System.Windows.Media.Color MainColor { - get { - return ((global::System.Windows.Media.Color)(this["MainColor"])); - } - set { - this["MainColor"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FF0000FF")] - public global::System.Windows.Media.Color ManaColor { - get { - return ((global::System.Windows.Media.Color)(this["ManaColor"])); - } - set { - this["ManaColor"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Default")] - public string KeyboardLayout { - get { - return ((string)(this["KeyboardLayout"])); - } - set { - this["KeyboardLayout"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FF00FF00")] - public global::System.Windows.Media.Color AbilityReadyColor { - get { - return ((global::System.Windows.Media.Color)(this["AbilityReadyColor"])); - } - set { - this["AbilityReadyColor"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("#FF6A5ACD")] - public global::System.Windows.Media.Color AbilityCooldownColor { - get { - return ((global::System.Windows.Media.Color)(this["AbilityCooldownColor"])); - } - set { - this["AbilityCooldownColor"] = value; - } - } } } diff --git a/Artemis/Artemis/Modules/Games/Dota2/Dota2.settings b/Artemis/Artemis/Modules/Games/Dota2/Dota2.settings index 557cc1a44..c3dafb56c 100644 --- a/Artemis/Artemis/Modules/Games/Dota2/Dota2.settings +++ b/Artemis/Artemis/Modules/Games/Dota2/Dota2.settings @@ -1,7 +1,5 @@  - - + @@ -13,35 +11,5 @@ - - True - - - True - - - True - - - True - - - True - - - #FFFF0000 - - - #FF0000FF - - - Default - - - #FF00FF00 - - - #FF6A5ACD - \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs b/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs index 6b3c77ce3..1c4fb8fb4 100644 --- a/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs +++ b/Artemis/Artemis/Modules/Games/Dota2/Dota2Model.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities.GameState; using Newtonsoft.Json; @@ -66,7 +66,7 @@ namespace Artemis.Modules.Games.Dota2 public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/Dota2/Dota2Settings.cs b/Artemis/Artemis/Modules/Games/Dota2/Dota2Settings.cs index ada8893d1..b57b5b478 100644 --- a/Artemis/Artemis/Modules/Games/Dota2/Dota2Settings.cs +++ b/Artemis/Artemis/Modules/Games/Dota2/Dota2Settings.cs @@ -1,5 +1,4 @@ -using System.Windows.Media; -using Artemis.Models; +using Artemis.Models; namespace Artemis.Modules.Games.Dota2 { @@ -10,74 +9,28 @@ namespace Artemis.Modules.Games.Dota2 Load(); } + public string GameDirectory { get; set; } - public override void Load() + + public sealed override void Load() { - KeyboardLayout = Dota2.Default.KeyboardLayout; - MainColor = Dota2.Default.MainColor; - ManaColor = Dota2.Default.ManaColor; - ShowHealth = Dota2.Default.ShowHealth; - CanCastAbility = Dota2.Default.CanCastAbility; Enabled = Dota2.Default.Enabled; GameDirectory = Dota2.Default.GameDirectory; - ShowDayCycle = Dota2.Default.ShowDayCycle; - ShowMana = Dota2.Default.ShowMana; - ShowEvents = Dota2.Default.ShowEvents; - AbilityReadyColor = Dota2.Default.AbilityReadyColor; - AbilityCooldownColor = Dota2.Default.AbilityCooldownColor; } - public override void Save() + public sealed override void Save() { Dota2.Default.Enabled = Enabled; Dota2.Default.LastProfile = LastProfile; Dota2.Default.GameDirectory = GameDirectory; - Dota2.Default.KeyboardLayout = KeyboardLayout; - Dota2.Default.MainColor = MainColor; - Dota2.Default.ManaColor = ManaColor; - Dota2.Default.ShowDayCycle = ShowDayCycle; - Dota2.Default.ShowHealth = ShowHealth; - Dota2.Default.CanCastAbility = CanCastAbility; - Dota2.Default.ShowMana = ShowMana; - Dota2.Default.ShowEvents = ShowEvents; - Dota2.Default.AbilityCooldownColor = AbilityCooldownColor; - Dota2.Default.AbilityReadyColor = AbilityReadyColor; - Dota2.Default.Save(); } - public override void ToDefault() + public sealed override void ToDefault() { Enabled = true; GameDirectory = string.Empty; - - KeyboardLayout = "Default"; - MainColor = Color.FromArgb(255, 255, 0, 0); - ManaColor = Color.FromArgb(255, 0, 0, 255); - AbilityCooldownColor = Color.FromArgb(255, 106, 90, 205); - AbilityReadyColor = Color.FromArgb(255, 0, 255, 0); - ShowHealth = true; - CanCastAbility = true; - ShowDayCycle = true; - ShowMana = true; - ShowEvents = true; } - - #region Variables - - public string GameDirectory { get; set; } - public bool CanCastAbility { get; set; } - public bool ShowHealth { get; set; } - public bool ShowDayCycle { get; set; } - public bool ShowMana { get; set; } - public bool ShowEvents { get; set; } - public Color MainColor { get; set; } - public Color ManaColor { get; set; } - public string KeyboardLayout { get; set; } - public Color AbilityCooldownColor { get; set; } - public Color AbilityReadyColor { get; set; } - - #endregion } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchDataModel.cs b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchDataModel.cs index 31bd573ee..f7e3ece14 100644 --- a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchDataModel.cs +++ b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchDataModel.cs @@ -9,13 +9,16 @@ namespace Artemis.Modules.Games.Overwatch public bool UltimateReady { get; set; } public bool Ability1Ready { get; set; } public bool Ability2Ready { get; set; } + public bool UltimateUsed { get; set; } + public bool CanChangeHero { get; set; } } public enum OverwatchStatus { - Unkown, + Unknown, InMainMenu, - InGame + InGame, + InCharacterSelect } public enum OverwatchCharacter diff --git a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchModel.cs b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchModel.cs index acab9c792..fa92cebaf 100644 --- a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchModel.cs +++ b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchModel.cs @@ -1,21 +1,28 @@ -using System.Collections.Generic; -using System.Drawing; +using System; +using System.Collections.Generic; using System.Linq; +using System.Windows.Media; using Artemis.Events; using Artemis.Managers; using Artemis.Models; using Artemis.Models.Interfaces; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; using Artemis.Utilities.DataReaders; using Caliburn.Micro; -using Color = System.Windows.Media.Color; namespace Artemis.Modules.Games.Overwatch { public class OverwatchModel : GameModel { private readonly IEventAggregator _events; + private DateTime _characterChange; + // Using sticky values on these since they can cause flickering + private StickyValue _stickyStatus; + private StickyValue _stickyUltimateReady; + private StickyValue _stickyUltimateUsed; + private DateTime _ultimateReady; + private DateTime _ultimateUsed; public OverwatchModel(IEventAggregator events, MainManager mainManager, OverwatchSettings settings) : base(mainManager, settings, new OverwatchDataModel()) @@ -27,7 +34,7 @@ namespace Artemis.Modules.Games.Overwatch Enabled = Settings.Enabled; Initialized = false; - MmfReader = new MmfReader("overwatchMmf"); + MmfReader = new MmfReader("overwatchMmf", MainManager.Logger); LoadOverwatchCharacters(); } @@ -46,102 +53,184 @@ namespace Artemis.Modules.Games.Overwatch { OverwatchCharacters = new List { - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Genji, Color = Color.FromRgb(13, 61, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Mccree, Color = Color.FromRgb(24, 1, 1)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Pharah, Color = Color.FromRgb(0, 6, 32)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Reaper, Color = Color.FromRgb(7, 0, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Soldier76, Color = Color.FromRgb(3, 5, 11)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Tracer, Color = Color.FromRgb(46, 12, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Bastion, Color = Color.FromRgb(6, 10, 5)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Hanzo, Color = Color.FromRgb(28, 24, 8)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Junkrat, Color = Color.FromRgb(59, 28, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Mei, Color = Color.FromRgb(3, 20, 55)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Torbjörn, Color = Color.FromRgb(31, 4, 3)}, - new CharacterColor - { - OverwatchCharacter = OverwatchCharacter.Widowmaker, - Color = Color.FromRgb(16, 3, 17) - }, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Dva, Color = Color.FromRgb(62, 12, 32)}, - new CharacterColor - { - OverwatchCharacter = OverwatchCharacter.Reinhardt, - Color = Color.FromRgb(12, 16, 16) - }, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Roadhog, Color = Color.FromRgb(26, 10, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Winston, Color = Color.FromRgb(17, 18, 26)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Zarya, Color = Color.FromRgb(58, 7, 24)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Lúcio, Color = Color.FromRgb(8, 35, 0)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Mercy, Color = Color.FromRgb(60, 56, 26)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Symmetra, Color = Color.FromRgb(11, 29, 37)}, - new CharacterColor {OverwatchCharacter = OverwatchCharacter.Zenyatta, Color = Color.FromRgb(62, 54, 6)} + new CharacterColor {Character = OverwatchCharacter.Genji, Color = Color.FromRgb(55, 245, 0)}, + new CharacterColor {Character = OverwatchCharacter.Mccree, Color = Color.FromRgb(97, 5, 5)}, + new CharacterColor {Character = OverwatchCharacter.Pharah, Color = Color.FromRgb(0, 24, 128)}, + new CharacterColor {Character = OverwatchCharacter.Reaper, Color = Color.FromRgb(28, 0, 2)}, + new CharacterColor {Character = OverwatchCharacter.Soldier76, Color = Color.FromRgb(14, 21, 45)}, + new CharacterColor {Character = OverwatchCharacter.Tracer, Color = Color.FromRgb(186, 49, 0)}, + new CharacterColor {Character = OverwatchCharacter.Bastion, Color = Color.FromRgb(26, 43, 20)}, + new CharacterColor {Character = OverwatchCharacter.Hanzo, Color = Color.FromRgb(113, 99, 33)}, + new CharacterColor {Character = OverwatchCharacter.Junkrat, Color = Color.FromRgb(237, 113, 2)}, + new CharacterColor {Character = OverwatchCharacter.Mei, Color = Color.FromRgb(15, 82, 222)}, + new CharacterColor {Character = OverwatchCharacter.Torbjörn, Color = Color.FromRgb(125, 18, 12)}, + new CharacterColor {Character = OverwatchCharacter.Widowmaker, Color = Color.FromRgb(65, 12, 70)}, + new CharacterColor {Character = OverwatchCharacter.Dva, Color = Color.FromRgb(248, 48, 129)}, + new CharacterColor {Character = OverwatchCharacter.Reinhardt, Color = Color.FromRgb(51, 65, 66)}, + new CharacterColor {Character = OverwatchCharacter.Roadhog, Color = Color.FromRgb(107, 40, 2)}, + new CharacterColor {Character = OverwatchCharacter.Winston, Color = Color.FromRgb(70, 73, 107)}, + new CharacterColor {Character = OverwatchCharacter.Zarya, Color = Color.FromRgb(235, 28, 97)}, + new CharacterColor {Character = OverwatchCharacter.Lúcio, Color = Color.FromRgb(34, 142, 2)}, + new CharacterColor {Character = OverwatchCharacter.Mercy, Color = Color.FromRgb(243, 226, 106)}, + new CharacterColor {Character = OverwatchCharacter.Symmetra, Color = Color.FromRgb(46, 116, 148)}, + new CharacterColor {Character = OverwatchCharacter.Zenyatta, Color = Color.FromRgb(248, 218, 26)} }; } - public override void Dispose() - { - Initialized = false; - } - public override void Enable() { + _stickyStatus = new StickyValue(300); + _stickyUltimateReady = new StickyValue(350); + _stickyUltimateUsed = new StickyValue(350); Initialized = true; } + public override void Dispose() + { + _stickyStatus.Dispose(); + _stickyUltimateReady.Dispose(); + _stickyUltimateUsed.Dispose(); + Initialized = false; + } + public override void Update() + { + UpdateOverwatch(); + ApplyStickyValues(); + } + + private void ApplyStickyValues() + { + var gameDataModel = (OverwatchDataModel) DataModel; + gameDataModel.Status = _stickyStatus.Value; + gameDataModel.UltimateReady = _stickyUltimateReady.Value; + gameDataModel.UltimateUsed = _stickyUltimateUsed.Value; + } + + public void UpdateOverwatch() { var gameDataModel = (OverwatchDataModel) DataModel; var colors = MmfReader.GetColorArray(); if (colors == null) return; - var bitmap = new Bitmap(22, 6); - - using (var g = Graphics.FromImage(bitmap)) - { - for (var y = 0; y < 6; y++) - { - for (var x = 0; x < 22; x++) - { - g.DrawRectangle(new Pen(ColorHelpers.ToDrawingColor(colors[y, x])), y, x, 1, 1); - } - } - } - _events.PublishOnUIThread(new ChangeBitmap(bitmap)); + _events.PublishOnUIThread(new RazerColorArrayChanged(colors)); + //MainManager.Logger.Trace("DataModel: \r\n{0}", + // JsonConvert.SerializeObject(gameDataModel, Formatting.Indented)); // Determine general game state - gameDataModel.Status = colors[0, 0].Equals(Color.FromRgb(55, 30, 0)) - ? OverwatchStatus.InMainMenu - : OverwatchStatus.Unkown; + ParseGameSate(gameDataModel, colors); - if (gameDataModel.Status == OverwatchStatus.InMainMenu) + // Parse the lighting + var characterMatch = ParseCharacter(gameDataModel, colors); + + // Ult can't possibly be ready within 2 seconds of changing, this avoids false positives. + // Filtering on ultReady and ultUsed removes false positives from the native ultimate effects + // The control keys don't show during character select, so don't continue on those either. + if (_characterChange.AddSeconds(2) >= DateTime.Now || + _ultimateUsed.AddSeconds(2) >= DateTime.Now || + _ultimateReady.AddSeconds(2) >= DateTime.Now || + _stickyStatus.Value == OverwatchStatus.InCharacterSelect) return; - // If ingame, look for a character - var characterMatch = OverwatchCharacters.FirstOrDefault(c => c.Color == colors[0, 0]); - if (characterMatch.OverwatchCharacter == OverwatchCharacter.None) + ParseSpecialKeys(gameDataModel, characterMatch, colors); + ParseAbilities(gameDataModel, colors); + } + + private void ParseGameSate(OverwatchDataModel gameDataModel, Color[,] colors) + { + if (_ultimateUsed.AddSeconds(5) >= DateTime.Now) return; - gameDataModel.Status = OverwatchStatus.InGame; - gameDataModel.Character = characterMatch.OverwatchCharacter; + if (colors[0, 0].Equals(Color.FromRgb(55, 30, 0))) + _stickyStatus.Value = OverwatchStatus.InMainMenu; + + if (_stickyStatus.Value != OverwatchStatus.InMainMenu) + return; + + gameDataModel.Character = OverwatchCharacter.None; + gameDataModel.Ability1Ready = false; + gameDataModel.Ability2Ready = false; + _stickyUltimateReady.Value = false; + _stickyUltimateUsed.Value = false; + } + + private CharacterColor? ParseCharacter(OverwatchDataModel gameDataModel, Color[,] colors) + { + var characterMatch = OverwatchCharacters.FirstOrDefault(c => c.Color == colors[0, 20]); + // If a new character was chosen, let the other methods know + if (characterMatch.Character != gameDataModel.Character) + _characterChange = DateTime.Now; + + // If no character was found, this method shouldn't continue + if (characterMatch.Character == OverwatchCharacter.None) + return characterMatch; + + // If WASD isn't orange (any of them will do), player is in character select + _stickyStatus.Value = ControlsShown(colors) ? OverwatchStatus.InGame : OverwatchStatus.InCharacterSelect; + + // Update the datamodel + gameDataModel.Character = characterMatch.Character; + return characterMatch; + } + + private bool ControlsShown(Color[,] colors) + { + var keyColor = Color.FromRgb(222, 153, 0); + return colors[2, 3] == keyColor || colors[3, 2] == keyColor || + colors[3, 3] == keyColor || colors[3, 4] == keyColor; + } + + private void ParseSpecialKeys(OverwatchDataModel gameDataModel, CharacterColor? characterMatch, Color[,] colors) + { + if (characterMatch == null || characterMatch.Value.Character == OverwatchCharacter.None) + return; - // Ability1 is ready when LShift is lid - gameDataModel.Ability1Ready = colors[4, 1].Equals(Color.FromRgb(4, 141, 144)); - // Ability2 is ready when E is lid - gameDataModel.Ability2Ready = colors[2, 4].Equals(Color.FromRgb(4, 141, 144)); // Ultimate is ready when Q is blinking - gameDataModel.UltimateReady = !characterMatch.Color.Equals(colors[2, 2]); + var charCol = characterMatch.Value.Color; + var backlidColor = Color.FromRgb((byte) (charCol.R*0.25), (byte) (charCol.G*0.25), (byte) (charCol.B*0.25)); + var ultReady = !backlidColor.Equals(colors[2, 2]); + + if (_ultimateUsed.AddSeconds(15) <= DateTime.Now) + { + // Player can change hero if H is blinking + gameDataModel.CanChangeHero = !colors[3, 7].Equals(backlidColor); + + if (!_stickyUltimateReady.Value && ultReady && ControlsShown(colors)) + { + _ultimateReady = DateTime.Now; + _stickyUltimateReady.Value = true; + } + } + + // If ult no longer ready but it was ready before, it was used. + if (_stickyUltimateReady.Value && !ultReady) + { + _stickyUltimateReady.Value = false; + if (_ultimateUsed.AddSeconds(15) <= DateTime.Now) + _ultimateUsed = DateTime.Now; + } + + // UltimateUsed is true for 10 seconds after ultimate went on cooldown + if (_ultimateUsed != DateTime.MinValue) + _stickyUltimateUsed.Value = _ultimateUsed.AddSeconds(10) >= DateTime.Now; + } + + private void ParseAbilities(OverwatchDataModel gameDataModel, Color[,] colors) + { + gameDataModel.Ability1Ready = colors[4, 1].Equals(Color.FromRgb(4, 141, 144)); + gameDataModel.Ability2Ready = colors[2, 4].Equals(Color.FromRgb(4, 141, 144)); } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } public struct CharacterColor { - public OverwatchCharacter OverwatchCharacter { get; set; } + public OverwatchCharacter Character { get; set; } public Color Color { get; set; } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs b/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs index 4b93dc620..8d5aa782b 100644 --- a/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs +++ b/Artemis/Artemis/Modules/Games/RocketLeague/RocketLeagueModel.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Settings; using Artemis.Utilities; using Artemis.Utilities.Memory; @@ -66,11 +66,20 @@ namespace Artemis.Modules.Games.RocketLeague ((RocketLeagueDataModel) DataModel).Boost = 0; if (((RocketLeagueDataModel) DataModel).Boost > 100) ((RocketLeagueDataModel) DataModel).Boost = 100; + + if (DateTime.Now.AddSeconds(-2) <= LastTrace) + return; + + MainManager.Logger.Trace("Offsets as JSON: \r\n{0}", + JsonConvert.SerializeObject(_pointer.GameAddresses, Formatting.Indented)); + MainManager.Logger.Trace("RL specific offsets: {0}", offsets); + MainManager.Logger.Trace("Boost address: {0}", boostAddress); + MainManager.Logger.Trace("Boost float: {0}", boostFloat); } public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs index 8087cb468..d7c683626 100644 --- a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs +++ b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; using Artemis.Utilities.LogitechDll; @@ -131,7 +131,7 @@ namespace Artemis.Modules.Games.TheDivision public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } } \ 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 a61da7526..3de8ee80f 100644 --- a/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs +++ b/Artemis/Artemis/Modules/Games/Witcher3/Witcher3Model.cs @@ -1,20 +1,19 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; namespace Artemis.Modules.Games.Witcher3 { public class Witcher3Model : GameModel { - private readonly Stopwatch _updateSw; private readonly Regex _configRegex; + private readonly Stopwatch _updateSw; private string _witcherSettings; public Witcher3Model(MainManager mainManager, Witcher3Settings settings) @@ -139,7 +138,7 @@ namespace Artemis.Modules.Games.Witcher3 public override List GetRenderLayers(bool renderMice, bool renderHeadsets) { - return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); + return Profile.GetRenderLayers(DataModel, renderMice, renderHeadsets); } } } \ No newline at end of file diff --git a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs index 7a2af183c..435fa4d0a 100644 --- a/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs +++ b/Artemis/Artemis/Modules/Overlays/VolumeDisplay/VolumeDisplayModel.cs @@ -4,9 +4,8 @@ using System.Runtime.InteropServices; using System.Windows.Forms; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using NAudio.CoreAudioApi; -using Brush = System.Windows.Media.Brush; namespace Artemis.Modules.Overlays.VolumeDisplay { @@ -46,10 +45,10 @@ namespace Artemis.Modules.Overlays.VolumeDisplay if (VolumeDisplay.Ttl < 1) return; - var decreaseAmount = 500 / fps; + var decreaseAmount = 500/fps; VolumeDisplay.Ttl = VolumeDisplay.Ttl - decreaseAmount; if (VolumeDisplay.Ttl < 128) - VolumeDisplay.Transparancy = (byte)(VolumeDisplay.Transparancy - 20); + VolumeDisplay.Transparancy = (byte) (VolumeDisplay.Transparancy - 20); try { @@ -57,7 +56,7 @@ namespace Artemis.Modules.Overlays.VolumeDisplay var volumeFloat = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console) .AudioEndpointVolume.MasterVolumeLevelScalar; - VolumeDisplay.Volume = (int)(volumeFloat * 100); + VolumeDisplay.Volume = (int) (volumeFloat*100); } catch (COMException) { @@ -78,11 +77,16 @@ namespace Artemis.Modules.Overlays.VolumeDisplay VolumeDisplay.Transparancy = 255; } - public override void RenderOverlay(Graphics keyboard, ref Brush mouse, ref Brush headset, bool renderMice, + public override void RenderOverlay(Bitmap keyboard, ref Bitmap mouse, ref Bitmap headset, bool renderMice, bool renderHeadsets) { - if (MainManager.DeviceManager.ActiveKeyboard != null && VolumeDisplay != null && VolumeDisplay.Ttl >= 1) - VolumeDisplay.Draw(keyboard); + if (MainManager.DeviceManager.ActiveKeyboard == null || VolumeDisplay == null || VolumeDisplay.Ttl < 1) + return; + + using (var g = Graphics.FromImage(keyboard)) + { + VolumeDisplay.Draw(g); + } } } } \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/GrowAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/GrowAnimation.cs new file mode 100644 index 000000000..da70c3808 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/GrowAnimation.cs @@ -0,0 +1,61 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class GrowAnimation : ILayerAnimation + { + public string Name { get; } = "Grow"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed/2.5; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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 clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + // Take an offset of 4 to allow layers to slightly leave their bounds + var progress = (6.0 - props.AnimationProgress)*10.0; + if (progress < 0) + { + // Can't meddle with the original brush because it's frozen. + var brush = applied.Brush.Clone(); + brush.Opacity = 1 + 0.025*progress; + if (brush.Opacity < 0) + brush.Opacity = 0; + if (brush.Opacity > 1) + brush.Opacity = 1; + applied.Brush = brush; + } + rect.Inflate(-rect.Width/100.0*progress, -rect.Height/100.0*progress); + clip.Inflate(-clip.Width/100.0*progress, -clip.Height/100.0*progress); + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, rect); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) => layer.Properties.AnimationProgress > 10; + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/NoneAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/NoneAnimation.cs new file mode 100644 index 000000000..3bbe7d9d1 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/NoneAnimation.cs @@ -0,0 +1,24 @@ +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class NoneAnimation : ILayerAnimation + { + public string Name { get; } = "None"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + } + + public bool MustExpire(LayerModel layer) + { + return true; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/PulseAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/PulseAnimation.cs new file mode 100644 index 000000000..0d5e34253 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/PulseAnimation.cs @@ -0,0 +1,50 @@ +using System; +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class PulseAnimation : ILayerAnimation + { + public string Name { get; } = "Pulse"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed/2; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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 clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + // Can't meddle with the original brush because it's frozen. + var brush = applied.Brush.Clone(); + brush.Opacity = (Math.Sin(props.AnimationProgress*Math.PI) + 1)*(props.Opacity/2); + applied.Brush = brush; + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, rect); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) => layer.Properties.AnimationProgress > 2; + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/SlideDownAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/SlideDownAnimation.cs new file mode 100644 index 000000000..e25af27df --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/SlideDownAnimation.cs @@ -0,0 +1,51 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class SlideDownAnimation : ILayerAnimation + { + public string Name { get; } = "Slide down"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed*2; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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(new Point(rect.X, rect.Y + props.AnimationProgress), new Size(rect.Width, rect.Height)); + var s2 = new Rect(new Point(s1.X, s1.Y - rect.Height), new Size(rect.Width, rect.Height)); + + var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, s1); + c.DrawRectangle(applied.Brush, null, s2); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) + { + return layer.Properties.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Height*4; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/SlideLeftAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/SlideLeftAnimation.cs new file mode 100644 index 000000000..ff277c1a9 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/SlideLeftAnimation.cs @@ -0,0 +1,52 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class SlideLeftAnimation : ILayerAnimation + { + public string Name { get; } = "Slide left"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed*2; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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(new Point(rect.X - props.AnimationProgress, rect.Y), + new Size(rect.Width + 0.05, rect.Height)); + var s2 = new Rect(new Point(s1.X + rect.Width, rect.Y), new Size(rect.Width, rect.Height)); + + var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, s1); + c.DrawRectangle(applied.Brush, null, s2); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) + { + return layer.Properties.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Width*4; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/SlideRightAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/SlideRightAnimation.cs new file mode 100644 index 000000000..88b02828b --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/SlideRightAnimation.cs @@ -0,0 +1,51 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class SlideRightAnimation : ILayerAnimation + { + public string Name { get; } = "Slide right"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed*2; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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(new Point(rect.X + props.AnimationProgress, rect.Y), new Size(rect.Width, rect.Height)); + var s2 = new Rect(new Point(s1.X - rect.Width, rect.Y), new Size(rect.Width + 1, rect.Height)); + + var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, s1); + c.DrawRectangle(applied.Brush, null, s2); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) + { + return layer.Properties.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Width*4; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Animations/SlideUpAnimation.cs b/Artemis/Artemis/Profiles/Layers/Animations/SlideUpAnimation.cs new file mode 100644 index 000000000..05c2f8b6c --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Animations/SlideUpAnimation.cs @@ -0,0 +1,51 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Animations +{ + public class SlideUpAnimation : ILayerAnimation + { + public string Name { get; } = "Slide up"; + + public void Update(LayerModel layerModel, bool updateAnimations) + { + var progress = layerModel.Properties.AnimationProgress; + if (MustExpire(layerModel)) + progress = 0; + progress = progress + layerModel.Properties.AnimationSpeed*2; + + // If not previewing, store the animation progress in the actual model for the next frame + if (updateAnimations) + layerModel.Properties.AnimationProgress = progress; + } + + public void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c) + { + if (applied.Brush == null) + return; + + const int scale = 4; + // Set up variables for this frame + 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(new Point(rect.X, rect.Y - props.AnimationProgress), new Size(rect.Width, rect.Height)); + var s2 = new Rect(new Point(s1.X, s1.Y + rect.Height), new Size(rect.Width, rect.Height)); + + var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(applied.Brush, null, s1); + c.DrawRectangle(applied.Brush, null, s2); + c.Pop(); + } + + public bool MustExpire(LayerModel layer) + { + return layer.Properties.AnimationProgress + layer.Properties.AnimationSpeed*2 >= layer.Properties.Height*4; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Conditions/DataModelCondition.cs b/Artemis/Artemis/Profiles/Layers/Conditions/DataModelCondition.cs new file mode 100644 index 000000000..aba92d32f --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Conditions/DataModelCondition.cs @@ -0,0 +1,15 @@ +using System.Linq; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Conditions +{ + public class DataModelCondition : ILayerCondition + { + public bool ConditionsMet(LayerModel layer, IDataModel dataModel) + { + return layer.Properties.Conditions.All(cm => cm.ConditionMet(dataModel)); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Conditions/EventCondition.cs b/Artemis/Artemis/Profiles/Layers/Conditions/EventCondition.cs new file mode 100644 index 000000000..5a7adc7d0 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Conditions/EventCondition.cs @@ -0,0 +1,21 @@ +using System.Linq; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Conditions +{ + public class EventCondition : ILayerCondition + { + public bool ConditionsMet(LayerModel layer, IDataModel dataModel) + { + var conditionsMet = layer.Properties.Conditions.All(cm => cm.ConditionMet(dataModel)); + layer.EventProperties.Update(layer, conditionsMet); + + if (conditionsMet && layer.EventProperties.MustTrigger) + layer.EventProperties.TriggerEvent(layer); + + return conditionsMet && layer.EventProperties.MustDraw; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerAnimation.cs b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerAnimation.cs new file mode 100644 index 000000000..c0df0f6a4 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerAnimation.cs @@ -0,0 +1,13 @@ +using System.Windows.Media; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Interfaces +{ + public interface ILayerAnimation + { + string Name { get; } + void Update(LayerModel layerModel, bool updateAnimations); + void Draw(LayerPropertiesModel props, LayerPropertiesModel applied, DrawingContext c); + bool MustExpire(LayerModel layer); + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerCondition.cs b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerCondition.cs new file mode 100644 index 000000000..56588ad8d --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerCondition.cs @@ -0,0 +1,10 @@ +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Interfaces +{ + public interface ILayerCondition + { + bool ConditionsMet(LayerModel layer, IDataModel dataModel); + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs new file mode 100644 index 000000000..e83eb6382 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.ViewModels.Profiles.Layers; + +namespace Artemis.Profiles.Layers.Interfaces +{ + public interface ILayerType + { + /// + /// Layer type name + /// + string Name { get; } + + /// + /// Gets whether this type must be drawn on the keyboard/the editor or not + /// + bool MustDraw { get; } + + /// + /// The the thumbnail for this layer type + /// + /// The layer to draw the thumbnail for + ImageSource DrawThumbnail(LayerModel layer); + + /// + /// Draws the layer + /// + /// The layer to draw + /// The drawing context to draw with + void Draw(LayerModel layer, DrawingContext c); + + /// + /// Updates the provided layer layerModel according to this type + /// + /// The layerModel to apply to + /// The datamodel to base the layer on + /// Set to true if previewing this layer + void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false); + + /// + /// Sets up the layer's properties to accommodate this layerType + /// + /// + void SetupProperties(LayerModel layerModel); + + /// + /// Sets up a viewmodel to accomodate this layerType + /// + /// The current viewmodel + /// + /// The datamodel to use in the new viewmodel + /// The layer to use in the new viewmodel + LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, List layerAnimations, IDataModel dataModel, LayerModel proposedLayer); + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/Properties/DynamicPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/DynamicPropertiesModel.cs similarity index 76% rename from Artemis/Artemis/Models/Profiles/Properties/DynamicPropertiesModel.cs rename to Artemis/Artemis/Profiles/Layers/Models/DynamicPropertiesModel.cs index 8c3956744..7c05955d3 100644 --- a/Artemis/Artemis/Models/Profiles/Properties/DynamicPropertiesModel.cs +++ b/Artemis/Artemis/Profiles/Layers/Models/DynamicPropertiesModel.cs @@ -1,9 +1,8 @@ using System.ComponentModel; using Artemis.Models.Interfaces; using Artemis.Utilities; -using static System.Decimal; -namespace Artemis.Models.Profiles.Properties +namespace Artemis.Profiles.Layers.Models { public class DynamicPropertiesModel { @@ -37,31 +36,31 @@ namespace Artemis.Models.Profiles.Properties /// public LayerPropertyOptions LayerPropertyOptions { get; set; } - internal void ApplyProperty(IDataModel dataModel, ref AppliedProperties properties) + internal void ApplyProperty(IDataModel dataModel, LayerPropertiesModel properties) { if (LayerPropertyType == LayerPropertyType.PercentageOf) - ApplyPercentageOf(dataModel, ref properties, PercentageSource); + ApplyPercentageOf(dataModel, properties, PercentageSource); if (LayerPropertyType == LayerPropertyType.PercentageOfProperty) - ApplyPercentageOfProperty(dataModel, ref properties); + ApplyPercentageOfProperty(dataModel, properties); } - private void ApplyPercentageOf(IDataModel dataModel, ref AppliedProperties properties, double src) + private void ApplyPercentageOf(IDataModel dataModel, LayerPropertiesModel properties, double src) { if (GameProperty == null) return; var gameProperty = dataModel.GetPropValue(GameProperty); - var percentage = ToDouble(gameProperty)/src; + var percentage = decimal.ToDouble(gameProperty)/src; if (LayerProperty == "Width") - ApplyWidth(ref properties, percentage); + ApplyWidth(properties, percentage); else if (LayerProperty == "Height") - ApplyHeight(ref properties, percentage); + ApplyHeight(properties, percentage); else if (LayerProperty == "Opacity") - ApplyOpacity(ref properties, percentage); + ApplyOpacity(properties, percentage); } - private void ApplyWidth(ref AppliedProperties properties, double percentage) + private void ApplyWidth(LayerPropertiesModel properties, double percentage) { var newWidth = percentage*properties.Width; var difference = properties.Width - newWidth; @@ -72,7 +71,7 @@ namespace Artemis.Models.Profiles.Properties properties.X = properties.X + difference; } - private void ApplyHeight(ref AppliedProperties properties, double percentage) + private void ApplyHeight(LayerPropertiesModel properties, double percentage) { var newHeight = percentage*properties.Height; var difference = properties.Height - newHeight; @@ -82,7 +81,7 @@ namespace Artemis.Models.Profiles.Properties properties.Y = properties.Y + difference; } - private void ApplyOpacity(ref AppliedProperties properties, double percentage) + private void ApplyOpacity(LayerPropertiesModel properties, double percentage) { properties.Opacity = percentage*properties.Opacity; if (properties.Opacity < 0.0) @@ -93,12 +92,16 @@ namespace Artemis.Models.Profiles.Properties // Apply the inverse/decrease option if (LayerPropertyOptions == LayerPropertyOptions.Decrease) properties.Opacity = 1.0 - properties.Opacity; + + var brush = properties.Brush.Clone(); + brush.Opacity = properties.Opacity; + properties.Brush = brush; } - private void ApplyPercentageOfProperty(IDataModel dataModel, ref AppliedProperties properties) + private void ApplyPercentageOfProperty(IDataModel dataModel, LayerPropertiesModel properties) { var value = dataModel.GetPropValue(PercentageProperty); - ApplyPercentageOf(dataModel, ref properties, value); + ApplyPercentageOf(dataModel, properties, value); } } diff --git a/Artemis/Artemis/Profiles/Layers/Models/EventPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/EventPropertiesModel.cs new file mode 100644 index 000000000..8acd950da --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Models/EventPropertiesModel.cs @@ -0,0 +1,49 @@ +using System; +using Newtonsoft.Json; + +namespace Artemis.Profiles.Layers.Models +{ + public abstract class EventPropertiesModel + { + public ExpirationType ExpirationType { get; set; } + public TimeSpan Length { get; set; } + public TimeSpan TriggerDelay { get; set; } + + [JsonIgnore] + public bool MustTrigger { get; set; } + + [JsonIgnore] + public DateTime AnimationStart { get; set; } + + [JsonIgnore] + public bool MustDraw { get; set; } + + /// + /// Resets the event's properties and triggers it + /// + public abstract void TriggerEvent(LayerModel layer); + + /// + /// Gets whether the event should stop + /// + /// + /// + public abstract bool MustStop(LayerModel layer); + + // Called every frame, if parent conditions met. + public void Update(LayerModel layerModel, bool conditionsMet) + { + if (MustStop(layerModel)) + MustDraw = false; + + if (!conditionsMet) + MustTrigger = true; + } + } + + public enum ExpirationType + { + Time, + Animation + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Models/KeyboardEventPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/KeyboardEventPropertiesModel.cs new file mode 100644 index 000000000..ce0cb8bd2 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Models/KeyboardEventPropertiesModel.cs @@ -0,0 +1,47 @@ +using System; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Profiles.Layers.Types.KeyboardGif; + +namespace Artemis.Profiles.Layers.Models +{ + public class KeyboardEventPropertiesModel : EventPropertiesModel + { + public override void TriggerEvent(LayerModel layer) + { + var keyboardProperties = layer.Properties as KeyboardPropertiesModel; + if (keyboardProperties == null) + throw new ArgumentException("Layer's properties cannot be null " + + "and must be of type KeyboardPropertiesModel"); + if (!MustTrigger) + return; + + MustTrigger = false; + MustDraw = true; + keyboardProperties.AnimationProgress = 0.0; + if (layer.GifImage != null) + layer.GifImage.CurrentFrame = 0; + } + + public override bool MustStop(LayerModel layer) + { + var keyboardProperties = layer.Properties as KeyboardPropertiesModel; + if (keyboardProperties == null) + throw new ArgumentException("Layer's properties cannot be null " + + "and must be of type KeyboardPropertiesModel"); + + switch (ExpirationType) + { + case ExpirationType.Time: + if (AnimationStart == DateTime.MinValue) + return false; + return DateTime.Now - Length > AnimationStart; + case ExpirationType.Animation: + if (layer.LayerType is KeyboardGifType) + return layer.GifImage?.CurrentFrame >= layer.GifImage?.FrameCount - 1; + return layer.LayerAnimation == null || layer.LayerAnimation.MustExpire(layer); + default: + return true; + } + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs b/Artemis/Artemis/Profiles/Layers/Models/LayerConditionModel.cs similarity index 85% rename from Artemis/Artemis/Models/Profiles/LayerConditionModel.cs rename to Artemis/Artemis/Profiles/Layers/Models/LayerConditionModel.cs index cc8f723dd..3e93af114 100644 --- a/Artemis/Artemis/Models/Profiles/LayerConditionModel.cs +++ b/Artemis/Artemis/Profiles/Layers/Models/LayerConditionModel.cs @@ -1,58 +1,58 @@ -using System; -using Artemis.Models.Interfaces; -using Artemis.Utilities; -using DynamicExpresso; - -namespace Artemis.Models.Profiles -{ - public class LayerConditionModel - { - private readonly Interpreter _interpreter; - - public LayerConditionModel() - { - _interpreter = new Interpreter(); - } - - public string Field { get; set; } - public string Value { get; set; } - public string Operator { get; set; } - public string Type { get; set; } - - public bool ConditionMet(IDataModel subject) - { - if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Value) || string.IsNullOrEmpty(Type)) - return false; - - var inspect = GeneralHelpers.GetPropertyValue(subject, Field); - if (inspect == null) - return false; - - // Put the subject in a list, allowing Dynamic Linq to be used. - if (Type == "String") - { - return _interpreter.Eval($"subject.{Field}.ToLower() {Operator} value", - new Parameter("subject", typeof(T), subject), - new Parameter("value", Value.ToLower())); - } - - Parameter rightParam = null; - switch (Type) - { - case "Enum": - var enumType = GeneralHelpers.GetPropertyValue(subject, Field).GetType(); - rightParam = new Parameter("value", Enum.Parse(enumType, Value)); - break; - case "Boolean": - rightParam = new Parameter("value", bool.Parse(Value)); - break; - case "Int32": - rightParam = new Parameter("value", int.Parse(Value)); - break; - } - - return _interpreter.Eval($"subject.{Field} {Operator} value", - new Parameter("subject", typeof(T), subject), rightParam); - } - } +using System; +using Artemis.Models.Interfaces; +using Artemis.Utilities; +using DynamicExpresso; + +namespace Artemis.Profiles.Layers.Models +{ + public class LayerConditionModel + { + private readonly Interpreter _interpreter; + + public LayerConditionModel() + { + _interpreter = new Interpreter(); + } + + public string Field { get; set; } + public string Value { get; set; } + public string Operator { get; set; } + public string Type { get; set; } + + public bool ConditionMet(IDataModel subject) + { + if (string.IsNullOrEmpty(Field) || string.IsNullOrEmpty(Value) || string.IsNullOrEmpty(Type)) + return false; + + var inspect = GeneralHelpers.GetPropertyValue(subject, Field); + if (inspect == null) + return false; + + // Put the subject in a list, allowing Dynamic Linq to be used. + if (Type == "String") + { + return _interpreter.Eval($"subject.{Field}.ToLower() {Operator} value", + new Parameter("subject", subject.GetType(), subject), + new Parameter("value", Value.ToLower())); + } + + Parameter rightParam = null; + switch (Type) + { + case "Enum": + var enumType = GeneralHelpers.GetPropertyValue(subject, Field).GetType(); + rightParam = new Parameter("value", Enum.Parse(enumType, Value)); + break; + case "Boolean": + rightParam = new Parameter("value", bool.Parse(Value)); + break; + case "Int32": + rightParam = new Parameter("value", int.Parse(Value)); + break; + } + + return _interpreter.Eval($"subject.{Field} {Operator} value", + new Parameter("subject", subject.GetType(), subject), rightParam); + } + } } \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/LayerModel.cs b/Artemis/Artemis/Profiles/Layers/Models/LayerModel.cs similarity index 50% rename from Artemis/Artemis/Models/Profiles/LayerModel.cs rename to Artemis/Artemis/Profiles/Layers/Models/LayerModel.cs index 2b8d647ae..5c2cf057e 100644 --- a/Artemis/Artemis/Models/Profiles/LayerModel.cs +++ b/Artemis/Artemis/Profiles/Layers/Models/LayerModel.cs @@ -1,276 +1,259 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Windows.Media; -using System.Xml.Serialization; -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; -using Artemis.Utilities.Layers; -using Artemis.Utilities.ParentChild; - -namespace Artemis.Models.Profiles -{ - public class LayerModel : IChildItem, IChildItem - { - public LayerModel() - { - Children = new ChildItemCollection(this); - } - - public string Name { get; set; } - public int Order { get; set; } - public LayerType LayerType { get; set; } - public bool Enabled { get; set; } - public bool Expanded { get; set; } - public LayerPropertiesModel Properties { get; set; } - public ChildItemCollection Children { get; } - - [XmlIgnore] - public ImageSource LayerImage => Drawer.DrawThumbnail(this); - - [XmlIgnore] - public LayerModel Parent { get; internal set; } - - [XmlIgnore] - public ProfileModel Profile { get; internal set; } - - [XmlIgnore] - public GifImage GifImage { get; set; } - - public bool ConditionsMet(IDataModel dataModel) - { - return Enabled && Properties.Conditions.All(cm => cm.ConditionMet(dataModel)); - } - - public void Draw(IDataModel dataModel, DrawingContext c, bool preview, bool updateAnimations) - { - if (LayerType != LayerType.Keyboard && LayerType != LayerType.KeyboardGif) - return; - - // Preview simply shows the properties as they are. When not previewing they are applied - var appliedProperties = !preview - ? Properties.GetAppliedProperties(dataModel) - : Properties.GetAppliedProperties(dataModel, true); - - // Update animations - AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties, updateAnimations); - - if (LayerType == LayerType.Keyboard) - Drawer.Draw(c, (KeyboardPropertiesModel) Properties, appliedProperties); - else if (LayerType == LayerType.KeyboardGif) - GifImage = Drawer.DrawGif(c, (KeyboardPropertiesModel) Properties, appliedProperties, GifImage); - } - - public Brush GenerateBrush(LayerType type, IDataModel dataModel, bool preview, bool updateAnimations) - { - if (!Enabled) - return null; - if (LayerType != LayerType.Folder && LayerType != type) - return null; - - // Preview simply shows the properties as they are. When not previewing they are applied - AppliedProperties appliedProperties; - if (!preview) - { - if (!ConditionsMet(dataModel)) - return null; // Return null when not previewing and the conditions arent met - appliedProperties = Properties.GetAppliedProperties(dataModel); - } - else - appliedProperties = Properties.GetAppliedProperties(dataModel, true); - - // TODO: Mouse/headset animations - - if (LayerType != LayerType.Folder) - return appliedProperties.Brush; - - Brush res = null; - foreach (var layerModel in Children.OrderByDescending(l => l.Order)) - { - var brush = layerModel.GenerateBrush(type, dataModel, preview, updateAnimations); - if (brush != null) - res = brush; - } - return res; - } - - public void SetupProperties() - { - if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) && - !(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 - { - Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor()) - }; - else if (LayerType == LayerType.Headset && !(Properties is HeadsetPropertiesModel)) - Properties = new HeadsetPropertiesModel - { - Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor()) - }; - } - - public void FixOrder() - { - Children.Sort(l => l.Order); - for (var i = 0; i < Children.Count; i++) - Children[i].Order = i; - } - - /// - /// Returns whether the layer meets the requirements to be drawn - /// - /// - public bool MustDraw() - { - // If any of the parents are disabled, this layer must not be drawn - var parent = Parent; - while (parent != null) - { - if (!parent.Enabled) - return false; - parent = parent.Parent; - } - return Enabled && (LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif); - } - - public IEnumerable GetLayers() - { - var layers = new List(); - foreach (var layerModel in Children) - { - layers.Add(layerModel); - layers.AddRange(layerModel.GetLayers()); - } - - return layers; - } - - public static LayerModel CreateLayer() - { - return new LayerModel - { - Name = "New layer", - Enabled = true, - Order = -1, - LayerType = LayerType.Keyboard, - Properties = new KeyboardPropertiesModel - { - Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor()), - Animation = LayerAnimation.None, - Height = 1, - Width = 1, - X = 0, - Y = 0, - Opacity = 1 - } - }; - } - - public void InsertBefore(LayerModel source) - { - source.Order = Order; - Insert(source); - } - - public void InsertAfter(LayerModel source) - { - source.Order = Order + 1; - Insert(source); - } - - private void Insert(LayerModel source) - { - if (Parent != null) - { - foreach (var child in Parent.Children.OrderBy(c => c.Order)) - { - if (child.Order >= source.Order) - child.Order++; - } - Parent.Children.Add(source); - } - else if (Profile != null) - { - foreach (var layer in Profile.Layers.OrderBy(l => l.Order)) - { - if (layer.Order >= source.Order) - layer.Order++; - } - Profile.Layers.Add(source); - } - } - - /// - /// Generates a flat list containing all layers that must be rendered on the keyboard, - /// the first mouse layer to be rendered and the first headset layer to be rendered - /// - /// The game data model to base the conditions on - /// Instance of said game data model - /// Whether or not to include mice in the list - /// Whether or not to include headsets in the list - /// - /// A flat list containing all layers that must be rendered - public List GetRenderLayers(IDataModel dataModel, bool includeMice, bool includeHeadsets, - bool ignoreConditions = false) - { - var layers = new List(); - foreach (var layerModel in Children.OrderByDescending(c => c.Order)) - { - if (!layerModel.Enabled || - !includeMice && layerModel.LayerType == LayerType.Mouse || - !includeHeadsets && layerModel.LayerType == LayerType.Headset) - continue; - - if (!ignoreConditions) - { - if (!layerModel.ConditionsMet(dataModel)) - continue; - } - - layers.Add(layerModel); - layers.AddRange(layerModel.GetRenderLayers(dataModel, includeMice, includeHeadsets, ignoreConditions)); - } - - return layers; - } - - #region IChildItem Members - - LayerModel IChildItem.Parent - { - get { return Parent; } - set { Parent = value; } - } - - ProfileModel IChildItem.Parent - { - get { return Profile; } - set { Profile = value; } - } - - #endregion - } - - public enum LayerType - { - [Description("Folder")] Folder, - [Description("Keyboard")] Keyboard, - [Description("Keyboard - GIF")] KeyboardGif, - [Description("Mouse")] Mouse, - [Description("Headset")] Headset - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Conditions; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Types.Headset; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Profiles.Layers.Types.Mouse; +using Artemis.Utilities; +using Artemis.Utilities.ParentChild; +using Newtonsoft.Json; + +namespace Artemis.Profiles.Layers.Models +{ + public class LayerModel : IChildItem, IChildItem + { + public LayerModel() + { + Children = new ChildItemCollection(this); + + var model = Properties as KeyboardPropertiesModel; + if (model != null) + GifImage = new GifImage(model.GifFile); + } + + public ILayerType LayerType { get; set; } + public ILayerCondition LayerCondition { get; set; } + public ILayerAnimation LayerAnimation { get; set; } + + public string Name { get; set; } + public int Order { get; set; } + + public bool Enabled { get; set; } + public bool Expanded { get; set; } + public bool IsEvent { get; set; } + public LayerPropertiesModel Properties { get; set; } + public EventPropertiesModel EventProperties { get; set; } + public ChildItemCollection Children { get; } + + [JsonIgnore] + public LayerPropertiesModel AppliedProperties { get; set; } + + [JsonIgnore] + public ImageSource LayerImage => LayerType.DrawThumbnail(this); + + [JsonIgnore] + public LayerModel Parent { get; internal set; } + + [JsonIgnore] + public ProfileModel Profile { get; internal set; } + + [JsonIgnore] + public GifImage GifImage { get; set; } + + /// + /// Checks whether this layers conditions are met. + /// If they are met and this layer is an event, this also triggers that event. + /// + /// + /// + public bool ConditionsMet(IDataModel dataModel) + { + // Conditions are not even checked if the layer isn't enabled + return Enabled && LayerCondition.ConditionsMet(this, dataModel); + } + + public void Update(IDataModel dataModel, bool preview, bool updateAnimations) + { + LayerType.Update(this, dataModel, preview); + LayerAnimation?.Update(this, updateAnimations); + } + + public void Draw(IDataModel dataModel, DrawingContext c, bool preview, bool updateAnimations) + { + LayerType.Draw(this, c); + } + + public void SetupProperties() + { + LayerType.SetupProperties(this); + + // If the type is an event, set it up + if (IsEvent && EventProperties == null) + { + EventProperties = new KeyboardEventPropertiesModel + { + ExpirationType = ExpirationType.Time, + Length = new TimeSpan(0, 0, 1), + TriggerDelay = new TimeSpan(0) + }; + } + } + + public void FixOrder() + { + Children.Sort(l => l.Order); + for (var i = 0; i < Children.Count; i++) + Children[i].Order = i; + } + + /// + /// Returns whether the layer meets the requirements to be drawn in the profile editor + /// + /// + public bool MustDraw() + { + // If any of the parents are disabled, this layer must not be drawn + var parent = Parent; + while (parent != null) + { + if (!parent.Enabled) + return false; + parent = parent.Parent; + } + return Enabled && LayerType.MustDraw; + } + + /// + /// Returns every descendant of this layer + /// + /// + public IEnumerable GetLayers() + { + var layers = new List(); + foreach (var layerModel in Children) + { + layers.Add(layerModel); + layers.AddRange(layerModel.GetLayers()); + } + + return layers; + } + + /// + /// Creates a new Keyboard layer with default settings + /// + /// + public static LayerModel CreateLayer() + { + return new LayerModel + { + Name = "New layer", + Enabled = true, + Order = -1, + LayerType = new KeyboardType(), + LayerCondition = new DataModelCondition(), + Properties = new KeyboardPropertiesModel + { + Brush = new SolidColorBrush(ColorHelpers.GetRandomRainbowMediaColor()), + Height = 1, + Width = 1, + X = 0, + Y = 0, + Opacity = 1 + } + }; + } + + public void InsertBefore(LayerModel source) + { + source.Order = Order; + Insert(source); + } + + public void InsertAfter(LayerModel source) + { + source.Order = Order + 1; + Insert(source); + } + + private void Insert(LayerModel source) + { + if (Parent != null) + { + foreach (var child in Parent.Children.OrderBy(c => c.Order)) + { + if (child.Order >= source.Order) + child.Order++; + } + Parent.Children.Add(source); + } + else if (Profile != null) + { + foreach (var layer in Profile.Layers.OrderBy(l => l.Order)) + { + if (layer.Order >= source.Order) + layer.Order++; + } + Profile.Layers.Add(source); + } + } + + public void Replace(LayerModel layer) + { + layer.Order = Order; + layer.Parent = null; + layer.Profile = null; + + if (Parent != null) + { + Parent.Children.Add(layer); + Parent.Children.Remove(this); + } + else if (Profile != null) + { + Profile.Layers.Add(layer); + Profile.Layers.Remove(this); + } + } + + /// + /// Generates a flat list containing all layers that must be rendered on the keyboard, + /// the first mouse layer to be rendered and the first headset layer to be rendered + /// + /// The game data model to base the conditions on + /// Instance of said game data model + /// Whether or not to include mice in the list + /// Whether or not to include headsets in the list + /// + /// A flat list containing all layers that must be rendered + public List GetRenderLayers(IDataModel dataModel, bool includeMice, bool includeHeadsets, + bool ignoreConditions = false) + { + var layers = new List(); + foreach (var layerModel in Children.OrderByDescending(c => c.Order)) + { + if (!layerModel.Enabled || !includeMice && layerModel.LayerType is MouseType || + !includeHeadsets && layerModel.LayerType is HeadsetType) + continue; + + if (!ignoreConditions && !layerModel.ConditionsMet(dataModel)) + continue; + + layers.Add(layerModel); + layers.AddRange(layerModel.GetRenderLayers(dataModel, includeMice, includeHeadsets, ignoreConditions)); + } + + return layers; + } + + #region IChildItem Members + + LayerModel IChildItem.Parent + { + get { return Parent; } + set { Parent = value; } + } + + ProfileModel IChildItem.Parent + { + get { return Profile; } + set { Profile = value; } + } + + #endregion + } } \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs new file mode 100644 index 000000000..6c80bc49f --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Windows.Media; +using Artemis.Utilities.Converters; +using Newtonsoft.Json; + +namespace Artemis.Profiles.Layers.Models +{ + public abstract class LayerPropertiesModel + { + public LayerPropertiesModel(LayerPropertiesModel source = null) + { + if (source == null) + return; + + // Clone the source's properties onto the new properties model (useful when changing property type) + X = source.X; + Y = source.Y; + Width = source.Width; + Height = source.Height; + Contain = source.Contain; + Opacity = source.Opacity; + AnimationSpeed = source.AnimationSpeed; + Conditions = source.Conditions; + DynamicProperties = source.DynamicProperties; + Brush = source.Brush; + } + + private Brush _brush; + + public double X { get; set; } + public double Y { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public bool Contain { get; set; } + public double Opacity { get; set; } + public double AnimationSpeed { get; set; } + public List Conditions { get; set; } = new List(); + public List DynamicProperties { get; set; } = new List(); + + [JsonIgnore] + + public double AnimationProgress { get; set; } + + [JsonConverter(typeof(BrushJsonConverter))] + public Brush Brush + { + get { return _brush; } + set + { + if (value == null) + { + _brush = null; + return; + } + + if (value.IsFrozen) + { + _brush = value; + return; + } + + // Clone the brush off of the UI thread and freeze it + var cloned = value.Dispatcher.Invoke(value.CloneCurrentValue); + cloned.Freeze(); + _brush = cloned; + } + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Models/SimplePropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/SimplePropertiesModel.cs new file mode 100644 index 000000000..463ca0006 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Models/SimplePropertiesModel.cs @@ -0,0 +1,12 @@ +namespace Artemis.Profiles.Layers.Models +{ + /// + /// An empty layer properties model used by layers that don't have their own special properties + /// + public class SimplePropertiesModel : LayerPropertiesModel + { + public SimplePropertiesModel(LayerPropertiesModel properties = null) : base(properties) + { + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs new file mode 100644 index 000000000..08b24cb63 --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Properties; +using Artemis.Utilities; +using Artemis.ViewModels.Profiles.Layers; + +namespace Artemis.Profiles.Layers.Types.Folder +{ + public class FolderType : ILayerType + { + public string Name { get; } = "Folder"; + public bool MustDraw { get; } = false; + + public ImageSource DrawThumbnail(LayerModel layer) + { + var thumbnailRect = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.folder), thumbnailRect); + + var image = new DrawingImage(visual.Drawing); + return image; + } + + public void Draw(LayerModel layer, DrawingContext c) + { + } + + public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false) + { + } + + public void SetupProperties(LayerModel layerModel) + { + if (layerModel.Properties is SimplePropertiesModel) + return; + + layerModel.Properties = new SimplePropertiesModel(layerModel.Properties); + } + + public LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, + List layerAnimations, IDataModel dataModel, LayerModel proposedLayer) + { + if (layerPropertiesViewModel is FolderPropertiesViewModel) + return layerPropertiesViewModel; + return new FolderPropertiesViewModel(proposedLayer, dataModel); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs new file mode 100644 index 000000000..b50921b8b --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Animations; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Properties; +using Artemis.Utilities; +using Artemis.ViewModels.Profiles.Layers; + +namespace Artemis.Profiles.Layers.Types.Headset +{ + public class HeadsetType : ILayerType + { + public string Name { get; } = "Headset"; + public bool MustDraw { get; } = false; + + public ImageSource DrawThumbnail(LayerModel layer) + { + var thumbnailRect = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.headset), thumbnailRect); + + var image = new DrawingImage(visual.Drawing); + return image; + } + + public void Draw(LayerModel layer, DrawingContext c) + { + // If an animation is present, let it handle the drawing + if (layer.LayerAnimation != null && !(layer.LayerAnimation is NoneAnimation)) + { + layer.LayerAnimation.Draw(layer.Properties, layer.AppliedProperties, c); + return; + } + + // Otherwise draw the rectangle with its applied dimensions and brush + var rect = new Rect(layer.AppliedProperties.X * 4, + layer.AppliedProperties.Y * 4, + layer.AppliedProperties.Width * 4, + layer.AppliedProperties.Height * 4); + + c.PushClip(new RectangleGeometry(rect)); + c.DrawRectangle(layer.AppliedProperties.Brush, null, rect); + c.Pop(); + } + + public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false) + { + // Headset layers are always drawn 10*10 (which is 40*40 when scaled up) + layerModel.Properties.Width = 10; + layerModel.Properties.Height = 10; + layerModel.Properties.X = 0; + layerModel.Properties.Y = 0; + layerModel.Properties.Contain = true; + + layerModel.AppliedProperties = new SimplePropertiesModel(layerModel.Properties); + + if (isPreview || dataModel == null) + return; + + // If not previewing, apply dynamic properties according to datamodel + var props = (SimplePropertiesModel)layerModel.AppliedProperties; + foreach (var dynamicProperty in props.DynamicProperties) + dynamicProperty.ApplyProperty(dataModel, layerModel.AppliedProperties); + } + + public void SetupProperties(LayerModel layerModel) + { + if (layerModel.Properties is SimplePropertiesModel) + return; + + layerModel.Properties = new SimplePropertiesModel(layerModel.Properties); + } + + public LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, + List layerAnimations, IDataModel dataModel, LayerModel proposedLayer) + { + if (layerPropertiesViewModel is HeadsetPropertiesViewModel) + return layerPropertiesViewModel; + return new HeadsetPropertiesViewModel(proposedLayer, dataModel, layerAnimations); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesModel.cs new file mode 100644 index 000000000..4ae12f40c --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesModel.cs @@ -0,0 +1,19 @@ +using System.Windows; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.Profiles.Layers.Types.Keyboard +{ + public class KeyboardPropertiesModel : LayerPropertiesModel + { + public KeyboardPropertiesModel(LayerPropertiesModel properties = null) : base(properties) + { + } + + public string GifFile { get; set; } + + public Rect GetRect(int scale = 4) + { + return new Rect(X*scale, Y*scale, Width*scale, Height*scale); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs new file mode 100644 index 000000000..a9b3bdfcf --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Animations; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Utilities; +using Artemis.ViewModels.Profiles.Layers; + + +namespace Artemis.Profiles.Layers.Types.Keyboard +{ + public class KeyboardType : ILayerType + { + public string Name { get; } = "Keyboard"; + public bool MustDraw { get; } = true; + + public ImageSource DrawThumbnail(LayerModel layer) + { + var thumbnailRect = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + { + if (layer.Properties.Brush != null) + { + c.DrawRectangle(layer.Properties.Brush, + new Pen(new SolidColorBrush(Colors.White), 1), + thumbnailRect); + } + } + + var image = new DrawingImage(visual.Drawing); + return image; + } + + public void Draw(LayerModel layer, DrawingContext c) + { + // If an animation is present, let it handle the drawing + if (layer.LayerAnimation != null && !(layer.LayerAnimation is NoneAnimation)) + { + layer.LayerAnimation.Draw(layer.Properties, layer.AppliedProperties, c); + return; + } + + // Otherwise draw the rectangle with its layer.AppliedProperties dimensions and brush + var rect = layer.Properties.Contain + ? new Rect(layer.AppliedProperties.X*4, + layer.AppliedProperties.Y*4, + layer.AppliedProperties.Width*4, + layer.AppliedProperties.Height*4) + : new Rect(layer.Properties.X*4, + layer.Properties.Y*4, + layer.Properties.Width*4, + layer.Properties.Height*4); + + var clip = new Rect(layer.AppliedProperties.X*4, layer.AppliedProperties.Y*4, + layer.AppliedProperties.Width*4, layer.AppliedProperties.Height*4); + + + c.PushClip(new RectangleGeometry(clip)); + c.DrawRectangle(layer.AppliedProperties.Brush, null, rect); + c.Pop(); + } + + public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false) + { + layerModel.AppliedProperties = new KeyboardPropertiesModel(layerModel.Properties); + if (isPreview || dataModel == null) + return; + + // If not previewing, apply dynamic properties according to datamodel + var keyboardProps = (KeyboardPropertiesModel) layerModel.AppliedProperties; + foreach (var dynamicProperty in keyboardProps.DynamicProperties) + dynamicProperty.ApplyProperty(dataModel, layerModel.AppliedProperties); + } + + public void SetupProperties(LayerModel layerModel) + { + if (layerModel.Properties is KeyboardPropertiesModel) + return; + + layerModel.Properties = new KeyboardPropertiesModel(layerModel.Properties); + } + + public LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, + List layerAnimations, IDataModel dataModel, LayerModel proposedLayer) + { + var model = layerPropertiesViewModel as KeyboardPropertiesViewModel; + if (model == null) + return new KeyboardPropertiesViewModel(proposedLayer, dataModel, layerAnimations) + { + IsGif = false + }; + + model.IsGif = false; + return layerPropertiesViewModel; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs b/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs new file mode 100644 index 000000000..e4814e51a --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Properties; +using Artemis.Utilities; +using Artemis.ViewModels.Profiles.Layers; + + +namespace Artemis.Profiles.Layers.Types.KeyboardGif +{ + internal class KeyboardGifType : ILayerType + { + public string Name { get; } = "Keyboard - GIF"; + public bool MustDraw { get; } = true; + + public ImageSource DrawThumbnail(LayerModel layer) + { + var thumbnailRect = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.gif), thumbnailRect); + + var image = new DrawingImage(visual.Drawing); + return image; + } + + public void Draw(LayerModel layer, DrawingContext c) + { + var props = (KeyboardPropertiesModel) layer.Properties; + if (string.IsNullOrEmpty(props.GifFile)) + return; + if (!File.Exists(props.GifFile)) + return; + + // Only reconstruct GifImage if the underlying source has changed + if (layer.GifImage == null) + layer.GifImage = new GifImage(props.GifFile); + if (layer.GifImage.Source != props.GifFile) + layer.GifImage = new GifImage(props.GifFile); + + var rect = new Rect(layer.AppliedProperties.X*4, + layer.AppliedProperties.Y*4, + layer.AppliedProperties.Width*4, + layer.AppliedProperties.Height*4); + + lock (layer.GifImage) + { + var draw = layer.GifImage.GetNextFrame(); + c.DrawImage(ImageUtilities.BitmapToBitmapImage(new Bitmap(draw)), rect); + } + } + + public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false) + { + layerModel.AppliedProperties = new KeyboardPropertiesModel(layerModel.Properties); + if (isPreview) + return; + + // If not previewing, apply dynamic properties according to datamodel + var keyboardProps = (KeyboardPropertiesModel) layerModel.AppliedProperties; + foreach (var dynamicProperty in keyboardProps.DynamicProperties) + dynamicProperty.ApplyProperty(dataModel, layerModel.AppliedProperties); + } + + public void SetupProperties(LayerModel layerModel) + { + if (layerModel.Properties is KeyboardPropertiesModel) + return; + + layerModel.Properties = new KeyboardPropertiesModel(layerModel.Properties); + } + + public LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, + List layerAnimations, IDataModel dataModel, LayerModel proposedLayer) + { + var model = layerPropertiesViewModel as KeyboardPropertiesViewModel; + if (model == null) + return new KeyboardPropertiesViewModel(proposedLayer, dataModel, layerAnimations) + { + IsGif = true + }; + + model.IsGif = true; + return layerPropertiesViewModel; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs new file mode 100644 index 000000000..1ad7d68ba --- /dev/null +++ b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Animations; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Properties; +using Artemis.Utilities; +using Artemis.ViewModels.Profiles.Layers; + + +namespace Artemis.Profiles.Layers.Types.Mouse +{ + public class MouseType : ILayerType + { + public string Name { get; } = "Mouse"; + public bool MustDraw { get; } = false; + + public ImageSource DrawThumbnail(LayerModel layer) + { + var thumbnailRect = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.mouse), thumbnailRect); + + var image = new DrawingImage(visual.Drawing); + return image; + } + + public void Draw(LayerModel layer, DrawingContext c) + { + // If an animation is present, let it handle the drawing + if (layer.LayerAnimation != null && !(layer.LayerAnimation is NoneAnimation)) + { + layer.LayerAnimation.Draw(layer.Properties, layer.AppliedProperties, c); + return; + } + + // Otherwise draw the rectangle with its applied dimensions and brush + var rect = new Rect(layer.AppliedProperties.X * 4, + layer.AppliedProperties.Y * 4, + layer.AppliedProperties.Width * 4, + layer.AppliedProperties.Height * 4); + + c.PushClip(new RectangleGeometry(rect)); + c.DrawRectangle(layer.AppliedProperties.Brush, null, rect); + c.Pop(); + } + + public void Update(LayerModel layerModel, IDataModel dataModel, bool isPreview = false) + { + // Mouse layers are always drawn 10*10 (which is 40*40 when scaled up) + layerModel.Properties.Width = 10; + layerModel.Properties.Height = 10; + layerModel.Properties.X = 0; + layerModel.Properties.Y = 0; + layerModel.Properties.Contain = true; + + layerModel.AppliedProperties = new SimplePropertiesModel(layerModel.Properties); + + if (isPreview || dataModel == null) + return; + + // If not previewing, apply dynamic properties according to datamodel + var props = (SimplePropertiesModel)layerModel.AppliedProperties; + foreach (var dynamicProperty in props.DynamicProperties) + dynamicProperty.ApplyProperty(dataModel, layerModel.AppliedProperties); + } + + public void SetupProperties(LayerModel layerModel) + { + if (layerModel.Properties is SimplePropertiesModel) + return; + + layerModel.Properties = new SimplePropertiesModel(layerModel.Properties); + } + + public LayerPropertiesViewModel SetupViewModel(LayerPropertiesViewModel layerPropertiesViewModel, + List layerAnimations, IDataModel dataModel, LayerModel proposedLayer) + { + if (layerPropertiesViewModel is MousePropertiesViewModel) + return layerPropertiesViewModel; + return new MousePropertiesViewModel(proposedLayer, dataModel, layerAnimations); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/ProfileModel.cs b/Artemis/Artemis/Profiles/ProfileModel.cs similarity index 61% rename from Artemis/Artemis/Models/Profiles/ProfileModel.cs rename to Artemis/Artemis/Profiles/ProfileModel.cs index 12c2ed998..66f2962d9 100644 --- a/Artemis/Artemis/Models/Profiles/ProfileModel.cs +++ b/Artemis/Artemis/Profiles/ProfileModel.cs @@ -1,223 +1,201 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Windows; -using System.Windows.Media; -using System.Xml.Serialization; -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; -using Artemis.Utilities.ParentChild; -using Brush = System.Windows.Media.Brush; -using Color = System.Windows.Media.Color; -using Point = System.Windows.Point; -using Size = System.Windows.Size; - -namespace Artemis.Models.Profiles -{ - public class ProfileModel - { - public ProfileModel() - { - Layers = new ChildItemCollection(this); - DrawingVisual = new DrawingVisual(); - } - - public ChildItemCollection Layers { get; } - - public string Name { get; set; } - public bool IsDefault { get; set; } - public string KeyboardSlug { get; set; } - public string GameName { get; set; } - - [XmlIgnore] - public DrawingVisual DrawingVisual { get; set; } - - protected bool Equals(ProfileModel other) - { - return string.Equals(Name, other.Name) && - string.Equals(KeyboardSlug, other.KeyboardSlug) && - string.Equals(GameName, other.GameName); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((ProfileModel)obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Name?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (KeyboardSlug?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (GameName?.GetHashCode() ?? 0); - return hashCode; - } - } - - public void FixOrder() - { - Layers.Sort(l => l.Order); - for (var i = 0; i < Layers.Count; i++) - Layers[i].Order = i; - } - - public void DrawProfile(Graphics keyboard, Rect keyboardRect, IDataModel dataModel, bool preview, - bool updateAnimations) - { - var visual = new DrawingVisual(); - using (var c = visual.RenderOpen()) - { - // Setup the DrawingVisual's size - c.PushClip(new RectangleGeometry(keyboardRect)); - c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); - - // Draw the layers - foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) - layerModel.Draw(dataModel, c, preview, updateAnimations); - - // Remove the clip - c.Pop(); - } - - using (Bitmap bmp = ImageUtilities.DrawinVisualToBitmap(visual, keyboardRect)) - keyboard.DrawImage(bmp, new PointF(0, 0)); - } - - public Brush GenerateBrush(IDataModel dataModel, LayerType type, bool preview, bool updateAnimations) - { - Brush result = null; - // Draw the layers - foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) - { - var generated = layerModel.GenerateBrush(type, dataModel, preview, updateAnimations); - if (generated != null) - result = generated; - } - - return result; - } - - /// - /// Gives all the layers and their children in a flat list - /// - public List GetLayers() - { - var layers = new List(); - foreach (var layerModel in Layers) - { - layers.Add(layerModel); - layers.AddRange(layerModel.GetLayers()); - } - - return layers; - } - - /// - /// Generates a flat list containing all layers that must be rendered on the keyboard, - /// the first mouse layer to be rendered and the first headset layer to be rendered - /// - /// The game data model to base the conditions on - /// Instance of said game data model - /// Whether or not to include mice in the list - /// Whether or not to include headsets in the list - /// - /// A flat list containing all layers that must be rendered - public List GetRenderLayers(IDataModel dataModel, bool includeMice, bool includeHeadsets, - bool ignoreConditions = false) - { - var layers = new List(); - foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) - { - if (!layerModel.Enabled || - !includeMice && layerModel.LayerType == LayerType.Mouse || - !includeHeadsets && layerModel.LayerType == LayerType.Headset) - continue; - - if (!ignoreConditions) - { - if (!layerModel.ConditionsMet(dataModel)) - continue; - } - - layers.Add(layerModel); - layers.AddRange(layerModel.GetRenderLayers(dataModel, includeMice, includeHeadsets, ignoreConditions)); - } - - return layers; - } - - /// - /// Looks at all the layers wthin the profile and makes sure they are within boundaries of the given rectangle - /// - /// - public void FixBoundaries(Rect keyboardRectangle) - { - foreach (var layer in GetLayers()) - { - if (layer.LayerType != LayerType.Keyboard && layer.LayerType != LayerType.KeyboardGif) - continue; - - var props = (KeyboardPropertiesModel)layer.Properties; - var layerRect = new Rect(new Point(props.X, props.Y), new Size(props.Width, props.Height)); - if (keyboardRectangle.Contains(layerRect)) - continue; - - props.X = 0; - props.Y = 0; - layer.Properties = props; - } - } - - /// - /// Draw all the provided layers of type Keyboard and KeyboardGif - /// - /// The graphics to draw on - /// The layers to render - /// The data model to base the layer's properties on - /// A rectangle matching the current keyboard's size on a scale of 4, used for clipping - /// Indicates wheter the layer is drawn as a preview, ignoring dynamic properties - /// Wheter or not to update the layer's animations - internal void DrawProfile(Graphics keyboard, List renderLayers, IDataModel dataModel, Rect keyboardRect, - bool preview, - bool updateAnimations) - { - var visual = new DrawingVisual(); - using (var c = visual.RenderOpen()) - { - // Setup the DrawingVisual's size - c.PushClip(new RectangleGeometry(keyboardRect)); - c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); - - // Draw the layers - foreach (var layerModel in renderLayers - .Where(l => l.LayerType == LayerType.Keyboard || - l.LayerType == LayerType.KeyboardGif)) - { - layerModel.Draw(dataModel, c, preview, updateAnimations); - } - - // Remove the clip - c.Pop(); - } - - using (Bitmap bmp = ImageUtilities.DrawinVisualToBitmap(visual, keyboardRect)) - keyboard.DrawImage(bmp, new PointF(0, 0)); - } - - /// - /// Generates a brush out of the given layer, for usage with mice and headsets - /// - /// The layer to base the brush on - /// The game data model to base the layer's properties on - /// The generated brush - public Brush GenerateBrush(LayerModel layerModel, IDataModel dataModel) - { - return layerModel?.Properties.GetAppliedProperties(dataModel).Brush; - } - } +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Headset; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Profiles.Layers.Types.Mouse; +using Artemis.Utilities; +using Artemis.Utilities.ParentChild; +using Newtonsoft.Json; +using Color = System.Windows.Media.Color; +using Point = System.Windows.Point; +using Size = System.Windows.Size; + +namespace Artemis.Profiles +{ + public class ProfileModel + { + public ProfileModel() + { + Layers = new ChildItemCollection(this); + DrawingVisual = new DrawingVisual(); + } + + public ChildItemCollection Layers { get; } + + public string Name { get; set; } + public bool IsDefault { get; set; } + public string KeyboardSlug { get; set; } + public string GameName { get; set; } + + [JsonIgnore] + + public DrawingVisual DrawingVisual { get; set; } + + public void FixOrder() + { + Layers.Sort(l => l.Order); + for (var i = 0; i < Layers.Count; i++) + Layers[i].Order = i; + } + + public void DrawLayers(Graphics keyboard, Rect keyboardRect, IDataModel dataModel, bool preview, + bool updateAnimations) + { + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + { + // Setup the DrawingVisual's size + c.PushClip(new RectangleGeometry(keyboardRect)); + c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); + + // Draw the layers + foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) + { + layerModel.Update(dataModel, preview, updateAnimations); + layerModel.Draw(dataModel, c, preview, updateAnimations); + } + // Remove the clip + c.Pop(); + } + + using (var bmp = ImageUtilities.DrawinVisualToBitmap(visual, keyboardRect)) + keyboard.DrawImage(bmp, new PointF(0, 0)); + } + + /// + /// Gives all the layers and their children in a flat list + /// + public List GetLayers() + { + var layers = new List(); + foreach (var layerModel in Layers) + { + layers.Add(layerModel); + layers.AddRange(layerModel.GetLayers()); + } + + return layers; + } + + /// + /// Generates a flat list containing all layers that must be rendered on the keyboard, + /// the first mouse layer to be rendered and the first headset layer to be rendered + /// + /// The game data model to base the conditions on + /// Instance of said game data model + /// Whether or not to include mice in the list + /// Whether or not to include headsets in the list + /// + /// A flat list containing all layers that must be rendered + public List GetRenderLayers(IDataModel dataModel, bool includeMice, bool includeHeadsets, + bool ignoreConditions = false) + { + var layers = new List(); + foreach (var layerModel in Layers.OrderByDescending(l => l.Order)) + { + if (!layerModel.Enabled || !includeMice && layerModel.LayerType is MouseType || + !includeHeadsets && layerModel.LayerType is HeadsetType) + continue; + + if (!ignoreConditions && !layerModel.ConditionsMet(dataModel)) + continue; + + layers.Add(layerModel); + layers.AddRange(layerModel.GetRenderLayers(dataModel, includeMice, includeHeadsets, ignoreConditions)); + } + + return layers; + } + + /// + /// Looks at all the layers wthin the profile and makes sure they are within boundaries of the given rectangle + /// + /// + public void FixBoundaries(Rect keyboardRectangle) + { + foreach (var layer in GetLayers()) + { + if (!layer.LayerType.MustDraw) + continue; + + var props = (KeyboardPropertiesModel) layer.Properties; + var layerRect = new Rect(new Point(props.X, props.Y), new Size(props.Width, props.Height)); + if (keyboardRectangle.Contains(layerRect)) + continue; + + props.X = 0; + props.Y = 0; + layer.Properties = props; + } + } + + /// + /// Draw all the given layers on the given rect + /// + /// The graphics to draw on + /// The layers to render + /// The data model to base the layer's properties on + /// A rectangle matching the current keyboard's size on a scale of 4, used for clipping + /// Indicates wheter the layer is drawn as a preview, ignoring dynamic properties + /// Wheter or not to update the layer's animations + internal void DrawLayers(Graphics g, IEnumerable renderLayers, IDataModel dataModel, Rect rect, + bool preview, bool updateAnimations) + { + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + { + // Setup the DrawingVisual's size + c.PushClip(new RectangleGeometry(rect)); + c.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, rect); + + // Draw the layers + foreach (var layerModel in renderLayers) + { + layerModel.Update(dataModel, preview, updateAnimations); + layerModel.Draw(dataModel, c, preview, updateAnimations); + } + + // Remove the clip + c.Pop(); + } + + using (var bmp = ImageUtilities.DrawinVisualToBitmap(visual, rect)) + g.DrawImage(bmp, new PointF(0, 0)); + } + + #region Compare + + protected bool Equals(ProfileModel other) + { + return string.Equals(Name, other.Name) && + string.Equals(KeyboardSlug, other.KeyboardSlug) && + string.Equals(GameName, other.GameName); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ProfileModel) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Name?.GetHashCode() ?? 0; + hashCode = (hashCode*397) ^ (KeyboardSlug?.GetHashCode() ?? 0); + hashCode = (hashCode*397) ^ (GameName?.GetHashCode() ?? 0); + return hashCode; + } + } + + #endregion + } } \ No newline at end of file diff --git a/Artemis/Artemis/Resources/Witcher3/Witcher3Artemis.zip b/Artemis/Artemis/Resources/Witcher3/Witcher3Artemis.zip index 8269c99d9..002796ac6 100644 Binary files a/Artemis/Artemis/Resources/Witcher3/Witcher3Artemis.zip and b/Artemis/Artemis/Resources/Witcher3/Witcher3Artemis.zip differ diff --git a/Artemis/Artemis/Settings/General.settings b/Artemis/Artemis/Settings/General.settings index 521a80d33..326ce89c3 100644 --- a/Artemis/Artemis/Settings/General.settings +++ b/Artemis/Artemis/Settings/General.settings @@ -1,5 +1,7 @@  - + + diff --git a/Artemis/Artemis/Settings/GeneralSettings.cs b/Artemis/Artemis/Settings/GeneralSettings.cs index 296ed2f52..573f4e20c 100644 --- a/Artemis/Artemis/Settings/GeneralSettings.cs +++ b/Artemis/Artemis/Settings/GeneralSettings.cs @@ -5,7 +5,6 @@ using System.Runtime.InteropServices.ComTypes; using System.Windows; using Artemis.Utilities; using MahApps.Metro; -using NLog; namespace Artemis.Settings { diff --git a/Artemis/Artemis/Styles/ColorBox.xaml b/Artemis/Artemis/Styles/ColorBox.xaml index 40a64f1fc..bbd797280 100644 --- a/Artemis/Artemis/Styles/ColorBox.xaml +++ b/Artemis/Artemis/Styles/ColorBox.xaml @@ -84,7 +84,7 @@ + BorderBrush="{TemplateBinding BorderBrush}" /> @@ -491,7 +491,8 @@ SelectedGradient="{Binding}" Margin="0,0,0,2"> - + diff --git a/Artemis/Artemis/Utilities/ColorHelpers.cs b/Artemis/Artemis/Utilities/ColorHelpers.cs index fb96440e3..0a28d5a77 100644 --- a/Artemis/Artemis/Utilities/ColorHelpers.cs +++ b/Artemis/Artemis/Utilities/ColorHelpers.cs @@ -33,7 +33,7 @@ namespace Artemis.Utilities { var colors = new List(); for (var i = 0; i < 3; i++) - colors.Add((byte)_rand.Next(0, 256)); + colors.Add((byte) _rand.Next(0, 256)); var highest = colors.Max(); var lowest = colors.Min(); diff --git a/Artemis/Artemis/Utilities/Converters/JsonConverters.cs b/Artemis/Artemis/Utilities/Converters/JsonConverters.cs new file mode 100644 index 000000000..cfc95ae1f --- /dev/null +++ b/Artemis/Artemis/Utilities/Converters/JsonConverters.cs @@ -0,0 +1,43 @@ +using System; +using System.Windows.Markup; +using System.Windows.Media; +using System.Xml; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Artemis.Utilities.Converters +{ + /// + /// Stores a brush by temporarily serializing it to XAML because Json.NET has trouble + /// saving it as JSON + /// + public class BrushJsonConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + // Turn the brush into an XML node + var doc = new XmlDocument(); + doc.LoadXml(XamlWriter.Save(value)); + + // Serialize the XML node as JSON + var jo = JObject.Parse(JsonConvert.SerializeXmlNode(doc.DocumentElement)); + jo.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + // Load JObject from stream + var jObject = JObject.Load(reader); + + // Seriaze the JSON node to XML + var xml = JsonConvert.DeserializeXmlNode(jObject.ToString()); + return XamlReader.Parse(xml.InnerXml); + } + + public override bool CanConvert(Type objectType) + { + return typeof(Brush).IsAssignableFrom(objectType); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/ValueConverters.cs b/Artemis/Artemis/Utilities/Converters/ValueConverters.cs similarity index 78% rename from Artemis/Artemis/Utilities/ValueConverters.cs rename to Artemis/Artemis/Utilities/Converters/ValueConverters.cs index bd60915d0..7af474bfb 100644 --- a/Artemis/Artemis/Utilities/ValueConverters.cs +++ b/Artemis/Artemis/Utilities/Converters/ValueConverters.cs @@ -1,66 +1,79 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Globalization; -using System.Linq; -using System.Windows.Data; -using Artemis.Models.Profiles; -using Artemis.Utilities.ParentChild; - -namespace Artemis.Utilities -{ - /// - /// Fredrik Hedblad - http://stackoverflow.com/a/3987099/5015269 - /// - public class EnumDescriptionConverter : IValueConverter - { - object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var myEnum = (Enum) value; - var description = GetEnumDescription(myEnum); - return description; - } - - object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return string.Empty; - } - - private string GetEnumDescription(Enum enumObj) - { - var fieldInfo = enumObj.GetType().GetField(enumObj.ToString()); - - var attribArray = fieldInfo.GetCustomAttributes(false); - - if (attribArray.Length == 0) - { - return enumObj.ToString(); - } - var attrib = attribArray[0] as DescriptionAttribute; - return attrib?.Description; - } - } - - public class LayerOrderConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - IList collection; - if (value is ChildItemCollection) - collection = ((ChildItemCollection) value).ToList(); - else - collection = (IList) value; - - var view = new ListCollectionView(collection); - var sort = new SortDescription(parameter.ToString(), ListSortDirection.Ascending); - view.SortDescriptions.Add(sort); - - return view; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return null; - } - } +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Windows.Data; +using Artemis.Profiles.Layers.Models; +using Artemis.Utilities.ParentChild; + +namespace Artemis.Utilities.Converters +{ + /// + /// Fredrik Hedblad - http://stackoverflow.com/a/3987099/5015269 + /// + public class EnumDescriptionConverter : IValueConverter + { + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var myEnum = (Enum) value; + var description = GetEnumDescription(myEnum); + return description; + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return string.Empty; + } + + private string GetEnumDescription(Enum enumObj) + { + var fieldInfo = enumObj.GetType().GetField(enumObj.ToString()); + + var attribArray = fieldInfo.GetCustomAttributes(false); + + if (attribArray.Length == 0) + { + return enumObj.ToString(); + } + var attrib = attribArray[0] as DescriptionAttribute; + return attrib?.Description; + } + } + + public class LayerOrderConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + IList collection; + if (value is ChildItemCollection) + collection = ((ChildItemCollection) value).ToList(); + else + collection = (IList) value; + + var view = new ListCollectionView(collection); + var sort = new SortDescription(parameter.ToString(), ListSortDirection.Ascending); + view.SortDescriptions.Add(sort); + + return view; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + } + + public class MilliSecondTimespanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null ? 0.0 : ((TimeSpan)value).TotalMilliseconds; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null ? new TimeSpan() : TimeSpan.FromMilliseconds((double)value); + } + } } \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/DataReaders/MmfReader.cs b/Artemis/Artemis/Utilities/DataReaders/MmfReader.cs index 744b9576f..51c0aaf57 100644 --- a/Artemis/Artemis/Utilities/DataReaders/MmfReader.cs +++ b/Artemis/Artemis/Utilities/DataReaders/MmfReader.cs @@ -4,6 +4,7 @@ using System.IO.MemoryMappedFiles; using System.Linq; using System.Text; using System.Windows.Media; +using Ninject.Extensions.Logging; namespace Artemis.Utilities.DataReaders { @@ -12,10 +13,11 @@ namespace Artemis.Utilities.DataReaders /// public class MmfReader { - private DateTime _lastFailure; + private readonly ILogger _logger; - public MmfReader(string mmfName) + public MmfReader(string mmfName, ILogger logger) { + _logger = logger; MmfName = mmfName; } @@ -54,8 +56,9 @@ namespace Artemis.Utilities.DataReaders } return colors; } - catch (FormatException) + catch (FormatException e) { + _logger.Trace(e, "Failed to parse to color array"); return null; } } @@ -67,10 +70,6 @@ namespace Artemis.Utilities.DataReaders /// private string ReadMmf(string fileName) { - // Don't read the file within one second after failing - //if (DateTime.Now - _lastFailure > new TimeSpan(0, 0, 1)) - // return null; - try { using (var mmf = MemoryMappedFile.OpenExisting(fileName)) @@ -85,9 +84,9 @@ namespace Artemis.Utilities.DataReaders } } } - catch (FileNotFoundException) + catch (FileNotFoundException e) { - _lastFailure = DateTime.Now; + _logger.Trace(e, "Failed to read mff"); return null; //ignored } diff --git a/Artemis/Artemis/Utilities/GeneralHelpers.cs b/Artemis/Artemis/Utilities/GeneralHelpers.cs index 8dc160b6e..3eb2c5ac2 100644 --- a/Artemis/Artemis/Utilities/GeneralHelpers.cs +++ b/Artemis/Artemis/Utilities/GeneralHelpers.cs @@ -5,8 +5,8 @@ using System.IO; using System.Reflection; using System.Text.RegularExpressions; using System.Windows; -using System.Xml.Serialization; using Microsoft.Win32; +using Newtonsoft.Json; using static System.String; namespace Artemis.Utilities @@ -39,7 +39,7 @@ namespace Artemis.Utilities } /// - /// Perform a deep Copy of the object. + /// Perform a deep Copy of the object, using Json as a serialisation method. /// /// The type of object being copied. /// The object instance to copy. @@ -50,35 +50,20 @@ namespace Artemis.Utilities if (ReferenceEquals(source, null)) return default(T); - var serializer = new XmlSerializer(typeof(T)); - Stream stream = new MemoryStream(); - using (stream) + var deserializeSettings = new JsonSerializerSettings { - serializer.Serialize(stream, source); - stream.Seek(0, SeekOrigin.Begin); - return (T) serializer.Deserialize(stream); - } - } - - public static string Serialize(T source) - { - // Don't serialize a null object, simply return the default for that object - if (ReferenceEquals(source, null)) - return null; - - var serializer = new XmlSerializer(typeof(T)); - var stream = new StringWriter(); - using (stream) - { - serializer.Serialize(stream, source); - return stream.ToString(); - } + ObjectCreationHandling = ObjectCreationHandling.Replace, + TypeNameHandling = TypeNameHandling.Auto + }; + return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), source.GetType(), + deserializeSettings); } public static object GetPropertyValue(object o, string path) { var propertyNames = path.Split('.'); - var value = o.GetType().GetProperty(propertyNames[0]).GetValue(o, null); + var prop = o.GetType().GetProperty(propertyNames[0]); + var value = prop.GetValue(o, null); if (propertyNames.Length == 1 || value == null) return value; diff --git a/Artemis/Artemis/Utilities/GifImage.cs b/Artemis/Artemis/Utilities/GifImage.cs index bf06ea040..085e51165 100644 --- a/Artemis/Artemis/Utilities/GifImage.cs +++ b/Artemis/Artemis/Utilities/GifImage.cs @@ -8,9 +8,7 @@ namespace Artemis.Utilities { private readonly int _delay; private readonly FrameDimension _dimension; - private readonly int _frameCount; private readonly Image _gifImage; - private int _currentFrame = -1; private DateTime _lastRequest; private int _step = 1; @@ -19,7 +17,7 @@ namespace Artemis.Utilities _lastRequest = DateTime.Now; _gifImage = Image.FromFile(path); //initialize _dimension = new FrameDimension(_gifImage.FrameDimensionsList[0]); //gets the GUID - _frameCount = _gifImage.GetFrameCount(_dimension); //total frames in the animation + FrameCount = _gifImage.GetFrameCount(_dimension); //total frames in the animation Source = path; @@ -27,7 +25,20 @@ namespace Artemis.Utilities _delay = (item.Value[0] + item.Value[1]*256)*10; // Time is in 1/100th of a second } - public string Source { get; set; } + /// + /// Gets the path the GifImage is based on + /// + public string Source { get; private set; } + + /// + /// Gets or sets the current frame, set to -1 to reset + /// + public int CurrentFrame { get; set; } = -1; + + /// + /// Gets the total amount of frames in the GIF + /// + public int FrameCount { get; } /// /// Whether the gif should play backwards when it reaches the end @@ -39,23 +50,23 @@ namespace Artemis.Utilities // Only pass the next frame if the proper amount of time has passed if ((DateTime.Now - _lastRequest).Milliseconds > _delay) { - _currentFrame += _step; + CurrentFrame += _step; _lastRequest = DateTime.Now; } //if the animation reaches a boundary... - if (_currentFrame < _frameCount && _currentFrame >= 1) - return GetFrame(_currentFrame); + if (CurrentFrame < FrameCount && CurrentFrame >= 1) + return GetFrame(CurrentFrame); if (ReverseAtEnd) { _step *= -1; //...reverse the count - _currentFrame += _step; //apply it + CurrentFrame += _step; //apply it } else - _currentFrame = 0; //...or start over + CurrentFrame = 0; //...or start over - return GetFrame(_currentFrame); + return GetFrame(CurrentFrame); } public Image GetFrame(int index) diff --git a/Artemis/Artemis/Utilities/ImageUtilities.cs b/Artemis/Artemis/Utilities/ImageUtilities.cs index 57203e821..c45875482 100644 --- a/Artemis/Artemis/Utilities/ImageUtilities.cs +++ b/Artemis/Artemis/Utilities/ImageUtilities.cs @@ -70,5 +70,24 @@ namespace Artemis.Utilities } return bitmap; } + + /// + /// Loads the BowIcon from resources and colors it according to the current theme + /// + /// + public static RenderTargetBitmap GenerateWindowIcon() + { + var iconImage = new System.Windows.Controls.Image + { + Source = (DrawingImage) Application.Current.MainWindow.Resources["BowIcon"], + Stretch = Stretch.Uniform, + Margin = new Thickness(20) + }; + + iconImage.Arrange(new Rect(0, 0, 100, 100)); + var bitmap = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32); + bitmap.Render(iconImage); + return bitmap; + } } } \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs b/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs deleted file mode 100644 index c4430b174..000000000 --- a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Artemis.Models.Profiles.Properties; - -namespace Artemis.Utilities.Layers -{ - public static class AnimationUpdater - { - public static void UpdateAnimation(KeyboardPropertiesModel properties, bool updateAnimations) - { - const int scale = 4; - var progress = properties.AnimationProgress; - - switch (properties.Animation) - { - case LayerAnimation.SlideRight: - case LayerAnimation.SlideLeft: - if (progress + properties.AnimationSpeed * 2 >= properties.Width*scale) - progress = 0; - progress = progress + properties.AnimationSpeed*2; - break; - case LayerAnimation.SlideDown: - case LayerAnimation.SlideUp: - if (progress + properties.AnimationSpeed * 2 >= properties.Height*scale) - progress = 0; - progress = progress + properties.AnimationSpeed*2; - break; - case LayerAnimation.Pulse: - if (progress > 2) - progress = 0; - progress = progress + properties.AnimationSpeed/2; - break; - case LayerAnimation.Grow: - if (progress > 10) - progress = 0; - progress = progress + properties.AnimationSpeed/2.5; - break; - default: - progress = progress + properties.AnimationSpeed*2; - break; - } - - // If not previewing, store the animation progress in the actual model for the next frame - if (updateAnimations) - properties.AnimationProgress = progress; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/Layers/Drawer.cs b/Artemis/Artemis/Utilities/Layers/Drawer.cs deleted file mode 100644 index 1fe261602..000000000 --- a/Artemis/Artemis/Utilities/Layers/Drawer.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Drawing; -using System.IO; -using System.Windows; -using System.Windows.Media; -using Artemis.Models.Profiles; -using Artemis.Models.Profiles.Properties; -using Artemis.Properties; -using Pen = System.Windows.Media.Pen; -using Point = System.Windows.Point; -using Size = System.Windows.Size; - -namespace Artemis.Utilities.Layers -{ - public static class Drawer - { - public static void Draw(DrawingContext c, KeyboardPropertiesModel props, AppliedProperties applied) - { - if (applied.Brush == null) - return; - - const int scale = 4; - // Set up variables for this frame - 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) - { - s1 = new Rect(new Point(rect.X + props.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) - { - s1 = new Rect(new Point(rect.X - props.AnimationProgress, rect.Y), - new Size(rect.Width + 0.05, rect.Height)); - s2 = new Rect(new Point(s1.X + rect.Width, rect.Y), new Size(rect.Width, rect.Height)); - } - if (props.Animation == LayerAnimation.SlideDown) - { - s1 = new Rect(new Point(rect.X, rect.Y + props.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) - { - s1 = new Rect(new Point(rect.X, rect.Y - props.AnimationProgress), new Size(rect.Width, rect.Height)); - s2 = new Rect(new Point(s1.X, s1.Y + rect.Height), new Size(rect.Width, rect.Height)); - } - - var clip = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, applied.Height*scale); - DrawRectangle(c, props, applied, clip, rect, s1, s2); - } - - private static void DrawRectangle(DrawingContext c, KeyboardPropertiesModel props, AppliedProperties applied, - Rect clip, Rect rectangle, Rect slide1, Rect slide2) - { - // Apply the pulse animation - if (props.Animation == LayerAnimation.Pulse) - applied.Brush.Opacity = (Math.Sin(props.AnimationProgress*Math.PI) + 1)*(props.Opacity/2); - else - applied.Brush.Opacity = props.Opacity; - - if (props.Animation == LayerAnimation.Grow) - { - // Take an offset of 4 to allow layers to slightly leave their bounds - var progress = (6.0 - props.AnimationProgress)*10.0; - if (progress < 0) - { - applied.Brush.Opacity = 1 + 0.025*progress; - if (applied.Brush.Opacity < 0) - applied.Brush.Opacity = 0; - if (applied.Brush.Opacity > 1) - applied.Brush.Opacity = 1; - } - rectangle.Inflate(-rectangle.Width/100.0*progress, -rectangle.Height/100.0*progress); - clip.Inflate(-clip.Width/100.0*progress, -clip.Height/100.0*progress); - } - - c.PushClip(new RectangleGeometry(clip)); - // Most animation types can be drawn regularly - if (props.Animation == LayerAnimation.None || - props.Animation == LayerAnimation.Grow || - props.Animation == LayerAnimation.Pulse) - { - c.DrawRectangle(applied.Brush, null, rectangle); - } - // Sliding animations however, require offsetting two rects - else - { - c.PushClip(new RectangleGeometry(clip)); - c.DrawRectangle(applied.Brush, null, slide1); - c.DrawRectangle(applied.Brush, null, slide2); - c.Pop(); - } - c.Pop(); - } - - public static GifImage DrawGif(DrawingContext c, KeyboardPropertiesModel props, AppliedProperties applied, - GifImage gifImage) - { - if (string.IsNullOrEmpty(props.GifFile)) - return null; - if (!File.Exists(props.GifFile)) - return null; - - const int scale = 4; - - // Only reconstruct GifImage if the underlying source has changed - if (gifImage == null) - gifImage = new GifImage(props.GifFile); - if (gifImage.Source != props.GifFile) - gifImage = new GifImage(props.GifFile); - - var gifRect = new Rect(applied.X*scale, applied.Y*scale, applied.Width*scale, - applied.Height*scale); - - lock (gifImage) - { - var draw = gifImage.GetNextFrame(); - c.DrawImage(ImageUtilities.BitmapToBitmapImage(new Bitmap(draw)), gifRect); - } - - return gifImage; - } - - public static void UpdateMouse(LayerPropertiesModel properties) - { - } - - public static void UpdateHeadset(LayerPropertiesModel properties) - { - } - - public static ImageSource DrawThumbnail(LayerModel layerModel) - { - var thumbnailRect = new Rect(0, 0, 18, 18); - var visual = new DrawingVisual(); - using (var c = visual.RenderOpen()) - { - // Draw the appropiate icon or draw the brush - if (layerModel.LayerType == LayerType.Folder) - c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.folder), thumbnailRect); - else if (layerModel.LayerType == LayerType.Headset) - c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.headset), thumbnailRect); - else if (layerModel.LayerType == LayerType.Mouse) - c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.mouse), thumbnailRect); - else if (layerModel.LayerType == LayerType.KeyboardGif) - c.DrawImage(ImageUtilities.BitmapToBitmapImage(Resources.gif), thumbnailRect); - else if (layerModel.LayerType == LayerType.Keyboard && layerModel.Properties.Brush != null) - c.DrawRectangle(layerModel.Properties.Brush, new Pen(new SolidColorBrush(Colors.White), 1), - thumbnailRect); - } - - var image = new DrawingImage(visual.Drawing); - return image; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/StickyValue.cs b/Artemis/Artemis/Utilities/StickyValue.cs index a9ad4c1fd..7df32ae79 100644 --- a/Artemis/Artemis/Utilities/StickyValue.cs +++ b/Artemis/Artemis/Utilities/StickyValue.cs @@ -48,14 +48,14 @@ namespace Artemis.Utilities { if (_waitTime < _stickyTime) { - Thread.Sleep(100); + Thread.Sleep(10); continue; } while (_waitTime > 0) { - Thread.Sleep(50); - _waitTime -= 50; + Thread.Sleep(10); + _waitTime -= 10; } _value = _toStick; } diff --git a/Artemis/Artemis/ViewModels/DebugViewModel.cs b/Artemis/Artemis/ViewModels/DebugViewModel.cs new file mode 100644 index 000000000..95e74588e --- /dev/null +++ b/Artemis/Artemis/ViewModels/DebugViewModel.cs @@ -0,0 +1,58 @@ +using System.Windows; +using System.Windows.Media; +using Artemis.Events; +using Caliburn.Micro; + +namespace Artemis.ViewModels +{ + public class DebugViewModel : Screen, IHandle + { + private readonly IEventAggregator _events; + private DrawingImage _razerDisplay; + + public DebugViewModel(IEventAggregator events) + { + _events = events; + } + + public DrawingImage RazerDisplay + { + get { return _razerDisplay; } + set + { + if (Equals(value, _razerDisplay)) return; + _razerDisplay = value; + NotifyOfPropertyChange(() => RazerDisplay); + } + } + + public void Handle(RazerColorArrayChanged message) + { + var visual = new DrawingVisual(); + using (var dc = visual.RenderOpen()) + { + dc.PushClip(new RectangleGeometry(new Rect(0, 0, 22, 6))); + for (var y = 0; y < 6; y++) + { + for (var x = 0; x < 22; x++) + dc.DrawRectangle(new SolidColorBrush(message.Colors[y, x]), null, new Rect(x, y, 1, 1)); + } + } + var drawnDisplay = new DrawingImage(visual.Drawing); + drawnDisplay.Freeze(); + RazerDisplay = drawnDisplay; + } + + protected override void OnActivate() + { + _events.Subscribe(this); + base.OnActivate(); + } + + protected override void OnDeactivate(bool close) + { + _events.Unsubscribe(this); + base.OnDeactivate(close); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs index 2d927fde8..946cba921 100644 --- a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs +++ b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs @@ -1,9 +1,11 @@ using System.ComponentModel; using System.Diagnostics; +using System.Dynamic; using System.Linq; using Artemis.Events; using Artemis.Managers; using Artemis.Settings; +using Artemis.Utilities; using Caliburn.Micro; using MahApps.Metro.Controls; using NLog; @@ -14,15 +16,18 @@ namespace Artemis.ViewModels.Flyouts public sealed class FlyoutSettingsViewModel : FlyoutBaseViewModel, IHandle, IHandle { + private readonly DebugViewModel _debugViewModel; private readonly ILogger _logger; private string _activeEffectName; private bool _enableDebug; private GeneralSettings _generalSettings; private string _selectedKeyboardProvider; - public FlyoutSettingsViewModel(MainManager mainManager, IEventAggregator events, ILogger logger) + public FlyoutSettingsViewModel(MainManager mainManager, IEventAggregator events, ILogger logger, + DebugViewModel debugViewModel) { _logger = logger; + _debugViewModel = debugViewModel; MainManager = mainManager; Header = "Settings"; @@ -178,6 +183,18 @@ namespace Artemis.ViewModels.Flyouts MainManager.EnableProgram(); } + public void ShowDebug() + { + IWindowManager manager = new WindowManager(); + dynamic settings = new ExpandoObject(); + var icon = ImageUtilities.GenerateWindowIcon(); + + settings.Title = "Artemis | Debugger"; + settings.Icon = icon; + + manager.ShowWindow(_debugViewModel, null, settings); + } + public void ResetSettings() { GeneralSettings.ResetSettings(); diff --git a/Artemis/Artemis/ViewModels/Profiles/Events/EventPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Events/EventPropertiesViewModel.cs new file mode 100644 index 000000000..8b1af58c7 --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Events/EventPropertiesViewModel.cs @@ -0,0 +1,42 @@ +using System; +using Artemis.Profiles.Layers.Models; +using Artemis.Utilities; +using Caliburn.Micro; + + +namespace Artemis.ViewModels.Profiles.Events +{ + public class EventPropertiesViewModel : PropertyChangedBase + { + private EventPropertiesModel _proposedProperties; + + public EventPropertiesViewModel(EventPropertiesModel eventPropertiesModel) + { + if (eventPropertiesModel == null) + ProposedProperties = new KeyboardEventPropertiesModel + { + ExpirationType = ExpirationType.Time, + Length = new TimeSpan(0, 0, 1), + TriggerDelay = new TimeSpan(0) + }; + else + ProposedProperties = GeneralHelpers.Clone(eventPropertiesModel); + } + + public EventPropertiesModel ProposedProperties + { + get { return _proposedProperties; } + set + { + if (Equals(value, _proposedProperties)) return; + _proposedProperties = value; + NotifyOfPropertyChange(() => ProposedProperties); + } + } + + public EventPropertiesModel GetAppliedProperties() + { + return GeneralHelpers.Clone(ProposedProperties); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/LayerConditionViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/LayerConditionViewModel.cs index 554173910..db5f5c219 100644 --- a/Artemis/Artemis/ViewModels/Profiles/LayerConditionViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/LayerConditionViewModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel; using System.Linq; -using Artemis.Models.Profiles; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; using Caliburn.Micro; diff --git a/Artemis/Artemis/ViewModels/Profiles/LayerDynamicPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/LayerDynamicPropertiesViewModel.cs index 9505f2ef4..bd5061cbd 100644 --- a/Artemis/Artemis/ViewModels/Profiles/LayerDynamicPropertiesViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/LayerDynamicPropertiesViewModel.cs @@ -1,10 +1,11 @@ using System.ComponentModel; using System.Linq; -using Artemis.Models.Profiles.Properties; +using Artemis.Profiles.Layers.Models; using Artemis.Utilities; using Caliburn.Micro; using Castle.Core.Internal; + namespace Artemis.ViewModels.Profiles { public sealed class LayerDynamicPropertiesViewModel : PropertyChangedBase @@ -21,13 +22,13 @@ namespace Artemis.ViewModels.Profiles public LayerDynamicPropertiesViewModel(string property, BindableCollection dataModelProps, - KeyboardPropertiesModel keyboardProperties) + LayerPropertiesModel layerPropertiesModel) { _property = property; // Look for the existing property model Proposed = new DynamicPropertiesModel(); - var original = keyboardProperties.DynamicProperties.FirstOrDefault(lp => lp.LayerProperty == _property); + var original = layerPropertiesModel.DynamicProperties.FirstOrDefault(lp => lp.LayerProperty == _property); if (original == null) { Proposed.LayerProperty = property; @@ -161,22 +162,22 @@ namespace Artemis.ViewModels.Profiles case "Width": LayerPropertyOptions = new BindableCollection { - Models.Profiles.Properties.LayerPropertyOptions.LeftToRight, - Models.Profiles.Properties.LayerPropertyOptions.RightToLeft + Artemis.Profiles.Layers.Models.LayerPropertyOptions.LeftToRight, + Artemis.Profiles.Layers.Models.LayerPropertyOptions.RightToLeft }; break; case "Height": LayerPropertyOptions = new BindableCollection { - Models.Profiles.Properties.LayerPropertyOptions.Downwards, - Models.Profiles.Properties.LayerPropertyOptions.Upwards + Artemis.Profiles.Layers.Models.LayerPropertyOptions.Downwards, + Artemis.Profiles.Layers.Models.LayerPropertyOptions.Upwards }; break; case "Opacity": LayerPropertyOptions = new BindableCollection { - Models.Profiles.Properties.LayerPropertyOptions.Increase, - Models.Profiles.Properties.LayerPropertyOptions.Decrease + Artemis.Profiles.Layers.Models.LayerPropertyOptions.Increase, + Artemis.Profiles.Layers.Models.LayerPropertyOptions.Decrease }; break; } @@ -206,14 +207,14 @@ namespace Artemis.ViewModels.Profiles } } - public void Apply(KeyboardPropertiesModel keyboardProperties) + public void Apply(LayerModel layerModel) { - var original = keyboardProperties.DynamicProperties.FirstOrDefault(lp => lp.LayerProperty == _property); + var original = layerModel.Properties.DynamicProperties.FirstOrDefault(lp => lp.LayerProperty == _property); if (original != null) - keyboardProperties.DynamicProperties.Remove(original); + layerModel.Properties.DynamicProperties.Remove(original); if (!Proposed.GameProperty.IsNullOrEmpty()) - keyboardProperties.DynamicProperties.Add(Proposed); + layerModel.Properties.DynamicProperties.Add(Proposed); } } } \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs index 46621f813..f43f71a54 100644 --- a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs @@ -1,14 +1,20 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using Artemis.Models.Interfaces; -using Artemis.Models.Profiles; -using Artemis.Models.Profiles.Properties; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Keyboard; +using Artemis.Profiles.Layers.Types.KeyboardGif; using Artemis.Services; using Artemis.Utilities; -using Artemis.ViewModels.Profiles.Properties; +using Artemis.ViewModels.Profiles.Events; +using Artemis.ViewModels.Profiles.Layers; using Caliburn.Micro; + +using Newtonsoft.Json; using Ninject; namespace Artemis.ViewModels.Profiles @@ -16,15 +22,18 @@ namespace Artemis.ViewModels.Profiles public sealed class LayerEditorViewModel : Screen { private readonly IDataModel _dataModel; + private readonly List _layerAnimations; + private EventPropertiesViewModel _eventPropertiesViewModel; private LayerModel _layer; private LayerPropertiesViewModel _layerPropertiesViewModel; - private LayerType _layerType; private LayerModel _proposedLayer; - private LayerPropertiesModel _proposedProperties; + private ILayerType _selectedLayerType; - public LayerEditorViewModel(IDataModel dataModel, LayerModel layer) + public LayerEditorViewModel(IDataModel dataModel, LayerModel layer, IEnumerable layerTypes, + List layerAnimations) { _dataModel = dataModel; + _layerAnimations = layerAnimations; Layer = layer; ProposedLayer = GeneralHelpers.Clone(layer); @@ -32,26 +41,28 @@ namespace Artemis.ViewModels.Profiles if (Layer.Properties == null) Layer.SetupProperties(); - DataModelProps = new BindableCollection(); - DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap(dataModel)); - LayerConditionVms = new BindableCollection(layer.Properties.Conditions - .Select(c => new LayerConditionViewModel(this, c, DataModelProps))); + LayerTypes = new BindableCollection(layerTypes); + DataModelProps = new BindableCollection( + GeneralHelpers.GenerateTypeMap(dataModel)); + LayerConditionVms = new BindableCollection( + layer.Properties.Conditions.Select(c => new LayerConditionViewModel(this, c, DataModelProps))); PropertyChanged += PropertiesViewModelHandler; PreSelect(); } + public bool ModelChanged { get; set; } [Inject] public MetroDialogService DialogService { get; set; } + public BindableCollection LayerTypes { get; set; } public BindableCollection DataModelProps { get; set; } - - public BindableCollection LayerTypes => new BindableCollection(); - public BindableCollection LayerConditionVms { get; set; } + public bool KeyboardGridIsVisible => ProposedLayer.LayerType is KeyboardType; + public bool GifGridIsVisible => ProposedLayer.LayerType is KeyboardGifType; public LayerModel Layer { @@ -75,17 +86,6 @@ namespace Artemis.ViewModels.Profiles } } - public LayerType LayerType - { - get { return _layerType; } - set - { - if (value == _layerType) return; - _layerType = value; - NotifyOfPropertyChange(() => LayerType); - } - } - public LayerPropertiesViewModel LayerPropertiesViewModel { get { return _layerPropertiesViewModel; } @@ -97,59 +97,66 @@ namespace Artemis.ViewModels.Profiles } } - public bool KeyboardGridIsVisible => ProposedLayer.LayerType == LayerType.Keyboard; - public bool GifGridIsVisible => ProposedLayer.LayerType == LayerType.KeyboardGif; + public EventPropertiesViewModel EventPropertiesViewModel + { + get { return _eventPropertiesViewModel; } + set + { + if (Equals(value, _eventPropertiesViewModel)) return; + _eventPropertiesViewModel = value; + NotifyOfPropertyChange(() => EventPropertiesViewModel); + } + } + + public ILayerType SelectedLayerType + { + get { return _selectedLayerType; } + set + { + if (Equals(value, _selectedLayerType)) return; + _selectedLayerType = value; + NotifyOfPropertyChange(() => SelectedLayerType); + } + } public void PreSelect() { - LayerType = ProposedLayer.LayerType; - - if (LayerType == LayerType.Folder && !(LayerPropertiesViewModel is FolderPropertiesViewModel)) - LayerPropertiesViewModel = new FolderPropertiesViewModel(_dataModel, ProposedLayer.Properties); + SelectedLayerType = LayerTypes.FirstOrDefault(t => t.Name == ProposedLayer.LayerType.Name); + ToggleIsEvent(); } private void PropertiesViewModelHandler(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName != "LayerType") + if (e.PropertyName != "SelectedLayerType") return; // Store the brush in case the user wants to reuse it - var oldBrush = LayerPropertiesViewModel?.GetAppliedProperties().Brush; + var oldBrush = ProposedLayer.Properties.Brush; // Update the model - if (ProposedLayer.LayerType != LayerType) + if (ProposedLayer.LayerType.GetType() != SelectedLayerType.GetType()) { - ProposedLayer.LayerType = LayerType; + ProposedLayer.LayerType = SelectedLayerType; ProposedLayer.SetupProperties(); } + // Let the layer type handle the viewmodel setup + LayerPropertiesViewModel = ProposedLayer.LayerType.SetupViewModel(LayerPropertiesViewModel, _layerAnimations, + _dataModel, ProposedLayer); + if (oldBrush != null) ProposedLayer.Properties.Brush = oldBrush; - // Update the KeyboardPropertiesViewModel if it's being used - var model = LayerPropertiesViewModel as KeyboardPropertiesViewModel; - if (model != null) - model.IsGif = LayerType == LayerType.KeyboardGif; - - // Apply the proper PropertiesViewModel - if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) && - !(LayerPropertiesViewModel is KeyboardPropertiesViewModel)) - { - LayerPropertiesViewModel = new KeyboardPropertiesViewModel(_dataModel, ProposedLayer.Properties) - { - IsGif = LayerType == LayerType.KeyboardGif - }; - } - else if (LayerType == LayerType.Mouse && !(LayerPropertiesViewModel is MousePropertiesViewModel)) - LayerPropertiesViewModel = new MousePropertiesViewModel(_dataModel, ProposedLayer.Properties); - else if (LayerType == LayerType.Headset && !(LayerPropertiesViewModel is HeadsetPropertiesViewModel)) - LayerPropertiesViewModel = new HeadsetPropertiesViewModel(_dataModel, ProposedLayer.Properties); - else if (LayerType == LayerType.Folder && !(LayerPropertiesViewModel is FolderPropertiesViewModel)) - LayerPropertiesViewModel = new FolderPropertiesViewModel(_dataModel, ProposedLayer.Properties); - NotifyOfPropertyChange(() => LayerPropertiesViewModel); } + public void ToggleIsEvent() + { + EventPropertiesViewModel = ProposedLayer.IsEvent + ? new EventPropertiesViewModel(Layer.EventProperties) + : null; + } + public void AddCondition() { var condition = new LayerConditionModel(); @@ -158,19 +165,20 @@ namespace Artemis.ViewModels.Profiles public void Apply() { - Layer.Name = ProposedLayer.Name; - Layer.LayerType = ProposedLayer.LayerType; + LayerPropertiesViewModel?.ApplyProperties(); + Layer = GeneralHelpers.Clone(ProposedLayer); + + // TODO: EventPropVM must have layer too + if (EventPropertiesViewModel != null) + Layer.EventProperties = EventPropertiesViewModel.GetAppliedProperties(); - if (LayerPropertiesViewModel != null) - Layer.Properties = LayerPropertiesViewModel.GetAppliedProperties(); Layer.Properties.Conditions.Clear(); foreach (var conditionViewModel in LayerConditionVms) - { Layer.Properties.Conditions.Add(conditionViewModel.LayerConditionModel); - } - if (Layer.LayerType != LayerType.KeyboardGif) - return; // Don't bother checking for a GIF path unless the type is GIF + // Don't bother checking for a GIF path unless the type is GIF + if (!(Layer.LayerType is KeyboardGifType)) + return; if (!File.Exists(((KeyboardPropertiesModel) Layer.Properties).GifFile)) DialogService.ShowErrorMessageBox("Couldn't find or access the provided GIF file."); } @@ -185,25 +193,26 @@ namespace Artemis.ViewModels.Profiles public override async void CanClose(Action callback) { // Create a fake layer and apply the properties to it - var fakeLayer = GeneralHelpers.Clone(ProposedLayer); - if (LayerPropertiesViewModel != null) - fakeLayer.Properties = LayerPropertiesViewModel.GetAppliedProperties(); - fakeLayer.Properties.Conditions.Clear(); + LayerPropertiesViewModel?.ApplyProperties(); + // TODO: EventPropVM must have layer too + if (EventPropertiesViewModel != null) + ProposedLayer.EventProperties = EventPropertiesViewModel.GetAppliedProperties(); + ProposedLayer.Properties.Conditions.Clear(); foreach (var conditionViewModel in LayerConditionVms) - fakeLayer.Properties.Conditions.Add(conditionViewModel.LayerConditionModel); + ProposedLayer.Properties.Conditions.Add(conditionViewModel.LayerConditionModel); - var fake = GeneralHelpers.Serialize(fakeLayer); - var real = GeneralHelpers.Serialize(Layer); + var current = JsonConvert.SerializeObject(Layer, Formatting.Indented); + var proposed = JsonConvert.SerializeObject(ProposedLayer, Formatting.Indented); - if (fake.Equals(real)) + if (current.Equals(proposed)) { callback(true); return; } - var close = - await DialogService.ShowQuestionMessageBox("Unsaved changes", "Do you want to discard your changes?"); + var close = await DialogService + .ShowQuestionMessageBox("Unsaved changes", "Do you want to discard your changes?"); callback(close.Value); } } diff --git a/Artemis/Artemis/ViewModels/Profiles/Layers/FolderPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Layers/FolderPropertiesViewModel.cs new file mode 100644 index 000000000..fd0ffaf50 --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Layers/FolderPropertiesViewModel.cs @@ -0,0 +1,16 @@ +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Models; + +namespace Artemis.ViewModels.Profiles.Layers +{ + public class FolderPropertiesViewModel : LayerPropertiesViewModel + { + public FolderPropertiesViewModel(LayerModel layerModel, IDataModel dataModel) : base(layerModel, dataModel) + { + } + + public override void ApplyProperties() + { + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Layers/HeadsetPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Layers/HeadsetPropertiesViewModel.cs new file mode 100644 index 000000000..8c3eb2906 --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Layers/HeadsetPropertiesViewModel.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Utilities; +using Caliburn.Micro; + +namespace Artemis.ViewModels.Profiles.Layers +{ + public class HeadsetPropertiesViewModel : LayerPropertiesViewModel + { + private ILayerAnimation _selectedLayerAnimation; + + public HeadsetPropertiesViewModel(LayerModel layerModel, IDataModel dataModel, + IEnumerable layerAnimations) : base(layerModel, dataModel) + { + LayerAnimations = new BindableCollection(layerAnimations); + OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", + new BindableCollection(GeneralHelpers.GenerateTypeMap(dataModel)), + layerModel.Properties); + } + + public BindableCollection LayerAnimations { get; set; } + public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } + + public ILayerAnimation SelectedLayerAnimation + { + get { return _selectedLayerAnimation; } + set + { + if (Equals(value, _selectedLayerAnimation)) return; + _selectedLayerAnimation = value; + NotifyOfPropertyChange(() => SelectedLayerAnimation); + } + } + + public override void ApplyProperties() + { + OpacityProperties.Apply(LayerModel); + LayerModel.Properties.Brush = Brush; + LayerModel.LayerAnimation = SelectedLayerAnimation; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Layers/KeyboardPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Layers/KeyboardPropertiesViewModel.cs new file mode 100644 index 000000000..28a935a4d --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Layers/KeyboardPropertiesViewModel.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Keyboard; +using Caliburn.Micro; +using static Artemis.Utilities.GeneralHelpers; + +namespace Artemis.ViewModels.Profiles.Layers +{ + public class KeyboardPropertiesViewModel : LayerPropertiesViewModel + { + private bool _isGif; + private ILayerAnimation _selectedLayerAnimation; + + public KeyboardPropertiesViewModel(LayerModel layerModel, IDataModel dataModel, + IEnumerable layerAnimations) : base(layerModel, dataModel) + { + LayerAnimations = new BindableCollection(layerAnimations); + + var dataModelProps = new BindableCollection(GenerateTypeMap(dataModel)); + HeightProperties = new LayerDynamicPropertiesViewModel("Height", dataModelProps, layerModel.Properties); + WidthProperties = new LayerDynamicPropertiesViewModel("Width", dataModelProps, layerModel.Properties); + OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", dataModelProps, layerModel.Properties); + + SelectedLayerAnimation = LayerAnimations.FirstOrDefault(l => l.Name == layerModel.LayerAnimation?.Name) ?? + LayerAnimations.First(l => l.Name == "None"); + } + + public bool ShowGif => IsGif; + public bool ShowBrush => !IsGif; + public BindableCollection DataModelProps { get; set; } + public BindableCollection LayerAnimations { get; set; } + public LayerDynamicPropertiesViewModel HeightProperties { get; set; } + public LayerDynamicPropertiesViewModel WidthProperties { get; set; } + public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } + + public bool IsGif + { + get { return _isGif; } + set + { + _isGif = value; + NotifyOfPropertyChange(() => ShowGif); + NotifyOfPropertyChange(() => ShowBrush); + } + } + + public ILayerAnimation SelectedLayerAnimation + { + get { return _selectedLayerAnimation; } + set + { + if (Equals(value, _selectedLayerAnimation)) return; + _selectedLayerAnimation = value; + NotifyOfPropertyChange(() => SelectedLayerAnimation); + } + } + + public void BrowseGif() + { + var dialog = new OpenFileDialog {Filter = "Animated image file (*.gif)|*.gif"}; + var result = dialog.ShowDialog(); + if (result != DialogResult.OK) + return; + + ((KeyboardPropertiesModel) LayerModel.Properties).GifFile = dialog.FileName; + NotifyOfPropertyChange(() => LayerModel); + } + + public override void ApplyProperties() + { + HeightProperties.Apply(LayerModel); + WidthProperties.Apply(LayerModel); + OpacityProperties.Apply(LayerModel); + LayerModel.Properties.Brush = Brush; + + LayerModel.LayerAnimation = SelectedLayerAnimation; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Layers/LayerPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Layers/LayerPropertiesViewModel.cs new file mode 100644 index 000000000..f633520b0 --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Layers/LayerPropertiesViewModel.cs @@ -0,0 +1,47 @@ +using System.Drawing; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Models; +using Caliburn.Micro; +using Brush = System.Windows.Media.Brush; + +namespace Artemis.ViewModels.Profiles.Layers +{ + public abstract class LayerPropertiesViewModel : PropertyChangedBase + { + private LayerModel _layerModel; + private Brush _brush; + + protected LayerPropertiesViewModel(LayerModel layerModel, IDataModel dataModel) + { + LayerModel = layerModel; + DataModel = dataModel; + Brush = LayerModel.Properties.Brush.Clone(); + } + + public Brush Brush + { + get { return _brush; } + set + { + if (Equals(value, _brush)) return; + _brush = value; + NotifyOfPropertyChange(() => Brush); + } + } + + public LayerModel LayerModel + { + get { return _layerModel; } + set + { + if (Equals(value, _layerModel)) return; + _layerModel = value; + NotifyOfPropertyChange(() => LayerModel); + } + } + + public IDataModel DataModel { get; set; } + + public abstract void ApplyProperties(); + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Layers/MousePropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Layers/MousePropertiesViewModel.cs new file mode 100644 index 000000000..0bfbc0eaa --- /dev/null +++ b/Artemis/Artemis/ViewModels/Profiles/Layers/MousePropertiesViewModel.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Linq; +using Artemis.Models.Interfaces; +using Artemis.Profiles.Layers.Interfaces; +using Artemis.Profiles.Layers.Models; +using Artemis.Utilities; +using Caliburn.Micro; + +namespace Artemis.ViewModels.Profiles.Layers +{ + public class MousePropertiesViewModel : LayerPropertiesViewModel + { + private ILayerAnimation _selectedLayerAnimation; + + public MousePropertiesViewModel(LayerModel layerModel, IDataModel dataModel, + IEnumerable layerAnimations) : base(layerModel, dataModel) + { + LayerAnimations = new BindableCollection(layerAnimations); + OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", + new BindableCollection(GeneralHelpers.GenerateTypeMap(dataModel)), + layerModel.Properties); + + SelectedLayerAnimation = LayerAnimations.FirstOrDefault(l => l.Name == layerModel.LayerAnimation.Name) ?? + LayerAnimations.First(l => l.Name == "None"); + } + + public BindableCollection LayerAnimations { get; set; } + public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } + + public ILayerAnimation SelectedLayerAnimation + { + get { return _selectedLayerAnimation; } + set + { + if (Equals(value, _selectedLayerAnimation)) return; + _selectedLayerAnimation = value; + NotifyOfPropertyChange(() => SelectedLayerAnimation); + } + } + + public override void ApplyProperties() + { + OpacityProperties.Apply(LayerModel); + LayerModel.Properties.Brush = Brush; + LayerModel.LayerAnimation = SelectedLayerAnimation; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs index e28eed39d..acda2190d 100644 --- a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs @@ -3,27 +3,26 @@ using System.Dynamic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Media.Imaging; using Artemis.DAL; using Artemis.DeviceProviders; using Artemis.Events; using Artemis.InjectionFactories; using Artemis.Managers; using Artemis.Models; -using Artemis.Models.Profiles; +using Artemis.Profiles; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Folder; using Artemis.Services; using Artemis.Styles.DropTargetAdorners; using Artemis.Utilities; using Caliburn.Micro; using GongSolutions.Wpf.DragDrop; using MahApps.Metro.Controls.Dialogs; + using Ninject; -using Application = System.Windows.Application; using DragDropEffects = System.Windows.DragDropEffects; using IDropTarget = GongSolutions.Wpf.DragDrop.IDropTarget; using MouseEventArgs = System.Windows.Input.MouseEventArgs; @@ -131,7 +130,7 @@ namespace Artemis.ViewModels.Profiles return; if (dropInfo.InsertPosition == RelativeInsertPosition.TargetItemCenter && - target.LayerType == LayerType.Folder) + target.LayerType is FolderType) { dropInfo.DropTargetAdorner = typeof(DropTargetMetroHighlightAdorner); dropInfo.Effects = DragDropEffects.Copy; @@ -169,7 +168,7 @@ namespace Artemis.ViewModels.Profiles } if (dropInfo.InsertPosition == RelativeInsertPosition.TargetItemCenter && - target.LayerType == LayerType.Folder) + target.LayerType is FolderType) { // Insert into folder source.Order = -1; @@ -257,7 +256,7 @@ namespace Artemis.ViewModels.Profiles public void EditLayerFromDoubleClick() { - if (ProfileViewModel.SelectedLayer?.LayerType == LayerType.Folder) + if (ProfileViewModel.SelectedLayer?.LayerType is FolderType) return; EditLayer(); @@ -280,23 +279,19 @@ namespace Artemis.ViewModels.Profiles IWindowManager manager = new WindowManager(); var editorVm = _layerEditorVmFactory.CreateLayerEditorVm(_gameModel.DataModel, layer); dynamic settings = new ExpandoObject(); - var iconImage = new Image - { - Source = (DrawingImage) Application.Current.MainWindow.Resources["BowIcon"], - Stretch = Stretch.Uniform, - Margin = new Thickness(20) - }; - iconImage.Arrange(new Rect(0, 0, 100, 100)); - var bitmap = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32); - bitmap.Render(iconImage); + var icon = ImageUtilities.GenerateWindowIcon(); settings.Title = "Artemis | Edit " + layer.Name; - settings.Icon = bitmap; + settings.Icon = icon; manager.ShowDialog(editorVm, null, settings); + // The layer editor VM may have created a new instance of the layer, reapply it to the list + layer.Replace(editorVm.Layer); + layer = editorVm.Layer; + // If the layer was a folder, but isn't anymore, assign it's children to it's parent. - if (layer.LayerType != LayerType.Folder && layer.Children.Any()) + if (!(layer.LayerType is FolderType) && layer.Children.Any()) { while (layer.Children.Any()) { @@ -596,7 +591,7 @@ namespace Artemis.ViewModels.Profiles "To import a profile, please select a keyboard in the options menu first."); return; } - var dialog = new OpenFileDialog {Filter = "Artemis profile (*.xml)|*.xml"}; + var dialog = new OpenFileDialog {Filter = "Artemis profile (*.json)|*.json"}; var result = dialog.ShowDialog(); if (result != DialogResult.OK) return; @@ -654,7 +649,7 @@ namespace Artemis.ViewModels.Profiles if (SelectedProfile == null) return; - var dialog = new SaveFileDialog {Filter = "Artemis profile (*.xml)|*.xml"}; + var dialog = new SaveFileDialog {Filter = "Artemis profile (*.json)|*.json"}; var result = dialog.ShowDialog(); if (result != DialogResult.OK) return; diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs index 660673f9c..4f7b0aede 100644 --- a/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs @@ -7,9 +7,10 @@ using System.Windows.Input; using System.Windows.Media; using Artemis.Events; using Artemis.Managers; -using Artemis.Models.Profiles; -using Artemis.Models.Profiles.Properties; using Artemis.Modules.Effects.ProfilePreview; +using Artemis.Profiles; +using Artemis.Profiles.Layers.Models; +using Artemis.Profiles.Layers.Types.Keyboard; using Artemis.Properties; using Artemis.Utilities; using Caliburn.Micro; @@ -109,10 +110,12 @@ namespace Artemis.ViewModels.Profiles drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); // Draw the layers - var drawLayers = SelectedProfile.GetRenderLayers( - new ProfilePreviewDataModel(), false, false, true); + var drawLayers = SelectedProfile.GetRenderLayers(new ProfilePreviewDataModel(), false, false, true); foreach (var layer in drawLayers) + { + layer.Update(null, true, false); layer.Draw(null, drawingContext, true, false); + } // Get the selection color var accentColor = ThemeManager.DetectAppStyle(Application.Current)?.Item2?.Resources["AccentColor"]; diff --git a/Artemis/Artemis/ViewModels/Profiles/Properties/FolderPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Properties/FolderPropertiesViewModel.cs deleted file mode 100644 index 4a0cb16e1..000000000 --- a/Artemis/Artemis/ViewModels/Profiles/Properties/FolderPropertiesViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; - -namespace Artemis.ViewModels.Profiles.Properties -{ - public class FolderPropertiesViewModel : LayerPropertiesViewModel - { - private LayerPropertiesModel _proposedProperties; - - public FolderPropertiesViewModel(IDataModel dataModel, LayerPropertiesModel properties) - : base(dataModel) - { - ProposedProperties = GeneralHelpers.Clone(properties); - } - - public LayerPropertiesModel ProposedProperties - { - get { return _proposedProperties; } - set - { - if (Equals(value, _proposedProperties)) return; - _proposedProperties = value; - NotifyOfPropertyChange(() => ProposedProperties); - } - } - - public override LayerPropertiesModel GetAppliedProperties() - { - return GeneralHelpers.Clone(ProposedProperties); - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Properties/HeadsetPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Properties/HeadsetPropertiesViewModel.cs deleted file mode 100644 index 23d853420..000000000 --- a/Artemis/Artemis/ViewModels/Profiles/Properties/HeadsetPropertiesViewModel.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Windows.Media; -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; - -namespace Artemis.ViewModels.Profiles.Properties -{ - public class HeadsetPropertiesViewModel : LayerPropertiesViewModel - { - private Brush _brush; - private LayerPropertiesModel _proposedProperties; - - public HeadsetPropertiesViewModel(IDataModel dataModel, LayerPropertiesModel properties) - : base(dataModel) - { - ProposedProperties = GeneralHelpers.Clone(properties); - Brush = ProposedProperties.Brush.CloneCurrentValue(); - } - - public Brush Brush - { - get { return _brush; } - set - { - if (Equals(value, _brush)) return; - _brush = value; - NotifyOfPropertyChange(() => Brush); - } - } - - public LayerPropertiesModel ProposedProperties - { - get { return _proposedProperties; } - set - { - if (Equals(value, _proposedProperties)) return; - _proposedProperties = value; - NotifyOfPropertyChange(() => ProposedProperties); - } - } - - public override LayerPropertiesModel GetAppliedProperties() - { - var properties = GeneralHelpers.Clone(ProposedProperties); - properties.Brush = Brush; - return properties; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Properties/KeyboardPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Properties/KeyboardPropertiesViewModel.cs deleted file mode 100644 index 1359dbed5..000000000 --- a/Artemis/Artemis/ViewModels/Profiles/Properties/KeyboardPropertiesViewModel.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Windows.Forms; -using System.Windows.Media; -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; -using Caliburn.Micro; - -namespace Artemis.ViewModels.Profiles.Properties -{ - public class KeyboardPropertiesViewModel : LayerPropertiesViewModel - { - private Brush _brush; - private bool _isGif; - private KeyboardPropertiesModel _proposedProperties; - - public KeyboardPropertiesViewModel(IDataModel dataModel, LayerPropertiesModel properties) - : base(dataModel) - { - var keyboardProperties = (KeyboardPropertiesModel) properties; - ProposedProperties = GeneralHelpers.Clone(keyboardProperties); - Brush = ProposedProperties.Brush.CloneCurrentValue(); - - DataModelProps = new BindableCollection(); - DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap(dataModel)); - - HeightProperties = new LayerDynamicPropertiesViewModel("Height", DataModelProps, keyboardProperties); - WidthProperties = new LayerDynamicPropertiesViewModel("Width", 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 - { - get { return _isGif; } - set - { - _isGif = value; - NotifyOfPropertyChange(() => ShowGif); - NotifyOfPropertyChange(() => ShowBrush); - } - } - - public Brush Brush - { - get { return _brush; } - set - { - if (Equals(value, _brush)) return; - _brush = value; - NotifyOfPropertyChange(() => Brush); - } - } - - public bool ShowGif => IsGif; - - public bool ShowBrush => !IsGif; - - public BindableCollection DataModelProps { get; set; } - - public LayerDynamicPropertiesViewModel HeightProperties { get; set; } - - public LayerDynamicPropertiesViewModel WidthProperties { get; set; } - - public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } - - public void BrowseGif() - { - var dialog = new OpenFileDialog {Filter = "Animated image file (*.gif)|*.gif"}; - var result = dialog.ShowDialog(); - if (result != DialogResult.OK) - return; - - ProposedProperties.GifFile = dialog.FileName; - NotifyOfPropertyChange(() => ProposedProperties); - } - - public override LayerPropertiesModel GetAppliedProperties() - { - HeightProperties.Apply(ProposedProperties); - WidthProperties.Apply(ProposedProperties); - OpacityProperties.Apply(ProposedProperties); - - var properties = GeneralHelpers.Clone(ProposedProperties); - properties.Brush = Brush; - return properties; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Properties/LayerPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Properties/LayerPropertiesViewModel.cs deleted file mode 100644 index 5cbc63f5b..000000000 --- a/Artemis/Artemis/ViewModels/Profiles/Properties/LayerPropertiesViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Caliburn.Micro; - -namespace Artemis.ViewModels.Profiles.Properties -{ - public abstract class LayerPropertiesViewModel : PropertyChangedBase - { - protected LayerPropertiesViewModel(IDataModel dataModel) - { - DataModel = dataModel; - } - - public IDataModel DataModel { get; set; } - - public abstract LayerPropertiesModel GetAppliedProperties(); - } -} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/Profiles/Properties/MousePropertiesViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/Properties/MousePropertiesViewModel.cs deleted file mode 100644 index 85106eff9..000000000 --- a/Artemis/Artemis/ViewModels/Profiles/Properties/MousePropertiesViewModel.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Windows.Media; -using Artemis.Models.Interfaces; -using Artemis.Models.Profiles.Properties; -using Artemis.Utilities; - -namespace Artemis.ViewModels.Profiles.Properties -{ - public class MousePropertiesViewModel : LayerPropertiesViewModel - { - private Brush _brush; - private LayerPropertiesModel _proposedProperties; - - public MousePropertiesViewModel(IDataModel dataModel, LayerPropertiesModel properties) - : base(dataModel) - { - ProposedProperties = GeneralHelpers.Clone(properties); - Brush = ProposedProperties.Brush.CloneCurrentValue(); - } - - public Brush Brush - { - get { return _brush; } - set - { - if (Equals(value, _brush)) return; - _brush = value; - NotifyOfPropertyChange(() => Brush); - } - } - - public LayerPropertiesModel ProposedProperties - { - get { return _proposedProperties; } - set - { - if (Equals(value, _proposedProperties)) return; - _proposedProperties = value; - NotifyOfPropertyChange(() => ProposedProperties); - } - } - - public override LayerPropertiesModel GetAppliedProperties() - { - var properties = GeneralHelpers.Clone(ProposedProperties); - properties.Brush = Brush; - return properties; - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/ShellViewModel.cs b/Artemis/Artemis/ViewModels/ShellViewModel.cs index e7a6f4fba..e8cc77059 100644 --- a/Artemis/Artemis/ViewModels/ShellViewModel.cs +++ b/Artemis/Artemis/ViewModels/ShellViewModel.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Threading.Tasks; using Artemis.Managers; using Artemis.Services; using Artemis.ViewModels.Abstract; @@ -42,7 +41,7 @@ namespace Artemis.ViewModels ActiveItem = _viewModels.FirstOrDefault(); } - + public void Settings() { Flyouts.First().IsOpen = !Flyouts.First().IsOpen; diff --git a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs index 33cdc0fca..c4b4223f5 100644 --- a/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs +++ b/Artemis/Artemis/ViewModels/SystemTrayViewModel.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using System.Threading.Tasks; using System.Windows; using Artemis.Events; @@ -8,7 +7,6 @@ using Artemis.Services; using Artemis.Settings; using Artemis.Utilities; using Caliburn.Micro; -using Ninject; namespace Artemis.ViewModels { @@ -21,7 +19,8 @@ namespace Artemis.ViewModels private bool _enabled; private string _toggleText; - public SystemTrayViewModel(IWindowManager windowManager, IEventAggregator events, MetroDialogService dialogService, ShellViewModel shellViewModel, + public SystemTrayViewModel(IWindowManager windowManager, IEventAggregator events, + MetroDialogService dialogService, ShellViewModel shellViewModel, MainManager mainManager) { _windowManager = windowManager; @@ -125,7 +124,7 @@ namespace Artemis.ViewModels private async void ShowKeyboardDialog() { - while(!_shellViewModel.IsActive) + while (!_shellViewModel.IsActive) await Task.Delay(200); NotifyOfPropertyChange(() => CanHideWindow); diff --git a/Artemis/Artemis/Views/DebugView.xaml b/Artemis/Artemis/Views/DebugView.xaml new file mode 100644 index 000000000..5c9dd7b8d --- /dev/null +++ b/Artemis/Artemis/Views/DebugView.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Artemis/Artemis/Views/DebugView.xaml.cs b/Artemis/Artemis/Views/DebugView.xaml.cs new file mode 100644 index 000000000..f08610dab --- /dev/null +++ b/Artemis/Artemis/Views/DebugView.xaml.cs @@ -0,0 +1,15 @@ +using MahApps.Metro.Controls; + +namespace Artemis.Views +{ + /// + /// Interaction logic for DebugView.xaml + /// + public partial class DebugView : MetroWindow + { + public DebugView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Views/Flyouts/FlyoutSettingsView.xaml b/Artemis/Artemis/Views/Flyouts/FlyoutSettingsView.xaml index 105fdbd39..1ea2699c1 100644 --- a/Artemis/Artemis/Views/Flyouts/FlyoutSettingsView.xaml +++ b/Artemis/Artemis/Views/Flyouts/FlyoutSettingsView.xaml @@ -4,24 +4,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls" - xmlns:cal="http://www.caliburnproject.org" - xmlns:utilities="clr-namespace:Artemis.Utilities" - xmlns:sys="clr-namespace:System;assembly=mscorlib" - xmlns:profileEnumerations="clr-namespace:Artemis.Models.Profiles" + xmlns:cal="http://www.caliburnproject.org" mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="300" Width="300"> - - - - - - - - - @@ -38,6 +24,7 @@ + @@ -103,17 +90,20 @@ +