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

@@ -1,26 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.Modules.ModuleRootView"
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:modules="clr-namespace:Artemis.UI.Screens.Modules"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance modules:ModuleRootViewModel}">
<TabControl Margin="0 -1 0 0"
ItemsSource="{Binding Items}"
SelectedItem="{Binding ActiveItem}"
DisplayMemberPath="DisplayName"
Style="{StaticResource MaterialDesignTabControl}">
<TabControl.ContentTemplate>
<DataTemplate>
<materialDesign:TransitioningContent OpeningEffect="{materialDesign:TransitionEffect FadeIn}">
<ContentControl s:View.Model="{Binding IsAsync=True}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</materialDesign:TransitioningContent>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</UserControl>
@@ -1,57 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Modules;
using Ninject;
using Ninject.Parameters;
using Stylet;
namespace Artemis.UI.Screens.Modules
{
public class ModuleRootViewModel : Conductor<Screen>.Collection.OneActive
{
private readonly IModuleVmFactory _moduleVmFactory;
public ModuleRootViewModel(Module module, IModuleVmFactory moduleVmFactory)
{
DisplayName = module?.DisplayName;
Module = module;
_moduleVmFactory = moduleVmFactory;
}
public Module Module { get; }
protected override void OnInitialActivate()
{
AddTabs();
base.OnInitialActivate();
}
private void AddTabs()
{
// Create the profile editor and module VMs
if (Module is ProfileModule profileModule)
Items.Add(_moduleVmFactory.CreateProfileEditorViewModel(profileModule));
if (Module.ActivationRequirements.Any())
Items.Add(_moduleVmFactory.CreateActivationRequirementsViewModel(Module));
if (Module.ModuleTabs != null)
{
List<ModuleTab> moduleTabs = new(Module.ModuleTabs);
foreach (ModuleTab moduleTab in moduleTabs.Where(m => m != null))
{
ConstructorArgument module = new("module", Module);
ConstructorArgument displayName = new("displayName", DisplayName);
ModuleViewModel viewModel = (ModuleViewModel) Module.Plugin.Kernel.Get(moduleTab.Type, module, displayName);
Items.Add(viewModel);
}
}
ActiveItem = Items.FirstOrDefault();
}
}
}
@@ -1,44 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.Modules.Tabs.ActivationRequirementView"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Modules.Tabs"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance tabs:ActivationRequirementViewModel}">
<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>
</UserControl>
@@ -1,54 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.Modules.Tabs.ActivationRequirementsView"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:local="clr-namespace:Artemis.UI.Screens.Modules.Tabs"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:ActivationRequirementsViewModel}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="15" MaxWidth="800">
<!-- General settings -->
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Activation requirements</TextBlock>
<TextBlock Margin="0 0 0 15" TextWrapping="Wrap" Style="{StaticResource MaterialDesignTextBlock}">
This module has built-in activation requirements and won't activate until
<Run Text="{Binding ActivationType}" FontWeight="Medium" Foreground="{StaticResource SecondaryHueMidBrush}" />. <LineBreak />
These requirements allow the module creator to decide when the module is activated and you cannot override them.
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap">
Note: While you have the profile editor open the module is always activated and any other modules are deactivated.
</TextBlock>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Separator Margin="0 -10">
<Separator.Style>
<Style TargetType="Separator" BasedOn="{StaticResource MaterialDesignSeparator}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Separator.Style>
</Separator>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" Margin="20" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</materialDesign:Card>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -1,36 +0,0 @@
using System.Linq;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Stylet;
namespace Artemis.UI.Screens.Modules.Tabs
{
public class ActivationRequirementsViewModel : Conductor<ActivationRequirementViewModel>.Collection.AllActive
{
private readonly IModuleVmFactory _moduleVmFactory;
public ActivationRequirementsViewModel(Module module, IModuleVmFactory moduleVmFactory)
{
_moduleVmFactory = moduleVmFactory;
DisplayName = "ACTIVATION REQUIREMENTS";
Module = module;
ActivationType = Module.ActivationRequirementMode == ActivationRequirementType.All
? "all requirements are met"
: "any requirement is met";
}
public Module Module { get; }
public string ActivationType { get; set; }
protected override void OnActivate()
{
Items.Clear();
Items.AddRange(Module.ActivationRequirements.Select(_moduleVmFactory.CreateActivationRequirementViewModel));
base.OnActivate();
}
}
}
-20
View File
@@ -1,20 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.News.NewsView"
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Margin="16">
<materialDesign:PackIcon Kind="Crane" Width="250" Height="250" HorizontalAlignment="Center" />
<TextBlock Style="{StaticResource MaterialDesignHeadline4TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 25">
News is not yet implemented
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 25">
The news page will keep you up-to-date with the latest developments in the Artemis community. <LineBreak />
You'll find the latest patch notes here and see featured workshop contributions.<LineBreak /><LineBreak />
</TextBlock>
</StackPanel>
</UserControl>
@@ -1,12 +0,0 @@
using Stylet;
namespace Artemis.UI.Screens.News
{
public class NewsViewModel : Screen, IMainScreenViewModel
{
public NewsViewModel()
{
DisplayName = "News";
}
}
}
@@ -4,6 +4,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Input;
@@ -16,6 +17,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
{
private readonly IConditionOperatorService _conditionOperatorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
private DataModelStaticViewModel _rightSideInputViewModel;
private DataModelDynamicViewModel _rightSideSelectionViewModel;
@@ -25,11 +27,13 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
protected DataModelConditionPredicateViewModel(
DataModelConditionPredicate dataModelConditionPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService) : base(dataModelConditionPredicate)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_conditionOperatorService = conditionOperatorService;
@@ -44,6 +48,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model;
public PluginSetting<bool> ShowDataModelValues { get; }
public bool CanSelectOperator => DataModelConditionPredicate.LeftPath is {IsValid: true};
public BaseConditionOperator SelectedOperator
{
get => _selectedOperator;
@@ -75,12 +81,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public virtual void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected;
if (LeftSideColor != null)
LeftSideSelectionViewModel.ButtonBrush = LeftSideColor;
@@ -107,10 +113,11 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
else if (!Operators.Contains(DataModelConditionPredicate.Operator))
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.Description == DataModelConditionPredicate.Operator.Description) ?? Operators.FirstOrDefault());
NotifyOfPropertyChange(nameof(CanSelectOperator));
SelectedOperator = DataModelConditionPredicate.Operator;
// Without a selected operator or one that supports a right side, leave the right side input empty
if (SelectedOperator == null || SelectedOperator.RightSideType == null)
if (SelectedOperator?.RightSideType == null)
{
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
@@ -132,7 +139,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
DisposeRightSideDynamicViewModel();
if (RightSideInputViewModel == null)
CreateRightSideInputViewModel();
Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType();
if (preferredType != null && RightSideInputViewModel.TargetType != preferredType)
RightSideInputViewModel.UpdateTargetType(preferredType);
@@ -149,7 +156,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
return;
DataModelConditionPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
SelectedOperator = DataModelConditionPredicate.Operator;
Update();
@@ -158,7 +165,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
public void ApplyRightSideDynamic()
{
DataModelConditionPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -166,7 +173,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
public void ApplyRightSideStatic(object value)
{
DataModelConditionPredicate.UpdateRightSideStatic(value);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -174,7 +181,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
public void ApplyOperator()
{
DataModelConditionPredicate.UpdateOperator(SelectedOperator);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -196,6 +203,26 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
ApplyOperator();
}
public override void UpdateModules()
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStaticViewModel();
DisposeRightSideDynamicViewModel();
// If the modules changed the paths may no longer be valid if they targeted a module no longer available, in that case clear the path
if (DataModelConditionPredicate.LeftPath?.Target != null && !DataModelConditionPredicate.LeftPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.LeftPath.Target.Module))
DataModelConditionPredicate.UpdateLeftSide(null);
if (DataModelConditionPredicate.RightPath?.Target != null && !DataModelConditionPredicate.RightPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.RightPath.Target.Module))
DataModelConditionPredicate.UpdateRightSideDynamic(null);
Initialize();
}
#region IDisposable
protected virtual void Dispose(bool disposing)
@@ -227,7 +254,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
private void CreateRightSideSelectionViewModel()
{
RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
RightSideSelectionViewModel.ButtonBrush = (SolidColorBrush) Application.Current.FindResource("PrimaryHueMidBrush");
RightSideSelectionViewModel.DisplaySwitchButton = true;
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
@@ -63,5 +63,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract
groupViewModel.ConvertToPredicate(this);
return true;
}
public abstract void UpdateModules();
}
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
@@ -14,15 +15,18 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
private DateTime _lastTrigger;
private string _triggerPastParticiple;
public DataModelConditionEventViewModel(DataModelConditionEvent dataModelConditionEvent,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionEvent)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
@@ -46,7 +50,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
public void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideSelectionViewModelOnPropertySelected;
LeftSideSelectionViewModel.LoadEventChildren = false;
@@ -82,7 +86,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
if (!(childModel is DataModelConditionGroup dataModelConditionGroup))
continue;
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.Event);
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.Event, _modules);
viewModel.IsRootGroup = true;
viewModels.Add(viewModel);
}
@@ -103,11 +107,18 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
public void ApplyEvent()
{
DataModelConditionEvent.UpdateEvent(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
public override void UpdateModules()
{
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel.PropertySelected -= LeftSideSelectionViewModelOnPropertySelected;
Initialize();
}
protected override void OnInitialActivate()
{
Initialize();
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
@@ -14,17 +16,23 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
public class DataModelConditionGroupViewModel : DataModelConditionViewModel
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly List<Module> _modules;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private bool _isEventGroup;
private bool _isRootGroup;
public DataModelConditionGroupViewModel(DataModelConditionGroup dataModelConditionGroup,
ConditionGroupType groupType,
List<Module> modules,
ICoreService coreService,
IProfileEditorService profileEditorService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory)
: base(dataModelConditionGroup)
{
GroupType = groupType;
_modules = modules;
_coreService = coreService;
_profileEditorService = profileEditorService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
@@ -66,7 +74,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionGroup.BooleanOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
@@ -87,7 +95,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
}
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddEventCondition()
@@ -104,7 +112,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionGroup.AddChild(new DataModelConditionEvent(DataModelConditionGroup), index);
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddGroup()
@@ -112,7 +120,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionGroup.AddChild(new DataModelConditionGroup(DataModelConditionGroup));
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public override void Update()
@@ -131,22 +139,22 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
switch (childModel)
{
case DataModelConditionGroup dataModelConditionGroup:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, GroupType));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, GroupType, _modules));
break;
case DataModelConditionList dataModelConditionList:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(dataModelConditionList));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(dataModelConditionList, _modules));
break;
case DataModelConditionEvent dataModelConditionEvent:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventViewModel(dataModelConditionEvent));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventViewModel(dataModelConditionEvent, _modules));
break;
case DataModelConditionGeneralPredicate dataModelConditionGeneralPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGeneralPredicateViewModel(dataModelConditionGeneralPredicate));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGeneralPredicateViewModel(dataModelConditionGeneralPredicate, _modules));
break;
case DataModelConditionListPredicate dataModelConditionListPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(dataModelConditionListPredicate));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(dataModelConditionListPredicate, _modules));
break;
case DataModelConditionEventPredicate dataModelConditionEventPredicate:
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventPredicateViewModel(dataModelConditionEventPredicate));
Items.Add(_dataModelConditionsVmFactory.DataModelConditionEventPredicateViewModel(dataModelConditionEventPredicate, _modules));
break;
}
}
@@ -173,6 +181,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
dataModelConditionViewModel.Evaluate();
}
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
public void ConvertToConditionList(DataModelConditionViewModel predicateViewModel)
{
// Store the old index and remove the old predicate
@@ -203,8 +217,33 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
Update();
}
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
{
if (IsRootGroup)
Evaluate();
}
public event EventHandler Updated;
#region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate()
{
base.OnInitialActivate();
Update();
_coreService.FrameRendered += CoreServiceOnFrameRendered;
}
/// <inheritdoc />
protected override void OnClose()
{
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
base.OnClose();
}
#endregion
protected virtual void OnUpdated()
{
Updated?.Invoke(this, EventArgs.Empty);
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms.VisualStyles;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
@@ -16,14 +16,16 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly List<Module> _modules;
private readonly IProfileEditorService _profileEditorService;
public DataModelConditionListViewModel(
DataModelConditionList dataModelConditionList,
public DataModelConditionListViewModel(DataModelConditionList dataModelConditionList,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IDataModelConditionsVmFactory dataModelConditionsVmFactory) : base(dataModelConditionList)
{
_modules = modules;
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
@@ -39,7 +41,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionList.ListOperator = enumValue;
NotifyOfPropertyChange(nameof(SelectedListOperator));
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
@@ -47,7 +49,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionList.AddChild(new DataModelConditionGeneralPredicate(DataModelConditionList, ProfileRightSideType.Dynamic));
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddGroup()
@@ -55,25 +57,32 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
DataModelConditionList.AddChild(new DataModelConditionGroup(DataModelConditionList));
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public override void Evaluate()
{
IsConditionMet = DataModelConditionList.Evaluate();
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.Evaluate();
}
public override void Delete()
{
base.Delete();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
/// <inheritdoc />
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
public void Initialize()
{
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules);
LeftSideSelectionViewModel.PropertySelected += LeftSideSelectionViewModelOnPropertySelected;
IReadOnlyCollection<DataModelVisualizationRegistration> editors = _dataModelUIService.RegisteredDataModelEditors;
@@ -96,7 +105,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
return;
DataModelConditionList.UpdateList(LeftSideSelectionViewModel.DataModelPath);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -120,7 +129,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
if (!(childModel is DataModelConditionGroup dataModelConditionGroup))
continue;
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.List);
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, ConditionGroupType.List, _modules);
viewModel.IsRootGroup = true;
viewModels.Add(viewModel);
}
@@ -55,6 +55,7 @@
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
IsEnabled="{Binding CanSelectOperator}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
@@ -16,11 +17,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionEventPredicateViewModel(DataModelConditionEventPredicate dataModelConditionEventPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionEventPredicate, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
: base(dataModelConditionEventPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
@@ -56,6 +56,7 @@
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
IsEnabled="{Binding CanSelectOperator}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
@@ -14,11 +15,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionGeneralPredicateViewModel(DataModelConditionGeneralPredicate dataModelConditionGeneralPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionGeneralPredicate, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
: base(dataModelConditionGeneralPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
}
@@ -55,6 +55,7 @@
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}"
IsEnabled="{Binding CanSelectOperator}"
Click="PropertyButton_OnClick">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
@@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
@@ -16,11 +16,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
private readonly IDataModelUIService _dataModelUIService;
public DataModelConditionListPredicateViewModel(DataModelConditionListPredicate dataModelConditionListPredicate,
List<Module> modules,
IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService,
IConditionOperatorService conditionOperatorService,
ISettingsService settingsService)
: base(dataModelConditionListPredicate, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
: base(dataModelConditionListPredicate, modules, profileEditorService, dataModelUIService, conditionOperatorService, settingsService)
{
_dataModelUIService = dataModelUIService;
@@ -44,6 +45,17 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
}
}
public override void Evaluate()
{
throw new NotImplementedException();
}
public override void UpdateModules()
{
foreach (DataModelConditionViewModel dataModelConditionViewModel in Items)
dataModelConditionViewModel.UpdateModules();
}
protected override void OnInitialActivate()
{
base.OnInitialActivate();
@@ -81,10 +93,5 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
return wrapper.CreateViewModel(_dataModelUIService, new DataModelUpdateConfiguration(true));
}
public override void Evaluate()
{
throw new NotImplementedException();
}
}
}
@@ -1,30 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Dialogs.ProfileCreateView"
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"
mc:Ignorable="d"
d:DesignHeight="213.053" d:DesignWidth="254.425">
<StackPanel Margin="16">
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
Add a new profile
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="Profile name"
Margin="0 8 0 16"
Width="300"
Style="{StaticResource MaterialDesignFilledTextBox}"
Text="{Binding ProfileName, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 0 8 0" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 0 0 0" Command="{s:Action Accept}">
ACCEPT
</Button>
</StackPanel>
</StackPanel>
</UserControl>
@@ -1,40 +0,0 @@
using System.Threading.Tasks;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{
public class ProfileCreateViewModel : DialogViewModelBase
{
private string _profileName;
public ProfileCreateViewModel(IModelValidator<ProfileCreateViewModel> validator) : base(validator)
{
}
public string ProfileName
{
get => _profileName;
set => SetAndNotify(ref _profileName, value);
}
public async Task Accept()
{
await ValidateAsync();
if (HasErrors)
return;
Session.Close(ProfileName);
}
}
public class ProfileCreateViewModelValidator : AbstractValidator<ProfileCreateViewModel>
{
public ProfileCreateViewModelValidator()
{
RuleFor(m => m.ProfileName).NotEmpty().WithMessage("Profile name may not be empty");
}
}
}
@@ -1,42 +0,0 @@
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{
public class ProfileEditViewModel : DialogViewModelBase
{
private string _profileName;
public ProfileEditViewModel(IModelValidator<ProfileEditViewModel> validator, Profile profile) : base(validator)
{
ProfileName = profile.Name;
}
public string ProfileName
{
get => _profileName;
set => SetAndNotify(ref _profileName, value);
}
public async Task Accept()
{
await ValidateAsync();
if (HasErrors)
return;
Session.Close(ProfileName);
}
}
public class ProfileEditViewModelValidator : AbstractValidator<ProfileEditViewModel>
{
public ProfileEditViewModelValidator()
{
RuleFor(m => m.ProfileName).NotEmpty().WithMessage("Profile name may not be empty");
}
}
}
@@ -1,44 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Dialogs.ProfileExportView"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="16" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Grid.Row="0">
Export current profile
</TextBlock>
<TextBlock Grid.Row="1"
Margin="0 10"
TextWrapping="Wrap"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}">
It looks like you have not set up any profile adaption hints. This means Artemis can't do much to make your profile look good on a different surface other than try finding the same LEDs as you have.
<LineBreak/><LineBreak/>
To configure adaption hints, right-click on a layer and choose <materialDesign:PackIcon Kind="AutoFix" /> <Run FontWeight="Bold">View Adaption Hints</Run>.
<LineBreak/><LineBreak/>
To learn more about profile adaption, check out
<Hyperlink Style="{StaticResource ArtemisHyperlink}" RequestNavigate="{s:Action OpenHyperlink}" NavigateUri="https://wiki.artemis-rgb.com/guides/user/profiles/layers/adaption-hints">
this wiki article
</Hyperlink>.
</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="2">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 8 0 0" Command="{s:Action Accept}">
EXPORT ANYWAY
</Button>
</StackPanel>
</Grid>
</UserControl>
@@ -1,49 +0,0 @@
using System.Windows;
using System.Windows.Navigation;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{
public class ProfileExportViewModel : DialogViewModelBase
{
private readonly IProfileService _profileService;
private readonly IMessageService _messageService;
public ProfileExportViewModel(ProfileDescriptor profileDescriptor, IProfileService profileService, IMessageService messageService)
{
ProfileDescriptor = profileDescriptor;
_profileService = profileService;
_messageService = messageService;
}
public ProfileDescriptor ProfileDescriptor { get; }
#region Overrides of Screen
/// <inheritdoc />
protected override void OnActivate()
{
// TODO: If the profile has hints on all layers, call Accept
base.OnActivate();
}
#endregion
public void OpenHyperlink(object sender, RequestNavigateEventArgs e)
{
Core.Utilities.OpenUrl(e.Uri.AbsoluteUri);
}
public void Accept()
{
string encoded = _profileService.ExportProfile(ProfileDescriptor);
Clipboard.SetText(encoded);
_messageService.ShowMessage("Profile contents exported to clipboard.");
Session.Close();
}
}
}
@@ -1,61 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Dialogs.ProfileImportView"
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:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="16" Width="800">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Grid.Row="0">
Import profile to current module
</TextBlock>
<TextBlock Grid.Row="1"
Margin="0 10"
TextWrapping="Wrap"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}">
Please note that importing profiles like this is placeholder functionality. The idea is that this will eventually happen via the workshop.
</TextBlock>
<TextBlock Grid.Row="2"
Margin="0 10"
TextWrapping="Wrap"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}">
The workshop will include tools to make profiles convert easily and look good on different layouts.
That means right now when you import this profile unless you have the exact same setup as
the person who exported it, you'll have to select LEDs for each layer in the profile.
</TextBlock>
<TextBox Grid.Row="3"
Text="{Binding ProfileJson}"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
FontFamily="Consolas"
VerticalAlignment="Top"
Height="400"
AcceptsReturn="True"
VerticalScrollBarVisibility="Auto"
materialDesign:HintAssist.Hint="Paste profile JSON here"
Margin="16" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="4">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 8 0 0" Command="{s:Action Accept}">
I UNDERSTAND, IMPORT
</Button>
</StackPanel>
</Grid>
</UserControl>
@@ -1,37 +0,0 @@
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{
public class ProfileImportViewModel : DialogViewModelBase
{
private readonly IProfileService _profileService;
private readonly IMessageService _messageService;
private string _profileJson;
public ProfileImportViewModel(ProfileModule profileModule, IProfileService profileService, IMessageService messageService)
{
ProfileModule = profileModule;
_profileService = profileService;
_messageService = messageService;
}
public ProfileModule ProfileModule { get; }
public string ProfileJson
{
get => _profileJson;
set => SetAndNotify(ref _profileJson, value);
}
public void Accept()
{
ProfileDescriptor descriptor = _profileService.ImportProfile(ProfileJson, ProfileModule);
_messageService.ShowMessage("Profile imported.");
Session.Close(descriptor);
}
}
}
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
@@ -14,17 +16,15 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public class DisplayConditionsViewModel : Conductor<DataModelConditionGroupViewModel>, IProfileEditorPanelViewModel
{
private readonly IDataModelConditionsVmFactory _dataModelConditionsVmFactory;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement _renderProfileElement;
private bool _displayStartHint;
private bool _isEventCondition;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory, ICoreService coreService)
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDataModelConditionsVmFactory dataModelConditionsVmFactory)
{
_profileEditorService = profileEditorService;
_dataModelConditionsVmFactory = dataModelConditionsVmFactory;
_coreService = coreService;
}
public bool DisplayStartHint
@@ -59,7 +59,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
TimelinePlayMode playMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
if (RenderProfileElement == null || RenderProfileElement?.Timeline.PlayMode == playMode) return;
RenderProfileElement.Timeline.PlayMode = playMode;
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
}
@@ -71,7 +71,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
TimelineStopMode stopMode = value ? TimelineStopMode.Finish : TimelineStopMode.SkipToEnd;
if (RenderProfileElement == null || RenderProfileElement?.Timeline.StopMode == stopMode) return;
RenderProfileElement.Timeline.StopMode = stopMode;
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
}
@@ -82,7 +82,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
if (RenderProfileElement == null || RenderProfileElement?.Timeline.EventOverlapMode == value) return;
RenderProfileElement.Timeline.EventOverlapMode = value;
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
}
@@ -90,19 +90,17 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
protected override void OnInitialActivate()
{
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
_coreService.FrameRendered += CoreServiceOnFrameRendered;
_profileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
base.OnInitialActivate();
}
protected override void OnClose()
{
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
_profileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
base.OnClose();
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
private void SelectedProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
if (RenderProfileElement != null)
{
@@ -127,9 +125,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (e.RenderProfileElement.DisplayCondition == null)
e.RenderProfileElement.DisplayCondition = new DataModelConditionGroup(null);
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(e.RenderProfileElement.DisplayCondition, ConditionGroupType.General);
List<Module> modules = new();
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
modules.Add(_profileEditorService.SelectedProfileConfiguration.Module);
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(e.RenderProfileElement.DisplayCondition, ConditionGroupType.General, modules);
ActiveItem.IsRootGroup = true;
ActiveItem.Update();
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
IsEventCondition = RenderProfileElement.DisplayCondition.Children.Any(c => c is DataModelConditionEvent);
@@ -146,11 +146,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
NotifyOfPropertyChange(nameof(EventOverlapMode));
}
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
{
ActiveItem?.Evaluate();
}
private void DisplayConditionOnChildrenModified(object sender, EventArgs e)
{
DisplayStartHint = !RenderProfileElement.DisplayCondition.Children.Any();
@@ -159,7 +154,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public void EventTriggerModeSelected()
{
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
}
}
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services;
@@ -15,16 +14,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
IDataBindingModeViewModel
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private bool _updating;
public ConditionalDataBindingModeViewModel(ConditionalDataBinding<TLayerProperty, TProperty> conditionalDataBinding,
ICoreService coreService,
IProfileEditorService profileEditorService,
IDataBindingsVmFactory dataBindingsVmFactory)
{
_coreService = coreService;
_profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory;
@@ -41,7 +37,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
DataBindingConditionViewModel<TLayerProperty, TProperty> viewModel = Items.First(c => c.DataBindingCondition == condition);
viewModel.ActiveItem.AddCondition();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void RemoveCondition(DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition)
@@ -52,22 +48,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
protected override void OnInitialActivate()
{
Initialize();
_coreService.FrameRendered += CoreServiceOnFrameRendered;
base.OnInitialActivate();
}
protected override void OnClose()
{
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
base.OnClose();
}
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
{
foreach (DataBindingConditionViewModel<TLayerProperty, TProperty> dataBindingConditionViewModel in Items)
dataBindingConditionViewModel.Evaluate();
}
private void UpdateItems()
{
_updating = true;
@@ -111,7 +94,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
ConditionalDataBinding.ApplyOrder();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void ConditionalDataBindingOnConditionsUpdated(object sender, EventArgs e)
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Conditions;
using Artemis.UI.Shared;
@@ -34,7 +36,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
protected override void OnInitialActivate()
{
base.OnInitialActivate();
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataBindingCondition.Condition, ConditionGroupType.General);
List<Module> modules = new();
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
modules.Add(_profileEditorService.SelectedProfileConfiguration.Module);
ActiveItem = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataBindingCondition.Condition, ConditionGroupType.General, modules);
ActiveItem.IsRootGroup = true;
ActiveItem.Update();
@@ -54,12 +59,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
private void ValueViewModelOnValueUpdated(object sender, DataModelInputStaticEventArgs e)
{
DataBindingCondition.Value = (TProperty) Convert.ChangeType(e.Value, typeof(TProperty));
_profileEditorService.UpdateSelectedProfileElement();
}
public void Evaluate()
{
ActiveItem?.Evaluate();
_profileEditorService.SaveSelectedProfileElement();
}
public void AddCondition()
@@ -178,7 +178,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Registration.DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
}
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -198,7 +198,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
}
CreateDataBindingModeModeViewModel();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void UpdateTestResult()
@@ -243,7 +243,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
return;
Registration.LayerProperty.EnableDataBinding(Registration);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void RemoveDataBinding()
@@ -255,7 +255,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Registration.LayerProperty.DisableDataBinding(Registration.DataBinding);
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void CopyDataBinding()
@@ -282,7 +282,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
@@ -9,6 +9,7 @@
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding"
xmlns:modifierTypes="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding.ModifierTypes"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@@ -16,7 +17,7 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
<ResourceDictionary>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
@@ -71,19 +71,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
public void Delete()
{
Modifier.DirectDataBinding.RemoveModifier(Modifier);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
Modifier.UpdateParameterDynamic(e.DataModelPath);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void StaticInputViewModelOnValueUpdated(object sender, DataModelInputStaticEventArgs e)
{
Modifier.UpdateParameterStatic(e.Value);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void Update()
@@ -100,7 +100,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
else if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
{
DisposeStaticInputViewModel();
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.SelectedProfileConfiguration.Module);
if (DynamicSelectionViewModel != null)
{
DynamicSelectionViewModel.DisplaySwitchButton = true;
@@ -149,7 +149,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
return;
Modifier.UpdateModifierType(modifierTypeViewModel.ModifierType);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared;
@@ -87,7 +88,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
private void Initialize()
{
DirectDataBinding.ModifiersUpdated += DirectDataBindingOnModifiersUpdated;
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.SelectedProfileConfiguration.Module);
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
ModifierViewModels.CollectionChanged += ModifierViewModelsOnCollectionChanged;
Update();
@@ -106,7 +107,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
DirectDataBinding.ApplyOrder();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
#region Target
@@ -116,7 +117,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
DirectDataBinding.UpdateSource(e.DataModelPath);
Update();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
#endregion
@@ -126,7 +127,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
public void AddModifier()
{
DirectDataBinding.AddModifier(ProfileRightSideType.Dynamic);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void UpdateModifierViewModels()
@@ -70,7 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
{
await Task.Delay(500);
renderElement.AddLayerEffect(SelectedLayerEffectDescriptor);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
});
}
}
@@ -18,7 +18,7 @@
d:DataContext="{d:DesignInstance {x:Type layerProperties:LayerPropertiesViewModel}}"
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True">
<UserControl.Resources>
<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<Converters:NullToBooleanConverter x:Key="NullToBooleanConverter" />
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
<Style x:Key="SvStyle" TargetType="{x:Type ScrollViewer}">
@@ -187,7 +187,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
{
PopulateProperties(ProfileEditorService.SelectedProfileElement);
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
ProfileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
ProfileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
@@ -197,7 +197,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
protected override void OnClose()
{
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
ProfileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
@@ -218,7 +218,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
EffectsViewModel.PopulateDescriptors();
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
private void SelectedProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
PopulateProperties(e.RenderProfileElement);
}
@@ -419,7 +419,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
MoveAfter(source, target);
ApplyCurrentEffectsOrder();
ProfileEditorService.UpdateSelectedProfile();
ProfileEditorService.SaveSelectedProfileConfiguration();
}
private void MoveBefore(LayerPropertyGroupViewModel source, LayerPropertyGroupViewModel target)
@@ -118,7 +118,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
foreach (TimelineEasingViewModel propertyTrackEasingViewModel in EasingViewModels.Where(vm => vm != easingViewModel))
propertyTrackEasingViewModel.IsEasingModeSelected = false;
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
#endregion
@@ -174,7 +174,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{
LayerPropertyKeyframe.LayerProperty.RemoveKeyframe(LayerPropertyKeyframe);
if (save)
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
#endregion
@@ -103,7 +103,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
if (Segment != SegmentViewModelType.Main || SelectedProfileElement == null)
return;
SelectedProfileElement.Timeline.PlayMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
NotifyOfPropertyChange(nameof(RepeatSegment));
}
}
@@ -159,7 +159,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
protected override void OnInitialActivate()
{
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
if (ProfileEditorService.SelectedProfileElement != null)
ProfileEditorService.SelectedProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
@@ -171,7 +171,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
protected override void OnClose()
{
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
if (SelectedProfileElement != null)
SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
base.OnClose();
@@ -263,7 +263,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
}
ShiftNextSegment(SegmentLength - oldSegmentLength);
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -277,7 +277,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
else if (Segment == SegmentViewModelType.End)
SelectedProfileElement.Timeline.EndSegmentLength = TimeSpan.FromSeconds(1);
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
Update();
}
@@ -296,7 +296,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
((IInputElement) sender).ReleaseMouseCapture();
_draggingSegment = false;
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
}
public void SegmentMouseMove(object sender, MouseEventArgs e)
@@ -367,7 +367,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#region Event handlers
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
private void SelectedProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
if (e.PreviousRenderProfileElement != null)
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
@@ -84,7 +84,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
Update();
}
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
private void SelectedProfileEditorServiceOnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
if (e.PreviousRenderProfileElement != null)
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
@@ -100,7 +100,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
protected override void OnInitialActivate()
{
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
_profileEditorService.SelectedProfileElementChanged += SelectedProfileEditorServiceOnSelectedProfileElementChanged;
if (_profileEditorService.SelectedProfileElement != null)
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
Update();
@@ -111,7 +111,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
protected override void OnClose()
{
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
_profileEditorService.SelectedProfileElementChanged -= SelectedProfileEditorServiceOnSelectedProfileElementChanged;
if (_profileEditorService.SelectedProfileElement != null)
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
base.OnClose();
@@ -142,7 +142,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
public void KeyframeMouseUp(object sender, MouseButtonEventArgs e)
{
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
ReleaseSelectedKeyframes();
((IInputElement) sender).ReleaseMouseCapture();
@@ -200,7 +200,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
foreach (ITimelineKeyframeViewModel keyframeViewModel in keyframeViewModels)
keyframeViewModel.Delete(false);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void DuplicateKeyframes(object sender)
@@ -213,7 +213,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in GetAllKeyframeViewModels())
timelineKeyframeViewModel.IsSelected = newKeyframes.Contains(timelineKeyframeViewModel.Keyframe);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public void CopyKeyframes()
@@ -230,7 +230,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in GetAllKeyframeViewModels())
timelineKeyframeViewModel.IsSelected = newKeyframes.Contains(timelineKeyframeViewModel.Keyframe);
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private TimeSpan GetPastePosition(ITimelineKeyframeViewModel viewModel)
@@ -14,8 +14,8 @@
d:DataContext="{d:DesignInstance local:TreeGroupViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<converters:PropertyTreeMarginConverter Length="20" x:Key="PropertyTreeMarginConverter" />
</UserControl.Resources>
@@ -79,7 +79,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
_windowManager.ShowDialog(_layerBrushSettingsWindowVm);
// Save changes after the dialog closes
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
catch (Exception e)
{
@@ -109,7 +109,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
_windowManager.ShowDialog(_layerEffectSettingsWindowVm);
// Save changes after the dialog closes
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
catch (Exception e)
{
@@ -132,7 +132,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
{
LayerPropertyGroup.LayerEffect.Name = newName;
LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
}
@@ -142,12 +142,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
return;
LayerPropertyGroup.ProfileElement.RemoveLayerEffect(LayerPropertyGroup.LayerEffect);
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
public void SuspendedToggled()
{
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
public double GetDepth()
@@ -48,7 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
public void ResetToDefault()
{
LayerProperty.ApplyDefaultValue();
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
private void ApplyKeyframesEnabled(bool enable)
@@ -67,7 +67,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
LayerProperty.KeyframesEnabled = enable;
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
}
public bool HasDataBinding => LayerProperty.HasDataBinding;
@@ -21,30 +21,6 @@
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="SimpleTemplate">
<StackPanel d:DataContext="{d:DesignInstance {x:Type profile:ProfileDescriptor}}" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ExtendedTemplate">
<Grid d:DataContext="{d:DesignInstance {x:Type profile:ProfileDescriptor}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
<Button Grid.Column="1"
Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="26"
Height="26"
VerticalAlignment="Top"
Command="{s:Action DeleteProfile}"
CommandParameter="{Binding}">
<materialDesign:PackIcon Kind="Delete" Height="16" Width="16" HorizontalAlignment="Right" />
</Button>
</Grid>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
@@ -53,7 +29,7 @@
<KeyBinding Command="{s:Action Redo}" Modifiers="Control" Key="Y" />
</UserControl.InputBindings>
<Grid Margin="16">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<!-- Left side -->
<ColumnDefinition Width="*" MinWidth="100" />
@@ -66,8 +42,6 @@
<!-- Left side -->
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<!-- Introduction -->
<RowDefinition Height="35" />
<!-- Design area -->
<RowDefinition Height="*" MinHeight="200" />
<!-- Bottom panels resize -->
@@ -75,25 +49,17 @@
<!-- Bottom panels -->
<RowDefinition Height="{Binding BottomPanelsHeight.Value, Mode=TwoWay}" MinHeight="108" />
</Grid.RowDefinitions>
<!-- Introduction -->
<StackPanel Grid.Row="0">
<TextBlock>
<materialDesign:PackIcon Kind="AboutOutline" /> The profile defines what colors the LEDs will have. Multiple groups of LEDs are defined into layers. On these layers you can apply effects.
</TextBlock>
<Separator Style="{StaticResource MaterialDesignDarkSeparator}" Margin="0 -2.5 0 0" />
</StackPanel>
<!-- Design area -->
<materialDesign:Card Grid.Row="1" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<materialDesign:Card Grid.Row="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<ContentControl s:View.Model="{Binding ProfileViewModel, IsAsync=True}" />
</materialDesign:Card>
<!-- Bottom panels resize -->
<GridSplitter Grid.Row="2" Grid.Column="0" Height="5" HorizontalAlignment="Stretch" Cursor="SizeNS" Margin="0 5" />
<GridSplitter Grid.Row="1" Grid.Column="0" Height="5" HorizontalAlignment="Stretch" Cursor="SizeNS" Margin="0 5" />
<!-- Bottom panels -->
<Grid Grid.Row="3">
<Grid Grid.Row="2">
<!-- Layer elements -->
<materialDesign:Card Grid.Column="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<ContentControl s:View.Model="{Binding LayerPropertiesViewModel, IsAsync=True}" />
@@ -102,105 +68,29 @@
</Grid>
<!-- Side panels resize -->
<GridSplitter Grid.Row="0" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" Cursor="SizeWE" Margin="5 35 5 0" />
<GridSplitter Grid.Row="0" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" Cursor="SizeWE" Margin="5 0" />
<!-- Side panels -->
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<!-- Profile selection -->
<RowDefinition Height="35" />
<!-- Profile elements -->
<!-- Profile elements -->
<RowDefinition Height="*" MinHeight="100" />
<!-- Conditions resize -->
<RowDefinition Height="Auto" />
<!-- Display conditions -->
<RowDefinition Height="{Binding DataModelConditionsHeight.Value, Mode=TwoWay}" MinHeight="100" />
</Grid.RowDefinitions>
<!-- Profile selection -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Height="26"
VerticalAlignment="Top"
ItemsSource="{Binding Profiles}"
SelectedItem="{Binding SelectedProfile}"
ItemTemplateSelector="{dataTemplateSelectors:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource SimpleTemplate},
DropdownItemsTemplate={StaticResource ExtendedTemplate}}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Add a new profile"
Margin="5 0 0 0"
Grid.Column="1"
Width="26"
Height="26"
VerticalAlignment="Top"
Command="{s:Action AddProfile}">
<materialDesign:PackIcon Kind="Add" Height="20" Width="20" />
</Button>
<materialDesign:PopupBox Grid.Column="2" Margin="0 -10 0 0" PlacementMode="BottomAndAlignRightEdges">
<StackPanel>
<Button Command="{s:Action RenameActiveProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="RenameBox" Margin="0 0 15 0" />
<TextBlock>Rename profile</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action DuplicateActiveProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="ContentDuplicate" Margin="0 0 15 0" />
<TextBlock>Duplicate profile</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action DeleteActiveProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Trash" Margin="0 0 15 0" />
<TextBlock>Delete profile</TextBlock>
</StackPanel>
</Button>
<Separator />
<Button Command="{s:Action AdaptActiveProfile}" ToolTip="Adapt the profile to the current surface">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="AutoFix" Margin="0 0 15 0" />
<TextBlock>Adapt profile</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action ExportActiveProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Upload" Margin="0 0 15 0" />
<TextBlock>Export profile</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action ImportProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Download" Margin="0 0 15 0" />
<TextBlock>Import profile</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</materialDesign:PopupBox>
</Grid>
<!-- Profile elements -->
<materialDesign:Card Grid.Row="1" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<materialDesign:Card Grid.Row="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<ContentControl s:View.Model="{Binding ProfileTreeViewModel, IsAsync=True}" Margin="0,-1,-0.2,1" />
</materialDesign:Card>
<!-- Conditions resize -->
<GridSplitter Grid.Row="2" Height="5" HorizontalAlignment="Stretch" Cursor="SizeNS" Margin="0 5" />
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" Cursor="SizeNS" Margin="0 5" />
<!-- Display conditions -->
<materialDesign:Card Grid.Row="3" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<materialDesign:Card Grid.Row="2" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch">
<ContentControl s:View.Model="{Binding DisplayConditionsViewModel, IsAsync=True}" />
</materialDesign:Card>
</Grid>
@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.ProfileEditor.Dialogs;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions;
using Artemis.UI.Screens.ProfileEditor.LayerProperties;
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
@@ -18,9 +14,8 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor
{
public class ProfileEditorViewModel : Screen
public class ProfileEditorViewModel : Screen, IMainScreenViewModel
{
private readonly IModuleService _moduleService;
private readonly IMessageService _messageService;
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileService _profileService;
@@ -30,14 +25,11 @@ namespace Artemis.UI.Screens.ProfileEditor
private DisplayConditionsViewModel _displayConditionsViewModel;
private PluginSetting<GridLength> _elementPropertiesWidth;
private LayerPropertiesViewModel _layerPropertiesViewModel;
private BindableCollection<ProfileDescriptor> _profiles;
private ProfileTreeViewModel _profileTreeViewModel;
private ProfileViewModel _profileViewModel;
private ProfileDescriptor _selectedProfile;
private PluginSetting<GridLength> _sidePanelsWidth;
public ProfileEditorViewModel(ProfileModule module,
ProfileViewModel profileViewModel,
public ProfileEditorViewModel(ProfileViewModel profileViewModel,
ProfileTreeViewModel profileTreeViewModel,
DisplayConditionsViewModel dataModelConditionsViewModel,
LayerPropertiesViewModel layerPropertiesViewModel,
@@ -45,21 +37,16 @@ namespace Artemis.UI.Screens.ProfileEditor
IProfileService profileService,
IDialogService dialogService,
ISettingsService settingsService,
IModuleService moduleService,
IMessageService messageService)
{
_profileEditorService = profileEditorService;
_profileService = profileService;
_settingsService = settingsService;
_moduleService = moduleService;
_messageService = messageService;
DisplayName = "PROFILE EDITOR";
Module = module;
DisplayName = "Profile Editor";
DialogService = dialogService;
Profiles = new BindableCollection<ProfileDescriptor>();
// Populate the panels
ProfileViewModel = profileViewModel;
ProfileViewModel.ConductWith(this);
@@ -71,7 +58,6 @@ namespace Artemis.UI.Screens.ProfileEditor
LayerPropertiesViewModel.ConductWith(this);
}
public ProfileModule Module { get; }
public IDialogService DialogService { get; }
public DisplayConditionsViewModel DisplayConditionsViewModel
@@ -98,12 +84,6 @@ namespace Artemis.UI.Screens.ProfileEditor
set => SetAndNotify(ref _profileViewModel, value);
}
public BindableCollection<ProfileDescriptor> Profiles
{
get => _profiles;
set => SetAndNotify(ref _profiles, value);
}
public PluginSetting<GridLength> SidePanelsWidth
{
get => _sidePanelsWidth;
@@ -128,89 +108,9 @@ namespace Artemis.UI.Screens.ProfileEditor
set => SetAndNotify(ref _elementPropertiesWidth, value);
}
public ProfileDescriptor SelectedProfile
{
get => _selectedProfile;
set
{
if (!SetAndNotify(ref _selectedProfile, value)) return;
NotifyOfPropertyChange(nameof(CanDeleteActiveProfile));
ActivateSelectedProfile();
}
}
public bool CanDeleteActiveProfile => SelectedProfile != null && Profiles.Count > 1;
public ProfileDescriptor CreateProfile(string name)
{
ProfileDescriptor profile = _profileService.CreateProfileDescriptor(Module, name);
Profiles.Add(profile);
return profile;
}
public async Task AddProfile()
{
object result = await DialogService.ShowDialog<ProfileCreateViewModel>();
if (result is string name)
{
ProfileDescriptor newProfile = CreateProfile(name);
SelectedProfile = newProfile;
}
}
public async Task DeleteProfile(ProfileDescriptor profileDescriptor)
{
bool result = await DialogService.ShowConfirmDialog(
"Delete profile",
$"Are you sure you want to delete '{profileDescriptor.Name}'? This cannot be undone."
);
if (result)
RemoveProfile(profileDescriptor);
}
public async Task DeleteActiveProfile()
{
bool result = await DialogService.ShowConfirmDialog(
"Delete active profile",
"Are you sure you want to delete your currently active profile? This cannot be undone."
);
if (result)
RemoveProfile(SelectedProfile);
}
public async Task RenameActiveProfile()
{
if (_profileEditorService.SelectedProfile == null)
return;
Profile profile = _profileEditorService.SelectedProfile;
object result = await DialogService.ShowDialog<ProfileEditViewModel>(new Dictionary<string, object> {{"profile", profile}});
if (result is string name)
{
profile.Name = name;
_profileEditorService.UpdateSelectedProfile();
// The descriptors are immutable and need to be reloaded to reflect the name change
LoadProfiles();
SelectedProfile = Profiles.FirstOrDefault(p => p.Id == _profileEditorService.SelectedProfile.EntityId) ?? Profiles.FirstOrDefault();
}
}
public void DuplicateActiveProfile()
{
string encoded = _profileService.ExportProfile(SelectedProfile);
ProfileDescriptor copy = _profileService.ImportProfile(encoded, Module, "copy");
Profiles.Add(copy);
Profiles.Sort(p => p.Name);
SelectedProfile = copy;
}
public async Task AdaptActiveProfile()
{
if (_profileEditorService.SelectedProfile == null)
if (_profileEditorService.SelectedProfileConfiguration?.Profile == null)
return;
if (!await DialogService.ShowConfirmDialog(
@@ -219,30 +119,7 @@ namespace Artemis.UI.Screens.ProfileEditor
))
return;
_profileService.AdaptProfile(_profileEditorService.SelectedProfile);
}
public async Task ExportActiveProfile()
{
await DialogService.ShowDialog<ProfileExportViewModel>(new Dictionary<string, object>
{
{"profileDescriptor", SelectedProfile}
});
}
public async Task ImportProfile()
{
object result = await DialogService.ShowDialog<ProfileImportViewModel>(new Dictionary<string, object>
{
{"profileModule", Module}
});
if (result != null && result is ProfileDescriptor descriptor)
{
Profiles.Add(descriptor);
Profiles.Sort(p => p.Name);
SelectedProfile = descriptor;
}
_profileService.AdaptProfile(_profileEditorService.SelectedProfileConfiguration.Profile);
}
public void Undo()
@@ -253,7 +130,7 @@ namespace Artemis.UI.Screens.ProfileEditor
// Store the focused element so we can restore it later
IInputElement focusedElement = FocusManager.GetFocusedElement(Window.GetWindow(View));
if (!_profileEditorService.UndoUpdateProfile())
if (!_profileEditorService.UndoSaveProfile())
{
_messageService.ShowMessage("Nothing to undo");
return;
@@ -280,7 +157,7 @@ namespace Artemis.UI.Screens.ProfileEditor
// Store the focused element so we can restore it later
IInputElement focusedElement = FocusManager.GetFocusedElement(Window.GetWindow(View));
if (!_profileEditorService.RedoUpdateProfile())
if (!_profileEditorService.RedoSaveProfile())
{
_messageService.ShowMessage("Nothing to redo");
return;
@@ -302,71 +179,17 @@ namespace Artemis.UI.Screens.ProfileEditor
protected override void OnInitialActivate()
{
LoadWorkspaceSettings();
Module.IsProfileUpdatingDisabled = true;
Module.ActiveProfile?.Reset();
Module.ActiveProfileChanged += ModuleOnActiveProfileChanged;
LoadProfiles();
// If the module already has an active profile use that, the override won't trigger a profile change
if (Module.ActiveProfile != null)
SelectedProfile = Profiles.FirstOrDefault(d => d.Id == Module.ActiveProfile.EntityId);
Task.Run(async () => { await _moduleService.SetActiveModuleOverride(Module); });
base.OnInitialActivate();
}
protected override void OnClose()
{
SaveWorkspaceSettings();
Module.IsProfileUpdatingDisabled = false;
Module.ActiveProfile?.Reset();
Module.ActiveProfileChanged -= ModuleOnActiveProfileChanged;
_profileEditorService.ChangeSelectedProfileConfiguration(null);
_profileEditorService.ChangeSelectedProfile(null);
Task.Run(async () => { await _moduleService.SetActiveModuleOverride(null); });
base.OnClose();
}
private void RemoveProfile(ProfileDescriptor profileDescriptor)
{
if (SelectedProfile == profileDescriptor && !CanDeleteActiveProfile)
return;
int index = Profiles.IndexOf(profileDescriptor);
// Get a new active profile
ProfileDescriptor newActiveProfile = index - 1 > -1 ? Profiles[index - 1] : Profiles[index + 1];
// Activate the new active profile
SelectedProfile = newActiveProfile;
// Remove the old one
Profiles.Remove(profileDescriptor);
_profileService.DeleteProfile(profileDescriptor);
}
private void ActivateSelectedProfile()
{
Execute.PostToUIThread(async () =>
{
if (SelectedProfile == null)
return;
Task<Profile> changeTask = _profileService.ActivateProfileAnimated(SelectedProfile);
_profileEditorService.ChangeSelectedProfile(null);
Profile profile = await changeTask;
_profileEditorService.ChangeSelectedProfile(profile);
});
}
private void ModuleOnActiveProfileChanged(object sender, EventArgs e)
{
if (Module.ActiveProfile == null)
SelectedProfile = null;
else
SelectedProfile = Profiles.FirstOrDefault(d => d.Id == Module.ActiveProfile.EntityId);
}
private void LoadWorkspaceSettings()
{
SidePanelsWidth = _settingsService.GetSetting("ProfileEditor.SidePanelsWidth", new GridLength(385));
@@ -382,12 +205,5 @@ namespace Artemis.UI.Screens.ProfileEditor
BottomPanelsHeight.Save();
ElementPropertiesWidth.Save();
}
private void LoadProfiles()
{
// Get all profiles from the database
Profiles.Clear();
Profiles.AddRange(_profileService.GetProfileDescriptors(Module).OrderBy(p => p.Name));
}
}
}
@@ -96,7 +96,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs
/// <inheritdoc />
protected override void OnClose()
{
_profileEditorService.UpdateSelectedProfileElement();
_profileEditorService.SaveSelectedProfileElement();
base.OnClose();
}
@@ -59,13 +59,13 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{
_updatingTree = true;
ProfileElement firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault();
if (!(firstChild is Folder folder))
if (firstChild is not Folder folder)
{
ActivateItem(null);
return;
}
ActivateItem(_profileTreeVmFactory.FolderViewModel(folder));
ActiveItem = _profileTreeVmFactory.FolderViewModel(folder);
_updatingTree = false;
}
@@ -141,7 +141,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
}
Unsubscribe();
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
Subscribe();
}
@@ -177,17 +177,17 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
private void Subscribe()
{
_profileEditorService.ProfileSelected += OnProfileSelected;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
_profileEditorService.SelectedProfileChanged += OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged += OnSelectedProfileElementChanged;
}
private void Unsubscribe()
{
_profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileChanged -= OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged -= OnSelectedProfileElementChanged;
}
private void OnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
private void OnSelectedProfileElementChanged(object sender, RenderProfileElementEventArgs e)
{
if (e.RenderProfileElement == SelectedTreeItem?.ProfileElement)
return;
@@ -213,7 +213,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
}
}
private void OnProfileSelected(object sender, EventArgs e)
private void OnSelectedProfileChanged(object sender, EventArgs e)
{
CreateRootFolderViewModel();
}
@@ -129,7 +129,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
throw new ArtemisUIException("Cannot add a folder to a profile element of type " + ProfileElement.GetType().Name);
Folder _ = new(ProfileElement, "New folder");
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
public void AddLayer()
@@ -145,7 +145,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
layer.ChangeLayerBrush(brush);
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
_profileEditorService.ChangeSelectedProfileElement(layer);
}
@@ -163,7 +163,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
if (result is string newName)
{
ProfileElement.Name = newName;
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
}
@@ -189,7 +189,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
ProfileElement.Parent?.RemoveChild(ProfileElement);
parent.RemoveExistingElement(this);
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
_profileEditorService.ChangeSelectedProfileElement(newSelection as RenderProfileElement);
}
@@ -243,7 +243,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
public void SuspendedToggled()
{
_profileEditorService.UpdateSelectedProfile();
_profileEditorService.SaveSelectedProfileConfiguration();
}
public void ContextMenuOpening(object sender, EventArgs e)
@@ -178,8 +178,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
{
Update();
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementUpdated;
_profileEditorService.SelectedProfileElementChanged += OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved += OnSelectedProfileElementSaved;
_profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated;
_panZoomViewModel.PropertyChanged += PanZoomViewModelOnPropertyChanged;
base.OnInitialActivate();
@@ -189,8 +189,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
protected override void OnClose()
{
Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
_profileEditorService.SelectedProfileElementChanged -= OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved -= OnSelectedProfileElementSaved;
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
_panZoomViewModel.PropertyChanged -= PanZoomViewModelOnPropertyChanged;
base.OnClose();
@@ -205,12 +205,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
Update();
}
private void OnProfileElementSelected(object sender, EventArgs e)
private void OnSelectedProfileElementChanged(object sender, EventArgs e)
{
IsSelected = _profileEditorService.SelectedProfileElement == Layer;
}
private void OnSelectedProfileElementUpdated(object sender, EventArgs e)
private void OnSelectedProfileElementSaved(object sender, EventArgs e)
{
Update();
}
@@ -153,9 +153,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
FocusSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
_profileEditorService.ProfileSelected += OnProfileSelected;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementUpdated;
_profileEditorService.SelectedProfileChanged += OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged += OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved += OnSelectedProfileElementSaved;
base.OnInitialActivate();
}
@@ -166,9 +166,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
FocusSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
_rgbService.DeviceAdded -= RgbServiceOnDevicesModified;
_rgbService.DeviceRemoved -= RgbServiceOnDevicesModified;
_profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
_profileEditorService.SelectedProfileChanged -= OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged -= OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved -= OnSelectedProfileElementSaved;
if (_previousSelectedLayer != null)
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
@@ -330,12 +330,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
UpdateLedsDimStatus();
}
private void OnProfileSelected(object sender, EventArgs e)
private void OnSelectedProfileChanged(object sender, EventArgs e)
{
ApplyActiveProfile();
}
private void OnProfileElementSelected(object sender, EventArgs e)
private void OnSelectedProfileElementChanged(object sender, EventArgs e)
{
if (_previousSelectedLayer != null)
{
@@ -370,7 +370,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
ActivateToolByIndex(1);
}
private void OnSelectedProfileElementUpdated(object sender, EventArgs e)
private void OnSelectedProfileElementSaved(object sender, EventArgs e)
{
ApplyActiveProfile();
UpdateLedsDimStatus();
@@ -75,9 +75,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
/// <inheritdoc />
protected override void OnInitialActivate()
{
ProfileEditorService.ProfileSelected += UpdateEventHandler;
ProfileEditorService.ProfileElementSelected += UpdateEventHandler;
ProfileEditorService.SelectedProfileElementUpdated += UpdateEventHandler;
ProfileEditorService.SelectedProfileChanged += UpdateEventHandler;
ProfileEditorService.SelectedProfileElementChanged += UpdateEventHandler;
ProfileEditorService.SelectedProfileElementSaved += UpdateEventHandler;
ProfileEditorService.ProfilePreviewUpdated += UpdateEventHandler;
Update();
@@ -92,9 +92,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
/// <inheritdoc />
protected override void OnClose()
{
ProfileEditorService.ProfileSelected -= UpdateEventHandler;
ProfileEditorService.ProfileElementSelected -= UpdateEventHandler;
ProfileEditorService.SelectedProfileElementUpdated -= UpdateEventHandler;
ProfileEditorService.SelectedProfileChanged -= UpdateEventHandler;
ProfileEditorService.SelectedProfileElementChanged -= UpdateEventHandler;
ProfileEditorService.SelectedProfileElementSaved -= UpdateEventHandler;
ProfileEditorService.ProfilePreviewUpdated -= UpdateEventHandler;
base.OnClose();
}
@@ -117,7 +117,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
public void RotateMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
_rotating = false;
}
@@ -173,7 +173,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
public void ResizeMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
_isResizing = false;
}
@@ -329,7 +329,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
public void MoveMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
_movingShape = false;
_movingAnchor = false;
}
@@ -45,7 +45,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
layer.ClearLeds();
layer.AddLeds(remainingLeds);
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
}
}
@@ -55,7 +55,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
layer.AddLeds(selectedLeds);
}
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
}
// If no layer selected, apply it to a new layer in the selected folder
else if (ProfileEditorService.SelectedProfileElement is Folder folder)
@@ -94,7 +94,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
newLayer.AddLeds(selectedLeds);
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
ProfileEditorService.UpdateSelectedProfileElement();
ProfileEditorService.SaveSelectedProfileElement();
}
}
}
+42 -83
View File
@@ -7,9 +7,7 @@
xmlns:s="https://github.com/canton7/Stylet"
xmlns:screens="clr-namespace:Artemis.UI.Screens"
xmlns:mde="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:localization="clr-namespace:MaterialDesignExtensions.Localization;assembly=MaterialDesignExtensions"
mc:Ignorable="d"
FadeContentIfInactive="False"
Icon="/Resources/Images/Logo/bow.ico"
@@ -25,97 +23,58 @@
KeyUp="{s:Action WindowKeyUp}"
MouseDown="{s:Action WindowMouseDown}"
MouseUp="{s:Action WindowMouseUp}"
d:DesignHeight="640" d:DesignWidth="1200" d:DataContext="{d:DesignInstance screens:RootViewModel}">
<mde:MaterialWindow.Resources>
<Style TargetType="ContentControl" x:Key="InitializingFade">
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveItemReady}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:0.4">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</mde:MaterialWindow.Resources>
<materialDesign:DialogHost IsTabStop="False"
Focusable="False"
Identifier="RootDialog"
DialogTheme="Inherit"
SnackbarMessageQueue="{Binding MainMessageQueue}">
d:DesignHeight="640" d:DesignWidth="1200"
d:DataContext="{d:DesignInstance screens:RootViewModel}">
<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.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" s:View.Model="{Binding SidebarViewModel}" Width="240" />
<Rectangle Grid.Column="1" Fill="DarkGray" Width="1" Margin="0 0 -1 0">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Color="{StaticResource MaterialDesignShadow}" />
</Rectangle.Effect>
</Rectangle>
</Grid>
</Border>
<materialDesign:DrawerHost IsLeftDrawerOpen="{Binding DockedSidebarViewModel.IsSidebarOpen}">
<materialDesign:DrawerHost.LeftDrawerContent>
<ContentControl s:View.Model="{Binding DockedSidebarViewModel}"
Width="280"
Visibility="{Binding PinSidebar.Value, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"/>
</materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel>
<Border DockPanel.Dock="Left"
DockPanel.ZIndex="2"
ClipToBounds="True"
Background="{DynamicResource MaterialDesignToolBarBackground}"
Visibility="{Binding PinSidebar.Value, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" s:View.Model="{Binding PinnedSidebarViewModel}" Width="280" />
<Rectangle Grid.Column="1" Fill="DarkGray" Width="1" Margin="0 0 -1 0">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Color="{StaticResource MaterialDesignShadow}"/>
</Rectangle.Effect>
</Rectangle>
</Grid>
</Border>
<mde:AppBar Type="Dense"
IsNavigationDrawerOpen="{Binding DockedSidebarViewModel.IsSidebarOpen, Mode=TwoWay}"
Title="{Binding ActiveItem.DisplayName}"
ShowNavigationDrawerButton="{Binding PinSidebar.Value, Converter={StaticResource InverseBooleanConverter}}"
DockPanel.Dock="Top"
DockPanel.ZIndex="1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FrameTime}"
VerticalAlignment="Center"
FontSize="14"
Margin="10 0"
ToolTip="The time the last frame took to render" />
<materialDesign:ColorZone Mode="PrimaryMid" DockPanel.Dock="Top" DockPanel.ZIndex="1" Height="42">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding SidebarViewModel.SelectedScreen.DisplayName}"
VerticalAlignment="Center"
FontSize="20"
Margin="15 0" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="{Binding FrameTime}" VerticalAlignment="Center" FontSize="14" Margin="10 0" ToolTip="The time the last frame took to render" />
<!-- Bug: materialDesign:RippleAssist.RippleOnTop doesn't look as nice but otherwise it doesn't work at all, not sure why -->
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
VerticalAlignment="Center"
ToolTip="Open debugger"
Command="{s:Action ShowDebugger}"
materialDesign:RippleAssist.RippleOnTop="True">
<materialDesign:PackIcon Kind="Matrix" />
</Button>
</StackPanel>
</mde:AppBar>
<Grid>
<ContentControl s:View.Model="{Binding ActiveItem}" Style="{StaticResource InitializingFade}" />
<materialDesign:Snackbar x:Name="MainSnackbar"
MessageQueue="{Binding MainMessageQueue}"
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
</Grid>
</DockPanel>
</materialDesign:DrawerHost>
</materialDesign:ColorZone>
<Grid>
<ContentControl s:View.Model="{Binding SidebarViewModel.SelectedScreen}" />
<materialDesign:Snackbar x:Name="MainSnackbar"
MessageQueue="{Binding MainMessageQueue}"
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
</Grid>
</DockPanel>
</materialDesign:DialogHost>
</mde:MaterialWindow>
+23 -77
View File
@@ -8,8 +8,6 @@ using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Modules;
using Artemis.UI.Screens.Settings.Tabs.General;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services;
@@ -23,20 +21,18 @@ using Constants = Artemis.Core.Constants;
namespace Artemis.UI.Screens
{
public sealed class RootViewModel : Conductor<IScreen>, IDisposable
public sealed class RootViewModel : Conductor<Screen>, IDisposable
{
private readonly IRegistrationService _builtInRegistrationService;
private readonly IMessageService _messageService;
private readonly ICoreService _coreService;
private readonly IWindowManager _windowManager;
private readonly IDebugService _debugService;
private readonly IKernel _kernel;
private readonly IEventAggregator _eventAggregator;
private readonly ISettingsService _settingsService;
private readonly Timer _frameTimeUpdateTimer;
private readonly SidebarViewModel _sidebarViewModel;
private readonly IKernel _kernel;
private readonly IMessageService _messageService;
private readonly ISettingsService _settingsService;
private readonly IWindowManager _windowManager;
private readonly PluginSetting<WindowSize> _windowSize;
private bool _activeItemReady;
private string _frameTime;
private bool _lostFocus;
private ISnackbarMessageQueue _mainMessageQueue;
@@ -62,38 +58,24 @@ namespace Artemis.UI.Screens
_debugService = debugService;
_builtInRegistrationService = builtInRegistrationService;
_messageService = messageService;
_sidebarViewModel = sidebarViewModel;
_frameTimeUpdateTimer = new Timer(500);
_windowSize = _settingsService.GetSetting<WindowSize>("UI.RootWindowSize");
_sidebarViewModel.ConductWith(this);
ActiveItem = sidebarViewModel.SelectedItem;
ActiveItemReady = true;
PinSidebar = _settingsService.GetSetting("UI.PinSidebar", false);
SidebarViewModel = sidebarViewModel;
SidebarViewModel.ConductWith(this);
AssemblyInformationalVersionAttribute versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
WindowTitle = $"Artemis {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}";
}
public PluginSetting<bool> PinSidebar { get; }
// Just a litte trick to get the non-active variant completely removed from XAML (that should probably be done in the view)
public SidebarViewModel PinnedSidebarViewModel => PinSidebar.Value ? _sidebarViewModel : null;
public SidebarViewModel DockedSidebarViewModel => PinSidebar.Value ? null : _sidebarViewModel;
public SidebarViewModel SidebarViewModel { get; }
public ISnackbarMessageQueue MainMessageQueue
{
get => _mainMessageQueue;
set => SetAndNotify(ref _mainMessageQueue, value);
}
public bool ActiveItemReady
{
get => _activeItemReady;
set => SetAndNotify(ref _activeItemReady, value);
}
public string WindowTitle
{
get => _windowTitle;
@@ -150,52 +132,15 @@ namespace Artemis.UI.Screens
_eventAggregator.Publish(new MainWindowMouseEvent(sender, false, e));
}
private void UpdateSidebarPinState()
{
_sidebarViewModel.IsSidebarOpen = true;
NotifyOfPropertyChange(nameof(PinnedSidebarViewModel));
NotifyOfPropertyChange(nameof(DockedSidebarViewModel));
}
private void UpdateFrameTime()
{
FrameTime = $"Frame time: {_coreService.FrameTime.TotalMilliseconds:F2} ms";
}
private void SidebarViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(_sidebarViewModel.SelectedItem) && ActiveItem != _sidebarViewModel.SelectedItem)
{
// Unless the sidebar is pinned, close it upon selecting an item
if (!PinSidebar.Value)
_sidebarViewModel.IsSidebarOpen = false;
ActiveItemReady = false;
// Allow the menu to close, it's slower but feels more responsive, funny how that works right
Execute.PostToUIThread(async () =>
{
await Task.Delay(400);
ActiveItem = _sidebarViewModel.SelectedItem;
await Task.Delay(200);
ActiveItemReady = true;
});
}
}
private void OnFrameTimeUpdateTimerOnElapsed(object sender, ElapsedEventArgs args)
{
UpdateFrameTime();
}
private void PinSidebarOnSettingChanged(object sender, EventArgs e)
{
UpdateSidebarPinState();
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
@@ -203,7 +148,15 @@ namespace Artemis.UI.Screens
_frameTimeUpdateTimer?.Dispose();
}
#endregion
private void SidebarViewModelOnSelectedScreenChanged(object? sender, EventArgs e)
{
ActiveItem = SidebarViewModel.SelectedScreen;
}
private void ShowSetupWizard()
{
_windowManager.ShowDialog(_kernel.Get<StartupWizardViewModel>());
}
#region Overrides of Screen
@@ -228,14 +181,14 @@ namespace Artemis.UI.Screens
MainMessageQueue = _messageService.MainMessageQueue;
UpdateFrameTime();
SidebarViewModel.SelectedScreenChanged += SidebarViewModelOnSelectedScreenChanged;
ActiveItem = SidebarViewModel.SelectedScreen;
_builtInRegistrationService.RegisterBuiltInDataModelDisplays();
_builtInRegistrationService.RegisterBuiltInDataModelInputs();
_builtInRegistrationService.RegisterBuiltInPropertyEditors();
_frameTimeUpdateTimer.Elapsed += OnFrameTimeUpdateTimerOnElapsed;
_sidebarViewModel.PropertyChanged += SidebarViewModelOnPropertyChanged;
PinSidebar.SettingChanged += PinSidebarOnSettingChanged;
_frameTimeUpdateTimer.Start();
_window = (MaterialWindow) View;
@@ -247,11 +200,6 @@ namespace Artemis.UI.Screens
base.OnInitialActivate();
}
private void ShowSetupWizard()
{
_windowManager.ShowDialog(_kernel.Get<StartupWizardViewModel>());
}
protected override void OnClose()
{
// Ensure no element with focus can leak, if we don't do this the root VM is retained by Window.EffectiveValues
@@ -261,15 +209,13 @@ namespace Artemis.UI.Screens
MainMessageQueue = null;
_frameTimeUpdateTimer.Stop();
SidebarViewModel.SelectedScreenChanged -= SidebarViewModelOnSelectedScreenChanged;
_windowSize.Value ??= new WindowSize();
_windowSize.Value.ApplyFromWindow(_window);
_windowSize.Save();
_frameTimeUpdateTimer.Elapsed -= OnFrameTimeUpdateTimerOnElapsed;
_sidebarViewModel.PropertyChanged -= SidebarViewModelOnPropertyChanged;
PinSidebar.SettingChanged -= PinSidebarOnSettingChanged;
_sidebarViewModel.Dispose();
// Lets force the GC to run after closing the window so it is obvious to users watching task manager
// that closing the UI will decrease the memory footprint of the application.
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Artemis.Core;
@@ -116,7 +117,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private void OnPluginFeatureToggled(object sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature is DataModelPluginFeature)
if (e.PluginFeature is Module)
PopulateModules();
}
@@ -134,7 +135,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private void GetDataModel()
{
MainDataModel = SelectedModule != null
? _dataModelUIService.GetPluginDataModelVisualization(SelectedModule, false)
? _dataModelUIService.GetPluginDataModelVisualization(new List<Module>() {SelectedModule}, false)
: _dataModelUIService.GetMainDataModelVisualization();
}
@@ -184,7 +184,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
if (HasErrors)
return;
_coreService.ModuleRenderingDisabled = true;
_coreService.ProfileRenderingDisabled = true;
await Task.Delay(100);
Device.X = X;
@@ -200,7 +200,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs
_rgbService.SaveDevice(Device);
_coreService.ModuleRenderingDisabled = false;
_coreService.ProfileRenderingDisabled = false;
}
public void Reset()
@@ -1,7 +1,6 @@
using Artemis.UI.Screens.Settings.Tabs.About;
using Artemis.UI.Screens.Settings.Tabs.Devices;
using Artemis.UI.Screens.Settings.Tabs.General;
using Artemis.UI.Screens.Settings.Tabs.Modules;
using Artemis.UI.Screens.Settings.Tabs.Plugins;
using Stylet;
@@ -11,7 +10,6 @@ namespace Artemis.UI.Screens.Settings
{
public SettingsViewModel(
GeneralSettingsTabViewModel generalSettingsTabViewModel,
ModuleOrderTabViewModel moduleOrderTabViewModel,
PluginSettingsTabViewModel pluginSettingsTabViewModel,
DeviceSettingsTabViewModel deviceSettingsTabViewModel,
AboutTabViewModel aboutTabViewModel)
@@ -19,7 +17,6 @@ namespace Artemis.UI.Screens.Settings
DisplayName = "Settings";
Items.Add(generalSettingsTabViewModel);
Items.Add(moduleOrderTabViewModel);
Items.Add(pluginSettingsTabViewModel);
Items.Add(deviceSettingsTabViewModel);
Items.Add(aboutTabViewModel);
@@ -11,406 +11,426 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:AboutTabViewModel}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="15" MaxWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{svgc:SvgImage Source=/Resources/Images/Logo/bow.svg}" Width="60" Height="80" Margin="0 0 20 0" />
<TextBlock Grid.Row="0" Grid.Column="1" Style="{StaticResource MaterialDesignHeadline2TextBlock}">
Artemis 2
</TextBlock>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="15" MaxWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{svgc:SvgImage Source=/Resources/Images/Logo/bow.svg}" Width="60" Height="80" Margin="0 0 20 0" />
<TextBlock Grid.Row="0" Grid.Column="1" Style="{StaticResource MaterialDesignHeadline2TextBlock}">
Artemis 2
</TextBlock>
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal">
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View website"
Command="{s:Action OpenUrl}"
CommandParameter="https://artemis-rgb.com">
<materialDesign:PackIcon Kind="Web" />
</Button>
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub repository"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/Artemis-RGB/Artemis">
<materialDesign:PackIcon Kind="Github" />
</Button>
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View Wiki"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com"
Margin="0 0 -10 0">
<materialDesign:PackIcon Kind="BookOpenOutline" />
</Button>
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal">
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View website"
Command="{s:Action OpenUrl}"
CommandParameter="https://artemis-rgb.com">
<materialDesign:PackIcon Kind="Web" />
</Button>
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub repository"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/Artemis-RGB/Artemis">
<materialDesign:PackIcon Kind="Github" />
</Button>
<Button
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View Wiki"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com"
Margin="0 0 -10 0">
<materialDesign:PackIcon Kind="BookOpenOutline" />
</Button>
</StackPanel>
<TextBlock Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
Text="{Binding Version}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
<Hyperlink Style="{StaticResource ArtemisHyperlink}" RequestNavigate="{s:Action OpenHyperlink}" NavigateUri="https://github.com/Artemis-RGB/Artemis/blob/master/LICENSE">
PolyForm Noncommercial License 1.0.0
</Hyperlink>
</TextBlock>
</Grid>
<materialDesign:Card Margin="0 25 0 0">
<StackPanel Margin="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/8858506" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Robert 'Spoinky' Beekman
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Project owner, main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/SpoinkyNL/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/1094841" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Darth Affe
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/DarthAffe/RGB.NET">
RGB.NET
</Hyperlink>
developer, main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/DarthAffe/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/29486064" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Diogo 'DrMeteor' Trindade
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/diogotr7/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://i.imgur.com/8mPWY1j.png" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Kai Werling
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Graphics design
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View website"
Command="{s:Action OpenUrl}"
CommandParameter="https://kwer.online/">
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="AccountHeart" Width="30" Height="30" />
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
Special thanks
</TextBlock>
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
- The various people creating PRs to Artemis.Plugins and the main repository<LineBreak />
- All the people on Discord providing feedback and testing
</TextBlock>
<StackPanel Orientation="Horizontal" Margin="0 20 0 0">
<materialDesign:PackIcon Kind="LibraryShelves" Width="30" Height="30" />
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
External libraries
</TextBlock>
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
- Ben.Demystifier
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/benaadams/Ben.Demystifier">
https://github.com/benaadams/Ben.Demystifier
</Hyperlink>
<LineBreak />
- EmbedIO
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://unosquare.github.io/embedio/">
https://unosquare.github.io/embedio/
</Hyperlink>
<LineBreak />
- FluentValidation
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://fluentvalidation.net/">
https://fluentvalidation.net/
</Hyperlink>
<LineBreak />
- Furl.Http
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://flurl.dev//">
https://flurl.dev/
</Hyperlink>
<LineBreak />
- gong-wpf-dragdrop
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/punker76/gong-wpf-dragdrop">
https://github.com/punker76/gong-wpf-dragdrop
</Hyperlink>
<LineBreak />
- Hardcodet.NotifyIcon.Wpf.NetCore
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/HavenDV/H.NotifyIcon.WPF">
https://github.com/HavenDV/H.NotifyIcon.WPF
</Hyperlink>
<LineBreak />
- Humanizer
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/Humanizr/Humanizer">
https://github.com/Humanizr/Humanizer
</Hyperlink>
<LineBreak />
- LiteDB
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://www.litedb.org/">
https://www.litedb.org/
</Hyperlink>
<LineBreak />
- MaterialDesignThemes
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit">
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit
</Hyperlink>
<LineBreak />
- MaterialDesignExtensions
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://spiegelp.github.io/MaterialDesignExtensions/">
https://spiegelp.github.io/MaterialDesignExtensions/
</Hyperlink>
<LineBreak />
- McMaster.NETCore.Plugins
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins">
https://github.com/natemcmaster/DotNetCorePlugins
</Hyperlink>
<LineBreak />
- Newtonsoft.Json
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://www.newtonsoft.com/json">
https://www.newtonsoft.com/json
</Hyperlink>
<LineBreak />
- Ninject
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="http://www.ninject.org/">
http://www.ninject.org/
</Hyperlink>
<LineBreak />
- Ookii.Dialogs.Wpf
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/ookii-dialogs/ookii-dialogs-wpf">
https://github.com/ookii-dialogs/ookii-dialogs-wpf
</Hyperlink>
<LineBreak />
- RawInput.Sharp
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/mfakane/rawinput-sharp">
https://github.com/mfakane/rawinput-sharp
</Hyperlink>
<LineBreak />
- RGB.NET
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/DarthAffe/RGB.NET">
https://github.com/DarthAffe/RGB.NET
</Hyperlink>
<LineBreak />
- Serilog
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://serilog.net/">
https://serilog.net/
</Hyperlink>
<LineBreak />
- SkiaSharp
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/mono/SkiaSharp/">
https://github.com/mono/SkiaSharp
</Hyperlink>
<LineBreak />
- Stylet
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/canton7/Stylet">
https://github.com/canton7/Stylet
</Hyperlink>
<LineBreak />
- Unclassified.NetRevisionTask
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://unclassified.software/en/apps/netrevisiontask">
https://unclassified.software/en/apps/netrevisiontask
</Hyperlink>
</TextBlock>
</StackPanel>
</materialDesign:Card>
</StackPanel>
<TextBlock Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
Text="{Binding Version}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
<Hyperlink Style="{StaticResource ArtemisHyperlink}" RequestNavigate="{s:Action OpenHyperlink}" NavigateUri="https://github.com/Artemis-RGB/Artemis/blob/master/LICENSE">
PolyForm Noncommercial License 1.0.0
</Hyperlink>
</TextBlock>
</Grid>
<materialDesign:Card Margin="0 25 0 0">
<StackPanel Margin="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/8858506" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Robert 'Spoinky' Beekman
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Project owner, main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/SpoinkyNL/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/1094841" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Darth Affe
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/DarthAffe/RGB.NET">
RGB.NET
</Hyperlink> developer, main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/DarthAffe/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/29486064" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Diogo 'DrMeteor' Trindade
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Main contributor
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View GitHub profile"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/diogotr7/">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="3"
VerticalAlignment="Top"
Height="75"
Width="75"
Margin="0 0 15 0"
RenderOptions.BitmapScalingMode="HighQuality">
<Ellipse.Fill>
<ImageBrush ImageSource="https://i.imgur.com/8mPWY1j.png" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Row="0"
Grid.Column="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Padding="0">
Kai Werling
</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
Padding="0">
Graphics design
</TextBlock>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
<Button Height="40"
Width="40"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="View website"
Command="{s:Action OpenUrl}"
CommandParameter="https://kwer.online/">
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="AccountHeart" Width="30" Height="30" />
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
Special thanks
</TextBlock>
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
- The various people creating PRs to Artemis.Plugins and the main repository<LineBreak />
- All the people on Discord providing feedback and testing
</TextBlock>
<StackPanel Orientation="Horizontal" Margin="0 20 0 0">
<materialDesign:PackIcon Kind="LibraryShelves" Width="30" Height="30" />
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
External libraries
</TextBlock>
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
- Ben.Demystifier
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/benaadams/Ben.Demystifier">
https://github.com/benaadams/Ben.Demystifier
</Hyperlink><LineBreak />
- EmbedIO
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://unosquare.github.io/embedio/">
https://unosquare.github.io/embedio/
</Hyperlink><LineBreak />
- FluentValidation
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://fluentvalidation.net/">
https://fluentvalidation.net/
</Hyperlink><LineBreak />
- Furl.Http
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://flurl.dev//">
https://flurl.dev/
</Hyperlink><LineBreak />
- gong-wpf-dragdrop
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/punker76/gong-wpf-dragdrop">
https://github.com/punker76/gong-wpf-dragdrop
</Hyperlink><LineBreak />
- Hardcodet.NotifyIcon.Wpf.NetCore
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/HavenDV/H.NotifyIcon.WPF">
https://github.com/HavenDV/H.NotifyIcon.WPF
</Hyperlink><LineBreak />
- Humanizer
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/Humanizr/Humanizer">
https://github.com/Humanizr/Humanizer
</Hyperlink><LineBreak />
- LiteDB
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://www.litedb.org/">
https://www.litedb.org/
</Hyperlink><LineBreak />
- MaterialDesignThemes
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit">
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit
</Hyperlink><LineBreak />
- MaterialDesignExtensions
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://spiegelp.github.io/MaterialDesignExtensions/">
https://spiegelp.github.io/MaterialDesignExtensions/
</Hyperlink><LineBreak />
- McMaster.NETCore.Plugins
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins">
https://github.com/natemcmaster/DotNetCorePlugins
</Hyperlink><LineBreak />
- Newtonsoft.Json
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://www.newtonsoft.com/json">
https://www.newtonsoft.com/json
</Hyperlink><LineBreak />
- Ninject
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="http://www.ninject.org/">
http://www.ninject.org/
</Hyperlink><LineBreak />
- Ookii.Dialogs.Wpf
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/ookii-dialogs/ookii-dialogs-wpf">
https://github.com/ookii-dialogs/ookii-dialogs-wpf
</Hyperlink><LineBreak />
- RawInput.Sharp
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/mfakane/rawinput-sharp">
https://github.com/mfakane/rawinput-sharp
</Hyperlink><LineBreak />
- RGB.NET
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/DarthAffe/RGB.NET">
https://github.com/DarthAffe/RGB.NET
</Hyperlink><LineBreak />
- Serilog
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://serilog.net/">
https://serilog.net/
</Hyperlink><LineBreak />
- SkiaSharp
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/mono/SkiaSharp/">
https://github.com/mono/SkiaSharp
</Hyperlink><LineBreak />
- Stylet
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://github.com/canton7/Stylet">
https://github.com/canton7/Stylet
</Hyperlink><LineBreak />
- Unclassified.NetRevisionTask
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
RequestNavigate="{s:Action OpenHyperlink}"
NavigateUri="https://unclassified.software/en/apps/netrevisiontask">
https://unclassified.software/en/apps/netrevisiontask
</Hyperlink>
</TextBlock>
</StackPanel>
</materialDesign:Card>
</StackPanel>
</ScrollViewer>
</ScrollViewer>
</UserControl>
@@ -1,20 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Modules.ModuleOrderModuleView"
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.Settings.Tabs.Modules"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:ModuleOrderModuleViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Priority}"
Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" />
<TextBlock Grid.Column="1" Text="{Binding Module.DisplayName}" Style="{StaticResource MaterialDesignTextBlock}" />
</Grid>
</UserControl>
@@ -1,43 +0,0 @@
using System;
using Artemis.Core.Modules;
using Stylet;
namespace Artemis.UI.Screens.Settings.Tabs.Modules
{
public class ModuleOrderModuleViewModel : PropertyChangedBase
{
private string _priority;
public ModuleOrderModuleViewModel(Module module)
{
Module = module;
Update();
}
public Module Module { get; }
public string Priority
{
get => _priority;
set => SetAndNotify(ref _priority, value);
}
public void Update()
{
switch (Module.PriorityCategory)
{
case ModulePriorityCategory.Normal:
Priority = "3." + (Module.Priority + 1);
break;
case ModulePriorityCategory.Application:
Priority = "2." + (Module.Priority + 1);
break;
case ModulePriorityCategory.Overlay:
Priority = "1." + (Module.Priority + 1);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
@@ -1,164 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Modules.ModuleOrderTabView"
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.Settings.Tabs.Modules"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:dd="urn:gong-wpf-dragdrop"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:ModuleOrderTabViewModel}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid Margin="15" MaxWidth="800">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15 0 0">Module priority</TextBlock>
<TextBlock TextWrapping="Wrap" Style="{StaticResource MaterialDesignTextBlock}">
Drag and drop the modules below to change their rendering priority. <LineBreak /><LineBreak />
- Like in the profile editor, the modules at the top render over modules at the bottom <LineBreak />
- The categories serve as a starting point for new modules, you may freely move modules between the categories <LineBreak />
</TextBlock>
<Grid Margin="0 0 0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}">1 - Overlays</TextBlock>
<TextBlock Grid.Column="1"
Style="{StaticResource MaterialDesignCaptionTextBlock}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="10 0">
Modules that should always render on top
</TextBlock>
</Grid>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel Margin="15">
<Border BorderThickness="2">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle StrokeDashArray="4 2" Stroke="{DynamicResource MaterialDesignDivider}" StrokeThickness="2"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}" />
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
<ListBox ItemsSource="{Binding OverlayModules}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.DropHandler="{Binding}"
MinHeight="40">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</StackPanel>
</materialDesign:Card>
</StackPanel>
<StackPanel Grid.Row="1">
<Grid Margin="0 15 0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}">2 - Applications/games</TextBlock>
<TextBlock Grid.Column="1"
Style="{StaticResource MaterialDesignCaptionTextBlock}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="10 0">
Modules that are related to specific applications or games
</TextBlock>
</Grid>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel Margin="15">
<Border BorderThickness="2">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle StrokeDashArray="4 2" Stroke="{DynamicResource MaterialDesignDivider}" StrokeThickness="2"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}" />
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
<ListBox ItemsSource="{Binding ApplicationModules}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.DropHandler="{Binding}"
MinHeight="40">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</StackPanel>
</materialDesign:Card>
</StackPanel>
<StackPanel Grid.Row="2">
<Grid Margin="0 15 0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}">3 - Normal</TextBlock>
<TextBlock Grid.Column="1"
Style="{StaticResource MaterialDesignCaptionTextBlock}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="10 0">
Regular modules that are always active in the background
</TextBlock>
</Grid>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel Margin="15">
<Border BorderThickness="2">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle StrokeDashArray="4 2" Stroke="{DynamicResource MaterialDesignDivider}" StrokeThickness="2"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}" />
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
<ListBox ItemsSource="{Binding NormalModules}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.DropHandler="{Binding}"
MinHeight="40">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</StackPanel>
</materialDesign:Card>
</StackPanel>
</Grid>
</ScrollViewer>
</UserControl>
@@ -1,97 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using GongSolutions.Wpf.DragDrop;
using Stylet;
namespace Artemis.UI.Screens.Settings.Tabs.Modules
{
public class ModuleOrderTabViewModel : Screen, IDropTarget
{
private readonly IModuleService _moduleService;
private readonly IPluginManagementService _pluginManagementService;
private readonly DefaultDropHandler _defaultDropHandler;
private List<ModuleOrderModuleViewModel> _modules;
public ModuleOrderTabViewModel(IPluginManagementService pluginManagementService, IModuleService moduleService)
{
DisplayName = "MODULE PRIORITY";
_pluginManagementService = pluginManagementService;
_moduleService = moduleService;
_defaultDropHandler = new DefaultDropHandler();
NormalModules = new BindableCollection<ModuleOrderModuleViewModel>();
ApplicationModules = new BindableCollection<ModuleOrderModuleViewModel>();
OverlayModules = new BindableCollection<ModuleOrderModuleViewModel>();
}
protected override void OnActivate()
{
base.OnActivate();
Update();
}
protected override void OnDeactivate()
{
base.OnDeactivate();
_modules = null;
}
public BindableCollection<ModuleOrderModuleViewModel> NormalModules { get; set; }
public BindableCollection<ModuleOrderModuleViewModel> ApplicationModules { get; set; }
public BindableCollection<ModuleOrderModuleViewModel> OverlayModules { get; set; }
public void DragOver(IDropInfo dropInfo)
{
_defaultDropHandler.DragOver(dropInfo);
}
public void Drop(IDropInfo dropInfo)
{
if (dropInfo.TargetItem == dropInfo.Data)
return;
if (!(dropInfo.Data is ModuleOrderModuleViewModel viewModel))
return;
if (!(dropInfo.TargetCollection is BindableCollection<ModuleOrderModuleViewModel> targetCollection))
return;
int insertIndex = dropInfo.InsertIndex;
ModulePriorityCategory category;
if (targetCollection == NormalModules)
category = ModulePriorityCategory.Normal;
else if (targetCollection == ApplicationModules)
category = ModulePriorityCategory.Application;
else
category = ModulePriorityCategory.Overlay;
// If moving down, take the removal of ourselves into consideration with the insert index
if (targetCollection.Contains(viewModel) && targetCollection.IndexOf(viewModel) < insertIndex)
insertIndex--;
_moduleService.UpdateModulePriority(viewModel.Module, category, insertIndex);
Update();
}
public void Update()
{
if (_modules == null)
_modules = _pluginManagementService.GetFeaturesOfType<Module>().Select(m => new ModuleOrderModuleViewModel(m)).ToList();
NormalModules.Clear();
NormalModules.AddRange(_modules.Where(m => m.Module.PriorityCategory == ModulePriorityCategory.Normal).OrderBy(m => m.Module.Priority));
ApplicationModules.Clear();
ApplicationModules.AddRange(_modules.Where(m => m.Module.PriorityCategory == ModulePriorityCategory.Application).OrderBy(m => m.Module.Priority));
OverlayModules.Clear();
OverlayModules.AddRange(_modules.Where(m => m.Module.PriorityCategory == ModulePriorityCategory.Overlay).OrderBy(m => m.Module.Priority));
foreach (ModuleOrderModuleViewModel moduleOrderModuleViewModel in _modules)
moduleOrderModuleViewModel.Update();
}
}
}
@@ -17,7 +17,7 @@
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
</ResourceDictionary.MergedDictionaries>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="-3 -8">
@@ -0,0 +1,127 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<Style x:Key="SidebarListBoxItem" TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource MaterialDesignToolToggleListBoxItem}">
<Setter Property="Padding" Value="8" />
<Setter Property="Margin" Value="6 1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="{TemplateBinding Margin}"
ClipToBounds="{TemplateBinding ClipToBounds}"
Cursor="Hand">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3" To="Normal">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseOut" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState Name="Normal" />
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="Opacity"
To="0.03" Duration="0" />
</Storyboard>
</VisualState>
<VisualState Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition From="*" To="Selected">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBackgroundBorder">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
<VisualTransition From="*" To="SelectedUnfocused">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SelectedBackgroundBorder">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
<VisualTransition From="Selected" To="Unselected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SelectedBackgroundBorder"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0" />
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SelectedBackgroundBorder"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
<DoubleAnimation Storyboard.TargetName="SelectedBorder"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
</Storyboard>
</VisualState>
<VisualState Name="Unselected" />
<VisualState Name="SelectedUnfocused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="SelectedBackgroundBorder"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
<DoubleAnimation Storyboard.TargetName="SelectedBorder"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Border Background="Black"
CornerRadius="4"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Grid}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}" />
</VisualBrush.Visual>
</VisualBrush>
</Grid.OpacityMask>
<Border x:Name="MouseOverBorder"
Opacity="0"
Background="{TemplateBinding Foreground, Converter={StaticResource BrushRoundConverter}}" />
<Border x:Name="SelectedBackgroundBorder"
Opacity="0"
Background="{DynamicResource MaterialDesignDivider}" />
<materialDesign:Ripple x:Name="Ripple" Feedback="{TemplateBinding Foreground, Converter={StaticResource BrushRoundConverter}}"
Opacity=".82"
Focusable="False"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}" />
<Border x:Name="SelectedBorder"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}, Path=SelectionMode, Converter={StaticResource EqualityToVisibilityConverter}, ConverterParameter={x:Static SelectionMode.Extended}, Mode=OneWay}"
Opacity="0"
BorderThickness="0"
BorderBrush="{DynamicResource MaterialDesignDivider}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Ripple" Property="Opacity" Value=".92" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".56" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -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>
@@ -4,16 +4,16 @@ using Artemis.Core.Modules;
using Humanizer;
using Stylet;
namespace Artemis.UI.Screens.Modules.Tabs
namespace Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit
{
public sealed class ActivationRequirementViewModel : Screen, IDisposable
public sealed class ModuleActivationRequirementViewModel : Screen, IDisposable
{
private readonly IModuleActivationRequirement _activationRequirement;
private readonly Timer _updateTimer;
private string _requirementDescription;
private bool _requirementMet;
public ActivationRequirementViewModel(IModuleActivationRequirement activationRequirement)
public ModuleActivationRequirementViewModel(IModuleActivationRequirement activationRequirement)
{
_activationRequirement = activationRequirement;
_updateTimer = new Timer(500);
@@ -63,7 +63,7 @@ namespace Artemis.UI.Screens.Modules.Tabs
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
@@ -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; }
}
}
@@ -1,30 +1,30 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Dialogs.ProfileEditView"
<UserControl x:Class="Artemis.UI.Screens.Sidebar.Dialogs.SidebarCategoryCreateView"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="213.053" d:DesignWidth="254.425">
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="280"
Width="300">
<StackPanel Margin="16">
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
Edit profile
Add a new category
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="Profile name"
Margin="0 8 0 16"
Width="300"
<TextBox materialDesign:HintAssist.Hint="Category name"
Margin="0 16 0 8"
Style="{StaticResource MaterialDesignFilledTextBox}"
Text="{Binding ProfileName, UpdateSourceTrigger=PropertyChanged}" />
Text="{Binding CategoryName, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 0 8 0" Command="{s:Action Cancel}">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 0 0 0" Command="{s:Action Accept}">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Command="{s:Action Accept}">
ACCEPT
</Button>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>
@@ -0,0 +1,56 @@
using System.Threading.Tasks;
using Artemis.Core.Services;
using Artemis.Storage.Repositories.Interfaces;
using Artemis.UI.Screens.ProfileEditor.Dialogs;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
namespace Artemis.UI.Screens.Sidebar.Dialogs
{
public class SidebarCategoryCreateViewModel : DialogViewModelBase
{
private readonly IProfileService _profileService;
private string _categoryName;
public SidebarCategoryCreateViewModel(IProfileService profileService, IModelValidator<SidebarCategoryCreateViewModel> validator) : base(validator)
{
_profileService = profileService;
}
public string CategoryName
{
get => _categoryName;
set => SetAndNotify(ref _categoryName, value);
}
public async Task Accept()
{
await ValidateAsync();
if (HasErrors)
return;
Session.Close(_profileService.CreateProfileCategory(CategoryName));
}
}
public class SidebarCategoryCreateViewModelValidator : AbstractValidator<SidebarCategoryCreateViewModel>
{
private readonly IProfileCategoryRepository _profileCategoryRepository;
public SidebarCategoryCreateViewModelValidator(IProfileCategoryRepository profileCategoryRepository)
{
_profileCategoryRepository = profileCategoryRepository;
RuleFor(m => m.CategoryName)
.NotEmpty().WithMessage("Category name may not be empty")
.Must(BeUniqueCategory).WithMessage("Category name already taken");
}
private bool BeUniqueCategory(SidebarCategoryCreateViewModel viewModel, string categoryName)
{
return _profileCategoryRepository.IsUnique(categoryName, null) == null;
}
}
}
@@ -0,0 +1,44 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.Dialogs.SidebarCategoryUpdateView"
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="280"
Width="300">
<StackPanel Margin="16" >
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
Update category
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="Category name"
Margin="0 16 0 8"
Style="{StaticResource MaterialDesignFilledTextBox}"
Text="{Binding CategoryName, UpdateSourceTrigger=PropertyChanged}" />
<Grid Margin="0 8 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
HorizontalAlignment="Left"
Style="{StaticResource MaterialDesignFlatAccentButton}"
Command="{s:Action Delete}">
DELETE
</Button>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Command="{s:Action Accept}">
ACCEPT
</Button>
</StackPanel>
</Grid>
</StackPanel>
</UserControl>
@@ -0,0 +1,73 @@
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.Storage.Repositories.Interfaces;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
using WinRT.Interop;
namespace Artemis.UI.Screens.Sidebar.Dialogs
{
public class SidebarCategoryUpdateViewModel : DialogViewModelBase
{
private readonly IDialogService _dialogService;
private readonly IProfileService _profileService;
private string _categoryName;
public SidebarCategoryUpdateViewModel(ProfileCategory profileCategory, IProfileService profileService, IDialogService dialogService,
IModelValidator<SidebarCategoryUpdateViewModel> validator) : base(validator)
{
_categoryName = profileCategory.Name;
_profileService = profileService;
_dialogService = dialogService;
ProfileCategory = profileCategory;
}
public ProfileCategory ProfileCategory { get; }
public string CategoryName
{
get => _categoryName;
set => SetAndNotify(ref _categoryName, value);
}
public async Task Accept()
{
await ValidateAsync();
if (HasErrors)
return;
ProfileCategory.Name = CategoryName;
_profileService.SaveProfileCategory(ProfileCategory);
Session.Close(nameof(Accept));
}
public void Delete()
{
Session.Close(nameof(Delete));
}
}
public class SidebarCategoryUpdateViewModelValidator : AbstractValidator<SidebarCategoryUpdateViewModel>
{
private readonly IProfileCategoryRepository _profileCategoryRepository;
public SidebarCategoryUpdateViewModelValidator(IProfileCategoryRepository profileCategoryRepository)
{
_profileCategoryRepository = profileCategoryRepository;
RuleFor(m => m.CategoryName)
.NotEmpty().WithMessage("Category name may not be empty")
.Must(BeUniqueCategory).WithMessage("Category name already taken");
}
private bool BeUniqueCategory(SidebarCategoryUpdateViewModel viewModel, string categoryName)
{
return _profileCategoryRepository.IsUnique(categoryName, viewModel.ProfileCategory.EntityId) == null;
}
}
}
@@ -0,0 +1,213 @@
<UserControl
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
x:Class="Artemis.UI.Screens.Sidebar.SidebarCategoryView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:SidebarCategoryViewModel}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ArtemisSidebar.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="0 8 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0"
x:Name="Expander"
Foreground="{DynamicResource MaterialDesignBody}"
Style="{StaticResource MaterialDesignExpandCollapseToggleStyle}"
IsChecked="{Binding ShowItems}"
ClickMode="Press"
Margin="5 0" />
<TextBlock Grid.Column="1"
Padding="0 5"
FontSize="14"
FontWeight="SemiBold"
VerticalAlignment="Center"
Text="{Binding ProfileCategory.Name}"
MouseLeftButtonUp="{s:Action OnMouseLeftButtonUp}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuspended}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.33" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Border Grid.Column="1" BorderThickness="1" BorderBrush="{DynamicResource MaterialDesignBody}" Height="1" Opacity="0">
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuspended}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.75" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Button Grid.Column="2"
ToolTip="Edit category"
Width="26"
Height="26"
Command="{s:Action UpdateCategory}"
HorizontalAlignment="Right">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<materialDesign:PackIcon Kind="Cog" Width="16" Height="16" />
</Button>
<ToggleButton Grid.Column="3" ToolTip="Suspend profile" Width="18" Height="18" Margin="2 0 0 0" IsChecked="{Binding IsSuspended}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MaterialDesignFlatToggleButton}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<materialDesign:PackIcon Kind="Pause" Height="14" Width="14" />
</ToggleButton>
<materialDesign:PopupBox Grid.Column="4"
PlacementMode="BottomAndAlignCentres"
ToolTip="Add profile"
Margin="0 0 10 0">
<materialDesign:PopupBox.Style>
<Style TargetType="{x:Type materialDesign:PopupBox}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</materialDesign:PopupBox.Style>
<materialDesign:PopupBox.ToggleContent>
<materialDesign:PackIcon Kind="Plus" Width="20" Height="20" Foreground="{DynamicResource MaterialDesignBody}" />
</materialDesign:PopupBox.ToggleContent>
<StackPanel>
<Button Command="{s:Action AddProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Plus" VerticalAlignment="Center" Margin="0 0 5 0" />
<TextBlock VerticalAlignment="Center"> Add new profile</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action ImportProfile}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Import" VerticalAlignment="Center" Margin="0 0 5 0" />
<TextBlock VerticalAlignment="Center">Import existing profile</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</materialDesign:PopupBox>
</Grid>
<Border Grid.Row="1">
<Border.Resources>
<DataTemplate x:Key="ProfileDragTemplate" DataType="{x:Type local:SidebarProfileConfigurationViewModel}">
<Border Background="{DynamicResource MaterialDesignDivider}" Padding="10" CornerRadius="4">
<StackPanel Orientation="Horizontal">
<shared:ProfileConfigurationIcon ConfigurationIcon="{Binding ProfileConfiguration.Icon}"
Width="20"
Height="20"
Margin="0 0 10 0"
Foreground="{DynamicResource MaterialDesignBody}" />
<TextBlock Text="{Binding ProfileConfiguration.Name}" VerticalAlignment="Center" Foreground="{DynamicResource MaterialDesignBody}" />
</StackPanel>
</Border>
</DataTemplate>
</Border.Resources>
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedProfileConfiguration}"
MinHeight="10"
PreviewMouseRightButtonDown="UIElement_OnPreviewMouseRightButtonDown"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}"
dd:DragDrop.DragAdornerTemplate="{StaticResource ProfileDragTemplate}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Paste" Command="{s:Action Paste}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentPaste" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource SidebarListBoxItem}" />
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
</UserControl>
@@ -0,0 +1,21 @@
using System.Windows.Controls;
using System.Windows.Input;
namespace Artemis.UI.Screens.Sidebar
{
/// <summary>
/// Interaction logic for SidebarCategoryView.xaml
/// </summary>
public partial class SidebarCategoryView : UserControl
{
public SidebarCategoryView()
{
InitializeComponent();
}
private void UIElement_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
@@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Sidebar.Dialogs;
using Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit;
using Artemis.UI.Shared.Services;
using GongSolutions.Wpf.DragDrop;
using MaterialDesignThemes.Wpf;
using MaterialDesignThemes.Wpf.Transitions;
using Newtonsoft.Json;
using Ookii.Dialogs.Wpf;
using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarCategoryViewModel : Conductor<SidebarProfileConfigurationViewModel>.Collection.AllActive, IDropTarget
{
private readonly IDialogService _dialogService;
private readonly ISidebarVmFactory _vmFactory;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileService _profileService;
private SidebarProfileConfigurationViewModel _selectedProfileConfiguration;
private bool _showItems;
private readonly DefaultDropHandler _defaultDropHandler;
private bool _addingProfile;
public SidebarCategoryViewModel(ProfileCategory profileCategory,
IProfileService profileService,
IDialogService dialogService,
ISidebarVmFactory vmFactory,
IEventAggregator eventAggregator)
{
_profileService = profileService;
_dialogService = dialogService;
_vmFactory = vmFactory;
_eventAggregator = eventAggregator;
_showItems = !profileCategory.IsCollapsed;
_defaultDropHandler = new DefaultDropHandler();
ProfileCategory = profileCategory;
}
public ProfileCategory ProfileCategory { get; }
public bool ShowItems
{
get => _showItems;
set
{
if (!SetAndNotify(ref _showItems, value)) return;
if (value)
CreateProfileViewModels();
else
Items.Clear();
}
}
public bool IsSuspended
{
get => ProfileCategory.IsSuspended;
set
{
ProfileCategory.IsSuspended = value;
NotifyOfPropertyChange(nameof(IsSuspended));
_profileService.SaveProfileCategory(ProfileCategory);
}
}
public SidebarProfileConfigurationViewModel SelectedProfileConfiguration
{
get => _selectedProfileConfiguration;
set
{
if (!SetAndNotify(ref _selectedProfileConfiguration, value)) return;
if (value == null) return;
try
{
((SidebarViewModel) Parent).SelectProfileConfiguration(value.ProfileConfiguration);
}
catch (Exception e)
{
_dialogService.ShowExceptionDialog("Failed select profile", e);
throw;
}
}
}
public async Task UpdateCategory()
{
object result = await _dialogService.ShowDialog<SidebarCategoryUpdateViewModel>(new Dictionary<string, object> {{"profileCategory", ProfileCategory}});
if (result is nameof(SidebarCategoryUpdateViewModel.Delete))
await DeleteCategory();
}
public async Task DeleteCategory()
{
bool confirmed = await _dialogService.ShowConfirmDialog(
"Delete category",
"Are you sure you want to delete this category?\r\nAll profiles will be deleted too."
);
if (!confirmed)
return;
// Close the editor first by heading to Home if any of the profiles are being edited
if (ProfileCategory.ProfileConfigurations.Any(p => p.IsBeingEdited))
_eventAggregator.Publish(new RequestSelectSidebarItemEvent("Home"));
_profileService.DeleteProfileCategory(ProfileCategory);
((SidebarViewModel) Parent).RemoveProfileCategoryViewModel(this);
}
public void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ShowItems = !ShowItems;
}
public async Task AddProfile()
{
try
{
_addingProfile = true;
ProfileConfiguration profileConfiguration = _profileService.CreateProfileConfiguration(ProfileCategory, "New profile", Enum.GetValues<PackIconKind>().First().ToString());
object result = await _dialogService.ShowDialog<ProfileEditViewModel>(new Dictionary<string, object>
{
{"profileConfiguration", profileConfiguration},
{"isNew", true}
});
if (result is nameof(ProfileEditViewModel.Accept))
{
if (!ShowItems)
ShowItems = true;
else
CreateProfileViewModels();
}
else
_profileService.RemoveProfileConfiguration(profileConfiguration);
}
finally
{
_addingProfile = false;
}
}
public async Task ImportProfile()
{
VistaOpenFileDialog dialog = new()
{
Filter = "Artemis Profile|*.json",
Title = "Export Artemis profile"
};
bool? result = dialog.ShowDialog();
if (result != true)
return;
string json = await File.ReadAllTextAsync(dialog.FileName);
try
{
ProfileConfigurationExportModel profileConfigurationExportModel = JsonConvert.DeserializeObject<ProfileConfigurationExportModel>(json, IProfileService.ExportSettings);
if (profileConfigurationExportModel == null)
{
await _dialogService.ShowConfirmDialog("Import profile", "Failed to import this profile, make sure it is a valid Artemis profile.", "Confirm", null);
return;
}
_profileService.ImportProfile(ProfileCategory, profileConfigurationExportModel);
}
catch (Exception e)
{
await _dialogService.ShowConfirmDialog("Import profile", $"Failed to import this profile, make sure it is a valid Artemis profile.\r\n{e.Message}", "Confirm", null);
}
}
private void CreateProfileViewModels()
{
Items.Clear();
foreach (ProfileConfiguration profileConfiguration in ProfileCategory.ProfileConfigurations.OrderBy(p => p.Order))
Items.Add(_vmFactory.SidebarProfileConfigurationViewModel(profileConfiguration));
SelectedProfileConfiguration = Items.FirstOrDefault(i => i.ProfileConfiguration.IsBeingEdited);
}
private void ProfileCategoryOnProfileConfigurationRemoved(object sender, ProfileConfigurationEventArgs e)
{
if (!_addingProfile && ShowItems)
{
Items.Remove(Items.FirstOrDefault(i => i.ProfileConfiguration == e.ProfileConfiguration));
((BindableCollection<SidebarProfileConfigurationViewModel>) Items).Sort(p => p.ProfileConfiguration.Order);
}
SelectedProfileConfiguration = Items.FirstOrDefault(i => i.ProfileConfiguration.IsBeingEdited);
}
private void ProfileCategoryOnProfileConfigurationAdded(object sender, ProfileConfigurationEventArgs e)
{
if (!_addingProfile && ShowItems)
{
Items.Add(_vmFactory.SidebarProfileConfigurationViewModel(e.ProfileConfiguration));
((BindableCollection<SidebarProfileConfigurationViewModel>) Items).Sort(p => p.ProfileConfiguration.Order);
}
SelectedProfileConfiguration = Items.FirstOrDefault(i => i.ProfileConfiguration.IsBeingEdited);
}
#region Overrides of Screen
#region Overrides of AllActive
/// <inheritdoc />
protected override void OnActivate()
{
CreateProfileViewModels();
base.OnActivate();
}
#endregion
/// <inheritdoc />
protected override void OnInitialActivate()
{
ProfileCategory.ProfileConfigurationAdded += ProfileCategoryOnProfileConfigurationAdded;
ProfileCategory.ProfileConfigurationRemoved += ProfileCategoryOnProfileConfigurationRemoved;
base.OnInitialActivate();
}
/// <inheritdoc />
protected override void OnClose()
{
ProfileCategory.ProfileConfigurationAdded -= ProfileCategoryOnProfileConfigurationAdded;
ProfileCategory.ProfileConfigurationRemoved -= ProfileCategoryOnProfileConfigurationRemoved;
base.OnClose();
}
#endregion
#region Implementation of IDropTarget
/// <inheritdoc />
public void DragOver(IDropInfo dropInfo)
{
_defaultDropHandler.DragOver(dropInfo);
}
/// <inheritdoc />
public void Drop(IDropInfo dropInfo)
{
if (dropInfo.Data is not SidebarProfileConfigurationViewModel sourceItem)
return;
if (dropInfo.Data == dropInfo.TargetItem)
return;
SidebarCategoryViewModel sourceCategory = (SidebarCategoryViewModel) sourceItem.Parent;
SidebarCategoryViewModel targetCategory = (SidebarCategoryViewModel) ((FrameworkElement) dropInfo.VisualTarget).DataContext;
int targetIndex = 0;
if (dropInfo.TargetItem is SidebarProfileConfigurationViewModel targetItem)
{
if (dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.BeforeTargetItem))
targetIndex = targetCategory.Items.IndexOf(targetItem);
else
targetIndex = targetCategory.Items.IndexOf(targetItem) + 1;
}
targetCategory.ProfileCategory.AddProfileConfiguration(sourceItem.ProfileConfiguration, targetIndex);
if (sourceCategory != targetCategory)
_profileService.SaveProfileCategory(sourceCategory.ProfileCategory);
_profileService.SaveProfileCategory(targetCategory.ProfileCategory);
}
#endregion
}
}
@@ -0,0 +1,188 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.SidebarProfileConfigurationView"
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:SidebarProfileConfigurationViewModel}"
Background="Transparent"
Margin="-8"
Padding="8">
<!-- Above is a dumb hack to get the context menu to cover the entire item -->
<UserControl.Resources>
<shared:StreamToBitmapImageConverter x:Key="StreamToBitmapImageConverter" />
</UserControl.Resources>
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="View properties" Command="{s:Action ViewProperties}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Cog" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Suspend" IsCheckable="True" IsChecked="{Binding IsSuspended}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Pause" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Suspend all">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Pause" />
</MenuItem.Icon>
<MenuItem Header="Above" Command="{s:Action SuspendAbove}" CommandParameter="suspend" />
<MenuItem Header="Below" Command="{s:Action SuspendBelow}" CommandParameter="suspend" />
</MenuItem>
<MenuItem Header="Resume all">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Play" />
</MenuItem.Icon>
<MenuItem Header="Above" Command="{s:Action SuspendAbove}" CommandParameter="resume" />
<MenuItem Header="Below" Command="{s:Action SuspendBelow}" CommandParameter="resume" />
</MenuItem>
<Separator />
<MenuItem Header="Export" Command="{s:Action Export}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Export" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Duplicate" Command="{s:Action Duplicate}" >
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentDuplicate" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Copy" Command="{s:Action Copy}" >
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentCopy" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Paste" Command="{s:Action Paste}" >
<MenuItem.Icon>
<materialDesign:PackIcon Kind="ContentPaste" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Delete" Command="{s:Action Delete}" InputGestureText="Del">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="TrashCan" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<shared:ProfileConfigurationIcon Grid.Column="0" VerticalAlignment="Center" ConfigurationIcon="{Binding ProfileConfiguration.Icon}" Width="20" Height="20">
<shared:ProfileConfigurationIcon.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsProfileActive}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.33" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</shared:ProfileConfigurationIcon.Style>
</shared:ProfileConfigurationIcon>
<TextBlock Grid.Column="1" FontSize="12" Margin="10 0 0 0" VerticalAlignment="Center" Text="{Binding ProfileConfiguration.Name}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsProfileActive}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.33" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Border Grid.Column="0" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="{DynamicResource MaterialDesignBody}" Height="1" Opacity="0">
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuspended}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.75" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Button Grid.Column="2" ToolTip="View properties" Width="20" Height="20" Command="{s:Action ViewProperties}" HorizontalAlignment="Right">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignIconForegroundButton}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<materialDesign:PackIcon Kind="Cog" Width="16" Height="16" />
</Button>
<ToggleButton Grid.Column="3" ToolTip="Suspend profile" Width="18" Height="18" Margin="2 0 0 0" IsChecked="{Binding IsSuspended}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource MaterialDesignFlatToggleButton}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<materialDesign:PackIcon Kind="Pause" Height="14" Width="14" />
</ToggleButton>
</Grid>
</UserControl>
@@ -0,0 +1,165 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Sidebar.Dialogs.ProfileEdit;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Newtonsoft.Json;
using Ookii.Dialogs.Wpf;
using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarProfileConfigurationViewModel : Screen
{
private readonly IDialogService _dialogService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileService _profileService;
public SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration,
IProfileService profileService,
IDialogService dialogService,
IEventAggregator eventAggregator)
{
_profileService = profileService;
_dialogService = dialogService;
_eventAggregator = eventAggregator;
ProfileConfiguration = profileConfiguration;
}
public ProfileConfiguration ProfileConfiguration { get; }
public bool IsProfileActive => ProfileConfiguration.Profile != null;
public bool IsSuspended
{
get => ProfileConfiguration.IsSuspended;
set
{
ProfileConfiguration.IsSuspended = value;
_profileService.SaveProfileCategory(ProfileConfiguration.Category);
}
}
public async Task ViewProperties()
{
object result = await _dialogService.ShowDialog<ProfileEditViewModel>(new Dictionary<string, object>
{
{"profileConfiguration", ProfileConfiguration},
{"isNew", false}
});
if (result is nameof(ProfileEditViewModel.Delete))
await Delete();
}
public void SuspendAbove(string action)
{
foreach (ProfileConfiguration profileConfiguration in ProfileConfiguration.Category.ProfileConfigurations.OrderBy(p => p.Order).TakeWhile(c => c != ProfileConfiguration))
{
if (profileConfiguration != ProfileConfiguration)
profileConfiguration.IsSuspended = action == "suspend";
}
}
public void SuspendBelow(string action)
{
foreach (ProfileConfiguration profileConfiguration in ProfileConfiguration.Category.ProfileConfigurations.OrderBy(p => p.Order).SkipWhile(c => c != ProfileConfiguration))
{
if (profileConfiguration != ProfileConfiguration)
profileConfiguration.IsSuspended = action == "suspend";
}
}
public async Task Export()
{
string filename = new string(ProfileConfiguration.Name.Select(ch => Path.GetInvalidFileNameChars().Contains(ch) ? '_' : ch).ToArray()) + ".json";
VistaSaveFileDialog dialog = new()
{
Filter = "Artemis Profile|*.json",
Title = "Export Artemis profile",
FileName = filename
};
bool? result = dialog.ShowDialog();
if (result != true)
return;
ProfileConfigurationExportModel profileConfigurationExportModel = _profileService.ExportProfile(ProfileConfiguration);
string json = JsonConvert.SerializeObject(profileConfigurationExportModel, IProfileService.ExportSettings);
if (!dialog.FileName.EndsWith(".json"))
dialog.FileName += ".json";
await File.WriteAllTextAsync(dialog.FileName, json);
}
public void Duplicate()
{
ProfileConfigurationExportModel export = _profileService.ExportProfile(ProfileConfiguration);
_profileService.ImportProfile(ProfileConfiguration.Category, export, true, false, "copy");
}
public void Copy()
{
JsonClipboard.SetObject(_profileService.ExportProfile(ProfileConfiguration));
}
public void Paste()
{
ProfileConfigurationExportModel profileConfiguration = JsonClipboard.GetData<ProfileConfigurationExportModel>();
if (profileConfiguration == null)
return;
_profileService.ImportProfile(ProfileConfiguration.Category, profileConfiguration, true, false, "copy");
}
public async Task Delete()
{
if (await _dialogService.ShowConfirmDialog("Delete profile", "Are you sure you want to delete this profile?\r\nThis cannot be undone."))
{
// Close the editor first by heading to Home if the profile is being edited
if (ProfileConfiguration.IsBeingEdited)
_eventAggregator.Publish(new RequestSelectSidebarItemEvent("Home"));
_profileService.RemoveProfileConfiguration(ProfileConfiguration);
}
}
#region Overrides of Screen
/// <inheritdoc />
protected override void OnActivate()
{
_profileService.LoadProfileConfigurationIcon(ProfileConfiguration);
ProfileConfiguration.PropertyChanged += ProfileConfigurationOnPropertyChanged;
NotifyOfPropertyChange(nameof(IsProfileActive));
base.OnActivate();
}
/// <inheritdoc />
protected override void OnDeactivate()
{
ProfileConfiguration.PropertyChanged -= ProfileConfigurationOnPropertyChanged;
base.OnDeactivate();
}
#endregion
#region Event handlers
private void ProfileConfigurationOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ProfileConfiguration.Profile))
NotifyOfPropertyChange(nameof(IsProfileActive));
if (e.PropertyName == nameof(ProfileConfiguration.IsSuspended))
NotifyOfPropertyChange(nameof(IsSuspended));
}
#endregion
}
}
@@ -0,0 +1,15 @@
<UserControl x:Class="Artemis.UI.Screens.Sidebar.SidebarScreenView"
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:SidebarScreenViewModel}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="{Binding DisplayName}" />
</StackPanel>
</UserControl>
@@ -0,0 +1,31 @@
using MaterialDesignThemes.Wpf;
using Ninject;
using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public class SidebarScreenViewModel<T> : SidebarScreenViewModel where T : Screen
{
public SidebarScreenViewModel(PackIconKind icon, string displayName) : base(icon, displayName)
{
}
public override Screen CreateInstance(IKernel kernel)
{
return kernel.Get<T>();
}
}
public abstract class SidebarScreenViewModel
{
protected SidebarScreenViewModel(PackIconKind icon, string displayName)
{
Icon = icon;
DisplayName = displayName;
}
public abstract Screen CreateInstance(IKernel kernel);
public PackIconKind Icon { get; }
public string DisplayName { get; }
}
}
+149 -42
View File
@@ -4,56 +4,163 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sidebar="clr-namespace:Artemis.UI.Screens.Sidebar"
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance sidebar:SidebarViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ArtemisSidebar.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="SidebarDialog" DialogTheme="Inherit" DialogMargin="14">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
Grid.RowSpan="2"
Source="/Resources/Images/Sidebar/sidebar-header.png"
Stretch="UniformToFill"
VerticalAlignment="Top" Height="120" />
<Grid Grid.Row="0" ClipToBounds="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Row="0"
Style="{StaticResource MaterialDesignSwitchLightToggleButton}"
ToolTip="Pin sidebar"
HorizontalAlignment="Right"
IsChecked="{Binding PinSidebar.Value, Delay=300}"
Margin="10 0">
<materialDesign:PackIcon Kind="Pin" RenderTransformOrigin=".5,.5">
<materialDesign:PackIcon.RenderTransform>
<RotateTransform Angle="45" />
</materialDesign:PackIcon.RenderTransform>
</materialDesign:PackIcon>
<materialDesign:ToggleButtonAssist.OnContent>
<materialDesign:PackIcon Kind="Pin" />
</materialDesign:ToggleButtonAssist.OnContent>
</ToggleButton>
<shared:DeviceVisualizer Grid.Column="0"
Grid.ColumnSpan="2"
Device="{Binding HeaderDevice}"
ShowColors="True"
RenderTransformOrigin="0.5 0.5"
VerticalAlignment="Center"
HorizontalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality">
<shared:DeviceVisualizer.RenderTransform>
<TransformGroup>
<RotateTransform Angle="20" />
<ScaleTransform ScaleX="2" ScaleY="2" />
</TransformGroup>
</shared:DeviceVisualizer.RenderTransform>
</shared:DeviceVisualizer>
<Rectangle Grid.Column="0" Grid.ColumnSpan="2" Height="60" VerticalAlignment="Bottom">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
<GradientStop Color="{Binding Path=Color, Source={StaticResource MaterialDesignToolBarBackground}}" Offset="0" /> <!-- TODO: Get the color from MaterialDesign -->
<GradientStop Color="#00000000" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Image Grid.Column="0" Source="{svgc:SvgImage Source=/Resources/Images/Logo/bow.svg}" Height="35" Width="35" Margin="10" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource MaterialDesignHeadline5TextBlock}"
Foreground="{DynamicResource MaterialDesignDarkForeground}">
Artemis 2
</TextBlock>
</Grid>
<TextBlock Grid.Row="1"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Foreground="{StaticResource PrimaryHueDarkForegroundBrush}"
Margin="15"
materialDesign:ShadowAssist.ShadowDepth="Depth1"
Text="{Binding ActiveModules}"
VerticalAlignment="Bottom" />
<!-- Built-in screens -->
<ListBox Grid.Row="1"
Margin="0 2"
ItemContainerStyle="{StaticResource SidebarListBoxItem}"
ItemsSource="{Binding SidebarScreens}"
SelectedItem="{Binding SelectedSidebarScreen}">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Separator Grid.Row="2" Margin="8" />
<controls:SideNavigation Grid.Row="3"
Items="{Binding SidebarItems}"
SelectedItem="{Binding SelectedItem}"
WillSelectNavigationItemCommand="{s:Action SelectItem}"
materialDesign:RippleAssist.Feedback="{StaticResource SecondaryHueMidBrush}"
Margin="0 5 0 0" />
</Grid>
<!-- Categories -->
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}" />
</ScrollViewer.Resources>
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="CategoryDragTemplate" DataType="{x:Type sidebar:SidebarCategoryViewModel}">
<DataTemplate.Resources>
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</DataTemplate.Resources>
<Border Background="{DynamicResource MaterialDesignTextFieldBoxHoverBackground}"
Padding="10"
CornerRadius="4"
Visibility="{Binding Path=ProfileCategory, FallbackValue=null, Converter={StaticResource NullToVisibilityConverter}}">
<TextBlock Text="{Binding ProfileCategory.Name,TargetNullValue='Oops, I am missing'}" VerticalAlignment="Center" Foreground="{DynamicResource MaterialDesignBody}" />
</Border>
</DataTemplate>
</StackPanel.Resources>
<ItemsControl ItemsSource="{Binding Items}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DragAdornerTemplate="{StaticResource CategoryDragTemplate}"
dd:DragDrop.DropHandler="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Content="ADD NEW CATEGORY" Margin="10 10 10 0" Command="{s:Action AddCategory}" />
</StackPanel>
</ScrollViewer>
<!-- Bottom buttons -->
<Separator Grid.Row="4" Margin="8" />
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5 0 5 5">
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44"
Height="44"
ToolTip="View website"
Command="{s:Action OpenUrl}"
CommandParameter="https://artemis-rgb.com">
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
</Button>
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44"
Height="44"
ToolTip="View GitHub repository"
Command="{s:Action OpenUrl}"
CommandParameter="https://github.com/Artemis-RGB/Artemis">
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
</Button>
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44"
Height="44"
ToolTip="View Wiki"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com">
<materialDesign:PackIcon Kind="BookOpenOutline" Width="20" Height="20" />
</Button>
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44"
Height="44"
ToolTip="Join our Discord"
Command="{s:Action OpenUrl}"
CommandParameter="https://discord.gg/S3MVaC9">
<materialDesign:PackIcon Kind="Discord" Width="20" Height="20" />
</Button>
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
Width="44"
Height="44"
ToolTip="View donation options"
Command="{s:Action OpenUrl}"
CommandParameter="https://wiki.artemis-rgb.com/en/donating">
<materialDesign:PackIcon Kind="Gift" Width="20" Height="20" />
</Button>
</StackPanel>
</Grid>
</materialDesign:DialogHost>
</UserControl>
@@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.Sidebar
{
/// <summary>
/// Interaction logic for SidebarView.xaml
/// </summary>
public partial class SidebarView : UserControl
{
public SidebarView()
{
InitializeComponent();
}
}
}
+138 -209
View File
@@ -1,285 +1,214 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Home;
using Artemis.UI.Screens.Modules;
using Artemis.UI.Screens.News;
using Artemis.UI.Screens.ProfileEditor;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Sidebar.Dialogs;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.Workshop;
using Artemis.UI.Shared;
using MaterialDesignExtensions.Controls;
using MaterialDesignExtensions.Model;
using Artemis.UI.Shared.Services;
using GongSolutions.Wpf.DragDrop;
using MaterialDesignThemes.Wpf;
using Ninject;
using RGB.NET.Core;
using Stylet;
namespace Artemis.UI.Screens.Sidebar
{
public sealed class SidebarViewModel : Screen, IHandle<RequestSelectSidebarItemEvent>, IDisposable
public sealed class SidebarViewModel : Conductor<SidebarCategoryViewModel>.Collection.AllActive, IHandle<RequestSelectSidebarItemEvent>, IDropTarget
{
private readonly Timer _activeModulesUpdateTimer;
private readonly IKernel _kernel;
private readonly IModuleVmFactory _moduleVmFactory;
private readonly IPluginManagementService _pluginManagementService;
private readonly IModuleService _moduleService;
private string _activeModules;
private bool _isSidebarOpen;
private IScreen _selectedItem;
private BindableCollection<INavigationItem> _sidebarItems;
private Dictionary<INavigationItem, Module> _sidebarModules;
private readonly ISidebarVmFactory _sidebarVmFactory;
private readonly IRgbService _rgbService;
private readonly IProfileService _profileService;
private readonly IProfileEditorService _profileEditorService;
private readonly IDialogService _dialogService;
private SidebarScreenViewModel _selectedSidebarScreen;
private ArtemisDevice _headerDevice;
private Screen _selectedScreen;
private readonly SidebarScreenViewModel<ProfileEditorViewModel> _profileEditor;
private readonly DefaultDropHandler _defaultDropHandler;
public SidebarViewModel(IKernel kernel, ISettingsService settingsService, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginManagementService pluginManagementService, IModuleService moduleService)
public SidebarViewModel(IKernel kernel,
IEventAggregator eventAggregator,
ISidebarVmFactory sidebarVmFactory,
IRgbService rgbService,
IProfileService profileService,
IProfileEditorService profileEditorService,
IDialogService dialogService)
{
_kernel = kernel;
_moduleVmFactory = moduleVmFactory;
_pluginManagementService = pluginManagementService;
_moduleService = moduleService;
_sidebarVmFactory = sidebarVmFactory;
_rgbService = rgbService;
_profileService = profileService;
_profileEditorService = profileEditorService;
_dialogService = dialogService;
_profileEditor = new SidebarScreenViewModel<ProfileEditorViewModel>(PackIconKind.Wrench, "Profile Editor");
_defaultDropHandler = new DefaultDropHandler();
SidebarModules = new Dictionary<INavigationItem, Module>();
SidebarItems = new BindableCollection<INavigationItem>();
PinSidebar = settingsService.GetSetting("UI.PinSidebar", false);
PinSidebar.AutoSave = true;
_activeModulesUpdateTimer = new Timer(1000);
eventAggregator.Subscribe(this);
SidebarScreens = new BindableCollection<SidebarScreenViewModel>
{
new SidebarScreenViewModel<HomeViewModel>(PackIconKind.Home, "Home"),
new SidebarScreenViewModel<WorkshopViewModel>(PackIconKind.TestTube, "Workshop"),
new SidebarScreenViewModel<SurfaceEditorViewModel>(PackIconKind.Devices, "Surface Editor"),
new SidebarScreenViewModel<SettingsViewModel>(PackIconKind.Cog, "Settings")
};
SelectedSidebarScreen = SidebarScreens.First();
UpdateProfileCategories();
UpdateHeaderDevice();
}
public PluginSetting<bool> PinSidebar { get; }
public BindableCollection<INavigationItem> SidebarItems
private void UpdateHeaderDevice()
{
get => _sidebarItems;
set => SetAndNotify(ref _sidebarItems, value);
HeaderDevice = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard && d.Layout.IsValid);
}
public Dictionary<INavigationItem, Module> SidebarModules
public ArtemisDevice HeaderDevice
{
get => _sidebarModules;
set => SetAndNotify(ref _sidebarModules, value);
get => _headerDevice;
set => SetAndNotify(ref _headerDevice, value);
}
public string ActiveModules
public BindableCollection<SidebarScreenViewModel> SidebarScreens { get; }
public Screen SelectedScreen
{
get => _activeModules;
set => SetAndNotify(ref _activeModules, value);
get => _selectedScreen;
private set => SetAndNotify(ref _selectedScreen, value);
}
public IScreen SelectedItem
public SidebarScreenViewModel SelectedSidebarScreen
{
get => _selectedItem;
set => SetAndNotify(ref _selectedItem, value);
}
public bool IsSidebarOpen
{
get => _isSidebarOpen;
get => _selectedSidebarScreen;
set
{
SetAndNotify(ref _isSidebarOpen, value);
if (value)
ActiveModulesUpdateTimerOnElapsed(this, EventArgs.Empty);
if (SetAndNotify(ref _selectedSidebarScreen, value))
ActivateScreenViewModel(_selectedSidebarScreen);
}
}
public void SetupSidebar()
private void ActivateScreenViewModel(SidebarScreenViewModel screenViewModel)
{
UpdateSidebarItems();
// Set the sidebar as open if it's pinned
if (PinSidebar.Value)
IsSidebarOpen = true;
// Select the top item, which will be one of the defaults
Task.Run(() => SelectSidebarItem(SidebarItems[0]));
SelectedScreen = screenViewModel.CreateInstance(_kernel);
OnSelectedScreenChanged();
if (screenViewModel != _profileEditor)
SelectProfileConfiguration(null);
}
private void UpdateSidebarItems()
private void UpdateProfileCategories()
{
SidebarItems.Clear();
SidebarModules.Clear();
// Add all default sidebar items
SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Home, Label = "Home" });
SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Newspaper, Label = "News" });
SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.TestTube, Label = "Workshop" });
SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Edit, Label = "Surface Editor" });
SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Settings, Label = "Settings" });
// Add all activated modules
SidebarItems.Add(new DividerNavigationItem());
List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
foreach (IGrouping<ModulePriorityCategory, Module> category in modules.OrderByDescending(m => m.PriorityCategory).GroupBy(m => m.PriorityCategory))
{
SidebarItems.Add(new SubheaderNavigationItem { Subheader = category.Key.ToString() });
foreach(Module module in category.OrderBy(m => m.Priority))
{
AddModule(module);
}
}
foreach (ProfileCategory profileCategory in _profileService.ProfileCategories.OrderBy(p => p.Order))
AddProfileCategoryViewModel(profileCategory);
}
// ReSharper disable once UnusedMember.Global - Called by view
public void SelectItem(WillSelectNavigationItemEventArgs args)
public async Task AddCategory()
{
if (args.NavigationItemToSelect == null)
{
SelectedItem = null;
return;
}
object result = await _dialogService.ShowDialog<SidebarCategoryCreateViewModel>();
if (result is ProfileCategory profileCategory)
AddProfileCategoryViewModel(profileCategory);
SelectSidebarItem(args.NavigationItemToSelect);
((BindableCollection<SidebarCategoryViewModel>) Items).Sort(p => p.ProfileCategory.Order);
}
public void AddModule(Module module)
public void OpenUrl(string url)
{
// Ensure the module is not already in the list
if (SidebarModules.Any(io => io.Value == module))
return;
FirstLevelNavigationItem sidebarItem = new()
{
Icon = PluginUtilities.GetPluginIcon(module.Plugin, module.DisplayIcon),
Label = module.DisplayName
};
SidebarItems.Add(sidebarItem);
SidebarModules.Add(sidebarItem, module);
}
public void RemoveModule(Module module)
{
// If not in the list there's nothing to do
if (SidebarModules.All(io => io.Value != module))
return;
KeyValuePair<INavigationItem, Module> existing = SidebarModules.First(io => io.Value == module);
SidebarItems.Remove(existing.Key);
SidebarModules.Remove(existing.Key);
}
private void ActiveModulesUpdateTimerOnElapsed(object sender, EventArgs e)
{
if (!IsSidebarOpen)
return;
int activeModules = SidebarModules.Count(m => m.Value.IsActivated);
ActiveModules = activeModules == 1 ? "1 active module" : $"{activeModules} active modules";
}
private void SelectSidebarItem(INavigationItem sidebarItem)
{
// A module was selected if the dictionary contains the selected item
if (SidebarModules.ContainsKey(sidebarItem))
ActivateModule(sidebarItem);
else if (sidebarItem is FirstLevelNavigationItem navigationItem)
ActivateViewModel(navigationItem.Label);
else
SelectedItem = null;
}
private void ActivateViewModel(string label)
{
if (label == "Home")
ActivateViewModel<HomeViewModel>();
else if (label == "News")
ActivateViewModel<NewsViewModel>();
else if (label == "Workshop")
ActivateViewModel<WorkshopViewModel>();
else if (label == "Surface Editor")
ActivateViewModel<SurfaceEditorViewModel>();
else if (label == "Settings")
ActivateViewModel<SettingsViewModel>();
}
private void ActivateViewModel<T>()
{
if (SelectedItem != null && SelectedItem.GetType() == typeof(T))
return;
SelectedItem = (IScreen) _kernel.Get<T>();
}
private void ActivateModule(INavigationItem sidebarItem)
{
if (!SidebarModules.ContainsKey(sidebarItem))
return;
if (SelectedItem is ModuleRootViewModel moduleRootViewModel && moduleRootViewModel.Module == SidebarModules[sidebarItem])
return;
SelectedItem = _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]);
}
#region IDisposable
public void Dispose()
{
_activeModulesUpdateTimer?.Dispose();
}
#endregion
#region Event handlers
private void OnFeatureEnabled(object sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature is Module)
UpdateSidebarItems();
}
private void OnFeatureDisabled(object sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature is Module)
UpdateSidebarItems();
}
private void OnModulePriorityUpdated(object sender, EventArgs e)
{
UpdateSidebarItems();
Core.Utilities.OpenUrl(url);
}
public void Handle(RequestSelectSidebarItemEvent message)
{
ActivateViewModel(message.Label);
SidebarScreenViewModel requested = SidebarScreens.FirstOrDefault(s => s.DisplayName == message.DisplayName);
if (requested != null)
SelectedSidebarScreen = requested;
}
#endregion
public SidebarCategoryViewModel AddProfileCategoryViewModel(ProfileCategory profileCategory)
{
SidebarCategoryViewModel viewModel = _sidebarVmFactory.SidebarCategoryViewModel(profileCategory);
Items.Add(viewModel);
return viewModel;
}
public void RemoveProfileCategoryViewModel(SidebarCategoryViewModel viewModel)
{
Items.Remove(viewModel);
}
public void SelectProfileConfiguration(ProfileConfiguration profileConfiguration)
{
foreach (SidebarCategoryViewModel sidebarCategoryViewModel in Items)
sidebarCategoryViewModel.SelectedProfileConfiguration = sidebarCategoryViewModel.Items.FirstOrDefault(i => i.ProfileConfiguration == profileConfiguration);
_profileEditorService.ChangeSelectedProfileConfiguration(profileConfiguration);
if (profileConfiguration != null)
{
// Little workaround to clear the selected item in the menu, ugly but oh well
if (_selectedSidebarScreen != _profileEditor)
{
_selectedSidebarScreen = null;
NotifyOfPropertyChange(nameof(SelectedSidebarScreen));
}
SelectedSidebarScreen = _profileEditor;
}
}
#region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate()
{
_activeModulesUpdateTimer.Start();
_activeModulesUpdateTimer.Elapsed += ActiveModulesUpdateTimerOnElapsed;
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnabled;
_pluginManagementService.PluginFeatureDisabled += OnFeatureDisabled;
_moduleService.ModulePriorityUpdated += OnModulePriorityUpdated;
SetupSidebar();
base.OnInitialActivate();
}
/// <inheritdoc />
protected override void OnClose()
{
_activeModulesUpdateTimer.Stop();
_activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed;
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnabled;
_pluginManagementService.PluginFeatureDisabled -= OnFeatureDisabled;
_moduleService.ModulePriorityUpdated -= OnModulePriorityUpdated;
base.OnClose();
}
#endregion
#region Events
public event EventHandler SelectedScreenChanged;
private void OnSelectedScreenChanged()
{
SelectedScreenChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region Implementation of IDropTarget
/// <inheritdoc />
public void DragOver(IDropInfo dropInfo)
{
_defaultDropHandler.DragOver(dropInfo);
}
/// <inheritdoc />
public void Drop(IDropInfo dropInfo)
{
_defaultDropHandler.Drop(dropInfo);
for (int index = 0; index < Items.Count; index++)
Items[index].ProfileCategory.Order = index;
// Bit dumb but gets the job done
foreach (SidebarCategoryViewModel viewModel in Items)
_profileService.SaveProfileCategory(viewModel.ProfileCategory);
}
#endregion
}
}
@@ -1,18 +1,10 @@
using System.Windows.Navigation;
using Artemis.Core.Services;
using Stylet;
namespace Artemis.UI.Screens.StartupWizard.Steps
{
public class FinishStepViewModel : Screen
{
private readonly ICoreService _coreService;
public FinishStepViewModel(ICoreService coreService)
{
_coreService = coreService;
}
public void OpenHyperlink(object sender, RequestNavigateEventArgs e)
{
Core.Utilities.OpenUrl(e.Uri.AbsoluteUri);
@@ -22,11 +14,5 @@ namespace Artemis.UI.Screens.StartupWizard.Steps
{
((StartupWizardViewModel) Parent).SkipOrFinishWizard();
}
protected override void OnActivate()
{
_coreService.PlayIntroAnimation();
base.OnActivate();
}
}
}
@@ -15,7 +15,7 @@
MouseLeave="{s:Action MouseLeave}"
ToolTip="{Binding Device.RgbDevice.DeviceInfo.DeviceName}">
<UserControl.Resources>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</UserControl.Resources>
<Grid>
<!-- Content -->
-5
View File
@@ -26,11 +26,6 @@
<materialDesign:PackIcon Kind="Home" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="News" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="News">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Newspaper" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Workshop" Command="{s:Action TrayActivateSidebarItem}" CommandParameter="Workshop">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="TestTube" />