From 1fb603b5d79d15a5b020cdd8cc4d84fe40a32407 Mon Sep 17 00:00:00 2001 From: Tom Laird-McConnell Date: Tue, 17 Dec 2024 10:50:46 -0800 Subject: [PATCH] fix caret cursor (#212) * changed CaretControl template to output a Rectangle * Changed DrawRectangle to detect MoveCaret brushes * Refactored DrawLine to detect MoveCaret Brushes. --- src/Consolonia.Core/Drawing/CaretStyle.cs | 12 ++++++ .../Drawing/DrawingContextImpl.cs | 27 +++++++++---- .../MoveConsoleCaretToPositionBrush.cs | 14 ++++++- .../PixelBufferImplementation/PixelBuffer.cs | 2 + src/Consolonia.Core/Drawing/RenderTarget.cs | 13 +++++++ .../InputLessDefaultNetConsole.cs | 14 +------ src/Consolonia.Core/Text/Esc.cs | 8 ++++ .../Templates/Controls/CaretControl.axaml | 38 ++++++------------- .../Controls/Helpers/CaretControl.cs | 9 +++++ 9 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 src/Consolonia.Core/Drawing/CaretStyle.cs diff --git a/src/Consolonia.Core/Drawing/CaretStyle.cs b/src/Consolonia.Core/Drawing/CaretStyle.cs new file mode 100644 index 00000000..c3467803 --- /dev/null +++ b/src/Consolonia.Core/Drawing/CaretStyle.cs @@ -0,0 +1,12 @@ +namespace Consolonia.Core.Drawing +{ + public enum CaretStyle + { + BlinkingBar, + SteadyBar, + BlinkingBlock, + SteadyBlock, + BlinkingUnderline, + SteadyUnderline + } +} \ No newline at end of file diff --git a/src/Consolonia.Core/Drawing/DrawingContextImpl.cs b/src/Consolonia.Core/Drawing/DrawingContextImpl.cs index 826aaf18..3582dee6 100644 --- a/src/Consolonia.Core/Drawing/DrawingContextImpl.cs +++ b/src/Consolonia.Core/Drawing/DrawingContextImpl.cs @@ -261,6 +261,17 @@ public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) if (sceneBrushContent != null) sceneBrushContent.Render(this, Matrix.Identity); return; } + case MoveConsoleCaretToPositionBrush moveBrush: + { + Point head = r.TopLeft.Transform(Transform); + _pixelBuffer.CaretStyle = moveBrush.CaretStyle; + CurrentClip.ExecuteWithClipping(head, + () => + { + _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); + }); + return; + } } FillRectangleWithBrush(brush, pen, r); @@ -458,6 +469,15 @@ private void DrawLineInternal(IPen pen, Line line) return; } + if (pen.Brush is MoveConsoleCaretToPositionBrush moveBrush) + { + _pixelBuffer.CaretStyle = moveBrush.CaretStyle; + Point head = line.PStart.Transform(Transform); + CurrentClip.ExecuteWithClipping(head, + () => { _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); }); + return; + } + DrawBoxLineInternal(pen, line, RectangleLinePosition.Unknown); } @@ -532,13 +552,6 @@ private void DrawBoxLineInternal(IPen pen, Line line, RectangleLinePosition line Point head = line.PStart; - if (pen.Brush is MoveConsoleCaretToPositionBrush) - { - CurrentClip.ExecuteWithClipping(head, - () => { _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); }); - return; - } - var extractColorCheckPlatformSupported = ExtractColorOrNullWithPlatformCheck(pen, out var lineStyle); if (extractColorCheckPlatformSupported == null) return; diff --git a/src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs b/src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs index 0f6f89e7..d91a2c57 100644 --- a/src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs +++ b/src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs @@ -3,8 +3,20 @@ namespace Consolonia.Core.Drawing { - public class MoveConsoleCaretToPositionBrush : IImmutableBrush + public class MoveConsoleCaretToPositionBrush : AvaloniaObject, IImmutableBrush { + public static readonly StyledProperty CaretStyleProperty = + AvaloniaProperty.Register(nameof(CaretStyle)); + + /// + /// style of caret + /// + public CaretStyle CaretStyle + { + get => GetValue(CaretStyleProperty); + set => SetValue(CaretStyleProperty, value); + } + //todo: Search for B75ABC91-2CDD-4557-9201-16AC483C8D7B public double Opacity => 1; public ITransform Transform => null; diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs index 8ffb2aaa..618c7e38 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs @@ -32,6 +32,8 @@ public PixelBuffer(ushort width, ushort height) public ushort Width { get; } public ushort Height { get; } + public CaretStyle CaretStyle { get; set; } = CaretStyle.BlinkingBar; + // ReSharper disable once UnusedMember.Global [JsonIgnore] public Pixel this[int i] diff --git a/src/Consolonia.Core/Drawing/RenderTarget.cs b/src/Consolonia.Core/Drawing/RenderTarget.cs index 35665b86..5bb18d05 100644 --- a/src/Consolonia.Core/Drawing/RenderTarget.cs +++ b/src/Consolonia.Core/Drawing/RenderTarget.cs @@ -10,6 +10,7 @@ using Avalonia.Platform; using Consolonia.Core.Drawing.PixelBufferImplementation; using Consolonia.Core.Infrastructure; +using Consolonia.Core.Text; namespace Consolonia.Core.Drawing { @@ -141,11 +142,23 @@ private void RenderToDevice() if (caretPosition != null) { _console.SetCaretPosition((PixelBufferCoordinate)caretPosition); + _console.WriteText(pixelBuffer.CaretStyle switch + { + CaretStyle.BlinkingBar => Esc.BlinkingBarCursor, + CaretStyle.SteadyBar => Esc.SteadyBarCursor, + CaretStyle.BlinkingBlock => Esc.BlinkingBlockCursor, + CaretStyle.SteadyBlock => Esc.SteadyBlockCursor, + CaretStyle.BlinkingUnderline => Esc.BlinkingUnderlineCursor, + CaretStyle.SteadyUnderline => Esc.SteadyUnderlineCursor, + _ => throw new ArgumentOutOfRangeException() + }); + _console.WriteText(Esc.ShowCursor); _console.CaretVisible = true; } else { _console.CaretVisible = false; + _console.WriteText(Esc.HideCursor); } } diff --git a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs index fb59bfab..dff6c716 100644 --- a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs @@ -14,7 +14,6 @@ namespace Consolonia.Core.Infrastructure public abstract class InputLessDefaultNetConsole : IConsole { private const string TestEmoji = "👨‍👩‍👧‍👦"; - private bool _caretVisible; private PixelBufferCoordinate _headBufferPoint; private bool? _supportEmoji; @@ -32,18 +31,7 @@ protected InputLessDefaultNetConsole() protected Task PauseTask { get; private set; } - public bool CaretVisible - { - get => _caretVisible; -#pragma warning disable CA1303 // Do not pass literals as localized parameters - set - { - if (_caretVisible == value) return; - WriteText(value ? Esc.ShowCursor : Esc.HideCursor); - _caretVisible = value; - } -#pragma warning restore CA1303 // Do not pass literals as localized parameters - } + public bool CaretVisible { get; set; } public PixelBufferSize Size { get; private set; } diff --git a/src/Consolonia.Core/Text/Esc.cs b/src/Consolonia.Core/Text/Esc.cs index 9eb86541..4d321f0c 100644 --- a/src/Consolonia.Core/Text/Esc.cs +++ b/src/Consolonia.Core/Text/Esc.cs @@ -31,6 +31,14 @@ internal static class Esc public const string HideCursor = "\u001b[?25l"; public const string ShowCursor = "\u001b[?25h"; + // cursor shape + public const string BlinkingBlockCursor = "\u001b[1 q"; + public const string SteadyBlockCursor = "\u001b[2 q"; + public const string BlinkingUnderlineCursor = "\u001b[3 q"; + public const string SteadyUnderlineCursor = "\u001b[4 q"; + public const string BlinkingBarCursor = "\u001b[5 q"; + public const string SteadyBarCursor = "\u001b[6 q"; + // move cursor public static string MoveCursorUp(int n) { diff --git a/src/Consolonia.Themes/Templates/Controls/CaretControl.axaml b/src/Consolonia.Themes/Templates/Controls/CaretControl.axaml index d631252e..8ce6f1e1 100644 --- a/src/Consolonia.Themes/Templates/Controls/CaretControl.axaml +++ b/src/Consolonia.Themes/Templates/Controls/CaretControl.axaml @@ -10,37 +10,23 @@ - - - - - - - - + + + + + + diff --git a/src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs b/src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs index c16d63bb..d763d924 100644 --- a/src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs +++ b/src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Consolonia.Core.Drawing; namespace Consolonia.Themes.Templates.Controls.Helpers { @@ -9,6 +10,8 @@ public class CaretControl : ContentControl public static readonly StyledProperty IsCaretShownProperty = AvaloniaProperty.Register(nameof(IsCaretShown)); + public static readonly StyledProperty CaretStyleProperty = + AvaloniaProperty.Register(nameof(CaretStyle)); public bool IsCaretShown { @@ -16,6 +19,12 @@ public bool IsCaretShown set => SetValue(IsCaretShownProperty, value); } + public CaretStyle CaretStyle + { + get => GetValue(CaretStyleProperty); + set => SetValue(CaretStyleProperty, value); + } + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e);