From ba5ed296e9faf92a047f68e83e9348eb43a40eec Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:00:01 +0100 Subject: [PATCH] Improve implementation of the macOS Window menu The macOS Window menu is connected to the item array of the macOS menubar via FL_SUBMENU_POINTER. This facilitates memorization of pointers to items of the macOS menubar because the item array no longer changes each time a window opens of gets closed. --- src/Fl_MacOS_Sys_Menu_Bar.mm | 80 ++++++++++++++----- src/Fl_cocoa.mm | 4 +- .../Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H | 1 + test/menubar.cxx | 3 +- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/Fl_MacOS_Sys_Menu_Bar.mm b/src/Fl_MacOS_Sys_Menu_Bar.mm index cf1ac93b9..c671288bd 100644 --- a/src/Fl_MacOS_Sys_Menu_Bar.mm +++ b/src/Fl_MacOS_Sys_Menu_Bar.mm @@ -446,6 +446,7 @@ static int process_sys_menu_shortcuts(int event) Fl_MacOS_Sys_Menu_Bar_Driver::Fl_MacOS_Sys_Menu_Bar_Driver() : Fl_Sys_Menu_Bar_Driver() { + window_menu_items = NULL; Fl::add_handler(process_sys_menu_shortcuts); } @@ -599,6 +600,7 @@ static void merge_all_windows_cb(Fl_Widget *, void *) static bool window_menu_installed = false; +static int window_menu_items_count = 0; void Fl_MacOS_Sys_Menu_Bar_Driver::create_window_menu(void) { @@ -621,27 +623,49 @@ void Fl_MacOS_Sys_Menu_Bar_Driver::create_window_menu(void) fl_open_display(); new Fl_Sys_Menu_Bar(0,0,0,0); } - rank = fl_sys_menu_bar->Fl_Menu_::insert(rank, "Window", 0, NULL, 0, FL_SUBMENU); + if (!window_menu_items_count) { + window_menu_items_count = 6; + window_menu_items = (Fl_Menu_Item*)malloc(window_menu_items_count * sizeof(Fl_Menu_Item)); + } + rank = fl_sys_menu_bar->Fl_Menu_::insert(rank, "Window", 0, NULL, window_menu_items, FL_SUBMENU_POINTER); localized_Window = NSLocalizedString(@"Window", nil); - - fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Minimize", FL_COMMAND+'m', minimize_win_cb, 0, FL_MENU_DIVIDER); + memset(window_menu_items, 0, sizeof(Fl_Menu_Item)); + window_menu_items[0].label("Minimize"); + window_menu_items[0].callback(minimize_win_cb); + window_menu_items[0].shortcut(FL_COMMAND+'m'); + window_menu_items[0].flags = FL_MENU_DIVIDER; + rank = 1; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 if (fl_mac_os_version >= 101200 && window_menu_style() != Fl_Sys_Menu_Bar::tabbing_mode_none) { - fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Show Previous Tab", FL_SHIFT+FL_CTRL+0x9, previous_tab_cb, 0, 0); - fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Show Next Tab", FL_CTRL+0x9, next_tab_cb, 0, 0); - fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Move Tab To New Window", 0, move_tab_cb, 0, 0); - fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Merge All Windows", 0, merge_all_windows_cb, 0, FL_MENU_DIVIDER); + memset(window_menu_items + 1, 0, sizeof(Fl_Menu_Item)); + window_menu_items[1].label("Show Previous Tab"); + window_menu_items[1].callback(previous_tab_cb); + window_menu_items[1].shortcut(FL_SHIFT+FL_CTRL+0x9); + memset(window_menu_items+2, 0, sizeof(Fl_Menu_Item)); + window_menu_items[2].label("Show Next Tab"); + window_menu_items[2].callback(next_tab_cb); + window_menu_items[2].shortcut(FL_CTRL+0x9); + memset(window_menu_items+3, 0, sizeof(Fl_Menu_Item)); + window_menu_items[3].label("Move Tab To New Window"); + window_menu_items[3].callback(move_tab_cb); + memset(window_menu_items+4, 0, sizeof(Fl_Menu_Item)); + window_menu_items[4].label("Merge All Windows"); + window_menu_items[4].callback(merge_all_windows_cb); + window_menu_items[4].flags = FL_MENU_DIVIDER; + rank = 5; } #endif - ((Fl_Menu_Item*)fl_sys_menu_bar->menu()+rank)->user_data(&window_menu_style_); + memset(window_menu_items+rank, 0, sizeof(Fl_Menu_Item)); + window_menu_items[rank-1].user_data(&window_menu_style_); + fl_sys_menu_bar->menu_end(); fl_sys_menu_bar->update(); } int Fl_MacOS_Sys_Menu_Bar_Driver::find_first_window() { - int count = bar->size(), i; + int count = window_menu_items->size(), i; for (i = 0; i < count; i++) { - if (bar->menu()[i].user_data() == &window_menu_style_) break; + if (window_menu_items[i].user_data() == &window_menu_style_) break; } return i < count ? i : -1; } @@ -649,12 +673,22 @@ int Fl_MacOS_Sys_Menu_Bar_Driver::find_first_window() void Fl_MacOS_Sys_Menu_Bar_Driver::new_window(Fl_Window *win) { if (!window_menu_style() || !win->label()) return; - int index = find_first_window(); - if (index < 0) return; - while ((bar->menu()+index+1)->label()) index++; + int index = window_menu_items->size() - 1; + if (index >= window_menu_items_count - 1) { + window_menu_items_count += 5; + window_menu_items = (Fl_Menu_Item*)realloc(window_menu_items, + window_menu_items_count * sizeof(Fl_Menu_Item)); + Fl_Menu_Item *item = (Fl_Menu_Item*)fl_sys_menu_bar->find_item("Window"); + item->user_data(window_menu_items); + } const char *p = win->iconlabel() ? win->iconlabel() : win->label(); - int index2 = bar->Fl_Menu_::insert(index+1, p, 0, window_menu_cb, win, FL_MENU_RADIO); - setonly((Fl_Menu_Item*)bar->menu()+index2); + window_menu_items[index].label(p); + window_menu_items[index].callback(window_menu_cb); + window_menu_items[index].user_data(win); + window_menu_items[index].flags = FL_MENU_RADIO; + window_menu_items[index+1].label(NULL); + window_menu_items[index].setonly(); + fl_sys_menu_bar->update(); } void Fl_MacOS_Sys_Menu_Bar_Driver::remove_window(Fl_Window *win) @@ -663,19 +697,22 @@ void Fl_MacOS_Sys_Menu_Bar_Driver::remove_window(Fl_Window *win) int index = find_first_window() + 1; if (index < 1) return; while (true) { - Fl_Menu_Item *item = (Fl_Menu_Item*)bar->menu() + index; + Fl_Menu_Item *item = window_menu_items + index; if (!item->label()) return; if (item->user_data() == win) { bool doit = item->value(); - remove(index); + int count = window_menu_items->size(); + if (count - index - 1 > 0) memmove(item, item + 1, (count - index - 1)*sizeof(Fl_Menu_Item)); + memset(window_menu_items + count - 2, 0, sizeof(Fl_Menu_Item)); if (doit) { // select Fl::first_window() in Window menu - item = (Fl_Menu_Item*)bar->menu() + find_first_window() + 1; + item = window_menu_items + find_first_window() + 1; while (item->label() && item->user_data() != Fl::first_window()) item++; if (item->label()) { ((Fl_Window*)item->user_data())->show(); - setonly(item); + item->setonly(); } } + bar->update(); break; } index++; @@ -688,10 +725,11 @@ void Fl_MacOS_Sys_Menu_Bar_Driver::rename_window(Fl_Window *win) int index = find_first_window() + 1; if (index < 1) return; while (true) { - Fl_Menu_Item *item = (Fl_Menu_Item*)bar->menu() + index; + Fl_Menu_Item *item = window_menu_items + index; if (!item->label()) return; if (item->user_data() == win) { - replace(index, win->iconlabel() ? win->iconlabel() : win->label()); + item->label(win->iconlabel() ? win->iconlabel() : win->label()); + bar->update(); return; } index++; diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 9e51a6efa..23efb6e15 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -1395,10 +1395,10 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; // select the corresponding Window menu item int index = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->find_first_window() + 1; while (index > 0) { - Fl_Menu_Item *item = (Fl_Menu_Item*)fl_sys_menu_bar->menu() + index; + Fl_Menu_Item *item = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->window_menu_items + index; if (!item->label()) break; if (item->user_data() == window) { - if (!item->value()) fl_sys_menu_bar->setonly(item); + if (!item->value()) item->setonly(); break; } index++; diff --git a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H b/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H index 94d890f3d..c670a643b 100644 --- a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H +++ b/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H @@ -21,6 +21,7 @@ class Fl_MacOS_Sys_Menu_Bar_Driver : public Fl_Sys_Menu_Bar_Driver { public: + Fl_Menu_Item *window_menu_items; Fl_MacOS_Sys_Menu_Bar_Driver(); virtual ~Fl_MacOS_Sys_Menu_Bar_Driver(); void update() FL_OVERRIDE; diff --git a/test/menubar.cxx b/test/menubar.cxx index e46a60be0..a0424732d 100644 --- a/test/menubar.cxx +++ b/test/menubar.cxx @@ -202,8 +202,7 @@ void menu_location_cb(Fl_Widget* w, void* data) smenubar->callback(test_cb); } else { // switch to window menu bar - menubar->menu(smenubar->menu()); - smenubar->clear(); + menubar->copy(smenubar->menu()); delete smenubar; menubar->show(); }