Skip to content

Fixes #4850 - Show URL of Link in Tooltip (+ StatusBar in UICatalog)#4850

Merged
tig merged 35 commits intogui-cs:v2_developfrom
ccoulioufr:fix/show-link-url-on-enter
Apr 6, 2026
Merged

Fixes #4850 - Show URL of Link in Tooltip (+ StatusBar in UICatalog)#4850
tig merged 35 commits intogui-cs:v2_developfrom
ccoulioufr:fix/show-link-url-on-enter

Conversation

@ccoulioufr
Copy link
Copy Markdown
Collaborator

Fixes

  • Modified Links.cs scenario to display the actual URL (Url) instead of the link text (Text) in the StatusBar when hovering over a link
    Rationale: This change makes the link hover behavior browser-like. When hovering over a hyperlink, users expect to see the destination URL in the status bar (similar to web browsers), not the display text. This provides better UX by allowing users to preview where the link will take them before clicking, matching the familiar behavior from modern browsers.

Testing

  • Run UICatalog > Links scenario
  • Hover mouse over a link
  • Verify StatusBar displays the URL (e.g., https://example.com) instead of the link text

@ccoulioufr ccoulioufr requested a review from tig as a code owner March 24, 2026 20:34
@ccoulioufr ccoulioufr changed the title Small FIX - Show URL instead of link text in StatusBar on mouse hover Fixes #4850 - Show URL instead of link text in StatusBar on mouse hover Mar 24, 2026
@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 24, 2026

Very cool idea.

You get extra credit if instead of changing the text drawn on hover you use a Popover to show a "tool tip". I've long wanted this capabilitiy. You'd modify Link to do this.

If you are not interested, no biggie... we'll get to it eventually.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

Very cool idea.

You get extra credit if instead of changing the text drawn on hover you use a Popover to show a "tool tip". I've long wanted this capabilitiy. You'd modify Link to do this.

If you are not interested, no biggie... we'll get to it eventually.

I like this idea too. Should I keep the same PR / branch?

@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 24, 2026

Very cool idea.

You get extra credit if instead of changing the text drawn on hover you use a Popover to show a "tool tip". I've long wanted this capabilitiy. You'd modify Link to do this.

If you are not interested, no biggie... we'll get to it eventually.

I like this idea too. Should I keep the same PR / branch?

Sure. Just edit titles.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

@tig My terminal already shows native tooltips via OSC 8 hyperlinks.
Question: Are there terminals that don't support this natively? Would this feature be a workaround for them, or is the StatusBar approach sufficient for all cases?

image

@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 24, 2026

As far as I know only WT does that.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

Add Tooltip support + Link integration

  • Introduce a lightweight Tooltip component inspired by Popover<TView, TResult> (no inheritance, no result handling)

  • Automatically shows/hides on MouseEnter / MouseLeave

  • Simplified usage via a Target property (redeclared with new)

  • Link update

    • Add UseTooltip property
    • When enabled, displays the link URL in a tooltip
  • Notes

    • Display-only component
    • Mouse interaction only (no keyboard support yet)

@ccoulioufr ccoulioufr changed the title Fixes #4850 - Show URL instead of link text in StatusBar on mouse hover Fixes #4850 - Show URL of Link in Tooltip (+ StatusBar in UICatalog) Mar 25, 2026
tig and others added 2 commits March 25, 2026 15:17
Refactored the UICatalog About dialog to use a new ShowAboutDialog method with a Dialog, tagline, ASCII art, and a clickable Link to the project URL. Removed GetAboutBoxMessage and made AboutUrl a public constant. Updated Link to trigger Command.Activate on mouse click and moved URL opening logic to OnActivated. OpenUrl now checks for the "DisableRealDriverIO" environment variable. Simplified UseToolTip property and improved tooltip management. Clarified hotkey handling in Link. Removed obsolete About box test from TextFormatterDrawTests.cs.
Replace MessageBox.Query with a custom Dialog containing 3 subviews:
- Tagline label
- GradientArtView: Terminal.Gui logo in box-drawing chars with diagonal color gradient
- Link view with clickable GitHub URL and tooltip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 25, 2026

@ccoulioufr please see my updates.

a) Activating instead of Accepting
b) Ensured tests cant make OpenUrl work.
c) Added Link to the UICatalog about box and used that as an opportunity to provide a new "logo".

