split even more and begin writing our own text editor even

This commit is contained in:
Qrakhen 2025-11-19 19:01:38 +01:00
parent 8603cfe3d8
commit c2f9b93b46
18 changed files with 463 additions and 2 deletions

View File

@ -0,0 +1,8 @@
<Application x:Class="Qrakhen.Qamp.Editor.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Qrakhen.Qamp.Editor">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,20 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace Qrakhen.Qamp.Editor;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
MainWindow window = new MainWindow() {
DataContext = new ViewModel.FileViewModel()
};
window.Show();
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,30 @@
<UserControl x:Class="Qrakhen.Qamp.Editor.Controls.EditorFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Qrakhen.Qamp.Editor.Controls"
mc:Ignorable="d"
Background="#161718"
d:DesignHeight="450" d:DesignWidth="800">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Lines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:LineRenderer LineBuffer="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
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;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Qrakhen.Qamp.Editor.Controls;
public partial class EditorFrame : UserControl
{
public EditorFrame()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,12 @@
<UserControl x:Class="Qrakhen.Qamp.Editor.Controls.LineRenderer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
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">
<Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,60 @@
using Qrakhen.Qamp.Memory;
using System;
using System.Collections.Generic;
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;
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 =
DependencyProperty.Register(
nameof(LineBuffer),
typeof(LineBuffer),
typeof(LineRenderer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
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;
public LineRenderer()
{
Height = LineHeight;
SnapsToDevicePixels = true;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (string.IsNullOrEmpty(LineBuffer.Cached))
return;
var formattedText = new FormattedText(
LineBuffer.Cached,
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
_typeface,
FontSize,
Brushes.White,
VisualTreeHelper.GetDpi(this).PixelsPerDip
);
drawingContext.DrawText(formattedText, new Point(2, 0));
}
}

View File

@ -0,0 +1,30 @@
<Window x:Class="Qrakhen.Qamp.Editor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Qrakhen.Qamp.Editor"
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">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
ItemsSource="{Binding Lines.Count}"
IsHitTestVisible="False"
Background="#252525" Foreground="#707070"
BorderThickness="0"
Padding="0,0,5,0"
VerticalContentAlignment="Top"
HorizontalContentAlignment="Right"/>
<controls:EditorFrame Grid.Column="1" />
</Grid>
</Window>

View File

@ -0,0 +1,11 @@
using System.Windows;
namespace Qrakhen.Qamp.Editor;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Qrakhen.Qamp.Core\Qrakhen.Qamp.Core.csproj" />
<ProjectReference Include="..\Qrakhen.Qamp.Digest\Qrakhen.Qamp.Digest.csproj" />
<ProjectReference Include="..\Qrakhen.Qamp.Memory\Qrakhen.Qamp.Memory.csproj" />
<ProjectReference Include="..\Qrakhen.Qamp.Reader\Qrakhen.Qamp.Reader.csproj" />
<ProjectReference Include="..\Qrakhen.Qamp.Runtime\Qrakhen.Qamp.Runtime.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
using Qrakhen.Qamp.Memory;
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;
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));
}
}

View File

