mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
UI polish
This commit is contained in:
parent
b01f9485ae
commit
eb59ddc816
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new layer with default settings to the profile
|
||||
/// </summary>
|
||||
/// <returns>The newly added layer</returns>
|
||||
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);
|
||||
|
||||
@ -24,6 +24,8 @@ namespace Artemis.Models.Profiles.Properties
|
||||
public double AnimationSpeed { get; set; }
|
||||
public string GifFile { get; set; }
|
||||
public List<DynamicPropertiesModel> DynamicProperties { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public double AnimationProgress { get; set; }
|
||||
|
||||
public Rect GetRect(int scale = 4)
|
||||
|
||||
@ -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<string> ShowInputDialog(string title, string message);
|
||||
public abstract Task<string> ShowInputDialog(string title, string message, MetroDialogSettings settings = null);
|
||||
|
||||
public abstract Task<bool?> ShowQuestionMessageBox(string title, string message);
|
||||
}
|
||||
|
||||
@ -75,12 +75,12 @@ namespace Artemis.Services
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<string> ShowInputDialog(string title, string message)
|
||||
public override Task<string> 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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<string> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes proper action when the selected keyboard is changed in the UI
|
||||
/// </summary>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -226,14 +232,14 @@ namespace Artemis.ViewModels.Profiles
|
||||
if (ProfileViewModel.SelectedLayer == null)
|
||||
return;
|
||||
|
||||
LayerEditor(ProfileViewModel.SelectedLayer);
|
||||
EditLayer(ProfileViewModel.SelectedLayer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a new LayerEditorView for the given layer
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer to open the view for</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -284,27 +307,58 @@ namespace Artemis.ViewModels.Profiles
|
||||
/// </summary>
|
||||
public void RemoveLayer()
|
||||
{
|
||||
if (SelectedProfile == null || ProfileViewModel.SelectedLayer == null)
|
||||
return;
|
||||
|
||||
SelectedProfile.Layers.Remove(ProfileViewModel.SelectedLayer);
|
||||
Layers.Remove(ProfileViewModel.SelectedLayer);
|
||||
|
||||
SelectedProfile.FixOrder();
|
||||
RemoveLayer(ProfileViewModel.SelectedLayer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given layer from the profile
|
||||
/// </summary>
|
||||
/// <param name="layer"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the currently selected layer and adds it to the profile, on top of the original
|
||||
/// </summary>
|
||||
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(() =>
|
||||
{
|
||||
|
||||
@ -158,10 +158,10 @@
|
||||
<StackPanel.ContextMenu>
|
||||
<ContextMenu
|
||||
cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Rename" />
|
||||
<MenuItem Header="Rename" cal:Message.Attach="RenameLayer($datacontext)" />
|
||||
<MenuItem Header="Duplicate" cal:Message.Attach="CloneLayer($datacontext)" />
|
||||
<MenuItem Header="Delete" cal:Message.Attach="RemoveLayerFromMenu($datacontext)" />
|
||||
<MenuItem Header="Properties" cal:Message.Attach="LayerEditor($datacontext)" />
|
||||
<MenuItem Header="Delete" cal:Message.Attach="RemoveLayer($datacontext)" />
|
||||
<MenuItem Header="Properties" cal:Message.Attach="EditLayer($datacontext)" />
|
||||
</ContextMenu>
|
||||
</StackPanel.ContextMenu>
|
||||
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" IsChecked="{Binding Enabled}" />
|
||||
@ -192,7 +192,7 @@
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button x:Name="EditLayer" VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}"
|
||||
Width="26" Height="26" HorizontalAlignment="Right" Margin="10,0,0,0" ToolTip="Rename layer"
|
||||
Width="26" Height="26" HorizontalAlignment="Right" Margin="10,0,0,0" ToolTip="Edit layer"
|
||||
IsEnabled="{Binding Path=LayerSelected}">
|
||||
<Button.Content>
|
||||
<Rectangle
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user