Thoughts?

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

@tig I was thinking about a more global handling of tooltips.
Currently you have to instanciate a tooltip for each view that needs it.
But I'm designing a tooltip manager that could be used with any view.
Let me compile informations to a more accurate description.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

Here it is:

🧠 Design & Implementation Rationale

This PR introduces a tooltip system designed to be simple, flexible, and low-overhead, while remaining consistent with existing UI patterns.

🔹 Shared TooltipManager (Singleton)

A single TooltipManager instance is used globally:

  • Allows tooltip registration before views are attached to the UI hierarchy
  • Avoids dependency on Window or other containers at registration time
  • Ensures only one tooltip is visible at a time
  • Simplifies usage and reduces API friction

The actual UI context (App) is resolved at display time using the hovered view.


🔹 Separation of Responsibilities

The implementation separates concerns clearly:

  • TooltipManager → manages hover events, registration, and visibility
  • ToolTip<TView> → handles rendering, positioning, and content hosting

This avoids mixing UI logic with event orchestration and keeps components reusable.


🔹 Dynamic Content via Factory

Tooltip content is defined using:

Func<View>

This allows:

  • fresh content creation on each display
  • no shared view instances (avoids parent conflicts)
  • automatic support for dynamic data

Example:

view.SetTooltipContent(() => new Label { Text = GetDynamicText() });

🔹 External Storage (No View Pollution)

Tooltip data is not stored directly on View.

Instead:

  • the manager maintains an internal registry (View → content factory)
  • avoids adding fields/properties to all views
  • keeps the feature fully opt-in

🔹 Lifecycle-Aware Registration

Tooltip registration is:

  • triggered when UseTooltip is set
  • updated when the view becomes attached to the UI hierarchy

This ensures tooltips work correctly regardless of when they are configured.


🔹 UseTooltip at View Level

The UseTooltip property is defined on to make tooltip support available to all controls.

  • Provides a consistent and discoverable API:
view.UseTooltip = true;
  • Avoids duplicating tooltip logic across controls (e.g., Link, Button, etc.)
  • Keeps the feature opt-in and lightweight

When enabled:

  • The view automatically registers itself with the shared TooltipManager
  • Tooltip content is resolved dynamically depending on the control

This ensures that any control can benefit from tooltips without additional implementation while keeping behavior centralized.


🔹 Why Not Per-Window?

Although initially considered, a per-window manager was not retained because:

  • tooltips can be configured before a view belongs to a window
  • the required context (App, position) is only needed at display time
  • a global manager simplifies the model without losing flexibility

✅ Summary

This design prioritizes:

  • minimal API surface
  • zero overhead for views without tooltips
  • robustness across lifecycle stages
  • clean separation between logic and rendering

@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 26, 2026

I think I like it all except View.UseToolTip.

this is not true:

zero overhead for views without tooltips

This property adds at least 8 bytes to View.

I've been working hard to reduce View footprint. It's a huge class. If we can avoid adding more properties to it we should.

Instead how about defining ITooltip that has an EnableToolTip bool.

Any View subclass that wants to support tooltips can implement this interface.

Will that work?

We'll want to provide a defult implementation (ToolTipImpl) so View authors can just inherit from that. Or, it coudl be done like how IOrientation works with OrientationHelper.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

@tig I will rework it, taking these points into account.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

🔄 Refactor: Tooltip System Simplification

This PR significantly refactors the tooltip system to improve simplicity, flexibility, and maintainability.


🔹 Avoid ToolTipImpl and IToolTipable

  • No inheritance-based tooltip support (ToolTipImpl)
  • No IToolTipable interface needed

Why:

  • Avoid adding weight and responsibilities to all controls
  • Remove the need for a dedicated base class
  • Eliminate coupling between UI elements and tooltip infrastructure

🔹 Introduced Extension-Based API

Tooltip support is now provided via extension methods on View:

view.SetToolTip("Hello");
view.SetToolTip(() => dynamicText);
view.SetToolTip(() => new Label { Text = "Custom" });
view.RemoveToolTip();

Benefits:

  • No modification to View
  • No inheritance required
  • Fully opt-in and lightweight
  • Works with all existing controls

🔹 Unified ToolTipProvider

A single ToolTipProvider class is used to define tooltip content:

  • string
  • Func
  • Func

This replaces multiple competing properties and removes ambiguity.

view.SetToolTip(new ToolTipProvider(() => Url));

🔹 Centralized State in TooltipManager

  • All tooltip state is now stored externally in TooltipManager
  • Uses a dictionary (View → Registration)
  • Manages event subscriptions (MouseEnter, MouseLeave, Disposing)

This avoids storing tooltip-related state inside View.


🔹 Safe Cleanup via Disposing

Tooltip registrations are now automatically cleaned up:

target.Disposing += OnDisposing;
  • Prevents memory leaks
  • Ensures event handlers are properly removed
  • Keeps the manager state consistent

🔹 API Summary

view.SetToolTip("Text");
view.SetToolTip(() => dynamicText);
view.SetToolTip(() => new CustomView());
view.RemoveToolTip();

✅ Result

This refactor delivers:

  • cleaner architecture (no inheritance, no interface)
  • zero overhead for views without tooltips
  • consistent behavior across all controls
  • safer memory management
  • simpler and more expressive API

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Terminal.Gui’s Link UX to be more browser-like by surfacing the destination URL on hover (UICatalog StatusBar) and introducing a new tooltip infrastructure used to show link URLs by default.

Changes:

  • Update Link to open via Command.Activate and set a default tooltip that returns Url.
  • Add new tooltip infrastructure (ToolTipHost, ToolTipProvider, TooltipManager, and view extensions) and wire it into Link.
  • Update UICatalog (Links scenario + About dialog) and adjust/remove related tests.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
Tests/UnitTestsParallelizable/Views/LinkTests.cs Updates expectations for Link.Url default behavior and OSC8 suppression.
Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs Removes UICatalog about-box text rendering test and associated dependency.
Terminal.sln.DotSettings Adds coord to the user dictionary.
Terminal.Gui/Views/Link.cs Switches click behavior to Activate, adds default tooltip, adjusts URL opening guard, and updates acceptance/activation handling.
Terminal.Gui/App/Popovers/ToolTipProvider.cs Adds a provider wrapper for tooltip content/view factories.
Terminal.Gui/App/Popovers/ToolTipManager.cs Introduces a singleton tooltip manager handling hover registration and shared tooltip display.
Terminal.Gui/App/Popovers/ToolTipHost.cs Adds a Popover-based host for tooltip content with positioning logic.
Terminal.Gui/App/Popovers/TooTipExtensions.cs Adds View extension methods for setting/removing tooltips.
Examples/UICatalog/UICatalogRunnable.cs Refactors About dialog to a custom dialog containing a Link and gradient logo view.
Examples/UICatalog/Scenarios/Links.cs Updates StatusBar hover indicator to show _link.Url instead of _link.Text.

Comment thread Terminal.Gui/Views/Link.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipHost.cs Outdated
Comment thread Terminal.Gui/App/Popovers/TooTipExtensions.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipExtensions.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipManager.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipManager.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipManager.cs Outdated
Comment thread Terminal.Gui/App/Popovers/TooTipExtensions.cs Outdated
Comment thread Terminal.Gui/App/Popovers/ToolTipManager.cs Outdated
Comment thread Terminal.Gui/Views/Link.cs
tig and others added 2 commits March 27, 2026 11:48
Refactored ToolTipExtensions to use a local static extension class for cleaner instance method usage. Updated method signatures, improved XML documentation formatting, and streamlined TooltipManager event registration. Applied minor code style fixes and updated the Link class to use the new tooltip extension pattern.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 27, 2026

@ccoulioufr Please see the code review comments and fix the issues.

