diff --git a/src/doc/doc/about/macro_in_menu.xml b/src/doc/doc/about/macro_in_menu.xml
index e97ff27b8..2c1b9ba6c 100644
--- a/src/doc/doc/about/macro_in_menu.xml
+++ b/src/doc/doc/about/macro_in_menu.xml
@@ -78,5 +78,20 @@
not defined.
+ A Note for MacOS Users
+
+
+ The Qt/MacOS combination does not allow for dynamically configuring
+ the main menu. On MacOS the behavior is as follows:
+
+
+
+ Every top-level menu entry that is generated either by code or by configuring
+ a macro correspondingly, will be shown in a separate entry in the main toolbar.
+ This entry is labelled "..." and is shown only when it is populated. All
+ non-standard main menu entries are placed there in the same order they would
+ appear in the main menu.
+
+
diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc
index d9c7fe0a7..51bc51462 100644
--- a/src/lay/lay/layMainWindow.cc
+++ b/src/lay/lay/layMainWindow.cc
@@ -4350,6 +4350,10 @@ public:
menu_entries.push_back (lay::submenu ("zoom_menu", at, tl::to_string (QObject::tr ("&Display"))));
menu_entries.push_back (lay::submenu ("tools_menu", at, tl::to_string (QObject::tr ("&Tools"))));
menu_entries.push_back (lay::submenu ("macros_menu", at, tl::to_string (QObject::tr ("&Macros"))));
+ if (lay::AbstractMenu::wants_extra_menu ()) {
+ // for MacOS
+ menu_entries.push_back (lay::submenu (lay::AbstractMenu::extra_menu_name (), at, "..."));
+ }
menu_entries.push_back (lay::separator ("help_group", at));
menu_entries.push_back (lay::submenu ("help_menu", at, tl::to_string (QObject::tr ("&Help"))));
menu_entries.push_back (lay::submenu ("@secrets", at, tl::to_string (QObject::tr ("Secret Features"))));
diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc
index a63b4c49c..c503a0e1b 100644
--- a/src/laybasic/laybasic/layAbstractMenu.cc
+++ b/src/laybasic/laybasic/layAbstractMenu.cc
@@ -1225,6 +1225,36 @@ static QAction *insert_action_after (QWidget *widget, QAction *after, QAction *a
return action;
}
+static const std::string s_extras_menu_name ("_extras_menu");
+
+static QMenu *find_extras_menu (QMenuBar *mbar)
+{
+ if (! mbar) {
+ return 0;
+ }
+
+ QList a = mbar->actions ();
+ for (QList::const_iterator i = a.begin (); i != a.end (); ++i) {
+ if (tl::to_string ((*i)->objectName ()) == s_extras_menu_name) {
+ return (*i)->menu ();
+ }
+ }
+
+ return 0;
+}
+
+bool
+AbstractMenu::wants_extra_menu ()
+{
+ return !s_can_move_menu;
+}
+
+const std::string &
+AbstractMenu::extra_menu_name ()
+{
+ return s_extras_menu_name;
+}
+
void
AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
{
@@ -1234,10 +1264,22 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
std::set > present_actions;
if (mbar) {
+
QList a = mbar->actions ();
for (QList::const_iterator i = a.begin (); i != a.end (); ++i) {
present_actions.insert (std::make_pair (id_from_action (*i), *i));
}
+
+ }
+
+ // NOTE: on MacOS, once the top level menu is created, we should not
+ // modify it and place menu items into the "Extras" menu instead.
+ bool menu_frozen = ! s_can_move_menu && ! present_actions.empty ();
+
+ // NOTE: the "extras" menu is relevant on MacOS only
+ QMenu *extras_menu = find_extras_menu (mbar);
+ if (extras_menu) {
+ extras_menu->clear ();
}
QAction *prev_action = 0;
@@ -1277,11 +1319,24 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
} else if (mbar) {
if (c->menu () == 0) {
+
QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ());
menu->setTitle (tl::to_qstring (c->action ()->get_title ()));
c->set_action (new Action (menu), true);
- prev_action = insert_action_after (mbar, prev_action, menu->menuAction ());
+
+ // This case happens when we dynamically create menus.
+ // MacOS does not like generating top-level menus dynamically, so
+ // we put them into the "_extra" top level one.
+ if (menu_frozen) {
+ if (extras_menu) {
+ extras_menu->addMenu (menu);
+ }
+ } else {
+ prev_action = insert_action_after (mbar, prev_action, menu->menuAction ());
+ }
+
} else {
+
// Move the action to the end if present in the menu already
std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ()));
if (a != present_actions.end ()) {
@@ -1291,12 +1346,20 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
}
prev_action = a->second;
present_actions.erase (*a);
+ } else if (menu_frozen) {
+ if (extras_menu) {
+ extras_menu->addMenu (c->menu ());
+ }
} else {
prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ());
}
+
}
- build (c->menu (), c->children);
+ // NOTE: the "extras" menu is built implicitly. You cannot put anything there.
+ if (c->menu () != extras_menu) {
+ build (c->menu (), c->children);
+ }
}
@@ -1311,6 +1374,11 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
}
prev_action = a->second;
present_actions.erase (*a);
+ } else if (menu_frozen) {
+ QMenu *extras_menu = find_extras_menu (mbar);
+ if (extras_menu) {
+ extras_menu->addAction (c->action ()->qaction ());
+ }
} else {
prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ());
}
@@ -1319,8 +1387,14 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
}
+ // Disable the (maybe new) "extras" menu if empty
+ extras_menu = find_extras_menu (mbar);
+ if (extras_menu) {
+ extras_menu->setEnabled (! extras_menu->isEmpty ());
+ }
+
// Remove all actions that have vanished
- if (mbar) {
+ if (mbar && s_can_move_menu) {
for (std::set >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
mbar->removeAction (a->second);
}
diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h
index 272d8131a..aec72ac7a 100644
--- a/src/laybasic/laybasic/layAbstractMenu.h
+++ b/src/laybasic/laybasic/layAbstractMenu.h
@@ -692,6 +692,16 @@ public:
*/
~AbstractMenu ();
+ /**
+ * @brief Returns a value indicating that a special "extras" menu is needed
+ */
+ static bool wants_extra_menu ();
+
+ /**
+ * @brief Returns the name of the special "extras" menu
+ */
+ static const std::string &extra_menu_name ();
+
#if defined(HAVE_QT)
/**
* @brief Rebuild the QMenu's and refill the QMenuBar object