OpenCPN Partial API docs
Loading...
Searching...
No Matches
tooltip.cpp
1/***************************************************************************
2 * Copyright (C) 2025 by OpenCPN developer team *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 **************************************************************************/
19
20#include "tooltip.h"
21#include "chcanv.h"
22#include "color_handler.h"
23#include "font_mgr.h"
24#include "ocpn_platform.h"
25#include "ocpn_frame.h"
26#include "navutil.h"
27
28extern OCPNPlatform *g_Platform;
29extern MyFrame *gFrame;
30extern bool g_btouch;
31
32// Define timer event ID
33#define TOOLTIP_TIMER_ID 10002
34
35//----------------------------------------------------------------------------
36// Tooltip Implementation
37//----------------------------------------------------------------------------
38
39BEGIN_EVENT_TABLE(Tooltip, wxFrame)
40EVT_PAINT(Tooltip::OnPaint)
41EVT_TIMER(TOOLTIP_TIMER_ID, Tooltip::OnTimer)
42END_EVENT_TABLE()
43
44Tooltip::Tooltip(wxWindow *parent, TooltipCallback on_destroy)
45 : wxFrame(parent, wxID_ANY, "", wxPoint(0, 0), wxSize(1, 1),
46 wxNO_BORDER | wxFRAME_FLOAT_ON_PARENT | wxFRAME_NO_TASKBAR),
47 m_showTimer(this, TOOLTIP_TIMER_ID),
48 m_on_destroy(std::move(on_destroy)) {
49 m_pbm = nullptr;
50 m_hiviz = false;
51 m_showPending = false;
52
53 // Initialize colors using SetColorScheme to avoid duplicate code
54 SetColorScheme(GLOBAL_COLOR_SCHEME_RGB);
55
56 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
57 SetBackgroundColour(m_back_color);
58 Hide();
59}
60
61Tooltip::~Tooltip() {
62 m_on_destroy(this);
63 m_showTimer.Stop();
64 delete m_pbm;
65}
66
67bool Tooltip::Destroy() {
68 m_on_destroy(this);
69 return wxFrame::Destroy();
70}
71
72void Tooltip::SetString(const wxString &text) { m_string = text; }
73
74void Tooltip::SetAbsolutePosition(const wxPoint &pt) {
75 m_position = pt;
76 m_requestedPosition = pt;
77}
78
79void Tooltip::SetHiviz(bool hiviz) { m_hiviz = hiviz; }
80
81void Tooltip::SetColorScheme(ColorScheme cs) {
82 m_cs = cs;
83 m_back_color =
84 GetGlobalColor("DILG0"); // Background gets progressively darker
85 m_text_color = GetGlobalColor("DILG4"); // Text gets progressively lighter
86}
87
88void Tooltip::SetRelativePosition(const wxRect &windowRect, int offsetX,
89 int offsetY) {
90 // Calculate initial position to the right of the window
91 wxPoint pos;
92 pos.x = windowRect.x + windowRect.width + offsetX;
93 pos.y = windowRect.y + offsetY;
94
95 // Get tooltip size for boundary checking
96 wxSize tooltipSize = GetRenderedSize();
97 wxSize screenSize = wxGetDisplaySize();
98
99 // Convert to screen coordinates if needed
100 wxPoint screenPos = pos;
101 if (GetParent() && GetParent() != wxTheApp->GetTopWindow()) {
102 screenPos = GetParent()->ClientToScreen(pos);
103 }
104
105 // Adjust horizontal position if tooltip would go off screen
106 if (screenPos.x + tooltipSize.x > screenSize.x) {
107 // Try positioning to the left of the window instead
108 pos.x = windowRect.x - tooltipSize.x - offsetX;
109 if (GetParent() && GetParent() != wxTheApp->GetTopWindow()) {
110 screenPos = GetParent()->ClientToScreen(pos);
111 } else {
112 screenPos = pos;
113 }
114
115 // If still off screen, clamp to screen edge
116 if (screenPos.x < 0) {
117 screenPos.x = offsetX;
118 }
119 }
120
121 // Adjust vertical position if tooltip would go off screen
122 if (screenPos.y + tooltipSize.y > screenSize.y) {
123 // Try positioning above the window instead
124 pos.y = windowRect.y - tooltipSize.y - offsetY;
125 if (GetParent() && GetParent() != wxTheApp->GetTopWindow()) {
126 screenPos = GetParent()->ClientToScreen(pos);
127 } else {
128 screenPos = pos;
129 }
130
131 // If still off screen, clamp to screen edge
132 if (screenPos.y < 0) {
133 screenPos.y = offsetY;
134 }
135 }
136
137 // SetAbsolutePosition expects screen coordinates
138 SetAbsolutePosition(screenPos);
139}
140
142 if (m_string.IsEmpty()) {
143 return wxSize(0, 0);
144 }
145
146 wxScreenDC cdc;
147 double scaler = g_Platform->GetDisplayDIPMult(const_cast<Tooltip *>(this));
148
149 wxFont *plabelFont = FontMgr::Get().GetFont(_("ToolTips"));
150 wxFont sFont = plabelFont->Scaled(1.0 / scaler);
151
152 int w, h;
153 cdc.GetMultiLineTextExtent(m_string, &w, &h, nullptr, &sFont);
154 int sizeX = w + GetCharWidth() * 2;
155 int sizeY = h + GetCharHeight() / 2;
156
157 sizeX *= scaler;
158 sizeY *= scaler;
159
160 return wxSize(sizeX, sizeY);
161}
162
163void Tooltip::CreateBitmap() {
164 if (m_string.IsEmpty()) return;
165
166 wxScreenDC cdc;
167 double scaler = g_Platform->GetDisplayDIPMult(this);
168
169 wxFont *plabelFont = FontMgr::Get().GetFont(_("ToolTips"));
170 wxFont sFont = plabelFont->Scaled(1.0 / scaler);
171
172 int w, h;
173 cdc.GetMultiLineTextExtent(m_string, &w, &h, nullptr, &sFont);
174
175 m_size.x = w + GetCharWidth() * 2;
176 m_size.y = h + GetCharHeight() / 2;
177
178 m_size.x *= scaler;
179 m_size.y *= scaler;
180
181 wxMemoryDC mdc;
182
183 delete m_pbm;
184 m_pbm = new wxBitmap(m_size.x, m_size.y, -1);
185 mdc.SelectObject(*m_pbm);
186
187 wxPen pborder(m_text_color);
188 wxBrush bback(m_back_color);
189 mdc.SetPen(pborder);
190 mdc.SetBrush(bback);
191
192 // High visibility mode for night/dusk color schemes
193 if (m_hiviz) {
194 if ((m_cs == GLOBAL_COLOR_SCHEME_DUSK) ||
195 (m_cs == GLOBAL_COLOR_SCHEME_NIGHT)) {
196 wxBrush hv_back(wxColour(200, 200, 200));
197 mdc.SetBrush(hv_back);
198 }
199 }
200
201 mdc.DrawRectangle(0, 0, m_size.x, m_size.y);
202
203 // Draw the text
204 mdc.SetFont(sFont);
205 mdc.SetTextForeground(m_text_color);
206 mdc.SetTextBackground(m_back_color);
207
208 int offx = GetCharWidth();
209 int offy = GetCharHeight() / 4;
210 offx *= scaler;
211 offy *= scaler;
212 mdc.DrawText(m_string, offx, offy);
213
214 SetClientSize(m_size.x, m_size.y);
215}
216
218 CreateBitmap();
219 CalculateOptimalPosition();
220 SetSize(m_position.x, m_position.y, m_size.x, m_size.y);
221}
222
223void Tooltip::CalculateOptimalPosition() {
224 if (!GetParent()) return;
225
226 wxPoint screenPos = m_requestedPosition;
227
228 // Adjust position to keep tooltip on screen
229 wxSize tooltipSize = GetRenderedSize();
230 wxSize screenSize = wxGetDisplaySize();
231
232 if (screenPos.x + tooltipSize.x > screenSize.x) {
233 screenPos.x = screenSize.x - tooltipSize.x - 10;
234 }
235 if (screenPos.y + tooltipSize.y > screenSize.y) {
236 screenPos.y = screenSize.y - tooltipSize.y - 10;
237 }
238
239 if (screenPos.x < 0) screenPos.x = 10;
240 if (screenPos.y < 0) screenPos.y = 10;
241
242 m_position = screenPos;
243}
244
245void Tooltip::ShowTooltip(int delay_ms) {
246 if (m_string.IsEmpty()) return;
247
248 if (delay_ms > 0) {
249 m_showPending = true;
250 m_showTimer.Start(delay_ms, wxTIMER_ONE_SHOT);
251 } else {
252 SetBitmap();
253 Show();
254#ifndef __WXOSX__
255 if (gFrame) gFrame->Raise();
256#endif
257 }
258}
259
261 m_showTimer.Stop();
262 m_showPending = false;
263 Hide();
264}
265
266void Tooltip::OnPaint(wxPaintEvent &event) {
267 int width, height;
268 GetClientSize(&width, &height);
269 wxPaintDC dc(this);
270
271 if (m_string.Len() && m_pbm) {
272 wxMemoryDC mdc;
273 mdc.SelectObject(*m_pbm);
274 dc.Blit(0, 0, width, height, &mdc, 0, 0);
275 }
276}
277
278void Tooltip::OnTimer(wxTimerEvent &event) {
279 if (event.GetId() == TOOLTIP_TIMER_ID && m_showPending) {
280 m_showPending = false;
281 // Ensure we're still in a valid state before proceeding
282 if (!IsBeingDeleted()) {
283 SetBitmap();
284 Show();
285#ifndef __WXOSX__
286 if (gFrame) gFrame->Raise();
287#endif
288 }
289 }
290}
291
292//----------------------------------------------------------------------------
293// TooltipManager Implementation
294//----------------------------------------------------------------------------
295
296TooltipManager *TooltipManager::s_instance = nullptr;
297
298TooltipManager::TooltipManager()
299 : m_currentTooltip(nullptr),
300 m_currentParent(nullptr),
301 m_colorScheme(GLOBAL_COLOR_SCHEME_RGB),
302 m_enabled(true),
303 m_showDelay(500),
304 m_hideDelay(5000) {}
305
306TooltipManager::~TooltipManager() { CleanupTooltip(); }
307
309 if (!s_instance) {
310 s_instance = new TooltipManager();
311 }
312 return *s_instance;
313}
314
316 const wxString &text,
317 const wxPoint &position,
318 bool hiviz) {
319 if (!m_enabled || text.IsEmpty()) return;
320
321 // Hide any existing tooltip
322 HideTooltip();
323
324 // Create or reuse tooltip
325 m_currentTooltip = GetOrCreateTooltip(parent);
326 m_currentParent = parent;
327
328 // Configure tooltip
329 m_currentTooltip->SetString(text);
330 m_currentTooltip->SetAbsolutePosition(position);
331 m_currentTooltip->SetHiviz(hiviz);
332 m_currentTooltip->SetColorScheme(m_colorScheme);
333
334 // Show with delay
335 m_currentTooltip->ShowTooltip(m_showDelay);
336}
337
339 const wxString &text, bool hiviz) {
340 if (!window) return;
341
342 // Hide any existing tooltip
343 HideTooltip();
344
345 // Create or reuse tooltip
346 m_currentTooltip = GetOrCreateTooltip(window->GetParent());
347 m_currentParent = window->GetParent();
348
349 // Configure tooltip
350 m_currentTooltip->SetString(text);
351 m_currentTooltip->SetHiviz(hiviz);
352 m_currentTooltip->SetColorScheme(m_colorScheme);
353
354 // Use enhanced positioning with automatic boundary detection
355 wxRect windowRect = window->GetRect();
356 m_currentTooltip->SetRelativePosition(windowRect, 2, 0);
357
358 // Show with delay
359 m_currentTooltip->ShowTooltip(m_showDelay);
360}
361
363 if (m_currentTooltip) {
364 m_currentTooltip->HideTooltip();
365 }
366}
367
369 HideTooltip();
370 CleanupTooltip();
371}
372
373void TooltipManager::SetColorScheme(ColorScheme cs) {
374 m_colorScheme = cs;
375 if (m_currentTooltip) {
376 m_currentTooltip->SetColorScheme(cs);
377 }
378}
379
381 m_enabled = enable;
382 if (!enable) {
384 }
385}
386
388 return m_currentTooltip && m_currentTooltip->IsShown();
389}
390
391Tooltip *TooltipManager::GetOrCreateTooltip(wxWindow *parent) {
392 if (m_currentTooltip && m_currentParent == parent) {
393 return m_currentTooltip;
394 }
395
396 CleanupTooltip();
397
398 m_currentTooltip = new Tooltip(parent, [&](const Tooltip *t) {
399 if (t == m_currentTooltip) m_currentTooltip = nullptr;
400 });
401 m_currentParent = parent;
402
403 return m_currentTooltip;
404}
405void TooltipManager::CleanupTooltip() {
406 if (m_currentTooltip) {
407 m_currentTooltip->HideTooltip();
408 m_currentTooltip->Destroy();
409 m_currentTooltip = nullptr;
410 m_currentParent = nullptr;
411 }
412}
Generic Chart canvas base.
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Get a font object for a UI element.
Definition font_mgr.cpp:193
Main application frame.
Definition ocpn_frame.h:139
Provides platform-specific support utilities for OpenCPN.
Coordinates tooltip display across OpenCPN components.
Definition tooltip.h:140
void SetColorScheme(ColorScheme cs)
Set color scheme for all tooltips.
Definition tooltip.cpp:373
void ShowTooltipForWindow(wxWindow *window, const wxString &text, bool hiviz=false)
Show tooltip for a window using automatic positioning relative to the window.
Definition tooltip.cpp:338
bool IsShown() const
Check if a tooltip is currently shown.
Definition tooltip.cpp:387
void HideAllTooltips()
Hide all tooltips.
Definition tooltip.cpp:368
static TooltipManager & Get()
Get the singleton instance.
Definition tooltip.cpp:308
void HideTooltip()
Hide the current tooltip.
Definition tooltip.cpp:362
void ShowTooltipAtPosition(wxWindow *parent, const wxString &text, const wxPoint &position, bool hiviz=false)
Show tooltip at specified position in absolute screen coordinates (physical pixels).
Definition tooltip.cpp:315
void EnableTooltips(bool enable)
Enable or disable tooltip system.
Definition tooltip.cpp:380
Tooltip with color scheme support and high-visibility mode.
Definition tooltip.h:39
void HideTooltip()
Hide the tooltip immediately.
Definition tooltip.cpp:260
void SetRelativePosition(const wxRect &windowRect, int offsetX=2, int offsetY=0)
Position tooltip relative to a window rectangle with automatic screen boundary detection.
Definition tooltip.cpp:88
void SetAbsolutePosition(const wxPoint &pt)
Set the tooltip position in absolute screen coordinates (physical pixels).
Definition tooltip.cpp:74
void SetHiviz(bool hiviz)
Enable/disable high visibility mode.
Definition tooltip.cpp:79
void SetColorScheme(ColorScheme cs)
Set the color scheme for tooltip appearance.
Definition tooltip.cpp:81
void SetBitmap()
Create the tooltip bitmap.
Definition tooltip.cpp:217
void SetString(const wxString &text)
Set the tooltip text to display.
Definition tooltip.cpp:72
wxSize GetRenderedSize() const
Get the rendered size of the tooltip.
Definition tooltip.cpp:141
void ShowTooltip(int delay_ms=0)
Show the tooltip with optional delay in milliseconds.
Definition tooltip.cpp:245
Global color handling by name.
Font list manager.
Utility functions.
OpenCPN top window.
OpenCPN Platform specific support utilities.