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

Visual scripting fixes

This commit is contained in:
Robert 2023-04-02 20:21:01 +02:00
parent 82b41425aa
commit 5a5a6819b1
11 changed files with 167 additions and 80 deletions

View File

@ -8,6 +8,7 @@
xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:collections="clr-namespace:System.Collections;assembly=System.Runtime" xmlns:collections="clr-namespace:System.Collections;assembly=System.Runtime"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.CableView" x:Class="Artemis.UI.Screens.VisualScripting.CableView"
x:DataType="visualScripting:CableViewModel" x:DataType="visualScripting:CableViewModel"
@ -18,23 +19,12 @@
<shared:SKColorToStringConverter x:Key="SKColorToStringConverter" /> <shared:SKColorToStringConverter x:Key="SKColorToStringConverter" />
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" /> <shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources> </UserControl.Resources>
<Canvas PointerEntered="OnPointerEnter" <Canvas PointerEntered="OnPointerEntered"
PointerExited="OnPointerLeave"> PointerExited="OnPointerExited">
<Path Name="CablePath" <Path Name="CablePath"
Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}" Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
StrokeThickness="4" StrokeThickness="4"
StrokeLineCap="Round"> StrokeLineCap="Round">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="False">
<PathFigure.Segments>
<BezierSegment />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path> </Path>
<Border Name="ValueBorder" <Border Name="ValueBorder"
Background="{DynamicResource ContentDialogBackground}" Background="{DynamicResource ContentDialogBackground}"
@ -79,7 +69,7 @@
<DataTemplate DataType="collections:IList"> <DataTemplate DataType="collections:IList">
<TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)', Mode=OneWay}" FontFamily="Consolas" /> <TextBlock Text="{Binding Count, StringFormat='List - {0} item(s)', Mode=OneWay}" FontFamily="Consolas" />
</DataTemplate> </DataTemplate>
<DataTemplate> <DataTemplate DataType="system:Object">
<TextBlock Text="{Binding Mode=OneWay}" FontFamily="Consolas" /> <TextBlock Text="{Binding Mode=OneWay}" FontFamily="Consolas" />
</DataTemplate> </DataTemplate>
</ContentControl.DataTemplates> </ContentControl.DataTemplates>

View File

@ -24,39 +24,48 @@ public partial class CableView : ReactiveUserControl<CableViewModel>
{ {
ValueBorder.GetObservable(BoundsProperty).Subscribe(rect => ValueBorder.RenderTransform = new TranslateTransform(rect.Width / 2 * -1, rect.Height / 2 * -1)).DisposeWith(d); ValueBorder.GetObservable(BoundsProperty).Subscribe(rect => ValueBorder.RenderTransform = new TranslateTransform(rect.Width / 2 * -1, rect.Height / 2 * -1)).DisposeWith(d);
ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update(true)).DisposeWith(d); ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update()).DisposeWith(d);
ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update(false)).DisposeWith(d); ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update()).DisposeWith(d);
Update(true); Update();
}); });
} }
private void Update()
private void Update(bool from)
{ {
// Workaround for https://github.com/AvaloniaUI/Avalonia/issues/4748 if (ViewModel == null)
CablePath.Margin = new Thickness(CablePath.Margin.Left + 1, CablePath.Margin.Top + 1, 0, 0); return;
if (CablePath.Margin.Left > 2)
CablePath.Margin = new Thickness(0, 0, 0, 0);
PathFigure pathFigure = ((PathGeometry) CablePath.Data).Figures.First(); PathGeometry geometry = new()
BezierSegment segment = (BezierSegment) pathFigure.Segments!.First(); {
pathFigure.StartPoint = ViewModel!.FromPoint; Figures = new PathFigures()
segment.Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y); };
segment.Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y); PathFigure pathFigure = new()
segment.Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y); {
StartPoint = ViewModel.FromPoint,
IsClosed = false,
Segments = new PathSegments
{
new BezierSegment
{
Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y),
Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y),
Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y)
}
}
};
geometry.Figures.Add(pathFigure);
CablePath.Data = geometry;
Canvas.SetLeft(ValueBorder, ViewModel.FromPoint.X + (ViewModel.ToPoint.X - ViewModel.FromPoint.X) / 2); Canvas.SetLeft(ValueBorder, ViewModel.FromPoint.X + (ViewModel.ToPoint.X - ViewModel.FromPoint.X) / 2);
Canvas.SetTop(ValueBorder, ViewModel.FromPoint.Y + (ViewModel.ToPoint.Y - ViewModel.FromPoint.Y) / 2); Canvas.SetTop(ValueBorder, ViewModel.FromPoint.Y + (ViewModel.ToPoint.Y - ViewModel.FromPoint.Y) / 2);
CablePath.InvalidateVisual();
} }
private void OnPointerEnter(object? sender, PointerEventArgs e) private void OnPointerEntered(object? sender, PointerEventArgs e)
{ {
ViewModel?.UpdateDisplayValue(true); ViewModel?.UpdateDisplayValue(true);
} }
private void OnPointerLeave(object? sender, PointerEventArgs e) private void OnPointerExited(object? sender, PointerEventArgs e)
{ {
ViewModel?.UpdateDisplayValue(false); ViewModel?.UpdateDisplayValue(false);
} }

