mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Core - Rewrote profile configuration icons
WPF UI - Adjusted for the above rewrite Avalonia UI - Implemented profile icons
This commit is contained in:
parent
4c5c785aa6
commit
a6f1b05c19
@ -27,7 +27,8 @@ namespace Artemis.Core
|
||||
_category = category;
|
||||
|
||||
Entity = new ProfileConfigurationEntity();
|
||||
Icon = new ProfileConfigurationIcon(Entity) {MaterialIcon = icon};
|
||||
Icon = new ProfileConfigurationIcon(Entity);
|
||||
Icon.SetIconByName(icon);
|
||||
ActivationCondition = new NodeScript<bool>("Activate profile", "Whether or not the profile should be active", this);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Artemis.Core.JsonConverters;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Newtonsoft.Json;
|
||||
@ -8,7 +9,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// A model that can be used to serialize a profile configuration, it's profile and it's icon
|
||||
/// </summary>
|
||||
public class ProfileConfigurationExportModel
|
||||
public class ProfileConfigurationExportModel : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the storage entity of the profile configuration
|
||||
@ -26,5 +27,11 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StreamConverter))]
|
||||
public Stream? ProfileImage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
ProfileImage?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
@ -10,9 +11,10 @@ namespace Artemis.Core
|
||||
public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
|
||||
{
|
||||
private readonly ProfileConfigurationEntity _entity;
|
||||
private Stream? _fileIcon;
|
||||
private string? _iconName;
|
||||
private Stream? _iconStream;
|
||||
private ProfileConfigurationIconType _iconType;
|
||||
private string? _materialIcon;
|
||||
private string? _originalFileName;
|
||||
|
||||
internal ProfileConfigurationIcon(ProfileConfigurationEntity entity)
|
||||
{
|
||||
@ -20,31 +22,82 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of icon this profile configuration uses
|
||||
/// Gets the type of icon this profile configuration uses
|
||||
/// </summary>
|
||||
public ProfileConfigurationIconType IconType
|
||||
{
|
||||
get => _iconType;
|
||||
set => SetAndNotify(ref _iconType, value);
|
||||
private set => SetAndNotify(ref _iconType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon if it is a Material icon
|
||||
/// Gets the name of the icon if <see cref="IconType" /> is <see cref="ProfileConfigurationIconType.MaterialIcon" />
|
||||
/// </summary>
|
||||
public string? MaterialIcon
|
||||
public string? IconName
|
||||
{
|
||||
get => _materialIcon;
|
||||
set => SetAndNotify(ref _materialIcon, value);
|
||||
get => _iconName;
|
||||
private set => SetAndNotify(ref _iconName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a stream containing the icon if it is bitmap or SVG
|
||||
/// Gets the original file name of the icon (if applicable)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Stream? FileIcon
|
||||
public string? OriginalFileName
|
||||
{
|
||||
get => _fileIcon;
|
||||
set => SetAndNotify(ref _fileIcon, value);
|
||||
get => _originalFileName;
|
||||
private set => SetAndNotify(ref _originalFileName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="IconName" /> to the provided value and changes the <see cref="IconType" /> is
|
||||
/// <see cref="ProfileConfigurationIconType.MaterialIcon" />
|
||||
/// </summary>
|
||||
/// <param name="iconName">The name of the icon</param>
|
||||
public void SetIconByName(string iconName)
|
||||
{
|
||||
IconName = iconName ?? throw new ArgumentNullException(nameof(iconName));
|
||||
OriginalFileName = null;
|
||||
IconType = ProfileConfigurationIconType.MaterialIcon;
|
||||
|
||||
_iconStream?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the stream returned by <see cref="GetIconStream" /> to the provided stream
|
||||
/// </summary>
|
||||
/// <param name="originalFileName">The original file name backing the stream, should include the extension</param>
|
||||
/// <param name="stream">The stream to copy</param>
|
||||
public void SetIconByStream(string originalFileName, Stream stream)
|
||||
{
|
||||
if (originalFileName == null) throw new ArgumentNullException(nameof(originalFileName));
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
_iconStream?.Dispose();
|
||||
_iconStream = new MemoryStream();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
stream.CopyTo(_iconStream);
|
||||
_iconStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
IconName = null;
|
||||
OriginalFileName = originalFileName;
|
||||
IconType = OriginalFileName.EndsWith(".svg") ? ProfileConfigurationIconType.SvgImage : ProfileConfigurationIconType.BitmapImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the stream containing the icon
|
||||
/// </summary>
|
||||
/// <returns>A stream containing the icon</returns>
|
||||
public Stream? GetIconStream()
|
||||
{
|
||||
if (_iconStream == null)
|
||||
return null;
|
||||
|
||||
MemoryStream stream = new();
|
||||
_iconStream.CopyTo(stream);
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
_iconStream.Seek(0, SeekOrigin.Begin);
|
||||
return stream;
|
||||
}
|
||||
|
||||
#region Implementation of IStorageModel
|
||||
@ -53,14 +106,15 @@ namespace Artemis.Core
|
||||
public void Load()
|
||||
{
|
||||
IconType = (ProfileConfigurationIconType) _entity.IconType;
|
||||
MaterialIcon = _entity.MaterialIcon;
|
||||
if (IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
IconName = _entity.MaterialIcon;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
_entity.IconType = (int) IconType;
|
||||
_entity.MaterialIcon = MaterialIcon;
|
||||
_entity.MaterialIcon = IconType == ProfileConfigurationIconType.MaterialIcon ? IconName : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -304,10 +304,13 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
if (profileConfiguration.Icon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
return;
|
||||
if (profileConfiguration.Icon.FileIcon != null)
|
||||
return;
|
||||
|
||||
// This can happen if the icon was saved before the original file name was stored (pre-Avalonia)
|
||||
profileConfiguration.Entity.IconOriginalFileName ??= profileConfiguration.Icon.IconType == ProfileConfigurationIconType.BitmapImage ? "icon.png" : "icon.svg";
|
||||
|
||||
profileConfiguration.Icon.FileIcon = _profileCategoryRepository.GetProfileIconStream(profileConfiguration.Entity.FileIconId);
|
||||
using Stream? stream = _profileCategoryRepository.GetProfileIconStream(profileConfiguration.Entity.FileIconId);
|
||||
if (stream != null)
|
||||
profileConfiguration.Icon.SetIconByStream(profileConfiguration.Entity.IconOriginalFileName, stream);
|
||||
}
|
||||
|
||||
public void SaveProfileConfigurationIcon(ProfileConfiguration profileConfiguration)
|
||||
@ -315,10 +318,11 @@ namespace Artemis.Core.Services
|
||||
if (profileConfiguration.Icon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
return;
|
||||
|
||||
if (profileConfiguration.Icon.FileIcon != null)
|
||||
using Stream? stream = profileConfiguration.Icon.GetIconStream();
|
||||
if (stream != null && profileConfiguration.Icon.OriginalFileName != null)
|
||||
{
|
||||
profileConfiguration.Icon.FileIcon.Position = 0;
|
||||
_profileCategoryRepository.SaveProfileIconStream(profileConfiguration.Entity, profileConfiguration.Icon.FileIcon);
|
||||
profileConfiguration.Entity.IconOriginalFileName = profileConfiguration.Icon.OriginalFileName;
|
||||
_profileCategoryRepository.SaveProfileIconStream(profileConfiguration.Entity, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,7 +536,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
ProfileConfigurationEntity = profileConfiguration.Entity,
|
||||
ProfileEntity = profile.ProfileEntity,
|
||||
ProfileImage = profileConfiguration.Icon.FileIcon
|
||||
ProfileImage = profileConfiguration.Icon.GetIconStream()
|
||||
};
|
||||
}
|
||||
|
||||
@ -579,12 +583,8 @@ namespace Artemis.Core.Services
|
||||
profileConfiguration = new ProfileConfiguration(category, profileEntity.Name, "Import");
|
||||
}
|
||||
|
||||
if (exportModel.ProfileImage != null)
|
||||
{
|
||||
profileConfiguration.Icon.FileIcon = new MemoryStream();
|
||||
exportModel.ProfileImage.Position = 0;
|
||||
exportModel.ProfileImage.CopyTo(profileConfiguration.Icon.FileIcon);
|
||||
}
|
||||
if (exportModel.ProfileImage != null && exportModel.ProfileConfigurationEntity?.IconOriginalFileName != null)
|
||||
profileConfiguration.Icon.SetIconByStream(exportModel.ProfileConfigurationEntity.IconOriginalFileName, exportModel.ProfileImage);
|
||||
|
||||
profileConfiguration.Entity.ProfileId = profileEntity.Id;
|
||||
category.AddProfileConfiguration(profileConfiguration, 0);
|
||||
|
||||
@ -8,6 +8,7 @@ namespace Artemis.Storage.Entities.Profile
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string MaterialIcon { get; set; }
|
||||
public string IconOriginalFileName { get; set; }
|
||||
public Guid FileIconId { get; set; }
|
||||
public int IconType { get; set; }
|
||||
public int Order { get; set; }
|
||||
|
||||
@ -70,7 +70,7 @@ namespace Artemis.Storage.Repositories
|
||||
if (stream == null && _profileIcons.Exists(profileConfigurationEntity.FileIconId))
|
||||
_profileIcons.Delete(profileConfigurationEntity.FileIconId);
|
||||
|
||||
_profileIcons.Upload(profileConfigurationEntity.FileIconId, "image", stream);
|
||||
_profileIcons.Upload(profileConfigurationEntity.FileIconId, profileConfigurationEntity.IconOriginalFileName, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,6 +166,17 @@
|
||||
<member name="M:Artemis.UI.Shared.Converters.ColorToSKColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Converters.EnumToBooleanConverter">
|
||||
<summary>
|
||||
Converts an enum into a boolean.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Converters.EnumToBooleanConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Converters.EnumToBooleanConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Converters.SKColorToColorConverter">
|
||||
<summary>
|
||||
Converts <see cref="T:SkiaSharp.SKColor" /> into <see cref="T:Avalonia.Media.Color" />.
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<materialDesign:PackIcon Kind="{Binding ConfigurationIcon.MaterialIcon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
<materialDesign:PackIcon Kind="{Binding ConfigurationIcon.IconName, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||
Width="Auto"
|
||||
Height="Auto" />
|
||||
</DataTemplate>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
xmlns:profileEdit="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit"
|
||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="280"
|
||||
d:DesignHeight="450" d:DesignWidth="600"
|
||||
d:DataContext="{d:DesignInstance {x:Type profileEdit:ProfileEditViewModel}}"
|
||||
Width="800">
|
||||
<UserControl.Resources>
|
||||
|
||||
@ -28,6 +28,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
private ProfileConfigurationIconType _selectedIconType;
|
||||
private Stream _selectedImage;
|
||||
private ProfileModuleViewModel _selectedModule;
|
||||
private string _selectedIconPath;
|
||||
|
||||
public ProfileEditViewModel(ProfileConfiguration profileConfiguration, bool isNew,
|
||||
IProfileService profileService,
|
||||
@ -48,7 +49,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m))
|
||||
);
|
||||
Initializing = true;
|
||||
|
||||
|
||||
ModuleActivationRequirementsViewModel = new ModuleActivationRequirementsViewModel(sidebarVmFactory);
|
||||
ModuleActivationRequirementsViewModel.ConductWith(this);
|
||||
ModuleActivationRequirementsViewModel.SetModule(ProfileConfiguration.Module);
|
||||
@ -60,7 +61,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
_profileName = ProfileConfiguration.Name;
|
||||
_selectedModule = Modules.FirstOrDefault(m => m.Module == ProfileConfiguration.Module);
|
||||
_selectedIconType = ProfileConfiguration.Icon.IconType;
|
||||
_selectedImage = ProfileConfiguration.Icon.FileIcon;
|
||||
_selectedImage = ProfileConfiguration.Icon.GetIconStream();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
@ -71,11 +72,11 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
if (IsNew)
|
||||
SelectedIcon = Icons[new Random().Next(0, Icons.Count - 1)];
|
||||
else
|
||||
SelectedIcon = Icons.FirstOrDefault(i => i.Icon.ToString() == ProfileConfiguration.Icon.MaterialIcon);
|
||||
SelectedIcon = Icons.FirstOrDefault(i => i.Icon.ToString() == ProfileConfiguration.Icon.IconName);
|
||||
Initializing = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public ModuleActivationRequirementsViewModel ModuleActivationRequirementsViewModel { get; }
|
||||
public ProfileConfigurationHotkeyViewModel EnableHotkeyViewModel { get; }
|
||||
public ProfileConfigurationHotkeyViewModel DisableHotkeyViewModel { get; }
|
||||
@ -167,18 +168,18 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
return;
|
||||
|
||||
ProfileConfiguration.Name = ProfileName;
|
||||
ProfileConfiguration.Icon.IconType = SelectedIconType;
|
||||
ProfileConfiguration.Icon.MaterialIcon = SelectedIcon?.Icon.ToString();
|
||||
ProfileConfiguration.Icon.FileIcon = SelectedImage;
|
||||
if (SelectedIconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
ProfileConfiguration.Icon.SetIconByName(SelectedIcon?.Icon.ToString());
|
||||
else if (_selectedIconPath != null)
|
||||
{
|
||||
await using FileStream fileStream = File.OpenRead(_selectedIconPath);
|
||||
ProfileConfiguration.Icon.SetIconByStream(Path.GetFileName(_selectedIconPath), fileStream);
|
||||
}
|
||||
|
||||
ProfileConfiguration.Module = SelectedModule?.Module;
|
||||
|
||||
if (_changedImage)
|
||||
{
|
||||
ProfileConfiguration.Icon.FileIcon = SelectedImage;
|
||||
_profileService.SaveProfileConfigurationIcon(ProfileConfiguration);
|
||||
}
|
||||
|
||||
_profileService.SaveProfileCategory(ProfileConfiguration.Category);
|
||||
|
||||
Session.Close(nameof(Accept));
|
||||
@ -199,6 +200,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
|
||||
// TODO: Scale down to 100x100-ish
|
||||
SelectedImage = File.OpenRead(dialog.FileName);
|
||||
_selectedIconPath = dialog.FileName;
|
||||
}
|
||||
|
||||
public void SelectSvgFile()
|
||||
@ -214,6 +216,7 @@ namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
|
||||
|
||||
_changedImage = true;
|
||||
SelectedImage = File.OpenRead(dialog.FileName);
|
||||
_selectedIconPath = dialog.FileName;
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Artemis.Core;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
@ -48,20 +49,29 @@ namespace Artemis.UI.Shared.Controls
|
||||
|
||||
try
|
||||
{
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.SvgImage && ConfigurationIcon.FileIcon != null)
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
{
|
||||
SvgSource source = new();
|
||||
source.Load(ConfigurationIcon.FileIcon);
|
||||
Content = new SvgImage {Source = source};
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon && ConfigurationIcon.MaterialIcon != null)
|
||||
{
|
||||
Content = Enum.TryParse(ConfigurationIcon.MaterialIcon, true, out MaterialIconKind parsedIcon)
|
||||
Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon)
|
||||
? new MaterialIcon {Kind = parsedIcon!}
|
||||
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
return;
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.BitmapImage && ConfigurationIcon.FileIcon != null)
|
||||
Content = new Image {Source = new Bitmap(ConfigurationIcon.FileIcon)};
|
||||
|
||||
Stream? stream = ConfigurationIcon.GetIconStream();
|
||||
if (stream == null)
|
||||
{
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.SvgImage)
|
||||
{
|
||||
SvgSource source = new();
|
||||
source.Load(stream);
|
||||
Content = new Image {Source = new SvgImage {Source = source}};
|
||||
}
|
||||
else if (ConfigurationIcon.IconType == ProfileConfigurationIconType.BitmapImage)
|
||||
Content = new Image {Source = new Bitmap(ConfigurationIcon.GetIconStream())};
|
||||
else
|
||||
Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
@ -83,10 +93,8 @@ namespace Artemis.UI.Shared.Controls
|
||||
if (ConfigurationIcon != null)
|
||||
ConfigurationIcon.PropertyChanged -= IconOnPropertyChanged;
|
||||
|
||||
if (Content is SvgImage svgImage)
|
||||
svgImage.Source?.Dispose();
|
||||
else if (Content is Image image)
|
||||
((Bitmap) image.Source).Dispose();
|
||||
if (Content is Image image && image.Source is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an enum into a boolean.
|
||||
/// </summary>
|
||||
public class EnumToBooleanConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return Equals(value, parameter);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value?.Equals(true) == true ? parameter : BindingOperations.DoNothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
|
||||
/// <typeparam name="TViewModel">The view model type</typeparam>
|
||||
/// <typeparam name="TResult">The return type</typeparam>
|
||||
/// <returns>A task containing the return value of type <typeparamref name="TResult" /></returns>
|
||||
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>;
|
||||
Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>;
|
||||
|
||||
/// <summary>
|
||||
/// Shows a content dialog asking the user to confirm an action
|
||||
|
||||
@ -56,7 +56,7 @@ namespace Artemis.UI.Shared.Services
|
||||
window.Show();
|
||||
}
|
||||
|
||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||
public async Task<TResult> ShowDialogAsync<TViewModel, TResult>(params (string name, object? value)[] parameters) where TViewModel : DialogViewModelBase<TResult>
|
||||
{
|
||||
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast<IParameter>().ToArray();
|
||||
TViewModel viewModel = _kernel.Get<TViewModel>(paramsArray)!;
|
||||
|
||||
@ -1,5 +1,17 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<VisualBrush x:Key="CheckerboardBrush" TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,15,15">
|
||||
<VisualBrush.Visual>
|
||||
<Grid Width="15" Height="15" RowDefinitions="*,*" ColumnDefinitions="*,*">
|
||||
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
|
||||
<Rectangle Grid.Row="0" Grid.Column="1" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="0" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
|
||||
</Grid>
|
||||
</VisualBrush.Visual>
|
||||
</VisualBrush>
|
||||
</Styles.Resources>
|
||||
<StyleInclude Source="/Styles/Border.axaml" />
|
||||
<StyleInclude Source="/Styles/Button.axaml" />
|
||||
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
||||
|
||||
@ -15,6 +15,10 @@
|
||||
</StackPanel>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Styles.Resources>
|
||||
<CornerRadius x:Key="CardCornerRadius">8</CornerRadius>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Add Styles Here -->
|
||||
<Style Selector="Border.router-container">
|
||||
<Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorTertiary}" />
|
||||
@ -25,13 +29,13 @@
|
||||
<Style Selector="Border.card">
|
||||
<Setter Property="Padding" Value="25" />
|
||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorDefaultBrush}" />
|
||||
<Setter Property="CornerRadius" Value="10" />
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource CardCornerRadius}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border.card-condensed">
|
||||
<Setter Property="Padding" Value="15" />
|
||||
<Setter Property="Background" Value="{DynamicResource ControlFillColorDefaultBrush}" />
|
||||
<Setter Property="CornerRadius" Value="10" />
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource CardCornerRadius}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Separator.card-separator">
|
||||
|
||||
@ -51,5 +51,11 @@
|
||||
<Compile Update="Screens\Debugger\Tabs\Settings\DebugSettingsView.axaml.cs">
|
||||
<DependentUpon>DebugSettingsView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Screens\Root\Sidebar\ContentDialogs\SidebarCategoryEditView.axaml.cs">
|
||||
<DependentUpon>SidebarCategoryEditView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Screens\Root\Sidebar\Dialogs\ProfileConfigurationEditView.axaml.cs">
|
||||
<DependentUpon>ProfileConfigurationEditView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -9,20 +9,7 @@
|
||||
x:Class="Artemis.UI.Screens.Device.DeviceSettingsView">
|
||||
<Border Classes="card" Padding="0" Width="200" ClipToBounds="True" Margin="5">
|
||||
<Grid RowDefinitions="140,*,Auto">
|
||||
<Rectangle Grid.Row="0">
|
||||
<Rectangle.Fill>
|
||||
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,15,15">
|
||||
<VisualBrush.Visual>
|
||||
<Grid Width="15" Height="15" RowDefinitions="*,*" ColumnDefinitions="*,*">
|
||||
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
|
||||
<Rectangle Grid.Row="0" Grid.Column="1" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="0" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
|
||||
</Grid>
|
||||
</VisualBrush.Visual>
|
||||
</VisualBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<Rectangle Grid.Row="0" Fill="{DynamicResource CheckerboardBrush}"/>
|
||||
<controls1:DeviceVisualizer VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="5"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Root.Sidebar.Dialogs.SidebarCategoryCreateView">
|
||||
x:Class="Artemis.UI.Screens.Root.Sidebar.Dialogs.SidebarCategoryEditView">
|
||||
<StackPanel>
|
||||
<StackPanel.KeyBindings>
|
||||
<KeyBinding Gesture="Enter" Command="{Binding Confirm}" />
|
||||
@ -5,9 +5,9 @@ using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public class SidebarCategoryCreateView : ReactiveUserControl<SidebarCategoryCreateViewModel>
|
||||
public class SidebarCategoryEditView : ReactiveUserControl<SidebarCategoryEditViewModel>
|
||||
{
|
||||
public SidebarCategoryCreateView()
|
||||
public SidebarCategoryEditView()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(_ => this.ClearAllDataValidationErrors());
|
||||
@ -8,13 +8,13 @@ using ReactiveUI.Validation.Extensions;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public class SidebarCategoryCreateViewModel : ContentDialogViewModelBase
|
||||
public class SidebarCategoryEditViewModel : ContentDialogViewModelBase
|
||||
{
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly ProfileCategory? _category;
|
||||
private string? _categoryName;
|
||||
|
||||
public SidebarCategoryCreateViewModel(IProfileService profileService, ProfileCategory? category)
|
||||
public SidebarCategoryEditViewModel(IProfileService profileService, ProfileCategory? category)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_category = category;
|
||||
@ -0,0 +1,150 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
||||
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Root.Sidebar.Dialogs"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="850"
|
||||
x:Class="Artemis.UI.Screens.Root.Sidebar.Dialogs.ProfileConfigurationEditView"
|
||||
Title="{Binding DisplayName}"
|
||||
Icon="/Assets/Images/Logo/bow.ico"
|
||||
Width="800"
|
||||
Height="850">
|
||||
<Window.Resources>
|
||||
<converters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
||||
</Window.Resources>
|
||||
<Grid Margin="16" RowDefinitions="*,Auto">
|
||||
<ScrollViewer>
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Style Selector="TextBlock.label">
|
||||
<Setter Property="Margin" Value="0 10 0 5" />
|
||||
</Style>
|
||||
</StackPanel.Styles>
|
||||
|
||||
<Grid>
|
||||
<TextBlock Classes="h4" IsVisible="{Binding IsNew}">Add a new profile</TextBlock>
|
||||
<TextBlock Classes="h4" IsVisible="{Binding !IsNew}" Text="{Binding ProfileConfiguration.Name}" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock Classes="h5">General</TextBlock>
|
||||
|
||||
<Border Classes="card" Margin="0 0 0 15">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label">Profile name</TextBlock>
|
||||
<TextBox Text="{Binding ProfileName}" />
|
||||
<TextBlock Classes="label">Module</TextBlock>
|
||||
<ComboBox SelectedItem="{Binding SelectedModule}" IsEnabled="{Binding Modules.Count}" Items="{Binding Modules}" HorizontalAlignment="Stretch">
|
||||
<ComboBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ComboBox.ItemsPanel>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type local:ProfileModuleViewModel}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:ArtemisIcon Icon="{Binding Icon}" Width="16" Height="16" Margin="0 0 5 0" />
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<Grid>
|
||||
<TextBlock Classes="subtitle" IsVisible="{Binding Modules.Count}">Optional and binds the profile to the selected module, making module data available</TextBlock>
|
||||
<TextBlock Classes="subtitle" IsVisible="{Binding !Modules.Count}">No available modules were found</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Classes="label">Icon type</TextBlock>
|
||||
<WrapPanel Orientation="Horizontal">
|
||||
<RadioButton Content="Material Icon"
|
||||
IsChecked="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.MaterialIcon}}" />
|
||||
<RadioButton Content="Bitmap image"
|
||||
IsChecked="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.BitmapImage}}" />
|
||||
<RadioButton Content="SVG image"
|
||||
IsChecked="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.SvgImage}}" />
|
||||
</WrapPanel>
|
||||
|
||||
<TextBlock Classes="label">Icon</TextBlock>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.BitmapImage}}">
|
||||
<Border Background="{DynamicResource CheckerboardBrush}" CornerRadius="{DynamicResource CardCornerRadius}" Width="98" Height="98">
|
||||
<Image Source="{Binding SelectedBitmapSource}" Margin="10"/>
|
||||
</Border>
|
||||
<Button Command="{Binding BrowseBitmapFile}"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="10 0"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.BitmapImage}}">
|
||||
Browse bitmap file
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.SvgImage}}">
|
||||
<Border Background="{DynamicResource CheckerboardBrush}" CornerRadius="{DynamicResource CardCornerRadius}" Width="98" Height="98">
|
||||
<Image Margin="10" Source="{Binding SelectedSvgSource}">
|
||||
|
||||
</Image>
|
||||
</Border>
|
||||
<Button Command="{Binding BrowseSvgFile}"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="10 0"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.SvgImage}}">
|
||||
Browse SVG file
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.MaterialIcon}}">
|
||||
<Border Background="{DynamicResource CheckerboardBrush}" CornerRadius="{DynamicResource CardCornerRadius}" Width="98" Height="98">
|
||||
<avalonia:MaterialIcon Kind="{Binding SelectedMaterialIcon.Icon}" Width="65" Height="65" />
|
||||
</Border>
|
||||
<ComboBox Items="{Binding MaterialIcons}"
|
||||
SelectedItem="{Binding SelectedMaterialIcon}"
|
||||
VirtualizationMode="Simple"
|
||||
VerticalAlignment="Bottom"
|
||||
IsTextSearchEnabled="True"
|
||||
Margin="10 0"
|
||||
Width="250"
|
||||
IsVisible="{Binding IconType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationIconType.MaterialIcon}}">
|
||||
<ComboBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ComboBox.ItemsPanel>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="local:ProfileIconViewModel">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<avalonia:MaterialIcon Kind="{Binding Icon}" Margin="0 0 5 0" />
|
||||
<TextBlock Text="{Binding DisplayName}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<TextBlock Classes="h5">Keybindings</TextBlock>
|
||||
<TextBlock Classes="subtitle">You may set up hotkeys to activate/deactivate the profile</TextBlock>
|
||||
<Border Classes="card" Margin="0 5 0 15">
|
||||
<TextBlock>TODO</TextBlock>
|
||||
</Border>
|
||||
|
||||
<TextBlock Classes="h5">Activation conditions</TextBlock>
|
||||
<TextBlock Classes="subtitle">If you only want this profile to be active under certain conditions, configure those conditions below</TextBlock>
|
||||
<Border Classes="card" Margin="0 5 0 15">
|
||||
<TextBlock>TODO</TextBlock>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
|
||||
<Button Grid.Column="0" Command="{Binding Import}">Import profile</Button>
|
||||
<Button Grid.Column="1" Margin="10" Classes="accent" Command="{Binding Confirm}">Confirm</Button>
|
||||
<Button Grid.Column="2" Command="{Binding Cancel}">Cancel</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Window>
|
||||
@ -0,0 +1,23 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public partial class ProfileConfigurationEditView : ReactiveWindow<ProfileConfigurationEditViewModel>
|
||||
{
|
||||
public ProfileConfigurationEditView()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Avalonia.Threading;
|
||||
using Castle.Core.Resource;
|
||||
using Material.Icons;
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public class ProfileConfigurationEditViewModel : DialogViewModelBase<bool>
|
||||
{
|
||||
private readonly ProfileCategory _profileCategory;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IWindowService _windowService;
|
||||
private ProfileConfigurationIconType _iconType;
|
||||
private ObservableCollection<ProfileIconViewModel>? _materialIcons;
|
||||
private ProfileConfiguration _profileConfiguration;
|
||||
private string _profileName;
|
||||
private Bitmap? _selectedBitmapSource;
|
||||
private ProfileIconViewModel? _selectedMaterialIcon;
|
||||
private ProfileModuleViewModel? _selectedModule;
|
||||
private string? _selectedIconPath;
|
||||
private SvgImage? _selectedSvgSource;
|
||||
|
||||
public ProfileConfigurationEditViewModel(ProfileCategory profileCategory, ProfileConfiguration? profileConfiguration, IWindowService windowService,
|
||||
IProfileService profileService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_profileCategory = profileCategory;
|
||||
_windowService = windowService;
|
||||
_profileService = profileService;
|
||||
_profileConfiguration = profileConfiguration ?? profileService.CreateProfileConfiguration(profileCategory, "New profile", Enum.GetValues<MaterialIconKind>().First().ToString());
|
||||
_profileName = _profileConfiguration.Name;
|
||||
_iconType = _profileConfiguration.Icon.IconType;
|
||||
|
||||
IsNew = profileConfiguration == null;
|
||||
DisplayName = IsNew ? "Artemis | Add profile" : "Artemis | Edit profile";
|
||||
Modules = new ObservableCollection<ProfileModuleViewModel>(
|
||||
pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m))
|
||||
);
|
||||
|
||||
Dispatcher.UIThread.Post(LoadIcon, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
public bool IsNew { get; }
|
||||
|
||||
public ProfileConfiguration ProfileConfiguration
|
||||
{
|
||||
get => _profileConfiguration;
|
||||
set => this.RaiseAndSetIfChanged(ref _profileConfiguration, value);
|
||||
}
|
||||
|
||||
public string ProfileName
|
||||
{
|
||||
get => _profileName;
|
||||
set => this.RaiseAndSetIfChanged(ref _profileName, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<ProfileModuleViewModel> Modules { get; }
|
||||
|
||||
public ProfileModuleViewModel? SelectedModule
|
||||
{
|
||||
get => _selectedModule;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedModule, value);
|
||||
}
|
||||
|
||||
public async Task Import()
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog()
|
||||
.HavingFilter(f => f.WithExtension("json").WithName("Artemis profile"))
|
||||
.ShowAsync();
|
||||
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
string json = await File.ReadAllTextAsync(result[0]);
|
||||
ProfileConfigurationExportModel? profileConfigurationExportModel = null;
|
||||
try
|
||||
{
|
||||
profileConfigurationExportModel = JsonConvert.DeserializeObject<ProfileConfigurationExportModel>(json, IProfileService.ExportSettings);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("Import profile failed", e);
|
||||
}
|
||||
|
||||
if (profileConfigurationExportModel == null)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Import profile", "Failed to import this profile, make sure it is a valid Artemis profile.", "Confirm", null);
|
||||
return;
|
||||
}
|
||||
|
||||
_profileService.ImportProfile(_profileCategory, profileConfigurationExportModel);
|
||||
Close(true);
|
||||
}
|
||||
|
||||
public async Task Confirm()
|
||||
{
|
||||
ProfileConfiguration.Name = ProfileName;
|
||||
ProfileConfiguration.Module = SelectedModule?.Module;
|
||||
await SaveIcon();
|
||||
|
||||
_profileService.SaveProfileConfigurationIcon(ProfileConfiguration);
|
||||
_profileService.SaveProfileCategory(_profileCategory);
|
||||
Close(true);
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if (IsNew)
|
||||
_profileService.RemoveProfileConfiguration(_profileConfiguration);
|
||||
Close(false);
|
||||
}
|
||||
|
||||
#region Icon
|
||||
|
||||
public ProfileConfigurationIconType IconType
|
||||
{
|
||||
get => _iconType;
|
||||
set => this.RaiseAndSetIfChanged(ref _iconType, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<ProfileIconViewModel>? MaterialIcons
|
||||
{
|
||||
get => _materialIcons;
|
||||
set => this.RaiseAndSetIfChanged(ref _materialIcons, value);
|
||||
}
|
||||
|
||||
public ProfileIconViewModel? SelectedMaterialIcon
|
||||
{
|
||||
get => _selectedMaterialIcon;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedMaterialIcon, value);
|
||||
}
|
||||
|
||||
public Bitmap? SelectedBitmapSource
|
||||
{
|
||||
get => _selectedBitmapSource;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedBitmapSource, value);
|
||||
}
|
||||
|
||||
public SvgImage? SelectedSvgSource
|
||||
{
|
||||
get => _selectedSvgSource;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedSvgSource, value);
|
||||
}
|
||||
|
||||
private void LoadIcon()
|
||||
{
|
||||
// Preselect the icon based on streams if needed
|
||||
if (_profileConfiguration.Icon.IconType == ProfileConfigurationIconType.BitmapImage)
|
||||
{
|
||||
SelectedBitmapSource = new Bitmap(_profileConfiguration.Icon.GetIconStream());
|
||||
}
|
||||
else if (_profileConfiguration.Icon.IconType == ProfileConfigurationIconType.SvgImage)
|
||||
{
|
||||
SvgSource newSource = new();
|
||||
newSource.Load(_profileConfiguration.Icon.GetIconStream());
|
||||
SelectedSvgSource = new SvgImage {Source = newSource};
|
||||
}
|
||||
|
||||
// Prepare the contents of the dropdown box, it should be virtualized so no need to wait with this
|
||||
MaterialIcons = new ObservableCollection<ProfileIconViewModel>(Enum.GetValues<MaterialIconKind>()
|
||||
.Select(kind => new ProfileIconViewModel(kind))
|
||||
.DistinctBy(vm => vm.DisplayName)
|
||||
.OrderBy(vm => vm.DisplayName));
|
||||
|
||||
// Preselect the icon or fall back to a random one
|
||||
SelectedMaterialIcon = !IsNew && Enum.TryParse(_profileConfiguration.Icon.IconName, out MaterialIconKind enumValue)
|
||||
? MaterialIcons.FirstOrDefault(m => m.Icon == enumValue)
|
||||
: MaterialIcons.ElementAt(new Random().Next(0, MaterialIcons.Count - 1));
|
||||
}
|
||||
|
||||
private async Task SaveIcon()
|
||||
{
|
||||
if (IconType == ProfileConfigurationIconType.MaterialIcon && SelectedMaterialIcon != null)
|
||||
ProfileConfiguration.Icon.SetIconByName(SelectedMaterialIcon.Icon.ToString());
|
||||
else if (_selectedIconPath != null)
|
||||
{
|
||||
await using FileStream fileStream = File.OpenRead(_selectedIconPath);
|
||||
ProfileConfiguration.Icon.SetIconByStream(Path.GetFileName(_selectedIconPath), fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BrowseBitmapFile()
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog()
|
||||
.HavingFilter(f => f.WithExtension("png").WithExtension("jpg").WithExtension("bmp").WithName("Bitmap image"))
|
||||
.ShowAsync();
|
||||
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
SelectedBitmapSource = new Bitmap(result[0]);
|
||||
_selectedIconPath = result[0];
|
||||
}
|
||||
|
||||
public async Task BrowseSvgFile()
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog()
|
||||
.HavingFilter(f => f.WithExtension("svg").WithName("SVG image"))
|
||||
.ShowAsync();
|
||||
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
SvgSource newSource = new();
|
||||
newSource.Load(result[0]);
|
||||
|
||||
SelectedSvgSource = new SvgImage {Source = newSource};
|
||||
_selectedIconPath = result[0];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
using Artemis.UI.Shared;
|
||||
using Material.Icons;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public class ProfileIconViewModel : ViewModelBase
|
||||
{
|
||||
public ProfileIconViewModel(MaterialIconKind icon)
|
||||
{
|
||||
Icon = icon;
|
||||
DisplayName = icon.ToString();
|
||||
}
|
||||
|
||||
public MaterialIconKind Icon { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared;
|
||||
using Material.Icons;
|
||||
|
||||
namespace Artemis.UI.Screens.Root.Sidebar.Dialogs
|
||||
{
|
||||
public class ProfileModuleViewModel : ViewModelBase
|
||||
{
|
||||
public ProfileModuleViewModel(Module module)
|
||||
{
|
||||
Module = module;
|
||||
Name = module.Info.Name;
|
||||
Icon = module.Info.ResolvedIcon ?? MaterialIconKind.QuestionMark.ToString();
|
||||
Description = module.Info.Description;
|
||||
}
|
||||
|
||||
public string Icon { get; }
|
||||
public string Name { get; }
|
||||
public string? Description { get; }
|
||||
|
||||
public Module Module { get; }
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Grid x:Name="ContainerGrid" Margin="0 8 0 0" RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" Background="Transparent" ColumnDefinitions="Auto,Auto,*,Auto,Auto">
|
||||
<Grid Grid.Row="0" Background="Transparent" ColumnDefinitions="Auto,Auto,*,Auto,Auto,Auto">
|
||||
|
||||
<avalonia:MaterialIcon Classes.chevron-collapsed="{Binding ShowItems}"
|
||||
Kind="ChevronUp"
|
||||
@ -84,11 +84,17 @@
|
||||
<ToggleButton Classes="category-button icon-button icon-button-small"
|
||||
Grid.Column="3"
|
||||
ToolTip.Tip="Suspend profile"
|
||||
Margin="2 0 0 0"
|
||||
Margin="5 0"
|
||||
IsChecked="{Binding IsSuspended}">
|
||||
<avalonia:MaterialIcon Kind="Pause" />
|
||||
</ToggleButton>
|
||||
|
||||
<Button Classes="category-button icon-button icon-button-small"
|
||||
Grid.Column="4"
|
||||
ToolTip.Tip="Add profile"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding AddProfile}">
|
||||
<avalonia:MaterialIcon Kind="Plus" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1">
|
||||
|
||||
@ -74,15 +74,21 @@ namespace Artemis.UI.Screens.Root.Sidebar
|
||||
{
|
||||
await _windowService.CreateContentDialog()
|
||||
.WithTitle("Edit category")
|
||||
.WithViewModel<SidebarCategoryCreateViewModel>(out var vm, ("category", ProfileCategory))
|
||||
.WithViewModel<SidebarCategoryEditViewModel>(out var vm, ("category", ProfileCategory))
|
||||
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
|
||||
.HavingSecondaryButton(b => b.WithText("Delete").WithCommand(vm.Delete))
|
||||
.WithCloseButtonText("Cancel")
|
||||
.WithDefaultButton(ContentDialogButton.Primary)
|
||||
.ShowAsync();
|
||||
|
||||
_sidebarViewModel.UpdateProfileCategories();
|
||||
}
|
||||
|
||||
public async Task AddProfile()
|
||||
{
|
||||
await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, bool>(("profileCategory", ProfileCategory), ("profileConfiguration", null));
|
||||
}
|
||||
|
||||
private void CreateProfileViewModels()
|
||||
{
|
||||
ProfileConfigurations.Clear();
|
||||
|
||||
@ -7,22 +7,8 @@
|
||||
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
|
||||
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Root.Sidebar.SidebarView">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" IsHitTestVisible="False">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid RowDefinitions="60,Auto,Auto,*,Auto,Auto">
|
||||
<Grid Grid.Row="0" IsHitTestVisible="False" ColumnDefinitions="Auto,*">
|
||||
<Image Grid.Column="0">
|
||||
<Image.Source>
|
||||
<svg:SvgImage Source="/Assets/Images/Logo/bow.svg" />
|
||||
|
||||
@ -90,8 +90,9 @@ namespace Artemis.UI.Screens.Root.Sidebar
|
||||
{
|
||||
await _windowService.CreateContentDialog()
|
||||
.WithTitle("Add new category")
|
||||
.WithViewModel<SidebarCategoryCreateViewModel>(out var vm, ("category", null))
|
||||
.WithViewModel<SidebarCategoryEditViewModel>(out var vm, ("category", null))
|
||||
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
|
||||
.WithCloseButtonText("Cancel")
|
||||
.WithDefaultButton(ContentDialogButton.Primary)
|
||||
.ShowAsync();
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="/ArtemisTrayIcon.axaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<SolidColorBrush x:Key="Warning">Yellow</SolidColorBrush>
|
||||
</ResourceDictionary>
|
||||
</Styles.Resources>
|
||||
<!-- Third party styles -->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user