diff --git a/src/Libraries/CoreNodeModels/Input/BaseTypes.cs b/src/Libraries/CoreNodeModels/Input/BaseTypes.cs
index 7c9430c7ebe..c729ce70073 100644
--- a/src/Libraries/CoreNodeModels/Input/BaseTypes.cs
+++ b/src/Libraries/CoreNodeModels/Input/BaseTypes.cs
@@ -13,7 +13,6 @@
using Dynamo.Utilities;
using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
-using ProtoCore.DSASM;
namespace CoreNodeModels.Input
{
@@ -37,6 +36,41 @@ public override string NodeType
}
}
+
+ private double _serializedWidth;
+ ///
+ /// The serialized Width property of the text input; Width is taken and JsonIgnore applied to
+ ///
+ public double SerializedWidth
+ {
+ get => _serializedWidth;
+ set
+ {
+ if (_serializedWidth != value)
+ {
+ _serializedWidth = value;
+ RaisePropertyChanged(nameof(SerializedWidth)); // Notify change
+ }
+ }
+ }
+
+ private double _serializedHeight;
+ ///
+ /// The serialized Height property of the text input; Height is taken and JsonIgnore applied to
+ ///
+ public double SerializedHeight
+ {
+ get => _serializedHeight;
+ set
+ {
+ if (_serializedHeight != value)
+ {
+ _serializedHeight = value;
+ RaisePropertyChanged(nameof(SerializedHeight)); // Notify change
+ }
+ }
+ }
+
[JsonConstructor]
private StringInput(IEnumerable inPorts, IEnumerable outPorts) : base(inPorts, outPorts)
{
@@ -61,6 +95,16 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams)
Value = value;
return true; // UpdateValueCore handled.
}
+ if (name == "SerializedWidth" && double.TryParse(value, out double width))
+ {
+ SerializedWidth = width;
+ return true; // UpdateValueCore handled
+ }
+ if (name == "SerializedHeight" && double.TryParse(value, out double height))
+ {
+ SerializedHeight = height;
+ return true; // UpdateValueCore handled
+ }
// There's another 'UpdateValueCore' method in 'String' base class,
// since they are both bound to the same property, 'StringInput'
@@ -76,6 +120,8 @@ protected override void SerializeCore(XmlElement nodeElement, SaveContext contex
var helper = new XmlElementHelper(outEl);
helper.SetAttribute("value", SerializeValue());
+ helper.SetAttribute("serializedWidth", SerializedWidth.ToString(CultureInfo.InvariantCulture));
+ helper.SetAttribute("serializedHeight", SerializedHeight.ToString(CultureInfo.InvariantCulture));
nodeElement.AppendChild(outEl);
}
@@ -92,6 +138,14 @@ protected override void DeserializeCore(XmlElement nodeElement, SaveContext cont
{
Value = DeserializeValue(attr.Value);
}
+ if (attr.Name.Equals("serializedWidth") && double.TryParse(attr.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double width))
+ {
+ SerializedWidth = width;
+ }
+ if (attr.Name.Equals("serializedHeight") && double.TryParse(attr.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out double height))
+ {
+ SerializedHeight = height;
+ }
}
}
}
diff --git a/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/StringInput.cs b/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/StringInput.cs
index e55793cb724..127bdc2c622 100644
--- a/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/StringInput.cs
+++ b/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/StringInput.cs
@@ -1,8 +1,14 @@
-using System.Windows;
+using System;
+using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
using CoreNodeModels.Input;
using Dynamo.Controls;
+using Dynamo.Graph.Workspaces;
+using Dynamo.Models;
using Dynamo.Nodes;
using Dynamo.UI.Prompts;
using Dynamo.ViewModels;
@@ -15,13 +21,20 @@ namespace CoreNodeModelsWpf.Nodes
public class StringInputNodeViewCustomization : INodeViewCustomization
{
private DynamoViewModel dynamoViewModel;
+ private WorkspaceModel workspace;
private StringInput nodeModel;
private MenuItem editWindowItem;
+ private NodeView nodeView;
+ private readonly int minWidthSize = 100;
+ private readonly int minHeightSize = 34;
+ private readonly int defMaxWidthSize = 200;
public void CustomizeView(StringInput stringInput, NodeView nodeView)
{
this.nodeModel = stringInput;
+ this.nodeView = nodeView;
this.dynamoViewModel = nodeView.ViewModel.DynamoViewModel;
+ this.workspace = this.dynamoViewModel.CurrentSpace;
this.editWindowItem = new MenuItem
{
@@ -32,16 +45,32 @@ public void CustomizeView(StringInput stringInput, NodeView nodeView)
editWindowItem.Click += editWindowItem_Click;
- //add a text box to the input grid of the control
+ // Add a text box to the input grid of the control
var tb = new StringTextBox
{
AcceptsReturn = true,
AcceptsTab = true,
TextWrapping = TextWrapping.Wrap,
- MaxWidth = 200,
- VerticalAlignment = VerticalAlignment.Top
+ MinHeight = minHeightSize,
+ VerticalAlignment = VerticalAlignment.Top,
};
+ // Set the recorded Width, if any
+ if (stringInput.SerializedWidth != 0)
+ {
+ tb.Width = stringInput.SerializedWidth;
+ }
+ else
+ {
+ tb.MaxWidth = defMaxWidthSize;
+ }
+
+ // Set the recorded Height, if any
+ if (stringInput.SerializedHeight != 0)
+ {
+ tb.Height = stringInput.SerializedHeight;
+ }
+
nodeView.inputGrid.Children.Add(tb);
Grid.SetColumn(tb, 0);
Grid.SetRow(tb, 0);
@@ -54,6 +83,86 @@ public void CustomizeView(StringInput stringInput, NodeView nodeView)
Source = stringInput,
UpdateSourceTrigger = UpdateSourceTrigger.Explicit
});
+
+ AddResizeThumb(tb, nodeView.inputGrid, stringInput);
+ }
+
+ ///
+ /// Adds a resize thumb to enable dynamic resizing of a StringTextBox.
+ ///
+ private void AddResizeThumb(StringTextBox tb, Grid inputGrid, StringInput stringInput)
+ {
+ var resizeThumb = CreateResizeThumb();
+ inputGrid.Children.Add(resizeThumb);
+
+ resizeThumb.DragStarted += (s, e) =>
+ {
+ // Set the binding to the Serialized Width/Height values here
+ SetNodeWidthHeightBinding(tb, stringInput);
+
+ // Record the undo/redo action prior to setting the new width/height values
+ RecordToUndoRedoStack(stringInput.SerializedWidth.ToString(), stringInput.SerializedHeight.ToString());
+ };
+
+ // Handle resizing logic in the resize thumb
+ resizeThumb.DragDelta += (s, e) =>
+ {
+ if (tb.MaxWidth == defMaxWidthSize)
+ {
+ tb.MaxWidth = double.PositiveInfinity;
+ }
+
+ var newWidth = tb.ActualWidth + e.HorizontalChange;
+ var newHeight = tb.ActualHeight + e.VerticalChange;
+
+ tb.Width = Math.Max(minWidthSize, newWidth);
+ tb.Height = Math.Max(minHeightSize, newHeight);
+
+ stringInput.Width = tb.ActualWidth;
+ stringInput.Height = tb.ActualHeight;
+
+ // Mark the graph as modified
+ if(!this.workspace.HasUnsavedChanges)
+ this.workspace.HasUnsavedChanges = true;
+ };
+ }
+
+ private void SetNodeWidthHeightBinding(StringTextBox tb, StringInput stringInput)
+ {
+ // Only set the binding if it hasn't been set already
+ var bindingExpression = tb.GetBindingExpression(FrameworkElement.WidthProperty);
+ if (bindingExpression == null)
+ {
+ // Bind Width property to SerializedWidth
+ BindingOperations.SetBinding(tb, FrameworkElement.WidthProperty, new Binding("SerializedWidth")
+ {
+ Mode = BindingMode.TwoWay,
+ Source = stringInput,
+ UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
+ });
+
+ // Bind Height property to SerializedHeight
+ BindingOperations.SetBinding(tb, FrameworkElement.HeightProperty, new Binding("SerializedHeight")
+ {
+ Mode = BindingMode.TwoWay,
+ Source = stringInput,
+ UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
+ });
+ }
+ }
+
+ private void RecordToUndoRedoStack(string v1, string v2)
+ {
+ this.dynamoViewModel.ExecuteCommand(
+ new DynamoModel.UpdateModelValueCommand(
+ this.nodeView.ViewModel.WorkspaceViewModel.Model.Guid,
+ this.nodeModel.GUID, "SerializedWidth", v1));
+
+
+ this.dynamoViewModel.ExecuteCommand(
+ new DynamoModel.UpdateModelValueCommand(
+ this.nodeView.ViewModel.WorkspaceViewModel.Model.Guid,
+ this.nodeModel.GUID, "SerializedHeight", v2));
}
public void editWindowItem_Click(object sender, RoutedEventArgs e)
@@ -73,6 +182,40 @@ public void editWindowItem_Click(object sender, RoutedEventArgs e)
editWindow.ShowDialog();
}
+ #region Helpers
+
+ ///
+ /// Helper to create a resize thumb.
+ ///
+ private static Thumb CreateResizeThumb()
+ {
+ return new Thumb
+ {
+ Width = 10,
+ Height = 10,
+ HorizontalAlignment = HorizontalAlignment.Right,
+ VerticalAlignment = VerticalAlignment.Bottom,
+ Cursor = Cursors.SizeNWSE,
+ Margin = new Thickness(0, 0, 3, 3),
+ Template = CreateThumbTemplate()
+ };
+ }
+
+ ///
+ /// Helper to create the thumb template.
+ ///
+ private static ControlTemplate CreateThumbTemplate()
+ {
+ var template = new ControlTemplate(typeof(Thumb));
+ var polygon = new FrameworkElementFactory(typeof(System.Windows.Shapes.Polygon));
+ polygon.SetValue(System.Windows.Shapes.Polygon.FillProperty, new SolidColorBrush(Color.FromRgb(175, 175, 175)));
+ polygon.SetValue(System.Windows.Shapes.Polygon.PointsProperty, new PointCollection(new[] { new Point(0, 8), new Point(8, 8), new Point(8, 0) }));
+ template.VisualTree = polygon;
+ return template;
+ }
+
+ #endregion
+
public void Dispose()
{
editWindowItem.Click -= editWindowItem_Click;