Browse Source

- Added Monaco editor to text input fields

- Started work on template renderer
master
Claire Davis 3 years ago
parent
commit
13b8e871c0
  1. 26
      Adaptive Card Editor UWP.sln
  2. 9
      Adaptive Card Editor UWP/Adaptive Card Editor UWP.csproj
  3. 41
      Adaptive Card Editor UWP/MainPage.xaml
  4. 123
      Adaptive Card Editor UWP/MainPage.xaml.cs
  5. 164
      MonacoEditorComponent/CodeEditor.Events.cs
  6. 178
      MonacoEditorComponent/CodeEditor.Methods.cs
  7. 311
      MonacoEditorComponent/CodeEditor.Properties.cs
  8. 279
      MonacoEditorComponent/CodeEditor.cs
  9. 206
      MonacoEditorComponent/Extensions/WebViewExtensions.cs
  10. 16
      MonacoEditorComponent/Helpers/DebugLogger.cs
  11. 35
      MonacoEditorComponent/Helpers/DispatcherTaskExtensions.cs
  12. 35
      MonacoEditorComponent/Helpers/InterfaceToClassConverter.cs
  13. 64
      MonacoEditorComponent/Helpers/KeyboardListener.cs
  14. 247
      MonacoEditorComponent/Helpers/ParentAccessor.cs
  15. 116
      MonacoEditorComponent/Helpers/ThemeListener.cs
  16. 65
      MonacoEditorComponent/Monaco/Editor/ContextKey.cs
  17. 57
      MonacoEditorComponent/Monaco/Editor/IActionDescriptor.cs
  18. 34
      MonacoEditorComponent/Monaco/Editor/IContextKey.cs
  19. 218
      MonacoEditorComponent/Monaco/Editor/IEditorConstructionOptions.cs
  20. 24
      MonacoEditorComponent/Monaco/Editor/IEditorFindOptions.cs
  21. 28
      MonacoEditorComponent/Monaco/Editor/IEditorMinimapOptions.cs
  22. 165
      MonacoEditorComponent/Monaco/Editor/IEditorOptions.cs
  23. 42
      MonacoEditorComponent/Monaco/Editor/IEditorScrollbarOptions.cs
  24. 22
      MonacoEditorComponent/Monaco/Editor/IMarker.cs
  25. 27
      MonacoEditorComponent/Monaco/Editor/IMarkerData.cs
  26. 92
      MonacoEditorComponent/Monaco/Editor/IModel.cs
  27. 75
      MonacoEditorComponent/Monaco/Editor/IModelDecorationOptions.cs
  28. 31
      MonacoEditorComponent/Monaco/Editor/IModelDeltaDecoration.cs
  29. 30
      MonacoEditorComponent/Monaco/Editor/IWordAtPosition.cs
  30. 50
      MonacoEditorComponent/Monaco/Editor/Marker.cs
  31. 44
      MonacoEditorComponent/Monaco/Editor/MarkerData.cs
  32. 13
      MonacoEditorComponent/Monaco/Editor/TrackedRangeStickiness.cs
  33. 28
      MonacoEditorComponent/Monaco/Editor/WordAtPosition.cs
  34. 25
      MonacoEditorComponent/Monaco/Helpers/CssGlyphStyle.cs
  35. 82
      MonacoEditorComponent/Monaco/Helpers/CssInlineStyle.cs
  36. 43
      MonacoEditorComponent/Monaco/Helpers/CssLineStyle.cs
  37. 74
      MonacoEditorComponent/Monaco/Helpers/CssStyleBroker.cs
  38. 70
      MonacoEditorComponent/Monaco/Helpers/Json.cs
  39. 57
      MonacoEditorComponent/Monaco/IMarkdownString.cs
  40. 15
      MonacoEditorComponent/Monaco/IPosition.cs
  41. 19
      MonacoEditorComponent/Monaco/IRange.cs
  42. 35
      MonacoEditorComponent/Monaco/IUri.cs
  43. 186
      MonacoEditorComponent/Monaco/KeyCode.cs
  44. 43
      MonacoEditorComponent/Monaco/KeyMod.cs
  45. 28
      MonacoEditorComponent/Monaco/Languages/Command.cs
  46. 21
      MonacoEditorComponent/Monaco/Languages/CompletionContext.cs
  47. 53
      MonacoEditorComponent/Monaco/Languages/CompletionItem.cs
  48. 28
      MonacoEditorComponent/Monaco/Languages/CompletionItemKind.cs
  49. 21
      MonacoEditorComponent/Monaco/Languages/CompletionItemProvider.cs
  50. 32
      MonacoEditorComponent/Monaco/Languages/CompletionList.cs
  51. 30
      MonacoEditorComponent/Monaco/Languages/Hover.cs
  52. 11
      MonacoEditorComponent/Monaco/Languages/HoverProvider.cs
  53. 35
      MonacoEditorComponent/Monaco/Languages/ILanguageExtensionPoint.cs
  54. 35
      MonacoEditorComponent/Monaco/Languages/SnippetString.cs
  55. 12
      MonacoEditorComponent/Monaco/Languages/SuggestTriggerKind.cs
  56. 102
      MonacoEditorComponent/Monaco/LanguagesHelper.cs
  57. 350
      MonacoEditorComponent/Monaco/ModelHelper.cs
  58. 123
      MonacoEditorComponent/Monaco/Position.cs
  59. 122
      MonacoEditorComponent/Monaco/Range.cs
  60. 89
      MonacoEditorComponent/Monaco/Selection.cs
  61. 19
      MonacoEditorComponent/Monaco/Severity.cs
  62. 241
      MonacoEditorComponent/MonacoEditor.html
  63. 212
      MonacoEditorComponent/MonacoEditorComponent.csproj
  64. 42
      MonacoEditorComponent/MonacoEditorComponent.nuspec
  65. 29
      MonacoEditorComponent/Properties/AssemblyInfo.cs
  66. 21
      MonacoEditorComponent/Properties/MonacoEditorComponent.rd.xml
  67. 26
      MonacoEditorComponent/Themes/generic.xaml
  68. 344
      MonacoEditorComponent/monaco-editor/CHANGELOG.md
  69. 21
      MonacoEditorComponent/monaco-editor/LICENSE
  70. 98
      MonacoEditorComponent/monaco-editor/README.md
  71. 278
      MonacoEditorComponent/monaco-editor/ThirdPartyNotices.txt
  72. 5311
      MonacoEditorComponent/monaco-editor/dev/bundleInfo.json
  73. 1691
      MonacoEditorComponent/monaco-editor/dev/nls.metadata.json
  74. 11890
      MonacoEditorComponent/monaco-editor/dev/vs/base/worker/workerMain.js
  75. 1
      MonacoEditorComponent/monaco-editor/dev/vs/base/worker/workerMain.js.map
  76. 35
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/_.contribution.js
  77. 16
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/bat/bat.contribution.js
  78. 99
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/bat/bat.js
  79. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/coffee/coffee.contribution.js
  80. 173
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/coffee/coffee.js
  81. 22
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/cpp/cpp.contribution.js
  82. 325
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/cpp/cpp.js
  83. 16
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/csharp/csharp.contribution.js
  84. 187
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/csharp/csharp.js
  85. 16
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/csp/csp.contribution.js
  86. 58
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/csp/csp.js
  87. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/css/css.contribution.js
  88. 174
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/css/css.js
  89. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/dockerfile/dockerfile.contribution.js
  90. 118
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/dockerfile/dockerfile.js
  91. 16
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/fsharp/fsharp.contribution.js
  92. 139
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/fsharp/fsharp.js
  93. 16
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/go/go.contribution.js
  94. 173
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/go/go.js
  95. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/handlebars/handlebars.contribution.js
  96. 196
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/handlebars/handlebars.js
  97. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/html/html.contribution.js
  98. 210
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/html/html.js
  99. 17
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/ini/ini.contribution.js
  100. 71
      MonacoEditorComponent/monaco-editor/dev/vs/basic-languages/ini/ini.js
  101. Some files were not shown because too many files changed in this diff diff.show_more

