Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DYN-6631 - Input/Output Node - part 2 (View customization) #15022

Merged
merged 22 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/DynamoCoreWpf/UI/Themes/Modern/DynamoModern.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4440,6 +4440,66 @@
</Setter>
</Style>

<!-- This is a Padlock toggle button -->
<PathGeometry x:Key="PadlockIcon" Figures="M18 3C15.7909 3 14 4.79086 14 7V10H6C5.44772 10 5 10.4477 5 11V19C5 19.5523 5.44772 20 6 20H16C16.5523 20 17 19.5523 17 19V11C17 10.4477 16.5523 10 16 10H15V7C15 5.34315 16.3431 4 18 4C19.6569 4 21 5.34315 21 7V9H22V7C22 4.79086 20.2091 3 18 3ZM6 11H16V19H6L6 11Z"/>
twastvedt marked this conversation as resolved.
Show resolved Hide resolved
<PathGeometry x:Key="PadlockLockedIcon" Figures="M11 4C8.79084 4 6.99998 5.79087 6.99999 8.00001L7 10H6C5.44772 10 5 10.4477 5 11V19C5 19.5523 5.44772 20 6 20H16C16.5523 20 17 19.5523 17 19V11C17 10.4477 16.5523 10 16 10H15V7.99999C15 5.79086 13.2091 4 11 4ZM14 10V7.99999C14 6.34314 12.6568 5 11 5C9.34313 5 7.99998 6.34315 7.99999 8L8 10H14ZM6 11H16V19H6L6 11Z"/>
<Style x:Key="PadlockToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="UseLayoutRounding" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid x:Name="mainGrid">
<Border Background="Transparent" Name="_borderOn">
<Viewbox Width="24"
Height="24"
VerticalAlignment="Center"
Margin="2 0 0 0">
<Path x:Name="_toggleOn"
Fill="#999999"
Data="{StaticResource PadlockIcon}"
StrokeThickness="0"
Stroke="#999999">
</Path>
</Viewbox>
</Border>
<Border Background="Transparent" Name="_borderOff">
<Viewbox Width="22"
Height="22"
VerticalAlignment="Center"
Margin="0 0 4 1">
<Path x:Name="_toggleOff"
Fill="#999999"
Data="{StaticResource PadlockLockedIcon}"
StrokeThickness="0"
Stroke="#999999">
</Path>
</Viewbox>
</Border>
</Grid>

<!-- triggers toggle visual appearance -->
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="_borderOff" Property="Visibility" Value="Collapsed" />
<Setter TargetName="_borderOn" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="_borderOff" Property="Visibility" Value="Visible" />
<Setter TargetName="_borderOn" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="_toggleOn" Property="Fill" Value="#6AC0E7" />
<Setter TargetName="_toggleOff" Property="Fill" Value="#6AC0E7" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="mainGrid" Property="OpacityMask" Value="{StaticResource SemiTransparent}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<!-- This is a Toogle button shown in the Preferences Window for the General tab, when is checked shows a Blue button -->
<Style x:Key="EllipseToggleButton1" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
Expand Down
104 changes: 82 additions & 22 deletions src/Libraries/CoreNodeModels/DefineData.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
Expand All @@ -9,28 +8,62 @@
using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using VMDataBridge;
using static DSCore.Data;


