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.
This commit is contained in:
ManoloFLTK 2024-02-20 16:00:01 +01:00
parent 4649188cf6
commit ba5ed296e9
4 changed files with 63 additions and 25 deletions

View File

@ -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++;

View File

@ -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++;

View File

@ -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;

View File

@ -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();
}