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

Profile tree - Implemented VM creation on all levels

This commit is contained in:
Robert 2022-01-04 22:02:43 +01:00
parent 353eec2529
commit aa8667544f
10 changed files with 139 additions and 140 deletions

View File

@ -31,7 +31,7 @@ namespace Artemis.UI.Screens.Plugins
: PluginPrerequisite.UninstallActions.Select(a => new PluginPrerequisiteActionViewModel(a))); : PluginPrerequisite.UninstallActions.Select(a => new PluginPrerequisiteActionViewModel(a)));
this.WhenAnyValue(x => x.Installing, x => x.Uninstalling, (i, u) => i || u).ToProperty(this, x => x.Busy, out _busy); this.WhenAnyValue(x => x.Installing, x => x.Uninstalling, (i, u) => i || u).ToProperty(this, x => x.Busy, out _busy);
this.WhenAnyValue(x => x.ActiveAction, a => Actions.IndexOf(a)).ToProperty(this, x => x.ActiveStepNumber, out _activeStepNumber); this.WhenAnyValue(x => x.ActiveAction, a => Actions.IndexOf(a!)).ToProperty(this, x => x.ActiveStepNumber, out _activeStepNumber);
PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged; PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged;

View File

@ -1,9 +1,10 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public partial class FolderTreeItemView : UserControl public class FolderTreeItemView : ReactiveUserControl<FolderTreeItemViewModel>
{ {
public FolderTreeItemView() public FolderTreeItemView()
{ {

View File

@ -1,22 +1,16 @@
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public class FolderTreeItemViewModel : TreeItemViewModel public class FolderTreeItemViewModel : TreeItemViewModel
{ {
public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IProfileEditorVmFactory profileEditorVmFactory, IWindowService windowService) : base(parent, folder, windowService) public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IWindowService windowService, IProfileEditorService profileEditorService,
IProfileEditorVmFactory profileEditorVmFactory) : base(parent, folder, windowService, profileEditorService, profileEditorVmFactory)
{ {
Folder = folder; Folder = folder;
foreach (ProfileElement profileElement in folder.Children)
{
if (profileElement is Folder childFolder)
Children.Add(profileEditorVmFactory.FolderTreeItemViewModel(this, childFolder));
else if (profileElement is Layer layer)
Children.Add(profileEditorVmFactory.LayerTreeItemViewModel(this, layer));
}
} }
public Folder Folder { get; } public Folder Folder { get; }

View File

@ -1,9 +1,10 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public partial class LayerTreeItemView : UserControl public class LayerTreeItemView : ReactiveUserControl<LayerTreeItemViewModel>
{ {
public LayerTreeItemView() public LayerTreeItemView()
{ {

View File

@ -1,11 +1,14 @@
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public class LayerTreeItemViewModel : TreeItemViewModel public class LayerTreeItemViewModel : TreeItemViewModel
{ {
public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer, IWindowService windowService) : base(parent, layer, windowService) public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer, IWindowService windowService, IProfileEditorService profileEditorService,
IProfileEditorVmFactory profileEditorVmFactory) : base(parent, layer, windowService, profileEditorService, profileEditorVmFactory)
{ {
Layer = layer; Layer = layer;
} }

View File

@ -6,12 +6,19 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView"> x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView">
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<TreeView Classes="no-right-margin" Items="{Binding TreeItems}" SelectedItem="{Binding SelectedTreeItem}"> <TreeView Classes="no-right-margin" Items="{Binding Children}" SelectedItem="{Binding SelectedChild}">
<TreeView.Styles> <TreeView.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.KeyBindings>
<KeyBinding Gesture="F2" Command="{Binding RenameElement}" CommandParameter="{Binding SelectedTreeItem}" />
<KeyBinding Gesture="Delete" Command="{Binding DeleteElement}" CommandParameter="{Binding SelectedTreeItem}" />
<KeyBinding Gesture="Ctrl+D" Command="{Binding DuplicateElement}" CommandParameter="{Binding SelectedTreeItem}" />
<KeyBinding Gesture="Ctrl+C" Command="{Binding CopyElement}" CommandParameter="{Binding SelectedTreeItem}" />
<KeyBinding Gesture="Ctrl+V" Command="{Binding PasteElement}" CommandParameter="{Binding SelectedTreeItem}" />
</TreeView.KeyBindings>
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeDataTemplate ItemsSource="{Binding Children}">
<ContentControl Content="{Binding}" /> <ContentControl Content="{Binding}" />

View File

@ -3,7 +3,7 @@ using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public partial class ProfileTreeView : ReactiveUserControl<ProfileTreeViewModel> public class ProfileTreeView : ReactiveUserControl<ProfileTreeViewModel>
{ {
public ProfileTreeView() public ProfileTreeView()
{ {

View File

@ -2,122 +2,54 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Commands;
using Artemis.UI.Services.ProfileEditor; using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared; using Artemis.UI.Shared.Services.Interfaces;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public class ProfileTreeViewModel : ActivatableViewModelBase public class ProfileTreeViewModel : TreeItemViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private TreeItemViewModel? _selectedChild;
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
private ReactiveCommand<Unit, Unit>? _addFolder;
private ReactiveCommand<Unit, Unit>? _addLayer;
private TreeItemViewModel? _selectedTreeItem;
public ProfileTreeViewModel(IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory) public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory)
: base(null, null, windowService, profileEditorService, profileEditorVmFactory)
{ {
_profileEditorService = profileEditorService;
_profileEditorVmFactory = profileEditorVmFactory;
this.WhenActivated(d => this.WhenActivated(d =>
{ {
profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration => profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
{ {
Folder rootFolder = configuration.Profile!.GetRootFolder(); ProfileElement = configuration.Profile!.GetRootFolder();
CreateTreeItems(rootFolder); SubscribeToProfileElement(d);
Observable.FromEventPattern<ProfileElementEventArgs>(x => rootFolder.ChildAdded += x, x => rootFolder.ChildAdded -= x).Subscribe(c => AddTreeItemIfMissing(c.EventArgs.ProfileElement)); CreateTreeItems();
Observable.FromEventPattern<ProfileElementEventArgs>(x => rootFolder.ChildRemoved += x, x => rootFolder.ChildRemoved -= x).Subscribe(c => RemoveTreeItemsIfFound(c.EventArgs.ProfileElement));
AddLayer = ReactiveCommand.Create(() => AddLayerToRoot(configuration.Profile), profileEditorService.ProfileConfiguration.Select(p => p != null));
AddFolder = ReactiveCommand.Create(() => AddFolderToRoot(configuration.Profile), profileEditorService.ProfileConfiguration.Select(p => p != null));
}).DisposeWith(d); }).DisposeWith(d);
profileEditorService.ProfileElement.WhereNotNull().Subscribe(SelectCurrentProfileElement).DisposeWith(d); profileEditorService.ProfileElement.WhereNotNull().Subscribe(SelectCurrentProfileElement).DisposeWith(d);
}); });
this.WhenAnyValue(vm => vm.SelectedTreeItem).Subscribe(model => profileEditorService.ChangeCurrentProfileElement(model?.ProfileElement));
this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model =>
{
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
});
} }
public ReactiveCommand<Unit, Unit>? AddLayer public TreeItemViewModel? SelectedChild
{ {
get => _addLayer; get => _selectedChild;
set => this.RaiseAndSetIfChanged(ref _addLayer, value); set => this.RaiseAndSetIfChanged(ref _selectedChild, value);
}
public ReactiveCommand<Unit, Unit>? AddFolder
{
get => _addFolder;
set => this.RaiseAndSetIfChanged(ref _addFolder, value);
}
public ObservableCollection<TreeItemViewModel> TreeItems { get; } = new();
public TreeItemViewModel? SelectedTreeItem
{
get => _selectedTreeItem;
set => this.RaiseAndSetIfChanged(ref _selectedTreeItem, value);
}
private void RemoveTreeItemsIfFound(ProfileElement profileElement)
{
List<TreeItemViewModel> toRemove = TreeItems.Where(t => t.ProfileElement == profileElement).ToList();
foreach (TreeItemViewModel treeItemViewModel in toRemove)
TreeItems.Remove(treeItemViewModel);
}
private void AddTreeItemIfMissing(ProfileElement profileElement)
{
if (TreeItems.Any(t => t.ProfileElement == profileElement))
return;
if (profileElement is Folder folder)
TreeItems.Insert(folder.Parent.Children.IndexOf(folder), _profileEditorVmFactory.FolderTreeItemViewModel(null, folder));
else if (profileElement is Layer layer)
TreeItems.Insert(layer.Parent.Children.IndexOf(layer), _profileEditorVmFactory.LayerTreeItemViewModel(null, layer));
}
private void AddFolderToRoot(Profile profile)
{
Folder rootFolder = profile.GetRootFolder();
Folder folder = new(rootFolder, "New folder");
_profileEditorService.ExecuteCommand(new AddProfileElement(folder, rootFolder, 0));
}
private void AddLayerToRoot(Profile profile)
{
Folder rootFolder = profile.GetRootFolder();
Layer layer = new(rootFolder, "New layer");
_profileEditorService.ExecuteCommand(new AddProfileElement(layer, rootFolder, 0));
}
private void CreateTreeItems(Folder rootFolder)
{
if (TreeItems.Any())
TreeItems.Clear();
foreach (ProfileElement profileElement in rootFolder.Children)
{
if (profileElement is Folder folder)
TreeItems.Add(_profileEditorVmFactory.FolderTreeItemViewModel(null, folder));
else if (profileElement is Layer layer)
TreeItems.Add(_profileEditorVmFactory.LayerTreeItemViewModel(null, layer));
}
} }
private void SelectCurrentProfileElement(RenderProfileElement element) private void SelectCurrentProfileElement(RenderProfileElement element)
{ {
if (SelectedTreeItem?.ProfileElement == element) if (SelectedChild?.ProfileElement == element)
return; return;
// Find the tree item belonging to the selected element // Find the tree item belonging to the selected element
List<TreeItemViewModel> treeItems = GetAllTreeItems(TreeItems); List<TreeItemViewModel> treeItems = GetAllTreeItems(Children);
TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element); TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element);
// Walk up the tree, expanding parents // Walk up the tree, expanding parents
@ -128,7 +60,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
currentParent = currentParent.Parent; currentParent = currentParent.Parent;
} }
SelectedTreeItem = selected; SelectedChild = selected;
} }
private List<TreeItemViewModel> GetAllTreeItems(ObservableCollection<TreeItemViewModel> treeItems) private List<TreeItemViewModel> GetAllTreeItems(ObservableCollection<TreeItemViewModel> treeItems)

View File

@ -1,9 +1,15 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Commands;
using Artemis.UI.Services.ProfileEditor;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using ReactiveUI; using ReactiveUI;
@ -12,44 +18,48 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{ {
public abstract class TreeItemViewModel : ActivatableViewModelBase public abstract class TreeItemViewModel : ActivatableViewModelBase
{ {
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
private readonly IWindowService _windowService; private readonly IWindowService _windowService;
private bool _isExpanded; private bool _isExpanded;
private ProfileElement? _profileElement;
protected TreeItemViewModel(TreeItemViewModel? parent, RenderProfileElement profileElement, IWindowService windowService) protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService,
IProfileEditorVmFactory profileEditorVmFactory)
{ {
_windowService = windowService; _windowService = windowService;
_profileEditorVmFactory = profileEditorVmFactory;
Parent = parent; Parent = parent;
ProfileElement = profileElement; ProfileElement = profileElement;
// AddLayerAtIndex = ReactiveCommandWithHistory.CreateWithHistory<int, Layer>( AddLayer = ReactiveCommand.Create(() =>
// (targetIndex, _) => new Layer(ProfileElement, "New folder"), {
// (targetIndex, _) => if (ProfileElement is Layer targetLayer)
// { profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(targetLayer.Parent, "New layer"), targetLayer.Parent, targetLayer.Order));
// Layer toRemove = (Layer) ProfileElement.Children.ElementAt(targetIndex); else if (ProfileElement != null)
// ProfileElement.RemoveChild(toRemove); profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(ProfileElement, "New layer"), ProfileElement, 0));
// return toRemove; });
// },
// historyId: ProfileElement.Profile.EntityId.ToString()
// );
// AddFolderAtIndex = ReactiveCommandWithHistory.CreateWithHistory<int, Folder>( AddFolder = ReactiveCommand.Create(() =>
// (targetIndex, _) => new Folder(ProfileElement, "New folder"), {
// (targetIndex, _) => if (ProfileElement is Layer targetLayer)
// { profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(targetLayer.Parent, "New folder"), targetLayer.Parent, targetLayer.Order));
// Folder toRemove = (Folder) ProfileElement.Children.ElementAt(targetIndex); else if (ProfileElement != null)
// ProfileElement.RemoveChild(toRemove); profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, "New folder"), ProfileElement, 0));
// return toRemove; });
// },
// historyId: ProfileElement.Profile.EntityId.ToString() this.WhenActivated(d =>
// ); {
SubscribeToProfileElement(d);
CreateTreeItems();
});
} }
public ReactiveCommand<int, Unit> AddLayerAtIndex { get; set; } public ProfileElement? ProfileElement
public ReactiveCommand<int, Unit> AddFolderAtIndex { get; set; } {
get => _profileElement;
public RenderProfileElement ProfileElement { get; } set => this.RaiseAndSetIfChanged(ref _profileElement, value);
public TreeItemViewModel? Parent { get; set; } }
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
public bool IsExpanded public bool IsExpanded
{ {
@ -57,19 +67,70 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
set => this.RaiseAndSetIfChanged(ref _isExpanded, value); set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
} }
public TreeItemViewModel? Parent { get; set; }
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
public ReactiveCommand<Unit, Unit> AddLayer { get; }
public ReactiveCommand<Unit, Unit> AddFolder { get; }
public async Task ShowBrokenStateExceptions() public async Task ShowBrokenStateExceptions()
{ {
if (ProfileElement == null)
return;
List<IBreakableModel> broken = ProfileElement.GetBrokenHierarchy().Where(b => b.BrokenStateException != null).ToList(); List<IBreakableModel> broken = ProfileElement.GetBrokenHierarchy().Where(b => b.BrokenStateException != null).ToList();
foreach (IBreakableModel current in broken) foreach (IBreakableModel current in broken)
{ {
_windowService.ShowExceptionDialog($"{current.BrokenDisplayName} - {current.BrokenState}", current.BrokenStateException!); _windowService.ShowExceptionDialog($"{current.BrokenDisplayName} - {current.BrokenState}", current.BrokenStateException!);
if (broken.Last() != current) if (broken.Last() != current)
{
if (!await _windowService.ShowConfirmContentDialog("Broken state", "Do you want to view the next exception?")) if (!await _windowService.ShowConfirmContentDialog("Broken state", "Do you want to view the next exception?"))
return; return;
} }
} }
protected void SubscribeToProfileElement(CompositeDisposable d)
{
if (ProfileElement == null)
return;
Observable.FromEventPattern<ProfileElementEventArgs>(x => ProfileElement.ChildAdded += x, x => ProfileElement.ChildAdded -= x)
.Subscribe(c => AddTreeItemIfMissing(c.EventArgs.ProfileElement)).DisposeWith(d);
Observable.FromEventPattern<ProfileElementEventArgs>(x => ProfileElement.ChildRemoved += x, x => ProfileElement.ChildRemoved -= x)
.Subscribe(c => RemoveTreeItemsIfFound(c.EventArgs.ProfileElement)).DisposeWith(d);
}
protected void RemoveTreeItemsIfFound(ProfileElement profileElement)
{
List<TreeItemViewModel> toRemove = Children.Where(t => t.ProfileElement == profileElement).ToList();
foreach (TreeItemViewModel treeItemViewModel in toRemove)
Children.Remove(treeItemViewModel);
}
protected void AddTreeItemIfMissing(ProfileElement profileElement)
{
if (Children.Any(t => t.ProfileElement == profileElement))
return;
if (profileElement is Folder folder)
Children.Insert(folder.Parent.Children.IndexOf(folder), _profileEditorVmFactory.FolderTreeItemViewModel(this, folder));
else if (profileElement is Layer layer)
Children.Insert(layer.Parent.Children.IndexOf(layer), _profileEditorVmFactory.LayerTreeItemViewModel(this, layer));
}
protected void CreateTreeItems()
{
if (Children.Any())
Children.Clear();
if (ProfileElement == null)
return;
foreach (ProfileElement profileElement in ProfileElement.Children)
if (profileElement is Folder folder)
Children.Add(_profileEditorVmFactory.FolderTreeItemViewModel(this, folder));
else if (profileElement is Layer layer)
Children.Add(_profileEditorVmFactory.LayerTreeItemViewModel(this, layer));
} }
} }
} }