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.Diagnostics.Eventing.Reader; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace Qrakhen.Qamp.Editor.Controls; public partial class EditorFrame : UserControl { public Caret Caret { get; private set; } public EditorFrame() { InitializeComponent(); Loaded += OnLoaded; PreviewKeyDown += OnKeyDown; PreviewMouseDown += OnMouseDown; ScrollView.ScrollChanged += OnScroll; } private void OnScroll(object sender, ScrollChangedEventArgs e) { UpdateCaret(false); } 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(LineList), ScrollView.VerticalOffset, ScrollView.HorizontalOffset), false, false)); } } } private void OnKeyDown(object sender, KeyEventArgs e) { if (DataContext is EditorFrameViewModel vm) { BufferPosition position = e.Key switch { Key.Right => new(0, 1), Key.Left => new(0, -1), Key.Up => new(-1, 0), Key.Down => new(1, 0), _ => new(0, 0) }; 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) vm.DeleteCommand.Execute(1); else if (e.Key == Key.Enter) vm.InsertCommand.Execute(Environment.NewLine); else if (e.Key == Key.Delete) vm.DeleteCommand.Execute(1); else { 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; } } private void OnLoaded(object sender, RoutedEventArgs e) { Focus(); AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this); if (adornerLayer != null) { Caret = new Caret(LineList, Brushes.LimeGreen); adornerLayer.Add(Caret); } else { System.Diagnostics.Debug.WriteLine("AdornerLayer still null. Check parent hierarchy."); } Loaded -= OnLoaded; } 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 - 5) / 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 + 5, line * TextHelper.LineHeight); } private void MoveCaret(Point point) { Caret?.UpdatePosition(point); } private void HideCaret() { Caret?.Disable(); } private void AutoScroll(Point point) { MoveCaret(point); } private void UpdateCaret(bool autoScroll = true) { int line = CursorPosition.Line; int charIndex = CursorPosition.Column; double scrollY = ScrollView.VerticalOffset; double scrollX = ScrollView.HorizontalOffset; Point position = CalculateCaretPosition( line, charIndex, scrollY, scrollX); Point offset = ScrollView.TranslatePoint(position, ScrollView); if (offset.X < 0 || offset.Y < 0 || offset.X >= ScrollView.ActualWidth || offset.Y >= ScrollView.ActualHeight) { if (autoScroll) AutoScroll(offset); else HideCaret(); } else MoveCaret(position); if (!CurrentSelection.IsVoid) { } } 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 BufferPosition CursorPosition { get => (BufferPosition)GetValue(CursorPositionProperty); set => SetValue(CursorPositionProperty, value); } public static readonly DependencyProperty CursorPositionProperty = DependencyProperty.Register( nameof(CursorPosition), typeof(BufferPosition), typeof(EditorFrame), 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(); } }