From 5dd1062df53c747339b875c8cb0c13df576ad4b8 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sat, 29 Mar 2025 23:36:09 +0100 Subject: [PATCH] Adding `FL_BEFORE_MENU` event to classes derived from `Fl_Menu_` --- FL/Enumerations.H | 6 +++++- FL/Fl_Choice.H | 4 ++++ FL/Fl_Menu_Bar.H | 4 ++++ FL/Fl_Menu_Button.H | 4 ++++ FL/Fl_Tooltip.H | 2 +- FL/names.h | 7 ++++--- src/Fl_Choice.cxx | 2 ++ src/Fl_Input_Choice.cxx | 5 +++++ src/Fl_Menu_Bar.cxx | 2 ++ src/Fl_Menu_Button.cxx | 1 + src/Fl_Tooltip.cxx | 14 +++++++------- test/color_chooser.cxx | 2 +- test/menubar.cxx | 31 +++++++++++++++++++++++++++++-- 13 files changed, 69 insertions(+), 15 deletions(-) diff --git a/FL/Enumerations.H b/FL/Enumerations.H index db6465096..5bc2951aa 100644 --- a/FL/Enumerations.H +++ b/FL/Enumerations.H @@ -418,7 +418,11 @@ enum Fl_Event { // events /** 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 + FL_BEFORE_TOOLTIP = 28, + /** Triggered just before a menu is displayed. Widgets derived from Fl_Menu_ + receive this event right before the menu appears, providing an opportunity + to update menu item states and activation. */ + FL_BEFORE_MENU = 29 // DEV NOTE: Keep this list in sync with FL/names.h }; diff --git a/FL/Fl_Choice.H b/FL/Fl_Choice.H index 944f4d3a4..2434a9ac3 100644 --- a/FL/Fl_Choice.H +++ b/FL/Fl_Choice.H @@ -100,6 +100,10 @@ } \endcode + FLTK triggers an `FL_BEFORE_MENU` event for this widget right before + displaying the menu. This event provides an opportunity to update menu + item states and activation. See `test/menubar.cxx`, class `Dynamic_Choice` + "Flip" and "Flop" for a usage example. */ class FL_EXPORT Fl_Choice : public Fl_Menu_ { protected: diff --git a/FL/Fl_Menu_Bar.H b/FL/Fl_Menu_Bar.H index 26c08539d..4e7af0d6e 100644 --- a/FL/Fl_Menu_Bar.H +++ b/FL/Fl_Menu_Bar.H @@ -61,6 +61,10 @@ Typing the shortcut() of any of the menu items will cause callbacks exactly the same as when you pick the item with the mouse. + + FLTK triggers an `FL_BEFORE_MENU` event for this widget right before + displaying the menu. This event provides an opportunity to update menu + item states and activation. */ class FL_EXPORT Fl_Menu_Bar : public Fl_Menu_ { friend class Fl_Sys_Menu_Bar_Driver; diff --git a/FL/Fl_Menu_Button.H b/FL/Fl_Menu_Button.H index 0e1036450..3f52593b4 100644 --- a/FL/Fl_Menu_Button.H +++ b/FL/Fl_Menu_Button.H @@ -52,6 +52,10 @@ is done instead, along with any userdata configured for it. The callback can determine which item was picked using value(), mvalue(), item_pathname(), etc. + + FLTK triggers an `FL_BEFORE_MENU` event for this widget right before + displaying the menu. This event provides an opportunity to update menu + item states and activation. */ class FL_EXPORT Fl_Menu_Button : public Fl_Menu_ { protected: diff --git a/FL/Fl_Tooltip.H b/FL/Fl_Tooltip.H index d417ba189..592371651 100644 --- a/FL/Fl_Tooltip.H +++ b/FL/Fl_Tooltip.H @@ -95,7 +95,7 @@ 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. */ + /** \brief Temporarily Override Tooltip Text during an FL_BEFORE_TOOLTIP event. */ static int override_text(const char *new_text); // These should not be public, but Fl_Widget::tooltip() needs them... diff --git a/FL/names.h b/FL/names.h index e47d005ec..90ef231e7 100644 --- a/FL/names.h +++ b/FL/names.h @@ -73,10 +73,11 @@ const char * const fl_eventnames[] = "FL_FULLSCREEN", "FL_ZOOM_GESTURE", "FL_ZOOM_EVENT", - "FL_TOOLTIP_EVENT", - "FL_EVENT_29", // not yet defined, just in case it /will/ be defined ... + "FL_BEFORE_TOOLTIP", + "FL_BEFORE_MENU", "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 ... + "FL_EVENT_31", // not yet defined, just in case it /will/ be defined ... + "FL_EVENT_32" // not yet defined, just in case it /will/ be defined ... }; /** diff --git a/src/Fl_Choice.cxx b/src/Fl_Choice.cxx index e5a0276af..ac9a260cf 100644 --- a/src/Fl_Choice.cxx +++ b/src/Fl_Choice.cxx @@ -195,6 +195,7 @@ int Fl_Choice::handle(int e) { J1: if (Fl::scheme() || fl_contrast(textcolor(), FL_BACKGROUND2_COLOR) != textcolor()) { + handle(FL_BEFORE_MENU); v = menu()->pulldown(x(), y(), w(), h(), mvalue(), this); if (wp.deleted()) return 1; } else { @@ -202,6 +203,7 @@ int Fl_Choice::handle(int e) { // temporarily override the color() of this widget... Fl_Color c = color(); color(FL_BACKGROUND2_COLOR); + handle(FL_BEFORE_MENU); v = menu()->pulldown(x(), y(), w(), h(), mvalue(), this); if (wp.deleted()) return 1; color(c); diff --git a/src/Fl_Input_Choice.cxx b/src/Fl_Input_Choice.cxx index 77a7d937e..4183b3e65 100644 --- a/src/Fl_Input_Choice.cxx +++ b/src/Fl_Input_Choice.cxx @@ -54,6 +54,10 @@ - 1: the user picked a different item in the choice menu - 0: the user typed or pasted directly into the input field + FLTK triggers an `FL_BEFORE_MENU` event for this widget right before + displaying the menu. This event provides an opportunity to update menu + item states and activation. + \par Example Use of Fl_Input_Choice \code #include @@ -152,6 +156,7 @@ void Fl_Input_Choice::InputMenuButton::draw() { // Make pulldown menu appear under entire width of widget const Fl_Menu_Item* Fl_Input_Choice::InputMenuButton::popup() { + handle(FL_BEFORE_MENU); menu_end(); redraw(); Fl_Widget_Tracker mb(this); diff --git a/src/Fl_Menu_Bar.cxx b/src/Fl_Menu_Bar.cxx index b26131b01..3a7530bee 100644 --- a/src/Fl_Menu_Bar.cxx +++ b/src/Fl_Menu_Bar.cxx @@ -49,6 +49,7 @@ int Fl_Menu_Bar::handle(int event) { case FL_PUSH: v = 0; J1: + handle(FL_BEFORE_MENU); v = menu()->pulldown(x(), y(), w(), h(), v, this, 0, 1); picked(v); return 1; @@ -71,6 +72,7 @@ Fl_Menu_Bar::Fl_Menu_Bar(int X, int Y, int W, int H,const char *l) void Fl_Menu_Bar::play_menu(const Fl_Menu_Item *v) { if (v) { + handle(FL_BEFORE_MENU); v = menu()->pulldown(x(), y(), w(), h(), v, this, 0, 1); picked(v); } diff --git a/src/Fl_Menu_Button.cxx b/src/Fl_Menu_Button.cxx index 22a85f52d..5321dbf48 100644 --- a/src/Fl_Menu_Button.cxx +++ b/src/Fl_Menu_Button.cxx @@ -59,6 +59,7 @@ void Fl_Menu_Button::draw() { \see Fl_Menu_::menu_end() */ const Fl_Menu_Item* Fl_Menu_Button::popup() { + handle(FL_BEFORE_MENU); menu_end(); const Fl_Menu_Item* m; pressed_menu_button_ = this; diff --git a/src/Fl_Tooltip.cxx b/src/Fl_Tooltip.cxx index f80956039..572d763d1 100644 --- a/src/Fl_Tooltip.cxx +++ b/src/Fl_Tooltip.cxx @@ -26,7 +26,7 @@ #include -#define DEBUG +// #define DEBUG float Fl_Tooltip::delay_ = 1.0f; float Fl_Tooltip::hidedelay_ = 12.0f; @@ -161,16 +161,16 @@ static void tooltip_hide_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, + When FLTK sends an FL_BEFORE_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. + \return always 1, so this call can finish the FL_BEFORE_TOOLTIP event handling. \see void Fl_Widget::tooltip(const char *text). @@ -198,7 +198,7 @@ void Fl_Tooltip::tooltip_timeout_(void*) { 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)) + if (Fl_Tooltip::current()->handle(FL_BEFORE_TOOLTIP)) tip = Fl_Tooltip::override_text_; } if (!tip || !*tip) { @@ -394,7 +394,7 @@ void Fl_Tooltip::set_enter_exit_once_() { the child’s 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 widget’s + is about to be shown, FLTK sends an `FL_BEFORE_TOOLTIP` event to the widget’s `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. diff --git a/test/color_chooser.cxx b/test/color_chooser.cxx index a9a7a5ca1..f5c9827cc 100644 --- a/test/color_chooser.cxx +++ b/test/color_chooser.cxx @@ -90,7 +90,7 @@ 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) { + if (event == FL_BEFORE_TOOLTIP) { 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]; diff --git a/test/menubar.cxx b/test/menubar.cxx index a5ccfc234..2aeec991c 100644 --- a/test/menubar.cxx +++ b/test/menubar.cxx @@ -210,7 +210,7 @@ void menu_location_cb(Fl_Widget* w, void* data) if (((Fl_Choice*)w)->value() == 1) { // switch to system menu bar menubar->hide(); const Fl_Menu_Item *menu = menubar->menu(); - smenubar = new Fl_Sys_Menu_Bar(0,0,0,30); + smenubar = new Fl_Sys_Menu_Bar(0,0,0,30); smenubar->menu(menu); smenubar->callback(test_cb); } @@ -236,6 +236,30 @@ void about_cb(Fl_Widget*, void*) { fl_message("The menubar test app."); } +class Dynamic_Choice: public Fl_Choice { +public: + Dynamic_Choice(int x, int y, int w, int h, const char *label=nullptr) + : Fl_Choice(x, y, w, h, label) { } + int handle(int event) { + static int flip_flop = 0; + if (event == FL_BEFORE_MENU) { + // The following line is legal because we used `copy()` to create a + // writable copy of the menu array when creating this Choice. + Fl_Menu_Item *mi = const_cast(menu()); + if (flip_flop == 1) { + mi[7].flags |= FL_MENU_INACTIVE; + mi[8].flags &= ~FL_MENU_INACTIVE; + flip_flop = 0; + } else { + mi[7].flags &= ~FL_MENU_INACTIVE; + mi[8].flags |= FL_MENU_INACTIVE; + flip_flop = 1; + } + } + return Fl_Choice::handle(event); + } +}; + int main(int argc, char **argv) { for (int i=0; i<99; i++) { char buf[100]; @@ -256,7 +280,10 @@ int main(int argc, char **argv) { mb1.tooltip("this is a menu button"); mb1.callback(test_cb); menus[1] = &mb1; - Fl_Choice ch(300,100,80,25,"&choice:"); ch.menu(pulldown); + Dynamic_Choice ch(300,100,80,25,"&choice:"); + ch.copy(pulldown); + ch.add("Flip"); + ch.add("Flop"); ch.tooltip("this is a choice menu"); ch.callback(test_cb); menus[2] = &ch;