You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
9.8 KiB
280 lines
9.8 KiB
4 years ago
|
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));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|