Skip to content

Commit

Permalink
Fix autocomplete popup - #3 (DynamoDS#11224)
Browse files Browse the repository at this point in the history
* fix autocomplete popup - part 3

* revert unchanged file

* fix failing test, fix analytics for node autocomplete

* add null check

* review comments
  • Loading branch information
aparajit-pratap authored Nov 3, 2020
1 parent 84f02b4 commit 905d976
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,9 @@ public NodeAutoCompleteSearchControl()

private void NodeAutoCompleteSearchControl_Loaded(object sender, RoutedEventArgs e)
{
if (ViewModel != null && ViewModel.PortViewModel != null)
{
ViewModel.PortViewModel.PlaceNodeAutocompleteWindow(this, e);
Analytics.TrackEvent(
Analytics.TrackEvent(
Dynamo.Logging.Actions.Open,
Dynamo.Logging.Categories.NodeAutoCompleteOperations);
}
}

private void NodeAutoCompleteSearchControl_Unloaded(object sender, RoutedEventArgs e)
Expand Down
3 changes: 3 additions & 0 deletions src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,9 @@ public double Y
}
}

internal double ActualHeight { get; set; }
internal double ActualWidth { get; set; }

#endregion

#region events
Expand Down
42 changes: 22 additions & 20 deletions src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class PortViewModel : ViewModelBase
private readonly NodeViewModel _node;
private DelegateCommand _useLevelsCommand;
private DelegateCommand _keepListStructureCommand;
private const double autocompleteUISpacing = 2.5;
private const double autocompletePopupSpacing = 2.5;

/// <summary>
/// Port model.
Expand Down Expand Up @@ -230,38 +230,40 @@ public override void Dispose()
}

/// <summary>
/// Places the node autocomplete window relative to the respective port
/// once the control is loaded and when its actual width is known.
/// The UI is first placed w.r.t to the X, Y position of the node (to which the port belongs),
/// then offset from that based on the port, the width of the UI itself and in some cases, the node width.
/// Sets up the node autocomplete window to be placed relative to the node.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
internal void PlaceNodeAutocompleteWindow(object sender, EventArgs e)
/// <param name="popup">Node autocomplete popup.</param>
internal void SetupNodeAutocompleteWindowPlacement(Popup popup)
{
var control = sender as NodeAutoCompleteSearchControl;
var popup = control.Parent as Popup;

_node.OnRequestAutoCompletePopupPlacementTarget(popup);
popup.CustomPopupPlacementCallback = PlaceAutocompletePopup;
}

private CustomPopupPlacement[] PlaceAutocompletePopup(Size popupSize, Size targetSize, Point offset)
{
var zoom = _node.WorkspaceViewModel.Zoom;

double x;
var scaledSpacing = autocompletePopupSpacing * targetSize.Width / _node.ActualWidth;
if (PortModel.PortType == PortType.Input)
{
// Offset popup to the left by its width from left edge of node and constant spacing.
// Note: MinWidth property of the control is set to a constant value in the XAML
// for the ActualWidth to return a consistent value.
x = -autocompleteUISpacing - control.ActualWidth / zoom;
// Offset popup to the left by its width from left edge of node and spacing.
x = -scaledSpacing - popupSize.Width;
}
else
{
// Offset popup to the right by node width from left edge of node.
x = autocompleteUISpacing + PortModel.Owner.Width;
// Offset popup to the right by node width and spacing from left edge of node.
x = scaledSpacing + targetSize.Width;
}
// Offset popup down from the upper edge of the node by the node header and corresponding to the respective port.
var y = NodeModel.HeaderHeight + PortModel.Index * PortModel.Height;
popup.PlacementRectangle = new Rect(x, y, 0, 0);
// Scale the absolute heights by the target height (passed to the callback) and the actual height of the node.
var scaledHeight = targetSize.Height / _node.ActualHeight;
var absoluteHeight = NodeModel.HeaderHeight + PortModel.Index * PortModel.Height;
var y = absoluteHeight * scaledHeight;

var placement = new CustomPopupPlacement(new Point(x, y), PopupPrimaryAxis.None);

return new[] { placement };
}

private void Workspace_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
Expand Down Expand Up @@ -406,7 +408,7 @@ private bool CanConnect(object parameter)
private void AutoComplete(object parameter)
{
var wsViewModel = _node.WorkspaceViewModel;
var svm = wsViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel;
var svm = wsViewModel.NodeAutoCompleteSearchViewModel;
svm.PortViewModel = this;

wsViewModel.OnRequestNodeAutoCompleteSearch(ShowHideFlags.Show);
Expand Down
2 changes: 1 addition & 1 deletion src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public DynamoPreferencesData DynamoPreferences
/// ViewModel that is used in NodeAutoComplete feature in context menu and called by Shift+DoubleClick.
/// </summary>
[JsonIgnore]
public SearchViewModel NodeAutoCompleteSearchViewModel { get; private set; }
public NodeAutoCompleteSearchViewModel NodeAutoCompleteSearchViewModel { get; private set; }

/// <summary>
/// Cursor Property Binding for WorkspaceView
Expand Down
3 changes: 3 additions & 0 deletions src/DynamoCoreWpf/Views/Core/NodeView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ private void CachedValueChanged()
private void ViewModel_RequestAutoCompletePopupPlacementTarget(Popup popup)
{
popup.PlacementTarget = this;

ViewModel.ActualHeight = ActualHeight;
ViewModel.ActualWidth = ActualWidth;
}

void ViewModel_RequestsSelection(object sender, EventArgs e)
Expand Down
1 change: 1 addition & 0 deletions src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@
StaysOpen="True"
AllowsTransparency="True"
IsOpen="False"
Placement="Custom"
DataContext="{Binding NodeAutoCompleteSearchViewModel}">
<ui:NodeAutoCompleteSearchControl RequestShowNodeAutoCompleteSearch="ShowHideNodeAutoCompleteControl" />
</Popup>
Expand Down
13 changes: 12 additions & 1 deletion src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Notes;
using Dynamo.Graph.Workspaces;
using Dynamo.Logging;
using Dynamo.Models;
using Dynamo.Search.SearchElements;
using Dynamo.Selection;
using Dynamo.UI;
using Dynamo.UI.Controls;
using Dynamo.Utilities;
using Dynamo.ViewModels;
using Dynamo.Wpf.UI;
Expand Down Expand Up @@ -186,7 +188,16 @@ private void ShowHidePopup(ShowHideFlags flag, Popup popup)
break;
case ShowHideFlags.Show:
// Show InCanvas search just in case, when mouse is over workspace.
popup.IsOpen = DynamoModel.IsTestMode || IsMouseOver;
var displayPopup = DynamoModel.IsTestMode || IsMouseOver;
if (displayPopup && popup == NodeAutoCompleteSearchBar)
{
if (ViewModel.NodeAutoCompleteSearchViewModel.PortViewModel == null) return;

ViewModel.NodeAutoCompleteSearchViewModel.PortViewModel.SetupNodeAutocompleteWindowPlacement(popup);
}
popup.IsOpen = displayPopup;
popup.CustomPopupPlacementCallback = null;

ViewModel.InCanvasSearchViewModel.SearchText = string.Empty;
ViewModel.InCanvasSearchViewModel.InCanvasSearchPosition = inCanvasSearchPosition;
break;
Expand Down
20 changes: 0 additions & 20 deletions test/DynamoCoreWpfTests/CoreUITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -886,26 +886,6 @@ public void WorkspaceContextMenu_IfSubmenuOpenOnMouseHover()
Assert.IsTrue(currentWs.WorkspaceLacingMenu.IsSubmenuOpen);
}

[Test]
[Category("UnitTests")]
public void ShowHideNodeAutoCompleteSearchControl()
{
var currentWs = View.ChildOfType<WorkspaceView>();

// Show Node AutoCompleteSearchBar
ViewModel.CurrentSpaceViewModel.OnRequestNodeAutoCompleteSearch(ShowHideFlags.Show);
Assert.IsTrue(currentWs.NodeAutoCompleteSearchBar.IsOpen);

RightClick(currentWs.zoomBorder);
// Notice AutoCompleteSearchBar can co-exist with right click search for now
Assert.IsTrue(currentWs.ContextMenuPopup.IsOpen);
Assert.IsTrue(currentWs.NodeAutoCompleteSearchBar.IsOpen);

// Hide Node AutoCompleteSearchBar
ViewModel.CurrentSpaceViewModel.OnRequestNodeAutoCompleteSearch(ShowHideFlags.Hide);
Assert.IsFalse(currentWs.NodeAutoCompleteSearchBar.IsOpen);
}