namespace CoreNodeModels
{
[NodeName("DefineData")]
[NodeDescription(nameof(Properties.Resources.RememberDescription), typeof(Properties.Resources))]
[NodeCategory("Core.Data")]
[InPortNames(">")]
[InPortTypes("var[]..[]")]
[InPortDescriptions(typeof(Properties.Resources),
nameof(Properties.Resources.RememberInputToolTip))]
[OutPortNames(">")]
[OutPortTypes("var[]..[]")]
[OutPortDescriptions(typeof(Properties.Resources), nameof(Properties.Resources.RememberOuputToolTip))]
[IsDesignScriptCompatible]
public class DefineData : DSDropDownBase
{
private bool context;
private List<DynamoDropDownItem> serializedItems;
private bool isAutoMode;
private bool isList;

/// <summary>
/// The IsAutoMode property enables the node to automatically validate and process input data.
/// AutoMode = true: the node checks input types for serialization compatibility, supports single values and non-nested lists,
/// and distinguishes between homogeneous and certain heterogeneous collections through inheritance.
/// Invalid or unsupported data types result in error messages,
/// while successful validation updates node properties and UI elements to reflect the processed data.
/// AutoMode = false: the node enters a manual processing mode,
/// where it strictly validates that the TypeID and Context predefined on the node match the attached input data.
/// Mismatches in expected data types or contexts—such as receiving a list instead of a single item,
/// or input data not matching the specified TypeID—result in errors, without automatically adjusting node settings.
/// This manual mode maintains the node's current configurations, ensuring an output is passed only when valid data is processed,
/// and retains the node's state in warning without resetting selections for invalid data.
/// </summary>
[JsonProperty]
public bool IsList { get; set; }
public bool IsAutoMode
Copy link
Member

@mjkkirschner mjkkirschner Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dnenov @twastvedt @saintentropy when this node's data types do not match and errors are thrown, does this node return null?

Does it make sense to explore not returning anything and to stop all downstream computation? For example, this node could compile to an imperative AST like scopeIF.

Anyhow, that may be out of scope and we could do it later - it just seems like a large potential waste of compute if this node is used upstream of the entire graph in many cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does (should) return null/nothing. Makes sense to me. Is that (stopping downstream computation) something that other nodes do? If it's not complicated, perhaps we can look at it now. Things left for later tend not to get done.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you point me to an existing implementation, I can take that on, up to you @mjkkirschner and @twastvedt !

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@twastvedt no - returning null does not stop downstream computation - it passes null and likely leads to a bunch of slow exceptions for all downstream node calls - that's why I am suggesting rethinking that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look here - but it's not simple, if it's something vera team is interested in it probably warrants a meeting as it's a new scope/feature for sure.

[NodeName("ScopeIf"), NodeCategory(BuiltinNodeCategories.LOGIC),

{
get { return isAutoMode; }
set
{
isAutoMode = value;
RaisePropertyChanged(nameof(IsAutoMode));
}
}

/// <summary>
/// IsList property defines if the input is of a type ArrayList.
/// The node supports only non-nested lists of homogeneous or heterogenous collections through inheritance
/// </summary>
[JsonProperty]
public bool IsList
{
get { return isList; }
set
{
isList = value;
RaisePropertyChanged(nameof(IsList));
}
}

/// <summary>
/// Copy of <see cref="DSDropDownBase.Items"/> to be serialized./>
Expand All @@ -57,10 +90,16 @@ protected List<DynamoDropDownItem> SerializedItems
/// </summary>
public DefineData() : base(">")
{
InPorts.Add(new PortModel(PortType.Input, this, new PortData("", Properties.Resources.WatchPortDataInputToolTip)));
OutPorts.Add(new PortModel(PortType.Output, this, new PortData("", Properties.Resources.WatchPortDataResultToolTip)));

RegisterAllPorts();

PropertyChanged += OnPropertyChanged;

foreach (var dataType in Data.GetDataNodeDynamoTypeList())
//Items.Add(new DynamoDropDownItem("Select a type", null));

foreach (var dataType in Data.DataNodeDynamoTypeList)
{
var displayName = dataType.Name;
var value = dataType;
Expand All @@ -79,7 +118,7 @@ private DefineData(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPor

private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{

}

protected override void OnBuilt()
Expand All @@ -95,6 +134,9 @@ public override void Dispose()
DataBridge.Instance.UnregisterCallback(GUID.ToString());
}

private static readonly string BuiltinDictionaryTypeName = typeof(DesignScript.Builtin.Dictionary).FullName;
private static readonly string BuiltinDictionaryGet = nameof(DesignScript.Builtin.Dictionary.ValueAtKey);

public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes)
{
var resultAst = new List<AssociativeNode>();
Expand All @@ -103,27 +145,33 @@ public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode
// the object to be (type) evaluated
// the expected datatype
// if the input is an ArrayList or not
var function = new Func<object, string, bool, bool>(DSCore.Data.IsSupportedDataNodeDynamoType);
var function = new Func<object, string, bool, bool, Dictionary<string, object>>(DSCore.Data.IsSupportedDataNodeType);
var funtionInputs = new List<AssociativeNode> {
inputAstNodes[0],
AstFactory.BuildStringNode((Items[SelectedIndex].Item as Data.DataNodeDynamoType).Type.ToString()),
AstFactory.BuildBooleanNode(IsList) };
AstFactory.BuildBooleanNode(IsList),
AstFactory.BuildBooleanNode(IsAutoMode)
};


var functionCall = AstFactory.BuildFunctionCall(function, funtionInputs);
var functionCallIdentifier = AstFactory.BuildIdentifier(GUID + "_func");

// build the function call
resultAst.Add(AstFactory.BuildAssignment(functionCallIdentifier, functionCall));

// build the output call
resultAst.Add(AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), functionCall));
//Next add the first key value pair to the output port
var getFirstKey = AstFactory.BuildFunctionCall(BuiltinDictionaryTypeName, BuiltinDictionaryGet,
new List<AssociativeNode> { functionCallIdentifier, AstFactory.BuildStringNode(">") });

resultAst.Add(AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), getFirstKey));

