Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a753d0c
links with tests
ccoulioufr Feb 22, 2026
5e30891
Merge branch 'v2_develop' into v2_develop
tig Feb 22, 2026
26f56b1
Merge branch 'v2_develop' into v2_develop
tig Feb 22, 2026
474573a
Merge branch 'v2_develop' into v2_develop
tig Feb 22, 2026
0e0df72
Merge branch 'v2_develop' into v2_develop
tig Feb 22, 2026
ba9bca5
Merge branch 'v2_develop' into v2_develop
ccoulioufr Feb 22, 2026
fac8853
Update Terminal.Gui/Views/Link.cs
ccoulioufr Feb 22, 2026
7888860
Update Terminal.Gui/Views/Link.cs
ccoulioufr Feb 22, 2026
7986d8b
explicit type declarations in tests
ccoulioufr Feb 22, 2026
e5c065c
Merge branch 'v2_develop' of https://github.com/ccoulioufr/Terminal.G…
ccoulioufr Feb 22, 2026
9977b43
using Cancellable Work Pattern recommandations
ccoulioufr Feb 22, 2026
7deb8ae
events testing
ccoulioufr Feb 22, 2026
4852380
Merge branch 'v2_develop' into v2_develop
tig Feb 22, 2026
34224c2
uicatalog url indicator + no crash Uri
ccoulioufr Feb 22, 2026
e887832
Merge branch 'v2_develop' of https://github.com/ccoulioufr/Terminal.G…
ccoulioufr Feb 22, 2026
d45b0a6
fix scenario test
ccoulioufr Feb 22, 2026
2b074bd
Link Osc8 test
ccoulioufr Feb 23, 2026
0ecfcdd
bad url is now has no effect and is o
ccoulioufr Feb 23, 2026
5481454
Url validation in Link class
ccoulioufr Feb 24, 2026
ba38682
Merge branch 'v2_develop' into v2_develop
tig Feb 26, 2026
b8db53e
fix link test scheme color
ccoulioufr Feb 26, 2026
d436c0e
Merge branch 'v2_develop' into v2_develop
tig Feb 27, 2026
6a66717
Merge branch 'v2_develop' into v2_develop
tig Mar 1, 2026
52463a6
no url in cell + SetNeedsDraw on url change + empty default url + up…
ccoulioufr Mar 1, 2026
cb4b3a2
Merge branch 'v2_develop' of https://github.com/ccoulioufr/Terminal.G…
ccoulioufr Mar 1, 2026
30f0348
Merge branch 'v2_develop' into v2_develop
ccoulioufr Mar 2, 2026
50f627f
Merge branch 'gui-cs:v2_develop' into v2_develop
ccoulioufr Mar 5, 2026
1e55338
Merge branch 'v2_develop' into v2_develop
tig Mar 6, 2026
db81f44
Refactor Link control: API, UI, and platform integration
tig Mar 6, 2026
f42e548
Refactor Link: decouple Text/Title/Url, expand tests
tig Mar 6, 2026
abdb993
Improve Link API documentation
tig Mar 6, 2026
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
102 changes: 102 additions & 0 deletions Examples/UICatalog/Scenarios/Links.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#nullable enable

namespace UICatalog.Scenarios;

