diff --git a/Qrakhen.Qamp.Editor/Controls/Caret.cs b/Qrakhen.Qamp.Editor/Controls/Caret.cs
index 7643fd9..99e424a 100644
--- a/Qrakhen.Qamp.Editor/Controls/Caret.cs
+++ b/Qrakhen.Qamp.Editor/Controls/Caret.cs
@@ -1,35 +1,52 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows;
+using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
+using System.Windows.Threading;
namespace Qrakhen.Qamp.Editor.Controls;
public class Caret : Adorner
{
- private readonly Pen _caretPen;
- private readonly double _lineHeight;
+ private readonly Pen _pen;
+ private readonly double _height;
+ private readonly Brush _brush;
+ private bool _blink;
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;
+
+ _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)
{
+ _timer.Stop();
+ Toggle(_blink = false);
_cursorPosition = newPosition;
InvalidateVisual();
+ _timer.Start();
}
protected override void OnRender(DrawingContext drawingContext)
{
Point startPoint = new(_cursorPosition.X, _cursorPosition.Y);
- Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _lineHeight);
- drawingContext.DrawLine(_caretPen, startPoint, endPoint);
+ Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _height);
+ drawingContext.DrawLine(_pen, startPoint, endPoint);
}
}
diff --git a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml
index 295e860..d2641cf 100644
--- a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml
+++ b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml
@@ -12,6 +12,7 @@
diff --git a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs
index 5be395b..a4701c3 100644
--- a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs
+++ b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs
@@ -1,9 +1,12 @@
using Qrakhen.Qamp.Editor.Primitives;
using Qrakhen.Qamp.Editor.ViewModel;
+using Qrakhen.Qamp.Memory;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
namespace Qrakhen.Qamp.Editor.Controls;
@@ -20,7 +23,7 @@ public partial class EditorFrame : UserControl
private void OnKeyDown(object sender, KeyEventArgs e)
{
- if (DataContext is EditorFrameViewModel vm) {
+ if (DataContext is EditorFrameViewModel vm) {
CursorPosition position = e.Key switch {
Key.Right => new(0, 1),
Key.Left => new(0, -1),
@@ -28,10 +31,22 @@ public partial class EditorFrame : UserControl
Key.Down => new(1, 0),
_ => new(0, 0)
};
- if (vm.MoveCaretCommand.CanExecute(position)) {
+ if (position != CursorPosition.Void &&
+ vm.MoveCaretCommand.CanExecute(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;
}
}
@@ -40,7 +55,7 @@ public partial class EditorFrame : UserControl
Focus();
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null) {
- Caret = new Caret(this, 18);
+ Caret = new Caret(this, Brushes.LimeGreen);
adornerLayer.Add(Caret);
} else {
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)
{
- 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)
@@ -74,6 +90,18 @@ public partial class EditorFrame : UserControl
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 {
get => (CursorPosition)GetValue(CursorPositionProperty);
set => SetValue(CursorPositionProperty, value);
diff --git a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
index 3fa25c2..2e09e37 100644
--- a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
+++ b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
@@ -1,6 +1,7 @@
using Qrakhen.Qamp.Memory;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
@@ -21,7 +22,7 @@ public partial class LineRenderer : UserControl
nameof(LineBuffer),
typeof(LineBuffer),
typeof(LineRenderer),
- new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
+ new FrameworkPropertyMetadata(null, OnLineBufferChanged));
public LineBuffer LineBuffer {
get => (LineBuffer)GetValue(LineBufferProperty);
@@ -45,11 +46,8 @@ public partial class LineRenderer : UserControl
if (string.IsNullOrEmpty(LineBuffer.Cached))
return;
- var formattedText = new FormattedText(
+ var formattedText = TextHelper.GetText(
LineBuffer.Cached,
- System.Globalization.CultureInfo.CurrentCulture,
- FlowDirection.LeftToRight,
- _typeface,
FontSize,
Brushes.White,
VisualTreeHelper.GetDpi(this).PixelsPerDip
@@ -57,4 +55,25 @@ public partial class LineRenderer : UserControl
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();
+ }
}
diff --git a/Qrakhen.Qamp.Editor/Primitives/CursorPosition.cs b/Qrakhen.Qamp.Editor/Primitives/CursorPosition.cs
index 577c49b..4dce632 100644
--- a/Qrakhen.Qamp.Editor/Primitives/CursorPosition.cs
+++ b/Qrakhen.Qamp.Editor/Primitives/CursorPosition.cs
@@ -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 Down = new(1, 0);
- public int Line = line < 0 ? 0 : line;
- public int Column = column < 0 ? 0 : column;
+ public int Line = line;
+ public int Column = column;
public static CursorPosition operator +(CursorPosition left, CursorPosition right)
=> new(left.Line + right.Line, left.Column + right.Column);
diff --git a/Qrakhen.Qamp.Editor/TextHelper.cs b/Qrakhen.Qamp.Editor/TextHelper.cs
new file mode 100644
index 0000000..7c7c8b2
--- /dev/null
+++ b/Qrakhen.Qamp.Editor/TextHelper.cs
@@ -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
+ );
+ }
+}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
index 0a566ba..cf4d346 100644
--- a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
+++ b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
@@ -51,6 +51,7 @@ public class EditorFrameViewModel : ObservableObject
public void MoveCursor(int lines, int columns)
{
SetCursor(CursorLine + lines, CursorColumn + columns);
+ OnPropertyChanged(nameof(CurrentLineBuffer));
}
public bool CanMoveCaret(object? relativeUpdate)
@@ -64,9 +65,19 @@ public class EditorFrameViewModel : ObservableObject
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);
+ if (follow)
+ MoveCursor(0, data.Length);
OnPropertyChanged(nameof(Lines));
}
}
diff --git a/Qrakhen.Qamp.Memory/LineBuffer.cs b/Qrakhen.Qamp.Memory/LineBuffer.cs
index 0a8192f..6bb4e21 100644
--- a/Qrakhen.Qamp.Memory/LineBuffer.cs
+++ b/Qrakhen.Qamp.Memory/LineBuffer.cs
@@ -1,11 +1,13 @@
-using System.Text;
+using System.ComponentModel;
+using System.Text;
namespace Qrakhen.Qamp.Memory;
-public sealed class LineBuffer : TailBuffer
+public sealed class LineBuffer : TailBuffer, INotifyPropertyChanged
{
public Encoding Encoding { get; }
public string Cached { get; private set; } = "";
+ public event PropertyChangedEventHandler? PropertyChanged;
public LineBuffer(Encoding encoding, int size = 0x80) : base(size)
{
@@ -23,6 +25,7 @@ public sealed class LineBuffer : TailBuffer
{
base.SetTail(tail);
Cached = ToString();
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Cached)));
}
public void Insert(int cursor, string data)
diff --git a/Qrakhen.Qamp.Memory/TailBuffer.cs b/Qrakhen.Qamp.Memory/TailBuffer.cs
index 00b24e1..0acb6f9 100644
--- a/Qrakhen.Qamp.Memory/TailBuffer.cs
+++ b/Qrakhen.Qamp.Memory/TailBuffer.cs
@@ -60,9 +60,9 @@ public abstract class TailBuffer
private void Prepare(int index, int count)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
- if (index + count <= _data.Length)
+ if (count + Tail <= _data.Length)
return;
- while (index + count > _data.Length) {
+ while (count + Tail > _data.Length) {
T[] grow = new T[_data.Length * 2];
Array.Copy(_data, grow, _data.Length);
_data = grow;