26
Adaptive Card Editor UWP.sln

@ -5,18 +5,23 @@ VisualStudioVersion = 16.0.29521.150
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adaptive Card Editor UWP", "Adaptive Card Editor UWP\Adaptive Card Editor UWP.csproj", "{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonacoEditorComponent", "MonacoEditorComponent\MonacoEditorComponent.csproj", "{DD49485C-2D9B-4824-B64E-52193B43E4CB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|Any CPU.ActiveCfg = Debug|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|ARM.ActiveCfg = Debug|ARM
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|ARM.Build.0 = Debug|ARM
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|ARM.Deploy.0 = Debug|ARM
@ -29,6 +34,7 @@ Global
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|x86.ActiveCfg = Debug|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|x86.Build.0 = Debug|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Debug|x86.Deploy.0 = Debug|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|Any CPU.ActiveCfg = Release|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|ARM.ActiveCfg = Release|ARM
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|ARM.Build.0 = Release|ARM
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|ARM.Deploy.0 = Release|ARM
@ -41,6 +47,26 @@ Global
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|x86.ActiveCfg = Release|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|x86.Build.0 = Release|x86
{E2F5AF78-D3E6-43F9-AF44-F342449B63A5}.Release|x86.Deploy.0 = Release|x86
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|ARM.ActiveCfg = Debug|ARM
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|ARM.Build.0 = Debug|ARM
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|ARM64.Build.0 = Debug|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|x64.ActiveCfg = Debug|x64
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|x64.Build.0 = Debug|x64
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|x86.ActiveCfg = Debug|x86
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Debug|x86.Build.0 = Debug|x86
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|Any CPU.Build.0 = Release|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|ARM.ActiveCfg = Release|ARM
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|ARM.Build.0 = Release|ARM
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|ARM64.ActiveCfg = Release|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|ARM64.Build.0 = Release|Any CPU
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|x64.ActiveCfg = Release|x64
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|x64.Build.0 = Release|x64
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|x86.ActiveCfg = Release|x86
{DD49485C-2D9B-4824-B64E-52193B43E4CB}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

9
Adaptive Card Editor UWP/Adaptive Card Editor UWP.csproj

@ -159,6 +159,15 @@
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.9</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MonacoEditorComponent\MonacoEditorComponent.csproj">
<Project>{dd49485c-2d9b-4824-b64e-52193b43e4cb}</Project>
<Name>MonacoEditorComponent</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>

41
Adaptive Card Editor UWP/MainPage.xaml

@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Adaptive_Card_Editor_UWP"
xmlns:monaco="using:Monaco"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
@ -16,24 +17,44 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
<RowDefinition Height="50*" />
<RowDefinition Height="20" />
<RowDefinition Height="50*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">
<TextBlock Grid.Row="0" Grid.Column="0" Padding="20 0">
Adaptive Card JSON Input:
</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1">
Rendered Output (UWP Library):
<TextBlock Grid.Row="0" Grid.Column="1" Padding="20 0">
Adaptive Card Data Model:
</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="2">
Rendered Output (Template Library):
<TextBlock Grid.Row="0" Grid.Column="2" Padding="20 0">
Rendered Output (UWP Library):
</TextBlock>
<TextBox Grid.Row="1" Grid.Column="0" x:Name="txtInput" Margin="20" KeyUp="txtInput_KeyUp" TextWrapping="Wrap"></TextBox>
<Border Grid.Row="1" BorderBrush="#777" BorderThickness="1" Grid.Column="1" Margin="20">
<Border Grid.Row="1" Grid.RowSpan="3" Grid.Column="0" BorderBrush="#777" BorderThickness="1" Margin="20 10">
<monaco:CodeEditor x:Name="txtInput"
TabIndex="0"
HasGlyphMargin="False"
CodeLanguage="json"
KeyDown="txtInput_KeyDown">
</monaco:CodeEditor>
</Border>
<Border Grid.Row="1" Grid.RowSpan="3" Grid.Column="1" BorderBrush="#777" BorderThickness="1" Margin="20 10">
<monaco:CodeEditor x:Name="txtData"
TabIndex="0"
HasGlyphMargin="False"
CodeLanguage="json"
KeyDown="txtData_KeyDown">
</monaco:CodeEditor>
</Border>
<Border Grid.Row="1" Grid.Column="2" BorderBrush="#777" BorderThickness="1" Margin="20 10">
<Grid x:Name="grdCard">
</Grid>
</Border>
<Border Grid.Row="1" BorderBrush="#777" BorderThickness="1" Grid.Column="2" Margin="20">
<TextBlock Grid.Row="2" Grid.Column="2" Padding="20 0">
Rendered Output (Template Library):
</TextBlock>
<Border Grid.Row="3" Grid.Column="2" BorderBrush="#777" BorderThickness="1" Margin="20 10">
<Grid x:Name="grdTemplated">
</Grid>

123
Adaptive Card Editor UWP/MainPage.xaml.cs

@ -1,19 +1,27 @@
using System;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using AdaptiveCards.Rendering.Uwp;
using Monaco;
using Monaco.Helpers;
using Newtonsoft.Json;
// probs don't need these
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using AdaptiveCards.Rendering.Uwp;
using System.Threading.Tasks;
using Monaco.Editor;
using Monaco.Languages;
using Newtonsoft.Json.Linq;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
@ -24,29 +32,82 @@ namespace Adaptive_Card_Editor_UWP
/// </summary>
public sealed partial class MainPage : Page
{
/// <summary>
/// Create DispatcherTimer for handling input delay logic
/// </summary>
public DispatcherTimer keyTimer = new DispatcherTimer();
/// <summary>
/// timestamp in ticks (hundred nanoseconds) of last captured KeyUp event
/// </summary>
public long lastKeyUp;
/// <summary>
/// boolean indicating whether input has changed since last tick
/// </summary>
public bool isRendered;
/// <summary>
/// timer interval in ticks (hundred nanoseconds)
/// </summary>
public long interval = 5000000;
/// <summary>
/// list of known entities that contain templated content
/// </summary>
/// <remarks>
/// If the data referenced in the container doesn't exist, the container shouldn't be included in the rendered output
/// </remarks>
public List<string> Containers = new List<string>
{
"ActionSet",
"Container",
"ColumnSet",
"Column",
"FactSheet",
"Fact",
"ImageSet"
};
public MainPage()
{
// set up a window timer for handling the keyup delay
TimerSetup();
this.InitializeComponent();
}
/// <summary>
/// Sets up a window timer with a 500ms tick interval and starts the timer
/// </summary>
public void TimerSetup()
{
// on every timer tick, run TimerTick()
keyTimer.Tick += TimerTick;
// keyTimer.Interval = new TimeSpan(0, 0, 1);
// set the timer interval to half a second
keyTimer.Interval = TimeSpan.Parse("00:00:00.05");
// start the timer
keyTimer.Start();
}
/// <summary>
/// fires when txtInput sees keyboard input
/// </summary>
private void txtInput_KeyDown(CodeEditor sender, WebKeyEventArgs e)
{
// last key up event = integer value of current time
lastKeyUp = DateTime.Now.Ticks;
// user is inputting text, so we're going to rerender
isRendered = false;
}
private void txtInput_KeyUp(object sender, KeyRoutedEventArgs e)
/// <summary>
/// fires when txtData sees keyboard input
/// </summary>
private void txtData_KeyDown(CodeEditor sender, WebKeyEventArgs args)
{
// last key up event = integer value of current time
lastKeyUp = DateTime.Now.Ticks;
@ -55,12 +116,17 @@ namespace Adaptive_Card_Editor_UWP
isRendered = false;
}
/// <summary>
/// Fires when the timer ticks
/// </summary>
void TimerTick(object sender, object args)
{
// if isRendered is true, there have been no changes to input since the last tick
if (isRendered)
{
return;
}
// otherwise, we done got input, so do the thing
else
{
if (DateTime.Now.Ticks >= lastKeyUp + interval )
@ -87,9 +153,33 @@ namespace Adaptive_Card_Editor_UWP
}
catch (Exception ex)
{
// this means bad data was ingested by the adaptive card renderer
// so just display the exception details
txtOutput.Text = ex.ToString();
grdCard.Children.Add(txtOutput);
}
// render a card using template and data
try
{
string Template = txtInput.Text;
string Data = txtData.Text;
string Rendered = JsonFromTemplate(Template, Data);
// render the card from the rendered template + data
AdaptiveCardRenderer cardRenderer = new AdaptiveCardRenderer();
AdaptiveCardParseResult parsedCard = AdaptiveCard.FromJsonString(Rendered);
RenderedAdaptiveCard theCard = cardRenderer.RenderAdaptiveCard(parsedCard.AdaptiveCard);
grdTemplated.Children.Add(theCard.FrameworkElement);
}
catch (Exception ex)
{
// this means bad data was ingested by the adaptive card renderer
// so just display the exception details
txtOutput.Text = ex.ToString();
grdTemplated.Children.Add(txtOutput);
}
grdCard.UpdateLayout();
isRendered = true;
@ -100,5 +190,24 @@ namespace Adaptive_Card_Editor_UWP
}
}
}
/// <summary>
/// Creates an adaptive card JSON payload from a template and data model. Follows the official templating language.
/// </summary>
/// <param name="Template">JSON adaptive card template</param>
/// <param name="Data">JSON data model</param>
/// <returns>JSON string to be ingested by adaptive card renderer</returns>
public string JsonFromTemplate(string strTemplate, string strData)
{
string output = "";
// first create JSON objects out of the input
JObject Template = JObject.Parse(strTemplate);
JObject Data = JObject.Parse(strData);
return output;
}
}
}