private void RightClick(IInputElement element)
{
element.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Right)
Expand Down
23 changes: 17 additions & 6 deletions test/DynamoCoreWpfTests/NodeAutoCompleteSearchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using Dynamo.Graph.Nodes;
using Dynamo.Models;
using Dynamo.Search.SearchElements;
using Dynamo.Utilities;
using Dynamo.ViewModels;
using Dynamo.Views;
using DynamoCoreWpfTests.Utility;
using NUnit.Framework;

Expand Down Expand Up @@ -78,7 +80,7 @@ public void NodeSuggestions_InputPortZeroTouchNode_AreCorrect()
type = port.GetInputPortType();
Assert.AreEqual("FFITarget.DummyVector", type);

var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = inPorts[1];
var suggestions = searchViewModel.GetMatchingSearchElements();
Assert.AreEqual(5, suggestions.Count());
Expand All @@ -91,6 +93,15 @@ public void NodeSuggestions_InputPortZeroTouchNode_AreCorrect()
{
Assert.AreEqual(expectedNodes.ElementAt(i), suggestedNodes.ElementAt(i));
}

// Show Node AutoCompleteSearchBar
ViewModel.CurrentSpaceViewModel.OnRequestNodeAutoCompleteSearch(ShowHideFlags.Show);
var currentWs = View.ChildOfType<WorkspaceView>();
Assert.IsTrue(currentWs.NodeAutoCompleteSearchBar.IsOpen);

// Hide Node AutoCompleteSearchBar
ViewModel.CurrentSpaceViewModel.OnRequestNodeAutoCompleteSearch(ShowHideFlags.Hide);
Assert.IsFalse(currentWs.NodeAutoCompleteSearchBar.IsOpen);
}

[Test]
Expand All @@ -108,7 +119,7 @@ public void NodeSuggestions_InputPortZeroTouchNodeForProperty_AreCorrect()
var type = port.GetInputPortType();
Assert.AreEqual("FFITarget.ClassFunctionality", type);

var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = inPorts[0];
var suggestions = searchViewModel.GetMatchingSearchElements();
Assert.AreEqual(4, suggestions.Count());
Expand All @@ -134,7 +145,7 @@ public void NodeSuggestions_GeometryNodes_SortedBy_NodeGroup_CreateActionQuery()
node, 0, 0, true, false));
DispatcherUtil.DoEvents();
var nodeView = NodeViewWithGuid(node.GUID.ToString());
var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = nodeView.ViewModel.InPorts.FirstOrDefault();

var suggestions = searchViewModel.GetMatchingSearchElements();
Expand Down Expand Up @@ -163,7 +174,7 @@ public void NodeSuggestions_InputPortBuiltInNode_AreCorrect()
type = port.GetInputPortType();
Assert.AreEqual("string", type);

var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = inPorts[0];
var suggestions = searchViewModel.GetMatchingSearchElements();
Assert.AreEqual(0, suggestions.Count());
Expand Down Expand Up @@ -240,7 +251,7 @@ public void NodeSuggestions_DefaultSuggestions()
var type = port.GetInputPortType();
Assert.AreEqual("DSCore.Color[]", type);

var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = inPorts[0];

// Running the default algorithm should return no suggestions
Expand All @@ -267,7 +278,7 @@ public void NodeSuggestions_SkippedSuggestions()
var type = port.GetInputPortType();
Assert.AreEqual("double", type);

var searchViewModel = (ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel as NodeAutoCompleteSearchViewModel);
var searchViewModel = ViewModel.CurrentSpaceViewModel.NodeAutoCompleteSearchViewModel;
searchViewModel.PortViewModel = inPorts[0];

// Running the algorithm against skipped nodes should return no suggestions
Expand Down

0 comments on commit 905d976

Please sign in to comment.