// build the call for the DataBridge
resultAst.Add(AstFactory.BuildAssignment(functionCallIdentifier,
DataBridge.GenerateBridgeDataAst(GUID.ToString(),
AstFactory.BuildExprList(inputAstNodes))));
//Second get the key value pair to pass to the databridge callback
var getSecondKey = AstFactory.BuildFunctionCall(BuiltinDictionaryTypeName, BuiltinDictionaryGet,
new List<AssociativeNode> { functionCallIdentifier, AstFactory.BuildStringNode("Validation") });

resultAst.Add(AstFactory.BuildAssignment(
AstFactory.BuildIdentifier(GUID + "_db"),
DataBridge.GenerateBridgeDataAst(GUID.ToString(), getSecondKey)));

return resultAst;
}
Expand All @@ -135,15 +183,27 @@ public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode
/// <param name="data"></param>
private void DataBridgeCallback(object data)
{
var inputs = data as ArrayList;
if (data == null) return;

var inputObject = inputs[0];
(bool IsValid, bool UpdateList, DataNodeDynamoType InputType) resultData = (ValueTuple<bool, bool, DataNodeDynamoType>)data;

if (!InPorts[0].IsConnected)
if (IsAutoMode && resultData.UpdateList)
{
return;
IsList = !IsList;
twastvedt marked this conversation as resolved.
Show resolved Hide resolved
}

if (!resultData.IsValid)
{
if (IsAutoMode)
{
// Assign to the correct value, if the object was of supported type
if (resultData.InputType != null)
{
var index = Items.IndexOf(Items.First(i => i.Name.Equals(resultData.InputType.Name)));
SelectedIndex = index;
}
}
}
}


Expand Down
57 changes: 11 additions & 46 deletions src/Libraries/CoreNodeModelsWpf/Controls/DefineDataControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,21 @@
<ui:SharedResourceDictionary Source="{x:Static ui:SharedDictionaryManager.DynamoConvertersDictionaryUri}" />
<ui:SharedResourceDictionary Source="{x:Static ui:SharedDictionaryManager.DynamoColorsAndBrushesDictionaryUri}" />
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="StrokeBrush" Color="#999999 " />