164
MonacoEditorComponent/CodeEditor.Events.cs

@ -0,0 +1,164 @@
using Monaco.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
namespace Monaco
{
public partial class CodeEditor
{
// Override default Loaded/Loading event so we can make sure we've initialized our WebView contents with the CodeEditor.
/// <summary>
/// When Editor is Loading, it is ready to receive commands to the Monaco Engine.
/// </summary>
public new event RoutedEventHandler Loading;
/// <summary>
/// When Editor is Loaded, it has been rendered and is ready to be displayed.
/// </summary>
public new event RoutedEventHandler Loaded;
/// <summary>
/// Called when a link is Ctrl+Clicked on in the editor, set Handled to true to prevent opening.
/// </summary>
public event TypedEventHandler<WebView, WebViewNewWindowRequestedEventArgs> OpenLinkRequested;
/// <summary>
/// Called when an internal exception is encountered while executing a command. (for testing/reporting issues)
/// </summary>
public event TypedEventHandler<CodeEditor, Exception> InternalException;
/// <summary>
/// Custom Keyboard Handler.
/// </summary>
public new event WebKeyEventHandler KeyDown;
private ThemeListener _themeListener;
private void WebView_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
#if DEBUG
Debug.WriteLine("DOM Content Loaded");
#endif
this._initialized = true;
}
private async void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
this.IsLoaded = true;
// Make sure inner editor is focused
await SendScriptAsync("editor.focus();");
// If we're supposed to have focus, make sure we try and refocus on our now loaded webview.
if (FocusManager.GetFocusedElement() == this)
{
this._view.Focus(FocusState.Programmatic);
}
Loaded?.Invoke(this, new RoutedEventArgs());
}
internal ParentAccessor _parentAccessor;
private KeyboardListener _keyboardListener;
private long _themeToken;
private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
#if DEBUG
Debug.WriteLine("Navigation Starting");
#endif
_parentAccessor = new ParentAccessor(this);
_parentAccessor.AddAssemblyForTypeLookup(typeof(Range).GetTypeInfo().Assembly);
_parentAccessor.RegisterAction("Loaded", CodeEditorLoaded);
_themeListener = new ThemeListener();
_themeListener.ThemeChanged += _themeListener_ThemeChanged;
_themeToken = RegisterPropertyChangedCallback(RequestedThemeProperty, RequestedTheme_PropertyChanged);
_keyboardListener = new KeyboardListener(this);
this._view.AddWebAllowedObject("Debug", new DebugLogger());
this._view.AddWebAllowedObject("Parent", _parentAccessor);
this._view.AddWebAllowedObject("Theme", _themeListener);
this._view.AddWebAllowedObject("Keyboard", _keyboardListener);
}
private async void CodeEditorLoaded()
{
if (Decorations != null && Decorations.Count > 0)
{
// Need to retrigger highlights after load if they were set before load.
await DeltaDecorationsHelperAsync(Decorations.ToArray());
}
// Now we're done loading
Loading?.Invoke(this, new RoutedEventArgs());
}
private void WebView_NewWindowRequested(WebView sender, WebViewNewWindowRequestedEventArgs args)
{
// TODO: Should probably create own event args here as we don't want to expose the referrer to our internal page?
OpenLinkRequested?.Invoke(sender, args);
}
private async void RequestedTheme_PropertyChanged(DependencyObject obj, DependencyProperty property)
{
var editor = obj as CodeEditor;
var theme = editor.RequestedTheme;
var tstr = string.Empty;
if (theme == ElementTheme.Default)
{
tstr = _themeListener.CurrentThemeName;
}
else
{
tstr = theme.ToString();
}
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
await this.InvokeScriptAsync("changeTheme", new string[] { tstr, _themeListener.IsHighContrast.ToString() });
});
}
private async void _themeListener_ThemeChanged(ThemeListener sender)
{
if (RequestedTheme == ElementTheme.Default)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
await this.InvokeScriptAsync("changeTheme", args: new string[] { sender.CurrentTheme.ToString(), sender.IsHighContrast.ToString() });
});
}
}
internal bool TriggerKeyDown(WebKeyEventArgs args)
{
this.KeyDown?.Invoke(this, args);
return args.Handled;
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (this._view != null && FocusManager.GetFocusedElement() == this)
{
// Forward Focus onto our inner WebView
this._view.Focus(FocusState.Programmatic);
}
}
}
}