View File

@ -29,14 +29,28 @@ public partial class DragCableView : ReactiveUserControl<DragCableViewModel>
private void Update() private void Update()
{ {
PathFigure? pathFigure = ((PathGeometry) CablePath.Data).Figures?.FirstOrDefault(); if (ViewModel == null)
if (pathFigure?.Segments == null)
return; return;
BezierSegment segment = (BezierSegment) pathFigure.Segments.First(); PathGeometry geometry = new()
pathFigure.StartPoint = ViewModel!.FromPoint; {
segment.Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y); Figures = new PathFigures()
segment.Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y); };
segment.Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y); PathFigure pathFigure = new()
{
StartPoint = ViewModel.FromPoint,
IsClosed = false,
Segments = new PathSegments
{
new BezierSegment
{
Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y),
Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y),
Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y)
}
}
};
geometry.Figures.Add(pathFigure);
CablePath.Data = geometry;
} }
} }

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeCategoryViewModel
{
public NodeCategoryViewModel(DynamicData.List.IGrouping<NodeData,string> category)
{
Category = category.Key;
Nodes = category.Items.ToList();
}
public string Category { get; set; }
public List<NodeData> Nodes { get; set; }
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using Artemis.Core;
using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting;
public class NodeMenuItemViewModel
{
public NodeMenuItemViewModel(ReactiveCommand<NodeData, Unit> createNode, DynamicData.List.IGrouping<NodeData, string> category)
{
Header = category.Key;
Items = category.Items.Select(d => new NodeMenuItemViewModel(createNode, d)).ToList();
}
public NodeMenuItemViewModel(ReactiveCommand<NodeData, Unit> createNode, NodeData nodeData)
{
Header = nodeData.Name;
Items = new List<NodeMenuItemViewModel>();
CreateNode = ReactiveCommand.Create(() => { createNode.Execute(nodeData).Subscribe(); });
}
public string Header { get; }
public List<NodeMenuItemViewModel> Items { get; }
public ReactiveCommand<Unit, Unit>? CreateNode { get; }
}

View File

@ -3,7 +3,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting" xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="650" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="650" d:DesignHeight="450"
@ -17,15 +16,16 @@
<Setter Property="InnerRightContent"> <Setter Property="InnerRightContent">
<Template> <Template>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="&#xE8BB;" <Button Content="&#xE8BB;"
FontFamily="{StaticResource SymbolThemeFontFamily}" FontFamily="{StaticResource SymbolThemeFontFamily}"
Classes="AppBarButton" Theme="{StaticResource TransparentButton}"
Command="{Binding $parent[TextBox].Clear}" Command="{Binding $parent[TextBox].Clear}"
IsVisible="{Binding $parent[TextBox].Text, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" /> IsVisible="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=TextBox}, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="&#xE721;" <Button Content="&#xE721;"
FontFamily="{StaticResource SymbolThemeFontFamily}" FontFamily="{StaticResource SymbolThemeFontFamily}"
Classes="AppBarButton" Theme="{StaticResource TransparentButton}"
IsHitTestVisible="False" /> Command="{Binding $parent[TextBox].Clear}"
IsHitTestVisible="False"/>
</StackPanel> </StackPanel>
</Template> </Template>
</Setter> </Setter>
@ -46,16 +46,21 @@
</Style> </Style>
</TreeView.Styles> </TreeView.Styles>
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="{x:Type core:NodeData}"> <TreeDataTemplate DataType="core:NodeData">
<StackPanel Margin="-15 1 0 1" Background="Transparent" PointerReleased="InputElement_OnPointerReleased"> <StackPanel Margin="-15 1 0 1" Background="Transparent" PointerReleased="InputElement_OnPointerReleased">
<TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}" TextWrapping="Wrap"></TextBlock> <TextBlock Classes="BodyStrongTextBlockStyle" Text="{Binding Name}" TextWrapping="Wrap"></TextBlock>
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}" TextWrapping="Wrap"></TextBlock> <TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
</StackPanel> </StackPanel>
</TreeDataTemplate> </TreeDataTemplate>
<TreeDataTemplate ItemsSource="{Binding Items}"> <TreeDataTemplate DataType="visualScripting:NodeCategoryViewModel" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Key}"></TextBlock> <TextBlock Text="{Binding Category}"></TextBlock>
</TreeDataTemplate> </TreeDataTemplate>
</TreeView.DataTemplates> </TreeView.DataTemplates>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView> </TreeView>
<StackPanel Grid.Row="1" VerticalAlignment="Center" Spacing="20" IsVisible="{CompiledBinding !Categories.Count}"> <StackPanel Grid.Row="1" VerticalAlignment="Center" Spacing="20" IsVisible="{CompiledBinding !Categories.Count}">
<avalonia:MaterialIcon Kind="CloseCircle" Width="64" Height="64"></avalonia:MaterialIcon> <avalonia:MaterialIcon Kind="CloseCircle" Width="64" Height="64"></avalonia:MaterialIcon>

