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:
parent
82b41425aa
commit
5a5a6819b1
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -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=""
|
<Button Content=""
|
||||||
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=""
|
<Button Content=""
|
||||||
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>
|
||||||
|
|||||||
@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user