178
MonacoEditorComponent/CodeEditor.Methods.cs

@ -0,0 +1,178 @@
using Monaco.Editor;
using Monaco.Helpers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Data.Json;
using Windows.Foundation;
namespace Monaco
{
/// <summary>
/// Action delegate for <see cref="CodeEditor.AddCommandAsync(int, CommandHandler)"/> and <see cref="CodeEditor.AddCommandAsync(int, CommandHandler, string)"/>.
/// </summary>
public delegate void CommandHandler();
/// <summary>
/// This file contains Monaco IEditor method implementations we can call on our control.
/// https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.ieditor.html
/// https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.icommoncodeeditor.html
/// </summary>
#pragma warning disable CS1591
public partial class CodeEditor
{
#region Reveal Methods
public IAsyncAction RevealLineAsync(uint lineNumber)
{
return SendScriptAsync("editor.revealLine(" + lineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealLineInCenterAsync(uint lineNumber)
{
return SendScriptAsync("editor.revealLineInCenter(" + lineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealLineInCenterIfOutsideViewportAsync(uint lineNumber)
{
return SendScriptAsync("editor.revealLineInCenterIfOutsideViewport(" + lineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealLinesAsync(uint startLineNumber, uint endLineNumber)
{
return SendScriptAsync("editor.revealLines(" + startLineNumber + ", " + endLineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealLinesInCenterAsync(uint startLineNumber, uint endLineNumber)
{
return SendScriptAsync("editor.revealLinesInCenter(" + startLineNumber + ", " + endLineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealLinesInCenterIfOutsideViewportAsync(uint startLineNumber, uint endLineNumber)
{
return SendScriptAsync("editor.revealLinesInCenterIfOutsideViewport(" + startLineNumber + ", " + endLineNumber + ")").AsAsyncAction();
}
public IAsyncAction RevealPositionAsync(IPosition position)
{
return RevealPositionAsync(position, false, false);
}
public IAsyncAction RevealPositionAsync(IPosition position, bool revealVerticalInCenter)
{
return RevealPositionAsync(position, revealVerticalInCenter, false);
}
public IAsyncAction RevealPositionAsync(IPosition position, bool revealVerticalInCenter, bool revealHorizontal)
{
return SendScriptAsync("editor.revealPosition(JSON.parse('" + position.ToJson() + "'), " + JsonConvert.ToString(revealVerticalInCenter) + ", " + JsonConvert.ToString(revealHorizontal) + ")").AsAsyncAction();
}
public IAsyncAction RevealPositionInCenterAsync(IPosition position)
{
return SendScriptAsync("editor.revealPositionInCenter(JSON.parse('" + position.ToJson() + "'))").AsAsyncAction();
}
public IAsyncAction RevealPositionInCenterIfOutsideViewportAsync(IPosition position)
{
return SendScriptAsync("editor.revealPositionInCenterIfOutsideViewport(JSON.parse('" + position.ToJson() + "'))").AsAsyncAction();
}
public IAsyncAction RevealRangeAsync(IRange range)
{
return SendScriptAsync("editor.revealRange(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
}
public IAsyncAction RevealRangeAtTopAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeAtTop(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
}
public IAsyncAction RevealRangeInCenterAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeInCenter(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
}
public IAsyncAction RevealRangeInCenterIfOutsideViewportAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeInCenterIfOutsideViewport(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
}
#endregion
public IAsyncAction AddActionAsync(IActionDescriptor action)
{
var wref = new WeakReference<CodeEditor>(this);
_parentAccessor.RegisterAction("Action" + action.Id, new Action(() => { if (wref.TryGetTarget(out CodeEditor editor)) { action?.Run(editor); }}));
return InvokeScriptAsync("addAction", action).AsAsyncAction();
}
public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler handler)
{
return AddCommandAsync(keybinding, handler, string.Empty);
}
public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler handler, string context)
{
var name = "Command" + keybinding;
_parentAccessor.RegisterAction(name, new Action(() => { handler?.Invoke(); }));
return InvokeScriptAsync<string>("addCommand", new object[] { keybinding, name, context }).AsAsyncOperation();
}
public IAsyncOperation<ContextKey> CreateContextKeyAsync(string key, bool defaultValue)
{
var ck = new ContextKey(this, key, defaultValue);
return InvokeScriptAsync("createContext", ck).ContinueWith((noop) =>
{
return ck;
}).AsAsyncOperation();
}
public IModel GetModel()
{
return _model;
}
public IAsyncOperation<IEnumerable<Marker>> GetModelMarkersAsync() // TODO: Filter (string? owner, Uri? resource, int? take)
{
return SendScriptAsync<IEnumerable<Marker>>("monaco.editor.getModelMarkers();").AsAsyncOperation();
}
public IAsyncAction SetModelMarkersAsync(string owner, [ReadOnlyArray] IMarkerData[] markers)
{
return SendScriptAsync("monaco.editor.setModelMarkers(model, " + JsonConvert.ToString(owner) + ", " + JsonConvert.SerializeObject(markers) + ");").AsAsyncAction();
}
public IAsyncOperation<Position> GetPositionAsync()
{
return SendScriptAsync<Position>("editor.getPosition();").AsAsyncOperation();
}
public IAsyncAction SetPositionAsync(IPosition position)
{
return SendScriptAsync("editor.setPosition(" + JsonConvert.SerializeObject(position) + ");").AsAsyncAction();
}
/// <summary>
/// https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.icommoncodeeditor.html#deltadecorations
///
/// Using <see cref="Decorations"/> Property to manipulate decorations instead of calling this directly.
/// </summary>
/// <param name="newDecorations"></param>
/// <returns></returns>
private IAsyncAction DeltaDecorationsHelperAsync([ReadOnlyArray] IModelDeltaDecoration[] newDecorations)
{
var newDecorationsAdjust = newDecorations ?? Array.Empty<IModelDeltaDecoration>();
// Update Styles
return InvokeScriptAsync("updateStyle", CssStyleBroker.Instance.GetStyles()).ContinueWith((noop) =>
{
// Send Command to Modify Decorations
// IMPORTANT: Need to cast to object here as we want this to be a single array object passed as a parameter, not a list of parameters to expand.
return InvokeScriptAsync("updateDecorations", (object)newDecorationsAdjust);
}).AsAsyncAction();
}
}
#pragma warning restore CS1591
}

311
MonacoEditorComponent/CodeEditor.Properties.cs

@ -0,0 +1,311 @@
using Monaco.Editor;
using Monaco.Helpers;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
namespace Monaco
{
partial class CodeEditor : IParentAccessorAcceptor
{
public bool IsSettingValue { get; set; }
/// <summary>
/// Get or Set the CodeEditor Text.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty TextPropertyField =
DependencyProperty.Register("Text", typeof(string), typeof(CodeEditor), new PropertyMetadata(string.Empty, (d, e) => {
if (!(d as CodeEditor).IsSettingValue)
{
(d as CodeEditor)?.InvokeScriptAsync("updateContent", e.NewValue.ToString());
}
}));
public static DependencyProperty TextProperty
{
get
{
return TextPropertyField;
}
}
/// <summary>
/// Get the current Primary Selected CodeEditor Text.
/// </summary>
public string SelectedText
{
get { return (string)GetValue(SelectedTextProperty); }
set { SetValue(SelectedTextProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty SelectedTextPropertyField =
DependencyProperty.Register(nameof(SelectedText), typeof(string), typeof(CodeEditor), new PropertyMetadata(string.Empty, (d, e) => {
if (!(d as CodeEditor).IsSettingValue)
{
(d as CodeEditor)?.InvokeScriptAsync("updateSelectedContent", e.NewValue.ToString());
}
}));
public static DependencyProperty SelectedTextProperty
{
get
{
return SelectedTextPropertyField;
}
}
public Selection SelectedRange
{
get { return (Selection)GetValue(SelectedRangeProperty); }
set { SetValue(SelectedRangeProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedRange. This enables animation, styling, binding, etc...
private static readonly DependencyProperty SelectedRangePropertyField =
DependencyProperty.Register(nameof(SelectedRange), typeof(Selection), typeof(CodeEditor), new PropertyMetadata(null));
public static DependencyProperty SelectedRangeProperty
{
get
{
return SelectedRangePropertyField;
}
}
/// <summary>
/// Set the Syntax Language for the Code CodeEditor.
///
/// Note: Most likely to change or move location.
/// </summary>
public string CodeLanguage
{
get { return (string)GetValue(CodeLanguageProperty); }
set { SetValue(CodeLanguageProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty CodeLanguagePropertyField =
DependencyProperty.Register("CodeLanguage", typeof(string), typeof(CodeEditor), new PropertyMetadata("xml", (d, e) => {
var editor = d as CodeEditor;
if (editor.Options != null)
{
// Will trigger its own update of Options, but need this for initialization changes.
editor.Options.Language = e.NewValue.ToString();
}
// TODO: Push this to Options property change check instead...
// Changes to Language are ignored in Updated Options.
// https://microsoft.github.io/monaco-editor/api/modules/monaco.editor.html#setmodellanguage.
(d as CodeEditor)?.InvokeScriptAsync("updateLanguage", e.NewValue.ToString());
}));
internal static DependencyProperty CodeLanguageProperty
{
get
{
return CodeLanguagePropertyField;
}
}
/// <summary>
/// Get or set the CodeEditor Options.
/// </summary>
public IEditorConstructionOptions Options
{
get { return (IEditorConstructionOptions)GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty OptionsPropertyField =
DependencyProperty.Register("Options", typeof(IEditorConstructionOptions), typeof(CodeEditor), new PropertyMetadata(new IEditorConstructionOptions(), (d, e) => {
var value = e.NewValue as IEditorConstructionOptions;
if (value != null)
{
var editor = d as CodeEditor;
if (editor != null)
{
editor.InvokeScriptAsync("updateOptions", value.ToJson());
// Register for sub-property changes on new object
value.PropertyChanged -= editor.Options_PropertyChanged;
value.PropertyChanged += editor.Options_PropertyChanged;
}
}
}));
public static DependencyProperty OptionsProperty
{
get
{
return OptionsPropertyField;
}
}
/// <summary>
/// Get or Set the CodeEditor Text.
/// </summary>
public bool HasGlyphMargin
{
get { return (bool)GetValue(HasGlyphMarginProperty); }
set { SetValue(HasGlyphMarginProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty HasGlyphMarginPropertyField =
DependencyProperty.Register("HasGlyphMargin", typeof(bool), typeof(CodeEditor), new PropertyMetadata(false, (d, e) => {
(d as CodeEditor).Options.GlyphMargin = e.NewValue as bool?;
}));
public static DependencyProperty HasGlyphMarginProperty
{
get
{
return HasGlyphMarginPropertyField;
}
}
/// <summary>
/// Gets or sets text Decorations.
/// </summary>
public IObservableVector<IModelDeltaDecoration> Decorations
{
get { return (IObservableVector<IModelDeltaDecoration>)GetValue(DecorationsProperty); }
set { SetValue(DecorationsProperty, value); }
}
private AsyncLock _mutexLineDecorations = new AsyncLock();
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty DecorationsPropertyField =
DependencyProperty.Register("Decorations", typeof(IModelDeltaDecoration), typeof(CodeEditor), new PropertyMetadata(null, async (d, e) => {
var editor = d as CodeEditor;
if (editor != null)
{
// We only want to do this one at a time per editor.
using (await editor._mutexLineDecorations.LockAsync())
{
var old = e.OldValue as IObservableVector<IModelDeltaDecoration>;
// Clear out the old line decorations if we're replacing them or setting back to null
if ((old != null && old.Count > 0) ||
e.NewValue == null)
{
await editor.DeltaDecorationsHelperAsync(null);
}
var value = e.NewValue as IObservableVector<IModelDeltaDecoration>;
if (value != null)
{
if (value.Count > 0)
{
await editor.DeltaDecorationsHelperAsync(value.ToArray());
}
value.VectorChanged -= editor.Decorations_VectorChanged;
value.VectorChanged += editor.Decorations_VectorChanged;
}
}
}
}));
private async void Decorations_VectorChanged(IObservableVector<IModelDeltaDecoration> sender, IVectorChangedEventArgs @event)
{
if (sender != null)
{
// Need to recall mutex as this is called from outside of this initial callback setting it up.
using (await _mutexLineDecorations.LockAsync())
{
await DeltaDecorationsHelperAsync(sender.ToArray());
}
}
}
public static DependencyProperty DecorationsProperty
{
get
{
return DecorationsPropertyField;
}
}
/// <summary>
/// Gets or sets the hint Markers.
/// Note: This property is a helper for <see cref="SetModelMarkersAsync(string, IMarkerData[])"/>; use this property or the method, not both.
/// </summary>
public IObservableVector<IMarkerData> Markers
{
get { return (IObservableVector<IMarkerData>)GetValue(MarkersProperty); }
set { SetValue(MarkersProperty, value); }
}
private AsyncLock _mutexMarkers = new AsyncLock();
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty MarkersPropertyField =
DependencyProperty.Register("Markers", typeof(IMarkerData), typeof(CodeEditor), new PropertyMetadata(null, async (d, e) => {
var editor = d as CodeEditor;
if (editor != null)
{
// We only want to do this one at a time per editor.
using (await editor._mutexMarkers.LockAsync())
{
var old = e.OldValue as IObservableVector<IMarkerData>;
// Clear out the old markers if we're replacing them or setting back to null
if ((old != null && old.Count > 0) ||
e.NewValue == null)
{
// TODO: Can I simplify this in this case?
await editor.SetModelMarkersAsync("CodeEditor", Array.Empty<IMarkerData>());
}
var value = e.NewValue as IObservableVector<IMarkerData>;
if (value != null)
{
if (value.Count > 0)
{
await editor.SetModelMarkersAsync("CodeEditor", value.ToArray());
}
value.VectorChanged -= editor.Markers_VectorChanged;
value.VectorChanged += editor.Markers_VectorChanged;
}
}
}
}));
private async void Markers_VectorChanged(IObservableVector<IMarkerData> sender, IVectorChangedEventArgs @event)
{
if (sender != null)
{
// Need to recall mutex as this is called from outside of this initial callback setting it up.
using (await _mutexMarkers.LockAsync())
{
await SetModelMarkersAsync("CodeEditor", sender.ToArray());
}
}
}
public static DependencyProperty MarkersProperty
{
get
{
return MarkersPropertyField;
}
}
}
}

279
MonacoEditorComponent/CodeEditor.cs

@ -0,0 +1,279 @@
using Collections.Generic;
using Monaco.Editor;
using Monaco.Extensions;
using Monaco.Helpers;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Templated Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234235
namespace Monaco
{
/// <summary>
/// UWP Windows Runtime Component wrapper for the Monaco CodeEditor
/// https://microsoft.github.io/monaco-editor/
/// </summary>
[TemplatePart(Name = "View", Type = typeof(WebView))]
public sealed partial class CodeEditor : Control, INotifyPropertyChanged
{
private bool _initialized;
private WebView _view;
private ModelHelper _model;
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Template Property used during loading to prevent blank control visibility when it's still loading WebView.
/// </summary>
public bool IsLoaded
{
get { return (bool)GetValue(IsLoadedProperty); }
private set { SetValue(IsLoadedProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty IsLoadedPropertyField =
DependencyProperty.Register("IsLoaded", typeof(string), typeof(CodeEditor), new PropertyMetadata(false));
public static DependencyProperty IsLoadedProperty
{
get
{
return IsLoadedPropertyField;
}
}
/// <summary>
/// Construct a new IStandAloneCodeEditor.
/// </summary>
public CodeEditor()
{
DefaultStyleKey = typeof(CodeEditor);
if (Options != null)
{
// Set Pass-Thru Properties
Options.GlyphMargin = HasGlyphMargin;
Options.Language = CodeLanguage;
// Register for changes
Options.PropertyChanged += Options_PropertyChanged;
}
// Initialize this here so property changed event will fire and register collection changed event.
Decorations = new ObservableVector<IModelDeltaDecoration>();
Markers = new ObservableVector<IMarkerData>();
_model = new ModelHelper(this);
base.Loaded += CodeEditor_Loaded;
Unloaded += CodeEditor_Unloaded;
}
private async void Options_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// TODO: Check for Language property and call other method instead?
await InvokeScriptAsync("updateOptions", sender);
}
private void CodeEditor_Loaded(object sender, RoutedEventArgs e)
{
// Do this the 2nd time around.
if (_model == null && _view != null)
{
_model = new ModelHelper(this);
_parentAccessor = new ParentAccessor(this);
_parentAccessor.AddAssemblyForTypeLookup(typeof(Range).GetTypeInfo().Assembly);
_parentAccessor.RegisterAction("Loaded", CodeEditorLoaded);
_themeListener = new ThemeListener();
_themeListener.ThemeChanged += _themeListener_ThemeChanged;
_themeToken = RegisterPropertyChangedCallback(RequestedThemeProperty, RequestedTheme_PropertyChanged);
_keyboardListener = new KeyboardListener(this);
_view.AddWebAllowedObject("Parent", _parentAccessor);
_view.AddWebAllowedObject("Theme", _themeListener);
_view.AddWebAllowedObject("Keyboard", _keyboardListener);
Options.PropertyChanged += Options_PropertyChanged;
Decorations.VectorChanged += Decorations_VectorChanged;
Markers.VectorChanged += Markers_VectorChanged;
_view.NewWindowRequested += WebView_NewWindowRequested;
_initialized = true;
Loading?.Invoke(this, new RoutedEventArgs());
Unloaded += CodeEditor_Unloaded;
Loaded?.Invoke(this, new RoutedEventArgs());
}
}
private void CodeEditor_Unloaded(object sender, RoutedEventArgs e)
{
Unloaded -= CodeEditor_Unloaded;
if (_view != null)
{
_view.NavigationStarting -= WebView_NavigationStarting;
_view.DOMContentLoaded -= WebView_DOMContentLoaded;
_view.NavigationCompleted -= WebView_NavigationCompleted;
_view.NewWindowRequested -= WebView_NewWindowRequested;
_initialized = false;
}
Decorations.VectorChanged -= Decorations_VectorChanged;
Markers.VectorChanged -= Markers_VectorChanged;
_parentAccessor?.Dispose();
_parentAccessor = null;
Options.PropertyChanged -= Options_PropertyChanged;
_themeListener.ThemeChanged -= _themeListener_ThemeChanged;
_themeListener = null;
UnregisterPropertyChangedCallback(RequestedThemeProperty, _themeToken);
_keyboardListener = null;
_model = null;
}
protected override void OnApplyTemplate()
{
if (_view != null)
{
_view.NavigationStarting -= WebView_NavigationStarting;
_view.DOMContentLoaded -= WebView_DOMContentLoaded;
_view.NavigationCompleted -= WebView_NavigationCompleted;
_view.NewWindowRequested -= WebView_NewWindowRequested;
_initialized = false;
}
_view = (WebView)GetTemplateChild("View");
if (_view != null)
{
_view.NavigationStarting += WebView_NavigationStarting;
_view.DOMContentLoaded += WebView_DOMContentLoaded;
_view.NavigationCompleted += WebView_NavigationCompleted;
_view.NewWindowRequested += WebView_NewWindowRequested;
_view.Source = new Uri("ms-appx-web:///Monaco/MonacoEditor.html");
}
base.OnApplyTemplate();
}
internal async Task SendScriptAsync(string script,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
await SendScriptAsync<object>(script, member, file, line);
}
internal async Task<T> SendScriptAsync<T>(string script,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
if (_initialized)
{
try
{
return await this._view.RunScriptAsync<T>(script, member, file, line);
}
catch (Exception e)
{
InternalException?.Invoke(this, e);
}
}
else
{
#if DEBUG
Debug.WriteLine("WARNING: Tried to call '" + script + "' before initialized.");
#endif
}
return default(T);
}
internal async Task InvokeScriptAsync(
string method,
object arg,
bool serialize = true,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
await this.InvokeScriptAsync<object>(method, new object[] { arg }, serialize, member, file, line);
}
internal async Task InvokeScriptAsync(
string method,
object[] args,
bool serialize = true,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
await this.InvokeScriptAsync<object>(method, args, serialize, member, file, line);
}
internal async Task<T> InvokeScriptAsync<T>(
string method,
object arg,
bool serialize = true,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
return await this.InvokeScriptAsync<T>(method, new object[] { arg }, serialize, member, file, line);
}
internal async Task<T> InvokeScriptAsync<T>(
string method,
object[] args,
bool serialize = true,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
if (_initialized)
{
try
{
return await this._view.InvokeScriptAsync<T>(method, args, serialize, member, file, line);
}
catch (Exception e)
{
InternalException?.Invoke(this, e);
}
}
else
{
#if DEBUG
Debug.WriteLine("WARNING: Tried to call " + method + " before initialized.");
#endif
}
return default(T);
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

206
MonacoEditorComponent/Extensions/WebViewExtensions.cs

@ -0,0 +1,206 @@
using Monaco.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Windows.Data.Json;
using Windows.UI.Xaml.Controls;
namespace Monaco.Extensions
{
internal static class WebViewExtensions
{
public static async Task RunScriptAsync(
this WebView _view,
string script,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
await _view.RunScriptAsync<object>(script, member, file, line);
}
public static async Task<T> RunScriptAsync<T>(
this WebView _view,
string script,
[CallerMemberName] string member = null,
[CallerFilePath] string file = null,
[CallerLineNumber] int line = 0)
{
var start = "try {\n";
if (typeof(T) != typeof(object))
{
script = script.Trim(';');
start += "JSON.stringify(" + script + ");";
}
else
{
start += script;
}
var fullscript = start +
"\n} catch (err) { JSON.stringify({ wv_internal_error: true, message: err.message, description: err.description, number: err.number, stack: err.stack }); }";
if (_view.Dispatcher.HasThreadAccess)
{
try
{
return await RunScriptHelperAsync<T>(_view, fullscript);
}
catch (Exception e)
{
throw new JavaScriptExecutionException(member, file, line, script, e);
}
}
else
{
return await _view.Dispatcher.RunTaskAsync(async () =>
{
try
{
return await RunScriptHelperAsync<T>(_view, fullscript);
}
catch (Exception e)
{
throw new JavaScriptExecutionException(member, file, line, script, e);
}
});
}
}
private static async Task<T> RunScriptHelperAsync<T>(WebView _view, string script)
{
var returnstring = await _view.InvokeScriptAsync("eval", new string[] { script });
if (JsonObject.TryParse(returnstring, out JsonObject result))
{
if (result.ContainsKey("wv_internal_error") && result["wv_internal_error"].ValueType == JsonValueType.Boolean && result["wv_internal_error"].GetBoolean())
{
throw new JavaScriptInnerException(result["message"].GetString(), result["stack"].GetString());
}
}
if (returnstring != null && returnstring != "null")
{
return JsonConvert.DeserializeObject<T>(returnstring);
}
return default(T);
}