View File

@ -42,7 +42,8 @@ public class NodePickerViewModel : ActivatableViewModelBase
.ThenByAscending(d => d.Category) .ThenByAscending(d => d.Category)
.ThenByAscending(d => d.Name)) .ThenByAscending(d => d.Name))
.GroupWithImmutableState(n => n.Category) .GroupWithImmutableState(n => n.Category)
.Bind(out ReadOnlyObservableCollection<DynamicData.List.IGrouping<NodeData, string>> categories) .Transform(c => new NodeCategoryViewModel(c))
.Bind(out ReadOnlyObservableCollection<NodeCategoryViewModel> categories)
.Subscribe(); .Subscribe();
Categories = categories; Categories = categories;
@ -62,7 +63,7 @@ public class NodePickerViewModel : ActivatableViewModelBase
}); });
} }
public ReadOnlyObservableCollection<DynamicData.List.IGrouping<NodeData, string>> Categories { get; } public ReadOnlyObservableCollection<NodeCategoryViewModel> Categories { get; }
public bool IsVisible public bool IsVisible
{ {

View File

@ -68,8 +68,9 @@ public partial class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
{ {
if (ViewModel == null) if (ViewModel == null)
return; return;
ViewModel.NodePickerViewModel.Position = point;
NodeScriptZoomBorder?.ContextFlyout?.ShowAt(NodeScriptZoomBorder); NodeScriptZoomBorder?.ContextFlyout?.ShowAt(NodeScriptZoomBorder);
ViewModel.NodePickerViewModel.Position = point;
} }
private void AutoFitIfPreview() private void AutoFitIfPreview()

View File

@ -7,6 +7,7 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia" xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.VisualScripting.NodeScriptWindowView" x:Class="Artemis.UI.Screens.VisualScripting.NodeScriptWindowView"
x:DataType="visualScripting:NodeScriptWindowViewModel" x:DataType="visualScripting:NodeScriptWindowViewModel"
@ -28,22 +29,11 @@
</MenuItem.Icon> </MenuItem.Icon>
<MenuItem.Styles> <MenuItem.Styles>
<Style Selector="MenuItem > MenuItem > MenuItem"> <Style Selector="MenuItem > MenuItem > MenuItem">
<Setter Property="Command" Value="{Binding $parent[visualScripting:NodeScriptWindowView].DataContext.CreateNode}" /> <Setter Property="Command" Value="{Binding CreateNode}" />
<Setter Property="CommandParameter" Value="{Binding}" /> <Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Items" Value="{Binding Items}" /> <Setter Property="Items" Value="{Binding Items}" />
</Style> </Style>
</MenuItem.Styles> </MenuItem.Styles>
<MenuItem.DataTemplates>
<DataTemplate DataType="{x:Type core:NodeData}">
<StackPanel Background="Transparent">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap"></TextBlock>
<TextBlock Foreground="{DynamicResource TextFillColorSecondary}" Text="{Binding Description}" TextWrapping="Wrap"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"></TextBlock>
</DataTemplate>
</MenuItem.DataTemplates>
</MenuItem> </MenuItem>
<MenuItem Header="Auto-arrange" Command="{CompiledBinding AutoArrange}" InputGesture="Ctrl+F"> <MenuItem Header="Auto-arrange" Command="{CompiledBinding AutoArrange}" InputGesture="Ctrl+F">
<MenuItem.Icon> <MenuItem.Icon>

View File

@ -48,19 +48,20 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase
_profileService = profileService; _profileService = profileService;
_windowService = windowService; _windowService = windowService;
SourceList<NodeData> nodeSourceList = new();
nodeSourceList.AddRange(nodeService.AvailableNodes);
nodeSourceList.Connect()
.GroupWithImmutableState(n => n.Category)
.Bind(out ReadOnlyObservableCollection<IGrouping<NodeData, string>> categories)
.Subscribe();
Categories = categories;
CreateNode = ReactiveCommand.Create<NodeData>(ExecuteCreateNode); CreateNode = ReactiveCommand.Create<NodeData>(ExecuteCreateNode);
AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange); AutoArrange = ReactiveCommand.CreateFromTask(ExecuteAutoArrange);
Export = ReactiveCommand.CreateFromTask(ExecuteExport); Export = ReactiveCommand.CreateFromTask(ExecuteExport);
Import = ReactiveCommand.CreateFromTask(ExecuteImport); Import = ReactiveCommand.CreateFromTask(ExecuteImport);
SourceList<NodeData> nodeSourceList = new();
nodeSourceList.AddRange(nodeService.AvailableNodes);
nodeSourceList.Connect()
.GroupWithImmutableState(n => n.Category)
.Transform(c => new NodeMenuItemViewModel(CreateNode, c))
.Bind(out ReadOnlyObservableCollection<NodeMenuItemViewModel> categories)
.Subscribe();
Categories = categories;
this.WhenActivated(d => this.WhenActivated(d =>
{ {
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Normal, Update); DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Normal, Update);
@ -83,7 +84,7 @@ public class NodeScriptWindowViewModel : NodeScriptWindowViewModelBase
public NodeEditorHistory History { get; } public NodeEditorHistory History { get; }
public ReactiveCommand<PluginSetting<bool>, Unit> ToggleBooleanSetting { get; set; } public ReactiveCommand<PluginSetting<bool>, Unit> ToggleBooleanSetting { get; set; }
public ReactiveCommand<string, Unit> OpenUri { get; set; } public ReactiveCommand<string, Unit> OpenUri { get; set; }
public ReadOnlyObservableCollection<IGrouping<NodeData, string>> Categories { get; } public ReadOnlyObservableCollection<NodeMenuItemViewModel> Categories { get; }
public ReactiveCommand<NodeData, Unit> CreateNode { get; } public ReactiveCommand<NodeData, Unit> CreateNode { get; }
public ReactiveCommand<Unit, Unit> AutoArrange { get; } public ReactiveCommand<Unit, Unit> AutoArrange { get; }
public ReactiveCommand<Unit, Unit> Export { get; } public ReactiveCommand<Unit, Unit> Export { get; }

View File

@ -1,8 +1,9 @@
using Avalonia; using System;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Rendering;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Artemis.UI.Screens.VisualScripting.Pins; namespace Artemis.UI.Screens.VisualScripting.Pins;
@ -12,12 +13,20 @@ public class PinView : ReactiveUserControl<PinViewModel>
private Canvas? _container; private Canvas? _container;
private bool _dragging; private bool _dragging;
private Border? _pinPoint; private Border? _pinPoint;
private PinViewRenderLoopTaks _renderLoopTask;
protected void InitializePin(Border pinPoint) protected void InitializePin(Border pinPoint)
{ {
_pinPoint = pinPoint; _pinPoint = pinPoint;
_pinPoint.PointerMoved += PinPointOnPointerMoved; _pinPoint.PointerMoved += PinPointOnPointerMoved;
_pinPoint.PointerReleased += PinPointOnPointerReleased; _pinPoint.PointerReleased += PinPointOnPointerReleased;
_pinPoint.PropertyChanged += PinPointOnPropertyChanged;
_renderLoopTask = new PinViewRenderLoopTaks(this);
}
private void PinPointOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
Console.WriteLine(e);
} }
private void PinPointOnPointerMoved(object? sender, PointerEventArgs e) private void PinPointOnPointerMoved(object? sender, PointerEventArgs e)
@ -67,16 +76,17 @@ public class PinView : ReactiveUserControl<PinViewModel>
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
_container = this.FindAncestorOfType<Canvas>(); _container = this.FindAncestorOfType<Canvas>();
AvaloniaLocator.Current.GetRequiredService<IRenderLoop>().Add(_renderLoopTask);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Render(DrawingContext context) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.Render(context); base.OnDetachedFromVisualTree(e);
UpdatePosition(); AvaloniaLocator.Current.GetRequiredService<IRenderLoop>().Remove(_renderLoopTask);
} }
private void UpdatePosition() public void UpdatePosition()
{ {
if (_container == null || _pinPoint == null || ViewModel == null) if (_container == null || _pinPoint == null || ViewModel == null)
return; return;
@ -87,4 +97,25 @@ public class PinView : ReactiveUserControl<PinViewModel>
} }
#endregion #endregion
}
public class PinViewRenderLoopTaks : IRenderLoopTask
{
private readonly PinView _pinView;
public PinViewRenderLoopTaks(PinView pinView)
{
_pinView = pinView;
}
public void Update(TimeSpan time)
{
_pinView.UpdatePosition();
}
public void Render()
{
}
public bool NeedsUpdate => true;
} }