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

Nodes - Added enum equality node

Conditions - Implemented event trigger conditions
This commit is contained in:
Robert 2021-09-16 21:11:23 +02:00
parent 00125d1478
commit d6372dffad
16 changed files with 321 additions and 93 deletions

View File

@ -10,18 +10,21 @@ namespace Artemis.Core
{ {
private readonly string _displayName; private readonly string _displayName;
private readonly EventConditionEntity _entity; private readonly EventConditionEntity _entity;
private EventDefaultNode _eventNode; private EventDefaultNode? _eventNode;
private TimeLineEventOverlapMode _eventOverlapMode; private TimeLineEventOverlapMode _eventOverlapMode;
private DataModelPath? _eventPath; private DataModelPath? _eventPath;
private DateTime _lastProcessedTrigger; private DateTime _lastProcessedTrigger;
private NodeScript<bool>? _script;
/// <summary>
/// Creates a new instance of the <see cref="EventCondition" /> class
/// </summary>
public EventCondition(ProfileElement profileElement) public EventCondition(ProfileElement profileElement)
{ {
_entity = new EventConditionEntity(); _entity = new EventConditionEntity();
_displayName = profileElement.GetType().Name; _displayName = profileElement.GetType().Name;
ProfileElement = profileElement; ProfileElement = profileElement;
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", profileElement.Profile);
} }
internal EventCondition(EventConditionEntity entity, ProfileElement profileElement) internal EventCondition(EventConditionEntity entity, ProfileElement profileElement)
@ -36,18 +39,21 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the script that drives the event condition /// Gets the script that drives the event condition
/// </summary> /// </summary>
public NodeScript<bool> Script { get; private set; } public NodeScript<bool>? Script
{
get => _script;
set => SetAndNotify(ref _script, value);
}
/// <summary> /// <summary>
/// Gets or sets the path to the event that drives this event condition /// Gets or sets the path to the event that drives this event condition
/// </summary> /// </summary>
public DataModelPath? EventPath public DataModelPath? EventPath
{ {
set => SetAndNotify(ref _eventPath, value);
get => _eventPath; get => _eventPath;
set => SetAndNotify(ref _eventPath, value);
} }
/// <summary> /// <summary>
/// Gets or sets how the condition behaves when events trigger before the timeline finishes /// Gets or sets how the condition behaves when events trigger before the timeline finishes
/// </summary> /// </summary>
@ -62,7 +68,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void UpdateEventNode() public void UpdateEventNode()
{ {
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent) if (Script == null || EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
return; return;
if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing) if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing)
@ -72,7 +78,7 @@ namespace Artemis.Core
} }
else else
{ {
_eventNode = new EventDefaultNode(); _eventNode = new EventDefaultNode() {X = -300};
_eventNode.UpdateDataModelEvent(dataModelEvent); _eventNode.UpdateDataModelEvent(dataModelEvent);
} }
@ -82,13 +88,24 @@ namespace Artemis.Core
Script.RemoveNode(_eventNode); Script.RemoveNode(_eventNode);
} }
/// <summary>
/// Updates the <see cref="Script" /> with a new empty node script
/// </summary>
public void CreateEmptyNodeScript()
{
Script?.Dispose();
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
UpdateEventNode();
}
private bool Evaluate() private bool Evaluate()
{ {
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger) if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
return false; return false;
_lastProcessedTrigger = dataModelEvent.LastTrigger; _lastProcessedTrigger = dataModelEvent.LastTrigger;
if (!Script.ExitNodeConnected)
if (Script == null)
return true; return true;
Script.Run(); Script.Run();
@ -149,7 +166,7 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Script.Dispose(); Script?.Dispose();
EventPath?.Dispose(); EventPath?.Dispose();
} }
@ -159,7 +176,9 @@ namespace Artemis.Core
public void Load() public void Load()
{ {
EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode; EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
if (_entity.Script != null)
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile); Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile);
if (_entity.EventPath != null)
EventPath = new DataModelPath(_entity.EventPath); EventPath = new DataModelPath(_entity.EventPath);
} }
@ -167,8 +186,8 @@ namespace Artemis.Core
public void Save() public void Save()
{ {
_entity.EventOverlapMode = (int) EventOverlapMode; _entity.EventOverlapMode = (int) EventOverlapMode;
Script.Save(); Script?.Save();
_entity.Script = Script.Entity; _entity.Script = Script?.Entity;
EventPath?.Save(); EventPath?.Save();
_entity.EventPath = EventPath?.Entity; _entity.EventPath = EventPath?.Entity;
} }
@ -176,6 +195,9 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public void LoadNodeScript() public void LoadNodeScript()
{ {
if (Script == null)
return;
Script.Load(); Script.Load();
UpdateEventNode(); UpdateEventNode();
Script.LoadConnections(); Script.LoadConnections();

View File

@ -189,6 +189,18 @@ namespace Artemis.Core
if (sourcePin.Direction == targetPin.Direction) if (sourcePin.Direction == targetPin.Direction)
continue; continue;
// Clear existing connections on input pins, we don't want none of that now
if (targetPin.Direction == PinDirection.Input)
{
while (targetPin.ConnectedTo.Any())
targetPin.DisconnectFrom(targetPin.ConnectedTo[0]);
}
if (sourcePin.Direction == PinDirection.Input)
{
while (sourcePin.ConnectedTo.Any())
sourcePin.DisconnectFrom(sourcePin.ConnectedTo[0]);
}
// Only connect the nodes if they aren't already connected (LoadConnections may be called twice or more) // Only connect the nodes if they aren't already connected (LoadConnections may be called twice or more)
if (!targetPin.ConnectedTo.Contains(sourcePin)) if (!targetPin.ConnectedTo.Contains(sourcePin))
targetPin.ConnectTo(sourcePin); targetPin.ConnectTo(sourcePin);

View File

@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
".NETCoreApp,Version=v5.0": { "net5.0-windows7.0": {
"Humanizer.Core": { "Humanizer.Core": {
"type": "Direct", "type": "Direct",
"requested": "[2.11.10, )", "requested": "[2.11.10, )",

View File

@ -52,7 +52,7 @@
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12"> <TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Close" VerticalAlignment="Center" Margin="0 0 2 -3" /> <materialDesign:PackIcon Kind="No" VerticalAlignment="Center" Margin="0 0 2 -3" />
NONE NONE
</TextBlock> </TextBlock>
</RadioButton> </RadioButton>

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event.EventConditionView" <UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -9,11 +9,13 @@
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting" xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:controls1="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:Shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event.EventConditionView"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:EventConditionViewModel}}"> d:DataContext="{d:DesignInstance {x:Type local:EventConditionViewModel}}">
<UserControl.Resources> <UserControl.Resources>
<Shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<converters:ComparisonConverter x:Key="ComparisonConverter" /> <converters:ComparisonConverter x:Key="ComparisonConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
@ -24,12 +26,17 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Stretch"> <Grid Grid.Row="0" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<controls1:DataModelPicker Grid.Row="0" <shared:DataModelPicker Grid.Row="0"
Grid.Column="0"
Margin="0 0 0 5" Margin="0 0 0 5"
ButtonBrush="DarkGoldenrod" ButtonBrush="DarkGoldenrod"
DataModelPath="{Binding EventCondition.EventPath}" DataModelPath="{Binding EventCondition.EventPath}"
@ -38,7 +45,20 @@
ShowFullPath="True" ShowFullPath="True"
Modules="{Binding Modules}" /> Modules="{Binding Modules}" />
<Grid Grid.Row="1" MouseUp="{s:Action ScriptGridMouseUp}" Cursor="Hand"> <CheckBox Grid.Row="0"
Grid.Column="1"
IsChecked="{Binding TriggerConditionally}"
Content="Conditional trigger"
VerticalAlignment="Center"
ToolTip="When enabled, the layer will only trigger if the script evaluates to true"
Style="{StaticResource MaterialDesignDarkCheckBox}" />
<Grid Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MouseUp="{s:Action ScriptGridMouseUp}"
Cursor="Hand"
Visibility="{Binding EventCondition.Script, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Normal, Mode=OneWay}">
<controls:VisualScriptPresenter Script="{Binding EventCondition.Script}" AutoFitScript="True" /> <controls:VisualScriptPresenter Script="{Binding EventCondition.Script}" AutoFitScript="True" />
<Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border.Background> <Border.Background>
@ -66,19 +86,29 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Border.Style> </Border.Style>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" TextAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center">
Click to edit script Click to edit script
<materialDesign:PackIcon Kind="OpenInNew" Margin="0 0 0 -6 " Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock> </TextBlock>
<materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" VerticalAlignment="Center" />
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
Scripts with nothing connected to their end-node are always true.
</TextBlock>
</StackPanel>
</Border> </Border>
</Grid> </Grid>
<Grid Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Visibility="{Binding EventCondition.Script, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, Mode=OneWay}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" TextWrapping="Wrap" TextAlignment="Center" Margin="0 10">
Conditional trigger disabled
<materialDesign:PackIcon Kind="No" Margin="0 0 0 -6" Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
When enabled, the layer will only trigger if the script evaluates to true
</TextBlock>
</StackPanel>
</Grid>
</Grid> </Grid>
<!-- Trigger mode --> <!-- Trigger mode -->
@ -86,7 +116,7 @@
<TextBlock.ToolTip> <TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30"> <ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock> <TextBlock>
Configure how the layer should act when the event(s) trigger <Run Text="Configure how the layer should act when the event(s) trigger" />
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</TextBlock.ToolTip> </TextBlock.ToolTip>
@ -102,63 +132,71 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" <RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}"> IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}, Converter={StaticResource ComparisonConverter}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
RESTART
</TextBlock>
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40"> <ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock> <TextBlock>
Stop the current run and restart the timeline <Run Text="Stop the current run and restart the timeline" />
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" RESTART" />
</TextBlock>
</RadioButton> </RadioButton>
<RadioButton Grid.Column="1" <RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}}"> IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}, Converter={StaticResource ComparisonConverter}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
TOGGLE
</TextBlock>
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40"> <ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock> <TextBlock>
Repeat the timeline until the event fires again <Run Text="Repeat the timeline until the event fires again" />
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" TOGGLE" />
</TextBlock>
</RadioButton> </RadioButton>
<RadioButton Grid.Column="2" <RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}"> IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}, Converter={StaticResource ComparisonConverter}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
IGNORE
</TextBlock>
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40"> <ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock> <TextBlock>
Ignore subsequent event fires until the timeline finishes <Run Text="Ignore subsequent event fires until the timeline finishes" />
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" IGNORE" />
</TextBlock>
</RadioButton> </RadioButton>
<RadioButton Grid.Column="3" <RadioButton Grid.Column="3"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}"> IsChecked="{Binding EventOverlapMode, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}, Converter={StaticResource ComparisonConverter}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
COPY
</TextBlock>
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40"> <ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock> <TextBlock>
Play another copy of the timeline on top of the current run <Run Text="Play another copy of the timeline on top of the current run" />
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12">
<InlineUIContainer>
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
</InlineUIContainer>
<Run Text=" COPY" />
</TextBlock>
</RadioButton> </RadioButton>
</Grid> </Grid>
</materialDesign:ColorZone> </materialDesign:ColorZone>

View File

@ -12,9 +12,10 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
{ {
public class EventConditionViewModel : Screen public class EventConditionViewModel : Screen
{ {
private readonly INodeVmFactory _nodeVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private readonly INodeVmFactory _nodeVmFactory; private NodeScript<bool> _oldScript;
public EventConditionViewModel(EventCondition eventCondition, IProfileEditorService profileEditorService, IWindowManager windowManager, INodeVmFactory nodeVmFactory) public EventConditionViewModel(EventCondition eventCondition, IProfileEditorService profileEditorService, IWindowManager windowManager, INodeVmFactory nodeVmFactory)
{ {
@ -22,6 +23,10 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
_windowManager = windowManager; _windowManager = windowManager;
_nodeVmFactory = nodeVmFactory; _nodeVmFactory = nodeVmFactory;
EventCondition = eventCondition; EventCondition = eventCondition;
FilterTypes = new BindableCollection<Type> {typeof(IDataModelEvent)};
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
Modules = new BindableCollection<Module> {_profileEditorService.SelectedProfileConfiguration.Module};
} }
public EventCondition EventCondition { get; } public EventCondition EventCondition { get; }
@ -39,6 +44,42 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
} }
} }
public bool TriggerConditionally
{
get => EventCondition.Script != null;
set
{
if (EventCondition.Script != null)
{
_oldScript = EventCondition.Script;
EventCondition.Script = null;
}
else
{
if (_oldScript != null)
{
EventCondition.Script = _oldScript;
_oldScript = null;
}
else
{
EventCondition.CreateEmptyNodeScript();
}
}
}
}
#region Overrides of Screen
/// <inheritdoc />
protected override void OnClose()
{
_oldScript?.Dispose();
base.OnClose();
}
#endregion
public void DataModelPathSelected(object sender, DataModelSelectedEventArgs e) public void DataModelPathSelected(object sender, DataModelSelectedEventArgs e)
{ {
EventCondition.UpdateEventNode(); EventCondition.UpdateEventNode();

View File

@ -49,17 +49,10 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Border.Style> </Border.Style>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" TextAlignment="Center">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center">
Click to edit script Click to edit script
<materialDesign:PackIcon Kind="OpenInNew" Margin="0 0 0 -6 " Width="30" Height="30" VerticalAlignment="Center" />
</TextBlock> </TextBlock>
<materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" VerticalAlignment="Center" />
</StackPanel>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
Scripts with nothing connected to their end-node are always true.
</TextBlock>
</StackPanel>
</Border> </Border>
</Grid> </Grid>
<Grid Grid.Row="1"> <Grid Grid.Row="1">

View File

@ -124,10 +124,10 @@ namespace Artemis.UI.Services
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(bool), new SKColor(0xFFCD3232)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(bool), new SKColor(0xFFCD3232));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(string), new SKColor(0xFFFFD700)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(string), new SKColor(0xFFFFD700));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(int), new SKColor(0xFF32CD32)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(int), new SKColor(0xFF32CD32));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(double), new SKColor(0xFF1E90FF));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(float), new SKColor(0xFFFF7C00)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(float), new SKColor(0xFFFF7C00));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(SKColor), new SKColor(0xFFAD3EED)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(SKColor), new SKColor(0xFFAD3EED));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(IList), new SKColor(0xFFED3E61)); _nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(IList), new SKColor(0xFFED3E61));
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(Enum), new SKColor(0xFF1E90FF));
foreach (Type nodeType in typeof(SumIntegersNode).Assembly.GetTypes().Where(t => typeof(INode).IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract && !t.IsInterface)) foreach (Type nodeType in typeof(SumIntegersNode).Assembly.GetTypes().Where(t => typeof(INode).IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract && !t.IsInterface))
_nodeService.RegisterNodeType(Constants.CorePlugin, nodeType); _nodeService.RegisterNodeType(Constants.CorePlugin, nodeType);