Also, and this is probably due to #4850 (comment), when I run the LInks Scenrio in UICatalgo and hover over the example link, then quit the scenario there are errors about non-disposed views. This must be fixed.

Thanks.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

@tig Here is the first batch of fixes.
I'm now working on coverage.
I also noticed that your implementation of OpenUrl is called twice when activated with mouse, once only when activated with keyboard.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

@tig I don't manage to reproduce the failed test on my computer. Any advice?

@tig
Copy link
Copy Markdown
Collaborator

tig commented Apr 1, 2026

@tig I don't manage to reproduce the failed test on my computer. Any advice?

If you have resharper, run the tests until failure:

image

the problem is this:

    public static ToolTipManager Instance { get; } = new ();

This breaks multiple apps running in parallel in the same process.

@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

ApplicationToolTip: Thread-Safe Tooltip Management

Summary

Refactored tooltip management to support parallel test execution by eliminating the singleton pattern and integrating tooltip management at the IApplication level.

Problem

The previous ToolTipManager.Instance singleton broke parallel test execution because multiple apps running in the same process shared the same manager instance, causing state conflicts.

Solution

Renamed ToolTipManager to ApplicationToolTip and integrated it into the application lifecycle, following the same pattern as ApplicationPopover and ApplicationNavigation.

Changes

Architecture

  • Renamed ToolTipManagerApplicationToolTip:

    • Removed static singleton Instance property
    • Made constructor public
    • Added IApplication? App property for app association
  • Integrated into IApplication:

    • Added ApplicationToolTip? ToolTips { get; set; } property to IApplication
    • Initialized in ApplicationImpl.Init()
    • Disposed in ApplicationImpl.Dispose()

Benefits

Thread-safe: Each IApplication instance has its own ApplicationToolTip
Parallel tests: No shared state between test runs
Consistent: Follows same pattern as Popovers and Navigation
Clean lifecycle: Automatic cleanup via IDisposable

Testing

  • All existing tooltip functionality preserved
  • Compatible with parallel test execution
  • No static state shared between app instances

Related

  • Follows architecture established by ApplicationPopover and ApplicationNavigation
  • Maintains external registry pattern to keep View lightweight

tig added 4 commits April 6, 2026 09:23
- Code Cleanup of UI Catalog to fix nullability risks
- Replace Label+URL with Link controls in AnimationScenario and FileDialogExamples; add tooltips for links.
- Refactor Links scenario to use local variables, add API docs link, and show URL in status bar on hover.
- Rename AboutUrl to ABOUT_URL and add tooltip in UICatalogRunnable.
- Apply code style improvements: pattern matching, event handler syntax, and field cleanup.
Update Link text to show 'Terminal.Gui.Views.Link'

Changed Link control text from "Docs" to "Terminal.Gui.Views.Link" for clearer destination indication; URL remains unchanged.
@tig
Copy link
Copy Markdown
Collaborator

tig commented Apr 6, 2026

@ccoulioufr I did some code cleanup and updated other places where urls were used to use Link.

I think this is ready to merge, although there is a bug in the output render with OSC8 where it's not getting cleared properly:

WindowsTerminal_hXqE02cuqm

I recommend we merge what we have here and then fix that bug in a separate issue.

Moved and updated IDesignable.EnableForDesign to set Title, Url, and attach a tooltip on Initialized. The tooltip informs users that the Link opens the URL in the default browser.
@ccoulioufr
Copy link
Copy Markdown
Collaborator Author

Great news!
This bug is still present, should I create an issue for it?

I also noticed that your implementation of OpenUrl is called twice when activated with mouse, once only when activated with keyboard.

Remove default left-click activation from Link

Removed MouseBindings.Add for LeftButtonClicked in Link,
so clicking a Link no longer triggers Activate by default.
@tig
Copy link
Copy Markdown
Collaborator

tig commented Apr 6, 2026

OpenUrl

Fixed...

@tig tig marked this pull request as ready for review April 6, 2026 18:01
@tig tig merged commit 9d40ced into gui-cs:v2_develop Apr 6, 2026
11 checks passed
@ccoulioufr ccoulioufr deleted the fix/show-link-url-on-enter branch April 20, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants