FLTK logo

Re: [HIGH] STR #3523: Fl_Menu_Buttons cross over text

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.bugs  ]
 
Previous Message ]New Message | Reply ]Next Message ]

Re: [HIGH] STR #3523: Fl_Menu_Buttons cross over text Albrecht Schlosser May 15, 2019  
 
DO NOT REPLY TO THIS MESSAGE.  INSTEAD, POST ANY RESPONSES TO THE LINK BELOW.

[STR Active]

Link: https://www.fltk.org/str.php?L3523
Version: 1.4.0


@Gonzalo: Please test 'patch_v1_debug.diff' to see if this fixes the issue
for you (it does for me with my test files). Apply with `patch -p0'.

This patch adds one new method to the API, but the test program works w/o
modifications since the new method Fl_Menu_::menu_end() is called
internally in Fl_Menu_Button::popup(). See also added documentation (work
in progress).

Note that I only fixed Fl_Menu_Button::popup() to call menu_end()
internally. If this fix is good we can also try to fix Fl_Menu_Bar and
Fl_Choice which may experience the same bug (not tested though).

Note also that Fl_Menu_add.cxx contains test/debug statements that can be
enabled (see DEBUG_MENU_ADD).

@devs: review and comments appreciated!


Link: https://www.fltk.org/str.php?L3523
Version: 1.4.0
diff --git FL/Fl_Menu_.H FL/Fl_Menu_.H
index f74670175..cbf7ceaa3 100644
--- FL/Fl_Menu_.H
+++ FL/Fl_Menu_.H
@@ -95,8 +95,25 @@ public:
 
   /**
     Returns a pointer to the array of Fl_Menu_Items.  This will either be
-    the value passed to menu(value) or the private copy.
-    \sa size() -- returns the size of the Fl_Menu_Item array.
+    the value passed to menu(value) or the private copy or an internal
+    (temporary) location (see note below).
+
+    \note <b>Implementation details - may be changed in the future.</b>
+      All modifications of the menu array are done by copying the entire
+      menu array to an internal storage for optimization of memory
+      allocations, for instance when using add() or insert(). While this
+      is done, menu() returns the pointer to this internal location. The
+      entire menu will be copied back to private storage when needed,
+      i.e. when \b another Fl_Menu_ is modified. You can force this
+      reallocation after you're done with all menu modifications by calling
+      Fl_Menu_::menu_end() to make sure menu() returns a permanent pointer
+      to private storage (until the menu is modified again).
+      Note also that some menu methods (e.g. Fl_Menu_Button::popup()) call
+      menu_end() internally to ensure a consistent menu array while the
+      menu is open.
+
+    \see size() -- returns the size of the Fl_Menu_Item array.
+    \see menu_end() -- finish %menu modifications (optional)
 
     \b Example: How to walk the array:
     \code
@@ -112,6 +129,7 @@ public:
 
   */
   const Fl_Menu_Item *menu() const {return menu_;}
+  const Fl_Menu_Item *menu_end(); // in src/Fl_Menu_add.cxx
   void menu(const Fl_Menu_Item *m);
   void copy(const Fl_Menu_Item *m, void* user_data = 0);
   int insert(int index, const char*, int shortcut, Fl_Callback*, void* = 0, int = 0);
diff --git src/Fl_Menu_Button.cxx src/Fl_Menu_Button.cxx
index 74bf62430..f887701c1 100644
--- src/Fl_Menu_Button.cxx
+++ src/Fl_Menu_Button.cxx
@@ -46,8 +46,14 @@ void Fl_Menu_Button::draw() {
   and if they pick one it sets value() and does the callback or
   sets changed() as described above.  The menu item is returned
   or NULL if the user dismisses the menu.
+
+  \note Since FLTK 1.4.0 Fl_Menu_::menu_end() is called before the menu
+    pops up to make sure the menu array is located in private storage.
+
+  \see Fl_Menu_::menu_end()
 */
 const Fl_Menu_Item* Fl_Menu_Button::popup() {
+  menu_end();
   const Fl_Menu_Item* m;
   pressed_menu_button_ = this;
   redraw();
diff --git src/Fl_Menu_add.cxx src/Fl_Menu_add.cxx
index 57534f4e3..05c2f8edd 100644
--- src/Fl_Menu_add.cxx
+++ src/Fl_Menu_add.cxx
@@ -44,6 +44,15 @@ extern Fl_Menu_* fl_menu_array_owner; // in Fl_Menu_.cxx
 // widget, and if so it reallocates as necessary.
 
 
+// *FIXME* For debugging only! Remove all debugging code under conditional
+// '#if DEBUG_MENU_ADD' including the following function before committing.
+// (Albrecht-S)
+#define DEBUG_MENU_ADD (0)
+#if DEBUG_MENU_ADD
+void *menu_local_array() { // used in developer's test code only
+  return local_array;
+}
+#endif // DEBUG_MENU_ADD
 
 // Insert a single Fl_Menu_Item into an array of size at offset n,
 // if this is local_array it will be reallocated if needed.
@@ -376,13 +385,7 @@ int Fl_Menu_::insert(
   // make this widget own the local array:
   if (this != fl_menu_array_owner) {
     if (fl_menu_array_owner) {
-      Fl_Menu_* o = fl_menu_array_owner;
-      // the previous owner gets its own correctly-sized array:
-      int value_offset = (int) (o->value_-local_array);
-      int n = local_array_size;
-      Fl_Menu_Item* newMenu = o->menu_ = new Fl_Menu_Item[n];
-      memcpy(newMenu, local_array, n*sizeof(Fl_Menu_Item));
-      if (o->value_) o->value_ = newMenu+value_offset;
+      fl_menu_array_owner->menu_end();
     }
     if (menu_) {
       // this already has a menu array, use it as the local one:
@@ -495,6 +498,57 @@ void Fl_Menu_::remove(int i) {
   memmove(item, next_item, (menu_+n-next_item)*sizeof(Fl_Menu_Item));
 }
 
+/**
+  Finishes menu modifications and returns menu().
+
+  \note	This is work in progress!
+
+  Call menu_end() after using add(), insert(), remove(), or any other
+  methods that may change the menu array if you want to access the
+  menu array anytime later with menu() or ... (?).
+
+  Does nothing if the menu array is already in a private location.
+
+  \note The menu() method is 'const' and would return a pointer to a
+	temporary internal menu array that may be relocated at unexpected
+	times (implementation details).
+
+  \todo Improve documentation.
+
+  \since 1.4.0
+
+  \returns New Fl_Menu_Item array pointer.
+
+  \see Fl_Menu_::menu()
+*/
+
+const Fl_Menu_Item *Fl_Menu_::menu_end() {
+
+#if DEBUG_MENU_ADD
+  fprintf(stderr, "Fl_Menu_::menu_end()\n");
+#endif // DEBUG_MENU_ADD
+
+  if (menu_ == local_array && fl_menu_array_owner == this) {
+
+#if DEBUG_MENU_ADD
+    fprintf(stderr, " ... copy menu to private array: %p -> ", menu_);
+#endif // DEBUG_MENU_ADD
+
+    // copy the menu array to a private correctly-sized array:
+    int value_offset = (int)(value_ - local_array);
+    int n = local_array_size;
+    Fl_Menu_Item* newMenu = menu_ = new Fl_Menu_Item[n];
+    memcpy(newMenu, local_array, n * sizeof(Fl_Menu_Item));
+    if (value_)
+      value_ = newMenu + value_offset;
+#if DEBUG_MENU_ADD
+    fprintf(stderr, "%p\n", menu_);
+#endif // DEBUG_MENU_ADD
+  }
+  fl_menu_array_owner = 0;
+  return menu_;
+}
+
 //
 // End of "$Id$".
 //
Direct Link to Message ]
 
     
Previous Message ]New Message | Reply ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.