Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live preview while editing AXAML files #150

Open
wants to merge 57 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
907352f
previewer seems to work pretty well
tomlm Nov 12, 2024
fe1e2aa
things seem to work cross platform, this is a snapshot of that state.
tomlm Nov 14, 2024
d42b9fa
add ConsolePreview control
tomlm Nov 14, 2024
7c31f74
fix bug in overlapping char
tomlm Nov 15, 2024
c568f85
things seem to work except for tabcontrol
tomlm Nov 15, 2024
b3798b6
move and rename previewhost applications into tools folder
tomlm Nov 16, 2024
490afd6
rewrote consolonia.previewer as Consolonia.PreviewHost
tomlm Nov 16, 2024
1702e20
split consolepanel out into seperate assembly
tomlm Nov 16, 2024
ebdc854
add material and fluent themes
tomlm Nov 16, 2024
3b2708a
* move pixelbuffer to consolewindow so that rendertarget is clean
tomlm Nov 16, 2024
9741315
move the design mode styles into ConsoloniaApplication.
tomlm Nov 17, 2024
8253d3f
Add readme and fix look up bug
tomlm Nov 17, 2024
c26b1ef
FINALLY found the multi-char disappearing char bug!
tomlm Nov 17, 2024
79d82a9
make emoji test transparent
tomlm Nov 17, 2024
9bb7324
Added DrawContextImplTests testing Boxes, rectangles, text drawing.
tomlm Nov 18, 2024
acfcafd
make drawtext static
tomlm Nov 18, 2024
e6de66c
fix char position bug
tomlm Nov 18, 2024
cd7d6ec
remove unused assemblies
tomlm Nov 18, 2024
8f94585
Delete src/Consolonia.Core/Text/ESC.cs
tomlm Nov 18, 2024
48b5697
add clscompilant flags
tomlm Nov 18, 2024
4b5b39d
Merge branch 'tomlm/preview' of https://github.com/jinek/Consolonia i…
tomlm Nov 18, 2024
fb42590
delete
tomlm Nov 18, 2024
99e5717
add Esc back
tomlm Nov 18, 2024
2ba6bec
fix color
tomlm Nov 18, 2024
2f93ed4
mark avalonia host as clscompliant(false)
tomlm Nov 18, 2024
1eba984
Imrpoved performance of PreviewControl, it was shutting down on every…
tomlm Nov 18, 2024
33dbcfa
perf improvements
tomlm Nov 19, 2024
0ed26ee
fix unit test
tomlm Nov 19, 2024
09c3de6
more lint
tomlm Nov 19, 2024
0a7ea66
more lint
tomlm Nov 19, 2024
29b3970
more lint more lint more lint
tomlm Nov 19, 2024
d036446
fix datacontext for views
tomlm Nov 19, 2024
7e99d74
cleanup x:DataType
tomlm Nov 19, 2024
275da5c
so much lint
tomlm Nov 19, 2024
c5dec7d
omg I'm spending more time with nit picky bullshit lints then it took…
tomlm Nov 19, 2024
0c388a5
i just can't even.
tomlm Nov 19, 2024
759a7b7
I finally understand what it's bitchinga bout.
tomlm Nov 19, 2024
140e11b
Automated JetBrains cleanup
github-actions[bot] Nov 19, 2024
bf5f0a8
Automated JetBrains cleanup
github-actions[bot] Nov 19, 2024
84ece5b
coderabbit review feedback
tomlm Nov 19, 2024
d91316e
merge changes from bot plus some cleanup
tomlm Nov 19, 2024
b042010
Automated JetBrains cleanup
github-actions[bot] Nov 19, 2024
db3900f
Update src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs
tomlm Nov 19, 2024
fdcb77b
Update src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs
tomlm Nov 19, 2024
d57e3bd
switched back to readonly struct for pixel classes, I have serializat…
tomlm Nov 19, 2024
d9f5f3d
Merge branch 'tomlm/preview' of https://github.com/jinek/Consolonia i…
tomlm Nov 19, 2024
3403705
Update src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs
tomlm Nov 19, 2024
a1dd72a
blarg, coderabbit broke release build
tomlm Nov 19, 2024
f39c232
Merge branch 'tomlm/preview' of https://github.com/jinek/Consolonia i…
tomlm Nov 19, 2024
04cbbe7
cleanup for struct change
tomlm Nov 19, 2024
67e29c3
revert lines to be middle of pixel based so drawline aligns with draw…
tomlm Nov 19, 2024
8976e1d
fix lint error
tomlm Nov 19, 2024
4408182
linty
tomlm Nov 20, 2024
e073ab7
this seems like overkill
tomlm Nov 20, 2024
1f07aee
Automated JetBrains cleanup
github-actions[bot] Nov 20, 2024
b04dd60
changes from jinek code review feedback
tomlm Nov 29, 2024
e6c07f9
remove pragma
tomlm Nov 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/Consolonia.Core/Assembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Consolonia.Core.Tests")]
[assembly: InternalsVisibleTo("Consolonia.Designer")]

