Adds a new event FL_TOOLTIP_EVENT...

... and Fl_Tootip::override_text() to allow users to dynamically
generate tooltips.
This commit is contained in:
Matthias Melcher 2025-03-29 22:40:13 +01:00
parent cb86a37676
commit b7189192e2
5 changed files with 98 additions and 26 deletions

View File

@ -414,7 +414,11 @@ enum Fl_Event { // events
/** A zoom event (ctrl/+/-/0/ or cmd/+/-/0/) was processed.
Use Fl::add_handler() to be notified of this event.
*/
FL_ZOOM_EVENT = 27
FL_ZOOM_EVENT = 27,
/** A tooltip is about to pop up for this widget. The mouse coordinates are
available in Fl::event_x() and Fl::event_y(). Change the widget tooltip
as needed. */
FL_TOOLTIP_EVENT = 28
// DEV NOTE: Keep this list in sync with FL/names.h
};

View File

@ -95,6 +95,8 @@ public:
static void wrap_width(int v) { wrap_width_ = v; }
/** Returns the window that is used for tooltips */
static Fl_Window* current_window(void);
/** \brief Temporarily Override Tooltip Text during an FL_TOOLTIP_EVENT. */
static int override_text(const char *new_text);
// These should not be public, but Fl_Widget::tooltip() needs them...
// fabien: made it private with only a friend function access
@ -104,20 +106,22 @@ private:
static void enter_(Fl_Widget* w);
static void exit_(Fl_Widget *w);
static void set_enter_exit_once_();
static void tooltip_timeout_(void*);
private:
static float delay_; //!< delay before a tooltip is shown
static float hidedelay_; //!< delay until tooltip is closed again
static float hoverdelay_; //!< delay between tooltips
static float delay_; //!< delay before a tooltip is shown
static float hidedelay_; //!< delay until tooltip is closed again
static float hoverdelay_; //!< delay between tooltips
static Fl_Color color_;
static Fl_Color textcolor_;
static Fl_Font font_;
static Fl_Fontsize size_;
static Fl_Widget* widget_; //!< Keeps track of the current target widget
static Fl_Widget* widget_; //!< Keeps track of the current target widget
static int margin_width_; //!< distance around tooltip text left+right
static int margin_height_; //!< distance around tooltip text top+bottom
static int wrap_width_; //!< maximum width of tooltip text before it word wraps
static const int draw_symbols_; // 1 = draw @-symbols in tooltips, 0 = no
static char *override_text_; //!< a copy of the last text for an overridden tooltip
};
#endif

View File

