Skip to content

Commit

Permalink
library node search fixes, fix node creation on enter
Browse files Browse the repository at this point in the history
  • Loading branch information
chubakueno committed Dec 20, 2024
1 parent 104e63d commit f085765
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 51 deletions.
5 changes: 2 additions & 3 deletions src/DynamoCore/Search/NodeSearchModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ internal string ProcessNodeCategory(string category, ref SearchElementGroup grou

internal IEnumerable<NodeSearchElement> Search(string search, LuceneSearchUtility luceneSearchUtility)
{

if (luceneSearchUtility != null)
if (luceneSearchUtility == null) return null;
lock (luceneSearchUtility)
{
//The DirectoryReader and IndexSearcher have to be assigned after commiting indexing changes and before executing the Searcher.Search() method, otherwise new indexed info won't be reflected
luceneSearchUtility.dirReader = luceneSearchUtility.writer != null ? luceneSearchUtility.writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(luceneSearchUtility.indexDir);
Expand Down Expand Up @@ -277,7 +277,6 @@ internal IEnumerable<NodeSearchElement> Search(string search, LuceneSearchUtilit
}
return candidates;
}
return null;
}

internal NodeSearchElement FindModelForNodeNameAndCategory(string nodeName, string nodeCategory, string parameters)
Expand Down
42 changes: 28 additions & 14 deletions src/DynamoCoreWpf/Controls/IncanvasSearchControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,18 @@ private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
private void ExecuteSearchElement(ListBoxItem listBoxItem)
{
var searchElement = listBoxItem.DataContext as NodeSearchElementViewModel;
if (searchElement != null)
{
searchElement.Position = ViewModel.InCanvasSearchPosition;
searchElement.ClickedCommand?.Execute(null);
Analytics.TrackEvent(
Dynamo.Logging.Actions.Select,
Dynamo.Logging.Categories.InCanvasSearchOperations,
searchElement.FullName);
}
ExecuteSearchElement(searchElement);
}

private void ExecuteSearchElement(NodeSearchElementViewModel searchElement)
{
if (searchElement == null) return;
searchElement.Position = ViewModel.InCanvasSearchPosition;
searchElement.ClickedCommand?.Execute(null);
Analytics.TrackEvent(
Dynamo.Logging.Actions.Select,
Dynamo.Logging.Categories.InCanvasSearchOperations,
searchElement.FullName);
}

private void OnMouseEnter(object sender, MouseEventArgs e)
Expand Down Expand Up @@ -183,15 +186,26 @@ private void OnInCanvasSearchKeyDown(object sender, KeyEventArgs e)

switch (key)
{
case Key.Escape:
case Key.Escape:
OnRequestShowInCanvasSearch(ShowHideFlags.Hide);
break;
case Key.Enter:
if (HighlightedItem != null && ViewModel.CurrentMode != SearchViewModel.ViewMode.LibraryView)
ViewModel.AfterLastPendingSearch(() =>
{
ExecuteSearchElement(HighlightedItem);
OnRequestShowInCanvasSearch(ShowHideFlags.Hide);
}
Dispatcher.BeginInvoke(() =>
{
var searchElement = HighlightedItem?.DataContext as NodeSearchElementViewModel;

//if dropdown hasn't yet fully loaded lets assume the user wants the first element
searchElement ??= ViewModel.FilteredResults.FirstOrDefault();

if (searchElement != null && ViewModel.CurrentMode != SearchViewModel.ViewMode.LibraryView)
{
ExecuteSearchElement(searchElement);
OnRequestShowInCanvasSearch(ShowHideFlags.Hide);
}
});
});
break;
case Key.Up:
index = MoveToNextMember(false, members, highlightedMember);
Expand Down
3 changes: 2 additions & 1 deletion src/DynamoCoreWpf/DynamoCoreWpf.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<UILib>true</UILib>
</PropertyGroup>
Expand Down Expand Up @@ -406,6 +406,7 @@
</Compile>
<Compile Include="UI\SharedResourceDictionary.cs" />
<Compile Include="Utilities\PreferencesPanelUtilities.cs" />
<Compile Include="Utilities\SerialQueue.cs" />
<Compile Include="Utilities\WebView2Utilities.cs" />
<Compile Include="ViewModels\Core\AnnotationExtension.cs" />
<Compile Include="ViewModels\Core\AnnotationViewModel.cs" />
Expand Down
12 changes: 9 additions & 3 deletions src/DynamoCoreWpf/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2867,6 +2867,7 @@ Dynamo.ViewModels.RequestBitmapSourceHandler
Dynamo.ViewModels.RequestOpenDocumentationLinkHandler
Dynamo.ViewModels.RequestPackagePublishDialogHandler
Dynamo.ViewModels.SearchViewModel
Dynamo.ViewModels.SearchViewModel.AfterLastPendingSearch(System.Action action) -> void
Dynamo.ViewModels.SearchViewModel.BrowserRootCategories.get -> System.Collections.ObjectModel.ObservableCollection<Dynamo.Wpf.ViewModels.NodeCategoryViewModel>
Dynamo.ViewModels.SearchViewModel.BrowserVisibility.get -> bool
Dynamo.ViewModels.SearchViewModel.BrowserVisibility.set -> void
Expand Down Expand Up @@ -3674,11 +3675,16 @@ Dynamo.Wpf.Utilities.GroupStyleItemSelector.GroupStyleItemSelector() -> void
Dynamo.Wpf.Utilities.JobDebouncer
Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken
Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken.DebounceQueueToken() -> void
Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken.IsDirty -> bool
Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken.LastExecutionId -> long
Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken.SerialQueue -> Dynamo.Wpf.Utilities.SerialQueue
Dynamo.Wpf.Utilities.LibraryDragAndDrop
Dynamo.Wpf.Utilities.LibraryDragAndDrop.LibraryDragAndDrop() -> void
Dynamo.Wpf.Utilities.MessageBoxService
Dynamo.Wpf.Utilities.MessageBoxService.MessageBoxService() -> void
Dynamo.Wpf.Utilities.SerialQueue
Dynamo.Wpf.Utilities.SerialQueue.DispatchAsync(System.Action action) -> void
Dynamo.Wpf.Utilities.SerialQueue.SerialQueue() -> void
Dynamo.Wpf.Utilities.SerialQueue.UnhandledException -> System.Action<System.Action, System.Exception>
Dynamo.Wpf.Utilities.WebView2Utilities
Dynamo.Wpf.VariableInputNodeViewCustomization
Dynamo.Wpf.VariableInputNodeViewCustomization.Dispose() -> void
Expand Down Expand Up @@ -4344,7 +4350,6 @@ readonly Dynamo.ViewModels.PortViewModel.node -> Dynamo.ViewModels.NodeViewModel
readonly Dynamo.ViewModels.PortViewModel.port -> Dynamo.Graph.Nodes.PortModel
readonly Dynamo.ViewModels.WorkspaceViewModel.Model -> Dynamo.Graph.Workspaces.WorkspaceModel
readonly Dynamo.Wpf.Extensions.ViewLoadedParams.dynamoMenu -> System.Windows.Controls.Menu
readonly Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken.JobLock -> object
readonly Dynamo.Wpf.ViewModels.Watch3D.DefaultWatch3DViewModel.dynamoModel -> Dynamo.Interfaces.IDynamoModel
readonly Dynamo.Wpf.ViewModels.Watch3D.DefaultWatch3DViewModel.engineManager -> Dynamo.Models.IEngineControllerManager
readonly Dynamo.Wpf.ViewModels.Watch3D.DefaultWatch3DViewModel.logger -> Dynamo.Logging.ILogger
Expand Down Expand Up @@ -5619,7 +5624,8 @@ static Dynamo.Wpf.UI.VisualConfigurations.ErrorTextFontWeight -> System.Windows.
static Dynamo.Wpf.UI.VisualConfigurations.LibraryTooltipTextFontWeight -> System.Windows.FontWeight
static Dynamo.Wpf.UI.VisualConfigurations.NodeTooltipTextFontWeight -> System.Windows.FontWeight
static Dynamo.Wpf.Utilities.CompactBubbleHandler.Process(ProtoCore.Mirror.MirrorData value) -> Dynamo.ViewModels.CompactBubbleViewModel
static Dynamo.Wpf.Utilities.JobDebouncer.EnqueueJobAsync(System.Action job, Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken token) -> System.Threading.Tasks.Task
static Dynamo.Wpf.Utilities.JobDebouncer.EnqueueMandatoryJobAsync(System.Action job, Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken token) -> void
static Dynamo.Wpf.Utilities.JobDebouncer.EnqueueOptionalJobAsync(System.Action job, Dynamo.Wpf.Utilities.JobDebouncer.DebounceQueueToken token) -> void
static Dynamo.Wpf.Utilities.MessageBoxService.Show(string msg, string title, bool showRichTextBox, System.Windows.MessageBoxButton button, System.Windows.MessageBoxImage img) -> System.Windows.MessageBoxResult
static Dynamo.Wpf.Utilities.MessageBoxService.Show(string msg, string title, System.Windows.MessageBoxButton button, System.Collections.Generic.IEnumerable<string> buttonNames, System.Windows.MessageBoxImage img) -> System.Windows.MessageBoxResult
static Dynamo.Wpf.Utilities.MessageBoxService.Show(string msg, string title, System.Windows.MessageBoxButton button, System.Windows.MessageBoxImage img) -> System.Windows.MessageBoxResult
Expand Down
37 changes: 22 additions & 15 deletions src/DynamoCoreWpf/Utilities/JobDebouncer.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
using System;
using System.Threading.Tasks;

namespace Dynamo.Wpf.Utilities
{
public static class JobDebouncer
{
public class DebounceQueueToken
{
public bool IsDirty = false;
public readonly object JobLock = new();
public long LastExecutionId = 0;
public SerialQueue SerialQueue = new();
};
/// <summary>
/// Action <paramref name="job"/> is guaranteed to run at most once for every call, and exactly once after the last call.
/// Execution is sequential, and jobs that share a <see cref="DebounceQueueToken"/> with a newer job will be ignored.
/// Execution is sequential, and optional jobs that share a <see cref="DebounceQueueToken"/> with a newer optional job will be ignored.
/// </summary>
/// <param name="job"></param>
/// <param name="token"></param>
/// <returns></returns>
public static Task EnqueueJobAsync(Action job, DebounceQueueToken token)
public static void EnqueueOptionalJobAsync(Action job, DebounceQueueToken token)
{
token.IsDirty = true;
return Task.Run(() =>
lock (token)
{
lock (token.JobLock)
token.LastExecutionId++;
var myExecutionId = token.LastExecutionId;
token.SerialQueue.DispatchAsync(() =>
{
while (token.IsDirty)
{
token.IsDirty = false;
job();
}
}
});
if (myExecutionId < token.LastExecutionId) return;
job();
});
}
}
public static void EnqueueMandatoryJobAsync(Action job, DebounceQueueToken token)
{
lock (token)
{
token.SerialQueue.DispatchAsync(() =>
{
job();
});
}
}
}
}
80 changes: 80 additions & 0 deletions src/DynamoCoreWpf/Utilities/SerialQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//https://github.com/gentlee/SerialQueue/blob/master/SerialQueue/SerialQueue.cs
using System;
using System.Threading;