[assembly: CLSCompliant(false)] //todo: should we make it compliant?
4 changes: 2 additions & 2 deletions src/Consolonia.Core/Consolonia.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

<ItemGroup>
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.FreeDesktop" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Skia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.8" />
<PackageReference Include="Unicode.net" Version="2.0.0" />
<PackageReference Include="Wcwidth" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
Expand Down
114 changes: 76 additions & 38 deletions src/Consolonia.Core/Drawing/DrawingContextImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ internal class DrawingContextImpl : IDrawingContextImpl
private readonly Matrix _postTransform = Matrix.Identity;
private Matrix _transform = Matrix.Identity;

public DrawingContextImpl(ConsoleWindow consoleWindow, PixelBuffer pixelBuffer)
public DrawingContextImpl(ConsoleWindow consoleWindow)
{
_consoleWindow = consoleWindow;
_pixelBuffer = pixelBuffer;
_clipStack.Push(pixelBuffer.Size);
_pixelBuffer = consoleWindow.PixelBuffer;
_clipStack.Push(_pixelBuffer.Size);
}

private Rect CurrentClip => _clipStack.Peek();
Expand Down Expand Up @@ -101,7 +101,7 @@ public void DrawBitmap(IBitmapImpl source, double opacity, Rect sourceRect, Rect
() =>
{
_pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py),
(existingPixel, _) => existingPixel.Blend(imagePixel), imagePixel.Background.Color);
existingPixel => existingPixel.Blend(imagePixel));
});
}
}
Expand Down Expand Up @@ -175,8 +175,8 @@ public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
CurrentClip.ExecuteWithClipping(new Point(px, py), () =>
{
_pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py),
(pixel, bb) => pixel.Blend(new Pixel(new PixelBackground(bb.Mode, bb.Color))),
backgroundBrush);
pixel => pixel.Blend(new Pixel(new PixelBackground(backgroundBrush.Mode,
backgroundBrush.Color))));
});
}
}
Expand Down Expand Up @@ -210,7 +210,8 @@ public void DrawGlyphRun(IBrush foreground, IGlyphRunImpl glyphRun)
return;
}

var shapedBuffer = (ShapedBuffer)glyphRunImpl.GlyphInfos;
var shapedBuffer = glyphRunImpl.GlyphInfos as ShapedBuffer;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cast was better? with "as" there is no difference between null and different type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the linter was bitching about it.

ArgumentNullException.ThrowIfNull(shapedBuffer);
string text = shapedBuffer.Text.ToString();
DrawStringInternal(foreground, text, glyphRun.GlyphTypeface);
}
Expand Down Expand Up @@ -301,41 +302,32 @@ public Matrix Transform
/// <param name="line">line</param>
private void DrawLineInternal(IPen pen, Line line)
{
if (pen.Thickness == 0) return;

line = TransformLineInternal(line);
if (pen.Thickness.IsNearlyEqual(0)) return;

Point head = line.PStart;
if (pen.Brush is MoveConsoleCaretToPositionBrush)
if (pen.Thickness.IsNearlyEqual(UnderlineThickness) || pen.Thickness.IsNearlyEqual(StrikethroughThickness))
{
CurrentClip.ExecuteWithClipping(head,
() => { _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); });
return;
}
if (line.Vertical)
throw new NotSupportedException(
"Vertical strikethrough or underline text decorations is not supported.");

if (line.Vertical == false && pen.Thickness > 1)
{
// horizontal lines with thickness larger than one are text decorations
ApplyTextDecorationLineInternal(ref head, pen, line);
ApplyTextDecorationLineInternal(pen, line);
return;
}

