diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs index 8a2c6c28f..57b24ee12 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs @@ -31,7 +31,7 @@ namespace Artemis.UI.Screens.Plugins : 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.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; diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml.cs index cbd0ff111..f12519c99 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemView.axaml.cs @@ -1,9 +1,10 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { - public partial class FolderTreeItemView : UserControl + public class FolderTreeItemView : ReactiveUserControl { public FolderTreeItemView() { @@ -15,4 +16,4 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree AvaloniaXamlLoader.Load(this); } } -} +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemViewModel.cs index 848a194a7..a2f4627b9 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/FolderTreeItemViewModel.cs @@ -1,22 +1,16 @@ using Artemis.Core; using Artemis.UI.Ninject.Factories; +using Artemis.UI.Services.ProfileEditor; using Artemis.UI.Shared.Services.Interfaces; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { 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; - - 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; } diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml index 607d2f68b..5c3b545bb 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml @@ -14,7 +14,7 @@ Command="{Binding ShowBrokenStateExceptions}"> - + - + \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml.cs index 362581616..a523edffa 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemView.axaml.cs @@ -1,9 +1,10 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { - public partial class LayerTreeItemView : UserControl + public class LayerTreeItemView : ReactiveUserControl { public LayerTreeItemView() { @@ -15,4 +16,4 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree AvaloniaXamlLoader.Load(this); } } -} +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemViewModel.cs index c296d121e..69908ad6a 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/LayerTreeItemViewModel.cs @@ -1,11 +1,14 @@ using Artemis.Core; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Services.ProfileEditor; using Artemis.UI.Shared.Services.Interfaces; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { 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; } diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml index be12de446..5ed606f00 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml @@ -6,12 +6,19 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView"> - + + + + + + + + diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml.cs index 8f74e4ba7..af16c92e8 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml.cs @@ -3,7 +3,7 @@ using Avalonia.ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { - public partial class ProfileTreeView : ReactiveUserControl + public class ProfileTreeView : ReactiveUserControl { public ProfileTreeView() { @@ -15,4 +15,4 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree AvaloniaXamlLoader.Load(this); } } -} +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs index 1ab846d2c..e193cd003 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs @@ -2,122 +2,54 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Reactive; using System.Reactive.Disposables; -using System.Reactive.Linq; 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.Services.Interfaces; using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { - public class ProfileTreeViewModel : ActivatableViewModelBase + public class ProfileTreeViewModel : TreeItemViewModel { - private readonly IProfileEditorService _profileEditorService; - private readonly IProfileEditorVmFactory _profileEditorVmFactory; - private ReactiveCommand? _addFolder; - private ReactiveCommand? _addLayer; - private TreeItemViewModel? _selectedTreeItem; + private TreeItemViewModel? _selectedChild; - 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 => { profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration => { - Folder rootFolder = configuration.Profile!.GetRootFolder(); - CreateTreeItems(rootFolder); - Observable.FromEventPattern(x => rootFolder.ChildAdded += x, x => rootFolder.ChildAdded -= x).Subscribe(c => AddTreeItemIfMissing(c.EventArgs.ProfileElement)); - Observable.FromEventPattern(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)); + ProfileElement = configuration.Profile!.GetRootFolder(); + SubscribeToProfileElement(d); + CreateTreeItems(); }).DisposeWith(d); profileEditorService.ProfileElement.WhereNotNull().Subscribe(SelectCurrentProfileElement).DisposeWith(d); }); - this.WhenAnyValue(vm => vm.SelectedTreeItem).Subscribe(model => profileEditorService.ChangeCurrentProfileElement(model?.ProfileElement)); - } - public ReactiveCommand? AddLayer - { - get => _addLayer; - set => this.RaiseAndSetIfChanged(ref _addLayer, value); - } - - public ReactiveCommand? AddFolder - { - get => _addFolder; - set => this.RaiseAndSetIfChanged(ref _addFolder, value); - } - - public ObservableCollection TreeItems { get; } = new(); - - public TreeItemViewModel? SelectedTreeItem - { - get => _selectedTreeItem; - set => this.RaiseAndSetIfChanged(ref _selectedTreeItem, value); - } - - private void RemoveTreeItemsIfFound(ProfileElement profileElement) - { - List 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) + this.WhenAnyValue(vm => vm.SelectedChild).Subscribe(model => { - if (profileElement is Folder folder) - TreeItems.Add(_profileEditorVmFactory.FolderTreeItemViewModel(null, folder)); - else if (profileElement is Layer layer) - TreeItems.Add(_profileEditorVmFactory.LayerTreeItemViewModel(null, layer)); - } + if (model?.ProfileElement is RenderProfileElement renderProfileElement) + profileEditorService.ChangeCurrentProfileElement(renderProfileElement); + }); + } + + public TreeItemViewModel? SelectedChild + { + get => _selectedChild; + set => this.RaiseAndSetIfChanged(ref _selectedChild, value); } private void SelectCurrentProfileElement(RenderProfileElement element) { - if (SelectedTreeItem?.ProfileElement == element) + if (SelectedChild?.ProfileElement == element) return; // Find the tree item belonging to the selected element - List treeItems = GetAllTreeItems(TreeItems); + List treeItems = GetAllTreeItems(Children); TreeItemViewModel? selected = treeItems.FirstOrDefault(e => e.ProfileElement == element); // Walk up the tree, expanding parents @@ -128,7 +60,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree currentParent = currentParent.Parent; } - SelectedTreeItem = selected; + SelectedChild = selected; } private List GetAllTreeItems(ObservableCollection treeItems) diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs index bfe429988..441089fc7 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs @@ -1,9 +1,15 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; +using System.Reactive.Disposables; +using System.Reactive.Linq; using System.Threading.Tasks; 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.Services.Interfaces; using ReactiveUI; @@ -12,44 +18,48 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { public abstract class TreeItemViewModel : ActivatableViewModelBase { + private readonly IProfileEditorVmFactory _profileEditorVmFactory; private readonly IWindowService _windowService; 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; + _profileEditorVmFactory = profileEditorVmFactory; + Parent = parent; ProfileElement = profileElement; - // AddLayerAtIndex = ReactiveCommandWithHistory.CreateWithHistory( - // (targetIndex, _) => new Layer(ProfileElement, "New folder"), - // (targetIndex, _) => - // { - // Layer toRemove = (Layer) ProfileElement.Children.ElementAt(targetIndex); - // ProfileElement.RemoveChild(toRemove); - // return toRemove; - // }, - // historyId: ProfileElement.Profile.EntityId.ToString() - // ); + AddLayer = ReactiveCommand.Create(() => + { + if (ProfileElement is Layer targetLayer) + profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(targetLayer.Parent, "New layer"), targetLayer.Parent, targetLayer.Order)); + else if (ProfileElement != null) + profileEditorService.ExecuteCommand(new AddProfileElement(new Layer(ProfileElement, "New layer"), ProfileElement, 0)); + }); - // AddFolderAtIndex = ReactiveCommandWithHistory.CreateWithHistory( - // (targetIndex, _) => new Folder(ProfileElement, "New folder"), - // (targetIndex, _) => - // { - // Folder toRemove = (Folder) ProfileElement.Children.ElementAt(targetIndex); - // ProfileElement.RemoveChild(toRemove); - // return toRemove; - // }, - // historyId: ProfileElement.Profile.EntityId.ToString() - // ); + AddFolder = ReactiveCommand.Create(() => + { + if (ProfileElement is Layer targetLayer) + profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(targetLayer.Parent, "New folder"), targetLayer.Parent, targetLayer.Order)); + else if (ProfileElement != null) + profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, "New folder"), ProfileElement, 0)); + }); + + this.WhenActivated(d => + { + SubscribeToProfileElement(d); + CreateTreeItems(); + }); } - public ReactiveCommand AddLayerAtIndex { get; set; } - public ReactiveCommand AddFolderAtIndex { get; set; } - - public RenderProfileElement ProfileElement { get; } - public TreeItemViewModel? Parent { get; set; } - public ObservableCollection Children { get; } = new(); + public ProfileElement? ProfileElement + { + get => _profileElement; + set => this.RaiseAndSetIfChanged(ref _profileElement, value); + } public bool IsExpanded { @@ -57,19 +67,70 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree set => this.RaiseAndSetIfChanged(ref _isExpanded, value); } + public TreeItemViewModel? Parent { get; set; } + public ObservableCollection Children { get; } = new(); + + public ReactiveCommand AddLayer { get; } + public ReactiveCommand AddFolder { get; } + public async Task ShowBrokenStateExceptions() { + if (ProfileElement == null) + return; + List broken = ProfileElement.GetBrokenHierarchy().Where(b => b.BrokenStateException != null).ToList(); foreach (IBreakableModel current in broken) { _windowService.ShowExceptionDialog($"{current.BrokenDisplayName} - {current.BrokenState}", current.BrokenStateException!); if (broken.Last() != current) - { if (!await _windowService.ShowConfirmContentDialog("Broken state", "Do you want to view the next exception?")) return; - } } } + + protected void SubscribeToProfileElement(CompositeDisposable d) + { + if (ProfileElement == null) + return; + + Observable.FromEventPattern(x => ProfileElement.ChildAdded += x, x => ProfileElement.ChildAdded -= x) + .Subscribe(c => AddTreeItemIfMissing(c.EventArgs.ProfileElement)).DisposeWith(d); + Observable.FromEventPattern(x => ProfileElement.ChildRemoved += x, x => ProfileElement.ChildRemoved -= x) + .Subscribe(c => RemoveTreeItemsIfFound(c.EventArgs.ProfileElement)).DisposeWith(d); + } + + protected void RemoveTreeItemsIfFound(ProfileElement profileElement) + { + List 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)); + } } } \ No newline at end of file