@ -0,0 +1,39 @@
using System.Text;
namespace Qrakhen.Qamp.Memory;
public sealed class LineBuffer : TailBuffer<byte>
{
public Encoding Encoding { get; }
public string Cached { get; private set; } = "";
public LineBuffer(Encoding encoding, int size = 0x80) : base(size)
{
Encoding = encoding;
}
public LineBuffer(string data) : this(data, Encoding.ASCII) { }
public LineBuffer(string data, Encoding encoding) : base(encoding.GetBytes(data))
{
Encoding = encoding;
Cached = ToString();
}
protected override void SetTail(int tail = -1)
{
base.SetTail(tail);
Cached = ToString();
}
public void Insert(int cursor, string data)
=> Insert(cursor, Encoding.GetBytes(data));
public override string ToString()
{
if (Tail == 0 || Encoding == null)
return string.Empty;
return Encoding.GetString(Data, 0, Tail);
}
public static implicit operator LineBuffer(string s) => new LineBuffer(s);
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,3 @@
namespace Qrakhen.Qamp.Memory;
public readonly record struct Range(int From, int To);

View File

@ -0,0 +1,80 @@
namespace Qrakhen.Qamp.Memory;
// add to qrakhen.memory package for clout
public abstract class TailBuffer<T>
{
private T[] _data;
public T[] Data {
get => _data;
}
public int Capacity => _data.Length;
public int Tail { get; private set; }
public TailBuffer(int size = 0x80)
{
_data = new T[size];
}
public TailBuffer(T[] data)
{
_data = new T[data.Length * 2];
Array.Copy(data, 0, _data, 0, data.Length);
SetTail(data.Length);
}
public void Insert(int cursor, T[] data)
{
cursor = SeekTail(cursor);
Prepare(cursor, data.Length);
int length = data.Length;
if (cursor == Tail) {
// Just append here.
Array.Copy(data, 0, _data, cursor, length);
SetTail(cursor + length);
} else {
// Shift everything to the right of insertion
Array.Copy(_data, cursor, _data, cursor + length, Tail - cursor);
Array.Copy(data, 0, _data, cursor, length);
SetTail(Tail + length);
}
}
public void Delete(Range range)
=> Delete(range.From, range.To - range.From);
public void Delete(int cursor, int count)
{
if (cursor + count >= Tail)
SetTail(cursor); // Directly terminate at cursor, nothing is behind tail.
else {
// Move everything from end of deletion until tail to start of deletion.
Array.Copy(_data, cursor + count, _data, cursor, Tail - (cursor + count));
SetTail(Tail - count);
}
}
// never write beyond tail (there might be invalid data)
private int SeekTail(int cursor) => cursor > Tail ? Tail : cursor;
private void Prepare(int index, int count)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
if (index + count <= _data.Length)
return;
while (index + count > _data.Length) {
T[] grow = new T[_data.Length * 2];
Array.Copy(_data, grow, _data.Length);
_data = grow;
}
}
/// <summary>
/// Sets the tail, or, if omitted, seeks the tail by finding the first null terminator.
/// </summary>
/// <param name="tail"></param>
protected virtual void SetTail(int tail = -1)
{
Tail = tail;
}
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
namespace Qrakhen.Qamp.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20 d17.14
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Core", "Qrakhen.Qamp.Core\Qrakhen.Qamp.Core.csproj", "{CA6F4D4E-AF35-4CA6-BC53-C83277EEE46C}"
EndProject
@ -15,6 +15,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Reader", "Qrak
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Core.Tests", "Qrakhen.Qamp.Core.Tests\Qrakhen.Qamp.Core.Tests.csproj", "{C3D5A175-2572-44CB-8161-FF51BD254824}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Tests", "Qrakhen.Qamp.Tests\Qrakhen.Qamp.Tests.csproj", "{5440D742-6149-417A-B9C8-4DEF951221E9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Editor", "Qrakhen.Qamp.Editor\Qrakhen.Qamp.Editor.csproj", "{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Memory", "Qrakhen.Qamp.Memory\Qrakhen.Qamp.Memory.csproj", "{A8876A55-8007-4D20-9637-2099F1B82DAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -71,6 +77,30 @@ Global
{C3D5A175-2572-44CB-8161-FF51BD254824}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D5A175-2572-44CB-8161-FF51BD254824}.Release|x64.ActiveCfg = Release|x64
{C3D5A175-2572-44CB-8161-FF51BD254824}.Release|x64.Build.0 = Release|x64
{5440D742-6149-417A-B9C8-4DEF951221E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Debug|x64.ActiveCfg = Debug|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Debug|x64.Build.0 = Debug|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Release|Any CPU.Build.0 = Release|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Release|x64.ActiveCfg = Release|Any CPU
{5440D742-6149-417A-B9C8-4DEF951221E9}.Release|x64.Build.0 = Release|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Debug|x64.ActiveCfg = Debug|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Debug|x64.Build.0 = Debug|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Release|Any CPU.Build.0 = Release|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Release|x64.ActiveCfg = Release|Any CPU
{BE50AA8C-7D8F-4DDA-8102-92CFCE74DA96}.Release|x64.Build.0 = Release|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Debug|x64.ActiveCfg = Debug|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Debug|x64.Build.0 = Debug|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Release|Any CPU.Build.0 = Release|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Release|x64.ActiveCfg = Release|Any CPU
{A8876A55-8007-4D20-9637-2099F1B82DAE}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE