Skip to content

Commit

Permalink
DYN-6631 - Input/Output Node - part 2 (View customization) (#15022)
Browse files Browse the repository at this point in the history
* initialize definedata model

- starting with the model and the test suite

* added test structure, node customization

- now types are contained inside an enum
- added the basic primitives to the test structure, including lists checks
- reworked the node to start getting the customization going (and make sense of the whole thing)

* hierarchical container, geometry tests

- created hierarchical container capable of tracking type inheritance
- added all geometry tests

* primitive tests done

- finished all primitive date type tests

* ggroup

* Revert "ggroup"

This reverts commit 1621855.

* inheritance tests done

- completed tests checking inf inheritance works on individual and on list level

* comments

- added a few additional comments
- additional test checking if inheritance stops at the desired level (ie. `Cone` does not inherit from `Curve`)

* finished refactoring for tests

- removed the Enum, replaced directly with Type

* refactor, comments

- removed dictionary in favor of list of datatypes
- renamed methods to better suit the specificity of the node functionality they were serving

* pass through

- now returns the input as an output
- not doing much with the result of the validation though, except returning to the first value of the drop-down

* inputs

- added functional IsAutoMode and IsList inputs to the Model
- removed the ViewModel

* comments

- do not change dropdown if not in AutoMode
- added detailed description to IsAutoMode property

* datatype list refactor

- public static method of retrieving the list of datatypes replaced by an internal static property DataNodeDynamoTypeList

* comments

- IsSupportedDataNodeDynamoType now internal
- refactor

---------

Co-authored-by: Ashish Aggarwal <[email protected]>
  • Loading branch information
dnenov and zeusongit authored Apr 18, 2024
1 parent 732ea62 commit c9f6dda
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 129 deletions.
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"/>
<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
{
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;
}

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

0 comments on commit c9f6dda

Please sign in to comment.