-
Notifications
You must be signed in to change notification settings - Fork 635
/
LibraryViewController.cs
747 lines (648 loc) · 30.2 KB
/
LibraryViewController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using CoreNodeModels.Properties;
using Dynamo.Extensions;
using Dynamo.LibraryViewExtensionWebView2.Handlers;
using Dynamo.LibraryViewExtensionWebView2.ViewModels;
using Dynamo.LibraryViewExtensionWebView2.Views;
using Dynamo.Models;
using Dynamo.Search;
using Dynamo.Search.SearchElements;
using Dynamo.ViewModels;
using Dynamo.Wpf.Interfaces;
using Dynamo.Wpf.UI.GuidedTour;
using Dynamo.Wpf.Utilities;
using Dynamo.Wpf.ViewModels;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using Newtonsoft.Json;
namespace Dynamo.LibraryViewExtensionWebView2
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[ComVisibleAttribute(true)]
/// <summary>
/// This is for the object we're gonna host exposing the functions
/// for clipboard management to React component
/// </summary>
public class ScriptObject
{
Action<string> onCopyToClipboard;
Func<string> onPasteFromClipboard;
internal ScriptObject(Action<string> onCopyToClipboard, Func<string> onPasteFromClipboard)
{
this.onCopyToClipboard = onCopyToClipboard;
this.onPasteFromClipboard = onPasteFromClipboard;
}
/// <summary>
/// This is the function we expose for adding a string to the clipboard
/// In React component will be accesible from chrome.webview.hostObjects.scriptObject.CopyToClipboard(text)
/// </summary>
/// <param name="text">text to be added to the clipboard</param>
public void CopyToClipboard(string text)
{
onCopyToClipboard(text);
}
/// <summary>
/// This is the function we expose for paste a string from the clipboard
/// In React component will be accesible from chrome.webview.hostObjects.scriptObject.PasteFromClipboard();
/// </summary>
public string PasteFromClipboard()
{
return onPasteFromClipboard();
}
}
public class LibraryViewController : IDisposable
{
private Window dynamoWindow;
private ICommandExecutive commandExecutive;
private DynamoViewModel dynamoViewModel;
private FloatingLibraryTooltipPopup libraryViewTooltip;
// private ResourceHandlerFactory resourceFactory;
private IDisposable observer;
internal DynamoWebView2 browser;
ScriptingObject twoWayScriptingObject;
private const string CreateNodeInstrumentationString = "Search-NodeAdded";
// TODO remove this when we can control the library state from Dynamo more precisely.
private bool disableObserver = false;
private LayoutSpecProvider layoutProvider;
private NodeItemDataProvider nodeProvider;
internal SearchResultDataProvider searchResultDataProvider;
internal IconResourceProvider iconProvider;
private LibraryViewCustomization customization;
internal string WebBrowserUserDataFolder { get; set; }
//Assuming that the fon size is 14px and the screen height is 1080 initially
private const int standardFontSize = 14;
private const int standardScreenHeight = 1080;
private double libraryFontSize;
private const double minimumZoomScale = 0.25d;
/// <summary>
/// Creates a LibraryViewController.
/// </summary>
/// <param name="dynamoView">DynamoView hosting library component</param>
/// <param name="commandExecutive">Command executive to run dynamo commands</param>
internal LibraryViewController(Window dynamoView, ICommandExecutive commandExecutive, LibraryViewCustomization customization)
{
this.dynamoWindow = dynamoView;
dynamoViewModel = dynamoView.DataContext as DynamoViewModel;
this.customization = customization;
libraryViewTooltip = CreateTooltipControl();
this.commandExecutive = commandExecutive;
InitializeResourceProviders(dynamoViewModel.Model, customization);
dynamoWindow.StateChanged += DynamoWindowStateChanged;
dynamoWindow.SizeChanged += DynamoWindow_SizeChanged;
dynamoViewModel.PreferencesWindowChanged += DynamoViewModel_PreferencesWindowChanged;
DirectoryInfo webBrowserUserDataFolder;
var userDataDir = new DirectoryInfo(dynamoViewModel.Model.PathManager.UserDataDirectory);
webBrowserUserDataFolder = userDataDir.Exists ? userDataDir : null;
if (webBrowserUserDataFolder != null)
{
WebBrowserUserDataFolder = webBrowserUserDataFolder.FullName;
}
/// Create and add the library view to the WPF visual tree.
/// Also load the library.html and js files.
LibraryViewModel model = new LibraryViewModel();
LibraryView view = new LibraryView(model);
//Adding the LibraryView to the sidebar ensures that the webview2 component is visible.
var sidebarGrid = dynamoWindow.FindName("sidebarGrid") as Grid;
sidebarGrid.Children.Add(view);
browser = view.mainGrid.Children.OfType<DynamoWebView2>().FirstOrDefault();
browser.Loaded += Browser_Loaded;
browser.SizeChanged += Browser_SizeChanged;
LibraryViewController.SetupSearchModelEventsObserver(browser, dynamoViewModel.Model.SearchModel,
this, this.customization);
}
private void DynamoViewModel_PreferencesWindowChanged(object sender, EventArgs e)
{
var preferencesView = (Wpf.Views.PreferencesView)sender;
preferencesView.LibraryZoomScalingSlider.ValueChanged += DynamoSliderValueChanged;
}
private void Browser_ZoomFactorChanged(object sender, EventArgs e)
{
//Multiplies by 100 so the value can be saved as a percentage
dynamoViewModel.Model.PreferenceSettings.LibraryZoomScale = (int)(browser.ZoomFactor * 100);
}
//if the window is resized toggle visibility of browser to force redraw
private void DynamoWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (browser != null)
{
browser.InvalidateVisual();
}
}
//if the dynamo window is minimized and then restored, force a layout update.
private void DynamoWindowStateChanged(object sender, EventArgs e)
{
if (browser != null)
{
browser.InvalidateVisual();
}
}
#region methods exposed to JS
/// <summary>
/// Call this method to create a new node in Dynamo canvas.
/// </summary>
/// <param name="nodeName">Node creation name</param>
public void CreateNode(string nodeName)
{
dynamoWindow.Dispatcher.BeginInvoke(new Action(() =>
{
//if the node we're trying to create is a customNode, lets disable the eventObserver.
// this will stop the libraryController from refreshing the libraryView on custom node creation.
var resultGuid = Guid.Empty;
if (Guid.TryParse(nodeName, out resultGuid))
{
this.disableObserver = true;
}
//Create the node of given item name
var cmd = new DynamoModel.CreateNodeCommand(Guid.NewGuid().ToString(), nodeName, -1, -1, true, false);
commandExecutive.ExecuteCommand(cmd, Guid.NewGuid().ToString(), LibraryViewExtensionWebView2.ExtensionName);
this.disableObserver = false;
}));
}
/// Call this method to import a zero touch library. It will prompt
/// user to select the zero touch dll.
/// </summary>
public void ImportLibrary()
{
dynamoWindow.Dispatcher.BeginInvoke(new Action(() =>
dynamoViewModel.ImportLibraryCommand.Execute(null)
));
}
/// <summary>
/// This method will read the layoutSpecs.json and layoutType and then pass the info to javacript so all the resources can be loaded
/// </summary>
/// <param name="browser"></param>
internal void RefreshLibraryView(WebView2 browser)
{
string layoutSpecsjson = String.Empty;
string loadedTypesjson = String.Empty;
if (!dynamoViewModel.Model.IsServiceMode)
{
dynamoWindow.Dispatcher.BeginInvoke(
new Action(() =>
{
var ext1 = string.Empty;
var ext2 = string.Empty;
var reader = new StreamReader(nodeProvider.GetResource(null, out ext1));
var loadedTypes = reader.ReadToEnd();
var reader2 = new StreamReader(layoutProvider.GetResource(null, out ext2));
var layoutSpec = reader2.ReadToEnd();
ExecuteScriptFunctionAsync(browser, "refreshLibViewFromData", loadedTypes, layoutSpec);
}));
}
}
#endregion
/// <summary>
/// This function will copy a string to clipboard
/// </summary>
/// <param name="text">text to be added to clipboard</param>
internal void OnCopyToClipboard(string text)
{
Clipboard.SetText(text);
}
/// <summary>
/// This function will return the clipboard content
/// </summary>
internal string OnPasteFromClipboard() {
return Clipboard.GetText();
}
private string ReplaceUrlWithBase64Image(string html, string minifiedURL, bool magicreplace = true)
{
var ext = string.Empty;
var magicstringprod = "__webpack_require__.p+";
var minifiedURLHtmlReplacement = minifiedURL;
// this depends on librariejs minification producing the same results -
// longterm this is fragile. We should intercept these requests and handle them instead.
if (magicreplace)
{
minifiedURL = $"{magicstringprod}\"{minifiedURL}\"";
}
var searchString = minifiedURL.Replace(magicstringprod, @"./dist").Replace("\"", "");
var base64 = iconProvider.GetResourceAsString(searchString, out ext);
if (string.IsNullOrEmpty(base64))
{
throw new Exception($"could not find resource {searchString}");
}
//replace some urls to svg icons with base64 data.
if (ext == "svg")
{
ext = "svg+xml";
base64 = $"data:image/{ext};base64, {base64}";
}
if (ext == "ttf" || ext == "woff" || ext == "woff2" || ext == "eot")
{
base64 = $"data:application/x-font-{ext};charset=utf-8;base64,{base64}";
}
//In Libraryjs project, Webpack5 is removing the initial slash "/" when using resource files so for example in the html we have the string like "/resources/image.svg" then we need to remove the first slash (the first char) otherwise it won't be found and replaced by the base64 content.
html = html.Replace(minifiedURL.Replace(minifiedURLHtmlReplacement, minifiedURLHtmlReplacement.TrimStart('/')), '"' + base64 + '"');
return html;
}
//list of resources which have paths embedded directly into the source.
private readonly Tuple<string, bool>[] dynamicResourcePaths = new Tuple<string, bool>[]
{
Tuple.Create("/resources/ArtifaktElement-Bold.woff",true),
Tuple.Create("/resources/ArtifaktElement-Regular.woff",true),
Tuple.Create("/resources/bin.svg",true),
Tuple.Create("/resources/default-icon.svg",true),
Tuple.Create("/resources/library-action.svg",true),
Tuple.Create("/resources/library-create.svg",true),
Tuple.Create("/resources/library-query.svg",true),
Tuple.Create("/resources/indent-arrow-category-down.svg",true),
Tuple.Create("/resources/indent-arrow-category-right.svg",true),
Tuple.Create("/resources/indent-arrow-down.svg",true),
Tuple.Create("/resources/indent-arrow-right.svg",true),
Tuple.Create("/resources/plus-symbol.svg",true),
Tuple.Create("/resources/search-detailed.svg",true),
Tuple.Create("/resources/search-filter.svg",true),
Tuple.Create("/resources/search-filter-selected.svg",true),
Tuple.Create("/resources/search-icon.svg",true),
Tuple.Create("/resources/search-icon-clear.svg",true)
};
async void InitializeAsync()
{
try
{
var absolutePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
@"runtimes\win-x64\native");
CoreWebView2Environment.SetLoaderDllFolderPath(absolutePath);
}
catch (InvalidOperationException e)
{
LogToDynamoConsole("WebView2Loader.dll is already loaded successfully.");
}
browser.CoreWebView2InitializationCompleted += Browser_CoreWebView2InitializationCompleted;
if (!string.IsNullOrEmpty(WebBrowserUserDataFolder))
{
//This indicates in which location will be created the WebView2 cache folder
this.browser.CreationProperties = new CoreWebView2CreationProperties()
{
UserDataFolder = WebBrowserUserDataFolder
};
}
await browser.EnsureCoreWebView2Async();
this.browser.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
twoWayScriptingObject = new ScriptingObject(this);
//register the interop object into the browser.
this.browser.CoreWebView2.AddHostObjectToScript("bridgeTwoWay", twoWayScriptingObject);
browser.CoreWebView2.Settings.IsZoomControlEnabled = true;
}
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args)
{
String strMessage = args.TryGetWebMessageAsString();
twoWayScriptingObject.Notify(strMessage);
}
private void Browser_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
{
LibraryViewModel model = new LibraryViewModel();
LibraryView view = new LibraryView(model);
var lib_min_template = "LIBPLACEHOLDER";
var libHTMLURI = "Dynamo.LibraryViewExtensionWebView2.web.library.library.html";
var stream = LoadResource(libHTMLURI);
var libMinURI = "Dynamo.LibraryViewExtensionWebView2.web.library.librarie.min.js";
var libminstream = LoadResource(libMinURI);
var libminstring = "LIBJS";
var libraryHTMLPage = "LIBRARY HTML WAS NOT FOUND";
using (var reader = new StreamReader(libminstream))
{
libminstring = reader.ReadToEnd();
// replace some resources and their paths so that no requests are generated
// instead the base64 data is already embedded. This list includes common icons and fonts.
dynamicResourcePaths.ToList().ForEach(path =>
{
libminstring = ReplaceUrlWithBase64Image(libminstring, path.Item1, path.Item2);
});
}
using (var reader = new StreamReader(stream))
{
libraryHTMLPage = reader.ReadToEnd().Replace(lib_min_template, libminstring);
}
try
{
this.browser.NavigateToString(libraryHTMLPage);
}
catch (Exception ex)
{
string msg = ex.Message;
}
SetLibraryFontSize();
SetTooltipText();
//The default value of the zoom factor is 1.0. The value that comes from the slider is in percentage, so we divide by 100 to be equivalent
double zoomFactor = ((double)dynamoViewModel.Model.PreferenceSettings.LibraryZoomScale / 100d);
//The default value of the zoom factor is 1.0. The value that comes from the slider is in percentage, so we divide by 100 to be equivalent
browser.ZoomFactor = (double)dynamoViewModel.Model.PreferenceSettings.LibraryZoomScale / 100;
browser.ZoomFactorChanged += Browser_ZoomFactorChanged;
browser.KeyDown += Browser_KeyDown;
// Hosts an object that will expose the properties and methods to be called from the javascript side
browser.CoreWebView2.AddHostObjectToScript("scriptObject",
new ScriptObject(OnCopyToClipboard, OnPasteFromClipboard));
}
private void Browser_Loaded(object sender, RoutedEventArgs e)
{
string msg = "Browser Loaded";
LogToDynamoConsole(msg);
InitializeAsync();
}
// This enum is for matching the modifier keys between C# and javaScript
enum ModifiersJS
{
none = 0,
altKey = 1,
ctrlKey = 2,
shiftKey = 4
}
// This enum is for define the events to be tracked
enum EventsTracked
{
Delete,
A,
C,
V
}
/// <summary>
/// Collect the main and modifier key from KeyEventArgs in order to pass
/// that data to eventDispatcher (located in library.html) which is responsible
/// for binding KeyDown events between dynamo and webview instances
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
private void Browser_KeyDown(object sender, KeyEventArgs e)
{
if (!Enum.IsDefined(typeof(EventsTracked), e.Key.ToString())) return;
var synteticEventData = new Dictionary<string, string>
{
[Enum.GetName(typeof(ModifiersJS), e.KeyboardDevice.Modifiers)] = "true",
["key"] = e.Key.ToString()
};
_ = ExecuteScriptFunctionAsync(browser, "eventDispatcher", synteticEventData);
}
//if the browser window itself is resized, toggle visibility to force redraw.
private void Browser_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (browser != null)
{
browser.InvalidateVisual();
UpdatePopupLocation();
SetLibraryFontSize();
}
}
//Changes the size of the font's library depending of the screen height
private async void SetLibraryFontSize()
{
//Gets the height of the primary monitor
var height = SystemParameters.PrimaryScreenHeight;
//Calculates the proportion of the font size depending on the screen height
//Changing the scale also changes the screen height (F.E: height of 1080px with 150% will be actually 720px)
var fontSize = (standardFontSize * height) / standardScreenHeight;
if(fontSize != libraryFontSize)
{
var result = await ExecuteScriptFunctionAsync(browser, "setLibraryFontSize", fontSize);
if(result != null)
libraryFontSize = fontSize;
}
}
private async void SetTooltipText()
{
var jsonTooltipText = new { create = Resources.TooltipTextCreate, action = Resources.TooltipTextAction, query = Resources.TooltipTextQuery };
var jsonString = JsonConvert.SerializeObject(jsonTooltipText);
var result = await ExecuteScriptFunctionAsync(browser, "setTooltipText", jsonString);
}
#region Tooltip
/// <summary>
/// Call this method to create a new node in Dynamo canvas.
/// </summary>
/// <param name="nodeName">Node creation name</param>
/// <param name="y">The y position</param>
public void ShowNodeTooltip(string nodeName, double y)
{
dynamoWindow.Dispatcher.BeginInvoke(new Action(() =>
{
ShowTooltip(nodeName, y);
}));
}
/// <summary>
/// Call this method to create a new node in Dynamo canvas.
/// </summary>
public void CloseNodeTooltip(bool closeImmediately)
{
dynamoWindow.Dispatcher.BeginInvoke(new Action(() =>
{
CloseTooltip(closeImmediately);
//The packages installed are shown at this moment then we need to update the Popup location and the interactions with the Library
if (GuideFlowEvents.IsAnyGuideActive)
{
GuideFlowEvents.OnUpdatePopupLocation();
GuideFlowEvents.OnUpdateLibraryInteractions();
}
}));
}
private FloatingLibraryTooltipPopup CreateTooltipControl()
{
var sidebarGrid = dynamoWindow.FindName("sidebarGrid") as Grid;
var tooltipPopup = new FloatingLibraryTooltipPopup(200)
{
Name = "libraryToolTipPopup",
StaysOpen = true,
AllowsTransparency = true,
PlacementTarget = sidebarGrid
};
sidebarGrid.Children.Add(tooltipPopup);
return tooltipPopup;
}
private NodeSearchElementViewModel FindTooltipContext(String nodeName)
{
return dynamoViewModel.CurrentSpaceViewModel.InCanvasSearchViewModel.FindViewModelForNode(nodeName);
}
private void ShowTooltip(String nodeName, double y)
{
var nseViewModel = FindTooltipContext(nodeName);
if (nseViewModel == null)
{
return;
}
libraryViewTooltip.UpdateYPosition(y);
libraryViewTooltip.SetDataContext(nseViewModel);
}
private void CloseTooltip(bool closeImmediately)
{
libraryViewTooltip.SetDataContext(null, closeImmediately);
}
#endregion
private Stream LoadResource(string url)
{
var assembly = Assembly.GetExecutingAssembly();
var textStream = assembly.GetManifestResourceStream(url);
return textStream;
}
internal static IDisposable SetupSearchModelEventsObserver(WebView2 webview, NodeSearchModel model, LibraryViewController controller, ILibraryViewCustomization customization, int throttleTime = 200)
{
customization.SpecificationUpdated += (o, e) =>
{
controller.RefreshLibraryView(webview);
controller.CloseNodeTooltip(true);
};
var observer = new EventObserver<NodeSearchElement, IEnumerable<NodeSearchElement>>(
elements => NotifySearchModelUpdate(customization, elements), CollectToList
).Throttle(TimeSpan.FromMilliseconds(throttleTime));
Action<NodeSearchElement> handler = (searchElement) =>
{
var libraryViewController = (controller as LibraryViewController);
if ((libraryViewController != null) && (libraryViewController.disableObserver))
{
return;
}
observer.OnEvent(searchElement);
};
Action<NodeSearchElement> onRemove = e => handler(null);
if (model != null)
{
//Set up the event callback
model.EntryAdded += handler;
model.EntryRemoved += onRemove;
model.EntryUpdated += handler;
//Set up the dispose event handler
observer.Disposed += () =>
{
model.EntryAdded -= handler;
model.EntryRemoved -= onRemove;
model.EntryUpdated -= handler;
};
}
return observer;
}
/// <summary>
/// Returns a new list by adding the given element to the given list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">This old list of elements</param>
/// <param name="element">The element to add to the list</param>
/// <returns>The new list containing all items from input list and the given element</returns>
private static IEnumerable<T> CollectToList<T>(IEnumerable<T> list, T element)
{
//Accumulate all non-null element into a list.
if (list == null) list = Enumerable.Empty<T>();
return element != null ? list.Concat(Enumerable.Repeat(element, 1)) : list;
}
/// <summary>
/// Notifies the SearchModel update event with list of updated elements
/// </summary>
/// <param name="customization">ILibraryViewCustomization to update the
/// specification and raise specification changed event.</param>
/// <param name="elements">List of updated elements</param>
private static void NotifySearchModelUpdate(ILibraryViewCustomization customization, IEnumerable<NodeSearchElement> elements)
{
//elements might be null if we have removed an element.
if (elements != null)
{
var includes = elements
.Select(NodeItemDataProvider.GetFullyQualifiedName)
.Select(name => name.Split('.').First())
.Distinct()
.SkipWhile(s => s.Contains("://"))
.Select(p => new LayoutIncludeInfo() { path = p });
customization.AddIncludeInfo(includes, "Add-ons");
}
}
/// <summary>
/// creates the resource providers that are used throughout the extensions lifetime
/// to retrieve images and other resource files from disk.
/// </summary>
/// <param name="model"></param>
/// <param name="customization"></param>
private void InitializeResourceProviders(DynamoModel model, LibraryViewCustomization customization)
{
var dllProvider = new DllResourceProvider("http://localhost/dist", "Dynamo.LibraryViewExtensionWebView2.web.library");
iconProvider = new IconResourceProvider(model.PathManager, dllProvider, customization);
nodeProvider = new NodeItemDataProvider(model.SearchModel, iconProvider);
searchResultDataProvider = new SearchResultDataProvider(model.SearchModel, iconProvider);
layoutProvider = new LayoutSpecProvider(customization, iconProvider, "Dynamo.LibraryViewExtensionWebView2.web.library.layoutSpecs.json");
}
private void DynamoSliderValueChanged(object sender, EventArgs e)
{
Slider slider = (Slider)sender;
//The default value of the zoom factor is 1.0. The value that comes from the slider is in percentage, so we divide by 100 to be equivalent
double zoomFactor = slider.Value / 100d;
//To avoid an invalid value for the zoom factor
if (zoomFactor < minimumZoomScale)
zoomFactor = minimumZoomScale;
browser.ZoomFactor = zoomFactor;
dynamoViewModel.Model.PreferenceSettings.LibraryZoomScale = ((int)slider.Value);
}
/// <summary>
/// This method will execute the action of moving the Guide to the next Step (it is triggered when a specific html div that contains the package is clicked).
/// </summary>
internal void MoveToNextStep()
{
GuideFlowEvents.OnGuidedTourNext();
}
//This method will be called when the Library was resized and the current Popup location needs to be updated
internal void UpdatePopupLocation()
{
GuideFlowEvents.OnUpdatePopupLocation();
}
/// <summary>
/// Convenience method for logging to Dynamo Console.
/// </summary>
/// <param name="meessage"></param>
internal void LogToDynamoConsole(string message)
{
this.dynamoViewModel.Model.Logger.Log(message);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!disposing) return;
if (observer != null) observer.Dispose();
observer = null;
if (this.dynamoWindow != null)
{
dynamoWindow.StateChanged -= DynamoWindowStateChanged;
dynamoWindow.SizeChanged -= DynamoWindow_SizeChanged;
browser.ZoomFactorChanged -= Browser_ZoomFactorChanged;
dynamoViewModel.PreferencesWindowChanged-= DynamoViewModel_PreferencesWindowChanged;
dynamoWindow = null;
browser.KeyDown -= Browser_KeyDown;
}
if (this.browser != null)
{
browser.CoreWebView2.RemoveHostObjectFromScript("bridgeTwoWay");
browser.SizeChanged -= Browser_SizeChanged;
browser.Loaded -= Browser_Loaded;
browser.Dispose();
browser = null;
}
twoWayScriptingObject.Dispose();
dynamoViewModel = null;
commandExecutive = null;
}
public static async Task<string> ExecuteScriptFunctionAsync(WebView2 webView2, string functionName, params object[] parameters)
{
if (webView2.CoreWebView2 == null)
return null;
string script = functionName + "(";
for (int i = 0; i < parameters.Length; i++)
{
script += JsonConvert.SerializeObject(parameters[i]);
if (i < parameters.Length - 1)
{
script += ", ";
}
}
script += ");";
return await webView2.ExecuteScriptAsync(script);
}
}
}