Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions examples/ted/TedApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ public TedApp (bool readOnly = false)
Editor.ConvertTabsToSpaces = e.NewValue == CheckState.Checked;
};

CheckBox useThemeBackgroundCheckBox = new ()
{
AllowCheckStateNone = false,
CanFocus = false,
Text = "Use _Theme Background",
Value = Editor.UseThemeBackground ? CheckState.Checked : CheckState.UnChecked
};

ThemeDropDown = new DropDownList<ThemeName>
{
Value = ThemeName.DarkPlus,
Expand Down Expand Up @@ -191,6 +199,15 @@ public TedApp (bool readOnly = false)
{
CommandView = convertTabsToSpacesCheckBox,
HelpText = "Insert spaces when Tab is pressed"
},
new MenuItem
{
Action = () =>
{
Editor.UseThemeBackground = useThemeBackgroundCheckBox.Value == CheckState.Checked;
},
CommandView = useThemeBackgroundCheckBox,
HelpText = "Use theme background for highlighted text"
}
]),
new MenuBarItem (Strings.menuHelp,
Expand Down
33 changes: 33 additions & 0 deletions src/Terminal.Gui.Editor/Editor.Drawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,46 @@ protected override bool OnDrawingContent (DrawContext? context)
Attribute normal = GetAttributeForRole (VisualRole.Normal);
Attribute selected = GetAttributeForRole (VisualRole.Active);

FillViewportBackground (viewport, normal);
DrawVisibleLines (viewport, normal, selected);
SetAttribute (normal);
UpdateCursor ();

return true;
}

/// <summary>
/// When <see cref="UseThemeBackground" /> is <see langword="true" /> and a syntax highlighter
/// provides a <see cref="ISyntaxHighlighter.DefaultBackground" />, fills the viewport with
/// that background so empty cells match per-token backgrounds.
/// </summary>
private void FillViewportBackground (Rectangle viewport, Attribute normal)
{
if (!UseThemeBackground)
{
return;
}

#pragma warning disable CS0618 // Type or member is obsolete
Terminal.Gui.Drawing.Color? themeBg = SyntaxHighlighter?.DefaultBackground;
#pragma warning restore CS0618 // Type or member is obsolete

if (themeBg is not { } bg)
{
return;
}

Attribute fillAttr = new (normal.Foreground, bg);
SetAttribute (fillAttr);

var spaces = new string (' ', viewport.Width);

for (var row = 0; row < viewport.Height; row++)
{
AddStr (0, row, spaces);
}
}

private void DrawVisibleLines (Rectangle viewport, Attribute normal, Attribute selected)
{
// The CS0618 here is the API's purpose: SyntaxHighlighter is [Obsolete] to warn
Expand Down
26 changes: 25 additions & 1 deletion src/Terminal.Gui.Editor/Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,29 @@ public int IndentationSize
/// <summary>Whether pressing Tab inserts spaces instead of a tab character.</summary>
public bool ConvertTabsToSpaces { get; set; }

/// <summary>
/// When <see langword="true" /> (the default), syntax-highlighted tokens keep both their
/// foreground and background from the syntax highlighting theme (e.g. Dark Plus's
/// <c>#1E1E1E</c> background). Set to <see langword="false" /> to override the
/// highlighter's background with the TG scheme's <see cref="VisualRole.Normal" />
/// background, matching the active application theme.
/// </summary>
public bool UseThemeBackground
{
get;
set
{
if (field == value)
{
return;
}

field = value;
ClearVisualLineCaches ();
SetNeedsDraw ();
}
} = true;

/// <summary>Whether tab characters render with a visible glyph in their first cell.</summary>
public bool ShowTabs
{
Expand Down Expand Up @@ -731,7 +754,8 @@ private CellVisualLine BuildVisualLine (
styledSegments,
selectionStart,
selectionEnd,
LineTransformers);
LineTransformers,
UseThemeBackground);

return _visualLineBuilder.Build (line, context);
}
Expand Down
10 changes: 9 additions & 1 deletion src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public sealed class VisualLineBuildContext (
IReadOnlyList<StyledSegment>? styledSegments,
int selectionStart,
int selectionEnd,
IEnumerable<IVisualLineTransformer> lineTransformers)
IEnumerable<IVisualLineTransformer> lineTransformers,
bool useThemeBackground = true)
{
public TextDocument Document { get; } = document;

Expand All @@ -34,5 +35,12 @@ public sealed class VisualLineBuildContext (

public IEnumerable<IVisualLineTransformer> LineTransformers { get; } = lineTransformers;

/// <summary>
/// When <see langword="true" />, styled-segment backgrounds are preserved from the
/// syntax highlighting theme. When <see langword="false" />, backgrounds are replaced
/// with <see cref="NormalAttribute" />'s background so text blends into the TG scheme.
/// </summary>
public bool UseThemeBackground { get; } = useThemeBackground;

public bool HasSelection => SelectionStart < SelectionEnd;
}
11 changes: 9 additions & 2 deletions src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,15 @@ private static Attribute GetAttribute (VisualLineBuildContext context, int segme
return context.NormalAttribute;
}

return context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute
?? context.NormalAttribute;
Attribute segmentAttribute = context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute
?? context.NormalAttribute;

if (!context.UseThemeBackground)
{
return new Attribute (segmentAttribute.Foreground, context.NormalAttribute.Background);
Comment thread
tig marked this conversation as resolved.
}

return segmentAttribute;
}

private static int GetSegmentEnd (IReadOnlyList<StyledSegment>? segments, int segmentIndex)
Expand Down
53 changes: 53 additions & 0 deletions tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,57 @@ public async Task Cursor_Position_After_Tab_Uses_Expanded_Tab_Columns ()

Assert.Equal (new Point (4, 0), fx.Top.Editor.Cursor.Position);
}

[Fact]
public async Task UseThemeBackground_True_Preserves_Highlighter_Background ()
{
const string text = "public class C";

await using AppFixture<EditorTestHost> fx = new (() => new EditorTestHost (text));
#pragma warning disable CS0618 // Type or member is obsolete
fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter ();
#pragma warning restore CS0618 // Type or member is obsolete
fx.Top.Editor.UseThemeBackground = true;
fx.Render ();

TextMateSyntaxHighlighter highlighter = new ();
Attribute expected = highlighter.Highlight (text, "csharp")[0].Attribute!.Value;

Cell cell = fx.Driver.Contents![0, 0];
Assert.Equal ("p", cell.Grapheme);
Assert.Equal (expected, cell.Attribute);
}

[Fact]
public async Task UseThemeBackground_False_Overrides_Highlighter_Background ()
{
const string text = "public class C";

await using AppFixture<EditorTestHost> fx = new (() => new EditorTestHost (text));
#pragma warning disable CS0618 // Type or member is obsolete
fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter ();
#pragma warning restore CS0618 // Type or member is obsolete
fx.Top.Editor.UseThemeBackground = false;
fx.Render ();

Attribute normal = fx.Top.Editor.GetAttributeForRole (VisualRole.Normal);
Cell cell = fx.Driver.Contents![0, 0];
Assert.Equal ("p", cell.Grapheme);

// The foreground should come from the highlighter (different from Normal's foreground).
TextMateSyntaxHighlighter highlighter = new ();
Attribute highlighterAttr = highlighter.Highlight (text, "csharp")[0].Attribute!.Value;
Assert.Equal (highlighterAttr.Foreground, cell.Attribute!.Value.Foreground);

// The background must match the TG theme's Normal background, not the highlighter's.
Assert.Equal (normal.Background, cell.Attribute!.Value.Background);
}

[Fact]
public async Task UseThemeBackground_Defaults_To_True ()
{
await using AppFixture<EditorTestHost> fx = new (() => new EditorTestHost ("Hello"));

Assert.True (fx.Top.Editor.UseThemeBackground);
}
}
Loading