View File

@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
".NETCoreApp,Version=v5.0": { "net5.0-windows10.0.17763": {
"FluentValidation": { "FluentValidation": {
"type": "Direct", "type": "Direct",
"requested": "[10.3.0, )", "requested": "[10.3.0, )",

View File

@ -114,8 +114,8 @@ namespace Artemis.VisualScripting.Editor.Controls
return nameContains; return nameContains;
if (SourcePin.Pin.Direction == PinDirection.Input) if (SourcePin.Pin.Direction == PinDirection.Input)
return nameContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType == SourcePin.Pin.Type); return nodeData.OutputType != null && nameContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType.IsAssignableTo(SourcePin.Pin.Type));
return nameContains && (nodeData.InputType == typeof(object) || nodeData.InputType == SourcePin.Pin.Type); return nodeData.InputType != null && nameContains && (nodeData.InputType == typeof(object) || nodeData.InputType.IsAssignableFrom(SourcePin.Pin.Type));
} }
private void ItemsSourceChanged() private void ItemsSourceChanged()

View File

@ -190,6 +190,7 @@ namespace Artemis.VisualScripting.Editor.Controls
} }
private bool IsTypeCompatible(Type type) => (Pin.Pin.Type == type) private bool IsTypeCompatible(Type type) => (Pin.Pin.Type == type)
|| (Pin.Pin.Type == typeof(Enum) && type.IsEnum)
|| ((Pin.Pin.Direction == PinDirection.Input) && (Pin.Pin.Type == typeof(object))) || ((Pin.Pin.Direction == PinDirection.Input) && (Pin.Pin.Type == typeof(object)))
|| ((Pin.Pin.Direction == PinDirection.Output) && (type == typeof(object))); || ((Pin.Pin.Direction == PinDirection.Output) && (type == typeof(object)));

View File

@ -537,7 +537,7 @@ namespace Artemis.VisualScripting.Editor.Controls
// Connect to the first matching input or output pin // Connect to the first matching input or output pin
List<IPin> pins = node.Pins.ToList(); List<IPin> pins = node.Pins.ToList();
pins.AddRange(node.PinCollections.SelectMany(c => c)); pins.AddRange(node.PinCollections.SelectMany(c => c));
pins = pins.Where(p => p.Type == typeof(object) || p.Type == SourcePin.Pin.Type).OrderBy(p => p.Type != typeof(object)).ToList(); pins = pins.Where(p => p.Type == typeof(object) || p.Type.IsAssignableFrom(SourcePin.Pin.Type)).OrderBy(p => p.Type != typeof(object)).ToList();
IPin preferredPin = SourcePin.Pin.Direction == PinDirection.Input IPin preferredPin = SourcePin.Pin.Direction == PinDirection.Input
? pins.FirstOrDefault(p => p.Direction == PinDirection.Output) ? pins.FirstOrDefault(p => p.Direction == PinDirection.Output)

View File

@ -0,0 +1,65 @@
using System;
using System.ComponentModel;
using Artemis.Core;
using Artemis.Core.Events;
using Artemis.UI.Shared;
using Stylet;
namespace Artemis.VisualScripting.Nodes.CustomViewModels
{
public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel
{
private readonly EnumEqualsNode _node;
public EnumEqualsNodeCustomViewModel(EnumEqualsNode node) : base(node)
{
_node = node;
}
public Enum Input
{
get => _node.Storage as Enum;
set => _node.Storage = value;
}
public BindableCollection<ValueDescription> EnumValues { get; } = new();
public override void OnActivate()
{
_node.InputPin.PinConnected += InputPinOnPinConnected;
_node.InputPin.PinDisconnected += InputPinOnPinDisconnected;
_node.PropertyChanged += NodeOnPropertyChanged;
if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum)
EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType()));
base.OnActivate();
}
public override void OnDeactivate()
{
_node.InputPin.PinConnected -= InputPinOnPinConnected;
_node.InputPin.PinDisconnected -= InputPinOnPinDisconnected;
_node.PropertyChanged -= NodeOnPropertyChanged;
base.OnDeactivate();
}
private void InputPinOnPinDisconnected(object sender, SingleValueEventArgs<IPin> e)
{
EnumValues.Clear();
}
private void InputPinOnPinConnected(object sender, SingleValueEventArgs<IPin> e)
{
EnumValues.Clear();
if (_node.InputPin.Value != null && _node.InputPin.Value.GetType().IsEnum)
EnumValues.AddRange(EnumUtilities.GetAllValuesAndDescriptions(_node.InputPin.Value.GetType()));
}
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Node.Storage))
OnPropertyChanged(nameof(Input));
}
}
}

View File

@ -0,0 +1,26 @@
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.EnumEqualsNodeCustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.VisualScripting.Nodes.CustomViews"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ComboBox Width="140"
materialDesign:ComboBoxAssist.ClassicMode="True"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
SelectedValue="{Binding Input}"
ItemsSource="{Binding EnumValues}"
SelectedValuePath="Value"
DisplayMemberPath="Description">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</Grid>
</UserControl>

View File

@ -0,0 +1,30 @@
using System;
using Artemis.Core;
using Artemis.Core.Events;
using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes
{
[Node("Enum Equals", "Determines the equality between an input and a selected enum value", InputType = typeof(Enum), OutputType = typeof(bool))]
public class EnumEqualsNode : Node<EnumEqualsNodeCustomViewModel>
{
public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value")
{
InputPin = CreateInputPin<Enum>();
OutputPin = CreateOutputPin<bool>();
}
public InputPin<Enum> InputPin { get; }
public OutputPin<bool> OutputPin { get; }
#region Overrides of Node
/// <inheritdoc />
public override void Evaluate()
{
OutputPin.Value = InputPin.Value != null && InputPin.Value.Equals(Storage);
}
#endregion
}
}

View File

@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
".NETCoreApp,Version=v5.0": { "net5.0-windows7.0": {
"JetBrains.Annotations": { "JetBrains.Annotations": {
"type": "Direct", "type": "Direct",
"requested": "[2021.1.0, )", "requested": "[2021.1.0, )",