mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-31 17:53:32 +00:00
Profile editor - Fixed new elements not rendering while paused
Profile editor - Use default brush on new layers Profile tree - Implement drag & drop
This commit is contained in:
parent
f5a902f5a5
commit
376a9142d3
@ -185,7 +185,7 @@ namespace Artemis.Core
|
|||||||
// No point rendering if all children are disabled
|
// No point rendering if all children are disabled
|
||||||
if (!Children.Any(c => c is RenderProfileElement {Enabled: true}))
|
if (!Children.Any(c => c is RenderProfileElement {Enabled: true}))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SKPaint layerPaint = new() {FilterQuality = SKFilterQuality.Low};
|
SKPaint layerPaint = new() {FilterQuality = SKFilterQuality.Low};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -266,7 +266,7 @@ namespace Artemis.Core
|
|||||||
/// Occurs when a property affecting the rendering properties of this folder has been updated
|
/// Occurs when a property affecting the rendering properties of this folder has been updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler? RenderPropertiesUpdated;
|
public event EventHandler? RenderPropertiesUpdated;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -27,5 +27,7 @@ namespace Artemis.Core.Services
|
|||||||
/// Returns the descriptor of the default layer brush
|
/// Returns the descriptor of the default layer brush
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LayerBrushDescriptor? GetDefaultLayerBrush();
|
LayerBrushDescriptor? GetDefaultLayerBrush();
|
||||||
|
|
||||||
|
void ApplyDefaultBrush(Layer layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,5 +46,13 @@ namespace Artemis.Core.Services
|
|||||||
defaultReference.Value.BrushType ??= "SolidBrush";
|
defaultReference.Value.BrushType ??= "SolidBrush";
|
||||||
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
|
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ApplyDefaultBrush(Layer layer)
|
||||||
|
{
|
||||||
|
LayerBrushDescriptor? brush = GetDefaultLayerBrush();
|
||||||
|
if (brush != null)
|
||||||
|
layer.ChangeLayerBrush(brush.CreateInstance(layer, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,6 +47,8 @@ public class AddProfileElement : IProfileEditorCommand, IDisposable
|
|||||||
{
|
{
|
||||||
_isAdded = true;
|
_isAdded = true;
|
||||||
_target.AddChild(_subject, _index);
|
_target.AddChild(_subject, _index);
|
||||||
|
|
||||||
|
_subject.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -134,7 +134,7 @@ public abstract class PropertyInputViewModel<T> : PropertyInputViewModel
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (_preview.DiscardPreview() && _preview.PreviewValue != null)
|
if (_preview.DiscardPreview() && _preview.PreviewValue != null)
|
||||||
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _preview.PreviewValue, Time));
|
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<T>(LayerProperty, _inputValue, _preview.OriginalValue, Time));
|
||||||
_preview = null;
|
_preview = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/Avalonia/Artemis.UI/Converters/ColorOpacityConverter.cs
Normal file
23
src/Avalonia/Artemis.UI/Converters/ColorOpacityConverter.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Converters;
|
||||||
|
|
||||||
|
public class ColorOpacityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is Color color && double.TryParse(parameter?.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out double multiplier))
|
||||||
|
return new Color((byte) (color.A * multiplier), color.R, color.G, color.B);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -55,7 +55,9 @@
|
|||||||
</TextBox>
|
</TextBox>
|
||||||
<controls:ColorPickerButton Classes="contained-color-picker-button"
|
<controls:ColorPickerButton Classes="contained-color-picker-button"
|
||||||
Color="{CompiledBinding InputValue, Converter={StaticResource SKColorToColor2Converter}}"
|
Color="{CompiledBinding InputValue, Converter={StaticResource SKColorToColor2Converter}}"
|
||||||
ShowAcceptDismissButtons="False" />
|
ShowAcceptDismissButtons="False"
|
||||||
|
FlyoutOpened="ColorPickerButton_OnFlyoutOpened"
|
||||||
|
FlyoutClosed="ColorPickerButton_OnFlyoutClosed" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,8 +1,10 @@
|
|||||||
|
using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
namespace Artemis.UI.DefaultTypes.PropertyInput
|
namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||||
{
|
{
|
||||||
@ -17,5 +19,15 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
|||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ColorPickerButton_OnFlyoutOpened(ColorPickerButton sender, EventArgs args)
|
||||||
|
{
|
||||||
|
ViewModel?.StartPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ColorPickerButton_OnFlyoutClosed(ColorPickerButton sender, EventArgs args)
|
||||||
|
{
|
||||||
|
ViewModel?.ApplyPreview();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,11 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Generators;
|
using Avalonia.Controls.Generators;
|
||||||
using Avalonia.Controls.Primitives;
|
|
||||||
using Avalonia.Controls.Shapes;
|
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using Avalonia.Xaml.Interactions.DragAndDrop;
|
using Avalonia.Xaml.Interactions.DragAndDrop;
|
||||||
|
|
||||||
@ -34,7 +30,7 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
if (sender is ItemsControl itemsControl)
|
if (sender is ItemsControl itemsControl)
|
||||||
{
|
{
|
||||||
foreach (TreeViewItem treeViewItem in GetFlattenedTreeView(itemsControl))
|
foreach (TreeViewItem treeViewItem in GetFlattenedTreeView(itemsControl))
|
||||||
SetDraggingPseudoClasses(treeViewItem, TreeDropType.After, false);
|
SetDraggingPseudoClasses(treeViewItem, TreeDropType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -42,8 +38,11 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
|
|
||||||
public override void Cancel(object? sender, RoutedEventArgs e)
|
public override void Cancel(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is IControl control && control.FindAncestorOfType<TreeViewItem>() != null)
|
if (sender is ItemsControl itemsControl)
|
||||||
SetDraggingPseudoClasses(control.FindAncestorOfType<TreeViewItem>(), TreeDropType.After, false);
|
{
|
||||||
|
foreach (TreeViewItem treeViewItem in GetFlattenedTreeView(itemsControl))
|
||||||
|
SetDraggingPseudoClasses(treeViewItem, TreeDropType.None);
|
||||||
|
}
|
||||||
|
|
||||||
base.Cancel(sender, e);
|
base.Cancel(sender, e);
|
||||||
}
|
}
|
||||||
@ -102,6 +101,9 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
if (e.DragEffects != DragDropEffects.Move)
|
if (e.DragEffects != DragDropEffects.Move)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
foreach (TreeViewItem treeViewItem in GetFlattenedTreeView(treeView))
|
||||||
|
SetDraggingPseudoClasses(treeViewItem, TreeDropType.None);
|
||||||
|
|
||||||
if (bExecute)
|
if (bExecute)
|
||||||
{
|
{
|
||||||
if (dropType == TreeDropType.Into)
|
if (dropType == TreeDropType.Into)
|
||||||
@ -118,7 +120,7 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetDraggingPseudoClasses((IControl) targetVisual, dropType, true);
|
SetDraggingPseudoClasses((IControl) targetVisual, dropType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -130,7 +132,7 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
|
|
||||||
foreach (ItemContainerInfo containerInfo in currentNode.ItemContainerGenerator.Containers)
|
foreach (ItemContainerInfo containerInfo in currentNode.ItemContainerGenerator.Containers)
|
||||||
{
|
{
|
||||||
if (containerInfo.ContainerControl is TreeViewItem treeViewItem && containerInfo.Item is TreeItemViewModel viewModel)
|
if (containerInfo.ContainerControl is TreeViewItem treeViewItem && containerInfo.Item is TreeItemViewModel)
|
||||||
{
|
{
|
||||||
result.Add(treeViewItem);
|
result.Add(treeViewItem);
|
||||||
if (treeViewItem.ItemContainerGenerator.Containers.Any())
|
if (treeViewItem.ItemContainerGenerator.Containers.Any())
|
||||||
@ -141,9 +143,16 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDraggingPseudoClasses(IControl control, TreeDropType type, bool isDragging)
|
private void SetDraggingPseudoClasses(IControl control, TreeDropType type)
|
||||||
{
|
{
|
||||||
if (isDragging)
|
if (type == TreeDropType.None)
|
||||||
|
{
|
||||||
|
((IPseudoClasses) control.Classes).Remove(":dragging");
|
||||||
|
((IPseudoClasses) control.Classes).Remove(":dragging-before");
|
||||||
|
((IPseudoClasses) control.Classes).Remove(":dragging-after");
|
||||||
|
((IPseudoClasses) control.Classes).Remove(":dragging-into");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
((IPseudoClasses) control.Classes).Add(":dragging");
|
((IPseudoClasses) control.Classes).Add(":dragging");
|
||||||
if (type == TreeDropType.Before)
|
if (type == TreeDropType.Before)
|
||||||
@ -165,19 +174,13 @@ public class ProfileTreeViewDropHandler : DropHandlerBase
|
|||||||
((IPseudoClasses) control.Classes).Add(":dragging-into");
|
((IPseudoClasses) control.Classes).Add(":dragging-into");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
((IPseudoClasses) control.Classes).Remove(":dragging");
|
|
||||||
((IPseudoClasses) control.Classes).Remove(":dragging-before");
|
|
||||||
((IPseudoClasses) control.Classes).Remove(":dragging-after");
|
|
||||||
((IPseudoClasses) control.Classes).Remove(":dragging-into");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum TreeDropType
|
private enum TreeDropType
|
||||||
{
|
{
|
||||||
Before,
|
Before,
|
||||||
After,
|
After,
|
||||||
Into
|
Into,
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,8 +8,14 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
public class FolderTreeItemViewModel : TreeItemViewModel
|
public class FolderTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
public FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder, IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
public FolderTreeItemViewModel(TreeItemViewModel? parent,
|
||||||
: base(parent, folder, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
Folder folder,
|
||||||
|
IWindowService windowService,
|
||||||
|
IProfileEditorService profileEditorService,
|
||||||
|
ILayerBrushService layerBrushService,
|
||||||
|
IProfileEditorVmFactory profileEditorVmFactory,
|
||||||
|
IRgbService rgbService)
|
||||||
|
: base(parent, folder, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
Folder = folder;
|
Folder = folder;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,14 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
public class LayerTreeItemViewModel : TreeItemViewModel
|
public class LayerTreeItemViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
public LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer, IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
public LayerTreeItemViewModel(TreeItemViewModel? parent,
|
||||||
: base(parent, layer, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
Layer layer,
|
||||||
|
IWindowService windowService,
|
||||||
|
IProfileEditorService profileEditorService,
|
||||||
|
IRgbService rgbService,
|
||||||
|
ILayerBrushService layerBrushService,
|
||||||
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
|
: base(parent, layer, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
Layer = layer;
|
Layer = layer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,14 +6,18 @@
|
|||||||
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
|
xmlns:profileTree="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree"
|
||||||
xmlns:profileBehaviors="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Behaviors"
|
xmlns:profileBehaviors="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileTree.Behaviors"
|
||||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||||
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
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"
|
||||||
x:DataType="profileTree:ProfileTreeViewModel">
|
x:DataType="profileTree:ProfileTreeViewModel">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<converters:ColorOpacityConverter x:Key="ColorOpacityConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<StyleInclude Source="avares://Avalonia.Xaml.Interactions/Draggable/Styles.axaml" />
|
<StyleInclude Source="avares://Avalonia.Xaml.Interactions/Draggable/Styles.axaml" />
|
||||||
<Style Selector="TreeView#ProfileTreeView">
|
<Style Selector="TreeView#ProfileTreeView">
|
||||||
<Style.Resources>
|
<Style.Resources>
|
||||||
<profileBehaviors:ProfileTreeViewDropHandler x:Key="ProfileTreeViewDropHandler" />
|
<profileBehaviors:ProfileTreeViewDropHandler x:Key="ProfileTreeViewDropHandler" />
|
||||||
</Style.Resources>
|
</Style.Resources>
|
||||||
<Setter Property="(Interaction.Behaviors)">
|
<Setter Property="(Interaction.Behaviors)">
|
||||||
<BehaviorCollectionTemplate>
|
<BehaviorCollectionTemplate>
|
||||||
@ -29,24 +33,56 @@
|
|||||||
<Setter Property="(Interaction.Behaviors)">
|
<Setter Property="(Interaction.Behaviors)">
|
||||||
<BehaviorCollectionTemplate>
|
<BehaviorCollectionTemplate>
|
||||||
<BehaviorCollection>
|
<BehaviorCollection>
|
||||||
<behaviors:SimpleContextDragBehavior />
|
<behaviors:SimpleContextDragBehavior />
|
||||||
</BehaviorCollection>
|
</BehaviorCollection>
|
||||||
</BehaviorCollectionTemplate>
|
</BehaviorCollectionTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-before">
|
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-before">
|
||||||
<Setter Property="Background" Value="DarkOrange" />
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-into">
|
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-into">
|
||||||
<Setter Property="Background" Value="Blue" />
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.0" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.05" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.05" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.25" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-after">
|
<Style Selector="TreeView#ProfileTreeView TreeViewItem:dragging-after">
|
||||||
<Setter Property="Background" Value="Green" />
|
<Setter Property="Background">
|
||||||
|
<Setter.Value>
|
||||||
|
<LinearGradientBrush StartPoint="0,0" EndPoint="0,28">
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0}" Offset="0.75" />
|
||||||
|
<GradientStop Color="{Binding Source={StaticResource SystemAccentColorLight3}, Converter={StaticResource ColorOpacityConverter}, ConverterParameter=0.25}" Offset="0.95" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="0.95" />
|
||||||
|
<GradientStop Color="{StaticResource SystemAccentColorLight3}" Offset="1" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
<TreeView Name="ProfileTreeView" Classes="no-right-margin draggable" Items="{CompiledBinding Children}" SelectedItem="{CompiledBinding SelectedChild}" SelectionChanged="ProfileTreeView_OnSelectionChanged">
|
<TreeView Name="ProfileTreeView" Classes="no-right-margin draggable" Items="{CompiledBinding Children}" SelectedItem="{CompiledBinding SelectedChild}"
|
||||||
|
SelectionChanged="ProfileTreeView_OnSelectionChanged">
|
||||||
<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}" />
|
||||||
|
|||||||
@ -15,13 +15,15 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
{
|
{
|
||||||
public class ProfileTreeViewModel : TreeItemViewModel
|
public class ProfileTreeViewModel : TreeItemViewModel
|
||||||
{
|
{
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
|
||||||
private TreeItemViewModel? _selectedChild;
|
private TreeItemViewModel? _selectedChild;
|
||||||
|
|
||||||
public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
|
public ProfileTreeViewModel(IWindowService windowService,
|
||||||
: base(null, null, windowService, profileEditorService, rgbService, profileEditorVmFactory)
|
IProfileEditorService profileEditorService,
|
||||||
|
ILayerBrushService layerBrushService,
|
||||||
|
IProfileEditorVmFactory profileEditorVmFactory,
|
||||||
|
IRgbService rgbService)
|
||||||
|
: base(null, null, windowService, profileEditorService, rgbService, layerBrushService, profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
_profileEditorService = profileEditorService;
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
|
profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
|
||||||
@ -46,7 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
|||||||
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
ClearSelection = ReactiveCommand.Create(() => _profileEditorService.ChangeCurrentProfileElement(null));
|
ClearSelection = ReactiveCommand.Create(() => profileEditorService.ChangeCurrentProfileElement(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> ClearSelection { get; }
|
public ReactiveCommand<Unit, Unit> ClearSelection { get; }
|
||||||
|
|||||||
@ -8,7 +8,6 @@ using System.Reactive.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Exceptions;
|
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
@ -16,220 +15,225 @@ using Artemis.UI.Shared.Services.ProfileEditor;
|
|||||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||||
|
|
||||||
|
public abstract class TreeItemViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
public abstract class TreeItemViewModel : ActivatableViewModelBase
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
|
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
private RenderProfileElement? _currentProfileElement;
|
||||||
|
private bool _isExpanded;
|
||||||
|
private ProfileElement? _profileElement;
|
||||||
|
private string? _renameValue;
|
||||||
|
private bool _renaming;
|
||||||
|
|
||||||
|
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement,
|
||||||
|
IWindowService windowService,
|
||||||
|
IProfileEditorService profileEditorService,
|
||||||
|
IRgbService rgbService,
|
||||||
|
ILayerBrushService layerBrushService,
|
||||||
|
IProfileEditorVmFactory profileEditorVmFactory)
|
||||||
{
|
{
|
||||||
private readonly IProfileEditorVmFactory _profileEditorVmFactory;
|
_windowService = windowService;
|
||||||
private readonly IWindowService _windowService;
|
_profileEditorService = profileEditorService;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
_profileEditorVmFactory = profileEditorVmFactory;
|
||||||
private bool _isExpanded;
|
|
||||||
private ProfileElement? _profileElement;
|
|
||||||
private RenderProfileElement? _currentProfileElement;
|
|
||||||
private bool _renaming;
|
|
||||||
private string? _renameValue;
|
|
||||||
|
|
||||||
protected TreeItemViewModel(TreeItemViewModel? parent, ProfileElement? profileElement, IWindowService windowService, IProfileEditorService profileEditorService, IRgbService rgbService,
|
Parent = parent;
|
||||||
IProfileEditorVmFactory profileEditorVmFactory)
|
ProfileElement = profileElement;
|
||||||
|
|
||||||
|
AddLayer = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
_windowService = windowService;
|
if (ProfileElement is Layer targetLayer)
|
||||||
_profileEditorService = profileEditorService;
|
|
||||||
_profileEditorVmFactory = profileEditorVmFactory;
|
|
||||||
|
|
||||||
Parent = parent;
|
|
||||||
ProfileElement = profileElement;
|
|
||||||
|
|
||||||
AddLayer = ReactiveCommand.Create(() =>
|
|
||||||
{
|
{
|
||||||
if (ProfileElement is Layer targetLayer)
|
Layer layer = new(targetLayer.Parent, targetLayer.GetNewLayerName());
|
||||||
{
|
layerBrushService.ApplyDefaultBrush(layer);
|
||||||
Layer layer = new(targetLayer.Parent, targetLayer.GetNewLayerName());
|
|
||||||
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
|
||||||
}
|
|
||||||
else if (ProfileElement != null)
|
|
||||||
{
|
|
||||||
Layer layer = new(ProfileElement, ProfileElement.GetNewLayerName());
|
|
||||||
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(layer, ProfileElement, 0));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AddFolder = ReactiveCommand.Create(() =>
|
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
{
|
profileEditorService.ExecuteCommand(new AddProfileElement(layer, targetLayer.Parent, targetLayer.Order));
|
||||||
if (ProfileElement is Layer targetLayer)
|
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(targetLayer.Parent, targetLayer.Parent.GetNewFolderName()), targetLayer.Parent, targetLayer.Order));
|
|
||||||
else if (ProfileElement != null)
|
|
||||||
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, ProfileElement.GetNewFolderName()), ProfileElement, 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
Rename = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
Renaming = true;
|
|
||||||
RenameValue = ProfileElement?.Name;
|
|
||||||
});
|
|
||||||
|
|
||||||
Duplicate = ReactiveCommand.Create(() => throw new NotImplementedException());
|
|
||||||
Copy = ReactiveCommand.Create(() => throw new NotImplementedException());
|
|
||||||
Paste = ReactiveCommand.Create(() => throw new NotImplementedException());
|
|
||||||
|
|
||||||
Delete = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
if (ProfileElement is RenderProfileElement renderProfileElement)
|
|
||||||
profileEditorService.ExecuteCommand(new RemoveProfileElement(renderProfileElement));
|
|
||||||
});
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
_profileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d);
|
|
||||||
SubscribeToProfileElement(d);
|
|
||||||
CreateTreeItems();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileElement? ProfileElement
|
|
||||||
{
|
|
||||||
get => _profileElement;
|
|
||||||
set => RaiseAndSetIfChanged(ref _profileElement, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsExpanded
|
|
||||||
{
|
|
||||||
get => _isExpanded;
|
|
||||||
set => RaiseAndSetIfChanged(ref _isExpanded, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Renaming
|
|
||||||
{
|
|
||||||
get => _renaming;
|
|
||||||
set => RaiseAndSetIfChanged(ref _renaming, 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 ReactiveCommand<Unit, Unit> Rename { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> Duplicate { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> Copy { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> Paste { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> Delete { get; }
|
|
||||||
public abstract bool SupportsChildren { get; }
|
|
||||||
|
|
||||||
public string? RenameValue
|
|
||||||
{
|
|
||||||
get => _renameValue;
|
|
||||||
set => RaiseAndSetIfChanged(ref _renameValue, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ShowBrokenStateExceptions()
|
|
||||||
{
|
|
||||||
if (ProfileElement == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<IBreakableModel> 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;
|
|
||||||
}
|
}
|
||||||
}
|
else if (ProfileElement != null)
|
||||||
|
|
||||||
public void SubmitRename()
|
|
||||||
{
|
|
||||||
if (ProfileElement == null)
|
|
||||||
{
|
{
|
||||||
Renaming = false;
|
Layer layer = new(ProfileElement, ProfileElement.GetNewLayerName());
|
||||||
return;
|
layerBrushService.ApplyDefaultBrush(layer);
|
||||||
|
|
||||||
|
layer.AddLeds(rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
|
profileEditorService.ExecuteCommand(new AddProfileElement(layer, ProfileElement, 0));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
_profileEditorService.ExecuteCommand(new RenameProfileElement(ProfileElement, RenameValue));
|
AddFolder = ReactiveCommand.Create(() =>
|
||||||
Renaming = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelRename()
|
|
||||||
{
|
{
|
||||||
Renaming = false;
|
if (ProfileElement is Layer targetLayer)
|
||||||
}
|
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(targetLayer.Parent, targetLayer.Parent.GetNewFolderName()), targetLayer.Parent, targetLayer.Order));
|
||||||
|
else if (ProfileElement != null)
|
||||||
|
profileEditorService.ExecuteCommand(new AddProfileElement(new Folder(ProfileElement, ProfileElement.GetNewFolderName()), ProfileElement, 0));
|
||||||
|
});
|
||||||
|
|
||||||
public void InsertElement(TreeItemViewModel elementViewModel, int targetIndex)
|
Rename = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
if (elementViewModel.Parent == this && Children.IndexOf(elementViewModel) == targetIndex)
|
Renaming = true;
|
||||||
return;
|
RenameValue = ProfileElement?.Name;
|
||||||
|
});
|
||||||
|
|
||||||
if (ProfileElement != null && elementViewModel.ProfileElement != null)
|
Duplicate = ReactiveCommand.Create(() => throw new NotImplementedException());
|
||||||
_profileEditorService.ExecuteCommand(new MoveProfileElement(ProfileElement, elementViewModel.ProfileElement, targetIndex));
|
Copy = ReactiveCommand.Create(() => throw new NotImplementedException());
|
||||||
}
|
Paste = ReactiveCommand.Create(() => throw new NotImplementedException());
|
||||||
|
|
||||||
protected void SubscribeToProfileElement(CompositeDisposable d)
|
Delete = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
if (ProfileElement == null)
|
if (ProfileElement is RenderProfileElement renderProfileElement)
|
||||||
return;
|
profileEditorService.ExecuteCommand(new RemoveProfileElement(renderProfileElement));
|
||||||
|
});
|
||||||
|
|
||||||
Observable.FromEventPattern<ProfileElementEventArgs>(x => ProfileElement.ChildAdded += x, x => ProfileElement.ChildAdded -= x)
|
this.WhenActivated(d =>
|
||||||
.Subscribe(c => AddTreeItemIfMissing(c.EventArgs.ProfileElement)).DisposeWith(d);
|
|
||||||
Observable.FromEventPattern<ProfileElementEventArgs>(x => ProfileElement.ChildRemoved += x, x => ProfileElement.ChildRemoved -= x)
|
|
||||||
.Subscribe(c => RemoveTreeItemsIfFound(c.Sender, c.EventArgs.ProfileElement)).DisposeWith(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void RemoveTreeItemsIfFound(object? sender, ProfileElement profileElement)
|
|
||||||
{
|
{
|
||||||
List<TreeItemViewModel> toRemove = Children.Where(t => t.ProfileElement == profileElement).ToList();
|
_profileEditorService.ProfileElement.Subscribe(element => _currentProfileElement = element).DisposeWith(d);
|
||||||
foreach (TreeItemViewModel treeItemViewModel in toRemove)
|
SubscribeToProfileElement(d);
|
||||||
Children.Remove(treeItemViewModel);
|
CreateTreeItems();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (_currentProfileElement != profileElement)
|
public ProfileElement? ProfileElement
|
||||||
return;
|
{
|
||||||
|
get => _profileElement;
|
||||||
|
set => RaiseAndSetIfChanged(ref _profileElement, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Find a good candidate for a new selection, preferring the next sibling, falling back to the previous sibling and finally the parent
|
public bool IsExpanded
|
||||||
ProfileElement? parent = sender as ProfileElement;
|
{
|
||||||
ProfileElement? newSelection = null;
|
get => _isExpanded;
|
||||||
if (parent != null)
|
set => RaiseAndSetIfChanged(ref _isExpanded, value);
|
||||||
{
|
}
|
||||||
newSelection = parent.Children.FirstOrDefault(c => c.Order == profileElement.Order) ?? parent.Children.FirstOrDefault(c => c.Order == profileElement.Order - 1);
|
|
||||||
if (newSelection == null && parent is Folder {IsRootFolder: false})
|
|
||||||
newSelection = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
_profileEditorService.ChangeCurrentProfileElement(newSelection as RenderProfileElement);
|
public bool Renaming
|
||||||
}
|
{
|
||||||
|
get => _renaming;
|
||||||
|
set => RaiseAndSetIfChanged(ref _renaming, value);
|
||||||
|
}
|
||||||
|
|
||||||
protected void AddTreeItemIfMissing(ProfileElement profileElement)
|
public TreeItemViewModel? Parent { get; set; }
|
||||||
|
public ObservableCollection<TreeItemViewModel> Children { get; } = new();
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> AddLayer { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> AddFolder { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Rename { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Duplicate { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Copy { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Paste { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> Delete { get; }
|
||||||
|
public abstract bool SupportsChildren { get; }
|
||||||
|
|
||||||
|
public string? RenameValue
|
||||||
|
{
|
||||||
|
get => _renameValue;
|
||||||
|
set => RaiseAndSetIfChanged(ref _renameValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ShowBrokenStateExceptions()
|
||||||
|
{
|
||||||
|
if (ProfileElement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<IBreakableModel> broken = ProfileElement.GetBrokenHierarchy().Where(b => b.BrokenStateException != null).ToList();
|
||||||
|
|
||||||
|
foreach (IBreakableModel current in broken)
|
||||||
{
|
{
|
||||||
if (Children.Any(t => t.ProfileElement == profileElement))
|
_windowService.ShowExceptionDialog($"{current.BrokenDisplayName} - {current.BrokenState}", current.BrokenStateException!);
|
||||||
return;
|
if (broken.Last() != current)
|
||||||
|
if (!await _windowService.ShowConfirmContentDialog("Broken state", "Do you want to view the next exception?"))
|
||||||
if (profileElement is Folder folder)
|
return;
|
||||||
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));
|
|
||||||
|
|
||||||
// Select the newly added element
|
|
||||||
if (profileElement is RenderProfileElement renderProfileElement)
|
|
||||||
_profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SubmitRename()
|
||||||
|
{
|
||||||
|
if (ProfileElement == null)
|
||||||
|
{
|
||||||
|
Renaming = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileEditorService.ExecuteCommand(new RenameProfileElement(ProfileElement, RenameValue));
|
||||||
|
Renaming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelRename()
|
||||||
|
{
|
||||||
|
Renaming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertElement(TreeItemViewModel elementViewModel, int targetIndex)
|
||||||
|
{
|
||||||
|
if (elementViewModel.Parent == this && Children.IndexOf(elementViewModel) == targetIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ProfileElement != null && elementViewModel.ProfileElement != null)
|
||||||
|
_profileEditorService.ExecuteCommand(new MoveProfileElement(ProfileElement, elementViewModel.ProfileElement, targetIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Sender, c.EventArgs.ProfileElement)).DisposeWith(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void RemoveTreeItemsIfFound(object? sender, ProfileElement profileElement)
|
||||||
|
{
|
||||||
|
List<TreeItemViewModel> toRemove = Children.Where(t => t.ProfileElement == profileElement).ToList();
|
||||||
|
foreach (TreeItemViewModel treeItemViewModel in toRemove)
|
||||||
|
Children.Remove(treeItemViewModel);
|
||||||
|
|
||||||
|
if (_currentProfileElement != profileElement)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find a good candidate for a new selection, preferring the next sibling, falling back to the previous sibling and finally the parent
|
||||||
|
ProfileElement? parent = sender as ProfileElement;
|
||||||
|
ProfileElement? newSelection = null;
|
||||||
|
if (parent != null)
|
||||||
|
{
|
||||||
|
newSelection = parent.Children.FirstOrDefault(c => c.Order == profileElement.Order) ?? parent.Children.FirstOrDefault(c => c.Order == profileElement.Order - 1);
|
||||||
|
if (newSelection == null && parent is Folder {IsRootFolder: false})
|
||||||
|
newSelection = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileEditorService.ChangeCurrentProfileElement(newSelection as RenderProfileElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
// Select the newly added element
|
||||||
|
if (profileElement is RenderProfileElement renderProfileElement)
|
||||||
|
_profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user