diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index d3962784b..a0da17063 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -418,8 +418,9 @@ - + + @@ -459,12 +460,15 @@ Witcher3View.xaml - - LayerEditorView.xaml - LayerConditionView.xaml + + LayerDynamicPropertiesView.xaml + + + LayerEditorView.xaml + OverlaysView.xaml @@ -623,14 +627,18 @@ Designer MSBuild:Compile - - Designer - MSBuild:Compile - Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/Artemis/Artemis/ItemBehaviours/BindableSelectedItemBehavior.cs b/Artemis/Artemis/ItemBehaviours/BindableSelectedItemBehavior.cs index a09841ed5..ffb3687ad 100644 --- a/Artemis/Artemis/ItemBehaviours/BindableSelectedItemBehavior.cs +++ b/Artemis/Artemis/ItemBehaviours/BindableSelectedItemBehavior.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.Collections; +using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; @@ -44,7 +45,42 @@ namespace Artemis.ItemBehaviours { var item = ((BindableSelectedItemBehavior) sender).AssociatedObject .ItemContainerGenerator.ContainerFromItem(e.NewValue) as TreeViewItem; - item?.SetValue(TreeViewItem.IsSelectedProperty, true); + if (item != null) + item.SetValue(TreeViewItem.IsSelectedProperty, true); + else + ClearTreeViewSelection(((BindableSelectedItemBehavior) sender).AssociatedObject); + } + + /// + /// Clears a TreeView's selected item recursively + /// Tom Wright - http://stackoverflow.com/a/1406116/5015269 + /// + /// + public static void ClearTreeViewSelection(TreeView tv) + { + if (tv != null) + ClearTreeViewItemsControlSelection(tv.Items, tv.ItemContainerGenerator); + } + + /// + /// Clears a TreeView's selected item recursively + /// Tom Wright - http://stackoverflow.com/a/1406116/5015269 + /// + /// + /// + private static void ClearTreeViewItemsControlSelection(ICollection ic, ItemContainerGenerator icg) + { + if ((ic == null) || (icg == null)) + return; + + for (var i = 0; i < ic.Count; i++) + { + var tvi = icg.ContainerFromIndex(i) as TreeViewItem; + if (tvi == null) + continue; + ClearTreeViewItemsControlSelection(tvi.Items, tvi.ItemContainerGenerator); + tvi.IsSelected = false; + } } #endregion diff --git a/Artemis/Artemis/Models/Profiles/LayerDynamicPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/LayerDynamicPropertiesModel.cs index 04e088568..43af20b1e 100644 --- a/Artemis/Artemis/Models/Profiles/LayerDynamicPropertiesModel.cs +++ b/Artemis/Artemis/Models/Profiles/LayerDynamicPropertiesModel.cs @@ -1,68 +1,67 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Linq.Dynamic; -using System.Reflection; +using System.ComponentModel; using Artemis.Models.Interfaces; +using Artemis.Utilities; +using static System.Decimal; namespace Artemis.Models.Profiles { public class LayerDynamicPropertiesModel { + /// + /// Property this dynamic property applies on + /// public string LayerProperty { get; set; } - public string GameProperty { get; set; } - public string RequiredOperator { get; set; } - public string RequiredValue { get; set; } - public LayerPopertyType LayerPopertyType { get; set; } /// - /// Only used when LayerPropertyType is PercentageOf or PercentageOfProperty + /// Property to base the percentage upon + /// + public string GameProperty { get; set; } + + /// + /// Percentage source, the number that defines 100% /// public string PercentageSource { get; set; } - internal void ApplyProperty(IGameDataModel dataModel, LayerPropertiesModel userProps, - LayerPropertiesModel props) + /// + /// Type of property + /// + public LayerPropertyType LayerPropertyType { get; set; } + + internal void ApplyProperty(IGameDataModel data, LayerPropertiesModel userProps, LayerPropertiesModel props) { - var dataList = new List {(T) dataModel}; - - // Attempt to set the property - var layerProp = props.GetType().GetProperty(LayerProperty); - var layerUserProp = userProps.GetType().GetProperty(LayerProperty); - - if (LayerPopertyType == LayerPopertyType.PercentageOf) - SetPercentageOf(props, userProps, dataModel, int.Parse(PercentageSource)); - if (LayerPopertyType == LayerPopertyType.PercentageOfProperty) - SetPercentageOfProperty(props, userProps, dataModel); + if (LayerPropertyType == LayerPropertyType.PercentageOf) + Apply(props, userProps, data, int.Parse(PercentageSource)); + if (LayerPropertyType == LayerPropertyType.PercentageOfProperty) + ApplyProp(props, userProps, data); } - private void SetPercentageOf(LayerPropertiesModel props, LayerPropertiesModel userProps, - IGameDataModel dataModel, int percentageSource) + private void Apply(LayerPropertiesModel props, LayerPropertiesModel userProps, IGameDataModel data, + int percentageSource) { - // Property that will be set + // Property to apply on var layerProp = props.GetType().GetProperty(LayerProperty); - // Property to use as a 100% + // User's settings var userProp = userProps.GetType().GetProperty(LayerProperty); - // Value to use as a source - var source = dataModel.GetType().GetProperty(GameProperty)?.GetValue(dataModel, null); - if (layerProp == null || userProp == null || source == null) + // Property to base the percentage upon + var gameProperty = data.GetPropValue(GameProperty); + if (layerProp == null || userProp == null) return; - var percentage = double.Parse(source.ToString())/percentageSource; + var percentage = ToDouble(gameProperty) / percentageSource; layerProp.SetValue(props, (int) (percentage*(int) userProp.GetValue(userProps, null))); } - private void SetPercentageOfProperty(LayerPropertiesModel props, LayerPropertiesModel userProps, - IGameDataModel dataModel) + private void ApplyProp(LayerPropertiesModel props, LayerPropertiesModel userProps, IGameDataModel data) { - var value = dataModel.GetType().GetProperty(PercentageSource)?.GetValue(dataModel, null); - if (value != null) - SetPercentageOf(props, userProps, dataModel, (int) value); + var value = data.GetPropValue(PercentageSource); + Apply(props, userProps, data, value); } } - public enum LayerPopertyType + public enum LayerPropertyType { - PercentageOf, - PercentageOfProperty, - Color + [Description("None")] None, + [Description("% of")] PercentageOf, + [Description("% of property")] PercentageOfProperty } } \ No newline at end of file diff --git a/Artemis/Artemis/Models/Profiles/LayerModel.cs b/Artemis/Artemis/Models/Profiles/LayerModel.cs index fae6b6dc5..23dc597b9 100644 --- a/Artemis/Artemis/Models/Profiles/LayerModel.cs +++ b/Artemis/Artemis/Models/Profiles/LayerModel.cs @@ -49,22 +49,6 @@ namespace Artemis.Models.Profiles [XmlIgnore] public ProfileModel ParentProfile { get; internal set; } - #region IChildItem Members - - LayerModel IChildItem.Parent - { - get { return ParentLayer; } - set { ParentLayer = value; } - } - - ProfileModel IChildItem.Parent - { - get { return ParentProfile; } - set { ParentProfile = value; } - } - - #endregion - public bool ConditionsMet(IGameDataModel dataModel) { return Enabled && LayerConditions.All(cm => cm.ConditionMet(dataModel)); @@ -116,7 +100,7 @@ namespace Artemis.Models.Profiles { // Fix the sorting just in case FixOrder(); - + int newOrder; if (moveUp) newOrder = selectedLayer.Order - 1; @@ -137,6 +121,22 @@ namespace Artemis.Models.Profiles for (var i = 0; i < Children.Count; i++) Children[i].Order = i; } + + #region IChildItem Members + + LayerModel IChildItem.Parent + { + get { return ParentLayer; } + set { ParentLayer = value; } + } + + ProfileModel IChildItem.Parent + { + get { return ParentProfile; } + set { ParentProfile = value; } + } + + #endregion } public enum LayerType diff --git a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs index 4a35dcf84..1ec23360d 100644 --- a/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs +++ b/Artemis/Artemis/Modules/Games/CounterStrike/CounterStrikeModel.cs @@ -68,6 +68,8 @@ namespace Artemis.Modules.Games.CounterStrike if (!jsonString.Contains("Counter-Strike: Global Offensive")) return; + + Debug.WriteLine("Got data"); // Parse the JSON GameDataModel = JsonConvert.DeserializeObject(jsonString); } diff --git a/Artemis/Artemis/Utilities/ExtensionMethods.cs b/Artemis/Artemis/Utilities/ExtensionMethods.cs index afa91d147..16d37edd9 100644 --- a/Artemis/Artemis/Utilities/ExtensionMethods.cs +++ b/Artemis/Artemis/Utilities/ExtensionMethods.cs @@ -24,5 +24,51 @@ namespace Artemis.Utilities // TODO: Convert ColorHelpers to ExtensionMethods #endregion + + #region Reflection + + /// + /// Gets a value by path + /// jheddings - http://stackoverflow.com/a/1954663/5015269 + /// + /// + /// Path, such as "TimeOfDay.Minutes" + /// + public static object GetPropValue(this object obj, string name) + { + foreach (var part in name.Split('.')) + { + if (obj == null) + return null; + + var type = obj.GetType(); + var info = type.GetProperty(part); + if (info == null) + return null; + + obj = info.GetValue(obj, null); + } + return obj; + } + + /// + /// Gets a value by path + /// jheddings - http://stackoverflow.com/a/1954663/5015269 + /// + /// + /// + /// + /// + public static T GetPropValue(this object obj, string name) + { + var retVal = GetPropValue(obj, name); + if (retVal == null) + return default(T); + + // throws InvalidCastException if types are incompatible + return (T) retVal; + } + + #endregion } } \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/GameState/GameStateWebServer.cs b/Artemis/Artemis/Utilities/GameState/GameStateWebServer.cs index dc1d062f3..44a39c4c4 100644 --- a/Artemis/Artemis/Utilities/GameState/GameStateWebServer.cs +++ b/Artemis/Artemis/Utilities/GameState/GameStateWebServer.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Net; -using System.Text; using System.Threading; using System.Windows.Forms; using Artemis.Settings; @@ -9,12 +8,18 @@ using Newtonsoft.Json; namespace Artemis.Utilities.GameState { + /// + /// Listens for JSON calls, parses them and raises an event. + /// Includes some code from https://github.com/rakijah/CSGSI + /// public class GameStateWebServer { public delegate void GameDataReceivedEventHandler( object sender, GameDataReceivedEventArgs gameDataReceivedEventArgs); - private readonly HttpListener _listener = new HttpListener(); + private readonly AutoResetEvent _waitForConnection = new AutoResetEvent(false); + + private HttpListener _listener; public GameStateWebServer() { @@ -31,64 +36,69 @@ namespace Artemis.Utilities.GameState if (Running) return; + Port = General.Default.GamestatePort; + + _listener = new HttpListener(); + _listener.Prefixes.Add($"http://localhost:{Port}/"); + var listenerThread = new Thread(ListenerRun); try { - _listener.Prefixes.Clear(); - Port = General.Default.GamestatePort; - _listener.Prefixes.Add($"http://localhost:{Port}/"); - _listener.Start(); } - catch (Exception) + catch (HttpListenerException) { - MessageBox.Show("Couldn't start the webserver. CS:GO/Dota2 effects won't work :c \n\nTry changing the port in Settings and restart Artemis."); + MessageBox.Show( + "Couldn't start the webserver. CS:GO/Dota2 effects won't work :c \n\nTry changing the port in Settings and restart Artemis."); } - ThreadPool.QueueUserWorkItem(o => - { - try - { - while (_listener.IsListening) - { - ThreadPool.QueueUserWorkItem(c => - { - var ctx = c as HttpListenerContext; - if (ctx == null) - return; - try - { - HandleRequest(ctx.Request); - var buf = Encoding.UTF8.GetBytes("ok"); - ctx.Response.ContentLength64 = buf.Length; - ctx.Response.OutputStream.Write(buf, 0, buf.Length); - } - catch - { - // ignored - } - finally - { - // always close the stream - ctx.Response.OutputStream.Close(); - } - }, _listener.GetContext()); - } - } - catch - { - // ignored - } - }); - Running = true; + listenerThread.Start(); + } + + private void ListenerRun() + { + while (Running) + { + _listener.BeginGetContext(HandleRequest, _listener); + _waitForConnection.WaitOne(); + _waitForConnection.Reset(); + } + } + + private void HandleRequest(IAsyncResult ar) + { + HttpListenerContext context = null; + try + { + context = _listener.EndGetContext(ar); + } + catch (ObjectDisposedException) + { + // Listener was Closed due to call of Stop(); + } + catch (HttpListenerException) + { + // Listener was Closed due to call of Stop(); + } + finally + { + _waitForConnection.Set(); + } + + if (context != null) + { + HandleRequest(context.Request); + context.Response.OutputStream.Close(); + } } public void Stop() { - _listener.Stop(); + _listener.Close(); + Running = false; } - private string HandleRequest(HttpListenerRequest request) + private void HandleRequest(HttpListenerRequest request) { object json; using (var reader = new StreamReader(request.InputStream, request.ContentEncoding)) @@ -99,7 +109,6 @@ namespace Artemis.Utilities.GameState if (json != null) OnGameDataReceived(new GameDataReceivedEventArgs(json)); - return JsonConvert.SerializeObject(json); } protected virtual void OnGameDataReceived(GameDataReceivedEventArgs e) diff --git a/Artemis/Artemis/Utilities/GeneralHelpers.cs b/Artemis/Artemis/Utilities/GeneralHelpers.cs index d426f7732..c1c6f9395 100644 --- a/Artemis/Artemis/Utilities/GeneralHelpers.cs +++ b/Artemis/Artemis/Utilities/GeneralHelpers.cs @@ -2,12 +2,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Linq; using System.Reflection; using System.Security.Principal; using System.Windows; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using static System.String; namespace Artemis.Utilities @@ -62,10 +59,7 @@ namespace Artemis.Utilities if (propertyNames.Length == 1 || value == null) return value; - else - { - return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", "")); - } + return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", "")); } public static List GenerateTypeMap(object o) => GenerateTypeMap(o.GetType().GetProperties()); @@ -102,7 +96,9 @@ namespace Artemis.Utilities if (friendlyName != Empty) list.Add(parent); - list.AddRange(GenerateTypeMap(propertyInfo.PropertyType.GetProperties(), path + $"{propertyInfo.Name}.")); + if (propertyInfo.PropertyType.Name != "String") + list.AddRange(GenerateTypeMap(propertyInfo.PropertyType.GetProperties(), + path + $"{propertyInfo.Name}.")); } return list; } diff --git a/Artemis/Artemis/Utilities/LayerDrawer.cs b/Artemis/Artemis/Utilities/LayerDrawer.cs index b0a4e00f1..f14079de8 100644 --- a/Artemis/Artemis/Utilities/LayerDrawer.cs +++ b/Artemis/Artemis/Utilities/LayerDrawer.cs @@ -1,7 +1,6 @@ using System; using System.Windows; using System.Windows.Media; -using System.Windows.Media.Imaging; using Artemis.Models.Profiles; namespace Artemis.Utilities @@ -112,46 +111,18 @@ namespace Artemis.Utilities } } - public BitmapImage GetThumbnail() + public DrawingImage GetThumbnail() { if (_layerModel.UserProps.Brush == null) return null; - _rectangle = new Rect(0, 0, 18, 18); + var visual = new DrawingVisual(); + using (var c = visual.RenderOpen()) + c.DrawRectangle(_layerModel.UserProps.Brush, new Pen(new SolidColorBrush(Colors.White), 1), + new Rect(0, 0, 18, 18)); - //var bitmap = new Bitmap(18, 18); - - //using (var g = Graphics.FromImage(bitmap)) - //{ - // g.SmoothingMode = SmoothingMode.AntiAlias; - // if (_layerModel.LayerType == LayerType.KeyboardEllipse) - // { - // g.FillEllipse(_layerModel.LayerUserProperties.Brush, _rectangle); - // g.DrawEllipse(new Pen(Color.Black, 1), 0, 0, 17, 17); - // } - // else if (_layerModel.LayerType == LayerType.KeyboardRectangle) - // { - // g.FillRectangle(_layerModel.LayerUserProperties.Brush, _rectangle); - // g.DrawRectangle(new Pen(Color.Black, 1), 0, 0, 17, 17); - // } - // else - // bitmap = Resources.folder; - //} - - //using (var memory = new MemoryStream()) - //{ - // bitmap.Save(memory, ImageFormat.Png); - // memory.Position = 0; - - // var bitmapImage = new BitmapImage(); - // bitmapImage.BeginInit(); - // bitmapImage.StreamSource = memory; - // bitmapImage.CacheOption = BitmapCacheOption.OnLoad; - // bitmapImage.EndInit(); - - // return bitmapImage; - //} - return null; + var image = new DrawingImage(visual.Drawing); + return image; } public void DrawRectangle(DrawingContext c) diff --git a/Artemis/Artemis/ViewModels/LayerEditor/LayerDynamicPropertiesViewModel.cs b/Artemis/Artemis/ViewModels/LayerEditor/LayerDynamicPropertiesViewModel.cs new file mode 100644 index 000000000..af66f2da7 --- /dev/null +++ b/Artemis/Artemis/ViewModels/LayerEditor/LayerDynamicPropertiesViewModel.cs @@ -0,0 +1,103 @@ +using System.ComponentModel; +using System.Linq; +using Artemis.Models.Profiles; +using Artemis.Utilities; +using Caliburn.Micro; + +namespace Artemis.ViewModels.LayerEditor +{ + public class LayerDynamicPropertiesViewModel : Screen + { + private LayerDynamicPropertiesModel _layerDynamicPropertiesModel; + private string _name; + private GeneralHelpers.PropertyCollection _selectedSource; + private GeneralHelpers.PropertyCollection _selectedTarget; + + public LayerDynamicPropertiesViewModel(string property, + BindableCollection dataModelProps, LayerModel layer) + { + // Look for the existing property model + LayerDynamicPropertiesModel = layer.LayerProperties.FirstOrDefault(lp => lp.LayerProperty == property); + if (LayerDynamicPropertiesModel == null) + { + // If it doesn't exist, create a new one + LayerDynamicPropertiesModel = new LayerDynamicPropertiesModel + { + LayerProperty = property, + LayerPropertyType = LayerPropertyType.None + }; + // Add it to the layer + layer.LayerProperties.Add(LayerDynamicPropertiesModel); + } + + Name = property + ":"; + Targets = new BindableCollection(); + Targets.AddRange(dataModelProps.Where(p => p.Type == "Int32")); + Sources = new BindableCollection(); + Sources.AddRange(dataModelProps.Where(p => p.Type == "Int32")); + + PropertyChanged += OnPropertyChanged; + } + + public string Name + { + get { return _name; } + set + { + if (value == _name) return; + _name = value; + NotifyOfPropertyChange(() => Name); + } + } + + public LayerDynamicPropertiesModel LayerDynamicPropertiesModel + { + get { return _layerDynamicPropertiesModel; } + set + { + if (Equals(value, _layerDynamicPropertiesModel)) return; + _layerDynamicPropertiesModel = value; + NotifyOfPropertyChange(() => LayerDynamicPropertiesModel); + } + } + + public BindableCollection Targets { get; set; } + + public GeneralHelpers.PropertyCollection SelectedTarget + { + get { return _selectedTarget; } + set + { + if (value.Equals(_selectedTarget)) return; + _selectedTarget = value; + NotifyOfPropertyChange(() => SelectedTarget); + } + } + + public BindableCollection Sources { get; set; } + + public GeneralHelpers.PropertyCollection SelectedSource + { + get { return _selectedSource; } + set + { + if (value.Equals(_selectedSource)) return; + _selectedSource = value; + NotifyOfPropertyChange(() => SelectedSource); + } + } + + /// + /// Updates the underlying model + /// + /// + /// + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "SelectedTarget") + LayerDynamicPropertiesModel.GameProperty = SelectedTarget.Path; + if (e.PropertyName == "SelectedSource") + LayerDynamicPropertiesModel.PercentageSource = SelectedSource.Path; + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/LayerEditorViewModel.cs b/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs similarity index 85% rename from Artemis/Artemis/ViewModels/LayerEditorViewModel.cs rename to Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs index 2a6ccd1b0..c4026d2d9 100644 --- a/Artemis/Artemis/ViewModels/LayerEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/LayerEditor/LayerEditorViewModel.cs @@ -1,149 +1,150 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Threading; -using System.Windows.Media; -using Artemis.DAL; -using Artemis.KeyboardProviders; -using Artemis.Models.Profiles; -using Artemis.Utilities; -using Artemis.ViewModels.LayerEditor; -using Caliburn.Micro; - -namespace Artemis.ViewModels -{ - public class LayerEditorViewModel : Screen - { - private readonly KeyboardProvider _activeKeyboard; - private readonly BackgroundWorker _previewWorker; - private readonly ProfileModel _profile; - private readonly bool _wasEnabled; - private LayerModel _layer; - private LayerPropertiesModel _proposedProperties; - - public LayerEditorViewModel(KeyboardProvider activeKeyboard, ProfileModel profile, LayerModel layer) - { - _activeKeyboard = activeKeyboard; - _profile = profile; - _wasEnabled = layer.Enabled; - Layer = layer; - - Layer.Enabled = false; - - DataModelProps = new BindableCollection(); - ProposedProperties = new LayerPropertiesModel(); - DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap()); - - LayerConditionVms = - new BindableCollection>( - layer.LayerConditions.Select(c => new LayerConditionViewModel(this, c, DataModelProps))); - - _previewWorker = new BackgroundWorker {WorkerSupportsCancellation = true}; - _previewWorker.DoWork += PreviewWorkerOnDoWork; - _previewWorker.RunWorkerAsync(); - - PropertyChanged += AnimationUiHandler; - PreSelect(); - } - - public BindableCollection DataModelProps { get; set; } - - public BindableCollection LayerTypes => new BindableCollection(); - - public BindableCollection> LayerConditionVms { get; set; } - - public LayerModel Layer - { - get { return _layer; } - set - { - if (Equals(value, _layer)) return; - _layer = value; - NotifyOfPropertyChange(() => Layer); - } - } - - public LayerPropertiesModel ProposedProperties - { - get { return _proposedProperties; } - set - { - if (Equals(value, _proposedProperties)) return; - _proposedProperties = value; - NotifyOfPropertyChange(() => ProposedProperties); - } - } - - public ImageSource LayerImage - { - get - { - var keyboardRect = _activeKeyboard.KeyboardRectangle(4); - - var visual = new DrawingVisual(); - using (var drawingContext = visual.RenderOpen()) - { - // Setup the DrawingVisual's size - drawingContext.PushClip(new RectangleGeometry(keyboardRect)); - drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); - - // Draw the layer - _layer.DrawPreview(drawingContext); - - // Remove the clip - drawingContext.Pop(); - } - var image = new DrawingImage(visual.Drawing); - - return image; - } - } - - private void PreviewWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs) - { - while (!_previewWorker.CancellationPending) - { - NotifyOfPropertyChange(() => LayerImage); - Thread.Sleep(1000/25); - } - } - - public void PreSelect() - { - GeneralHelpers.CopyProperties(ProposedProperties, Layer.UserProps); - } - - private void AnimationUiHandler(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName != "_proposedProperties") - return; - } - - public void AddCondition() - { - var condition = new LayerConditionModel(); - Layer.LayerConditions.Add(condition); - LayerConditionVms.Add(new LayerConditionViewModel(this, condition, DataModelProps)); - } - - public void Apply() - { - GeneralHelpers.CopyProperties(Layer.UserProps, ProposedProperties); - ProfileProvider.AddOrUpdate(_profile); - } - - public void DeleteCondition(LayerConditionViewModel layerConditionViewModel, - LayerConditionModel layerConditionModel) - { - LayerConditionVms.Remove(layerConditionViewModel); - Layer.LayerConditions.Remove(layerConditionModel); - } - - public override void CanClose(Action callback) - { - _previewWorker.CancelAsync(); - _layer.Enabled = _wasEnabled; - base.CanClose(callback); - } - } +using System; +using System.ComponentModel; +using System.Linq; +using System.Threading; +using System.Windows.Media; +using Artemis.KeyboardProviders; +using Artemis.Models.Profiles; +using Artemis.Utilities; +using Caliburn.Micro; + +namespace Artemis.ViewModels.LayerEditor +{ + public class LayerEditorViewModel : Screen + { + private readonly KeyboardProvider _activeKeyboard; + private readonly BackgroundWorker _previewWorker; + private readonly bool _wasEnabled; + private LayerModel _layer; + private LayerPropertiesModel _proposedProperties; + + public LayerEditorViewModel(KeyboardProvider activeKeyboard, LayerModel layer) + { + _activeKeyboard = activeKeyboard; + _wasEnabled = layer.Enabled; + + Layer = layer; + Layer.Enabled = false; + DataModelProps = new BindableCollection(); + ProposedProperties = new LayerPropertiesModel(); + DataModelProps.AddRange(GeneralHelpers.GenerateTypeMap()); + LayerConditionVms = new BindableCollection>( + layer.LayerConditions.Select(c => new LayerConditionViewModel(this, c, DataModelProps))); + HeightProperties = new LayerDynamicPropertiesViewModel("Height", DataModelProps, layer); + WidthProperties = new LayerDynamicPropertiesViewModel("Width", DataModelProps, layer); + OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", DataModelProps, layer); + + _previewWorker = new BackgroundWorker {WorkerSupportsCancellation = true}; + _previewWorker.DoWork += PreviewWorkerOnDoWork; + _previewWorker.RunWorkerAsync(); + + PropertyChanged += AnimationUiHandler; + PreSelect(); + } + + public LayerDynamicPropertiesViewModel OpacityProperties { get; set; } + + public LayerDynamicPropertiesViewModel WidthProperties { get; set; } + + public LayerDynamicPropertiesViewModel HeightProperties { get; set; } + + public BindableCollection DataModelProps { get; set; } + + public BindableCollection LayerTypes => new BindableCollection(); + + public BindableCollection> LayerConditionVms { get; set; } + + public LayerModel Layer + { + get { return _layer; } + set + { + if (Equals(value, _layer)) return; + _layer = value; + NotifyOfPropertyChange(() => Layer); + } + } + + public LayerPropertiesModel ProposedProperties + { + get { return _proposedProperties; } + set + { + if (Equals(value, _proposedProperties)) return; + _proposedProperties = value; + NotifyOfPropertyChange(() => ProposedProperties); + } + } + + public ImageSource LayerImage + { + get + { + var keyboardRect = _activeKeyboard.KeyboardRectangle(4); + + var visual = new DrawingVisual(); + using (var drawingContext = visual.RenderOpen()) + { + // Setup the DrawingVisual's size + drawingContext.PushClip(new RectangleGeometry(keyboardRect)); + drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect); + + // Draw the layer + _layer.DrawPreview(drawingContext); + + // Remove the clip + drawingContext.Pop(); + } + var image = new DrawingImage(visual.Drawing); + + return image; + } + } + + private void PreviewWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs) + { + while (!_previewWorker.CancellationPending) + { + NotifyOfPropertyChange(() => LayerImage); + Thread.Sleep(1000/25); + } + } + + public void PreSelect() + { + GeneralHelpers.CopyProperties(ProposedProperties, Layer.UserProps); + } + + private void AnimationUiHandler(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName != "_proposedProperties") + return; + } + + public void AddCondition() + { + var condition = new LayerConditionModel(); + Layer.LayerConditions.Add(condition); + LayerConditionVms.Add(new LayerConditionViewModel(this, condition, DataModelProps)); + } + + public void Apply() + { + GeneralHelpers.CopyProperties(Layer.UserProps, ProposedProperties); + } + + public void DeleteCondition(LayerConditionViewModel layerConditionViewModel, + LayerConditionModel layerConditionModel) + { + LayerConditionVms.Remove(layerConditionViewModel); + Layer.LayerConditions.Remove(layerConditionModel); + } + + public override void CanClose(Action callback) + { + _previewWorker.CancelAsync(); + _layer.Enabled = _wasEnabled; + base.CanClose(callback); + } + } } \ No newline at end of file diff --git a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs index 38f3dc6a6..25a33eb3a 100644 --- a/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs @@ -15,6 +15,7 @@ using Artemis.KeyboardProviders; using Artemis.Managers; using Artemis.Models; using Artemis.Models.Profiles; +using Artemis.ViewModels.LayerEditor; using Caliburn.Micro; using MahApps.Metro; @@ -78,6 +79,7 @@ namespace Artemis.ViewModels if (Equals(value, _selectedLayer)) return; _selectedLayer = value; NotifyOfPropertyChange(() => SelectedLayer); + NotifyOfPropertyChange(() => CanRemoveLayer); } } @@ -105,6 +107,7 @@ namespace Artemis.ViewModels NotifyOfPropertyChange(() => SelectedProfile); NotifyOfPropertyChange(() => CanAddLayer); + NotifyOfPropertyChange(() => CanRemoveLayer); } } @@ -169,6 +172,7 @@ namespace Artemis.ViewModels { if (ActiveKeyboard?.PreviewSettings.Image == null) return null; + ActiveKeyboard.PreviewSettings.Image.Save(memory, ImageFormat.Png); memory.Position = 0; @@ -186,6 +190,7 @@ namespace Artemis.ViewModels public PreviewSettings? PreviewSettings => ActiveKeyboard?.PreviewSettings; public bool CanAddLayer => _selectedProfile != null; + public bool CanRemoveLayer => _selectedProfile != null && _selectedLayer != null; private KeyboardProvider ActiveKeyboard => _mainManager.KeyboardManager.ActiveKeyboard; @@ -197,8 +202,13 @@ namespace Artemis.ViewModels private void PropertyChangeHandler(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName != "KeyboardPreview") - NotifyOfPropertyChange(() => KeyboardPreview); + if (e.PropertyName == "KeyboardPreview") + return; + + NotifyOfPropertyChange(() => KeyboardPreview); + + if (SelectedProfile != null) + ProfileProvider.AddOrUpdate(SelectedProfile); } private void LoadProfiles() @@ -247,7 +257,7 @@ namespace Artemis.ViewModels public void LayerEditor(LayerModel layer) { IWindowManager manager = new WindowManager(); - _editorVm = new LayerEditorViewModel(ActiveKeyboard, SelectedProfile, layer); + _editorVm = new LayerEditorViewModel(ActiveKeyboard, layer); dynamic settings = new ExpandoObject(); settings.Title = "Artemis | Edit " + layer.Name; @@ -272,6 +282,16 @@ namespace Artemis.ViewModels SelectedProfile.Layers.Remove(_selectedLayer); Layers.Remove(_selectedLayer); + + SelectedProfile.FixOrder(); + } + + public void RemoveLayer(LayerModel layer) + { + SelectedProfile.Layers.Remove(layer); + Layers.Remove(layer); + + SelectedProfile.FixOrder(); } public void LayerUp() @@ -324,8 +344,7 @@ namespace Artemis.ViewModels var hoverLayer = SelectedProfile.Layers.OrderBy(l => l.Order).Where(l => l.Enabled) .FirstOrDefault(l => l.UserProps.GetRect(1).Contains(x, y)); - if (hoverLayer != null) - SelectedLayer = hoverLayer; + SelectedLayer = hoverLayer; } public void MouseMoveKeyboardPreview(MouseEventArgs e) diff --git a/Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml b/Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml new file mode 100644 index 000000000..bf4cc9107 --- /dev/null +++ b/Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Artemis/Artemis/Views/LayerEditorView.xaml.cs b/Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml.cs similarity index 65% rename from Artemis/Artemis/Views/LayerEditorView.xaml.cs rename to Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml.cs index d34a94ff6..bbb08551e 100644 --- a/Artemis/Artemis/Views/LayerEditorView.xaml.cs +++ b/Artemis/Artemis/Views/LayerEditor/LayerDynamicPropertiesView.xaml.cs @@ -1,29 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; -using MahApps.Metro.Controls; -using ColorBox; - -namespace Artemis.Views -{ - /// - /// Interaction logic for LayerEditorView.xaml - /// - public partial class LayerEditorView : MetroWindow - { - public LayerEditorView() - { - InitializeComponent(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Artemis.Views.LayerEditor +{ + /// + /// Interaction logic for LayerDynamicPropertiesView.xaml + /// + public partial class LayerDynamicPropertiesView : UserControl + { + public LayerDynamicPropertiesView() + { + InitializeComponent(); + } + } +} diff --git a/Artemis/Artemis/Views/LayerEditorView.xaml b/Artemis/Artemis/Views/LayerEditor/LayerEditorView.xaml similarity index 72% rename from Artemis/Artemis/Views/LayerEditorView.xaml rename to Artemis/Artemis/Views/LayerEditor/LayerEditorView.xaml index 0d81496ca..7676dc8a4 100644 --- a/Artemis/Artemis/Views/LayerEditorView.xaml +++ b/Artemis/Artemis/Views/LayerEditor/LayerEditorView.xaml @@ -1,151 +1,141 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -