1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge branch 'development'

This commit is contained in:
Robert 2021-06-09 22:12:28 +02:00
commit a21f1a5555
46 changed files with 413 additions and 255 deletions

View File

@ -99,11 +99,12 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public override void Reset() public override void Reset()
{ {
DisplayConditionMet = false; UpdateDisplayCondition();
Timeline.JumpToEnd();
foreach (ProfileElement child in Children) if (DisplayConditionMet)
child.Reset(); Timeline.JumpToStart();
else
Timeline.JumpToEnd();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -202,13 +203,13 @@ namespace Artemis.Core
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended)) foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint); baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint);
canvas.SaveLayer(layerPaint);
canvas.Translate(Bounds.Left - basePosition.X, Bounds.Top - basePosition.Y);
// No point rendering if the alpha was set to zero by one of the effects // No point rendering if the alpha was set to zero by one of the effects
if (layerPaint.Color.Alpha == 0) if (layerPaint.Color.Alpha == 0)
return; return;
canvas.SaveLayer(layerPaint);
canvas.Translate(Bounds.Left - basePosition.X, Bounds.Top - basePosition.Y);
// Iterate the children in reverse because the first layer must be rendered last to end up on top // Iterate the children in reverse because the first layer must be rendered last to end up on top
for (int index = Children.Count - 1; index > -1; index--) for (int index = Children.Count - 1; index > -1; index--)
Children[index].Render(canvas, new SKPointI(Bounds.Left, Bounds.Top)); Children[index].Render(canvas, new SKPointI(Bounds.Left, Bounds.Top));
@ -243,6 +244,7 @@ namespace Artemis.Core
internal override void Load() internal override void Load()
{ {
ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups); ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
Reset();
// Load child folders // Load child folders
foreach (FolderEntity childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId)) foreach (FolderEntity childFolder in Profile.ProfileEntity.Folders.Where(f => f.ParentId == EntityId))

View File

@ -172,6 +172,9 @@ namespace Artemis.Core
Disposed = true; Disposed = true;
LayerBrushStore.LayerBrushAdded -= LayerBrushStoreOnLayerBrushAdded;
LayerBrushStore.LayerBrushRemoved -= LayerBrushStoreOnLayerBrushRemoved;
// Brush first in case it depends on any of the other disposables during it's own disposal // Brush first in case it depends on any of the other disposables during it's own disposal
_layerBrush?.Dispose(); _layerBrush?.Dispose();
_general.Dispose(); _general.Dispose();
@ -326,7 +329,11 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public override void Reset() public override void Reset()
{ {
DisplayConditionMet = false; UpdateDisplayCondition();
if (DisplayConditionMet)
Timeline.JumpToStart();
else
Timeline.JumpToEnd(); Timeline.JumpToEnd();
} }
@ -355,8 +362,6 @@ namespace Artemis.Core
if (Enabled) if (Enabled)
return; return;
Debug.WriteLine($"Enabling {this}");
LayerBrush?.InternalEnable(); LayerBrush?.InternalEnable();
foreach (BaseLayerEffect baseLayerEffect in LayerEffects) foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalEnable(); baseLayerEffect.InternalEnable();
@ -370,8 +375,6 @@ namespace Artemis.Core
if (!Enabled) if (!Enabled)
return; return;
Debug.WriteLine($"Disabling {this}");
LayerBrush?.InternalDisable(); LayerBrush?.InternalDisable();
foreach (BaseLayerEffect baseLayerEffect in LayerEffects) foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalDisable(); baseLayerEffect.InternalDisable();

View File

@ -125,6 +125,7 @@ namespace Artemis.Core
bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet; bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet;
if (DisplayCondition != null && DisplayCondition.ContainsEvents) if (DisplayCondition != null && DisplayCondition.ContainsEvents)
stickToMainSegment = false; stickToMainSegment = false;
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment); Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
namespace Artemis.Core namespace Artemis.Core

View File

@ -61,7 +61,6 @@ namespace Artemis.Core.Modules
} }
} }
/// <summary> /// <summary>
/// For internal use only, please use <see cref="Module{T}" />. /// For internal use only, please use <see cref="Module{T}" />.
/// </summary> /// </summary>
@ -81,18 +80,13 @@ namespace Artemis.Core.Modules
public IReadOnlyCollection<(DefaultCategoryName, string)> DefaultProfilePaths => _defaultProfilePaths.AsReadOnly(); public IReadOnlyCollection<(DefaultCategoryName, string)> DefaultProfilePaths => _defaultProfilePaths.AsReadOnly();
/// <summary> /// <summary>
/// The modules display name that's shown in the menu /// A list of activation requirements
/// </summary>
public string? DisplayName { get; protected set; }
/// <summary>
/// The modules display icon that's shown in the UI accepts:
/// <para> /// <para>
/// Either set to the name of a Material Icon see (<see href="https://materialdesignicons.com" /> for available /// If this list is not <see langword="null" /> and not empty <see cref="IsAlwaysAvailable" /> becomes
/// icons) or set to a path relative to the plugin folder pointing to a .svg file /// <see langword="false" /> and the data of this module is only available to profiles specifically targeting it.
/// </para> /// </para>
/// </summary> /// </summary>
public string? DisplayIcon { get; set; } public abstract List<IModuleActivationRequirement>? ActivationRequirements { get; }
/// <summary> /// <summary>
/// Gets whether this module is activated. A module can only be active while its <see cref="ActivationRequirements" /> /// Gets whether this module is activated. A module can only be active while its <see cref="ActivationRequirements" />
@ -114,12 +108,6 @@ namespace Artemis.Core.Modules
/// </summary> /// </summary>
public bool UpdateDuringActivationOverride { get; protected set; } public bool UpdateDuringActivationOverride { get; protected set; }
/// <summary>
/// A list of activation requirements
/// <para>Note: if empty the module is always activated</para>
/// </summary>
public List<IModuleActivationRequirement> ActivationRequirements { get; } = new();
/// <summary> /// <summary>
/// Gets or sets the activation requirement mode, defaults to <see cref="ActivationRequirementType.Any" /> /// Gets or sets the activation requirement mode, defaults to <see cref="ActivationRequirementType.Any" />
/// </summary> /// </summary>
@ -133,7 +121,7 @@ namespace Artemis.Core.Modules
/// <see langword="false" /> /// <see langword="false" />
/// </para> /// </para>
/// </summary> /// </summary>
public bool IsAlwaysAvailable => ActivationRequirements.Count == 0; public bool IsAlwaysAvailable => ActivationRequirements == null || ActivationRequirements.Count == 0;
/// <summary> /// <summary>
/// Gets whether updating this module is currently allowed /// Gets whether updating this module is currently allowed
@ -181,12 +169,12 @@ namespace Artemis.Core.Modules
/// <returns>The evaluated result of the activation requirements</returns> /// <returns>The evaluated result of the activation requirements</returns>
public bool EvaluateActivationRequirements() public bool EvaluateActivationRequirements()
{ {
if (!ActivationRequirements.Any()) if (IsAlwaysAvailable)
return true; return true;
if (ActivationRequirementMode == ActivationRequirementType.All) if (ActivationRequirementMode == ActivationRequirementType.All)
return ActivationRequirements.All(r => r.Evaluate()); return ActivationRequirements!.All(r => r.Evaluate());
if (ActivationRequirementMode == ActivationRequirementType.Any) if (ActivationRequirementMode == ActivationRequirementType.Any)
return ActivationRequirements.Any(r => r.Evaluate()); return ActivationRequirements!.Any(r => r.Evaluate());
return false; return false;
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.DeviceProviders; using Artemis.Core.DeviceProviders;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
@ -132,6 +131,19 @@ namespace Artemis.Core
internal set => SetAndNotify(ref _instance, value); internal set => SetAndNotify(ref _instance, value);
} }
/// <summary>
/// Gets a string representing either a full path pointing to an svg or the markdown icon
/// </summary>
public string? ResolvedIcon
{
get
{
if (Icon == null)
return null;
return Icon.EndsWith(".svg") ? Plugin.ResolveRelativePath(Icon) : Icon;
}
}
internal PluginFeatureEntity Entity { get; } internal PluginFeatureEntity Entity { get; }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -152,6 +152,19 @@ namespace Artemis.Core
internal set => SetAndNotify(ref _plugin, value); internal set => SetAndNotify(ref _plugin, value);
} }
/// <summary>
/// Gets a string representing either a full path pointing to an svg or the markdown icon
/// </summary>
public string? ResolvedIcon
{
get
{
if (Icon == null)
return null;
return Icon.EndsWith(".svg") ? Plugin.ResolveRelativePath(Icon) : Icon;
}
}
internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}"; internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}";
/// <inheritdoc /> /// <inheritdoc />

View File

@ -26,10 +26,29 @@ namespace Artemis.Core
ShowProgressBar = true; ShowProgressBar = true;
} }
/// <summary>
/// Creates a new instance of a copy folder action
/// </summary>
/// <param name="name">The name of the action</param>
/// <param name="urlFunction">A function returning the URL to download</param>
/// <param name="fileName">The target file to save as (will be created if needed)</param>
public DownloadFileAction(string name, Func<Task<string>> urlFunction, string fileName) : base(name)
{
UrlFunction = urlFunction ?? throw new ArgumentNullException(nameof(urlFunction));
FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
ShowProgressBar = true;
}
/// <summary> /// <summary>
/// Gets the source URL to download /// Gets the source URL to download
/// </summary> /// </summary>
public string Url { get; } public string? Url { get; }
/// <summary>
/// Gets the function returning the URL to download
/// </summary>
public Func<Task<string>>? UrlFunction { get; }
/// <summary> /// <summary>
/// Gets the target file to save as (will be created if needed) /// Gets the target file to save as (will be created if needed)
@ -41,19 +60,25 @@ namespace Artemis.Core
{ {
using HttpClient client = new(); using HttpClient client = new();
await using FileStream destinationStream = new(FileName, FileMode.OpenOrCreate); await using FileStream destinationStream = new(FileName, FileMode.OpenOrCreate);
string? url = Url;
if (url is null)
{
Status = "Retrieving download URL";
url = await UrlFunction!();
}
void ProgressOnProgressReported(object? sender, EventArgs e) void ProgressOnProgressReported(object? sender, EventArgs e)
{ {
if (Progress.ProgressPerSecond != 0) if (Progress.ProgressPerSecond != 0)
Status = $"Downloading {Url} - {Progress.ProgressPerSecond.Bytes().Humanize("#.##")}/sec"; Status = $"Downloading {url} - {Progress.ProgressPerSecond.Bytes().Humanize("#.##")}/sec";
else else
Status = $"Downloading {Url}"; Status = $"Downloading {url}";
} }
Progress.ProgressReported += ProgressOnProgressReported; Progress.ProgressReported += ProgressOnProgressReported;
// Get the http headers first to examine the content length // Get the http headers first to examine the content length
using HttpResponseMessage response = await client.GetAsync(Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
await using Stream download = await response.Content.ReadAsStreamAsync(cancellationToken); await using Stream download = await response.Content.ReadAsStreamAsync(cancellationToken);
long? contentLength = response.Content.Headers.ContentLength; long? contentLength = response.Content.Headers.ContentLength;

View File

@ -529,7 +529,7 @@ namespace Artemis.Core.Services
string targetDirectory = pluginInfo.PreferredPluginDirectory; string targetDirectory = pluginInfo.PreferredPluginDirectory;
if (Directory.Exists(Path.Combine(pluginDirectory.FullName, targetDirectory))) if (Directory.Exists(Path.Combine(pluginDirectory.FullName, targetDirectory)))
throw new ArtemisPluginException($"A directory for this plugin already exists {Path.Combine(pluginDirectory.FullName, targetDirectory)}"); Directory.Delete(Path.Combine(pluginDirectory.FullName, targetDirectory), true);
// Extract everything in the same archive directory to the unique plugin directory // Extract everything in the same archive directory to the unique plugin directory
DirectoryInfo directoryInfo = new(Path.Combine(pluginDirectory.FullName, targetDirectory)); DirectoryInfo directoryInfo = new(Path.Combine(pluginDirectory.FullName, targetDirectory));

View File

@ -1,4 +1,5 @@
using Artemis.Core.LayerEffects; using System.Collections.Generic;
using Artemis.Core.LayerEffects;
using Artemis.Core.Modules; using Artemis.Core.Modules;
namespace Artemis.Core namespace Artemis.Core
@ -14,6 +15,8 @@ namespace Artemis.Core
IsEnabled = true; IsEnabled = true;
} }
public override List<IModuleActivationRequirement>? ActivationRequirements => null;
public override void Enable() public override void Enable()
{ {
} }

View File

@ -41,6 +41,7 @@ namespace Artemis.Storage.Repositories
public ProfileCategoryEntity IsUnique(string name, Guid? id) public ProfileCategoryEntity IsUnique(string name, Guid? id)
{ {
name = name.Trim();
if (id == null) if (id == null)
return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name); return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name);
return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name && p.Id != id.Value); return _repository.FirstOrDefault<ProfileCategoryEntity>(p => p.Name == name && p.Id != id.Value);

View File

@ -53,7 +53,6 @@ namespace Artemis.UI.Shared
// Run an update timer at 25 fps // Run an update timer at 25 fps
_timer = new Timer(40); _timer = new Timer(40);
_timer.Elapsed += TimerOnTick;
MouseLeftButtonUp += OnMouseLeftButtonUp; MouseLeftButtonUp += OnMouseLeftButtonUp;
Loaded += OnLoaded; Loaded += OnLoaded;
@ -158,7 +157,8 @@ namespace Artemis.UI.Shared
/// </param> /// </param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) _timer.Stop(); if (disposing)
_timer.Dispose();
} }
@ -191,6 +191,7 @@ namespace Artemis.UI.Shared
private void OnUnloaded(object? sender, RoutedEventArgs e) private void OnUnloaded(object? sender, RoutedEventArgs e)
{ {
_timer.Stop(); _timer.Stop();
_timer.Elapsed -= TimerOnTick;
if (_oldDevice != null) if (_oldDevice != null)
{ {
@ -222,6 +223,7 @@ namespace Artemis.UI.Shared
private void OnLoaded(object? sender, RoutedEventArgs e) private void OnLoaded(object? sender, RoutedEventArgs e)
{ {
_timer.Start(); _timer.Start();
_timer.Elapsed += TimerOnTick;
} }
private void TimerOnTick(object? sender, EventArgs e) private void TimerOnTick(object? sender, EventArgs e)

View File

@ -11,6 +11,7 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources> <UserControl.Resources>
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter" /> <shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter" />
<shared:StreamToSvgImageConverter x:Key="StreamToSvgImageConverter" />
</UserControl.Resources> </UserControl.Resources>
<ContentControl> <ContentControl>
<ContentControl.Style> <ContentControl.Style>
@ -47,7 +48,8 @@
<Setter Property="ContentTemplate"> <Setter Property="ContentTemplate">
<Setter.Value> <Setter.Value>
<DataTemplate> <DataTemplate>
<svgc:SvgViewbox StreamSource="{Binding ConfigurationIcon.FileIcon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" <Image
Source="{Binding ConfigurationIcon.FileIcon, Converter={StaticResource StreamToSvgImageConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.BitmapScalingMode="HighQuality"
Width="Auto" Width="Auto"
Height="Auto" /> Height="Auto" />

View File

@ -8,7 +8,7 @@ namespace Artemis.UI.Shared
{ {
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// Converts <see cref="T:Stream" /> into <see cref="T:BitmapImage" />. /// Converts bitmap file in the form of a <see cref="T:Stream" /> into <see cref="T:BitmapImage" />.
/// </summary> /// </summary>
[ValueConversion(typeof(Stream), typeof(BitmapImage))] [ValueConversion(typeof(Stream), typeof(BitmapImage))]
public class StreamToBitmapImageConverter : IValueConverter public class StreamToBitmapImageConverter : IValueConverter

View File

@ -0,0 +1,48 @@
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace Artemis.UI.Shared
{
/// <inheritdoc />
/// <summary>
/// Converts SVG file in the form of a <see cref="T:Stream" /> into <see cref="T:BitmapImage" />.
/// </summary>
[ValueConversion(typeof(Stream), typeof(BitmapImage))]
public class StreamToSvgImageConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not Stream stream)
return null;
stream.Position = 0;
StreamSvgConverter converter = new(new WpfDrawingSettings());
using MemoryStream imageStream = new();
converter.Convert(stream, imageStream);
BitmapImage selectedBitmap = new();
selectedBitmap.BeginInit();
selectedBitmap.StreamSource = imageStream;
selectedBitmap.CacheOption = BitmapCacheOption.OnLoad;
selectedBitmap.EndInit();
selectedBitmap.Freeze();
stream.Position = 0;
return selectedBitmap;
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

View File

@ -1,43 +0,0 @@
using System;
using System.IO;
using Artemis.Core;
using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Shared
{
/// <summary>
/// Provides utilities for UI-related plugin tasks
/// </summary>
public static class PluginUtilities
{
/// <summary>
/// Transforms the provided icon so that it is usable by the <see cref="ArtemisIcon" /> control
/// </summary>
/// <param name="plugin">The plugin the icon belongs to</param>
/// <param name="icon">
/// The icon, may be a string representation of a <see cref="PackIconKind" /> or a relative path
/// pointing to a .svg file
/// </param>
/// <returns></returns>
public static object GetPluginIcon(Plugin plugin, string icon)
{
if (icon == null)
return PackIconKind.QuestionMarkCircle;
// Icon is provided as a path
if (icon.EndsWith(".svg"))
{
string iconPath = plugin.ResolveRelativePath(icon);
if (!File.Exists(iconPath))
return PackIconKind.QuestionMarkCircle;
return iconPath;
}
// Icon is provided as string to avoid having to reference MaterialDesignThemes
bool parsedIcon = Enum.TryParse(icon, true, out PackIconKind iconEnum);
if (parsedIcon == false)
iconEnum = PackIconKind.QuestionMarkCircle;
return iconEnum;
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace Artemis.UI.Converters
{
public class ValuesAdditionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Where(v => v is double).Cast<double>().Sum();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -11,7 +11,7 @@
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" /> <converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources> </UserControl.Resources>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<ComboBox Width="132" <ComboBox Width="132"
Margin="0 2" Margin="0 2"
Padding="0 -1" Padding="0 -1"
@ -22,6 +22,6 @@
<ComboBoxItem Content="True" IsSelected="{Binding InputValue}" /> <ComboBoxItem Content="True" IsSelected="{Binding InputValue}" />
<ComboBoxItem Content="False" IsSelected="{Binding InputValue, Converter={StaticResource InverseBooleanConverter}}" /> <ComboBoxItem Content="False" IsSelected="{Binding InputValue, Converter={StaticResource InverseBooleanConverter}}" />
</ComboBox> </ComboBox>
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -18,7 +18,7 @@
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<ComboBox Width="132" <ComboBox Width="132"
Margin="0 2" Margin="0 2"
Padding="0 -1" Padding="0 -1"
@ -31,6 +31,6 @@
ItemTemplateSelector="{dataTemplateSelectors:ComboBoxTemplateSelector ItemTemplateSelector="{dataTemplateSelectors:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource SimpleLayerBrushDescriptorTemplate}, SelectedItemTemplate={StaticResource SimpleLayerBrushDescriptorTemplate},
DropdownItemsTemplate={StaticResource ExtendedLayerBrushDescriptorTemplate}}" /> DropdownItemsTemplate={StaticResource ExtendedLayerBrushDescriptorTemplate}}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -10,7 +10,7 @@
d:DesignHeight="25" d:DesignWidth="800" d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:ColorGradientPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:ColorGradientPropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:GradientPicker Width="132" <shared:GradientPicker Width="132"
Margin="0 2" Margin="0 2"
Padding="0 -1" Padding="0 -1"
@ -18,6 +18,6 @@
DialogClosed="{s:Action DialogClosed}" DialogClosed="{s:Action DialogClosed}"
DialogHost="PropertyTreeDialogHost" DialogHost="PropertyTreeDialogHost"
ToolTip="Click to edit gradient colors" /> ToolTip="Click to edit gradient colors" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -7,7 +7,7 @@
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<ComboBox Width="132" <ComboBox Width="132"
Margin="0 2" Margin="0 2"
Padding="0 -1" Padding="0 -1"
@ -19,6 +19,6 @@
SelectedValuePath="Value" SelectedValuePath="Value"
DisplayMemberPath="Description" DisplayMemberPath="Description"
SelectedValue="{Binding Path=InputValue}" /> SelectedValue="{Binding Path=InputValue}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -11,7 +11,7 @@
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:FloatPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:FloatPropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat Value="{Binding InputValue}" <shared:DraggableFloat Value="{Binding InputValue}"
materialDesign:ValidationAssist.UsePopup="True" materialDesign:ValidationAssist.UsePopup="True"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -20,6 +20,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEnabled}" /> IsEnabled="{Binding IsEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,11 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:FloatRangePropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:FloatRangePropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat ToolTip="Start" <shared:DraggableFloat ToolTip="Start"
Value="{Binding Start}" Value="{Binding Start}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -27,6 +28,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEndEnabled}" /> IsEnabled="{Binding IsEndEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -6,11 +6,12 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:IntPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:IntPropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat Value="{Binding InputValue}" <shared:DraggableFloat Value="{Binding InputValue}"
materialDesign:ValidationAssist.UsePopup="True" materialDesign:ValidationAssist.UsePopup="True"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -19,6 +20,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEnabled}" /> IsEnabled="{Binding IsEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,11 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:IntRangePropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:IntRangePropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat ToolTip="Start" <shared:DraggableFloat ToolTip="Start"
Value="{Binding Start}" Value="{Binding Start}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -27,6 +28,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEndEnabled}" /> IsEnabled="{Binding IsEndEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="800" d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}">
@ -12,7 +13,7 @@
<artemis:SKColorToColorConverter x:Key="SKColorToColorConverter" /> <artemis:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources> </UserControl.Resources>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<artemis:ColorPicker Width="132" <artemis:ColorPicker Width="132"
Margin="0 -2 0 3" Margin="0 -2 0 3"
Padding="0 -1" Padding="0 -1"
@ -20,6 +21,6 @@
IsEnabled="{Binding IsEnabled}" IsEnabled="{Binding IsEnabled}"
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}"/> DragEnded="{s:Action InputDragEnded}"/>
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,11 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="800" d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:SKPointPropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:SKPointPropertyInputViewModel}">
<StackPanel Orientation="Horizontal" KeyboardNavigation.IsTabStop="True"> <StackPanel Orientation="Horizontal" KeyboardNavigation.IsTabStop="True">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat ToolTip="X-coordinate (horizontal)" <shared:DraggableFloat ToolTip="X-coordinate (horizontal)"
Value="{Binding X}" Value="{Binding X}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -27,6 +28,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsYEnabled}" /> IsEnabled="{Binding IsYEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,11 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}"> d:DataContext="{d:DesignInstance propertyInput:SKSizePropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" VerticalAlignment="Center" />
<shared:DraggableFloat ToolTip="Height" <shared:DraggableFloat ToolTip="Height"
Value="{Binding Height}" Value="{Binding Height}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}" StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
@ -27,6 +28,6 @@
DragStarted="{s:Action InputDragStarted}" DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsWidthEnabled}" /> IsEnabled="{Binding IsWidthEnabled}" />
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" /> <TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -72,12 +72,17 @@
<TextBlock Grid.Column="2" Text="{Binding Layer.Name}" VerticalAlignment="Center" Margin="5 0 0 0" /> <TextBlock Grid.Column="2" Text="{Binding Layer.Name}" VerticalAlignment="Center" Margin="5 0 0 0" />
<ToggleButton Grid.Column="3" <ToggleButton Grid.Column="3"
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
ToolTip="Toggle suspended state"
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding ProfileElement.Suspended, Converter={StaticResource InverseBooleanConverter}}" IsChecked="{Binding ProfileElement.Suspended, Converter={StaticResource InverseBooleanConverter}}"
Command="{s:Action SuspendedToggled}" Command="{s:Action SuspendedToggled}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<ToggleButton.ToolTip>
<TextBlock>
Toggle suspended state <LineBreak />
<Bold>Shift+click</Bold> to toggle focus
</TextBlock>
</ToggleButton.ToolTip>
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>
</Grid> </Grid>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.Services; using Artemis.Core.Services;
@ -18,9 +19,9 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly ILayerBrushService _layerBrushService; private readonly ILayerBrushService _layerBrushService;
private readonly IRgbService _rgbService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IProfileTreeVmFactory _profileTreeVmFactory; private readonly IProfileTreeVmFactory _profileTreeVmFactory;
private readonly IRgbService _rgbService;
private ProfileElement _profileElement; private ProfileElement _profileElement;
protected TreeItemViewModel(ProfileElement profileElement, protected TreeItemViewModel(ProfileElement profileElement,
@ -243,6 +244,39 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public void SuspendedToggled() public void SuspendedToggled()
{ {
// If shift is held toggle focus state
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
// Get all profile elements
List<ProfileElement> elements = ProfileElement.Profile.GetAllFolders().Cast<ProfileElement>().ToList();
elements.AddRange(ProfileElement.Profile.GetAllLayers().Cast<ProfileElement>().ToList());
// Separate out the targets of the focus state, the current profile element and all its parents
List<ProfileElement> targets = ProfileElement.GetAllFolders().Cast<ProfileElement>().ToList();
targets.AddRange(ProfileElement.GetAllLayers().Cast<ProfileElement>().ToList());
ProfileElement target = ProfileElement;
while (target != null)
{
targets.Add(target);
target = target.Parent;
}
// If any element is suspended, untoggle focus and unsuspend everything
if (elements.Except(targets).Any(e => e.Suspended))
{
foreach (ProfileElement profileElement in elements)
profileElement.Suspended = false;
}
// Otherwise suspend everything except the targets
else
{
foreach (ProfileElement profileElement in elements.Except(targets))
profileElement.Suspended = true;
foreach (ProfileElement profileElement in targets)
profileElement.Suspended = false;
}
}
_profileEditorService.SaveSelectedProfileConfiguration(); _profileEditorService.SaveSelectedProfileConfiguration();
} }

View File

@ -26,23 +26,23 @@
d:DesignHeight="640" d:DesignWidth="1200" d:DesignHeight="640" d:DesignWidth="1200"
d:DataContext="{d:DesignInstance screens:RootViewModel}"> d:DataContext="{d:DesignInstance screens:RootViewModel}">
<materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="RootDialog" DialogTheme="Inherit" SnackbarMessageQueue="{Binding MainMessageQueue}"> <materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="RootDialog" DialogTheme="Inherit" SnackbarMessageQueue="{Binding MainMessageQueue}">
<DockPanel>
<Border DockPanel.Dock="Left" DockPanel.ZIndex="2" ClipToBounds="True" Background="{DynamicResource MaterialDesignToolBarBackground}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition Width="{Binding SidebarWidth.Value, Mode=TwoWay}" MinWidth="175" MaxWidth="400" />
<ColumnDefinition /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" s:View.Model="{Binding SidebarViewModel}" Width="240" /> <Border Grid.Column="0" ClipToBounds="True" Background="{DynamicResource MaterialDesignToolBarBackground}">
<Rectangle Grid.Column="1" Fill="DarkGray" Width="1" Margin="0 0 -1 0"> <ContentControl s:View.Model="{Binding SidebarViewModel}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Color="{StaticResource MaterialDesignShadow}" />
</Rectangle.Effect>
</Rectangle>
</Grid>
</Border> </Border>
<materialDesign:ColorZone Mode="PrimaryMid" DockPanel.Dock="Top" DockPanel.ZIndex="1" Height="42"> <GridSplitter Grid.Column="0" Width="8" Margin="0 0 -4 0" Background="Transparent" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<materialDesign:ColorZone Grid.Row="0" Mode="PrimaryMid" DockPanel.Dock="Top" DockPanel.ZIndex="1" Height="42">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@ -68,13 +68,18 @@
</Grid> </Grid>
</materialDesign:ColorZone> </materialDesign:ColorZone>
<Grid> <ContentControl Grid.Row="1"
<ContentControl s:View.Model="{Binding SidebarViewModel.SelectedScreen}" /> s:View.Model="{Binding SidebarViewModel.SelectedScreen}"
<materialDesign:Snackbar x:Name="MainSnackbar" VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
<materialDesign:Snackbar Grid.Row="0"
Grid.RowSpan="2"
x:Name="MainSnackbar"
MessageQueue="{Binding MainMessageQueue}" MessageQueue="{Binding MainMessageQueue}"
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80" materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
materialDesign:SnackbarMessage.ContentMaxHeight="200" /> materialDesign:SnackbarMessage.ContentMaxHeight="200" />
</Grid> </Grid>
</DockPanel> </Grid>
</materialDesign:DialogHost> </materialDesign:DialogHost>
</mde:MaterialWindow> </mde:MaterialWindow>

View File

@ -61,6 +61,7 @@ namespace Artemis.UI.Screens
_frameTimeUpdateTimer = new Timer(500); _frameTimeUpdateTimer = new Timer(500);
_windowSize = _settingsService.GetSetting<WindowSize>("UI.RootWindowSize"); _windowSize = _settingsService.GetSetting<WindowSize>("UI.RootWindowSize");
SidebarWidth = _settingsService.GetSetting("UI.SidebarWidth", new GridLength(240));
SidebarViewModel = sidebarViewModel; SidebarViewModel = sidebarViewModel;
SidebarViewModel.ConductWith(this); SidebarViewModel.ConductWith(this);
@ -68,6 +69,7 @@ namespace Artemis.UI.Screens
WindowTitle = $"Artemis {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}"; WindowTitle = $"Artemis {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}";
} }
public PluginSetting<GridLength> SidebarWidth { get; }
public SidebarViewModel SidebarViewModel { get; } public SidebarViewModel SidebarViewModel { get; }
public ISnackbarMessageQueue MainMessageQueue public ISnackbarMessageQueue MainMessageQueue
@ -210,7 +212,7 @@ namespace Artemis.UI.Screens
_frameTimeUpdateTimer.Stop(); _frameTimeUpdateTimer.Stop();
SidebarViewModel.SelectedScreenChanged -= SidebarViewModelOnSelectedScreenChanged; SidebarViewModel.SelectedScreenChanged -= SidebarViewModelOnSelectedScreenChanged;
SidebarWidth.Save();
_windowSize.Value ??= new WindowSize(); _windowSize.Value ??= new WindowSize();
_windowSize.Value.ApplyFromWindow(_window); _windowSize.Value.ApplyFromWindow(_window);
_windowSize.Save(); _windowSize.Save();

View File

@ -142,7 +142,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private void PopulateModules() private void PopulateModules()
{ {
Modules.Clear(); Modules.Clear();
Modules.AddRange(_pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).OrderBy(m => m.DisplayName)); Modules.AddRange(_pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).OrderBy(m => m.Info.Name));
if (SelectedModule == null) if (SelectedModule == null)
_dataModelUIService.UpdateModules(MainDataModel); _dataModelUIService.UpdateModules(MainDataModel);

View File

@ -17,7 +17,7 @@
<ColumnDefinition Width="40"/> <ColumnDefinition Width="40"/>
<ColumnDefinition /> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<shared:ArtemisIcon Grid.Column="0" Icon="{Binding Icon}" Width="24" Height="24" /> <shared:ArtemisIcon Grid.Column="0" Icon="{Binding Plugin.Info.ResolvedIcon}" Width="24" Height="24" />
<TextBlock Grid.Column="1" VerticalAlignment="Center" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Text="{Binding Plugin.Info.Name}" /> <TextBlock Grid.Column="1" VerticalAlignment="Center" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Text="{Binding Plugin.Info.Name}" />
</Grid> </Grid>

View File

@ -1,29 +1,25 @@
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
{ {
public class PerformanceDebugPluginViewModel : Screen public class PerformanceDebugPluginViewModel : Screen
{ {
public Plugin Plugin { get; }
public object Icon { get; }
public PerformanceDebugPluginViewModel(Plugin plugin) public PerformanceDebugPluginViewModel(Plugin plugin)
{ {
Plugin = plugin; Plugin = plugin;
Icon = PluginUtilities.GetPluginIcon(Plugin, Plugin.Info.Icon);
} }
public Plugin Plugin { get; }
public BindableCollection<PerformanceDebugProfilerViewModel> Profilers { get; } = new(); public BindableCollection<PerformanceDebugProfilerViewModel> Profilers { get; } = new();
public void Update() public void Update()
{ {
foreach (Profiler pluginProfiler in Plugin.Profilers.Where(p => p.Measurements.Any())) foreach (Profiler pluginProfiler in Plugin.Profilers.Where(p => p.Measurements.Any()))
{
if (Profilers.All(p => p.Profiler != pluginProfiler)) if (Profilers.All(p => p.Profiler != pluginProfiler))
Profilers.Add(new PerformanceDebugProfilerViewModel(pluginProfiler)); Profilers.Add(new PerformanceDebugProfilerViewModel(pluginProfiler));
}
foreach (PerformanceDebugProfilerViewModel profilerViewModel in Profilers) foreach (PerformanceDebugProfilerViewModel profilerViewModel in Profilers)
profilerViewModel.Update(); profilerViewModel.Update();

View File

@ -29,8 +29,9 @@
<!-- Icon column --> <!-- Icon column -->
<shared:ArtemisIcon Grid.Column="0" <shared:ArtemisIcon Grid.Column="0"
Icon="{Binding FeatureInfo.Icon}" Icon="{Binding FeatureInfo.ResolvedIcon}"
Width="20" Width="20"
Height="20"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" /> Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />

View File

@ -55,6 +55,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
if (!Items.Contains(pluginSettingsViewModel)) if (!Items.Contains(pluginSettingsViewModel))
Items.Add(pluginSettingsViewModel); Items.Add(pluginSettingsViewModel);
} }
foreach (PluginSettingsViewModel pluginSettingsViewModel in Items.ToList()) foreach (PluginSettingsViewModel pluginSettingsViewModel in Items.ToList())
{ {
if (!instances.Contains(pluginSettingsViewModel)) if (!instances.Contains(pluginSettingsViewModel))
@ -76,21 +77,28 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
base.OnInitialActivate(); base.OnInitialActivate();
} }
public void ImportPlugin() public async Task ImportPlugin()
{ {
VistaOpenFileDialog dialog = new(); VistaOpenFileDialog dialog = new() {Filter = "ZIP files (*.zip)|*.zip", Title = "Import Artemis plugin"};
dialog.Filter = "ZIP files (*.zip)|*.zip";
dialog.Title = "Import Artemis plugin";
bool? result = dialog.ShowDialog(); bool? result = dialog.ShowDialog();
if (result == true) if (result != true)
return;
// Take the actual import off of the UI thread
await Task.Run(() =>
{ {
Plugin plugin = _pluginManagementService.ImportPlugin(dialog.FileName); Plugin plugin = _pluginManagementService.ImportPlugin(dialog.FileName);
GetPluginInstances(); GetPluginInstances();
SearchPluginInput = plugin.Info.Name; SearchPluginInput = plugin.Info.Name;
// Enable it via the VM to enable the prerequisite dialog
PluginSettingsViewModel pluginViewModel = Items.FirstOrDefault(i => i.Plugin == plugin);
if (pluginViewModel is {IsEnabled: false})
pluginViewModel.IsEnabled = true;
_messageService.ShowMessage($"Imported plugin: {plugin.Info.Name}"); _messageService.ShowMessage($"Imported plugin: {plugin.Info.Name}");
} });
} }
public void GetPluginInstances() public void GetPluginInstances()

View File

@ -42,7 +42,7 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<shared:ArtemisIcon Icon="{Binding Icon}" <shared:ArtemisIcon Icon="{Binding Plugin.Info.ResolvedIcon}"
Width="48" Width="48"
Height="48" Height="48"
Margin="0 5 0 0" Margin="0 5 0 0"

View File

@ -45,8 +45,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
_dialogService = dialogService; _dialogService = dialogService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_messageService = messageService; _messageService = messageService;
Icon = PluginUtilities.GetPluginIcon(Plugin, Plugin.Info.Icon);
} }
public Plugin Plugin public Plugin Plugin
@ -61,7 +59,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
set => SetAndNotify(ref _enabling, value); set => SetAndNotify(ref _enabling, value);
} }
public object Icon { get; set; }
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name; public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null; public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null;
@ -102,7 +99,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
try try
{ {
PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type); PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type);
_windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel, Icon)); _windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -8,10 +8,10 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{ {
private readonly PluginConfigurationViewModel _configurationViewModel; private readonly PluginConfigurationViewModel _configurationViewModel;
public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel, object icon) public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel)
{ {
_configurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel)); _configurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel));
Icon = icon; Icon = configurationViewModel.Plugin.Info.Icon;
} }
public object Icon { get; } public object Icon { get; }