[ScenarioMetadata ("Links", "Demonstrates how Links work.")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("Mouse and Keyboard")]
public class Links : Scenario
{
public override void Main ()
{
ConfigurationManager.Enable (ConfigLocations.All);
using IApplication app = Application.Create ();
app.Init ();

using Window mainWindow = new ()
{
Title = GetQuitKeyAndName ()
};

Label textLabel = new ()
{
Text = "_Text:",
X = 1,
Y = 1
};
mainWindow.Add (textLabel);

TextField textField = new ()
{
X = Pos.Right (textLabel) + 2,
Y = 1,
Width = 20
};
mainWindow.Add (textField);

Label urlLabel = new ()
{
Text = "_Url:",
X = 1,
Y = Pos.Bottom (textField) + 1
};
mainWindow.Add (urlLabel);

TextField urlField = new ()
{
X = Pos.Right (urlLabel) + 2,
Y = Pos.Bottom (textField) + 1,
Width = 64
};
mainWindow.Add (urlField);

Label simpleUrlLabel = new ()
{
X = 1,
Y = Pos.Bottom (urlField) + 2
};
mainWindow.Add (simpleUrlLabel);

FrameView linkFrame = new ()
{
Title = "_Link rendering",
X = 0,
Y = Pos.Bottom (simpleUrlLabel) + 2,
Width = 64,
Height = 8,
AssignHotKeys = true
};

Link link = new ()
{
X = 1,
Y = 1,
Height = 1,
Width = 64
};

link.UrlChanged += (s, e) => simpleUrlLabel.Text = link.Url;
textField.ValueChanged += (s, e) => link.Text = e.NewValue ?? link.Url;
urlField.ValueChanged += (s, e) => link.Url = e.NewValue ?? Link.DEFAULT_URL;
linkFrame.Add (link);

textField.Text = "GitHub repo";
urlField.Text = "https://github.com/gui-cs/Terminal.Gui";

Button copyButton = new ()
{
Title = "_Copy",
X = Pos.Center (),
Y = Pos.Bottom (link) + 2,

};
copyButton.Accepting += (s, e) => link.Copy ();

linkFrame.Add (copyButton);

mainWindow.Add (linkFrame);

app.Run (mainWindow);
}

}
8 changes: 7 additions & 1 deletion Terminal.Gui/Drawing/Cell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Terminal.Gui.Drawing;
/// Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
/// <see cref="IDriver"/>).
/// </summary>
public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, string Grapheme = "")
public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, string Grapheme = "", string? Url = null)
{
/// <summary>The attributes to use when drawing the Glyph.</summary>
public Attribute? Attribute { get; set; } = Attribute;
Expand All @@ -16,6 +16,12 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, st
/// </summary>
public bool IsDirty { get; set; } = IsDirty;

/// <summary>
/// Gets or sets the URL associated with this cell for OSC 8 hyperlink support.
/// When set, the cell will be rendered as a clickable hyperlink in terminals that support OSC 8.
/// </summary>
public string? Url { get; set; } = Url;
Comment thread
tig marked this conversation as resolved.
Outdated

private string _grapheme = Grapheme;

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions Terminal.Gui/Drivers/DriverImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ public virtual void SetScreenSize (int width, int height)
/// <inheritdoc/>
public Region? Clip { get => _outputBuffer.Clip; set => _outputBuffer.Clip = value; }

/// <inheritdoc/>
public string? CurrentUrl { get => _outputBuffer.CurrentUrl; set => _outputBuffer.CurrentUrl = value; }

/// <summary>Clears the <see cref="IDriver.Contents"/> of the driver.</summary>
public void ClearContents ()
{
Expand Down
7 changes: 7 additions & 0 deletions Terminal.Gui/Drivers/IDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ public interface IDriver : IDisposable
/// </summary>
Attribute CurrentAttribute { get; set; }

/// <summary>
/// Gets or sets the URL that will be associated with cells added via <see cref="AddRune(Rune)"/> or <see cref="AddStr(string)"/>.
/// When set, subsequent cells will include this URL for OSC 8 hyperlink rendering.
/// Set to <see langword="null"/> to stop associating URLs with cells.
/// </summary>
string? CurrentUrl { get; set; }

/// <summary>
/// Updates <see cref="IDriver.Col"/> and <see cref="IDriver.Row"/> to the specified column and row in
/// <see cref="IDriver.Contents"/>.
Expand Down
6 changes: 6 additions & 0 deletions Terminal.Gui/Drivers/Output/IOutputBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ public interface IOutputBuffer
/// <summary>The number of rows visible in the terminal.</summary>
int Rows { get; set; }

/// <summary>
/// Gets or sets the URL that will be associated with cells added via <see cref="AddRune(Rune)"/> or <see cref="AddStr(string)"/>.
/// When set, subsequent cells will include this URL for OSC 8 hyperlink rendering.
/// </summary>
string? CurrentUrl { get; set; }

/// <summary>
/// Changes the size of the buffer to the given size
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions Terminal.Gui/Drivers/Output/OutputBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public bool IsLegacyConsole
// Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange().
private TextStyle _redrawTextStyle = TextStyle.None;

// Last URL used for tracking hyperlink state
private string? _lastUrl = null;

StringBuilder _lastOutputStringBuilder = new ();

/// <summary>
Expand All @@ -75,6 +78,7 @@ public virtual void Write (IOutputBuffer buffer)
}

outputStringBuilder.Clear ();
_lastUrl = null; // Reset URL state at the start of each row

// Process columns in row
for (int col = left; col < cols; col++)
Expand Down Expand Up @@ -138,6 +142,13 @@ public virtual void Write (IOutputBuffer buffer)
{
SetCursorPositionImpl (lastCol, row);

// Close any open hyperlink before processing URLs
if (_lastUrl is { })
{
outputStringBuilder.Append (EscSeqUtils.OSC_EndHyperlink ());
_lastUrl = null;
}

// Wrap URLs with OSC 8 hyperlink sequences
StringBuilder processed = Osc8UrlLinker.WrapOsc8 (outputStringBuilder);
Write (processed);
Expand Down Expand Up @@ -258,6 +269,24 @@ protected void AppendCellAnsi (Cell cell, StringBuilder output, ref Attribute? l
{
Attribute? attribute = cell.Attribute;

// Handle URL hyperlink state changes
if (!IsLegacyConsole && cell.Url != _lastUrl)
{
// If we were in a hyperlink, end it
if (_lastUrl is { })
{
output.Append (EscSeqUtils.OSC_EndHyperlink ());
}

// If starting a new hyperlink, begin it
if (!string.IsNullOrEmpty (cell.Url))
{
output.Append (EscSeqUtils.OSC_StartHyperlink (cell.Url));
}

_lastUrl = cell.Url;
}

// Add ANSI escape sequence for attribute change
if (attribute.HasValue && attribute.Value != lastAttr)
{
Expand Down
7 changes: 7 additions & 0 deletions Terminal.Gui/Drivers/Output/OutputBufferImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public class OutputBufferImpl : IOutputBuffer
/// </summary>
public Attribute CurrentAttribute { get; set; }

/// <summary>
/// Gets or sets the URL that will be associated with cells added via <see cref="AddRune(Rune)"/> or <see cref="AddStr(string)"/>.
/// When set, subsequent cells will include this URL for OSC 8 hyperlink rendering.
/// </summary>
public string? CurrentUrl { get; set; }

/// <summary>The leftmost column in the terminal.</summary>
public virtual int Left { get; set; } = 0;

Expand Down Expand Up @@ -209,6 +215,7 @@ private void AddGrapheme (string grapheme)
private void SetAttributeAndDirty (int col, int row)
{
Contents! [row, col].Attribute = CurrentAttribute;
Contents [row, col].Url = CurrentUrl;
Contents [row, col].IsDirty = true;
}

Expand Down
Loading
Loading