var extractColorCheckPlatformSupported = ExtractColorOrNullWithPlatformCheck(pen, out var lineStyle);
if (extractColorCheckPlatformSupported == null)
return;

var color = (Color)extractColorCheckPlatformSupported;

byte pattern = (byte)(line.Vertical ? 0b1010 : 0b0101);
DrawPixelAndMoveHead(ref head, line, lineStyle, pattern, color, line.Length); //line
DrawRectangleLineInternal(pen, line);
}

private void ApplyTextDecorationLineInternal(ref Point head, IPen pen, Line line)
private void ApplyTextDecorationLineInternal(IPen pen, Line line)
{
TextDecorationCollection textDecoration = pen.Thickness switch
line = TransformLineInternal(line);

Point head = line.PStart;

TextDecorationLocation textDecoration = pen.Thickness switch
{
UnderlineThickness => TextDecorations.Underline,
StrikethroughThickness => TextDecorations.Strikethrough,
UnderlineThickness => TextDecorationLocation.Underline,
StrikethroughThickness => TextDecorationLocation.Strikethrough,
_ => throw new ArgumentOutOfRangeException($"Unsupported thickness {pen.Thickness}")
};

Expand Down Expand Up @@ -517,7 +509,7 @@ private void DrawPixelAndMoveHead(ref Point head, Line line, LineStyle? lineStyl
CurrentClip.ExecuteWithClipping(newCharacterPoint, () =>
{
_pixelBuffer.Set((PixelBufferCoordinate)newCharacterPoint,
(oldPixel, cp) => oldPixel.Blend(cp), consolePixel);
oldPixel => oldPixel.Blend(consolePixel));
});
}

Expand All @@ -533,17 +525,63 @@ private void DrawPixelAndMoveHead(ref Point head, Line line, LineStyle? lineStyl
default:
{
var symbol = new SimpleSymbol(glyph);
var consolePixel = new Pixel(symbol, foregroundColor, typeface.Style, typeface.Weight);
// if we are attempting to draw a wide glyph we need to make sure that the clipping point
// is for the last physical char. Aka a double char should be clipped if it's second rendered
// char would break the boundary of the clip.
// var clippingPoint = new Point(characterPoint.X + symbol.Width - 1, characterPoint.Y);
var newPixel = new Pixel(symbol, foregroundColor, typeface.Style, typeface.Weight);
CurrentClip.ExecuteWithClipping(characterPoint, () =>
{
_pixelBuffer.Set((PixelBufferCoordinate)characterPoint,
(oldPixel, cp) => oldPixel.Blend(cp), consolePixel);
oldPixel =>
{
if (oldPixel.Width == 0)
{
// if the oldPixel was empty, we need to set the previous pixel to space
double targetX = characterPoint.X - 1;
if (targetX >= 0)
_pixelBuffer.Set(
(PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
oldPixel2 =>
new Pixel(
new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
oldPixel2.Background));
}
else if (oldPixel.Width > 1)
{
// if oldPixel was wide we need to reset overlapped symbols from empty to space
for (ushort i = 1; i < oldPixel.Width; i++)
{
double targetX = characterPoint.X + i;
if (targetX < _pixelBuffer.Size.Width)
_pixelBuffer.Set(
(PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
oldPixel2 =>
new Pixel(
new PixelForeground(new SimpleSymbol(' '),
Colors.Transparent), oldPixel2.Background));
}
}

// if the pixel was a wide character, we need to set the overlapped pixels to empty pixels.
if (newPixel.Width > 1)
for (int i = 1; i < symbol.Width; i++)
{
double targetX = characterPoint.X + i;
if (targetX < _pixelBuffer.Size.Width)
_pixelBuffer.Set(
(PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
oldPixel2 =>
new Pixel(
new PixelForeground(new SimpleSymbol(), Colors.Transparent),
oldPixel2.Background));
}

return oldPixel.Blend(newPixel);
});
});

if (symbol.Width > 1)
currentXPosition += symbol.Width;
else
currentXPosition++;
currentXPosition += symbol.Width;
}
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Avalonia.Media;
using Newtonsoft.Json;

namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
public class ColorConverter : JsonConverter<Color>
{
public override Color ReadJson(JsonReader reader, Type objectType, Color existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (Color.TryParse(reader.Value!.ToString(), out Color color))
return color;
return Colors.Transparent;
}
tomlm marked this conversation as resolved.
Show resolved Hide resolved

public override void WriteJson(JsonWriter writer, Color value, JsonSerializer serializer)
{
writer.WriteValue(value!.ToString());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,55 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;

namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
/// <summary>
/// https://en.wikipedia.org/wiki/Box-drawing_character
/// </summary>
[DebuggerDisplay("DrawingBox {Text}")]
public struct DrawingBoxSymbol : ISymbol, IEquatable<DrawingBoxSymbol>
[JsonConverter(typeof(SymbolConverter))]
public readonly struct DrawingBoxSymbol : ISymbol, IEquatable<DrawingBoxSymbol>
{
// all 0bXXXX_0000 are special values
private const byte BoldSymbol = 0b0001_0000;
private const byte EmptySymbol = 0b0;
private readonly byte _upRightDownLeft;

public DrawingBoxSymbol(byte upRightDownLeft)
{
_upRightDownLeft = upRightDownLeft;
Text = GetBoxSymbol(_upRightDownLeft).ToString();
UpRightDownLeft = upRightDownLeft;
Text = GetBoxSymbol(UpRightDownLeft).ToString();
}

public string Text { get; private init; }
public byte UpRightDownLeft { get; init; }

public ushort Width { get; } = 1;
public bool Equals(DrawingBoxSymbol other)
{
return UpRightDownLeft == other!.UpRightDownLeft;
}

[JsonIgnore] public string Text { get; init; }

[JsonIgnore] public ushort Width { get; } = 1;

public bool IsWhiteSpace()
{
return UpRightDownLeft == EmptySymbol;
}

public ISymbol Blend(ref ISymbol symbolAbove)
{
if (symbolAbove.IsWhiteSpace()) return this;

if (symbolAbove is not DrawingBoxSymbol drawingBoxSymbol)
return symbolAbove;

if (drawingBoxSymbol.UpRightDownLeft == BoldSymbol || UpRightDownLeft == BoldSymbol)
return new DrawingBoxSymbol(BoldSymbol);

return new DrawingBoxSymbol((byte)(UpRightDownLeft | drawingBoxSymbol.UpRightDownLeft));
}

/// <summary>
/// https://en.wikipedia.org/wiki/Code_page_437
Expand Down Expand Up @@ -99,24 +125,6 @@ private static char GetBoxSymbol(byte upRightDownLeft)
}
}

public bool IsWhiteSpace()
{
return _upRightDownLeft == EmptySymbol;
}

public ISymbol Blend(ref ISymbol symbolAbove)
{
if (symbolAbove.IsWhiteSpace()) return this;

if (symbolAbove is not DrawingBoxSymbol drawingBoxSymbol)
return symbolAbove;

if (drawingBoxSymbol._upRightDownLeft == BoldSymbol || _upRightDownLeft == BoldSymbol)
return new DrawingBoxSymbol(BoldSymbol);

return new DrawingBoxSymbol((byte)(_upRightDownLeft | drawingBoxSymbol._upRightDownLeft));
}

public static DrawingBoxSymbol UpRightDownLeftFromPattern(byte pattern, LineStyle lineStyle)
{
if (pattern == EmptySymbol) return new DrawingBoxSymbol(EmptySymbol);
Expand All @@ -134,19 +142,14 @@ public static DrawingBoxSymbol UpRightDownLeftFromPattern(byte pattern, LineStyl
}
}

public bool Equals(DrawingBoxSymbol other)
{
return _upRightDownLeft == other._upRightDownLeft;
}

public override bool Equals([NotNullWhen(true)] object obj)
{
return obj is DrawingBoxSymbol other && Equals(other);
}

public override int GetHashCode()
{
return _upRightDownLeft.GetHashCode();
return UpRightDownLeft.GetHashCode();
tomlm marked this conversation as resolved.
Show resolved Hide resolved
}

public static bool operator ==(DrawingBoxSymbol left, DrawingBoxSymbol right)
Expand All @@ -156,7 +159,7 @@ public override int GetHashCode()

public static bool operator !=(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
return !left.Equals(right);
return !left!.Equals(right);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Newtonsoft.Json;

namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
[JsonConverter(typeof(SymbolConverter))]
public interface ISymbol
{
/// <summary>
Expand Down
Loading
Loading