namespace Dynamo.Wpf.Utilities
{
public class SerialQueue
{
class LinkedListNode(Action action)
{
public readonly Action Action = action;
public LinkedListNode Next;
}

public event Action<Action, Exception> UnhandledException = delegate { };

private LinkedListNode _queueFirst;
private LinkedListNode _queueLast;
private bool _isRunning = false;

public void DispatchAsync(Action action)
{
var newNode = new LinkedListNode(action);

lock (this)
{
if (_queueFirst == null)
{
_queueFirst = newNode;
_queueLast = newNode;

if (!_isRunning)
{
_isRunning = true;
ThreadPool.QueueUserWorkItem(Run);
}
}
else
{
_queueLast!.Next = newNode;
_queueLast = newNode;
}
}
}

private void Run(object _)
{
while (true)
{
LinkedListNode firstNode;

lock (this)
{
if (_queueFirst == null)
{
_isRunning = false;
return;
}
firstNode = _queueFirst;
_queueFirst = null;
_queueLast = null;
}

while (firstNode != null)
{
var action = firstNode.Action;
firstNode = firstNode.Next;
try
{
action();
}
catch (Exception error)
{
UnhandledException.Invoke(action, error);
}
}
}
}
}
}
13 changes: 6 additions & 7 deletions src/DynamoCoreWpf/ViewModels/Search/SearchViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -863,12 +863,6 @@ private static string MakeFullyQualifiedName(string path, string addition)
/// </summary>
internal void SearchAndUpdateResults()
{
//Unit tests don't have an Application.Current
(Application.Current?.Dispatcher ?? Dispatcher.CurrentDispatcher).Invoke(() =>
{
searchResults.Clear();
});

if (!String.IsNullOrEmpty(SearchText.Trim()))
{
SearchAndUpdateResults(SearchText);
Expand Down Expand Up @@ -1178,7 +1172,12 @@ public void OnSearchElementClicked(NodeModel nodeModel, Point position)
private static readonly JobDebouncer.DebounceQueueToken DebounceQueueToken = new();
public void Search(object parameter)
{
JobDebouncer.EnqueueJobAsync(SearchAndUpdateResults, DebounceQueueToken);
JobDebouncer.EnqueueOptionalJobAsync(SearchAndUpdateResults, DebounceQueueToken);
}

public void AfterLastPendingSearch(Action action)
{
JobDebouncer.EnqueueMandatoryJobAsync(action, DebounceQueueToken);
}

internal bool CanSearch(object parameter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public SearchResultDataProvider(NodeSearchModel model, IconResourceProvider icon
public override Stream GetResource(string searchText, out string extension)
{
var text = Uri.UnescapeDataString(searchText);
var elements = model.Search(text, LuceneSearch.LuceneUtilityNodeSearch);
var elements = string.IsNullOrWhiteSpace(text) ? new List<NodeSearchElement>() : model.Search(text, LuceneSearch.LuceneUtilityNodeSearch);
extension = "json";
return GetNodeItemDataStream(elements, true);
}
Expand Down
9 changes: 8 additions & 1 deletion src/LibraryViewExtensionWebView2/LibraryViewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,17 @@ private void Browser_KeyDown(object sender, KeyEventArgs e)

var synteticEventData = new Dictionary<string, string>
{
[Enum.GetName(typeof(ModifiersJS), e.KeyboardDevice.Modifiers)] = "true",
["key"] = e.Key.ToString()
};

foreach(ModifiersJS modifier in Enum.GetValues(typeof(ModifiersJS)))
{
if (((int)e.KeyboardDevice.Modifiers & (int)modifier) != 0)
{
synteticEventData[Enum.GetName(typeof(ModifiersJS), modifier)] = "true";
}
}

_ = ExecuteScriptFunctionAsync(browser, "eventDispatcher", synteticEventData);
}

Expand Down
25 changes: 19 additions & 6 deletions src/LibraryViewExtensionWebView2/ScriptingObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Threading;
using Dynamo.Wpf.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static Dynamo.Wpf.Utilities.JobDebouncer;

namespace Dynamo.LibraryViewExtensionWebView2
{
Expand Down Expand Up @@ -46,6 +50,8 @@ public string GetBase64StringFromPath(string iconurl)
return $"data:image/{ext};base64, {iconAsBase64}";
}

private static readonly JobDebouncer.DebounceQueueToken DebounceQueueToken = new();

/// <summary>
/// This method will receive any message sent from javascript and execute a specific code according to the message
/// </summary>
Expand Down Expand Up @@ -98,12 +104,19 @@ internal void Notify(string dataFromjs)
{
var data = simpleRPCPayload["data"] as string;
var extension = string.Empty;
var searchStream = controller.searchResultDataProvider.GetResource(data, out extension);
var searchReader = new StreamReader(searchStream);
var results = searchReader.ReadToEnd();
//send back results to librarie.js
LibraryViewController.ExecuteScriptFunctionAsync(controller.browser, "completeSearch", results);
searchReader.Dispose();

var dispatcher = Application.Current?.Dispatcher ?? Dispatcher.CurrentDispatcher;
JobDebouncer.EnqueueOptionalJobAsync(() => {
var searchStream = controller.searchResultDataProvider.GetResource(data, out extension);
var searchReader = new StreamReader(searchStream);
var results = searchReader.ReadToEnd();
dispatcher.Invoke(() =>
{
//send back results to librarie.js
LibraryViewController.ExecuteScriptFunctionAsync(controller.browser, "completeSearch", results);
searchReader.Dispose();
});
}, DebounceQueueToken);
}
//When the html <div> that contains the sample package is clicked then we will be moved to the next Step in the Guide
else if (funcName == "NextStep")
Expand Down

0 comments on commit f085765

Please sign in to comment.