@ -73,9 +73,10 @@ const char * const fl_eventnames[] =
"FL_FULLSCREEN",
"FL_ZOOM_GESTURE",
"FL_ZOOM_EVENT",
"FL_EVENT_28", // not yet defined, just in case it /will/ be defined ...
"FL_TOOLTIP_EVENT",
"FL_EVENT_29", // not yet defined, just in case it /will/ be defined ...
"FL_EVENT_30" // not yet defined, just in case it /will/ be defined ...
"FL_EVENT_30", // not yet defined, just in case it /will/ be defined ...
"FL_EVENT_31" // not yet defined, just in case it /will/ be defined ...
};
/**

View File

@ -25,6 +25,9 @@
#include <stdio.h>
#define DEBUG
float Fl_Tooltip::delay_ = 1.0f;
float Fl_Tooltip::hidedelay_ = 12.0f;
float Fl_Tooltip::hoverdelay_ = 0.2f;
@ -38,6 +41,7 @@ int Fl_Tooltip::margin_width_ = 3;
int Fl_Tooltip::margin_height_ = 3;
int Fl_Tooltip::wrap_width_ = 400;
const int Fl_Tooltip::draw_symbols_ = 1;
char *Fl_Tooltip::override_text_ = nullptr;
static const char* tip;
@ -154,14 +158,49 @@ static void tooltip_hide_timeout(void*) {
recent_tooltip = 0;
}
static void tooltip_timeout(void*) {
/**
Use this method to temporarily change the tooltip text before it is displayed.
When FLTK sends an FL_TOOLTIP_EVENT to the widget under the mouse pointer,
you can handle this event to modify the tooltip text dynamically. The
provided text will be copied into a local buffer. To apply the override,
the event handler must return 1.
To disable the tooltip for the current event, set the override text to nullptr
or an empty string ("") and return 1.
\param[in] new_text a C string that will be copied into a buffer
\return always 1, so this call can finish the FL_TOOLTIP_EVENT handling.
\see void Fl_Widget::tooltip(const char *text).
\see `test/color_chooser.cxx` for a usage example.
*/
int Fl_Tooltip::override_text(const char *new_text) {
if (new_text != override_text_) {
if (window && window->label()==override_text_)
((Fl_Widget *) window)->label(nullptr);
if (override_text_)
::free(override_text_);
override_text_ = nullptr;
if (new_text)
override_text_ = ::strdup(new_text);
}
return 1;
}
void Fl_Tooltip::tooltip_timeout_(void*) {
#ifdef DEBUG
puts("tooltip_timeout();");
puts("tooltip_timeout_();");
#endif // DEBUG
if (recursion) return;
recursion = 1;
if (!top_win_iconified_()) { // no tooltip if top win iconified (STR #3157)
if (Fl_Tooltip::current()) {
if (Fl_Tooltip::current()->handle(FL_TOOLTIP_EVENT))
tip = Fl_Tooltip::override_text_;
}
if (!tip || !*tip) {
if (window) window->hide();
Fl::remove_timeout(tooltip_hide_timeout);
@ -253,7 +292,7 @@ void Fl_Tooltip::exit_(Fl_Widget *w) {
if (!widget_ || (w && w == window)) return;
widget_ = 0;
Fl::remove_timeout(tooltip_timeout);
Fl::remove_timeout(tooltip_timeout_);
Fl::remove_timeout(recent_timeout);
if (window && window->visible()) {
window->hide();
@ -298,7 +337,7 @@ void Fl_Tooltip::enter_area(Fl_Widget* wid, int x,int y,int w,int h, const char*
}
// do nothing if it is the same:
if (wid==widget_ /*&& x==X && y==currentTooltipY && w==W && h==currentTooltipH*/ && t==tip) return;
Fl::remove_timeout(tooltip_timeout);
Fl::remove_timeout(tooltip_timeout_);
Fl::remove_timeout(recent_timeout);
// remember it:
widget_ = wid; currentTooltipY = y; currentTooltipH = h; tip = t;
@ -308,7 +347,7 @@ void Fl_Tooltip::enter_area(Fl_Widget* wid, int x,int y,int w,int h, const char*
window->hide();
Fl::remove_timeout(tooltip_hide_timeout);
}
Fl::add_timeout(Fl_Tooltip::hoverdelay(), tooltip_timeout);
Fl::add_timeout(Fl_Tooltip::hoverdelay(), tooltip_timeout_);
} else if (Fl_Tooltip::delay() < .1) {
// possible fix for the Windows titlebar, it seems to want the
// window to be destroyed, moving it messes up the parenting:
@ -316,13 +355,13 @@ void Fl_Tooltip::enter_area(Fl_Widget* wid, int x,int y,int w,int h, const char*
window->hide();
Fl::remove_timeout(tooltip_hide_timeout);
}
tooltip_timeout(0);
tooltip_timeout_(nullptr);
} else {
if (window && window->visible()) {
window->hide();
Fl::remove_timeout(tooltip_hide_timeout);
}
Fl::add_timeout(Fl_Tooltip::delay(), tooltip_timeout);
Fl::add_timeout(Fl_Tooltip::delay(), tooltip_timeout_);
}
#ifdef DEBUG
@ -341,19 +380,25 @@ void Fl_Tooltip::set_enter_exit_once_() {
}
/**
Sets the current tooltip text.
Sets the Tooltip Text for a Widget.
Sets a string of text to display in a popup tooltip window when the user
hovers the mouse over the widget. The string is <I>not</I> copied, so
make sure any formatted string is stored in a static, global,
or allocated buffer. If you want a copy made and managed for you,
use the copy_tooltip() method, which will manage the tooltip string
automatically.
Assigns a tooltip string that appears in a popup when the user hovers over
the widget. The provided string is not copied. The caller must eensure it
remains valid by storing it in a static, global, or dynamically allocated
buffer. If you need the tooltip string to be copied and managed automatically,
use copy_tooltip().
By default, a widget inherits the tooltip of its parent if none is explicitly
set. If you assign a tooltip to a group but not to its child widgets, the
child widgets will display the groups tooltip. To prevent inheritance, set
the childs tooltip to an empty string ("").
Tooltips can be updated dynamically before they are displayed. When a tooltip
is about to be shown, FLTK sends an `FL_TOOLTIP_EVENT` to the widgets
`handle()` method. Developers can override the tooltip text temporarily
using `Fl_Tooltip::override_text(const char* new_text)` and returning 1 from
`handle()` to apply the change.
If no tooltip is set, the tooltip of the parent is inherited. Setting a
tooltip for a group and setting no tooltip for a child will show the
group's tooltip instead. To avoid this behavior, you can set the child's
tooltip to an empty string ("").
\param[in] text New tooltip text (no copy is made)
\see copy_tooltip(const char*), tooltip()
*/

View File

@ -21,6 +21,7 @@
#include <FL/fl_show_colormap.H>
#include <FL/Fl_Color_Chooser.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Tooltip.H>
#include <FL/platform.H>
#include <FL/fl_draw.H>
@ -84,6 +85,22 @@ void cb2(Fl_Widget *, void *v) {
bx->parent()->redraw();
}
class Image_Box: public Fl_Box {
public:
Image_Box(int x, int y, int w, int h, const char *label = nullptr)
: Fl_Box(x, y, w, h, label) { }
int handle(int event) {
if (event == FL_TOOLTIP_EVENT) {
const char *color_name_lut[] = { "blue", "green", "black", "red" };
int quadrant = (Fl::event_x() < x()+w()/2) + 2*(Fl::event_y() < y()+h()/2);
char buf[80];
::snprintf(buf, 79, "Color %s at x=%d, y=%d", color_name_lut[quadrant], Fl::event_x(), Fl::event_y());
return Fl_Tooltip::override_text(buf);
}
return Fl_Box::handle(event);
}
};
int main(int argc, char ** argv) {
Fl::set_color(fullcolor_cell,145,159,170);
Fl_Window window(400,400);
@ -98,7 +115,8 @@ int main(int argc, char ** argv) {
b1.callback(cb1,&box);
Fl_Button b2(120,120,180,30,"fl_color_chooser()");
b2.callback(cb2,&box);
Fl_Box image_box(160,190,width,height,0);
Image_Box image_box(160,190,width,height,0);
image_box.tooltip("Image Box");
make_image();
(new Fl_RGB_Image(image, width, height))->label(&image_box);
Fl_Box b(160,310,120,30,"Example of fl_draw_image()");