Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 10 additions & 2 deletions src/DynamoCore/Graph/Nodes/PortModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class PortModel : ModelBase, IEquatable<PortModel>
private bool keepListStructure = false;
private int level = 1;
private string toolTip;
private string name;

#endregion

Expand Down Expand Up @@ -74,8 +75,15 @@ public ObservableCollection<ConnectorModel> Connectors
/// </summary>
public string Name
{
get;
internal set;
get => name;
internal set
{
if (name != value)
{
name = value;
RaisePropertyChanged(nameof(Name));
}
}
}

/// <summary>
Expand Down
52 changes: 1 addition & 51 deletions src/DynamoCoreWpf/ViewModels/Core/InPortViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Media;
Expand All @@ -9,7 +7,6 @@
using Dynamo.UI;
using Dynamo.UI.Commands;
using ProtoCore.Utils;
using UI.Prompts;

namespace Dynamo.ViewModels
{
Expand All @@ -20,7 +17,7 @@ public partial class InPortViewModel : PortViewModel
private DelegateCommand useLevelsCommand;
private DelegateCommand keepListStructureCommand;
private DelegateCommand portMouseLeftButtonOnLevelCommand;
private DelegateCommand editPortPropertiesCommand;


private bool showUseLevelMenu;
private bool isPythonNodePort;
Expand Down Expand Up @@ -306,53 +303,6 @@ private void ChangeLevel(int level)
}
}

/// <summary>
/// Used by the 'Edit Port Properties' button in the node output context menu.
/// Triggers the Port Properties Panel
/// </summary>
public DelegateCommand EditPortPropertiesCommand
{
get
{
return editPortPropertiesCommand ??
(editPortPropertiesCommand = new DelegateCommand(EditPortProperties));
}
}
/// <summary>
/// Used by the 'Edit Port Properties' button in the node output context menu.
/// Triggers the Port Properties Panel
/// </summary>
private void EditPortProperties(object parameter)
{
var wsViewModel = node.WorkspaceViewModel;

// Hide the popup, we no longer need it
wsViewModel.OnRequestPortContextMenu(ShowHideFlags.Show, this);

var dialog = new PortPropertiesEditPrompt()
{
DescriptionInput = { Text = port.ToolTip },
nameBox = { Text = port.Name },
PortType = PortType.Output,
OutPortNames = ListOutportNames(this.NodeViewModel.InPorts),
};

if (dialog.ShowDialog() != true)
{
return;
}

port.Name = dialog.PortName;
port.ToolTip = dialog.Description;

RaisePropertyChanged(nameof(PortName));
}

private List<string> ListOutportNames(ObservableCollection<PortViewModel> outPorts)
{
return outPorts.Where(x => !x.PortName.Equals(this.PortName)).Select(x => x.PortName).ToList();
}

/// <summary>
/// Handles the Mouse left button down on the level.
/// </summary>
Expand Down
53 changes: 1 addition & 52 deletions src/DynamoCoreWpf/ViewModels/Core/OutPortViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
Expand All @@ -11,7 +9,6 @@
using Dynamo.UI;
using Dynamo.UI.Commands;
using ProtoCore.Utils;
using UI.Prompts;
using Color = System.Windows.Media.Color;

namespace Dynamo.ViewModels
Expand All @@ -22,7 +19,7 @@ public partial class OutPortViewModel : PortViewModel

private DelegateCommand breakConnectionsCommand;
private DelegateCommand hideConnectionsCommand;
private DelegateCommand editPortPropertiesCommand;

private DelegateCommand portMouseLeftButtonOnContextCommand;

private SolidColorBrush portValueMarkerColor = new SolidColorBrush(Color.FromArgb(255, 204, 204, 204));
Expand Down Expand Up @@ -278,19 +275,6 @@ public DelegateCommand HideConnectionsCommand
get { return hideConnectionsCommand ?? (hideConnectionsCommand = new DelegateCommand(HideConnections)); }
}

/// <summary>
/// Used by the 'Edit Port Properties' button in the node output context menu.
/// Triggers the Port Properties Panel
/// </summary>
public DelegateCommand EditPortPropertiesCommand
{
get
{
return editPortPropertiesCommand ??
(editPortPropertiesCommand = new DelegateCommand(EditPortProperties));
}
}

/// <summary>
/// Used by the 'Break Connections' button in the node output context menu.
/// Removes any current connections this port has.
Expand Down Expand Up @@ -340,41 +324,6 @@ private void HideConnections(object parameter)
RefreshHideWiresState();
}

/// <summary>
/// Used by the 'Edit Port Properties' button in the node output context menu.
/// Triggers the Port Properties Panel
/// </summary>
private void EditPortProperties(object parameter)
{
var wsViewModel = node.WorkspaceViewModel;

// Hide the popup, we no longer need it
wsViewModel.OnRequestPortContextMenu(ShowHideFlags.Show, this);

var dialog = new PortPropertiesEditPrompt()
{
DescriptionInput = { Text = port.ToolTip },
nameBox = { Text = port.Name },
PortType = PortType.Output,
OutPortNames = ListOutportNames(this.NodeViewModel.OutPorts),
};

if (dialog.ShowDialog() != true)
{
return;
}

port.Name = dialog.PortName;
port.ToolTip = dialog.Description;

RaisePropertyChanged(nameof(PortName));
}

private List<string> ListOutportNames(ObservableCollection<PortViewModel> outPorts)
{
return outPorts.Where(x => !x.PortName.Equals(this.PortName)).Select(x => x.PortName).ToList();
}

/// <summary>
/// Returns true if they are hidden.
/// </summary>
Expand Down
58 changes: 57 additions & 1 deletion src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Windows.Threading;
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Workspaces;
using UI.Prompts;
using Dynamo.Models;
using Dynamo.Search.SearchElements;
using Dynamo.UI.Commands;
Expand All @@ -27,6 +28,7 @@
protected readonly NodeViewModel node;
private DelegateCommand useLevelsCommand;
private DelegateCommand keepListStructureCommand;
private DelegateCommand editPortPropertiesCommand;
private bool showUseLevelMenu;
private const double autocompletePopupSpacing = 2.5;
private const double proxyPortContextMenuOffset = 20;
Expand Down Expand Up @@ -500,7 +502,7 @@
case nameof(PortType):
RaisePropertyChanged(nameof(PortType));
break;
case nameof(PortName):
case nameof(PortModel.Name):
RaisePropertyChanged(nameof(PortName));
break;
case nameof(IsConnected):
Expand Down Expand Up @@ -740,5 +742,59 @@
}
return ">";
}

/// <summary>
/// Used by the 'Edit Port Properties' button in the node port context menu.
/// Opens the Port Properties dialog to rename the port and edit its description.
/// </summary>
public DelegateCommand EditPortPropertiesCommand
{
get
{
return editPortPropertiesCommand ??
(editPortPropertiesCommand = new DelegateCommand(EditPortProperties));
}
}

/// <summary>
/// Opens the Port Properties dialog and, on confirmation, records the owning node
/// for undo before applying the new name and description.
/// </summary>
private void EditPortProperties(object parameter)
{
var wsViewModel = node.WorkspaceViewModel;

// Hide the popup, we no longer need it
wsViewModel.OnRequestPortContextMenu(ShowHideFlags.Hide, this);

// Collect sibling port names (same port type) to prevent duplicates.
var siblingPorts = port.PortType == PortType.Input ? node.InPorts : node.OutPorts;

var dialog = new PortPropertiesEditPrompt()
{
DescriptionInput = { Text = port.ToolTip },
nameBox = { Text = port.Name },
PortType = port.PortType,
OutPortNames = siblingPorts.Where(p => !p.PortName.Equals(PortName))
.Select(p => p.PortName)
.ToList(),
};

if (dialog.ShowDialog() != true)

Check warning on line 783 in src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal(s).

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1j8wGnSEoVqOBVhCcP&open=AZ1j8wGnSEoVqOBVhCcP&pullRequest=17021
{
return;
}

var workspaceModel = node.WorkspaceViewModel.Model;
using (workspaceModel.UndoRecorder.BeginActionGroup())
{
WorkspaceModel.RecordModelForModification(port.Owner, workspaceModel.UndoRecorder);
port.Name = dialog.PortName;
port.ToolTip = dialog.Description;
}
workspaceModel.HasUnsavedChanges = true;

RaisePropertyChanged(nameof(PortName));
}
}
}
83 changes: 83 additions & 0 deletions src/Libraries/PythonNodeModels/PythonNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -173,6 +174,88 @@
return base.UpdateValueCore(updateValueParams);
}

#region SerializeCore/DeserializeCore