View File

@ -36,7 +36,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
: "any requirement is met"; : "any requirement is met";
Items.Clear(); Items.Clear();
if (Module != null) if (Module?.ActivationRequirements != null)
Items.AddRange(Module.ActivationRequirements.Select(_sidebarVmFactory.ModuleActivationRequirementViewModel)); Items.AddRange(Module.ActivationRequirements.Select(_sidebarVmFactory.ModuleActivationRequirementViewModel));
} }
} }

View File

@ -6,10 +6,8 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:dialogs="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:profileEdit="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit" xmlns:profileEdit="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="280" d:DesignHeight="450" d:DesignWidth="280"
@ -17,9 +15,10 @@
Width="800"> Width="800">
<UserControl.Resources> <UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" /> <converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter"/>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" /> <shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter" />
<shared:StreamToSvgImageConverter x:Key="StreamToSvgImageConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="16"> <Grid Margin="16">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -121,6 +120,7 @@
<ComboBox Grid.Column="1" <ComboBox Grid.Column="1"
materialDesign:HintAssist.Hint="Profile icon" materialDesign:HintAssist.Hint="Profile icon"
IsEditable="True"
Style="{StaticResource MaterialDesignFilledComboBox}" Style="{StaticResource MaterialDesignFilledComboBox}"
SelectedItem="{Binding DataContext.SelectedIcon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" SelectedItem="{Binding DataContext.SelectedIcon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
IsEnabled="{Binding DataContext.Initializing, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource InverseBooleanConverter}}" IsEnabled="{Binding DataContext.Initializing, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource InverseBooleanConverter}}"
@ -156,11 +156,11 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image Grid.Column="0" <Image Grid.Column="0"
Source="{Binding DataContext.SelectedImage, Converter={StaticResource StreamToBitmapImageConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Source="{Binding DataContext.SelectedImage, Converter={StaticResource StreamToBitmapImageConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
RenderOptions.BitmapScalingMode="HighQuality"
Margin="0 0 10 0" Margin="0 0 10 0"
Width="45" Width="45"
Height="45" Height="45"
VerticalAlignment="Center" /> VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality" />
<Button Grid.Column="1" <Button Grid.Column="1"
Command="{s:Action SelectBitmapFile}" Command="{s:Action SelectBitmapFile}"
Style="{StaticResource MaterialDesignRaisedButton}" Style="{StaticResource MaterialDesignRaisedButton}"
@ -185,12 +185,13 @@
<ColumnDefinition /> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<svgc:SvgViewbox Grid.Column="0" <Image Grid.Column="0"
StreamSource="{Binding DataContext.SelectedImage, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Source="{Binding DataContext.SelectedImage, Converter={StaticResource StreamToSvgImageConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Margin="0 0 10 0" Margin="0 0 10 0"
Width="45" Width="45"
Height="45" Height="45"
VerticalAlignment="Center" /> VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality" />
<Button Grid.Column="1" <Button Grid.Column="1"
Command="{s:Action SelectSvgFile}" Command="{s:Action SelectSvgFile}"
Style="{StaticResource MaterialDesignRaisedButton}" Style="{StaticResource MaterialDesignRaisedButton}"

View File

@ -9,11 +9,8 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
public ProfileModuleViewModel(Module module) public ProfileModuleViewModel(Module module)
{ {
Module = module; Module = module;
Name = module.DisplayName; Name = module.Info.Name;
if (module.DisplayIcon != null) Icon = module.Info.ResolvedIcon ?? PackIconKind.QuestionMark.ToString();
Icon = module.DisplayIcon.EndsWith(".svg") ? module.Plugin.ResolveRelativePath(module.DisplayIcon) : module.DisplayIcon;
else
Icon = PackIconKind.QuestionMark.ToString();
Description = module.Info.Description; Description = module.Info.Description;
} }

View File

@ -18,7 +18,7 @@
Padding="8"> Padding="8">
<!-- Above is a dumb hack to get the context menu to cover the entire item --> <!-- Above is a dumb hack to get the context menu to cover the entire item -->
<UserControl.Resources> <UserControl.Resources>
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter" /> <converters:ValuesAdditionConverter x:Key="ValuesAddition" />
</UserControl.Resources> </UserControl.Resources>
<UserControl.ContextMenu> <UserControl.ContextMenu>
<ContextMenu> <ContextMenu>
@ -77,12 +77,17 @@
</UserControl.ContextMenu> </UserControl.ContextMenu>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<shared:ProfileConfigurationIcon Grid.Column="0" VerticalAlignment="Center" ConfigurationIcon="{Binding ProfileConfiguration.Icon}" Width="20" Height="20"> <shared:ProfileConfigurationIcon Grid.Column="0"
x:Name="ProfileIcon"
VerticalAlignment="Center"
ConfigurationIcon="{Binding ProfileConfiguration.Icon}"
Width="20"
Height="20">
<shared:ProfileConfigurationIcon.Style> <shared:ProfileConfigurationIcon.Style>
<Style> <Style>
<Style.Triggers> <Style.Triggers>
@ -107,15 +112,22 @@
</shared:ProfileConfigurationIcon.Style> </shared:ProfileConfigurationIcon.Style>
</shared:ProfileConfigurationIcon> </shared:ProfileConfigurationIcon>
<TextBlock Grid.Column="1" FontSize="12" Margin="10 0 0 0" VerticalAlignment="Center" Text="{Binding ProfileConfiguration.Name}"> <TextBlock Grid.Column="1"
x:Name="ProfileName"
FontSize="12"
Margin="10 0 0 0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Text="{Binding ProfileConfiguration.Name}"
TextTrimming="CharacterEllipsis">
<TextBlock.Style> <TextBlock.Style>
<Style> <Style TargetType="TextBlock">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding IsProfileActive}" Value="False"> <DataTrigger Binding="{Binding IsProfileActive}" Value="False">
<DataTrigger.EnterActions> <DataTrigger.EnterActions>
<BeginStoryboard> <BeginStoryboard>
<Storyboard> <Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.33" Duration="0:0:0.25" /> <DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.33" />
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</DataTrigger.EnterActions> </DataTrigger.EnterActions>
@ -132,7 +144,21 @@
</TextBlock.Style> </TextBlock.Style>
</TextBlock> </TextBlock>
<Border Grid.Column="0" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="{DynamicResource MaterialDesignBody}" Height="1" Opacity="0"> <Border Grid.Column="0"
Grid.ColumnSpan="2"
BorderThickness="1"
BorderBrush="{DynamicResource MaterialDesignBody}"
Height="1"
Opacity="0"
HorizontalAlignment="Left">
<!-- Ensure the line covers the profile and the text but not the full two columns -->
<Border.Width>
<MultiBinding Converter="{StaticResource ValuesAddition}">
<Binding Path="ActualWidth" ElementName="ProfileIcon" />
<Binding Path="ActualWidth" ElementName="ProfileName" />
<Binding Path="Margin.Left" ElementName="ProfileName" />
</MultiBinding>
</Border.Width>
<Border.Style> <Border.Style>
<Style> <Style>
<Style.Triggers> <Style.Triggers>
@ -160,7 +186,7 @@
<Button Grid.Column="2" ToolTip="View properties" Width="20" Height="20" Command="{s:Action ViewProperties}" HorizontalAlignment="Right"> <Button Grid.Column="2" ToolTip="View properties" Width="20" Height="20" Command="{s:Action ViewProperties}" HorizontalAlignment="Right">
<Button.Style> <Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}"> <Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}">
<Setter Property="Visibility" Value="Hidden" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True"> <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
@ -173,7 +199,7 @@
<ToggleButton Grid.Column="3" ToolTip="Suspend profile" Width="18" Height="18" Margin="2 0 0 0" IsChecked="{Binding IsSuspended}"> <ToggleButton Grid.Column="3" ToolTip="Suspend profile" Width="18" Height="18" Margin="2 0 0 0" IsChecked="{Binding IsSuspended}">
<ToggleButton.Style> <ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MaterialDesignFlatToggleButton}"> <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MaterialDesignFlatToggleButton}">
<Setter Property="Visibility" Value="Hidden" /> <Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True"> <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Visibility" Value="Visible" />
@ -183,6 +209,5 @@
</ToggleButton.Style> </ToggleButton.Style>
<materialDesign:PackIcon Kind="Pause" Height="14" Width="14" /> <materialDesign:PackIcon Kind="Pause" Height="14" Width="14" />
</ToggleButton> </ToggleButton>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -119,7 +119,7 @@
<!-- Bottom buttons --> <!-- Bottom buttons -->
<Separator Grid.Row="4" Margin="8" /> <Separator Grid.Row="4" Margin="8" />
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5 0 5 5"> <WrapPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5 0 5 5">
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" <Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44" Width="44"
Height="44" Height="44"
@ -160,7 +160,7 @@
CommandParameter="https://wiki.artemis-rgb.com/en/donating"> CommandParameter="https://wiki.artemis-rgb.com/en/donating">
<materialDesign:PackIcon Kind="Gift" Width="20" Height="20" /> <materialDesign:PackIcon Kind="Gift" Width="20" Height="20" />
</Button> </Button>
</StackPanel> </WrapPanel>
</Grid> </Grid>
</materialDesign:DialogHost> </materialDesign:DialogHost>
</UserControl> </UserControl>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
@ -129,8 +130,10 @@ namespace Artemis.UI.Screens
_eventAggregator.Publish(new RequestSelectSidebarItemEvent(sidebarItem)); _eventAggregator.Publish(new RequestSelectSidebarItemEvent(sidebarItem));
} }
public void TrayExit() public async Task TrayExit()
{ {
// Don't freeze the UI right after clicking
await Task.Delay(200);
Core.Utilities.Shutdown(); Core.Utilities.Shutdown();
} }