diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index fb8d1517ee..29b7671d19 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -8,14 +8,10 @@
7.1.2
7.0.0
2.0.1
- 6.2.14
- 10.0.22621.755
4.7.37
7.1.100
8.0.0-dev.65
- 1.0.1462.37
4.9.4
- 1.2.0
@@ -34,14 +30,15 @@
+
-
+
-
-
+
+
diff --git a/src/app/dev/DevToys.Api/Core/IFontProvider.cs b/src/app/dev/DevToys.Api/Core/IFontProvider.cs
new file mode 100644
index 0000000000..6ba923d9ca
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Core/IFontProvider.cs
@@ -0,0 +1,12 @@
+namespace DevToys.Api.Core;
+
+///
+/// Provides a platform agnostic way to retrieve information about fonts installed on the operating system.
+///
+public interface IFontProvider
+{
+ ///
+ /// Retrieves the list of font families available on the operating system.
+ ///
+ string[] GetFontFamilies();
+}
diff --git a/src/app/dev/DevToys.Api/Settings/PredefinedSettings.cs b/src/app/dev/DevToys.Api/Settings/PredefinedSettings.cs
index 9807653b05..b791b64c4d 100644
--- a/src/app/dev/DevToys.Api/Settings/PredefinedSettings.cs
+++ b/src/app/dev/DevToys.Api/Settings/PredefinedSettings.cs
@@ -10,6 +10,76 @@ public static readonly SettingDefinition CompactMode
name: nameof(CompactMode),
defaultValue: false);
+ ///
+ /// Preferred default fonts. The app will the first font in this list that is available on the system.
+ ///
+ public static readonly string[] DefaultFonts
+ = new[]
+ {
+ // Popular fonts developers install. If it's on the system, users likely want to use it.
+ "Fira Code",
+ "Source Code Pro",
+ "DejaVu Sans Mono",
+ "Hack",
+
+ // Default Visual Studio fonts.
+ "Cascadia Mono",
+ "Cascadia Code",
+
+ // Fonts included on MacOS.
+ "Menlo",
+ "Monaco",
+ "SF Mono",
+ "SF Pro",
+
+ // Fonts included on Windows and MacOS.
+ "Consolas",
+ "Courier New",
+
+ // Fonts included on Windows.
+ "Segoe UI",
+ };
+
+ ///
+ /// The font family name to use in the text editor.
+ ///
+ public static readonly SettingDefinition TextEditorFont
+ = new(
+ name: nameof(TextEditorFont),
+ defaultValue: string.Empty); // Default value will be defined by the app depending on the operating system.
+
+ ///
+ /// Whether the text in the text editor should wrap.
+ ///
+ public static readonly SettingDefinition TextEditorTextWrapping
+ = new(
+ name: nameof(TextEditorTextWrapping),
+ defaultValue: false);
+
+ ///
+ /// Whether the line numbers should be displayed in the text editor.
+ ///
+ public static readonly SettingDefinition TextEditorLineNumbers
+ = new(
+ name: nameof(TextEditorLineNumbers),
+ defaultValue: true);
+
+ ///
+ /// Whether the line where the caret is should be highlighted in the text editor.
+ ///
+ public static readonly SettingDefinition TextEditorHighlightCurrentLine
+ = new(
+ name: nameof(TextEditorHighlightCurrentLine),
+ defaultValue: true);
+
+ ///
+ /// Whether white spaces should be rendered in the text editor.
+ ///
+ public static readonly SettingDefinition TextEditorRenderWhitespace
+ = new(
+ name: nameof(TextEditorRenderWhitespace),
+ defaultValue: false);
+
///
/// Whether when using the Paste command, the text in the editor should be replaced or appended.
///
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIButton.cs b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIButton.cs
index a7aa8f6dcb..42c3f4fe44 100644
--- a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIButton.cs
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIButton.cs
@@ -49,7 +49,7 @@ internal set
public static partial class GUI
{
///
- /// Create component that represents a button, which reacts when clicking on it.
+ /// Create a component that represents a button, which reacts when clicking on it.
///
public static IUIButton Button()
{
@@ -57,7 +57,7 @@ public static IUIButton Button()
}
///
- /// Create component that represents a button, which reacts when clicking on it.
+ /// Create a component that represents a button, which reacts when clicking on it.
///
/// An optional unique identifier for this UI element.
public static IUIButton Button(string? id)
@@ -66,7 +66,7 @@ public static IUIButton Button(string? id)
}
///
- /// Create component that represents a button, which reacts when clicking on it.
+ /// Create a component that represents a button, which reacts when clicking on it.
///
/// An optional unique identifier for this UI element.
/// The text to display in the button.
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIMultilineLineTextInput.cs b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIMultilineLineTextInput.cs
index abfd3e7db5..6f22a198ba 100644
--- a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIMultilineLineTextInput.cs
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUIMultilineLineTextInput.cs
@@ -108,7 +108,7 @@ public static IUIMultilineLineTextInput MultilineTextInput(string? id, string pr
///
/// Sets the list of spans to highlight in the text document.
///
- public static IUIMultilineLineTextInput Hihglight(this IUIMultilineLineTextInput element, params TextSpan[] spans)
+ public static IUIMultilineLineTextInput Highlight(this IUIMultilineLineTextInput element, params TextSpan[] spans)
{
((UIMultilineTextInput)element).HighlightedSpans = spans;
return element;
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISetting.cs b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISetting.cs
index 231d1f59fc..12524b1235 100644
--- a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISetting.cs
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISetting.cs
@@ -1,6 +1,4 @@
-using System;
-
-namespace DevToys.Api;
+namespace DevToys.Api;
///
/// A component that represents a setting, with a title, description, icon and for the option value.
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISinglelineTextInput.cs b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISinglelineTextInput.cs
index cf3bf6efdd..8fc2e44696 100644
--- a/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISinglelineTextInput.cs
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Components/IUISinglelineTextInput.cs
@@ -142,7 +142,7 @@ public static IUISinglelineTextInput SinglelineTextInput(string? id)
///
/// Sets the text input control as read-only.
///
- public static T ReadOnly(this T element) where T : IUITitledElement
+ public static T ReadOnly(this T element) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -154,7 +154,7 @@ public static T ReadOnly(this T element) where T : IUITitledElement
///
/// Sets the text input control as editable.
///
- public static T Editable(this T element) where T : IUITitledElement
+ public static T Editable(this T element) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -166,7 +166,7 @@ public static T Editable(this T element) where T : IUITitledElement
///
/// Shows the "copy" button when the editor is editable.
///
- public static T CanCopyWhenEditable(this T element) where T : IUITitledElement
+ public static T CanCopyWhenEditable(this T element) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -178,7 +178,7 @@ public static T CanCopyWhenEditable(this T element) where T : IUITitledElemen
///
/// Hides the "copy" button when the editor is editable.
///
- public static T CannotCopyWhenEditable(this T element) where T : IUITitledElement
+ public static T CannotCopyWhenEditable(this T element) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -190,7 +190,7 @@ public static T CannotCopyWhenEditable(this T element) where T : IUITitledEle
///
/// Sets the unformatted text of the control.
///
- public static T Text(this T element, string text) where T : IUITitledElement
+ public static T Text(this T element, string text) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -202,7 +202,7 @@ public static T Text(this T element, string text) where T : IUITitledElement
///
/// Selects the given span in the text document.
///
- public static T Select(this T element, TextSpan span) where T : IUITitledElement
+ public static T Select(this T element, TextSpan span) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
@@ -214,7 +214,7 @@ public static T Select(this T element, TextSpan span) where T : IUITitledElem
///
/// Selects the given span in the text document.
///
- public static T Select(this T element, int start, int length) where T : IUITitledElement
+ public static T Select(this T element, int start, int length) where T : IUISinglelineTextInput
{
if (element is UISinglelineTextInput strongElement)
{
diff --git a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditor.cs b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditor.cs
index 040693757d..178f398f25 100644
--- a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditor.cs
+++ b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditor.cs
@@ -4,14 +4,19 @@
using DevToys.MonacoEditor.Extensions;
using DevToys.MonacoEditor.Monaco;
using DevToys.MonacoEditor.Monaco.Editor;
+using DevToys.MonacoEditor.Monaco.Helpers;
using DevToys.MonacoEditor.WebInterop;
-using DevToys.UI.Framework.Threading;
+using DevToys.UI.Framework.Controls;
+using DevToys.UI.Framework.Helpers;
+using Microsoft.Extensions.Logging;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
+using Uno.Extensions;
using Windows.Foundation;
+using Windows.UI;
+using static System.Net.Mime.MediaTypeNames;
using Range = DevToys.MonacoEditor.Monaco.Range;
-using Microsoft.UI.Dispatching;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
namespace DevToys.MonacoEditor;
@@ -24,7 +29,7 @@ namespace DevToys.MonacoEditor;
[TemplateVisualState(Name = PointerOverState, GroupName = CommonStates)]
[TemplateVisualState(Name = FocusedState, GroupName = CommonStates)]
[TemplateVisualState(Name = DisabledState, GroupName = CommonStates)]
-public sealed partial class CodeEditor : Control, IParentAccessorAcceptor, IDisposable
+public sealed partial class CodeEditor : Control, IParentAccessorAcceptor, IDisposable, IMonacoEditor
{
internal const string CommonStates = "CommonStates";
internal const string NormalState = "Normal";
@@ -32,12 +37,22 @@ public sealed partial class CodeEditor : Control, IParentAccessorAcceptor, IDisp
internal const string FocusedState = "Focused";
internal const string DisabledState = "Disabled";
- private DispatcherQueue UIAccess => this.DispatcherQueue;
+ private static readonly IModelDecorationOptions HighlightedSpanStyle
+ = new IModelDecorationOptions()
+ {
+ ClassName = new CssLineStyle()
+ {
+ BackgroundColor = Color.FromArgb(85, 234, 92, 0) // #55EA5C00
+ }
+ };
+ private readonly ILogger _logger;
+ private readonly ISettingsProvider _settingsProvider = Parts.SettingsProvider;
private readonly DebugLogger _debugLogger = new();
- private readonly ThemeListener _themeListener;
- private readonly long _themeToken;
+ private readonly ThemeListener _themeListener = new();
+ private readonly CssStyleBroker _cssBroker;
+ private IReadOnlyList? _spansToHighlight;
private ICodeEditorPresenter? _view;
private int _focusCount;
private bool _initialized;
@@ -46,17 +61,19 @@ public sealed partial class CodeEditor : Control, IParentAccessorAcceptor, IDisp
public CodeEditor()
{
+ _logger = this.Log();
DefaultStyleKey = typeof(CodeEditor);
ParentAccessor = new ParentAccessor(this);
ParentAccessor.AddAssemblyForTypeLookup(typeof(Range).GetTypeInfo().Assembly);
+ ParentAccessor.AddAssemblyForTypeLookup(typeof(TextSpan).GetTypeInfo().Assembly);
ParentAccessor.RegisterAction("Loaded", OnMonacoEditorLoaded);
ParentAccessor.RegisterAction("GotFocus", OnMonacoEditorGotFocus);
ParentAccessor.RegisterAction("LostFocus", OnMonacoEditorLostFocus);
- _themeListener = new ThemeListener(UIAccess);
_themeListener.ThemeChanged += ThemeListener_ThemeChanged;
- _themeToken = RegisterPropertyChangedCallback(ActualThemeProperty, ActualTheme_PropertyChanged);
+
+ _cssBroker = new CssStyleBroker(this);
Options = new StandaloneEditorConstructionOptions();
DiffOptions = new DiffEditorConstructionOptions();
@@ -66,8 +83,7 @@ public CodeEditor()
Options.PropertyChanged += Options_PropertyChanged;
DiffOptions.PropertyChanged += DiffOptions_PropertyChanged;
-
- Unloaded += CodeEditor_Unloaded;
+ _settingsProvider.SettingChanged += SettingsProvider_SettingChanged;
}
public static DependencyProperty IsEditorLoadedProperty { get; }
@@ -111,10 +127,17 @@ public bool IsDiffViewMode
string.Empty,
async (d, e) =>
{
- if (d is CodeEditor { IsSettingValue: false } codeEditor && codeEditor._initialized)
+ if (d is CodeEditor codeEditor && codeEditor._initialized)
{
- // link:otherScriptsToBeOrganized.ts:updateContent
- await codeEditor.InvokeScriptAsync("updateContent", e.NewValue.ToString() ?? string.Empty);
+ if (!codeEditor.IsSettingValue)
+ {
+ // link:otherScriptsToBeOrganized.ts:updateContent
+ await codeEditor.InvokeScriptAsync("updateContent", e.NewValue.ToString() ?? string.Empty);
+ }
+ else
+ {
+ codeEditor.TextChanged?.Invoke(d, EventArgs.Empty);
+ }
}
}));
@@ -127,47 +150,6 @@ public string Text
set => SetValue(TextProperty, value);
}
- public static DependencyProperty SelectedTextProperty { get; }
- = DependencyProperty.Register(
- nameof(SelectedText),
- typeof(string),
- typeof(CodeEditor),
- new PropertyMetadata(
- string.Empty,
- async (d, e) =>
- {
- if (d is CodeEditor { IsSettingValue: false } codeEditor && codeEditor._initialized)
- {
- // link:updateSelectedContent.ts:updateSelectedContent
- await codeEditor.InvokeScriptAsync("updateSelectedContent", e.NewValue.ToString() ?? string.Empty);
- }
- }));
-
- ///
- /// Gets the current Primary Selected CodeEditor Text, or replace the current selection by the given text.
- ///
- public string SelectedText
- {
- get => (string)GetValue(SelectedTextProperty);
- set => SetValue(SelectedTextProperty, value);
- }
-
- public static DependencyProperty SelectedRangeProperty { get; }
- = DependencyProperty.Register(
- nameof(SelectedRange),
- typeof(Selection),
- typeof(CodeEditor),
- new PropertyMetadata(null));
-
- ///
- /// Gets or sets the span to select in the editor
- ///
- public Selection SelectedRange
- {
- get => (Selection)GetValue(SelectedRangeProperty);
- internal set => SetValue(SelectedRangeProperty, value);
- }
-
public static DependencyProperty ReadOnlyProperty { get; }
= DependencyProperty.Register(
nameof(ReadOnly),
@@ -194,6 +176,38 @@ public Selection SelectedRange
}
}));
+ public static DependencyProperty SelectedSpanProperty { get; }
+ = DependencyProperty.Register(
+ nameof(SelectedSpan),
+ typeof(TextSpan),
+ typeof(CodeEditor),
+ new PropertyMetadata(
+ new TextSpan(0, 0),
+ async (d, e) =>
+ {
+ if (d is CodeEditor codeEditor && codeEditor._initialized)
+ {
+ if (!codeEditor.IsSettingValue)
+ {
+ // link:updateSelection.ts:updateSelectedSpan
+ await codeEditor.InvokeScriptAsync("updateSelectedSpan", e.NewValue ?? new TextSpan(0, 0));
+ }
+ else
+ {
+ codeEditor.SelectedSpanChanged?.Invoke(d, EventArgs.Empty);
+ }
+ }
+ }));
+
+ ///
+ /// Gets the current selection in the editor, or replace the current selection by the given span.
+ ///
+ public TextSpan SelectedSpan
+ {
+ get => (TextSpan)GetValue(SelectedSpanProperty);
+ set => SetValue(SelectedSpanProperty, value);
+ }
+
///
/// Gets or sets whether the editor is read-only.
///
@@ -220,6 +234,7 @@ public bool ReadOnly
if (editor.Options != null && editor._initialized)
{
editor.Options.Language = e.NewValue.ToString();
+ editor.Options.Folding = !string.IsNullOrWhiteSpace(editor.Options.Language) && !string.Equals("text", editor.Options.Language, StringComparison.OrdinalIgnoreCase);
}
}));
@@ -348,6 +363,8 @@ public DiffEditorConstructionOptions DiffOptions
public bool IsSettingValue { get; set; }
+ public Control UIHost => this;
+
internal ParentAccessor ParentAccessor { get; }
///
@@ -365,6 +382,10 @@ public DiffEditorConstructionOptions DiffOptions
///
public event TypedEventHandler? OpenLinkRequested;
+ public event EventHandler? TextChanged;
+
+ public event EventHandler? SelectedSpanChanged;
+
#if HAS_UNO
public new void Dispose()
#else
@@ -372,6 +393,52 @@ public void Dispose()
#endif
{
ParentAccessor.Dispose();
+ _cssBroker.Dispose();
+ }
+
+ public async Task HighlightSpansAsync(IReadOnlyList? spans)
+ {
+ if (!_initialized)
+ {
+ _spansToHighlight = spans;
+ return;
+ }
+
+ var newDecorationsAdjust = new List();
+
+ if (spans is not null)
+ {
+ for (int i = 0; i < spans.Count; i++)
+ {
+ TextSpan span = spans[i];
+ if (span.StartPosition + span.Length < Text.Length)
+ {
+ Position? startPosition = await GetModel().GetPositionAtAsync((uint)span.StartPosition);
+ Position? endPosition = await GetModel().GetPositionAtAsync((uint)(span.StartPosition + span.Length));
+ if (startPosition is not null && endPosition is not null)
+ {
+ newDecorationsAdjust.Add(
+ new IModelDeltaDecoration(
+ new Range(
+ startPosition.LineNumber,
+ startPosition.Column,
+ endPosition.LineNumber,
+ endPosition.Column),
+ HighlightedSpanStyle));
+ }
+ }
+ }
+ }
+
+ if (_cssBroker.AssociateStyles(newDecorationsAdjust))
+ {
+ // Update Styles First
+ await InvokeScriptAsync("updateStyle", _cssBroker.GetStyles());
+ }
+
+ // 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.
+ await InvokeScriptAsync("updateDecorations", (object)newDecorationsAdjust);
}
internal IModel GetModel()
@@ -399,18 +466,20 @@ internal async Task SendScriptAsync(
Guard.IsNotNull(_view);
try
{
- return await _view.RunScriptAsync(script, member, file, line);
+ return await _view.RunScriptAsync(script, serializeResult: false, member, file, line);
}
catch (Exception e)
{
+ LogInternalError(e);
InternalException?.Invoke(this, e);
}
}
else
{
-#if DEBUG
- Debug.WriteLine("WARNING: Tried to call '" + script + "' before initialized.");
-#endif
+ if (Debugger.IsAttached)
+ {
+ Debug.WriteLine("WARNING: Tried to call '" + script + "' before initialized.");
+ }
}
return default;
@@ -466,14 +535,16 @@ internal async Task InvokeScriptAsync(
}
catch (Exception e)
{
+ LogInternalError(e);
InternalException?.Invoke(this, e);
}
}
else
{
-#if DEBUG
- Debug.WriteLine("WARNING: Tried to call '" + method + "' before initialized.");
-#endif
+ if (Debugger.IsAttached)
+ {
+ Debug.WriteLine("WARNING: Tried to call '" + method + "' before initialized.");
+ }
}
return default;
@@ -516,26 +587,6 @@ protected override void OnGotFocus(RoutedEventArgs e)
GiveFocusToInnerEditor();
}
- private void CodeEditor_Unloaded(object sender, RoutedEventArgs e)
- {
- if (_view != null)
- {
- _view.NavigationStarting -= WebView_NavigationStarting;
- _view.DOMContentLoaded -= WebView_DOMContentLoaded;
- _view.NavigationCompleted -= WebView_NavigationCompleted;
- _view.NewWindowRequested -= WebView_NewWindowRequested;
- _view.DotNetObjectInjectionRequested -= WebView_DotNetObjectInjectionRequested;
- }
-
- Options.PropertyChanged -= Options_PropertyChanged;
- DiffOptions.PropertyChanged -= DiffOptions_PropertyChanged;
- _themeListener.ThemeChanged -= ThemeListener_ThemeChanged;
-
- UnregisterPropertyChangedCallback(ActualThemeProperty, _themeToken);
-
- _initialized = false;
- }
-
private void Options_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is not StandaloneEditorConstructionOptions options || IsDiffViewMode || !_initialized)
@@ -587,29 +638,23 @@ private void DiffOptions_PropertyChanged(object? sender, PropertyChangedEventArg
}
}
- private void ActualTheme_PropertyChanged(DependencyObject obj, DependencyProperty property)
+ private void ThemeListener_ThemeChanged(ThemeListener sender)
{
- ElementTheme theme = ActualTheme;
- string themeName;
- if (theme == ElementTheme.Default)
- {
- themeName = _themeListener.CurrentThemeName;
- }
- else
- {
- themeName = theme.ToString();
- }
-
- InvokeScriptAsync("setTheme", args: new string[] { _themeListener!.AccentColorHtmlHex }).Forget();
- InvokeScriptAsync("changeTheme", new string[] { themeName, _themeListener.IsHighContrast.ToString() }).Forget();
+ InvokeScriptAsync(
+ "setTheme",
+ args: new string[] { sender.AccentColorHtmlHex })
+ .Forget();
+ InvokeScriptAsync(
+ "changeTheme",
+ args: new object[] { sender.CurrentTheme.ToString(), sender.IsHighContrast, IsFocusEngaged })
+ .Forget();
}
- private void ThemeListener_ThemeChanged(ThemeListener sender)
+ private void SettingsProvider_SettingChanged(object? sender, SettingChangedEventArgs e)
{
- if (RequestedTheme == ElementTheme.Default)
+ if (e.SettingName.Contains("TextEditor"))
{
- InvokeScriptAsync("setTheme", args: new string[] { sender.AccentColorHtmlHex }).Forget();
- InvokeScriptAsync("changeTheme", args: new string[] { sender.CurrentTheme.ToString(), sender.IsHighContrast.ToString() }).Forget();
+ ApplySettings();
}
}
@@ -651,6 +696,10 @@ private void OnMonacoEditorGotFocus()
_focusCount++;
if (_focusCount > 0)
{
+ InvokeScriptAsync(
+ "changeTheme",
+ args: new object[] { _themeListener.CurrentTheme.ToString(), _themeListener.IsHighContrast, true })
+ .Forget();
VisualStateManager.GoToState(this, FocusedState, false);
}
}
@@ -660,6 +709,10 @@ private void OnMonacoEditorLostFocus()
_focusCount = Math.Max(0, _focusCount - 1);
if (_focusCount == 0)
{
+ InvokeScriptAsync(
+ "changeTheme",
+ args: new object[] { _themeListener.CurrentTheme.ToString(), _themeListener.IsHighContrast, false })
+ .Forget();
VisualStateManager.GoToState(this, NormalState, false);
}
}
@@ -677,19 +730,42 @@ private void OnMonacoEditorLoaded()
.Forget();
InvokeScriptAsync(
"changeTheme",
- new string[] { _themeListener.CurrentTheme.ToString(), _themeListener.IsHighContrast.ToString() })
+ new object[] { _themeListener.CurrentTheme.ToString(), _themeListener.IsHighContrast, IsFocusEngaged })
.Forget();
// Update options.
_refrainFromUpdatingOptionsInternally = true;
Options.SmoothScrolling = true;
+ Options.GlyphMargin = false;
+ Options.MouseWheelZoom = false;
+ Options.OverviewRulerBorder = false;
+ Options.ScrollBeyondLastLine = false;
+ Options.FontLigatures = false;
+ Options.SnippetSuggestions = SnippetSuggestions.None;
+ Options.CodeLens = true;
+ Options.QuickSuggestions = false;
+ Options.WordBasedSuggestions = false;
Options.Minimap = new EditorMinimapOptions() { Enabled = false };
+ Options.ShowFoldingControls = Show.Always;
Options.ReadOnly = ReadOnly;
Options.Language = CodeLanguage;
+ Options.Folding = !string.IsNullOrWhiteSpace(CodeLanguage) && !string.Equals("text", CodeLanguage, StringComparison.OrdinalIgnoreCase);
+ DiffOptions.SmoothScrolling = true;
+ DiffOptions.GlyphMargin = false;
+ DiffOptions.MouseWheelZoom = false;
+ DiffOptions.OverviewRulerBorder = false;
+ DiffOptions.ScrollBeyondLastLine = false;
+ DiffOptions.FontLigatures = false;
+ DiffOptions.SnippetSuggestions = SnippetSuggestions.None;
+ DiffOptions.CodeLens = true;
+ DiffOptions.QuickSuggestions = false;
+ DiffOptions.ShowFoldingControls = Show.Always;
DiffOptions.OriginalEditable = !ReadOnly;
DiffOptions.ReadOnly = ReadOnly;
+ ApplySettings();
+
_refrainFromUpdatingOptionsInternally = false;
if (IsDiffViewMode)
{
@@ -700,11 +776,32 @@ private void OnMonacoEditorLoaded()
InvokeScriptAsync("updateOptions", Options).Forget();
}
+ // Set text, selection, highlighted spans that may have been set but not sent to the editor yet.
+ InvokeScriptAsync("updateContent", Text ?? string.Empty).Forget();
+ InvokeScriptAsync("updateSelectedSpan", SelectedSpan).Forget();
+ HighlightSpansAsync(_spansToHighlight).Forget();
+
// We're done loading Monaco Editor.
IsEditorLoaded = true;
EditorLoaded?.Invoke(this, new RoutedEventArgs());
}
+ private void ApplySettings()
+ {
+ Options.WordWrapMinified = _settingsProvider.GetSetting(PredefinedSettings.TextEditorTextWrapping);
+ Options.WordWrap = _settingsProvider.GetSetting(PredefinedSettings.TextEditorTextWrapping) ? WordWrap.On : WordWrap.Off;
+ Options.LineNumbers = _settingsProvider.GetSetting(PredefinedSettings.TextEditorLineNumbers) ? LineNumbersType.On : LineNumbersType.Off;
+ Options.RenderLineHighlight = _settingsProvider.GetSetting(PredefinedSettings.TextEditorHighlightCurrentLine) ? RenderLineHighlight.All : RenderLineHighlight.None;
+ Options.RenderWhitespace = _settingsProvider.GetSetting(PredefinedSettings.TextEditorRenderWhitespace) ? RenderWhitespace.All : RenderWhitespace.None;
+ Options.FontFamily = _settingsProvider.GetSetting(PredefinedSettings.TextEditorFont);
+ DiffOptions.WordWrapMinified = _settingsProvider.GetSetting(PredefinedSettings.TextEditorTextWrapping);
+ DiffOptions.WordWrap = _settingsProvider.GetSetting(PredefinedSettings.TextEditorTextWrapping) ? WordWrap.On : WordWrap.Off;
+ DiffOptions.LineNumbers = _settingsProvider.GetSetting(PredefinedSettings.TextEditorLineNumbers) ? LineNumbersType.On : LineNumbersType.Off;
+ DiffOptions.RenderLineHighlight = _settingsProvider.GetSetting(PredefinedSettings.TextEditorHighlightCurrentLine) ? RenderLineHighlight.All : RenderLineHighlight.None;
+ DiffOptions.RenderWhitespace = _settingsProvider.GetSetting(PredefinedSettings.TextEditorRenderWhitespace) ? RenderWhitespace.All : RenderWhitespace.None;
+ DiffOptions.FontFamily = _settingsProvider.GetSetting(PredefinedSettings.TextEditorFont);
+ }
+
private void GiveFocusToInnerEditor()
{
if (_initialized)
@@ -715,4 +812,7 @@ private void GiveFocusToInnerEditor()
SendScriptAsync("editorContext.editor.focus();").Forget();
}
}
+
+ [LoggerMessage(2, LogLevel.Error, "An error occured related to the Monaco Editor.")]
+ partial void LogInternalError(Exception ex);
}
diff --git a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Wasm.cs b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Wasm.cs
index 96c5d51f66..7e8390d232 100644
--- a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Wasm.cs
+++ b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Wasm.cs
@@ -168,7 +168,7 @@ public async Task InvokeScriptAsync(string script)
}}
}})()";
- _debugLogger?.Debug($"Invoke Script: {script}");
+ _debugLogger?.Debug($"Invoking script");
try
{
diff --git a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Windows.cs b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Windows.cs
index 4bce657f36..bad366f38b 100644
--- a/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Windows.cs
+++ b/src/app/dev/DevToys.MonacoEditor/CodeEditor/CodeEditorPresenter.Windows.cs
@@ -13,6 +13,7 @@
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System.Runtime.CompilerServices;
+using Microsoft.UI.Xaml.Media;
namespace DevToys.MonacoEditor;
@@ -57,8 +58,9 @@ public CodeEditorPresenter()
{
_logger = this.Log();
- // make the WebView2 transparent.
- Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
+ // Fill the WebView2 with ControlFillColorInputActive.
+ var controlFillColorInputActive = (Windows.UI.Color)Application.Current.Resources["ControlFillColorInputActive"];
+ Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", $"{controlFillColorInputActive.R:X2}{controlFillColorInputActive.G:X2}{controlFillColorInputActive.B:X2}");
Content = _webView;
@@ -92,6 +94,18 @@ public async Task LaunchAsync()
{
await _webView.EnsureCoreWebView2Async();
+ _webView.CoreWebView2.Settings.IsZoomControlEnabled = false;
+ _webView.CoreWebView2.Settings.IsPinchZoomEnabled = false;
+ _webView.CoreWebView2.Settings.IsSwipeNavigationEnabled = false;
+ _webView.CoreWebView2.Settings.IsStatusBarEnabled = false;
+ _webView.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
+ _webView.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
+ _webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
+#if !DEBUG
+ _webView.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled = false;
+ _webView.CoreWebView2.Settings.AreDevToolsEnabled = false;
+#endif
+
string path = Path.Combine(AppContext.BaseDirectory, "DevToys.MonacoEditor", "CodeEditor", "CodeEditor.Windows.html");
if (!File.Exists(path))
{
@@ -359,7 +373,7 @@ public async Task InvokeScriptAsync(string script)
}}
}})();";
- LogInvokingJavaScript(script);
+ LogInvokingJavaScript();
try
{
@@ -415,8 +429,8 @@ private void WebView_CoreWebView2Initialized(WebView2 sender, CoreWebView2Initia
[LoggerMessage(7, LogLevel.Error, "{caller}-CALLBACK: Exception in {name}.{propertyName}.")]
partial void LogDotNetObjectInjectionCallbackPropertyFailed(string name, string propertyName, Exception exception, [CallerMemberName] string? caller = null);
- [LoggerMessage(8, LogLevel.Debug, "{caller}: Invoking JavaScript: {script}")]
- partial void LogInvokingJavaScript(string script, [CallerMemberName] string? caller = null);
+ [LoggerMessage(8, LogLevel.Debug, "{caller}: Invoking JavaScript...")]
+ partial void LogInvokingJavaScript([CallerMemberName] string? caller = null);
[LoggerMessage(9, LogLevel.Debug, "{caller}: JavaScript invoked successfully: {result}")]
partial void LogInvokedJavaScriptSuccessfully(string result, [CallerMemberName] string? caller = null);
diff --git a/src/app/dev/DevToys.MonacoEditor/Extensions/ICodeEditorPresenterExtensions.cs b/src/app/dev/DevToys.MonacoEditor/Extensions/ICodeEditorPresenterExtensions.cs
index de7e5da043..1617efd93e 100644
--- a/src/app/dev/DevToys.MonacoEditor/Extensions/ICodeEditorPresenterExtensions.cs
+++ b/src/app/dev/DevToys.MonacoEditor/Extensions/ICodeEditorPresenterExtensions.cs
@@ -14,18 +14,19 @@ public static async Task RunScriptAsync(
[CallerFilePath] string? file = null,
[CallerLineNumber] int line = 0)
{
- await view.RunScriptAsync