writing in the text editor? easy!
This commit is contained in:
parent
7d9e81c60f
commit
69ef06afd9
|
|
@ -1,35 +1,52 @@
|
||||||
using System;
|
using System.Windows;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Editor.Controls;
|
namespace Qrakhen.Qamp.Editor.Controls;
|
||||||
|
|
||||||
public class Caret : Adorner
|
public class Caret : Adorner
|
||||||
{
|
{
|
||||||
private readonly Pen _caretPen;
|
private readonly Pen _pen;
|
||||||
private readonly double _lineHeight;
|
private readonly double _height;
|
||||||
|
private readonly Brush _brush;
|
||||||
|
private bool _blink;
|
||||||
private Point _cursorPosition;
|
private Point _cursorPosition;
|
||||||
|
private DispatcherTimer _timer;
|
||||||
|
|
||||||
public Caret(UIElement adornedElement, double lineHeight) : base(adornedElement)
|
public Caret(UIElement adornedElement, Brush brush, double height = 18, double thickness = 1.72) : base(adornedElement)
|
||||||
{
|
{
|
||||||
_lineHeight = lineHeight;
|
|
||||||
_caretPen = new Pen(Brushes.LimeGreen, 1.5);
|
|
||||||
IsHitTestVisible = false;
|
IsHitTestVisible = false;
|
||||||
|
|
||||||
|
_height = height;
|
||||||
|
_brush = brush;
|
||||||
|
_pen = new Pen(brush, 1.5);
|
||||||
|
_timer = new DispatcherTimer();
|
||||||
|
_timer.Interval = TimeSpan.FromMilliseconds(428);
|
||||||
|
_timer.Tick += new EventHandler((s, e) => {
|
||||||
|
Toggle(_blink = !_blink);
|
||||||
|
});
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Toggle(bool hide)
|
||||||
|
{
|
||||||
|
_pen.Brush = hide ? Brushes.Transparent : _brush;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdatePosition(Point newPosition)
|
public void UpdatePosition(Point newPosition)
|
||||||
{
|
{
|
||||||
|
_timer.Stop();
|
||||||
|
Toggle(_blink = false);
|
||||||
_cursorPosition = newPosition;
|
_cursorPosition = newPosition;
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
|
_timer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRender(DrawingContext drawingContext)
|
protected override void OnRender(DrawingContext drawingContext)
|
||||||
{
|
{
|
||||||
Point startPoint = new(_cursorPosition.X, _cursorPosition.Y);
|
Point startPoint = new(_cursorPosition.X, _cursorPosition.Y);
|
||||||
Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _lineHeight);
|
Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _height);
|
||||||
drawingContext.DrawLine(_caretPen, startPoint, endPoint);
|
drawingContext.DrawLine(_pen, startPoint, endPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
<UserControl.Style>
|
<UserControl.Style>
|
||||||
<Style TargetType="{x:Type local:EditorFrame}">
|
<Style TargetType="{x:Type local:EditorFrame}">
|
||||||
<Setter Property="CursorPosition" Value="{Binding CursorPosition}" />
|
<Setter Property="CursorPosition" Value="{Binding CursorPosition}" />
|
||||||
|
<Setter Property="CurrentLineBuffer" Value="{Binding CurrentLineBuffer}" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Style>
|
</UserControl.Style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
using Qrakhen.Qamp.Editor.Primitives;
|
using Qrakhen.Qamp.Editor.Primitives;
|
||||||
using Qrakhen.Qamp.Editor.ViewModel;
|
using Qrakhen.Qamp.Editor.ViewModel;
|
||||||
|
using Qrakhen.Qamp.Memory;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Editor.Controls;
|
namespace Qrakhen.Qamp.Editor.Controls;
|
||||||
|
|
||||||
|
|
@ -28,19 +31,31 @@ public partial class EditorFrame : UserControl
|
||||||
Key.Down => new(1, 0),
|
Key.Down => new(1, 0),
|
||||||
_ => new(0, 0)
|
_ => new(0, 0)
|
||||||
};
|
};
|
||||||
if (vm.MoveCaretCommand.CanExecute(position)) {
|
if (position != CursorPosition.Void &&
|
||||||
|
vm.MoveCaretCommand.CanExecute(position))
|
||||||
|
{
|
||||||
vm.MoveCaretCommand.Execute(position);
|
vm.MoveCaretCommand.Execute(position);
|
||||||
e.Handled = true;
|
} else {
|
||||||
|
if (e.Key == Key.Back) {
|
||||||
|
vm.Delete(1);
|
||||||
|
} else {
|
||||||
|
string c = e.Key.ToString();
|
||||||
|
if (c.Length == 1) {
|
||||||
|
vm.Insert(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Focus();
|
Focus();
|
||||||
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||||
if (adornerLayer != null) {
|
if (adornerLayer != null) {
|
||||||
Caret = new Caret(this, 18);
|
Caret = new Caret(this, Brushes.LimeGreen);
|
||||||
adornerLayer.Add(Caret);
|
adornerLayer.Add(Caret);
|
||||||
} else {
|
} else {
|
||||||
System.Diagnostics.Debug.WriteLine("AdornerLayer still null. Check parent hierarchy.");
|
System.Diagnostics.Debug.WriteLine("AdornerLayer still null. Check parent hierarchy.");
|
||||||
|
|
@ -50,7 +65,8 @@ public partial class EditorFrame : UserControl
|
||||||
|
|
||||||
private Point CalculateCaretPosition(int line, int index, double scrollY, double scrollX)
|
private Point CalculateCaretPosition(int line, int index, double scrollY, double scrollX)
|
||||||
{
|
{
|
||||||
return new Point(10 * index + scrollX, line * 18 + scrollY);
|
var ft = TextHelper.GetText(new string('_', index), 14, null, VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
||||||
|
return new Point(ft.Width + scrollX + 2, line * TextHelper.LineHeight + scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoveCaret(Point point)
|
private void MoveCaret(Point point)
|
||||||
|
|
@ -74,6 +90,18 @@ public partial class EditorFrame : UserControl
|
||||||
scrollX));
|
scrollX));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LineBuffer CurrentLineBuffer {
|
||||||
|
get => (LineBuffer)GetValue(CurrentLineBufferProperty);
|
||||||
|
set => SetValue(CurrentLineBufferProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CurrentLineBufferProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(CurrentLineBuffer),
|
||||||
|
typeof(LineBuffer),
|
||||||
|
typeof(EditorFrame),
|
||||||
|
new PropertyMetadata(null));
|
||||||
|
|
||||||
public CursorPosition CursorPosition {
|
public CursorPosition CursorPosition {
|
||||||
get => (CursorPosition)GetValue(CursorPositionProperty);
|
get => (CursorPosition)GetValue(CursorPositionProperty);
|
||||||
set => SetValue(CursorPositionProperty, value);
|
set => SetValue(CursorPositionProperty, value);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Qrakhen.Qamp.Memory;
|
using Qrakhen.Qamp.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
@ -21,7 +22,7 @@ public partial class LineRenderer : UserControl
|
||||||
nameof(LineBuffer),
|
nameof(LineBuffer),
|
||||||
typeof(LineBuffer),
|
typeof(LineBuffer),
|
||||||
typeof(LineRenderer),
|
typeof(LineRenderer),
|
||||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
|
new FrameworkPropertyMetadata(null, OnLineBufferChanged));
|
||||||
|
|
||||||
public LineBuffer LineBuffer {
|
public LineBuffer LineBuffer {
|
||||||
get => (LineBuffer)GetValue(LineBufferProperty);
|
get => (LineBuffer)GetValue(LineBufferProperty);
|
||||||
|
|
@ -45,11 +46,8 @@ public partial class LineRenderer : UserControl
|
||||||
if (string.IsNullOrEmpty(LineBuffer.Cached))
|
if (string.IsNullOrEmpty(LineBuffer.Cached))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var formattedText = new FormattedText(
|
var formattedText = TextHelper.GetText(
|
||||||
LineBuffer.Cached,
|
LineBuffer.Cached,
|
||||||
System.Globalization.CultureInfo.CurrentCulture,
|
|
||||||
FlowDirection.LeftToRight,
|
|
||||||
_typeface,
|
|
||||||
FontSize,
|
FontSize,
|
||||||
Brushes.White,
|
Brushes.White,
|
||||||
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
||||||
|
|
@ -57,4 +55,25 @@ public partial class LineRenderer : UserControl
|
||||||
|
|
||||||
drawingContext.DrawText(formattedText, new Point(2, 0));
|
drawingContext.DrawText(formattedText, new Point(2, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnLineBufferChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is LineRenderer renderer) {
|
||||||
|
if (e.OldValue is LineBuffer oldBuffer && oldBuffer != null) {
|
||||||
|
oldBuffer.PropertyChanged -= renderer.OnLineBufferPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewValue is LineBuffer newBuffer && newBuffer != null) {
|
||||||
|
newBuffer.PropertyChanged += renderer.OnLineBufferPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.InvalidateVisual();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLineBufferPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(LineBuffer.Cached))
|
||||||
|
InvalidateVisual();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ public record struct CursorPosition(int line = 0, int column = 0)
|
||||||
public static readonly CursorPosition Up = new(-1, 0);
|
public static readonly CursorPosition Up = new(-1, 0);
|
||||||
public static readonly CursorPosition Down = new(1, 0);
|
public static readonly CursorPosition Down = new(1, 0);
|
||||||
|
|
||||||
public int Line = line < 0 ? 0 : line;
|
public int Line = line;
|
||||||
public int Column = column < 0 ? 0 : column;
|
public int Column = column;
|
||||||
|
|
||||||
public static CursorPosition operator +(CursorPosition left, CursorPosition right)
|
public static CursorPosition operator +(CursorPosition left, CursorPosition right)
|
||||||
=> new(left.Line + right.Line, left.Column + right.Column);
|
=> new(left.Line + right.Line, left.Column + right.Column);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace Qrakhen.Qamp.Editor;
|
||||||
|
|
||||||
|
public static class TextHelper // temp
|
||||||
|
{
|
||||||
|
public static double Size { get; set; } = 14;
|
||||||
|
public static double LineHeight { get; set; } = 18;
|
||||||
|
public static Brush DefaultColor { get; set; } = Brushes.AntiqueWhite;
|
||||||
|
public static Typeface Typeface { get; set; } = new(
|
||||||
|
new FontFamily("Consolas"),
|
||||||
|
FontStyles.Normal,
|
||||||
|
FontWeights.Normal,
|
||||||
|
FontStretches.Normal);
|
||||||
|
|
||||||
|
public static FormattedText GetText(string text, double size, Brush? brush = null, double dpiScale = 1f)
|
||||||
|
{
|
||||||
|
return new FormattedText(
|
||||||
|
text,
|
||||||
|
System.Globalization.CultureInfo.CurrentCulture,
|
||||||
|
FlowDirection.LeftToRight,
|
||||||
|
Typeface,
|
||||||
|
size,
|
||||||
|
brush ?? DefaultColor,
|
||||||
|
dpiScale
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -51,6 +51,7 @@ public class EditorFrameViewModel : ObservableObject
|
||||||
public void MoveCursor(int lines, int columns)
|
public void MoveCursor(int lines, int columns)
|
||||||
{
|
{
|
||||||
SetCursor(CursorLine + lines, CursorColumn + columns);
|
SetCursor(CursorLine + lines, CursorColumn + columns);
|
||||||
|
OnPropertyChanged(nameof(CurrentLineBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanMoveCaret(object? relativeUpdate)
|
public bool CanMoveCaret(object? relativeUpdate)
|
||||||
|
|
@ -64,9 +65,19 @@ public class EditorFrameViewModel : ObservableObject
|
||||||
MoveCursor(pos.Line, pos.Column);
|
MoveCursor(pos.Line, pos.Column);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Insert(string data)
|
public void Delete(int count = 1, bool follow = true)
|
||||||
|
{
|
||||||
|
if (follow)
|
||||||
|
MoveCursor(0, -count);
|
||||||
|
CurrentLineBuffer.Delete(CursorColumn, count);
|
||||||
|
OnPropertyChanged(nameof(Lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(string data, bool follow = true)
|
||||||
{
|
{
|
||||||
CurrentLineBuffer.Insert(CursorColumn, data);
|
CurrentLineBuffer.Insert(CursorColumn, data);
|
||||||
|
if (follow)
|
||||||
|
MoveCursor(0, data.Length);
|
||||||
OnPropertyChanged(nameof(Lines));
|
OnPropertyChanged(nameof(Lines));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
using System.Text;
|
using System.ComponentModel;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Memory;
|
namespace Qrakhen.Qamp.Memory;
|
||||||
|
|
||||||
public sealed class LineBuffer : TailBuffer<byte>
|
public sealed class LineBuffer : TailBuffer<byte>, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public Encoding Encoding { get; }
|
public Encoding Encoding { get; }
|
||||||
public string Cached { get; private set; } = "";
|
public string Cached { get; private set; } = "";
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
public LineBuffer(Encoding encoding, int size = 0x80) : base(size)
|
public LineBuffer(Encoding encoding, int size = 0x80) : base(size)
|
||||||
{
|
{
|
||||||
|
|
@ -23,6 +25,7 @@ public sealed class LineBuffer : TailBuffer<byte>
|
||||||
{
|
{
|
||||||
base.SetTail(tail);
|
base.SetTail(tail);
|
||||||
Cached = ToString();
|
Cached = ToString();
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Cached)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Insert(int cursor, string data)
|
public void Insert(int cursor, string data)
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,9 @@ public abstract class TailBuffer<T>
|
||||||
private void Prepare(int index, int count)
|
private void Prepare(int index, int count)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
||||||
if (index + count <= _data.Length)
|
if (count + Tail <= _data.Length)
|
||||||
return;
|
return;
|
||||||
while (index + count > _data.Length) {
|
while (count + Tail > _data.Length) {
|
||||||
T[] grow = new T[_data.Length * 2];
|
T[] grow = new T[_data.Length * 2];
|
||||||
Array.Copy(_data, grow, _data.Length);
|
Array.Copy(_data, grow, _data.Length);
|
||||||
_data = grow;
|
_data = grow;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue