diff --git a/Qrakhen.Qamp.Editor/Commands/MoveCaretOptions.cs b/Qrakhen.Qamp.Editor/Commands/MoveCaretOptions.cs
new file mode 100644
index 0000000..1e41038
--- /dev/null
+++ b/Qrakhen.Qamp.Editor/Commands/MoveCaretOptions.cs
@@ -0,0 +1,7 @@
+using Qrakhen.Qamp.Editor.Primitives;
+
+namespace Qrakhen.Qamp.Editor.Commands;
+
+public readonly record struct MoveCaretOptions(BufferPosition Position,
+ bool Relative = true,
+ bool Jump = false);
diff --git a/Qrakhen.Qamp.Editor/Commands/RelayCommand.cs b/Qrakhen.Qamp.Editor/Commands/RelayCommand.cs
index f218422..9f75f97 100644
--- a/Qrakhen.Qamp.Editor/Commands/RelayCommand.cs
+++ b/Qrakhen.Qamp.Editor/Commands/RelayCommand.cs
@@ -27,4 +27,4 @@ public class RelayCommand : ICommand
public bool CanExecute(object? parameter) => _canExecute(parameter);
public void Execute(object? parameter) => _execute(parameter);
-}
\ No newline at end of file
+}
diff --git a/Qrakhen.Qamp.Editor/Controls/Caret.cs b/Qrakhen.Qamp.Editor/Controls/Caret.cs
index 99e424a..5ae77d9 100644
--- a/Qrakhen.Qamp.Editor/Controls/Caret.cs
+++ b/Qrakhen.Qamp.Editor/Controls/Caret.cs
@@ -12,7 +12,10 @@ public class Caret : Adorner
private readonly Brush _brush;
private bool _blink;
private Point _cursorPosition;
- private DispatcherTimer _timer;
+ private Point _targetPosition;
+ private (Point from, Point to)[] _lines = [];
+ private DispatcherTimer _blinkTimer;
+ private DispatcherTimer _moveTimer;
public Caret(UIElement adornedElement, Brush brush, double height = 18, double thickness = 1.72) : base(adornedElement)
{
@@ -20,13 +23,28 @@ public class Caret : Adorner
_height = height;
_brush = brush;
- _pen = new Pen(brush, 1.5);
- _timer = new DispatcherTimer();
- _timer.Interval = TimeSpan.FromMilliseconds(428);
- _timer.Tick += new EventHandler((s, e) => {
+ _pen = new Pen(brush, 1.92);
+
+ _blinkTimer = new DispatcherTimer();
+ _blinkTimer.Interval = TimeSpan.FromMilliseconds(428);
+ _blinkTimer.Tick += new EventHandler((s, e) => {
Toggle(_blink = !_blink);
});
- _timer.Start();
+ _blinkTimer.Start();
+
+ _moveTimer = new DispatcherTimer();
+ _moveTimer.Interval = TimeSpan.FromMilliseconds(12);
+ _moveTimer.Tick += new EventHandler((s, e) => {
+ var delta = _targetPosition - _cursorPosition;
+ if (delta.Length > 2.4)
+ _cursorPosition += delta * .84;
+ else {
+ _cursorPosition = _targetPosition;
+ _moveTimer.Stop();
+ _blinkTimer.Start();
+ }
+ InvalidateVisual();
+ });
}
public void Toggle(bool hide)
@@ -34,13 +52,19 @@ public class Caret : Adorner
_pen.Brush = hide ? Brushes.Transparent : _brush;
}
+ public void UpdateSelection((Point from, Point to)[] lines)
+ {
+ _lines = lines;
+ InvalidateVisual();
+ }
+
public void UpdatePosition(Point newPosition)
{
- _timer.Stop();
+ _cursorPosition = _targetPosition = newPosition;
Toggle(_blink = false);
- _cursorPosition = newPosition;
+ //_blinkTimer.Stop();
+ //_moveTimer.Start();
InvalidateVisual();
- _timer.Start();
}
protected override void OnRender(DrawingContext drawingContext)
diff --git a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml
index d2641cf..ff6ac05 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 8e8680e..a294dbc 100644
--- a/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs
+++ b/Qrakhen.Qamp.Editor/Controls/EditorFrame.xaml.cs
@@ -1,4 +1,6 @@
-using Qrakhen.Qamp.Editor.Primitives;
+using Qrakhen.Qamp.Editor.Commands;
+using Qrakhen.Qamp.Editor.Input;
+using Qrakhen.Qamp.Editor.Primitives;
using Qrakhen.Qamp.Editor.ViewModel;
using Qrakhen.Qamp.Memory;
using System.Windows;
@@ -6,6 +8,7 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
+using System.Windows.Shapes;
namespace Qrakhen.Qamp.Editor.Controls;
@@ -18,8 +21,22 @@ public partial class EditorFrame : UserControl
InitializeComponent();
Loaded += OnLoaded;
PreviewKeyDown += OnKeyDown;
+ PreviewMouseDown += OnMouseDown;
}
+ protected void OnMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed) {
+ if (DataContext is EditorFrameViewModel vm) {
+ vm.MoveCaretCommand.Execute(
+ new MoveCaretOptions(
+ CalculateCursorPosition(e.GetPosition(this), 0, 0),
+ false,
+ false));
+ }
+ }
+ }
+
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is EditorFrameViewModel vm) {
@@ -30,8 +47,12 @@ public partial class EditorFrame : UserControl
Key.Down => new(1, 0),
_ => new(0, 0)
};
- if (position != BufferPosition.Void)
- vm.MoveCaretCommand.Execute(position);
+ if (position != BufferPosition.Initial)
+ vm.MoveCaretCommand.Execute(
+ new MoveCaretOptions(
+ position,
+ true,
+ InputService.CtrlHeld));
else if (e.Key == Key.Back)
vm.DeleteCommand.Execute(-1);
else if (e.Key == Key.Delete)
@@ -41,9 +62,11 @@ public partial class EditorFrame : UserControl
else if (e.Key == Key.Delete)
vm.DeleteCommand.Execute(1);
else {
- string c = e.Key.ToString();
- if (c.Length == 1)
- vm.InsertCommand.Execute(c);
+ char c = KeyHelper.Get(e.Key,
+ InputService.ShiftHeld ? 1 :
+ (InputService.CtrlHeld && InputService.AltHeld) ? 2 : 0);
+ if (c != 0)
+ vm.InsertCommand.Execute(c);
}
e.Handled = true;
@@ -63,7 +86,13 @@ public partial class EditorFrame : UserControl
Loaded -= OnLoaded;
}
- private Point CalculateCaretPosition(int line, int index, double scrollY, double scrollX)
+ private BufferPosition CalculateCursorPosition(Point point, double scrollY, double scrollX)
+ {
+ var ft = TextHelper.GetText(new string('_', 1), 14, null, VisualTreeHelper.GetDpi(this).PixelsPerDip);
+ return new BufferPosition((int)(point.Y / TextHelper.LineHeight), (int)(point.X / ft.Width));
+ }
+
+ private Point CalculateCaretPosition(int line, int index, double scrollY, double scrollX)
{
var ft = TextHelper.GetText(new string('_', index), 14, null, VisualTreeHelper.GetDpi(this).PixelsPerDip);
return new Point(ft.Width + scrollX + 2, line * TextHelper.LineHeight + scrollY);
@@ -74,7 +103,12 @@ public partial class EditorFrame : UserControl
Caret?.UpdatePosition(point);
}
- private void UpdateCaret()
+ private void SelectRegion((Point from, Point to)[] lines)
+ {
+ Caret?.UpdateSelection(lines);
+ }
+
+ private void UpdateCaret()
{
int line = CursorPosition.Line;
int charIndex = CursorPosition.Column;
@@ -88,6 +122,10 @@ public partial class EditorFrame : UserControl
charIndex,
scrollY,
scrollX));
+
+ if (!CurrentSelection.IsVoid) {
+
+ }
}
public LineBuffer CurrentLineBuffer {
@@ -112,11 +150,29 @@ public partial class EditorFrame : UserControl
nameof(CursorPosition),
typeof(BufferPosition),
typeof(EditorFrame),
- new PropertyMetadata(BufferPosition.Void, OnCursorPositionChanged));
+ new PropertyMetadata(BufferPosition.Initial, OnCursorPositionChanged));
private static void OnCursorPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var frame = (EditorFrame)d;
frame.UpdateCaret();
}
+
+ public BufferRegion CurrentSelection {
+ get => (BufferRegion)GetValue(CurrentSelectionProperty);
+ set => SetValue(CurrentSelectionProperty, value);
+ }
+
+ public static readonly DependencyProperty CurrentSelectionProperty =
+ DependencyProperty.Register(
+ nameof(CurrentSelection),
+ typeof(BufferRegion),
+ typeof(EditorFrame),
+ new PropertyMetadata(BufferRegion.Void, OnCurrentSelectionChanged));
+
+ private static void OnCurrentSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var frame = (EditorFrame)d;
+ frame.UpdateCaret();
+ }
}
diff --git a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml
index b3c0f66..c1a3fdc 100644
--- a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml
+++ b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml
@@ -7,6 +7,5 @@
mc:Ignorable="d"
d:DesignHeight="18" d:DesignWidth="420">
-
diff --git a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
index 2e09e37..3a3d474 100644
--- a/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
+++ b/Qrakhen.Qamp.Editor/Controls/LineRenderer.xaml.cs
@@ -9,71 +9,114 @@ using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
-
namespace Qrakhen.Qamp.Editor.Controls;
public partial class LineRenderer : UserControl
{
- public static readonly DependencyProperty LineBufferProperty =
+ private readonly Typeface _typeface = new(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
+ private const double FontSize = 14;
+ private const double LineHeight = 18;
+
+ public Pen Pen { get; set; }
+ public Brush Highlighter { get; set; }
+
+ public LineRenderer()
+ {
+ Height = LineHeight;
+ SnapsToDevicePixels = true;
+ Highlighter = new SolidColorBrush(Color.FromArgb(0xff, 0x24, 0x92, 0x72));
+ Pen = new Pen(Highlighter, 2);
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ base.OnRender(drawingContext);
+
+ if (string.IsNullOrEmpty(LineBuffer.Cached))
+ return;
+
+ var formattedText = TextHelper.GetText(
+ LineBuffer.Cached,
+ FontSize,
+ Brushes.White,
+ VisualTreeHelper.GetDpi(this).PixelsPerDip
+ );
+
+ var x = Random.Shared.Next(0, LineBuffer.Tail);
+ var s = new BufferSpan(x, Random.Shared.Next(x, LineBuffer.Tail));
+ Selection = s;
+
+ var highlighter = TextHelper.GetText(
+ new string(' ', Selection.Start) + new string('█', Selection.End - Selection.Start),
+ FontSize,
+ Highlighter,
+ VisualTreeHelper.GetDpi(this).PixelsPerDip
+ );
+
+ drawingContext.DrawText(highlighter, new Point(2, 0));
+ drawingContext.DrawText(formattedText, new Point(2, 0));
+ }
+
+ public static readonly DependencyProperty LineBufferProperty =
DependencyProperty.Register(
- nameof(LineBuffer),
- typeof(LineBuffer),
- typeof(LineRenderer),
+ nameof(LineBuffer),
+ typeof(LineBuffer),
+ typeof(LineRenderer),
new FrameworkPropertyMetadata(null, OnLineBufferChanged));
- public LineBuffer LineBuffer {
- get => (LineBuffer)GetValue(LineBufferProperty);
- set => SetValue(LineBufferProperty, value);
- }
+ public LineBuffer LineBuffer {
+ get => (LineBuffer)GetValue(LineBufferProperty);
+ set => SetValue(LineBufferProperty, value);
+ }
- private readonly Typeface _typeface = new(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
- private const double FontSize = 14;
- private const double LineHeight = 18;
+ 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;
+ }
- public LineRenderer()
- {
- Height = LineHeight;
- SnapsToDevicePixels = true;
- }
+ if (e.NewValue is LineBuffer newBuffer && newBuffer != null) {
+ newBuffer.PropertyChanged += renderer.OnLineBufferPropertyChanged;
+ }
- protected override void OnRender(DrawingContext drawingContext)
- {
- base.OnRender(drawingContext);
+ renderer.InvalidateVisual();
+ }
+ }
- if (string.IsNullOrEmpty(LineBuffer.Cached))
- return;
+ private void OnLineBufferPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(LineBuffer.Cached))
+ InvalidateVisual();
+ }
- var formattedText = TextHelper.GetText(
- LineBuffer.Cached,
- FontSize,
- Brushes.White,
- VisualTreeHelper.GetDpi(this).PixelsPerDip
- );
- 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;
- }
+ public static readonly DependencyProperty SelectionProperty =
+ DependencyProperty.Register(
+ nameof(Selection),
+ typeof(BufferSpan),
+ typeof(LineRenderer),
+ new FrameworkPropertyMetadata(BufferSpan.None, OnSelectionChanged));
- if (e.NewValue is LineBuffer newBuffer && newBuffer != null) {
- newBuffer.PropertyChanged += renderer.OnLineBufferPropertyChanged;
- }
+ public BufferSpan Selection {
+ get => (BufferSpan)GetValue(SelectionProperty);
+ set => SetValue(SelectionProperty, value);
+ }
- renderer.InvalidateVisual();
- }
- }
+ private static void OnSelectionChanged(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/InputService.cs b/Qrakhen.Qamp.Editor/InputService.cs
index 4d56a2d..baed6ad 100644
--- a/Qrakhen.Qamp.Editor/InputService.cs
+++ b/Qrakhen.Qamp.Editor/InputService.cs
@@ -1,9 +1,19 @@
using System;
+using System.CodeDom.Compiler;
using System.Collections.Generic;
+using System.Drawing.Drawing2D;
+using System.Runtime.ConstrainedExecution;
+using System.Security.Policy;
using System.Text;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Input;
+using System.Xml;
+using System.Xml.Linq;
+using static System.Net.Mime.MediaTypeNames;
+using static System.Runtime.InteropServices.JavaScript.JSType;
-namespace Qrakhen.Qamp.Editor.InputService;
+namespace Qrakhen.Qamp.Editor.Input;
public interface IInputHandler
{
@@ -34,7 +44,7 @@ public class InputService : IInputHandler
public static bool LeftShiftHeld => Keyboard.IsKeyDown(Key.LeftShift);
public static bool LeftAltHeld => Keyboard.IsKeyDown(Key.LeftAlt);
public static bool RightCtrlHeld => Keyboard.IsKeyDown(Key.RightCtrl);
- public static bool RightShiftHeld => Keyboard.IsKeyDown(Key.LeftCtrl);
+ public static bool RightShiftHeld => Keyboard.IsKeyDown(Key.RightShift);
public static bool RightAltHeld => Keyboard.IsKeyDown(Key.RightAlt);
public static bool CtrlHeld => LeftCtrlHeld || RightCtrlHeld;
public static bool ShiftHeld => LeftShiftHeld || RightShiftHeld;
@@ -44,4 +54,212 @@ public class InputService : IInputHandler
{
}
+}
+
+public static class KeyHelper
+{
+ private static readonly Dictionary _chars = [];
+
+ public static char Get(Key key, int modifier = 0)
+ {
+ return _chars[key][modifier];
+ }
+
+ static KeyHelper()
+ {
+ for (Key i = Key.A; i <= Key.Z; i++) {
+ char c = i.ToString()[^1];
+ Add(i, char.ToLower(c), char.ToUpper(c));
+ }
+
+ _chars[Key.Q] = ['q', 'Q', '@'];
+
+ Add(Key.None);
+ Add(Key.Cancel);
+ Add(Key.Back);
+ Add(Key.Tab, '\t');
+ Add(Key.LineFeed);
+ Add(Key.Clear);
+ Add(Key.Enter, '\n');
+ Add(Key.Return);
+ Add(Key.Pause);
+ Add(Key.Capital);
+ Add(Key.CapsLock);
+ Add(Key.HangulMode);
+ Add(Key.KanaMode);
+ Add(Key.JunjaMode);
+ Add(Key.FinalMode);
+ Add(Key.HanjaMode);
+ Add(Key.KanjiMode);
+ Add(Key.Escape);
+ Add(Key.ImeConvert);
+ Add(Key.ImeNonConvert);
+ Add(Key.ImeAccept);
+ Add(Key.ImeModeChange);
+ Add(Key.Space, ' ');
+ Add(Key.PageUp);
+ Add(Key.Prior);
+ Add(Key.Next);
+ Add(Key.PageDown);
+ Add(Key.End);
+ Add(Key.Home);
+ Add(Key.Left);
+ Add(Key.Up);
+ Add(Key.Right);
+ Add(Key.Down);
+ Add(Key.Select);
+ Add(Key.Print);
+ Add(Key.Execute);
+ Add(Key.PrintScreen);
+ Add(Key.Snapshot);
+ Add(Key.Insert);
+ Add(Key.Delete);
+ Add(Key.Help);
+ Add(Key.LWin);
+ Add(Key.RWin);
+ Add(Key.Apps);
+ Add(Key.Sleep);
+ Add(Key.D0, '0', '=', '}');
+ Add(Key.D1, '1', '!');
+ Add(Key.D2, '2', '"', '²');
+ Add(Key.D3, '3', '§', '³');
+ Add(Key.D4, '4', '$');
+ Add(Key.D5, '5', '%');
+ Add(Key.D6, '6', '&');
+ Add(Key.D7, '7', '/', '{');
+ Add(Key.D8, '8', '(', '[');
+ Add(Key.D9, '9', ')', ']');
+ Add(Key.NumPad0, '0');
+ Add(Key.NumPad1, '1');
+ Add(Key.NumPad2, '2');
+ Add(Key.NumPad3, '3');
+ Add(Key.NumPad4, '4');
+ Add(Key.NumPad5, '5');
+ Add(Key.NumPad6, '6');
+ Add(Key.NumPad7, '7');
+ Add(Key.NumPad8, '8');
+ Add(Key.NumPad9, '9');
+ Add(Key.Multiply, '*');
+ Add(Key.Add, '+');
+ Add(Key.Separator, '.');
+ Add(Key.Subtract, '-');
+ Add(Key.Decimal, ',');
+ Add(Key.Divide, '/');
+ Add(Key.F1);
+ Add(Key.F2);
+ Add(Key.F3);
+ Add(Key.F4);
+ Add(Key.F5);
+ Add(Key.F6);
+ Add(Key.F7);
+ Add(Key.F8);
+ Add(Key.F9);
+ Add(Key.F10);
+ Add(Key.F11);
+ Add(Key.F12);
+ Add(Key.F13);
+ Add(Key.F14);
+ Add(Key.F15);
+ Add(Key.F16);
+ Add(Key.F17);
+ Add(Key.F18);
+ Add(Key.F19);
+ Add(Key.F20);
+ Add(Key.F21);
+ Add(Key.F22);
+ Add(Key.F23);
+ Add(Key.F24);
+ Add(Key.NumLock);
+ Add(Key.Scroll);
+ Add(Key.LeftShift);
+ Add(Key.RightShift);
+ Add(Key.LeftCtrl);
+ Add(Key.RightCtrl);
+ Add(Key.LeftAlt);
+ Add(Key.RightAlt);
+ Add(Key.BrowserBack);
+ Add(Key.BrowserForward);
+ Add(Key.BrowserRefresh);
+ Add(Key.BrowserStop);
+ Add(Key.BrowserSearch);
+ Add(Key.BrowserFavorites);
+ Add(Key.BrowserHome);
+ Add(Key.VolumeMute);
+ Add(Key.VolumeDown);
+ Add(Key.VolumeUp);
+ Add(Key.MediaNextTrack);
+ Add(Key.MediaPreviousTrack);
+ Add(Key.MediaStop);
+ Add(Key.MediaPlayPause);
+ Add(Key.LaunchMail);
+ Add(Key.SelectMedia);
+ Add(Key.LaunchApplication1);
+ Add(Key.LaunchApplication2);
+ Add(Key.Oem1, 'ü', 'Ü');
+ Add(Key.OemSemicolon, ',', ';');
+ Add(Key.OemPlus, '+', '*', '~');
+ Add(Key.OemComma, ',', ';');
+ Add(Key.OemMinus, '-', '_');
+ Add(Key.OemPeriod, '.', ':');
+ Add(Key.Oem2);
+ Add(Key.OemQuestion, '#', '\'');
+ Add(Key.Oem3, 'ö', 'Ö');
+ Add(Key.OemTilde);
+ Add(Key.AbntC1);
+ Add(Key.AbntC2);
+ Add(Key.Oem4);
+ Add(Key.OemOpenBrackets);
+ Add(Key.Oem5, '^', '°');
+ Add(Key.OemPipe);
+ Add(Key.Oem6, '´', '`');
+ Add(Key.OemCloseBrackets);
+ Add(Key.Oem7, 'ß', '?');
+ Add(Key.OemQuotes, 'ä', 'Ä');
+ Add(Key.Oem8);
+ Add(Key.Oem102);
+ Add(Key.OemBackslash, '<', '>', '|');
+ Add(Key.ImeProcessed);
+ Add(Key.System);
+ Add(Key.DbeAlphanumeric);
+ Add(Key.OemAttn);
+ Add(Key.DbeKatakana);
+ Add(Key.OemFinish);
+ Add(Key.DbeHiragana);
+ Add(Key.OemCopy);
+ Add(Key.DbeSbcsChar);
+ Add(Key.OemAuto);
+ Add(Key.DbeDbcsChar);
+ Add(Key.OemEnlw);
+ Add(Key.DbeRoman);
+ Add(Key.OemBackTab);
+ Add(Key.Attn);
+ Add(Key.DbeNoRoman);
+ Add(Key.CrSel);
+ Add(Key.DbeEnterWordRegisterMode);
+ Add(Key.DbeEnterImeConfigureMode);
+ Add(Key.ExSel);
+ Add(Key.DbeFlushString);
+ Add(Key.EraseEof);
+ Add(Key.DbeCodeInput);
+ Add(Key.Play);
+ Add(Key.DbeNoCodeInput);
+ Add(Key.Zoom);
+ Add(Key.DbeDetermineString);
+ Add(Key.NoName);
+ Add(Key.DbeEnterDialogConversionMode);
+ Add(Key.Pa1);
+ Add(Key.OemClear);
+ Add(Key.DeadCharProcessed);
+
+ }
+
+ private static void Add(Key key, params char[] chars)
+ {
+ char[] padded = new char[3];
+ for (int i = 0; i < chars.Length; i++)
+ padded[i] = chars[i];
+ if (_chars.ContainsKey(key) && _chars[key][0] > 0)
+ return;
+ _chars[key] = padded;
+ }
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Editor/Primitives/BufferPosition.cs b/Qrakhen.Qamp.Editor/Primitives/BufferPosition.cs
index b8547af..d7f4686 100644
--- a/Qrakhen.Qamp.Editor/Primitives/BufferPosition.cs
+++ b/Qrakhen.Qamp.Editor/Primitives/BufferPosition.cs
@@ -2,7 +2,9 @@
public readonly record struct BufferPosition(int Line = 0, int Column = 0)
{
- public static readonly BufferPosition Void = new(0, 0);
+ public bool IsInitial => Line == 0 && Column == 0;
+
+ public static readonly BufferPosition Initial = new(0, 0);
public static readonly BufferPosition Left = new(0, -1);
public static readonly BufferPosition Right = new(0, 1);
public static readonly BufferPosition Up = new(-1, 0);
diff --git a/Qrakhen.Qamp.Editor/Primitives/BufferRegion.cs b/Qrakhen.Qamp.Editor/Primitives/BufferRegion.cs
index 255c26b..c3aeb86 100644
--- a/Qrakhen.Qamp.Editor/Primitives/BufferRegion.cs
+++ b/Qrakhen.Qamp.Editor/Primitives/BufferRegion.cs
@@ -2,6 +2,10 @@
public struct BufferRegion(BufferPosition from, BufferPosition to)
{
+ public static readonly BufferRegion Void = new BufferRegion(default, default);
+
+ public bool IsVoid => (From - To).IsInitial;
+
public BufferPosition From = from;
public BufferPosition To = to;
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
index 75985ad..040e210 100644
--- a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
+++ b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs
@@ -1,123 +1,181 @@
using Qrakhen.Qamp.Editor.Commands;
using Qrakhen.Qamp.Editor.Primitives;
using Qrakhen.Qamp.Memory;
+using System;
using System.Collections.ObjectModel;
+using System.Windows.Documents;
using System.Windows.Input;
namespace Qrakhen.Qamp.Editor.ViewModel;
public class EditorFrameViewModel : ObservableObject
{
- private BufferPosition _cursorPosition;
- public BufferPosition CursorPosition {
+ private BufferPosition _cursorPosition;
+ public BufferPosition CursorPosition {
get => _cursorPosition;
set => SetProperty(ref _cursorPosition, value);
- }
+ }
- public int CursorLine => _cursorPosition.Line;
+ private BufferRegion _currentSelection;
+ public BufferRegion CurrentSelection {
+ get => _currentSelection;
+ set => SetProperty(ref _currentSelection, value);
+ }
- public int CursorColumn => _cursorPosition.Column;
+ public int CursorLine => _cursorPosition.Line;
- public LineBuffer CurrentLineBuffer => Lines[CursorLine];
+ public int CursorColumn => _cursorPosition.Column;
- public ICommand MoveCaretCommand { get; }
- public ICommand MoveBlockCommand { get; }
- public ICommand InsertCommand { get; }
- public ICommand DeleteCommand { get; }
- public ICommand ClipBoardCommand { get; }
+ public LineBuffer CurrentLineBuffer => Lines[CursorLine];
- public ObservableCollection Lines { get; private set; } = new() {
- new LineBuffer("test"),
- new LineBuffer(" is very good;"),
- new LineBuffer(" jaja();"),
- new LineBuffer("}")
- };
+ public ICommand MoveCaretCommand { get; }
+ public ICommand MoveBlockCommand { get; }
+ public ICommand InsertCommand { get; }
+ public ICommand DeleteCommand { get; }
+ public ICommand ClipBoardCommand { get; }
- public EditorFrameViewModel()
- {
- MoveCaretCommand = new RelayCommand(ExecuteMoveCaret);
- MoveBlockCommand = new RelayCommand(ExecuteMoveBlock);
- InsertCommand = new RelayCommand(ExecuteInsert);
- DeleteCommand = new RelayCommand(ExecuteDelete);
- ClipBoardCommand = new RelayCommand(() => { });
- }
+ public ObservableCollection Lines { get; private set; } = new() {
+ new LineBuffer("test"),
+ new LineBuffer(" is very good;"),
+ new LineBuffer(" jaja();"),
+ new LineBuffer("}")
+ };
- private BufferPosition AlignBufferPosition(BufferPosition position)
- {
- int line = Math.Max(0, Math.Min(position.Line, Lines.Count - 1));
- LineBuffer buffer = Lines[line];
- int column = Math.Max(0, Math.Min(position.Column, buffer.Tail));
- return new BufferPosition(line, column);
- }
+ public EditorFrameViewModel()
+ {
+ MoveCaretCommand = new RelayCommand(ExecuteMoveCaret);
+ MoveBlockCommand = new RelayCommand(ExecuteMoveBlock);
+ InsertCommand = new RelayCommand(ExecuteInsert);
+ DeleteCommand = new RelayCommand(ExecuteDelete);
+ ClipBoardCommand = new RelayCommand(() => { });
+ }
- public void SetCursor(BufferPosition position)
- {
- CursorPosition = AlignBufferPosition(position);
- OnPropertyChanged(nameof(CurrentLineBuffer));
- }
+
- public void MoveCursor(BufferPosition relative)
- {
- SetCursor(CursorPosition + relative);
- OnPropertyChanged(nameof(CurrentLineBuffer));
- }
+ public void SetCursor(int line, int column) => SetCursor(new BufferPosition(line, column));
+ public void SetCursor(BufferPosition position)
+ {
+ CursorPosition = AlignBufferPosition(position);
+ OnPropertyChanged(nameof(CurrentLineBuffer));
+ }
- public void Delete(BufferRegion region, bool follow = true)
- {
- if (follow)
- SetCursor(new BufferPosition(region.From.Line, region.From.Column));
- for (int line = region.From.Line; line < region.To.Line; line++)
- {
+ public void MoveCursor(int lines, int columns) => MoveCursor(new BufferPosition(lines, columns));
+ public void MoveCursor(BufferPosition relative, bool jump = false)
+ {
+ SetCursor(CursorPosition + relative);
+ OnPropertyChanged(nameof(CurrentLineBuffer));
+ }
- }
- }
-
- public void Delete(int count = 1, bool follow = true)
- {
- if (follow)
- MoveCursor(new BufferPosition(0, -count));
- CurrentLineBuffer.Delete(CursorColumn, count);
- }
-
- public void Insert(string data, bool follow = true)
- {
- CurrentLineBuffer.Insert(CursorColumn, data);
-
- int newLine = data.IndexOf(Environment.NewLine);
- if (newLine > -1) {
- int position = CursorColumn - (data.Length - newLine + 1);
- LineBuffer split = new LineBuffer(CurrentLineBuffer.Slice(position), CurrentLineBuffer.Encoding);
- Lines.Insert(CursorLine, split);
- SetCursor(new BufferPosition(CursorLine + 1, split.Tail));
- } else {
+ public void Delete(BufferRegion region, bool follow = true)
+ {
if (follow)
- MoveCursor(new BufferPosition(0, data.Length));
- }
- }
+ SetCursor(new BufferPosition(region.From.Line, region.From.Column));
+ for (int line = region.From.Line; line < region.To.Line; line++) {
- private void ExecuteMoveCaret(object? parameter)
- {
- if (parameter is BufferPosition relative)
- MoveCursor(relative);
- }
+ }
+ }
- private void ExecuteMoveBlock(object? parameter)
- {
- if (parameter is BufferPosition relative)
- MoveCursor(relative);
- }
+ public void Delete(int count = 1, bool follow = true)
+ {
+ if (count < 1)
+ return;
- private void ExecuteInsert(object? parameter)
- {
- if (parameter is string str)
- Insert(str, true);
- }
+ if (follow) {
+ // remove newline
+ if (CursorColumn - count < 0 && CursorLine > 0) {
+ int position = Lines[CursorLine - 1].Tail;
+ Lines[CursorLine - 1].Insert(
+ position,
+ CurrentLineBuffer.Slice(0));
+ Lines.RemoveAt(CursorLine);
+ SetCursor(CursorLine - 1, position);
+ return;
+ } else
+ MoveCursor(0, -count);
+ } else {
+ if (CursorColumn + count >= CurrentLineBuffer.Tail &&
+ CursorLine < Lines.Count - 1) {
+ CurrentLineBuffer.Insert(
+ CurrentLineBuffer.Tail,
+ Lines[CursorLine + 1].Slice(0));
+ Lines.RemoveAt(CursorLine + 1);
+ return;
+ }
+ }
- private void ExecuteDelete(object? parameter)
- {
- if (parameter is int count)
- Delete(Math.Abs(count), count < 0);
- if (parameter is BufferRegion region)
- Delete(region);
- }
+ CurrentLineBuffer.Delete(CursorColumn, count);
+ }
+
+ /*
+
+ asdasdy
+ klöklöklökl
+ köäläälööx
+ cyxc
+
+ asdasd
+
+ * */
+
+ public void Insert(string data, bool follow = true)
+ {
+ data = data.Replace("\t", " ");
+ var lines = data.Split(Environment.NewLine, StringSplitOptions.None);
+ for (int i = 0; i < lines.Length; i++) {
+ if (i > 0) {
+ LineBuffer split = new LineBuffer(
+ CurrentLineBuffer.Slice(CursorColumn),
+ CurrentLineBuffer.Encoding);
+ Lines.Insert(CursorLine + 1, split);
+ SetCursor(new BufferPosition(CursorLine + 1, 0));
+ }
+ CurrentLineBuffer.Insert(CursorColumn, lines[i]);
+ if (follow)
+ MoveCursor(new BufferPosition(0, lines[i].Length));
+ }
+ }
+
+ private BufferPosition AlignBufferPosition(BufferPosition position)
+ {
+ int line = Math.Max(0, Math.Min(position.Line, Lines.Count - 1));
+ LineBuffer buffer = Lines[line];
+ int column = Math.Max(0, Math.Min(position.Column, buffer.Tail));
+ return new BufferPosition(line, column);
+ }
+
+ #region Commands
+
+ private void ExecuteMoveCaret(object? parameter)
+ {
+ if (parameter is MoveCaretOptions options) {
+ if (options.Relative)
+ MoveCursor(options.Position, options.Jump);
+ else
+ SetCursor(options.Position);
+ }
+ }
+
+ private void ExecuteMoveBlock(object? parameter)
+ {
+ if (parameter is BufferPosition relative)
+ MoveCursor(relative);
+ }
+
+ private void ExecuteInsert(object? parameter)
+ {
+ if (parameter is string str)
+ Insert(str, true);
+ if (parameter is char c)
+ Insert(c + "", true);
+ }
+
+ private void ExecuteDelete(object? parameter)
+ {
+ if (parameter is int count)
+ Delete(Math.Abs(count), count < 0);
+ if (parameter is BufferRegion region)
+ Delete(region);
+ }
+
+ #endregion
}
diff --git a/Qrakhen.Qamp.Memory/LineBuffer.cs b/Qrakhen.Qamp.Memory/LineBuffer.cs
index c5adb88..8f8af5c 100644
--- a/Qrakhen.Qamp.Memory/LineBuffer.cs
+++ b/Qrakhen.Qamp.Memory/LineBuffer.cs
@@ -3,7 +3,33 @@ using System.Text;
namespace Qrakhen.Qamp.Memory;
-public sealed class LineBuffer : TailBuffer, INotifyPropertyChanged
+public struct BufferSpan
+{
+ public static readonly BufferSpan None = new(0, 0);
+
+ public int Start { get; set; } = 0;
+ public int End { get; set; } = 0;
+
+ public bool HasSelection => Start - End != 0;
+
+ public BufferSpan(int start, int end)
+ {
+ Start = start;
+ End = end;
+ }
+
+ public BufferSpan Synchronized(TailBuffer source)
+ {
+ return new BufferSpan(Start, Math.Min(End, source.Tail));
+ }
+
+ public T[] GetSelectedRegion(TailBuffer source)
+ {
+ return source.Data[Start..End];
+ }
+}
+
+public class LineBuffer : TailBuffer, INotifyPropertyChanged
{
public Encoding Encoding { get; }
public string Cached { get; private set; } = "";
@@ -32,6 +58,14 @@ public sealed class LineBuffer : TailBuffer, INotifyPropertyChanged
public void Insert(int cursor, string data)
=> Insert(cursor, Encoding.GetBytes(data));
+ public string ToString(BufferSpan span)
+ {
+ var synced = span.Synchronized(this);
+ if (!synced.HasSelection)
+ return string.Empty;
+ return Encoding.GetString(Data[synced.Start..synced.End]);
+ }
+
public override string ToString()
{
if (Tail == 0 || Encoding == null)
diff --git a/Qrakhen.Qamp.Memory/TailBuffer.cs b/Qrakhen.Qamp.Memory/TailBuffer.cs
index e9a8532..1529cf0 100644
--- a/Qrakhen.Qamp.Memory/TailBuffer.cs
+++ b/Qrakhen.Qamp.Memory/TailBuffer.cs
@@ -13,12 +13,12 @@ public abstract class TailBuffer : ICloneable
public TailBuffer(int size = 0x80)
{
- _data = new T[size];
+ _data = new T[size];
}
public TailBuffer(T[] data)
{
- _data = new T[data.Length * 2];
+ _data = new T[Math.Max(0x10, data.Length * 2)];
Array.Copy(data, 0, _data, 0, data.Length);
SetTail(data.Length);
}
@@ -54,20 +54,21 @@ public abstract class TailBuffer : ICloneable
}
}
- public T[] Slice(int position)
- {
- position = SeekTail(position);
- if (position == Tail)
- return [];
- T[] data = _data[position..Tail];
- Delete(position, Tail - position);
- return data;
- }
+ public T[] Slice(int from = 0, int until = -1)
+ {
+ from = SeekTail(from);
+ if (from == Tail)
+ return [];
+ until = until < 0 ? Tail : SeekTail(until);
+ T[] data = _data[from..until];
+ Delete(from, until - from);
+ return data;
+ }
public abstract object Clone();
// never write beyond tail (there might be invalid data)
- private int SeekTail(int cursor) => cursor > Tail ? Tail : cursor;
+ private int SeekTail(int cursor) => Math.Max(0, Math.Min(cursor, Tail));
private void Prepare(int index, int count)
{