From eb59ddc8163a61b7b5de8e0632c6a8a1d9eb0f1e Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 30 May 2016 23:53:53 +0200 Subject: [PATCH] UI polish --- Artemis/Artemis/DAL/ProfileProvider.cs | 26 +++-- Artemis/Artemis/Models/Profiles/LayerModel.cs | 28 ++++- .../Artemis/Models/Profiles/ProfileModel.cs | 31 +---- .../Properties/KeyboardPropertiesModel.cs | 2 + Artemis/Artemis/Services/DialogService.cs | 3 +- .../Artemis/Services/MetroDialogService.cs | 4 +- .../Utilities/Layers/AnimationUpdater.cs | 9 +- .../Flyouts/FlyoutSettingsViewModel.cs | 43 ++++++- .../Profiles/LayerEditorViewModel.cs | 6 + .../Profiles/ProfileEditorViewModel.cs | 110 ++++++++++++++---- .../Views/Profiles/ProfileEditorView.xaml | 8 +- 11 files changed, 194 insertions(+), 76 deletions(-) diff --git a/Artemis/Artemis/DAL/ProfileProvider.cs b/Artemis/Artemis/DAL/ProfileProvider.cs index 6f1ce47c8..e6c5237ec 100644 --- a/Artemis/Artemis/DAL/ProfileProvider.cs +++ b/Artemis/Artemis/DAL/ProfileProvider.cs @@ -6,11 +6,13 @@ using System.Xml.Serialization; using Artemis.DeviceProviders; using Artemis.Models; using Artemis.Models.Profiles; +using NLog; namespace Artemis.DAL { public static class ProfileProvider { + private static Logger _logger = LogManager.GetCurrentClassLogger(); private static readonly string ProfileFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\profiles"; @@ -56,9 +58,12 @@ namespace Artemis.DAL Directory.CreateDirectory(path); var serializer = new XmlSerializer(typeof(ProfileModel)); - using (var file = new StreamWriter(path + $@"\{prof.Name}.xml")) + + // Could use a StreamWriter but should serializing fail this method doesn't ruin the existing XML file + using (var xml = new StringWriter()) { - serializer.Serialize(file, prof); + serializer.Serialize(xml, prof); + File.WriteAllText(path + $@"\{prof.Name}.xml", xml.ToString()); } } @@ -71,16 +76,23 @@ namespace Artemis.DAL var profilePaths = Directory.GetFiles(ProfileFolder, "*.xml", SearchOption.AllDirectories); // Parse the JSON files into objects and add them if they are valid - // TODO: Invalid file handling var deserializer = new XmlSerializer(typeof(ProfileModel)); foreach (var path in profilePaths) { - using (var file = new StreamReader(path)) + try { - var prof = (ProfileModel) deserializer.Deserialize(file); - if (prof.GameName?.Length > 1 && prof.KeyboardName?.Length > 1 && prof.Name?.Length > 1) - profiles.Add(prof); + using (var file = new StreamReader(path)) + { + var prof = (ProfileModel)deserializer.Deserialize(file); + if (prof.GameName?.Length > 1 && prof.KeyboardName?.Length > 1 && prof.Name?.Length > 1) + profiles.Add(prof); + } } + catch (InvalidOperationException e) + { + _logger.Error("Failed to load profile: {0} - {1}", path, e.InnerException.Message); + } + } return profiles; diff --git a/Artemis/Artemis/Models/Profiles/LayerModel.cs b/Artemis/Artemis/Models/Profiles/LayerModel.cs index 16a8afa72..524e62b94 100644 --- a/Artemis/Artemis/Models/Profiles/LayerModel.cs +++ b/Artemis/Artemis/Models/Profiles/LayerModel.cs @@ -59,12 +59,11 @@ namespace Artemis.Models.Profiles } else appliedProperties = GeneralHelpers.Clone(Properties); - + // Update animations on layer types that support them - if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif) && updateAnimations) + if ((LayerType == LayerType.Keyboard || LayerType == LayerType.KeyboardGif)) { - AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties, - (KeyboardPropertiesModel) appliedProperties); + AnimationUpdater.UpdateAnimation((KeyboardPropertiesModel) Properties, (KeyboardPropertiesModel) appliedProperties, updateAnimations); } switch (LayerType) @@ -195,6 +194,27 @@ namespace Artemis.Models.Profiles } #endregion + + 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 enum LayerType diff --git a/Artemis/Artemis/Models/Profiles/ProfileModel.cs b/Artemis/Artemis/Models/Profiles/ProfileModel.cs index 83e237b8f..58c30b6dd 100644 --- a/Artemis/Artemis/Models/Profiles/ProfileModel.cs +++ b/Artemis/Artemis/Models/Profiles/ProfileModel.cs @@ -28,6 +28,7 @@ namespace Artemis.Models.Profiles public string Name { get; set; } public string KeyboardName { get; set; } public string GameName { get; set; } + public bool IsDefault { get; set; } [XmlIgnore] public DrawingVisual DrawingVisual { get; set; } @@ -58,36 +59,6 @@ namespace Artemis.Models.Profiles } } - /// - /// Adds a new layer with default settings to the profile - /// - /// The newly added layer - public LayerModel AddLayer() - { - var layer = 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 - } - }; - - Layers.Add(layer); - FixOrder(); - - return layer; - } - public void FixOrder() { Layers.Sort(l => l.Order); diff --git a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs b/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs index 50398a3ad..3f02b5950 100644 --- a/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs +++ b/Artemis/Artemis/Models/Profiles/Properties/KeyboardPropertiesModel.cs @@ -24,6 +24,8 @@ namespace Artemis.Models.Profiles.Properties public double AnimationSpeed { get; set; } public string GifFile { get; set; } public List DynamicProperties { get; set; } + + [XmlIgnore] public double AnimationProgress { get; set; } public Rect GetRect(int scale = 4) diff --git a/Artemis/Artemis/Services/DialogService.cs b/Artemis/Artemis/Services/DialogService.cs index b5a312a7d..b051ab978 100644 --- a/Artemis/Artemis/Services/DialogService.cs +++ b/Artemis/Artemis/Services/DialogService.cs @@ -22,6 +22,7 @@ using System; using System.Threading.Tasks; +using MahApps.Metro.Controls.Dialogs; namespace Artemis.Services { @@ -41,7 +42,7 @@ namespace Artemis.Services public abstract bool ShowOpenDialog(out string path, string defaultExt, string filter, string initialDir = null); - public abstract Task ShowInputDialog(string title, string message); + public abstract Task ShowInputDialog(string title, string message, MetroDialogSettings settings = null); public abstract Task ShowQuestionMessageBox(string title, string message); } diff --git a/Artemis/Artemis/Services/MetroDialogService.cs b/Artemis/Artemis/Services/MetroDialogService.cs index 1f4a59c54..ba7328bff 100644 --- a/Artemis/Artemis/Services/MetroDialogService.cs +++ b/Artemis/Artemis/Services/MetroDialogService.cs @@ -75,12 +75,12 @@ namespace Artemis.Services } } - public override Task ShowInputDialog(string title, string message) + public override Task ShowInputDialog(string title, string message, MetroDialogSettings settings = null) { if (GetActiveWindow() == null) return null; - return GetActiveWindow().ShowInputAsync(title, message); + return GetActiveWindow().ShowInputAsync(title, message, settings); } public override bool ShowOpenDialog(out string path, string defaultExt, string filter, string initialDir = null) diff --git a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs b/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs index 9745bb90e..359260155 100644 --- a/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs +++ b/Artemis/Artemis/Utilities/Layers/AnimationUpdater.cs @@ -4,10 +4,11 @@ namespace Artemis.Utilities.Layers { public static class AnimationUpdater { - public static void UpdateAnimation(KeyboardPropertiesModel properties, KeyboardPropertiesModel appliedProperties) + public static void UpdateAnimation(KeyboardPropertiesModel properties, KeyboardPropertiesModel appliedProperties, bool updateAnimations) { const int scale = 4; var animateProperties = properties.Contain ? appliedProperties : properties; + appliedProperties.AnimationProgress = properties.AnimationProgress; var progress = appliedProperties.AnimationProgress; // Horizontal sliding @@ -40,8 +41,10 @@ namespace Artemis.Utilities.Layers } appliedProperties.AnimationProgress = progress; - // Store the animation progress in the actual model for the next frame - properties.AnimationProgress = progress; + + // 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/ViewModels/Flyouts/FlyoutSettingsViewModel.cs b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs index 433fc4e54..d965ff689 100644 --- a/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs +++ b/Artemis/Artemis/ViewModels/Flyouts/FlyoutSettingsViewModel.cs @@ -6,7 +6,10 @@ using Artemis.Managers; using Artemis.Settings; using Caliburn.Micro; using MahApps.Metro.Controls; -using Ninject.Extensions.Logging; +using NLog; +using NLog.Config; +using NLog.Targets; +using ILogger = Ninject.Extensions.Logging.ILogger; namespace Artemis.ViewModels.Flyouts { @@ -15,6 +18,7 @@ namespace Artemis.ViewModels.Flyouts { private readonly ILogger _logger; private string _activeEffectName; + private bool _enableDebug; private GeneralSettings _generalSettings; private string _selectedKeyboardProvider; @@ -29,6 +33,20 @@ namespace Artemis.ViewModels.Flyouts PropertyChanged += KeyboardUpdater; events.Subscribe(this); + ApplyLogging(); + } + + public MainManager MainManager { get; set; } + + public bool EnableDebug + { + get { return _enableDebug; } + set + { + if (value == _enableDebug) return; + _enableDebug = value; + NotifyOfPropertyChange(() => EnableDebug); + } } public GeneralSettings GeneralSettings @@ -42,8 +60,6 @@ namespace Artemis.ViewModels.Flyouts } } - public MainManager MainManager { get; set; } - public BindableCollection KeyboardProviders { get @@ -100,6 +116,27 @@ namespace Artemis.ViewModels.Flyouts NotifyOfPropertyChange(() => Enabled); } + // TODO https://github.com/ninject/Ninject.Extensions.Logging/issues/35 + private void ApplyLogging() + { + var c = NLog.LogManager.Configuration; + var file = c.FindTargetByName("file") as FileTarget; + if (file == null) + return; + + var rule = c.LoggingRules.FirstOrDefault(r => r.Targets.Contains(file)); + if (rule == null) + return; + + if (EnableDebug) + rule.EnableLoggingForLevel(LogLevel.Debug); + else + rule.DisableLoggingForLevel(LogLevel.Debug); + + NLog.LogManager.ReconfigExistingLoggers(); + _logger.Info("Set debug logging to: {0}", EnableDebug); + } + /// /// Takes proper action when the selected keyboard is changed in the UI /// diff --git a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs index 28257dcc9..c15bbb9a7 100644 --- a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs @@ -120,6 +120,9 @@ namespace Artemis.ViewModels.Profiles if (e.PropertyName != "LayerType") return; + // Store the brush in case the user wants to reuse it + var oldBrush = LayerPropertiesViewModel?.GetAppliedProperties().Brush; + // Update the model if (Layer.LayerType != LayerType) { @@ -127,6 +130,9 @@ namespace Artemis.ViewModels.Profiles Layer.SetupProperties(); } + if (oldBrush != null) + Layer.Properties.Brush = oldBrush; + // Update the KeyboardPropertiesViewModel if it's being used var model = LayerPropertiesViewModel as KeyboardPropertiesViewModel; if (model != null) diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs index b2ffce466..0b3dabf26 100644 --- a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs +++ b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs @@ -17,6 +17,7 @@ using Artemis.Styles.DropTargetAdorners; using Artemis.Utilities; using Caliburn.Micro; using GongSolutions.Wpf.DragDrop; +using MahApps.Metro.Controls.Dialogs; using Ninject; using DragDropEffects = System.Windows.DragDropEffects; using IDropTarget = GongSolutions.Wpf.DragDrop.IDropTarget; @@ -191,6 +192,12 @@ namespace Artemis.ViewModels.Profiles if (e.PropertyName == "KeyboardPreview") return; + if (e.PropertyName == "SelectedLayer") + { + NotifyOfPropertyChange(() => LayerSelected); + return; + } + if (SelectedProfile != null) ProfileProvider.AddOrUpdate(SelectedProfile); @@ -203,9 +210,8 @@ namespace Artemis.ViewModels.Profiles Layers.Clear(); if (SelectedProfile != null) Layers.AddRange(SelectedProfile.Layers); - // Update booleans + NotifyOfPropertyChange(() => ProfileSelected); - NotifyOfPropertyChange(() => LayerSelected); } /// @@ -226,14 +232,14 @@ namespace Artemis.ViewModels.Profiles if (ProfileViewModel.SelectedLayer == null) return; - LayerEditor(ProfileViewModel.SelectedLayer); + EditLayer(ProfileViewModel.SelectedLayer); } /// /// Opens a new LayerEditorView for the given layer /// /// The layer to open the view for - public void LayerEditor(LayerModel layer) + public void EditLayer(LayerModel layer) { IWindowManager manager = new WindowManager(); _editorVm = new LayerEditorViewModel(_gameModel.GameDataModel, layer); @@ -273,10 +279,27 @@ namespace Artemis.ViewModels.Profiles if (SelectedProfile == null) return; - var layer = SelectedProfile.AddLayer(); - Layers.Add(layer); + // Create a new layer + var layer = LayerModel.CreateLayer(); - ProfileViewModel.SelectedLayer = layer; + // If there is a selected layer and it has a parent, bind the new layer to it + if (ProfileViewModel.SelectedLayer?.Parent != null) + { + layer.Order = ProfileViewModel.SelectedLayer.Order + 1; + ProfileViewModel.SelectedLayer.Parent.Children.Add(layer); + ProfileViewModel.SelectedLayer.Parent.FixOrder(); + } + else + { + // If there was no parent but there is a layer selected, put it below the selected layer + if (ProfileViewModel.SelectedLayer != null) + layer.Order = ProfileViewModel.SelectedLayer.Order + 1; + + SelectedProfile.Layers.Add(layer); + SelectedProfile.FixOrder(); + } + + UpdateLayerList(layer); } /// @@ -284,27 +307,58 @@ namespace Artemis.ViewModels.Profiles /// public void RemoveLayer() { - if (SelectedProfile == null || ProfileViewModel.SelectedLayer == null) - return; - - SelectedProfile.Layers.Remove(ProfileViewModel.SelectedLayer); - Layers.Remove(ProfileViewModel.SelectedLayer); - - SelectedProfile.FixOrder(); + RemoveLayer(ProfileViewModel.SelectedLayer); } /// /// Removes the given layer from the profile /// /// - public void RemoveLayerFromMenu(LayerModel layer) + public void RemoveLayer(LayerModel layer) { - SelectedProfile.Layers.Remove(layer); - Layers.Remove(layer); + if (layer == null) + return; - SelectedProfile.FixOrder(); + if (layer.Parent != null) + { + var parent = layer.Parent; + layer.Parent.Children.Remove(layer); + parent.FixOrder(); + } + else if (layer.Profile != null) + { + var profile = layer.Profile; + layer.Profile.Layers.Remove(layer); + profile.FixOrder(); + } + + // Extra cleanup in case of a wonky layer that has no parent + if (SelectedProfile.Layers.Contains(layer)) + SelectedProfile.Layers.Remove(layer); + + UpdateLayerList(null); } + public async void RenameLayer(LayerModel layer) + { + if (layer == null) + return; + + var newName = + await + DialogService.ShowInputDialog("Rename layer", "Please enter a name for the layer", + new MetroDialogSettings {DefaultText = layer.Name}); + // Null when the user cancelled + if (string.IsNullOrEmpty(newName)) + return; + + layer.Name = newName; + UpdateLayerList(layer); + } + + /// + /// Clones the currently selected layer and adds it to the profile, on top of the original + /// public void CloneLayer() { if (ProfileViewModel.SelectedLayer == null) @@ -320,11 +374,20 @@ namespace Artemis.ViewModels.Profiles public void CloneLayer(LayerModel layer) { var clone = GeneralHelpers.Clone(layer); - clone.Order = layer.Order - 1; - SelectedProfile.Layers.Add(clone); - Layers.Add(clone); + clone.Order++; - SelectedProfile.FixOrder(); + if (layer.Parent != null) + { + layer.Parent.Children.Add(clone); + layer.Parent.FixOrder(); + } + else if (layer.Profile != null) + { + layer.Profile.Layers.Add(clone); + layer.Profile.FixOrder(); + } + + UpdateLayerList(clone); } private void UpdateLayerList(LayerModel selectModel) @@ -334,6 +397,9 @@ namespace Artemis.ViewModels.Profiles if (SelectedProfile != null) Layers.AddRange(SelectedProfile.Layers); + if (selectModel == null) + return; + // A small delay to allow the profile list to rebuild Task.Factory.StartNew(() => { diff --git a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml b/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml index 9b893fdf3..4e915fa00 100644 --- a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml +++ b/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml @@ -158,10 +158,10 @@ - + - - + + @@ -192,7 +192,7 @@