This commit is contained in:
Qrakhen 2025-11-22 17:34:27 +01:00
parent e2022ef80e
commit b3385c0860
6 changed files with 269 additions and 185 deletions

View File

@ -11,6 +11,7 @@ public class Caret : Adorner
private readonly double _height;
private readonly Brush _brush;
private bool _blink;
private bool _enabled;
private Point _cursorPosition;
private Point _targetPosition;
private (Point from, Point to)[] _lines = [];
@ -21,12 +22,13 @@ public class Caret : Adorner
{
IsHitTestVisible = false;
_enabled = true;
_height = height;
_brush = brush;
_pen = new Pen(brush, 1.92);
_blinkTimer = new DispatcherTimer();
_blinkTimer.Interval = TimeSpan.FromMilliseconds(428);
_blinkTimer.Interval = TimeSpan.FromMilliseconds(324);
_blinkTimer.Tick += new EventHandler((s, e) => {
Toggle(_blink = !_blink);
});
@ -50,6 +52,15 @@ public class Caret : Adorner
public void Toggle(bool hide)
{
_pen.Brush = hide ? Brushes.Transparent : _brush;
InvalidateVisual();
}
public void Disable()
{
_blinkTimer?.Stop();
_enabled = false;
Toggle(true);
InvalidateVisual();
}
public void UpdateSelection((Point from, Point to)[] lines)
@ -61,10 +72,9 @@ public class Caret : Adorner
public void UpdatePosition(Point newPosition)
{
_cursorPosition = _targetPosition = newPosition;
_enabled = true;
Toggle(_blink = false);
//_blinkTimer.Stop();
//_moveTimer.Start();
InvalidateVisual();
_blinkTimer.Start();
}
protected override void OnRender(DrawingContext drawingContext)

View File

@ -16,25 +16,39 @@
<Setter Property="CurrentLineBuffer" Value="{Binding CurrentLineBuffer}" />
</Style>
</UserControl.Style>
<ScrollViewer x:Name="ScrollView"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Lines}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LinesList"
Grid.Column="0"
ItemsSource="{Binding Lines.Count}"
IsHitTestVisible="False"
Background="#121314"
Foreground="#969696"
BorderThickness="0"
Padding="0,0,5,0"
VerticalContentAlignment="Top"
HorizontalContentAlignment="Right"/>
<ItemsControl ItemsSource="{Binding Lines}"
Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:LineRenderer LineBuffer="{Binding}" />
<local:LineRenderer LineBuffer="{Binding Buffer}"
Selection="{Binding Selection}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ScrollViewer>
</UserControl>

View File

@ -3,12 +3,12 @@ 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;
using System.Windows.Shapes;
namespace Qrakhen.Qamp.Editor.Controls;
@ -22,6 +22,12 @@ public partial class EditorFrame : UserControl
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)
@ -89,13 +95,13 @@ public partial class EditorFrame : UserControl
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));
return new BufferPosition((int)(point.Y / TextHelper.LineHeight), (int)((point.X - 34) / 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);
return new Point(ft.Width - scrollX + 34, line * TextHelper.LineHeight - scrollY);
}
private void MoveCaret(Point point)
@ -103,12 +109,17 @@ public partial class EditorFrame : UserControl
Caret?.UpdatePosition(point);
}
private void SelectRegion((Point from, Point to)[] lines)
private void HideCaret()
{
Caret?.UpdateSelection(lines);
Caret?.Disable();
}
private void UpdateCaret()
private void AutoScroll(Point point)
{
MoveCaret(point);
}
private void UpdateCaret(bool autoScroll = true)
{
int line = CursorPosition.Line;
int charIndex = CursorPosition.Column;
@ -116,12 +127,25 @@ public partial class EditorFrame : UserControl
double scrollY = ScrollView.VerticalOffset;
double scrollX = ScrollView.HorizontalOffset;
MoveCaret(
CalculateCaretPosition(
Point position = CalculateCaretPosition(
line,
charIndex,
scrollY,
scrollX));
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) {

View File

@ -1,13 +1,8 @@
using Qrakhen.Qamp.Memory;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Qrakhen.Qamp.Editor.Controls;

View File

@ -7,39 +7,74 @@
xmlns:vm="clr-namespace:Qrakhen.Qamp.Editor.ViewModel"
xmlns:controls="clr-namespace:Qrakhen.Qamp.Editor.Controls"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:FileViewModel}"
Title="MainWindow" Height="450" Width="800">
Background="#161718"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
Title="MainWindow" Height="640" Width="920">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
<RowDefinition Height="18" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"
Background="#202122">
<Menu Background="#202122"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Height="32">
<MenuItem Header="File"
Height="32"
Foreground="#fefefe">
<MenuItem></MenuItem>
<MenuItem></MenuItem>
<MenuItem></MenuItem>
<MenuItem></MenuItem>
<MenuItem></MenuItem>
</MenuItem>
<MenuItem Header="Fun" Foreground="#fefefe">
</MenuItem>
<MenuItem Header="Run" Foreground="#fefefe">
</MenuItem>
</Menu>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="#242526">
</Grid>
<GridSplitter Grid.Column="0"
ResizeDirection="Columns"
Width="1"
Background="#40fefefe" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="5*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
ItemsSource="{Binding Lines.Count}"
IsHitTestVisible="False"
Background="#242628"
Foreground="#969696"
BorderThickness="0"
Padding="0,0,5,0"
VerticalContentAlignment="Top"
HorizontalContentAlignment="Right"/>
<controls:EditorFrame Grid.Column="1" />
<controls:EditorFrame />
</Grid>
<Grid Grid.Row="1" Background="#161718">
<GridSplitter Grid.Row="0"
ResizeDirection="Rows"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Height="1"
Background="#40fefefe" />
<Grid Grid.Row="1" Background="#1e1f20">
</Grid>
</Grid>
</Grid>
<Grid Grid.Row="2"
Background="#141516">
<Label Foreground="#80efefef"
FontSize="10"
Padding="2">Fun &amp; Status</Label>
</Grid>
</Grid>
</Window>

View File

@ -8,6 +8,12 @@ using System.Windows.Input;
namespace Qrakhen.Qamp.Editor.ViewModel;
public class SelectableLineBuffer(LineBuffer buffer)
{
public LineBuffer Buffer { get; private set; } = buffer;
public BufferRegion Selection { get; private set; } = new();
}
public class EditorFrameViewModel : ObservableObject
{
private BufferPosition _cursorPosition;
@ -26,7 +32,7 @@ public class EditorFrameViewModel : ObservableObject
public int CursorColumn => _cursorPosition.Column;
public LineBuffer CurrentLineBuffer => Lines[CursorLine];
public SelectableLineBuffer CurrentLine => Lines[CursorLine];
public ICommand MoveCaretCommand { get; }
public ICommand MoveBlockCommand { get; }
@ -34,11 +40,11 @@ public class EditorFrameViewModel : ObservableObject
public ICommand DeleteCommand { get; }
public ICommand ClipBoardCommand { get; }
public ObservableCollection<LineBuffer> Lines { get; private set; } = new() {
new LineBuffer("test"),
new LineBuffer(" is very good;"),
new LineBuffer(" jaja();"),
new LineBuffer("}")
public ObservableCollection<SelectableLineBuffer> Lines { get; private set; } = new() {
new SelectableLineBuffer(new LineBuffer("test {")),
new SelectableLineBuffer(new LineBuffer(" is very good;")),
new SelectableLineBuffer(new LineBuffer(" jaja();")),
new SelectableLineBuffer(new LineBuffer("}"))
};
public EditorFrameViewModel()
@ -56,14 +62,14 @@ public class EditorFrameViewModel : ObservableObject
public void SetCursor(BufferPosition position)
{
CursorPosition = AlignBufferPosition(position);
OnPropertyChanged(nameof(CurrentLineBuffer));
OnPropertyChanged(nameof(CurrentLine));
}
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));
OnPropertyChanged(nameof(CurrentLine));
}
public void Delete(BufferRegion region, bool follow = true)
@ -83,25 +89,25 @@ public class EditorFrameViewModel : ObservableObject
if (follow) {
// remove newline
if (CursorColumn - count < 0 && CursorLine > 0) {
int position = Lines[CursorLine - 1].Tail;
Lines[CursorLine - 1].Insert(
int position = Lines[CursorLine - 1].Buffer.Tail;
Lines[CursorLine - 1].Buffer.Insert(
position,
CurrentLineBuffer.Slice(0));
CurrentLine.Buffer.Slice(0));
Lines.RemoveAt(CursorLine);
SetCursor(CursorLine - 1, position);
} else {
MoveCursor(0, -count);
CurrentLineBuffer.Delete(CursorColumn, count);
CurrentLine.Buffer.Delete(CursorColumn, count);
}
} else {
if (CursorColumn + count > CurrentLineBuffer.Tail &&
if (CursorColumn + count > CurrentLine.Buffer.Tail &&
CursorLine < Lines.Count - 1) {
CurrentLineBuffer.Insert(
CurrentLineBuffer.Tail,
Lines[CursorLine + 1].Slice(0));
CurrentLine.Buffer.Insert(
CurrentLine.Buffer.Tail,
Lines[CursorLine + 1].Buffer.Slice(0));
Lines.RemoveAt(CursorLine + 1);
}
CurrentLineBuffer.Delete(CursorColumn, count);
CurrentLine.Buffer.Delete(CursorColumn, count);
}
}
@ -123,12 +129,12 @@ public class EditorFrameViewModel : ObservableObject
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);
CurrentLine.Buffer.Slice(CursorColumn),
CurrentLine.Buffer.Encoding);
Lines.Insert(CursorLine + 1, new SelectableLineBuffer(split));
SetCursor(new BufferPosition(CursorLine + 1, 0));
}
CurrentLineBuffer.Insert(CursorColumn, lines[i]);
CurrentLine.Buffer.Insert(CursorColumn, lines[i]);
if (follow)
MoveCursor(new BufferPosition(0, lines[i].Length));
}
@ -137,7 +143,7 @@ public class EditorFrameViewModel : ObservableObject
private BufferPosition AlignBufferPosition(BufferPosition position)
{
int line = Math.Max(0, Math.Min(position.Line, Lines.Count - 1));
LineBuffer buffer = Lines[line];
LineBuffer buffer = Lines[line].Buffer;
int column = Math.Max(0, Math.Min(position.Column, buffer.Tail));
return new BufferPosition(line, column);
}