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

Add support for richer console colors and FontWeight and Bold TextDecorator #122

Closed
wants to merge 98 commits into from
Closed
Show file tree
Hide file tree
Changes from 96 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
17d9623
Avalonia 11.0.9
jinek Apr 13, 2024
bd8e2e3
Refactor multiple code files and update code comments
jinek Apr 14, 2024
395c6e2
Update helper method syntax and adjust bindings
jinek Apr 14, 2024
4dd755d
more fixes
jinek Apr 17, 2024
85d5eac
Add Avalonia threading and improve text shaping
jinek Apr 17, 2024
6e8aa2b
Refactor drawing and rendering methods
jinek Apr 17, 2024
6efaf75
application is now being run, but does not render
jinek Apr 17, 2024
b0cf503
Welcome page is rendered
jinek Apr 17, 2024
bb117b0
Window is painted when resized
jinek Apr 20, 2024
acfc4fd
Clickable button
jinek Apr 27, 2024
ed52281
- unused code
jinek May 1, 2024
259893e
- unused button
jinek May 1, 2024
dc51e56
Button shadow drawing
jinek May 1, 2024
773b1e6
Button drawing/ SymbolsControl.cs drawing
jinek May 1, 2024
2eff951
text trimming is fixed
jinek May 2, 2024
d69b033
Wild text rendering
jinek May 2, 2024
575f539
Initialize transform with Matrix Identity.
jinek May 6, 2024
7edf517
Platform settings for investigation
jinek May 6, 2024
40e45ec
LineBrush is rendered
jinek May 7, 2024
2a609ce
ScrollViewer now works
jinek Aug 12, 2024
52bf2fb
Null reference check brought back
jinek Aug 12, 2024
9204d7f
hit test for GlyphRun
jinek Aug 12, 2024
3a2579d
+1, similar to avalonia
jinek Aug 12, 2024
2b66acf
Textbox working
jinek Aug 14, 2024
d372108
more textbox
jinek Aug 14, 2024
d0d399f
keyboardnavigationhandler is back
jinek Aug 16, 2024
48d031f
combobox fix 1
jinek Aug 17, 2024
f15265e
ComboBox drop-down works fine
jinek Aug 20, 2024
874b29d
array in the combobox items
jinek Aug 20, 2024
478fd6a
Single test run. Buttons and TextBlock tests success separately.
jinek Aug 24, 2024
0e8f835
Multiple tests now can run
jinek Sep 4, 2024
1ffe787
ProgressBar.axaml adjusted
jinek Sep 8, 2024
afb4833
Separator
jinek Sep 8, 2024
d8e7eef
Dialog fixed
jinek Sep 15, 2024
82b946f
Entire list item is clickable
jinek Sep 15, 2024
bd34f74
Entire row in datagrid is clickable
jinek Sep 15, 2024
7871b19
Custom textbox Caret
jinek Sep 17, 2024
a807e3e
Comment removed
jinek Sep 20, 2024
4527aec
datagrid lines
jinek Oct 18, 2024
6cbff99
Calendar fixed
jinek Oct 19, 2024
5944506
build fixes
jinek Oct 20, 2024
eaefba3
more quality fixes
jinek Oct 20, 2024
112f9ad
TextBoxTests.cs
jinek Oct 20, 2024
456630a
ComboBoxTests.cs
jinek Oct 20, 2024
77af91f
FlyoutTests.cs
jinek Oct 20, 2024
e845e75
PR fixes
jinek Oct 20, 2024
8efff9e
build fixes
jinek Oct 20, 2024
121221a
refactoring
jinek Oct 20, 2024
8962d2c
formatting and small fixes
jinek Oct 21, 2024
d1cbe6b
Limits to run jobs
jinek Oct 21, 2024
5f0bd23
newer analysis version and null referene fix
jinek Oct 21, 2024
36cfbcc
Delegate error
jinek Oct 21, 2024
c1de176
other errors
jinek Oct 21, 2024
1b0ab64
PR fixes
jinek Oct 21, 2024
5a84e0f
last warning
jinek Oct 21, 2024
e9c3497
Automated JetBrains cleanup
github-actions[bot] Oct 21, 2024
2dcb1c8
newer jb version
jinek Oct 22, 2024
34c9336
Revert "Automated JetBrains cleanup"
jinek Oct 22, 2024
fc81b86
fix click scroll bug and wheel scroll bug.
tomlm Oct 29, 2024
0d834d6
Added full color support
tomlm Oct 29, 2024
8a79a69
cleanup brush detection code
tomlm Oct 29, 2024
8da4f66
add relative Shade()/Brighten() methods
tomlm Oct 29, 2024
ff2767f
add support for underline
tomlm Oct 29, 2024
98f6ae6
merge main into cryaon
tomlm Oct 29, 2024
242628b
merge with master
tomlm Oct 30, 2024
47de318
clean up sample#
tomlm Oct 30, 2024
32f020e
cleanup sample again
tomlm Oct 30, 2024
b38f9cd
cleanup switch statement
tomlm Oct 30, 2024
784fa2b
cleanup sample
tomlm Oct 30, 2024
0273b81
Consolidated code for converting IBrush in ConsoleBrush.FromBrush() m…
tomlm Oct 30, 2024
4cc39fe
Merge branch 'main' into tomlm/crayon
tomlm Oct 30, 2024
ab1617e
add suport for linearGradientBrush on rectangles.
tomlm Oct 30, 2024
2f5df27
Update src/Tests/Consolonia.TestsCore/Consolonia.TestsCore.csproj
tomlm Oct 30, 2024
b355163
Update src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs
tomlm Oct 30, 2024
ebf1499
Update src/Consolonia.Gallery/Gallery/GalleryViews/GalleryColors.axam…
tomlm Oct 30, 2024
21ab9bf
Update src/Tests/Consolonia.TestsCore/UnitTestConsole.cs
tomlm Oct 30, 2024
ac51348
plumb textdecorations through instead of using Oblique
tomlm Oct 30, 2024
be95d7b
Merge branch 'tomlm/crayon' of https://github.com/tomlm/Consolonia in…
tomlm Oct 30, 2024
e41651d
code review
tomlm Oct 30, 2024
7090b8f
fix bad auto-merge <sigh>
tomlm Oct 30, 2024
f08287d
update packages
tomlm Oct 30, 2024
7c22861
cleanup based on code review
tomlm Oct 30, 2024
9245be5
Update src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs
tomlm Oct 30, 2024
1b518d6
Merge branch 'tomlm/crayon' of https://github.com/tomlm/Consolonia in…
tomlm Oct 30, 2024
b6858fa
code review cleanup
tomlm Oct 30, 2024
c6b906c
final round of code reviews
tomlm Oct 30, 2024
5f1bd74
more cleanup
tomlm Oct 30, 2024
37501e3
more cleanup
tomlm Oct 30, 2024
cec48c2
removed consolebrush converter
tomlm Oct 30, 2024
8ef8a47
Consolidate Special character logic in Symbol.GetCharacer()
tomlm Oct 30, 2024
eea9ba7
add support for Radial and Conic brushs
tomlm Oct 30, 2024
3b45704
fix unit tests
tomlm Oct 30, 2024
2c28b9e
fix bug in gradient interpolation
tomlm Oct 30, 2024
56daa77
remove -1, it was not necessary
tomlm Oct 30, 2024
894317d
remove testsCore from packable list
tomlm Oct 30, 2024
c5d315c
lint fixes
tomlm Oct 31, 2024
d2580a4
code rabbit changes
tomlm Oct 31, 2024
baca3bb
more lint
tomlm Oct 31, 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
1 change: 1 addition & 0 deletions src/Consolonia.Core/Consolonia.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.FreeDesktop" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
<PackageReference Include="Crayon" Version="2.0.69" />
<PackageReference Include="NullLib.ConsoleEx" Version="1.0.4.4" />
</ItemGroup>

Expand Down
214 changes: 214 additions & 0 deletions src/Consolonia.Core/Drawing/ConsoleBrush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
using System;
using Avalonia;
using Avalonia.Media;
using Consolonia.Core.Drawing.PixelBufferImplementation;
using Consolonia.Core.Infrastructure;

namespace Consolonia.Core.Drawing
{
public class ConsoleBrush : AvaloniaObject, IImmutableBrush
{
public static readonly StyledProperty<Color> ColorProperty =
AvaloniaProperty.Register<ConsoleBrush, Color>(nameof(Color));

public static readonly StyledProperty<PixelBackgroundMode> ModeProperty =
AvaloniaProperty.Register<ConsoleBrush, PixelBackgroundMode>(nameof(Mode));

// ReSharper disable once UnusedMember.Global
public ConsoleBrush(Color color, PixelBackgroundMode mode) : this(color)
{
Mode = mode;
}

public ConsoleBrush(Color color)
{
Color = color;
}

public ConsoleBrush()
{
}

/// <summary>
/// Convert a IBrush to a Brush.
/// </summary>
/// <remarks>
/// NOTE: If it's a ConsoleBrush it will be passed through unchanged, unless mode is set then it will convert consolebrush to mode
/// </remarks>
/// <param name="brush"></param>
/// <param name="mode">Default is Colored.</param>
/// <returns></returns>
public static ConsoleBrush FromBrush(IBrush brush, PixelBackgroundMode? mode = null)
{
ArgumentNullException.ThrowIfNull(brush, nameof(brush));

switch (brush)
{
case ConsoleBrush consoleBrush:
if (mode != null && consoleBrush.Mode != mode)
{
return new ConsoleBrush(consoleBrush.Color, mode.Value);
}
return consoleBrush;

case LineBrush lineBrush:
switch (lineBrush.Brush)
{
case ConsoleBrush consoleBrush:
return consoleBrush;
case ISolidColorBrush br:
return new ConsoleBrush(br.Color, mode ?? PixelBackgroundMode.Colored);
default:
ConsoloniaPlatform.RaiseNotSupported(6);
return null;
}
tomlm marked this conversation as resolved.
Show resolved Hide resolved

case ISolidColorBrush solidBrush:
return new ConsoleBrush(solidBrush.Color, mode ?? PixelBackgroundMode.Colored);

default:
ConsoloniaPlatform.RaiseNotSupported(6);
return null;
}
}

public static ConsoleBrush FromPosition(IBrush brush, int x, int y, int width, int height)
{
ArgumentNullException.ThrowIfNull(brush);
if (x < 0 || x > width)
throw new ArgumentOutOfRangeException(nameof(x), "x is out bounds");
if (y < 0 || y > height)
throw new ArgumentOutOfRangeException(nameof(y), "y is out bounds");
if (width <= 0)
throw new ArgumentOutOfRangeException(nameof(width), "Width must be positive");
if (height <= 0)
throw new ArgumentOutOfRangeException(nameof(height), "Height must be positive");

switch (brush)
{
case ILinearGradientBrush gradientBrush:
{
// Calculate the relative position within the gradient
double horizontalRelativePosition = (double)x / width;
double verticalRelativePosition = (double)y / height;
Comment on lines +92 to +93
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent division by zero in position calculations.

When width or height is 0, division by zero will occur.

-double horizontalRelativePosition = (double)x / width;
-double verticalRelativePosition = (double)y / height;
+double horizontalRelativePosition = width == 0 ? 0 : (double)x / width;
+double verticalRelativePosition = height == 0 ? 0 : (double)y / height;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
double horizontalRelativePosition = (double)x / width;
double verticalRelativePosition = (double)y / height;
double horizontalRelativePosition = width == 0 ? 0 : (double)x / width;
double verticalRelativePosition = height == 0 ? 0 : (double)y / height;


// Interpolate horizontal and vertical colors
var horizontalColor = InterpolateColor(gradientBrush, horizontalRelativePosition);
var verticalColor = InterpolateColor(gradientBrush, verticalRelativePosition);

// Average the two colors to get the final color
var color = BlendColors(horizontalColor, verticalColor);
return new ConsoleBrush(color);
}
case IRadialGradientBrush radialBrush:
{
// Calculate the normalized center coordinates
double centerX = radialBrush.Center.Point.X * width;
double centerY = radialBrush.Center.Point.Y * height;

// Calculate the distance from the center
double dx = x - centerX;
double dy = y - centerY;
double distance = Math.Sqrt(dx * dx + dy * dy);

// Normalize the distance based on the brush radius
double normalizedDistance = distance / (Math.Min(width, height) * radialBrush.Radius);

// Clamp the normalized distance to [0, 1]
normalizedDistance = Math.Min(Math.Max(normalizedDistance, 0), 1);

// Interpolate the color based on the normalized distance
var color = InterpolateColor(radialBrush, normalizedDistance);
return new ConsoleBrush(color);
}
case IConicGradientBrush conicBrush:
{
// Calculate the relative position within the gradient
double horizontalRelativePosition = (double)x / width;
double verticalRelativePosition = (double)y / height;

// Interpolate horizontal and vertical colors
var horizontalColor = InterpolateColor(conicBrush, horizontalRelativePosition);
var verticalColor = InterpolateColor(conicBrush, verticalRelativePosition);

// Average the two colors to get the final color
var color = BlendColors(horizontalColor, verticalColor);
return new ConsoleBrush(color);
}

default:
return FromBrush(brush);
}
}

public PixelBackgroundMode Mode
{
get => GetValue(ModeProperty);
set => SetValue(ModeProperty, value);
}

public Color Color
{
get => GetValue(ColorProperty);
set => SetValue(ColorProperty, value);
}

// ReSharper disable once UnusedMember.Global used by Avalonia
// ReSharper disable once UnusedParameter.Global
public IBrush ProvideValue(IServiceProvider _)
{
return this;
}

public double Opacity => 1;
public ITransform Transform => null;
public RelativePoint TransformOrigin => RelativePoint.TopLeft;

private static Color InterpolateColor(IGradientBrush brush, double relativePosition)
{
IGradientStop before = null;
IGradientStop after = null;

foreach (var stop in brush.GradientStops)
{
if (stop.Offset <= relativePosition)
{
before = stop;
}
else if (stop.Offset >= relativePosition)
{
after = stop;
break;
}
}
if (before == null && after == null)
throw new ArgumentException("no gradientstops defined");

if (before == null)
{
return after.Color;

Check warning on line 189 in src/Consolonia.Core/Drawing/ConsoleBrush.cs

View workflow job for this annotation

GitHub Actions / build

"[PossibleNullReferenceException] Possible 'System.NullReferenceException'" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/ConsoleBrush.cs(189,24)
}
if (after == null)
{
return before.Color;
}

double ratio = (relativePosition - before.Offset) / (after.Offset - before.Offset);
byte r = (byte)(before.Color.R + ratio * (after.Color.R - before.Color.R));
byte g = (byte)(before.Color.G + ratio * (after.Color.G - before.Color.G));
byte b = (byte)(before.Color.B + ratio * (after.Color.B - before.Color.B));
byte a = (byte)(before.Color.A + ratio * (after.Color.A - before.Color.A));

return Color.FromArgb(a, r, g, b);
}
Comment on lines +167 to +203
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize color interpolation using LINQ.

The current implementation can be simplified and made more efficient using LINQ.

 private static Color InterpolateColor(IGradientBrush brush, double relativePosition)
 {
-    IGradientStop before = null;
-    IGradientStop after = null;
-
-    foreach (var stop in brush.GradientStops)
-    {
-        if (stop.Offset <= relativePosition)
-        {
-            before = stop;
-        }
-        else if (stop.Offset >= relativePosition)
-        {
-            after = stop;
-            break;
-        }
-    }
+    var stops = brush.GradientStops.OrderBy(s => s.Offset).ToList();
+    if (!stops.Any())
+        throw new ArgumentException("No gradient stops defined", nameof(brush));
+
+    var before = stops.Where(s => s.Offset <= relativePosition).MaxBy(s => s.Offset);
+    var after = stops.Where(s => s.Offset > relativePosition).MinBy(s => s.Offset);

     if (before == null && after == null)
         throw new ArgumentException("no gradientstops defined");

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: build

[warning] 189-189:
"[PossibleNullReferenceException] Possible 'System.NullReferenceException'" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/ConsoleBrush.cs(189,24)


private static Color BlendColors(Color color1, Color color2)
{
int r = (color1.R + color2.R) / 2;
int g = (color1.G + color2.G) / 2;
int b = (color1.B + color2.B) / 2;
int a = (color1.A + color2.A) / 2;
return Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b);
}
}
}
2 changes: 1 addition & 1 deletion src/Consolonia.Core/Drawing/ConsoloniaRenderInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ IBitmapImpl IPlatformRenderInterface.ResizeBitmap(IBitmapImpl bitmapImpl, PixelS
{
throw new NotImplementedException();
}

public IBitmapImpl LoadBitmap(
PixelFormat format,
AlphaFormat alphaFormat,
Expand Down
Loading
Loading