editor caret works! (i should focus on the language, shouldnt i?)
This commit is contained in:
parent
c2f9b93b46
commit
7d9e81c60f
|
|
@ -11,7 +11,7 @@ public partial class App : Application
|
|||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
MainWindow window = new MainWindow() {
|
||||
DataContext = new ViewModel.FileViewModel()
|
||||
DataContext = new ViewModel.EditorFrameViewModel()
|
||||
};
|
||||
|
||||
window.Show();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
using System.Windows.Input;
|
||||
|
||||
namespace Qrakhen.Qamp.Editor.Commands;
|
||||
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
private readonly Action<object?> _execute;
|
||||
private readonly Func<object?, bool> _canExecute;
|
||||
|
||||
public RelayCommand(Action<object?> execute) : this(execute, null) { }
|
||||
|
||||
public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute ?? (p => true);
|
||||
}
|
||||
|
||||
public RelayCommand(Action execute) : this(p => execute()) { }
|
||||
|
||||
public RelayCommand(Action execute, Func<bool> canExecute) : this(p => execute(), p => canExecute()) { }
|
||||
|
||||
public event EventHandler? CanExecuteChanged {
|
||||
add => CommandManager.RequerySuggested += value;
|
||||
remove => CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
|
||||
public bool CanExecute(object? parameter) => _canExecute(parameter);
|
||||
|
||||
public void Execute(object? parameter) => _execute(parameter);
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Qrakhen.Qamp.Editor.Controls;
|
||||
|
||||
public class Caret : Adorner
|
||||
{
|
||||
private readonly Pen _caretPen;
|
||||
private readonly double _lineHeight;
|
||||
private Point _cursorPosition;
|
||||
|
||||
public Caret(UIElement adornedElement, double lineHeight) : base(adornedElement)
|
||||
{
|
||||
_lineHeight = lineHeight;
|
||||
_caretPen = new Pen(Brushes.LimeGreen, 1.5);
|
||||
IsHitTestVisible = false;
|
||||
}
|
||||
|
||||
public void UpdatePosition(Point newPosition)
|
||||
{
|
||||
_cursorPosition = newPosition;
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,17 @@
|
|||
xmlns:local="clr-namespace:Qrakhen.Qamp.Editor.Controls"
|
||||
mc:Ignorable="d"
|
||||
Background="#161718"
|
||||
Focusable="True"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
|
||||
<UserControl.Style>
|
||||
<Style TargetType="{x:Type local:EditorFrame}">
|
||||
<Setter Property="CursorPosition" Value="{Binding CursorPosition}" />
|
||||
</Style>
|
||||
</UserControl.Style>
|
||||
|
||||
<ScrollViewer x:Name="ScrollView"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Lines}">
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Qrakhen.Qamp.Editor.Primitives;
|
||||
using Qrakhen.Qamp.Editor.ViewModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
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 EditorFrame : UserControl
|
||||
{
|
||||
public Caret Caret { get; private set; }
|
||||
|
||||
public EditorFrame()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
PreviewKeyDown += OnKeyDown;
|
||||
}
|
||||
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (DataContext is EditorFrameViewModel vm) {
|
||||
CursorPosition 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 (vm.MoveCaretCommand.CanExecute(position)) {
|
||||
vm.MoveCaretCommand.Execute(position);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Focus();
|
||||
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
if (adornerLayer != null) {
|
||||
Caret = new Caret(this, 18);
|
||||
adornerLayer.Add(Caret);
|
||||
} else {
|
||||
System.Diagnostics.Debug.WriteLine("AdornerLayer still null. Check parent hierarchy.");
|
||||
}
|
||||
Loaded -= OnLoaded;
|
||||
}
|
||||
|
||||
private Point CalculateCaretPosition(int line, int index, double scrollY, double scrollX)
|
||||
{
|
||||
return new Point(10 * index + scrollX, line * 18 + scrollY);
|
||||
}
|
||||
|
||||
private void MoveCaret(Point point)
|
||||
{
|
||||
Caret?.UpdatePosition(point);
|
||||
}
|
||||
|
||||
private void UpdateCaret()
|
||||
{
|
||||
int line = CursorPosition.Line;
|
||||
int charIndex = CursorPosition.Column;
|
||||
|
||||
double scrollY = ScrollView.VerticalOffset;
|
||||
double scrollX = ScrollView.HorizontalOffset;
|
||||
|
||||
MoveCaret(
|
||||
CalculateCaretPosition(
|
||||
line,
|
||||
charIndex,
|
||||
scrollY,
|
||||
scrollX));
|
||||
}
|
||||
|
||||
public CursorPosition CursorPosition {
|
||||
get => (CursorPosition)GetValue(CursorPositionProperty);
|
||||
set => SetValue(CursorPositionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CursorPositionProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(CursorPosition),
|
||||
typeof(CursorPosition),
|
||||
typeof(EditorFrame),
|
||||
new PropertyMetadata(CursorPosition.Void, OnCursorPositionChanged));
|
||||
|
||||
private static void OnCursorPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var frame = (EditorFrame)d;
|
||||
frame.UpdateCaret();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Qrakhen.Qamp.Editor.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
d:DesignHeight="18" d:DesignWidth="420">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Qrakhen.Qamp.Editor.Primitives;
|
||||
|
||||
public record struct CursorPosition(int line = 0, int column = 0)
|
||||
{
|
||||
public static readonly CursorPosition Void = new(0, 0);
|
||||
public static readonly CursorPosition Left = new(0, -1);
|
||||
public static readonly CursorPosition Right = new(0, 1);
|
||||
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 static CursorPosition operator +(CursorPosition left, CursorPosition right)
|
||||
=> new(left.Line + right.Line, left.Column + right.Column);
|
||||
|
||||
public static CursorPosition operator -(CursorPosition left, CursorPosition right)
|
||||
=> new(left.Line - right.Line, left.Column - right.Column);
|
||||
|
||||
public static CursorPosition operator *(CursorPosition left, int factor)
|
||||
=> new(left.Line * factor, left.Column * factor);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Qrakhen.Qamp.Editor.Primitives;
|
||||
|
||||
public struct CursorRegion(CursorPosition from, CursorPosition to)
|
||||
{
|
||||
public CursorPosition From = from;
|
||||
public CursorPosition To = to;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using Qrakhen.Qamp.Editor.Commands;
|
||||
using Qrakhen.Qamp.Editor.Primitives;
|
||||
using Qrakhen.Qamp.Memory;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Qrakhen.Qamp.Editor.ViewModel;
|
||||
|
||||
public class EditorFrameViewModel : ObservableObject
|
||||
{
|
||||
private CursorPosition _cursorPosition;
|
||||
public CursorPosition CursorPosition {
|
||||
get => _cursorPosition;
|
||||
set => SetProperty(ref _cursorPosition, value);
|
||||
}
|
||||
|
||||
public int CursorLine {
|
||||
get => _cursorPosition.Line;
|
||||
set => SetProperty(ref _cursorPosition.Line, value);
|
||||
}
|
||||
|
||||
public int CursorColumn {
|
||||
get => _cursorPosition.Column;
|
||||
set => SetProperty(ref _cursorPosition.Column, value);
|
||||
}
|
||||
|
||||
public LineBuffer CurrentLineBuffer => Lines[CursorLine];
|
||||
|
||||
public ICommand MoveCaretCommand { get; }
|
||||
|
||||
public ObservableCollection<LineBuffer> Lines { get; private set; } = new() {
|
||||
new LineBuffer("test"),
|
||||
new LineBuffer(" is very good;"),
|
||||
new LineBuffer(" jaja();"),
|
||||
new LineBuffer("}")
|
||||
};
|
||||
|
||||
public EditorFrameViewModel()
|
||||
{
|
||||
MoveCaretCommand = new RelayCommand(ExecuteMoveCaret, CanMoveCaret);
|
||||
}
|
||||
|
||||
public void SetCursor(int line, int column)
|
||||
{
|
||||
line = line < 0 ? 0 : line >= Lines.Count ? Lines.Count - 1 : line;
|
||||
LineBuffer buffer = Lines[line];
|
||||
column = column < 0 ? 0 : column > buffer.Tail ? buffer.Tail : column;
|
||||
CursorPosition = new(line, column);
|
||||
}
|
||||
|
||||
public void MoveCursor(int lines, int columns)
|
||||
{
|
||||
SetCursor(CursorLine + lines, CursorColumn + columns);
|
||||
}
|
||||
|
||||
public bool CanMoveCaret(object? relativeUpdate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ExecuteMoveCaret(object? relativeUpdate)
|
||||
{
|
||||
if (relativeUpdate is CursorPosition pos)
|
||||
MoveCursor(pos.Line, pos.Column);
|
||||
}
|
||||
|
||||
public void Insert(string data)
|
||||
{
|
||||
CurrentLineBuffer.Insert(CursorColumn, data);
|
||||
OnPropertyChanged(nameof(Lines));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
using Qrakhen.Qamp.Memory;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Qrakhen.Qamp.Editor.ViewModel;
|
||||
|
|
@ -13,33 +8,3 @@ public class MainViewModel
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
public class FileViewModel : ObservableObject
|
||||
{
|
||||
public ObservableCollection<LineBuffer> Lines { get; private set; } = new() {
|
||||
new LineBuffer("test"),
|
||||
new LineBuffer(" is very good;"),
|
||||
new LineBuffer(" jaja();"),
|
||||
new LineBuffer("}")
|
||||
};
|
||||
}
|
||||
|
||||
public abstract class ObservableObject : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, newValue))
|
||||
return false;
|
||||
|
||||
field = newValue;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Qrakhen.Qamp.Editor.ViewModel;
|
||||
|
||||
public abstract class ObservableObject : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, newValue))
|
||||
return false;
|
||||
|
||||
field = newValue;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue