Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d40e3bb
Show URL on enter link
ccoulioufr Mar 24, 2026
5dbab39
tooltip used by link
ccoulioufr Mar 24, 2026
96a40a3
Refactor About dialog and update Link activation logic
tig Mar 25, 2026
37b4fd3
Upgrade About box to use Link and gradient box-drawing logo
tig Mar 25, 2026
bfb24ad
tooltip system
ccoulioufr Mar 25, 2026
0d7424c
merge tig fixes
ccoulioufr Mar 25, 2026
488f315
fix UseToolTip case
ccoulioufr Mar 25, 2026
bf17542
refactor with provider
ccoulioufr Mar 26, 2026
bd8c3c8
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Mar 27, 2026
20fc680
Code cleanup & review.
tig Mar 27, 2026
6d9d6ec
Update Terminal.Gui/Views/Link.cs
tig Mar 27, 2026
e4e842a
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Mar 28, 2026
c4a7117
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Mar 30, 2026
7814520
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Mar 30, 2026
5862ce8
RemoveTooltip case
ccoulioufr Mar 30, 2026
4963bf1
MakeVisible multi event
ccoulioufr Mar 30, 2026
5b133f4
fix ToolTipExtensions name
ccoulioufr Mar 30, 2026
5de6af6
fix ToolTipManager name
ccoulioufr Mar 30, 2026
72c81ad
fix tooltip case
ccoulioufr Mar 30, 2026
4563bd1
unregister Disposing
ccoulioufr Mar 30, 2026
9c80674
empty tooltip removes tooltip
ccoulioufr Mar 30, 2026
ede92d2
link comment
ccoulioufr Mar 30, 2026
87a04cb
threadsafe manager
ccoulioufr Mar 30, 2026
76d0f80
internal fields for tests
ccoulioufr Mar 30, 2026
47dff31
Merge branch 'fix/show-link-url-on-enter' of https://github.com/ccoul…
tig Mar 30, 2026
b2a066c
tooltip tests
ccoulioufr Mar 30, 2026
3229bd9
ShowsHidesToolTip with runnable
ccoulioufr Mar 31, 2026
e333fbe
ApplicationToolTip
ccoulioufr Apr 4, 2026
108975a
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Apr 6, 2026
3beee2d
Merge branch 'fix/show-link-url-on-enter' of https://github.com/ccoul…
tig Apr 6, 2026
069f001
Improve link handling and UI consistency in scenarios
tig Apr 6, 2026
448f707
more code cleanup.
tig Apr 6, 2026
bc6c94c
Enhance Link design mode with tooltip on initialization
tig Apr 6, 2026
0d8b7c5
Fixed double OpenUrl on mouse.
tig Apr 6, 2026
56cb119
Merge branch 'v2_develop' into fix/show-link-url-on-enter
tig Apr 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
4 changes: 0 additions & 4 deletions Examples/UICatalog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ said concepts & features.
The original `demo.cs` sample app for Terminal.Gui is neither good to showcase, nor does it explain different concepts. In addition, because it is built on a single source file, it has proven to cause friction when multiple contributors are simultaneously working on different aspects of Terminal.Gui.
See [Issue #368](https://github.com/giu-cs/Terminal.Gui/issues/368) for more background.

# API Reference

* [UI Catalog API Reference](https://gui-cs.github.io/Terminal.Gui/api/UICatalog/UICatalog.html)

## How To Use

Build and run UI Catalog by typing `dotnet run` from the `UI Catalog` folder or by using the `Terminal.Gui` Visual Studio solution.
Expand Down
57 changes: 21 additions & 36 deletions Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
#nullable enable
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
Expand All @@ -30,7 +27,7 @@ public override void Main ()
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Fill (),
Height = Dim.Fill ()
};

_imageView = new ImageView { Width = Dim.Fill (), Height = Dim.Fill ()! - 2 };
Expand All @@ -40,22 +37,21 @@ public override void Main ()
var lbl = new Label { Y = Pos.AnchorEnd (), Text = "Image by Wikiscient" };
win.Add (lbl);

var lbl2 = new Label
Link link = new ()
{
// This ensures the URL that has an underscore is drawn correctly
HotKeySpecifier = new Rune ('\xFFFF'),
X = Pos.AnchorEnd (), Y = Pos.AnchorEnd (),
Text = "https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"
X = Pos.AnchorEnd (),
Y = Pos.AnchorEnd (),
Url = "https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"
};
win.Add (lbl2);
app.ToolTips!.SetToolTip (link, () => link.Url);
win.Add (link);

// Start the animation after the window is initialized
win.Initialized += OnWinOnInitialized;

app.Run (win);
}


private void OnWinOnInitialized (object? sender, EventArgs args)
{
DirectoryInfo dir;
Expand All @@ -71,9 +67,7 @@ private void OnWinOnInitialized (object? sender, EventArgs args)
dir = new DirectoryInfo (AppContext.BaseDirectory);
}

var f = new FileInfo (
Path.Combine (dir.FullName, "Scenarios/AnimationScenario", "Spinning_globe_dark_small.gif")
);
var f = new FileInfo (Path.Combine (dir.FullName, "Scenarios/AnimationScenario", "Spinning_globe_dark_small.gif"));

if (!f.Exists)
{
Expand All @@ -85,18 +79,16 @@ private void OnWinOnInitialized (object? sender, EventArgs args)

_imageView!.SetImage (Image.Load<Rgba32> (File.ReadAllBytes (f.FullName)));

Task.Run (
() =>
Task.Run (() =>
{
while (_imageView?.App?.Initialized == true)
{
// When updating from a Thread/Task always use Invoke
_imageView?.App?.Invoke (
(_) =>
{
_imageView?.NextFrame ();
_imageView?.SetNeedsDraw ();
});
_imageView?.App?.Invoke (_ =>
{
_imageView?.NextFrame ();
_imageView?.SetNeedsDraw ();
});

Task.Delay (100).Wait ();
}
Expand Down Expand Up @@ -171,14 +163,15 @@ private class ImageView : View
private Image<Rgba32> []? _fullResImages;
private Image<Rgba32> []? _matchSizes;
private Rectangle _oldSize = Rectangle.Empty;
public void NextFrame () { _currentFrame = (_currentFrame + 1) % _frameCount; }
public void NextFrame () => _currentFrame = (_currentFrame + 1) % _frameCount;

protected override bool OnDrawingContent (DrawContext? context)
{
if (_frameCount == 0)
{
return false;
}

if (_oldSize != Viewport)
{
// Invalidate cached images now size has changed
Expand All @@ -198,18 +191,14 @@ protected override bool OnDrawingContent (DrawContext? context)
int newSize = Math.Min (Viewport.Width, Viewport.Height);

// generate one
if (_matchSizes is not null && imgFull is not null)
if (_matchSizes is { } && imgFull is { })
{
_matchSizes [_currentFrame] = imgScaled = imgFull.Clone (
x => x.Resize (
newSize * BitmapToBraille.CHAR_HEIGHT,
newSize * BitmapToBraille.CHAR_HEIGHT
)
);
_matchSizes [_currentFrame] =
imgScaled = imgFull.Clone (x => x.Resize (newSize * BitmapToBraille.CHAR_HEIGHT, newSize * BitmapToBraille.CHAR_HEIGHT));
}
}

if (braille == null && _brailleCache is not null)
if (braille == null && _brailleCache is { })
{
_brailleCache [_currentFrame] = braille = GetBraille (_matchSizes? [_currentFrame]!);
}
Expand Down Expand Up @@ -249,11 +238,7 @@ internal void SetImage (Image<Rgba32> image)

private string GetBraille (Image<Rgba32> img)
{
var braille = new BitmapToBraille (
img.Width,
img.Height,
(x, y) => IsLit (img, x, y)
);
var braille = new BitmapToBraille (img.Width, img.Height, (x, y) => IsLit (img, x, y));

return braille.GenerateImage ();
}
Expand Down
7 changes: 5 additions & 2 deletions Examples/UICatalog/Scenarios/FileDialogExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ public override void Main ()
_osIcons.Labels = ["_None", "_Unicode", "Nerd_*"];
win.Add (_osIcons);

win.Add (new Label { Y = Pos.AnchorEnd (2), Text = "* Requires installing Nerd fonts" });
win.Add (new Label { Y = Pos.AnchorEnd (1), Text = " (see: https://github.com/devblackops/Terminal-Icons)" });
Label label = new () { Y = Pos.AnchorEnd (), Text = "* Requires installing Nerd fonts:" };
win.Add (label);
Link link = new () { Y = Pos.Top (label), X = Pos.Right (label) + 1, Url = "https://github.com/devblackops/Terminal-Icons" };
app.ToolTips!.SetToolTip (link, () => link.Url);
win.Add (link);

y = 5;
x = 24;
Expand Down
78 changes: 44 additions & 34 deletions Examples/UICatalog/Scenarios/Links.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,91 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Mouse and Keyboard")]
public class Links : Scenario
{
private IApplication? _app;
private Window? _appWindow;
private Link? _link;

public override void Main ()
{
ConfigurationManager.Enable (ConfigLocations.All);
using IApplication app = Application.Create ();
app.Init ();
_app = app;

_appWindow = new Window { Title = GetName (), BorderStyle = LineStyle.None };
using Window appWindow = new ();
appWindow.Title = GetName ();
appWindow.BorderStyle = LineStyle.None;

Label titleLabel = new () { Text = "_Title:", X = 1, Y = 1 };
_appWindow.Add (titleLabel);
appWindow.Add (titleLabel);

TextField titleTextField = new () { X = Pos.Right (titleLabel) + 1, Y = Pos.Top (titleLabel), Width = Dim.Fill () };
_appWindow.Add (titleTextField);
appWindow.Add (titleTextField);

Label textLabel = new () { Text = " Te_xt:", X = Pos.Left (titleLabel), Y = Pos.Bottom(titleLabel) };
_appWindow.Add (textLabel);
Label textLabel = new () { Text = " Te_xt:", X = Pos.Left (titleLabel), Y = Pos.Bottom (titleLabel) };
appWindow.Add (textLabel);

TextField textTextField = new () { X = Pos.Right (textLabel) + 1, Y = Pos.Top(textLabel), Width = Dim.Fill () };
_appWindow.Add (textTextField);
TextField textTextField = new () { X = Pos.Right (textLabel) + 1, Y = Pos.Top (textLabel), Width = Dim.Fill () };
appWindow.Add (textTextField);

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

TextField urlTextField = new () { X = Pos.Right (urlLabel) + 1, Y = Pos.Bottom (titleTextField) + 1, Width = Dim.Fill () };
_appWindow.Add (urlTextField);
appWindow.Add (urlTextField);

Label simpleUrlLabel = new () { X = 1, Y = Pos.Bottom (urlTextField) + 2 };
_appWindow.Add (simpleUrlLabel);
appWindow.Add (simpleUrlLabel);

FrameView linkFrame = new ()
{
Title = "_Link Demo",
X = 0,
Y = Pos.Bottom (simpleUrlLabel) + 2,
Width = Dim.Fill(),
Width = Dim.Fill (),
Height = Dim.Auto (),
AssignHotKeys = true,
TabStop = TabBehavior.TabStop
TabStop = TabBehavior.TabStop,
Arrangement = ViewArrangement.Resizable
};

_link = new Link { X = 1, Y = 1, BorderStyle = LineStyle.Dotted };
Link linkWithBorder = new () { BorderStyle = LineStyle.Dotted };
app.ToolTips!.SetToolTip (linkWithBorder, () => linkWithBorder.Url);

_link.TextChanged += (s, e) => simpleUrlLabel.Text = $"This is just a Label with a URL in Text (WT automatically enables URLs) - {_link.Text}";
titleTextField.ValueChanged += (s, e) => _link.Title = e.NewValue ?? string.Empty;
textTextField.ValueChanged += (s, e) => _link.Text = e.NewValue ?? string.Empty;
urlTextField.ValueChanged += (s, e) => _link.Url = e.NewValue ?? Link.DEFAULT_URL;
linkFrame.Add (_link);
linkWithBorder.TextChanged +=
(_, _) => simpleUrlLabel.Text = $"This is just a Label with a URL in Text (WT automatically enables URLs) - {linkWithBorder.Text}";
titleTextField.ValueChanged += (_, e) => linkWithBorder.Title = e.NewValue ?? string.Empty;
textTextField.ValueChanged += (_, e) => linkWithBorder.Text = e.NewValue ?? string.Empty;
urlTextField.ValueChanged += (_, e) => linkWithBorder.Url = e.NewValue ?? string.Empty;
linkFrame.Add (linkWithBorder);

titleTextField.Text = "Title";
textTextField.Text = "GitHub repo";
urlTextField.Text = "https://github.com/gui-cs/Terminal.Gui";

Button copyButton = new () { Title = "_Copy", X = Pos.Center (), Y = Pos.AnchorEnd () };
copyButton.Accepting += (s, e) => _link.Copy ();
Button copyButton = new () { Title = "_Copy", X = Pos.Right (linkWithBorder) + 1, Y = Pos.Top (linkWithBorder) + 1 };
copyButton.Accepting += (_, _) => linkWithBorder.Copy ();

linkFrame.Add (copyButton);

_appWindow.Add (linkFrame);
Label label = new () { Y = Pos.Bottom (linkFrame), Title = "_Link to API Docs:" };

Link link = new ()
{
X = Pos.Right (label) + 1, Y = Pos.Top (label), Text = "Terminal.Gui.Views.Link", Url = "https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.Views.Link.html"
};
appWindow.Add (label, link);
app.ToolTips!.SetToolTip (link, () => link.Url);

appWindow.Add (linkFrame);

// StatusBar
Shortcut urlIndicator = new (Key.Empty, "", null);

StatusBar statusBar = new ([new Shortcut (Application.GetDefaultKey (Command.Quit), "Quit", Quit), urlIndicator]);
_link.MouseEnter += (s, e) => urlIndicator.Title = _link.Text;
_link.MouseLeave += (s, e) => urlIndicator.Title = "";
_appWindow.Add (statusBar);
StatusBar statusBar = new ([new Shortcut (Application.GetDefaultKey (Command.Quit), "Quit", () => appWindow.RequestStop ()), urlIndicator]);

_app.Run (_appWindow);
_appWindow.Dispose ();
}
// Demonstrate dynamically showing URL in the status bar when hovering over the link.
// Note that we use a Shortcut here to show how they can be used in a StatusBar, but you could use any View.
linkWithBorder.MouseEnter += (_, _) => urlIndicator.Title = linkWithBorder.Url;
linkWithBorder.MouseLeave += (_, _) => urlIndicator.Title = "";

private void Quit () => _appWindow?.RequestStop ();
appWindow.Add (statusBar);

app.Run (appWindow);
}
}
Loading
Loading