/// <summary>
/// Preserves custom input and output port names and tooltips during XML serialization.
/// The base NodeModel serialization writes PortInfo elements for input ports;
/// this override adds "portName" and "portToolTip" attributes to each of those
/// elements and appends OutPortInfo elements for output ports.
/// These fields are emitted and consumed for all SaveContext values (Copy, Undo, Save, etc.).
/// </summary>
protected override void SerializeCore(XmlElement element, SaveContext context)
{
base.SerializeCore(element, context);

// Add portName and portToolTip attributes to each PortInfo element written by NodeModel.SerializeCore.
foreach (XmlNode child in element.ChildNodes)
{
if (child.Name == "PortInfo" && child is XmlElement portInfo)
{
var indexAttr = portInfo.GetAttribute("index");

Check warning on line 195 in src/Libraries/PythonNodeModels/PythonNode.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of using this literal 'index' 4 times.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1j3mVEZXo6OFpyg61G&open=AZ1j3mVEZXo6OFpyg61G&pullRequest=17021
if (int.TryParse(indexAttr, NumberStyles.Integer, CultureInfo.InvariantCulture, out int index)
&& index >= 0 && index < InPorts.Count)
{
portInfo.SetAttribute("portName", InPorts[index].Name);

Check warning on line 199 in src/Libraries/PythonNodeModels/PythonNode.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of using this literal 'portName' 6 times.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1j3mVEZXo6OFpyg61H&open=AZ1j3mVEZXo6OFpyg61H&pullRequest=17021
portInfo.SetAttribute("portToolTip", InPorts[index].ToolTip);

Check warning on line 200 in src/Libraries/PythonNodeModels/PythonNode.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of using this literal 'portToolTip' 6 times.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1j3mVEZXo6OFpyg61I&open=AZ1j3mVEZXo6OFpyg61I&pullRequest=17021
}
Comment thread
johnpierson marked this conversation as resolved.
}
}

// Write output port names and tooltips as separate elements (NodeModel does not serialize output PortInfo).
for (int i = 0; i < OutPorts.Count; i++)
{
XmlElement outPortInfo = element.OwnerDocument.CreateElement("OutPortInfo");
outPortInfo.SetAttribute("index", i.ToString(CultureInfo.InvariantCulture));
outPortInfo.SetAttribute("portName", OutPorts[i].Name);
outPortInfo.SetAttribute("portToolTip", OutPorts[i].ToolTip);
element.AppendChild(outPortInfo);
}
}

/// <summary>
/// Restores custom input and output port names after base deserialization has
/// recreated the ports with their default names.
/// </summary>
protected override void DeserializeCore(XmlElement nodeElement, SaveContext context)

Check failure on line 220 in src/Libraries/PythonNodeModels/PythonNode.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 30 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ1j3mVEZXo6OFpyg61J&open=AZ1j3mVEZXo6OFpyg61J&pullRequest=17021
{
base.DeserializeCore(nodeElement, context);

// After base.DeserializeCore, all ports exist with default names and tooltips.
// Re-apply any custom values that were serialized.
foreach (XmlNode child in nodeElement.ChildNodes)
{
if (child.Name == "PortInfo" && child is XmlElement portInfo)

@aparajit-pratap aparajit-pratap Apr 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, so this is not InPortInfo - not symmetrically named like OutPortInfo? Not a comment on this PR but just an observation about confusing naming.

{
var indexAttr = portInfo.GetAttribute("index");
if (int.TryParse(indexAttr, NumberStyles.Integer, CultureInfo.InvariantCulture, out int index)
&& index >= 0 && index < InPorts.Count)
{
if (portInfo.HasAttribute("portName"))
InPorts[index].Name = portInfo.GetAttribute("portName");

if (portInfo.HasAttribute("portToolTip"))
InPorts[index].ToolTip = portInfo.GetAttribute("portToolTip");
}
}
else if (child.Name == "OutPortInfo" && child is XmlElement outPortInfo)
{
var indexAttr = outPortInfo.GetAttribute("index");
if (int.TryParse(indexAttr, NumberStyles.Integer, CultureInfo.InvariantCulture, out int outIndex)
&& outIndex >= 0 && outIndex < OutPorts.Count)
{
if (outPortInfo.HasAttribute("portName"))
OutPorts[outIndex].Name = outPortInfo.GetAttribute("portName");

Comment thread
johnpierson marked this conversation as resolved.
if (outPortInfo.HasAttribute("portToolTip"))
OutPorts[outIndex].ToolTip = outPortInfo.GetAttribute("portToolTip");
}
}
}
}

#endregion

}

[NodeName("Python Script")]
Expand Down
Loading
Loading