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

Use node suggestion API in node autocomplete UI #11132

Merged
Merged
52 changes: 52 additions & 0 deletions src/DynamoCore/Graph/Nodes/PortModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Xml;
using Dynamo.Configuration;
using Dynamo.Engine;
using Dynamo.Graph.Connectors;
using Dynamo.Graph.Nodes.ZeroTouch;
using Dynamo.Graph.Workspaces;
using Dynamo.Utilities;
using Newtonsoft.Json;
Expand Down Expand Up @@ -386,6 +389,55 @@ public override int GetHashCode()
return GUID.GetHashCode();
}

/// <summary>
/// Returns the string representation of the fully qualified typename
/// where possible for the port if it's an input port. This method currently
/// returns a valid type for only Zero Touch, Builtin and NodeModel nodes,
/// and returns null otherwise. The string representation of the type also
/// contains the rank information of the type, e.g. Point[], or var[]..[].
/// </summary>
/// <returns>input port type</returns>
internal string GetInputPortType()
{
if (PortType == PortType.Output) return null;

var ztNode = Owner as DSFunction;
if (ztNode != null)
{
var fd = ztNode.Controller.Definition;
string type;
// In the case of a node for an instance method, the first port
// type is the declaring class type of the method itself.
if (fd.Type == FunctionType.InstanceMethod)
{
if (Index > 0)
{
var param = fd.Parameters.ElementAt(Index - 1);
type = param.Type.ToString();
}
else
{
type = fd.ClassName;
}
}
else
{
var param = fd.Parameters.ElementAt(Index);
type = param.Type.ToString();
}
return type;
}

var nmNode = Owner as NodeModel;
if (nmNode != null)
{
var classType = nmNode.GetType();
var inPortAttribute = classType.GetCustomAttributes().OfType<InPortTypesAttribute>().FirstOrDefault();

return inPortAttribute?.PortTypes.ElementAt(Index);
}
return null;
}
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/DynamoCore/Search/SearchElements/NodeSearchElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public SearchElementGroup Group
}

/// <summary>
/// Group to which Node belongs to
/// Assembly to which Node belongs to
/// </summary>
public string Assembly
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void ExecuteSearchElement(ListBoxItem listBoxItem)
if (searchElement != null)
{
searchElement.Position = ViewModel.InCanvasSearchPosition;
PortViewModel port = ViewModel.targetPortViewModel;
PortViewModel port = ViewModel.PortViewModel;
searchElement.CreateAndConnectCommand.Execute(port.PortModel);
}
}
Expand Down Expand Up @@ -121,7 +121,8 @@ private void OnNodeAutoCompleteSearchControlVisibilityChanged(object sender, Dep
Dispatcher.BeginInvoke(new Action(() =>
{
SearchTextBox.Focus();
ViewModel.InitializeDefaultAutoCompleteCandidates();
//ViewModel.InitializeDefaultAutoCompleteCandidates();
ViewModel.PopulateAutoCompleteCandidates();
Copy link
Contributor

@QilongTang QilongTang Sep 21, 2020

Choose a reason for hiding this comment

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

We probably want to consider returning the number of filtered results in PopulateAutoCompleteCandidates(), if the number is 0 which means we failed to find any match, then we still call InitializeDefaultAutoCompleteCandidates().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I commented this out was I noticed issues with this. On a mouse up, the node suggestion list was reverting to this default list each time.

}), DispatcherPriority.Loaded);
}

Expand Down
62 changes: 57 additions & 5 deletions src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Dynamo.Graph.Nodes;
using Dynamo.Models;
using Dynamo.Search.SearchElements;
using Dynamo.UI.Commands;
using Dynamo.Utilities;

Expand Down Expand Up @@ -367,14 +370,18 @@ private bool CanConnect(object parameter)
// Handler to invoke node Auto Complete
private void AutoComplete(object parameter)
{
DynamoViewModel dynamoViewModel = this._node.DynamoViewModel;
(dynamoViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel).targetPortViewModel = parameter as PortViewModel;
dynamoViewModel.CurrentSpaceViewModel.ShowNodeAutoCompleteSearchCommand.Execute(ShowHideFlags.Show);
DynamoViewModel dynamoViewModel = _node.DynamoViewModel;
var svm = dynamoViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel;

svm.PortViewModel = parameter as PortViewModel;
//svm.PopulateAutoCompleteCandidates();

dynamoViewModel.CurrentSpaceViewModel.ShowNodeAutoCompleteSearchCommand.Execute(ShowHideFlags.Show);
}

private bool CanAutoComplete(object parameter)
{
DynamoViewModel dynamoViewModel = this._node.DynamoViewModel;
DynamoViewModel dynamoViewModel = _node.DynamoViewModel;
// If the feature is enabled from Dynamo experiment setting and if user interaction is on input port.
return dynamoViewModel.EnableNodeAutoComplete && this.PortType == PortType.Input;
}
Expand All @@ -386,7 +393,9 @@ private bool CanAutoComplete(object parameter)
private void OnRectangleMouseEnter(object parameter)
{
if (MouseEnter != null)
{
MouseEnter(parameter, null);
}
}

/// <summary>
Expand Down Expand Up @@ -427,6 +436,49 @@ private void OnMouseLeftUseLevel(object parameter)
ShowUseLevelMenu = false;
}


/// <summary>
/// Returns a collection of node search elements for nodes
/// that output a type compatible with the port type if it's an input port.
/// These search elements can belong to either zero touch, NodeModel or Builtin nodes.
/// This method returns an empty collection if the input port type cannot be inferred or
/// there are no matching nodes found for the type. Currently the match is an exact match
/// done including the rank information in the type, e.g. Point[] or var[]..[].
/// The search elements can be made to appear in the node autocomplete search dialog.
/// </summary>
/// <returns>collection of node search elements</returns>
internal IEnumerable<NodeSearchElement> GetMatchingNodes()
{
var elements = new List<NodeSearchElement>();

var inputPortType = PortModel.GetInputPortType();
if (inputPortType == null) return elements;

var dynamoModel = _node.DynamoViewModel.Model;
var libraryServices = dynamoModel.LibraryServices;

// Builtin functions and zero-touch functions
// Multi-return ports for these nodes do not contain type information
// and are therefore skipped.
var functionGroups = libraryServices.GetAllFunctionGroups();
var functionDescriptors = functionGroups.SelectMany(fg => fg.Functions).Where(fd => fd.IsVisibleInLibrary);

foreach (var descriptor in functionDescriptors)
{
if (descriptor.ReturnType.ToString() == inputPortType)
{
elements.Add(new ZeroTouchSearchElement(descriptor));
}
}

// NodeModel nodes
var searchModel = dynamoModel.SearchModel;
foreach (var element in searchModel.SearchEntries.OfType<NodeModelSearchElement>())
{
if (element.OutputParameters.Any(op => op == inputPortType))
elements.Add(element);
}

return elements;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace Dynamo.ViewModels
/// </summary>
public class NodeAutoCompleteSearchViewModel : SearchViewModel
{
internal PortViewModel targetPortViewModel { get; set; }
internal PortViewModel PortViewModel { get; set; }

internal NodeAutoCompleteSearchViewModel(DynamoViewModel dynamoViewModel) : base(dynamoViewModel)
{
InitializeDefaultAutoCompleteCandidates();
//InitializeDefaultAutoCompleteCandidates();
}

internal void InitializeDefaultAutoCompleteCandidates()
Expand All @@ -32,5 +32,16 @@ internal void InitializeDefaultAutoCompleteCandidates()
}
FilteredResults = candidates;
}

internal void PopulateAutoCompleteCandidates()
{
var searchElements = PortViewModel.GetMatchingNodes();
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for moving it. But I was hoping we can move all of the code from PortViewModel to the customized search view model here given that 1. those code are not really related to PortView 2. I feel PortViewModel does not necessarily obtain knowledge about search elements 3. We still have access to DynamoViewModel from here. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't moved the code yet. Doing so now.

FilteredResults = searchElements.Select(e =>
{
var vm = new NodeSearchElementViewModel(e, this);
vm.RequestBitmapSource += SearchViewModelRequestBitmapSource;
return vm;
});
}
}
}
2 changes: 1 addition & 1 deletion src/DynamoCoreWpf/ViewModels/Search/SearchViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ private void AddEntryToExistingCategory(NodeCategoryViewModel category,
}
}

private void SearchViewModelRequestBitmapSource(IconRequestEventArgs e)
protected void SearchViewModelRequestBitmapSource(IconRequestEventArgs e)
{
var warehouse = iconServices.GetForAssembly(e.IconAssembly, e.UseAdditionalResolutionPaths);
ImageSource icon = null;
Expand Down
2 changes: 1 addition & 1 deletion src/Libraries/CoreNodeModels/ColorRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace CoreNodeModels
[NodeSearchTags("ColorRangeSearchTags", typeof(Resources))]

[InPortNames("colors", "indices", "value")]
[InPortTypes("Color[]", "double[]", "double")]
[InPortTypes("DSCore.Color[]", "double[]", "double")]
[InPortDescriptions(typeof(Resources),
"ColorRangePortDataColorsToolTip",
"ColorRangePortDataIndicesToolTip",
Expand Down
1 change: 1 addition & 0 deletions src/Libraries/CoreNodeModels/WatchImageCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace CoreNodeModels
[NodeCategory(BuiltinNodeCategories.CORE_VIEW)]
[NodeSearchTags("WatchImageSearchTags", typeof(Resources))]
[IsDesignScriptCompatible]
[InPortTypes("System.Drawing.Bitmap")]
[OutPortTypes("var")]
[AlsoKnownAs("Dynamo.Nodes.WatchImageCore", "DSCoreNodesUI.WatchImageCore")]
public class WatchImageCore : NodeModel
Expand Down
1 change: 0 additions & 1 deletion src/Libraries/CoreNodes/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ public static IList Reverse(IList list)
/// Creates a new list containing the given items.
/// </summary>
/// <param name="items">Items to be stored in the new list.</param>
[IsVisibleInDynamoLibrary(true)]
public static IList __Create(IList items)
{
return items;
Expand Down
5 changes: 5 additions & 0 deletions src/Libraries/DesignScriptBuiltin/IndexingExceptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using Autodesk.DesignScript.Runtime;

namespace DesignScript.Builtin
{
[SupressImportIntoVM]
public class KeyNotFoundException : Exception
{
public KeyNotFoundException(string message)
Expand All @@ -10,6 +12,7 @@ public KeyNotFoundException(string message)
}
}

[SupressImportIntoVM]
public class IndexOutOfRangeException : Exception
{
public IndexOutOfRangeException(string message)
Expand All @@ -18,6 +21,7 @@ public IndexOutOfRangeException(string message)
}
}

[SupressImportIntoVM]
public class StringOverIndexingException : Exception
{
public StringOverIndexingException(string message)
Expand All @@ -30,6 +34,7 @@ public StringOverIndexingException(string message)
/// Null reference exception thrown with null DS builtin types:
/// lists, dictionaries and strings.
/// </summary>
[SupressImportIntoVM]
public class BuiltinNullReferenceException : NullReferenceException
{
public BuiltinNullReferenceException(string message)
Expand Down
Loading