<SolidColorBrush x:Key="StrokeBrush" Color="#999999" />
<PathGeometry x:Key="QuestionIcon" Figures="M8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 5.87827 15.1571 3.84344 13.6569 2.34315C12.1566 0.842855 10.1217 0 8 0ZM9 14H7V12H9V14ZM10 8.45C9.38 8.74 9 8.95 9 9.45V11H7V8.92C6.97651 8.37388 7.19188 7.84457 7.59 7.47C8.05 7.06 9.75 6.56 9.75 5.59C9.75 5 9.45 4.33 8.23 4.33C6.75 4.33 6.23 6.66 6.23 6.66L4.45 6.25C4.45 6.25 4.86 2.39 8.25 2.39C9.13189 2.33809 9.99827 2.63874 10.6584 3.22576C11.3186 3.81279 11.7185 4.63809 11.77 5.52C11.8034 6.75866 11.1119 7.90321 10 8.45Z"/>
<PathGeometry x:Key="PadlockIcon" Figures="M 35 7 C 30.038 7 26 11.038 26 16 L 26 20 L 9 20 C 7.343 20 6 21.343 6 23 L 6 38 C 6 39.657 7.343 41 9 41 L 27 41 C 28.657 41 30 39.657 30 38 L 30 23 C 30 21.343 28.657 20 27 20 L 27 16 C 27 11.589 30.589 8 35 8 C 39.411 8 43 11.589 43 16 L 43 20.5 C 43 20.776 43.224 21 43.5 21 C 43.776 21 44 20.776 44 20.5 L 44 16 C 44 11.038 39.962 7 35 7 z M 9 21 L 27 21 C 28.105 21 29 21.895 29 23 L 29 38 C 29 39.105 28.105 40 27 40 L 9 40 C 7.895 40 7 39.105 7 38 L 7 23 C 7 21.895 7.895 21 9 21 z"/>
</ResourceDictionary>
</UserControl.Resources>
<DockPanel VerticalAlignment="Bottom">
<DockPanel VerticalAlignment="Bottom" LastChildFill="False" Margin="0 0 0 -5">
<ToggleButton VerticalAlignment="Center"
Width="30"
Height="15"
Margin="2,0,0,0"
IsEnabled="True"
IsChecked="{Binding Path=StaticSplashScreenEnabled}"
Style="{StaticResource EllipseToggleButton1}"/>
Width="30"
Height="15"
Margin="2,0,0,0"
IsEnabled="True"
IsChecked="{Binding Path=IsList}"
Style="{StaticResource EllipseToggleButton1}"/>
<Label Content="List"
Margin="4 0"
Foreground="{StaticResource PreferencesWindowFontColor}"/>
Margin="4 0"
Foreground="{StaticResource PreferencesWindowFontColor}"/>
<Canvas VerticalAlignment="Center" Width="14" Height="14">
<Viewbox Width="14" Height="14">
<Path x:Name="ExpandPath" Data="{StaticResource QuestionIcon}"
Expand All @@ -41,41 +39,8 @@
</Viewbox>
<Canvas.ToolTip>
<ToolTip Content="Fill in later"
Style="{StaticResource GenericToolTipLight}"/>
Style="{StaticResource GenericToolTipLight}"/>
</Canvas.ToolTip>
</Canvas>

<Button Margin="0 0 10 3"
x:Name="LockButton"
BorderThickness="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Cursor="Hand">
<Button.ToolTip>
<ToolTip Content="Lock tooltup"
Style="{StaticResource GenericToolTipLight}" />
</Button.ToolTip>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<Viewbox Width="16"
Height="16"
VerticalAlignment="Center">
<Path x:Name="Folder"
Fill="#999999"
Data="{StaticResource PadlockIcon}"
StrokeThickness="1"
Stroke="{StaticResource StrokeBrush}">
</Path>
</Viewbox>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Folder" Property="Fill" Value="#6AC0E7" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DockPanel>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ namespace CoreNodeModelsWpf.Controls
/// </summary>
public partial class DefineDataControl : UserControl
{
private readonly DefineData model;

internal ComboBox BaseComboBox { get; set; }

public DefineDataControl(DefineDataViewModel viewModel)
public DefineDataControl(DefineData model)
{
DataContext = viewModel;
model = viewModel.Model;
DataContext = model;

InitializeComponent();
}
Expand Down
Loading
Loading