Profiles - Reworked profile system

Sidebar - Redesigned sidebar with customizable categories
Profiles - Added the ability to configure custom profile icons
Profiles - Added the ability to activate multiple profiles for modules at once
Profiles - Added the ability to create profiles for no modules
Profiles - Added the ability to suspend a profile or an entire category
Profiles - Added profile activation conditions
Profiles - Added file-based importing/exporting
Profile editor - Condensed UI, removed tabs 
Profile editor - Disable condition operators until a left-side is picked
This commit is contained in:
Robert Beekman authored and GitHub committed 2021-06-03 22:34:43 +02:00
1 parent 4152c290d2
commit ceeaa4bf6d
164 files changed
+5019 -4220

No files matched your search

@@ -0,0 +1,48 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit.ModuleActivationRequirementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:ModuleActivationRequirementViewModel}">
<Border BorderThickness="0 1 0 0" BorderBrush="{DynamicResource MaterialDesignDivider}" Padding="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Text="{Binding RequirementName}" />
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding RequirementDescription}" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleButton Style="{StaticResource MaterialDesignActionToggleButton}"
Focusable="False"
IsHitTestVisible="False"
IsChecked="{Binding RequirementMet}">
<ToggleButton.Content>
<Border Background="#E74C4C" Width="32" Height="32">
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</ToggleButton.Content>
<materialDesign:ToggleButtonAssist.OnContent>
<materialDesign:PackIcon Kind="Check" />
</materialDesign:ToggleButtonAssist.OnContent>
</ToggleButton>
</StackPanel>
</Grid>
</Border>
</UserControl>
@@ -0,0 +1,75 @@
using System;
using System.Timers;
using Artemis.Core.Modules;
using Humanizer;
using Stylet;
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public sealed class ModuleActivationRequirementViewModel : Screen, IDisposable
{
private readonly IModuleActivationRequirement _activationRequirement;
private readonly Timer _updateTimer;
private string _requirementDescription;
private bool _requirementMet;
public ModuleActivationRequirementViewModel(IModuleActivationRequirement activationRequirement)
{
_activationRequirement = activationRequirement;
_updateTimer = new Timer(500);
RequirementName = activationRequirement.GetType().Name.Humanize();
RequirementDescription = activationRequirement.GetUserFriendlyDescription();
_updateTimer.Elapsed += UpdateTimerOnElapsed;
}
public string RequirementName { get; }
public string RequirementDescription
{
get => _requirementDescription;
set => SetAndNotify(ref _requirementDescription, value);
}
public bool RequirementMet
{
get => _requirementMet;
set => SetAndNotify(ref _requirementMet, value);
}
protected override void OnActivate()
{
Update();
_updateTimer.Start();
base.OnActivate();
}
protected override void OnDeactivate()
{
_updateTimer.Stop();
base.OnDeactivate();
}
private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
Update();
}
private void Update()
{
RequirementDescription = _activationRequirement.GetUserFriendlyDescription();
RequirementMet = _activationRequirement.Evaluate();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
_updateTimer?.Dispose();
}
#endregion
}
}
@@ -0,0 +1,27 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit.ModuleActivationRequirementsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:ModuleActivationRequirementsViewModel}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Style="{StaticResource MaterialDesignTextBlock}">
This module has built-in activation requirements and your profile won't activate until
<Run Text="{Binding ActivationType}" FontWeight="Medium" Foreground="{StaticResource SecondaryHueMidBrush}" />. <LineBreak />
These requirements allow the module creator to decide when the data is available to your profile you cannot override them.
</TextBlock>
<ItemsControl ItemsSource="{Binding Items}" Margin="0 10 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>
@@ -0,0 +1,43 @@
using System.Linq;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Stylet;
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public class ModuleActivationRequirementsViewModel : Conductor<ModuleActivationRequirementViewModel>.Collection.AllActive
{
private readonly ISidebarVmFactory _sidebarVmFactory;
private Module _module;
private string _activationType;
public ModuleActivationRequirementsViewModel(ISidebarVmFactory sidebarVmFactory)
{
_sidebarVmFactory = sidebarVmFactory;
}
public Module Module
{
get => _module;
set => SetAndNotify(ref _module, value);
}
public string ActivationType
{
get => _activationType;
set => SetAndNotify(ref _activationType, value);
}
public void SetModule(Module module)
{
Module = module;
ActivationType = Module != null && Module.ActivationRequirementMode == ActivationRequirementType.All
? "all requirements are met"
: "any requirement is met";
Items.Clear();
if (Module != null)
Items.AddRange(Module.ActivationRequirements.Select(_sidebarVmFactory.ModuleActivationRequirementViewModel));
}
}
}
@@ -0,0 +1,304 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit.ProfileEditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
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: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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="280"
d:DataContext="{d:DesignInstance {x:Type profileEdit:ProfileEditViewModel}}"
Width="800">
<UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter"/>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</UserControl.Resources>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Margin="0 0 0 10"
Visibility="{Binding IsNew, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
Add a new profile
</TextBlock>
<TextBlock Grid.Row="0"
Grid.Column="0"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Margin="0 0 0 10"
Visibility="{Binding IsNew, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<Run Text="{Binding ProfileConfiguration.Name}" /> | Properties
</TextBlock>
<StackPanel Grid.Row="1" Grid.Column="0">
<StackPanel>
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 0 0 14">
General
</TextBlock>
</StackPanel>
<TextBox materialDesign:HintAssist.Hint="Profile name"
Margin="0 4 0 8"
Style="{StaticResource MaterialDesignFilledTextBox}"
Text="{Binding ProfileName, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox materialDesign:HintAssist.Hint="Module"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:HintAssist.HelperText="Optional and binds the profile to the selected module, making module data available."
Margin="0 0 0 32"
Style="{StaticResource MaterialDesignFilledComboBox}"
SelectedItem="{Binding SelectedModule}">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="False"
Visibility="{Binding HasUsableModules, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
No usable modules found
</ComboBoxItem>
<CollectionContainer Collection="{Binding Data.Modules, Source={StaticResource DataContextProxy}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type profileEdit:ProfileModuleViewModel}">
<StackPanel Orientation="Horizontal">
<shared:ArtemisIcon Icon="{Binding Icon}" Width="16" Height="16" Margin="0 0 5 0" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox materialDesign:HintAssist.Hint="Icon type"
Margin="0 0 0 8"
Style="{StaticResource MaterialDesignFilledComboBox}"
SelectedValue="{Binding SelectedIconType}"
ItemsSource="{Binding IconTypes}"
SelectedValuePath="Value"
DisplayMemberPath="Description" />
<ContentControl VerticalAlignment="Center">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIconType}" Value="{x:Static core:ProfileConfigurationIconType.MaterialIcon}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<materialDesign:PackIcon Grid.Column="0"
Kind="{Binding DataContext.SelectedIcon.Icon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=OneWay}"
Width="45"
Height="45"
Margin="0 0 10 0"
VerticalAlignment="Center" />
<ComboBox Grid.Column="1"
materialDesign:HintAssist.Hint="Profile icon"
Style="{StaticResource MaterialDesignFilledComboBox}"
SelectedItem="{Binding DataContext.SelectedIcon, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
IsEnabled="{Binding DataContext.Initializing, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource InverseBooleanConverter}}"
ItemsSource="{Binding DataContext.Icons, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
TextSearch.TextPath="IconName">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type profileEdit:ProfileIconViewModel}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" Width="16" Margin="0 0 5 0" />
<TextBlock Text="{Binding IconName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedIconType}" Value="{x:Static core:ProfileConfigurationIconType.BitmapImage}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding DataContext.SelectedImage, Converter={StaticResource StreamToBitmapImageConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
RenderOptions.BitmapScalingMode="HighQuality"
Margin="0 0 10 0"
Width="45"
Height="45"
VerticalAlignment="Center" />
<Button Grid.Column="1"
Command="{s:Action SelectBitmapFile}"
Style="{StaticResource MaterialDesignRaisedButton}"
ToolTip="Select an image">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Paperclip" Margin="0 0 10 0" />
<TextBlock>BROWSE IMAGE</TextBlock>
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedIconType}" Value="{x:Static core:ProfileConfigurationIconType.SvgImage}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<svgc:SvgViewbox Grid.Column="0"
StreamSource="{Binding DataContext.SelectedImage, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Margin="0 0 10 0"
Width="45"
Height="45"
VerticalAlignment="Center" />
<Button Grid.Column="1"
Command="{s:Action SelectSvgFile}"
Style="{StaticResource MaterialDesignRaisedButton}"
ToolTip="Select an image">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Paperclip" Margin="0 0 10 0" />
<TextBlock>BROWSE IMAGE</TextBlock>
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</StackPanel>
<Border Grid.Row="1" Grid.Column="1" BorderThickness="1" BorderBrush="{StaticResource MaterialDesignDivider}" Width="1" Margin="10 0" />
<Grid Grid.Row="1" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Keybinds
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody2TextBlock}">You may set up keybinds to activate/deactivate the profile</TextBlock>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Margin="16">
<materialDesign:PackIcon Kind="Crane" Width="100" Height="100" HorizontalAlignment="Center" />
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 20">
Keybinds are not yet implemented
</TextBlock>
</StackPanel>
</StackPanel>
</Grid>
<Border Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="0 16" BorderThickness="1" Height="1" BorderBrush="{StaticResource MaterialDesignDivider}" />
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Activation conditions
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody2TextBlock}">If you only want this profile to be active under certain conditions, configure those conditions below</TextBlock>
</StackPanel>
<materialDesign:Card Grid.Row="1" Background="{DynamicResource MaterialDesignToolBarBackground}" Padding="10">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
MinHeight="150"
MaxHeight="300"
Margin="0 5">
<ContentControl s:View.Model="{Binding ActivationConditionViewModel}" />
</ScrollViewer>
</materialDesign:Card>
<Grid Grid.Row="2" Margin="0 10" Visibility="{Binding SelectedModule, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderThickness="1" Height="1" BorderBrush="{StaticResource MaterialDesignDivider}" />
<TextBlock Grid.Column="1" Margin="16 0" Style="{StaticResource MaterialDesignBody2TextBlock}">AND</TextBlock>
<Border Grid.Column="2" BorderThickness="1" Height="1" BorderBrush="{StaticResource MaterialDesignDivider}" />
</Grid>
<materialDesign:Card Grid.Row="3"
Background="{DynamicResource MaterialDesignToolBarBackground}"
Padding="10"
Visibility="{Binding SelectedModule, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}">
<ContentControl s:View.Model="{Binding ModuleActivationRequirementsViewModel}" />
</materialDesign:Card>
</Grid>
<Grid Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" Margin="0 16 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
HorizontalAlignment="Left"
Style="{StaticResource MaterialDesignFlatAccentButton}"
Command="{s:Action Delete}"
Visibility="{Binding IsNew, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
DELETE
</Button>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" Command="{s:Action Accept}">
ACCEPT
</Button>
</StackPanel>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using FluentValidation;
using MaterialDesignThemes.Wpf;
using Ookii.Dialogs.Wpf;
using Stylet;
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public class ProfileEditViewModel : DialogViewModelBase
{
private readonly DataModelConditionGroup _dataModelConditionGroup;
private readonly List<Module> _modules;
private readonly IProfileService _profileService;
private bool _changedImage;
private bool _initializing;
private string _profileName;
private ProfileIconViewModel _selectedIcon;
private ProfileConfigurationIconType _selectedIconType;
private Stream _selectedImage;
private ProfileModuleViewModel _selectedModule;
public ProfileEditViewModel(ProfileConfiguration profileConfiguration, bool isNew,
IProfileService profileService,
IPluginManagementService pluginManagementService,
ISidebarVmFactory sidebarVmFactory,
IDataModelConditionsVmFactory dataModelConditionsVmFactory,
IModelValidator<ProfileEditViewModel> validator) : base(validator)
{
ProfileConfiguration = profileConfiguration;
IsNew = isNew;
_profileService = profileService;
_dataModelConditionGroup = ProfileConfiguration.ActivationCondition ?? new DataModelConditionGroup(null);
_modules = ProfileConfiguration.Module != null ? new List<Module> {ProfileConfiguration.Module} : new List<Module>();
IconTypes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ProfileConfigurationIconType)));
Icons = new BindableCollection<ProfileIconViewModel>();
Modules = new BindableCollection<ProfileModuleViewModel>(
pluginManagementService.GetFeaturesOfType<Module>().Where(m => !m.IsAlwaysAvailable).Select(m => new ProfileModuleViewModel(m))
);
Initializing = true;
ActivationConditionViewModel = dataModelConditionsVmFactory.DataModelConditionGroupViewModel(_dataModelConditionGroup, ConditionGroupType.General, _modules);
ActivationConditionViewModel.ConductWith(this);
ActivationConditionViewModel.IsRootGroup = true;
ModuleActivationRequirementsViewModel = new ModuleActivationRequirementsViewModel(sidebarVmFactory);
ModuleActivationRequirementsViewModel.ConductWith(this);
ModuleActivationRequirementsViewModel.SetModule(ProfileConfiguration.Module);
_profileName = ProfileConfiguration.Name;
_selectedModule = Modules.FirstOrDefault(m => m.Module == ProfileConfiguration.Module);
_selectedIconType = ProfileConfiguration.Icon.IconType;
_selectedImage = ProfileConfiguration.Icon.FileIcon;
Task.Run(() =>
{
Icons.AddRange(Enum.GetValues<PackIconKind>()
.GroupBy(i => i).Select(g => g.First()).Select(i => new ProfileIconViewModel(i))
.OrderBy(i => i.IconName)
.ToList());
if (IsNew)
SelectedIcon = Icons[new Random().Next(0, Icons.Count - 1)];
else
SelectedIcon = Icons.FirstOrDefault(i => i.Icon.ToString() == ProfileConfiguration.Icon.MaterialIcon);
Initializing = false;
});
}
public ProfileConfiguration ProfileConfiguration { get; }
public bool IsNew { get; }
public BindableCollection<ValueDescription> IconTypes { get; }
public BindableCollection<ProfileIconViewModel> Icons { get; }
public BindableCollection<ProfileModuleViewModel> Modules { get; }
public bool HasUsableModules => Modules.Any();
public bool Initializing
{
get => _initializing;
set => SetAndNotify(ref _initializing, value);
}
public string ProfileName
{
get => _profileName;
set => SetAndNotify(ref _profileName, value);
}
public ProfileConfigurationIconType SelectedIconType
{
get => _selectedIconType;
set
{
if (!SetAndNotify(ref _selectedIconType, value)) return;
SelectedImage = null;
}
}
public Stream SelectedImage
{
get => _selectedImage;
set => SetAndNotify(ref _selectedImage, value);
}
public ProfileIconViewModel SelectedIcon
{
get => _selectedIcon;
set => SetAndNotify(ref _selectedIcon, value);
}
public ProfileModuleViewModel SelectedModule
{
get => _selectedModule;
set
{
if (!SetAndNotify(ref _selectedModule, value)) return;
_modules.Clear();
if (value != null)
_modules.Add(value.Module);
ActivationConditionViewModel.UpdateModules();
ModuleActivationRequirementsViewModel.SetModule(value?.Module);
}
}
public DataModelConditionGroupViewModel ActivationConditionViewModel { get; }
public ModuleActivationRequirementsViewModel ModuleActivationRequirementsViewModel { get; }
public void Delete()
{
Session.Close(nameof(Delete));
}
public async Task Accept()
{
await ValidateAsync();
if (HasErrors)
return;
ProfileConfiguration.Name = ProfileName;
ProfileConfiguration.Icon.IconType = SelectedIconType;
ProfileConfiguration.Icon.MaterialIcon = SelectedIcon?.Icon.ToString();
ProfileConfiguration.Icon.FileIcon = SelectedImage;
ProfileConfiguration.Module = SelectedModule?.Module;
if (_dataModelConditionGroup.Children.Any())
ProfileConfiguration.ActivationCondition = _dataModelConditionGroup;
if (_changedImage)
{
ProfileConfiguration.Icon.FileIcon = SelectedImage;
_profileService.SaveProfileConfigurationIcon(ProfileConfiguration);
}
_profileService.SaveProfileCategory(ProfileConfiguration.Category);
Session.Close(nameof(Accept));
}
public void SelectBitmapFile()
{
VistaOpenFileDialog dialog = new()
{
Filter = "All Graphics Types|*.bmp;*.jpg;*.jpeg;*.png;*.tif;*.tiff|BMP |*.bmp|GIF|*.gif|JPG|*.jpg;*.jpeg|PNG|*.png|TIFF|*.tif;*.tiff",
Title = "Select profile icon"
};
bool? result = dialog.ShowDialog();
if (result != true)
return;
_changedImage = true;
// TODO: Scale down to 100x100-ish
SelectedImage = File.OpenRead(dialog.FileName);
}
public void SelectSvgFile()
{
VistaOpenFileDialog dialog = new()
{
Filter = "Scalable Vector Graphics|*.svg",
Title = "Select profile icon"
};
bool? result = dialog.ShowDialog();
if (result != true)
return;
_changedImage = true;
SelectedImage = File.OpenRead(dialog.FileName);
}
}
public class ProfileEditViewModelValidator : AbstractValidator<ProfileEditViewModel>
{
public ProfileEditViewModelValidator()
{
RuleFor(m => m.ProfileName).NotEmpty().WithMessage("Profile name may not be empty");
}
}
}
@@ -0,0 +1,16 @@
using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public class ProfileIconViewModel
{
public ProfileIconViewModel(PackIconKind icon)
{
Icon = icon;
IconName = icon.ToString();
}
public PackIconKind Icon { get; }
public string IconName { get; }
}
}
@@ -0,0 +1,22 @@
using Artemis.Core.Modules;
using Stylet;
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public class ProfileModuleViewModel : PropertyChangedBase
{
public ProfileModuleViewModel(Module module)
{
Module = module;
Icon = module.DisplayIcon;
Name = module.DisplayName;
Description = module.Info.Description;
}
public string Icon { get; }
public string Name { get; }
public string Description { get; }
public Module Module { get; }
}
}