An editor for Microsoft Adaptive Cards that supports the new templating language and DOESN'T use JavaScript, because JavaScript isn't a real programming language.
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.
 
 
 

279 lines
9.8 KiB

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));
}
}
}
}