From b65a9485c7d0bc3c7930a03596d720c8666fa075 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 28 Oct 2026 07:41:15 -0400 Subject: [PATCH] fix(ui): stop status-bar tooltips flashing on and off The strip's built-in item tooltips open below the cursor. With the window maximized the strip is at the bottom of the screen, so Windows pushes the tooltip back over the cursor; the strip then sees a mouse leave, hides it, re-shows it on the next hover, and it flickers. Show the tooltips above the strip instead. Co-Authored-By: Claude Opus 5.5 --- OpenNest/Forms/MainForm.Designer.cs | 3 + OpenNest/Forms/MainForm.cs | 3 +- OpenNest/Forms/StatusStripToolTips.cs | 94 +++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 OpenNest/Forms/StatusStripToolTips.cs diff --git a/OpenNest/Forms/MainForm.Designer.cs b/OpenNest/Forms/MainForm.Designer.cs index 2a0a542c..de9ee010 100644 --- a/OpenNest/Forms/MainForm.Designer.cs +++ b/OpenNest/Forms/MainForm.Designer.cs @@ -21,7 +21,10 @@ namespace OpenNest.Forms components.Dispose(); } if (disposing) + { updaterStatus?.Dispose(); + statusStripToolTips?.Dispose(); + } base.Dispose(disposing); } diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index 3b5ec573..61b27b53 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -112,7 +112,7 @@ namespace OpenNest.Forms Icon = Resources.app_icon; statusStrip1.Items.Insert(statusStrip1.Items.IndexOf(materialStatusLabel) + 1, ledgerStatusLabel); ledgerStatusLabel.Click += LedgerStatusLabel_Click; - statusStrip1.ShowItemToolTips = true; // The hold list lives in the ledger label's tooltip. + statusStripToolTips = new StatusStripToolTips(statusStrip1); // The hold list lives in the ledger label's tooltip. statusBar = new MainStatusBar(statusLabel1, locationStatusLabel, selectionStatusLabel, machineStatusLabel, materialStatusLabel, plateUtilStatusLabel, ledgerStatusLabel); toolStrip1.Items.Add(CreateActionButton("btnCutPlanner", "Plan Cutting", @@ -168,6 +168,7 @@ namespace OpenNest.Forms } private UpdaterStatus updaterStatus; + private StatusStripToolTips statusStripToolTips; private void InitializeHelpMenu() { diff --git a/OpenNest/Forms/StatusStripToolTips.cs b/OpenNest/Forms/StatusStripToolTips.cs new file mode 100644 index 00000000..44e88c27 --- /dev/null +++ b/OpenNest/Forms/StatusStripToolTips.cs @@ -0,0 +1,94 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + /// + /// Shows a status strip's item tooltips above the strip. The strip's own tooltips open + /// below the cursor; at the bottom of the screen Windows pushes them back over the + /// cursor, the strip sees a mouse leave and hides them, and they flash on and off. + /// + internal sealed class StatusStripToolTips : IDisposable + { + private const int Gap = 2; + + private readonly ToolStrip strip; + private readonly ToolTip toolTip = new(); + private readonly Timer hoverTimer = new() { Interval = SystemInformation.MouseHoverTime }; + private ToolStripItem hoverItem; + private string shownText; + + public StatusStripToolTips(ToolStrip strip) + { + this.strip = strip; + strip.ShowItemToolTips = false; + strip.MouseMove += Strip_MouseMove; + strip.MouseLeave += Strip_MouseLeave; + strip.MouseDown += Strip_MouseDown; + hoverTimer.Tick += HoverTimer_Tick; + } + + public void Dispose() + { + hoverTimer.Dispose(); + toolTip.Dispose(); + } + + private void Strip_MouseMove(object sender, MouseEventArgs e) + { + var item = strip.GetItemAt(e.Location); + if (item == hoverItem) + return; + + Hide(); + hoverItem = item; + if (!string.IsNullOrEmpty(item?.ToolTipText)) + hoverTimer.Start(); + } + + private void Strip_MouseLeave(object sender, EventArgs e) + { + Hide(); + hoverItem = null; + } + + // The next move re-arms the hover, so a click that changes the text (a hold refresh) shows the new tip. + private void Strip_MouseDown(object sender, MouseEventArgs e) + { + Hide(); + hoverItem = null; + } + + private void HoverTimer_Tick(object sender, EventArgs e) + { + hoverTimer.Stop(); + Show(); + } + + private void Show() + { + var text = hoverItem?.ToolTipText; + if (string.IsNullOrEmpty(text)) + { + Hide(); + return; + } + + // The tooltip draws its text in the status font plus a few pixels of border and margin. + var height = TextRenderer.MeasureText(text, SystemFonts.StatusFont).Height + 10; + toolTip.Show(text, strip, hoverItem.Bounds.Left, -height - Gap); + shownText = text; + } + + private void Hide() + { + hoverTimer.Stop(); + if (shownText == null) + return; + + toolTip.Hide(strip); + shownText = null; + } + } +}