Merge pull request #24 from klayoutmatthias/menu_issue

Menu issue
This commit is contained in:
Kazunari Sekigawa 2018-02-10 06:48:58 +09:00 committed by GitHub
commit c162fa81d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 101 deletions

View File

@ -1380,6 +1380,17 @@ GuiApplication::notify (QObject *receiver, QEvent *e)
return ret;
}
void
GuiApplication::force_update_app_menu ()
{
#if defined(__APPLE__)
// This is a workaround for a bug in the MacOS native menu integration:
// this signal forces the menu to become updated. Without this, any
// new menu items stay disabled.
emit focusWindowChanged (focusWindow ());
#endif
}
int
GuiApplication::exec ()
{

View File

@ -432,6 +432,12 @@ public:
return mp_mw;
}
/**
* @brief Forces update of the application menu
* This function is used for work around a MacOS issue.
*/
void force_update_app_menu ();
protected:
virtual void setup ();
virtual void shutdown ();

View File

@ -1195,10 +1195,6 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
pydoc = pya::PythonInterpreter::instance ()->python_doc (i->second.first);
}
os << "<a name=\"method" << n << "\"/>"
<< "<a name=\"m_" << escape_xml (i->first) << "\"/>"
<< "<keyword title=\"" << tl::to_string (QObject::tr ("API reference - Class")) << " " << escape_xml (cls) << ", " << tl::to_string (QObject::tr ("Method")) << " " << escape_xml (i->first) << "\" name=\"" << escape_xml (cls) << "#" << escape_xml (i->first) << "\"/>" << std::endl;
os << "<tr>";
if (i->first != prev_title) {
int rows = 0;
@ -1212,6 +1208,10 @@ GSIHelpProvider::produce_class_doc (const std::string &cls) const
}
os << "<td style=\"padding-bottom: 16px\">";
os << "<a name=\"method" << n << "\"/>"
<< "<a name=\"m_" << escape_xml (i->first) << "\"/>"
<< "<keyword title=\"" << tl::to_string (QObject::tr ("API reference - Class")) << " " << escape_xml (cls) << ", " << tl::to_string (QObject::tr ("Method")) << " " << escape_xml (i->first) << "\" name=\"" << escape_xml (cls) << "#" << escape_xml (i->first) << "\"/>" << std::endl;
os << "<p><b>" << tl::to_string (QObject::tr ("Signature")) << "</b>: ";
std::string attr = method_attributes (i->second.first, method_doc);
if (! attr.empty ()) {

View File

@ -450,6 +450,7 @@ MainWindow::MainWindow (QApplication *app, const char *name)
m_disable_tab_selected (false),
m_exited (false),
dm_do_update_menu (this, &MainWindow::do_update_menu),
dm_do_update_file_menu (this, &MainWindow::do_update_file_menu),
dm_exit (this, &MainWindow::exit),
m_grid_micron (0.001),
m_default_grids_updated (true),
@ -613,10 +614,6 @@ MainWindow::MainWindow (QApplication *app, const char *name)
tl_assert (bookmark_menu != 0);
connect (bookmark_menu, SIGNAL (aboutToShow ()), this, SLOT (bookmark_menu_show ()));
QMenu *file_menu = mp_menu->menu ("file_menu");
tl_assert (file_menu != 0);
connect (file_menu, SIGNAL (aboutToShow ()), this, SLOT (file_menu_show ()));
// select the default mode
select_mode (lay::LayoutView::default_mode ());
@ -1595,6 +1592,8 @@ MainWindow::configure (const std::string &name, const std::string &value)
}
}
dm_do_update_file_menu ();
return true;
} else if (name == cfg_micron_digits) {
@ -4173,30 +4172,31 @@ MainWindow::add_mru (const std::string &fn_rel, const std::string &tech)
}
void
MainWindow::file_menu_show ()
MainWindow::do_update_file_menu ()
{
if (mp_menu->is_valid ("file_menu.open_recent_menu")) {
std::string mru_menu = "file_menu.open_recent_menu";
Action open_recent_action = mp_menu->action ("file_menu.open_recent_menu");
if (mp_menu->is_valid (mru_menu)) {
Action open_recent_action = mp_menu->action (mru_menu);
open_recent_action.set_enabled (true);
if (m_mru.size () > 0 && edits_enabled ()) {
open_recent_action.set_enabled (true);
QMenu *open_recent_menu = open_recent_action.qaction ()->menu ();
if (open_recent_menu) {
open_recent_menu->clear ();
for (std::vector<std::pair<std::string, std::string> >::iterator mru = m_mru.end (); mru != m_mru.begin (); ) {
--mru;
unsigned int i = std::distance (m_mru.begin (), mru);
QAction *action = open_recent_menu->addAction (tl::to_qstring (mru->first));
action->setObjectName (tl::to_qstring (tl::sprintf ("open_recent_%d", i + 1)));
gtf::action_connect (action, SIGNAL (triggered ()), this, SLOT (open_recent ()));
action->setData (QVariant (int (i)));
}
// rebuild MRU menu
std::vector<std::string> items = mp_menu->items (mru_menu);
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
mp_menu->delete_item (mru_menu + "." + *i);
}
for (std::vector<std::pair<std::string, std::string> >::iterator mru = m_mru.end (); mru != m_mru.begin (); ) {
--mru;
unsigned int i = std::distance (m_mru.begin (), mru);
Action action;
gtf::action_connect (action.qaction (), SIGNAL (triggered ()), this, SLOT (open_recent ()));
action.set_title (mru->first);
action.qaction ()->setData (QVariant (int (i)));
mp_menu->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action);
}
} else {
@ -5016,6 +5016,10 @@ void
MainWindow::do_update_menu ()
{
mp_menu->build (menuBar (), mp_tool_bar);
lay::GuiApplication *app = dynamic_cast<lay::GuiApplication *> (qApp);
if (app) {
app->force_update_app_menu ();
}
}
void

View File

@ -859,7 +859,6 @@ protected slots:
void menu_changed ();
void message_timer ();
void bookmark_menu_show ();
void file_menu_show ();
void edits_enabled_changed ();
void file_changed_timer ();
void file_changed (const QString &path);
@ -868,6 +867,7 @@ protected slots:
protected:
void update_content ();
void do_update_menu ();
void do_update_file_menu ();
private:
TextProgressDelegate m_text_progress;
@ -911,6 +911,7 @@ private:
bool m_disable_tab_selected;
bool m_exited;
tl::DeferredMethod<MainWindow> dm_do_update_menu;
tl::DeferredMethod<MainWindow> dm_do_update_file_menu;
tl::DeferredMethod<MainWindow> dm_exit;
QTimer m_message_timer;
QTimer m_file_changed_timer;

View File

@ -66,7 +66,7 @@ static const bool s_can_move_menu = true;
#endif
// ---------------------------------------------------------------
// Helper function to parse a title with potential shortcut and
// Helper function to parse a title with potential shortcut and
// icon specification
static void
@ -122,19 +122,19 @@ parse_menu_title (const std::string &s, std::string &title, std::string &shortcu
// ---------------------------------------------------------------
// AbstractMenuItem implementation
AbstractMenuItem::AbstractMenuItem ()
AbstractMenuItem::AbstractMenuItem ()
: m_has_submenu (false), m_remove_on_empty (false)
{
// ... nothing yet ..
}
AbstractMenuItem::AbstractMenuItem (const AbstractMenuItem &)
AbstractMenuItem::AbstractMenuItem (const AbstractMenuItem &)
: m_has_submenu (false), m_remove_on_empty (false)
{
{
// ... nothing yet ..
}
void
void
AbstractMenuItem::setup_item (const std::string &pn, const std::string &s, const Action &a)
{
m_basename.clear ();
@ -162,7 +162,7 @@ AbstractMenuItem::setup_item (const std::string &pn, const std::string &s, const
set_action (a, false);
}
void
void
AbstractMenuItem::set_action (const Action &a, bool copy_properties)
{
Action acopy = a;
@ -187,13 +187,13 @@ AbstractMenuItem::set_action (const Action &a, bool copy_properties)
}
}
void
void
AbstractMenuItem::set_action_title (const std::string &s)
{
m_action.set_title (s);
}
void
void
AbstractMenuItem::set_has_submenu ()
{
m_has_submenu = true;
@ -214,10 +214,20 @@ static std::set<ActionHandle *> *sp_actionHandles = 0;
* @brief A specialization that provides a way to catch ambiguous key shortcuts
*/
class ActionObject
: public QAction
: public QAction
{
public:
ActionObject (QObject *parent) : QAction (parent) { }
ActionObject (QObject *parent)
: QAction (parent)
{
static size_t s_id = 0;
m_id = ++s_id;
}
size_t id () const
{
return m_id;
}
bool event(QEvent *e)
{
@ -251,8 +261,18 @@ public:
return QAction::event(e);
}
private:
size_t m_id;
};
static size_t
id_from_action (QAction *action)
{
ActionObject *ao = dynamic_cast<ActionObject *> (action);
return ao ? ao->id () : 0;
}
ActionHandle::ActionHandle (QWidget *parent)
: mp_menu (0),
mp_action (new ActionObject (parent)),
@ -260,7 +280,7 @@ ActionHandle::ActionHandle (QWidget *parent)
m_owned (true),
m_visible (true),
m_hidden (false)
{
{
if (! sp_actionHandles) {
sp_actionHandles = new std::set<ActionHandle *> ();
}
@ -273,11 +293,11 @@ ActionHandle::ActionHandle (QWidget *parent)
ActionHandle::ActionHandle (QAction *action, bool owned)
: mp_menu (0),
mp_action (action),
m_ref_count (0),
m_ref_count (0),
m_owned (owned),
m_visible (true),
m_hidden (false)
{
{
if (! sp_actionHandles) {
sp_actionHandles = new std::set<ActionHandle *> ();
}
@ -330,13 +350,13 @@ ActionHandle::~ActionHandle ()
}
}
void
ActionHandle::add_ref ()
void
ActionHandle::add_ref ()
{
++m_ref_count;
}
void
void
ActionHandle::remove_ref ()
{
if (--m_ref_count == 0) {
@ -356,7 +376,7 @@ ActionHandle::menu () const
return mp_menu;
}
void
void
ActionHandle::destroyed (QObject * /*obj*/)
{
mp_action = 0;
@ -548,7 +568,7 @@ Action::triggered_slot ()
END_PROTECTED
}
void
void
Action::set_title (const std::string &t)
{
if (qaction ()) {
@ -556,7 +576,7 @@ Action::set_title (const std::string &t)
}
}
std::string
std::string
Action::get_title () const
{
if (qaction ()) {
@ -566,7 +586,7 @@ Action::get_title () const
}
}
void
void
Action::set_shortcut (const QKeySequence &s)
{
if (mp_handle) {
@ -732,7 +752,7 @@ Action::set_separator (bool s)
}
}
void
void
Action::set_icon (const std::string &filename)
{
if (qaction ()) {
@ -744,7 +764,7 @@ Action::set_icon (const std::string &filename)
}
}
std::string
std::string
Action::get_tool_tip () const
{
if (qaction ()) {
@ -754,7 +774,7 @@ Action::get_tool_tip () const
}
}
void
void
Action::set_tool_tip (const std::string &text)
{
if (qaction ()) {
@ -766,7 +786,7 @@ Action::set_tool_tip (const std::string &text)
}
}
std::string
std::string
Action::get_icon_text () const
{
if (qaction ()) {
@ -776,7 +796,7 @@ Action::get_icon_text () const
}
}
void
void
Action::set_icon_text (const std::string &icon_text)
{
if (qaction ()) {
@ -788,7 +808,7 @@ Action::set_icon_text (const std::string &icon_text)
}
}
void
void
Action::set_object_name (const std::string &name)
{
if (qaction ()) {
@ -814,7 +834,7 @@ ConfigureAction::ConfigureAction (lay::PluginRoot *pr, const std::string &cname,
}
reg ();
}
}
ConfigureAction::ConfigureAction (lay::PluginRoot *pr, const std::string &title, const std::string &cname, const std::string &cvalue)
: Action (title), m_pr (pr), m_cname (cname), m_cvalue (cvalue), m_type (ConfigureAction::setter_type)
@ -831,19 +851,19 @@ ConfigureAction::ConfigureAction (lay::PluginRoot *pr, const std::string &title,
}
reg ();
}
}
ConfigureAction::~ConfigureAction ()
{
unreg ();
}
void
void
ConfigureAction::triggered ()
{
if (m_type == boolean_type) {
m_cvalue = tl::to_string (is_checked ());
}
}
m_pr->config_set (m_cname, m_cvalue);
}
@ -880,7 +900,7 @@ ConfigureAction::configure (const std::string &value)
set_checkable (true);
set_checked (m_cvalue == value);
}
}
}
// ---------------------------------------------------------------
@ -897,7 +917,7 @@ AbstractMenu::create_action (const std::string &s)
std::string tool_tip;
parse_menu_title (s, title, shortcut, res, tool_tip);
ActionHandle *ah = new ActionHandle (lay::AbstractMenuProvider::instance ()->menu_parent_widget ());
ah->ptr ()->setText (tl::to_qstring (title));
@ -927,7 +947,7 @@ AbstractMenu::~AbstractMenu ()
// .. nothing yet ..
}
void
void
AbstractMenu::init (const MenuLayoutEntry *layout)
{
m_root.set_has_submenu ();
@ -943,12 +963,12 @@ AbstractMenu::make_exclusive_group (const std::string &name)
QActionGroup *ag = new QActionGroup (this);
ag->setExclusive (true);
a = m_action_groups.insert (std::make_pair (name, ag)).first;
}
}
return a->second;
}
void
void
AbstractMenu::build_detached (const std::string &name, QFrame *mbar)
{
// Clean up the menu bar before rebuilding
@ -1005,7 +1025,7 @@ AbstractMenu::build_detached (const std::string &name, QFrame *mbar)
menu_layout->addStretch (1);
}
void
void
AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
{
tl_assert (mp_provider != 0);
@ -1013,9 +1033,11 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
m_helper_menu_items.clear ();
tbar->clear ();
std::set<QAction *> present_actions;
std::set<std::pair<size_t, QAction *> > present_actions;
QList<QAction *> a = mbar->actions ();
present_actions.insert (a.begin (), a.end ());
for (QList<QAction *>::const_iterator i = a.begin (); i != a.end (); ++i) {
present_actions.insert (std::make_pair (id_from_action (*i), *i));
}
for (std::list<AbstractMenuItem>::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) {
@ -1027,7 +1049,7 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
} else if (c->name ().find ("@@") == 0) {
// nothing: let build_detached build the menu
// nothing: let build_detached build the menu
} else if (c->name ().find ("@") == 0) {
@ -1035,9 +1057,9 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
QMenu *menu = new QMenu (tl::to_qstring (c->action ().get_title ()));
// HINT: it is necessary to add the menu action to a widget below the main window.
// Otherwise, the keyboard shortcuts do not work for menu items inside such a
// popup menu. It seems not to have a negative effect to add the menu to the
// popup menu. It seems not to have a negative effect to add the menu to the
// main widget.
mp_provider->menu_parent_widget ()->addAction (menu->menuAction ());
mp_provider->menu_parent_widget ()->addAction (menu->menuAction ());
c->set_action (Action (new ActionHandle (menu)), true);
}
@ -1053,10 +1075,10 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
c->set_action (Action (new ActionHandle (menu)), true);
} else {
// Move the action to the end if present in the menu already
std::set<QAction *>::iterator a = present_actions.find (c->menu ()->menuAction ());
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ()));
if (a != present_actions.end ()) {
if (s_can_move_menu) {
mbar->removeAction (*a);
mbar->removeAction (a->second);
mbar->addMenu (c->menu ());
}
present_actions.erase (*a);
@ -1071,10 +1093,10 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
} else {
// Move the action to the end if present in the menu already
std::set<QAction *>::iterator a = present_actions.find (c->action ().qaction ());
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ().qaction ()), c->action ().qaction ()));
if (a != present_actions.end ()) {
if (s_can_move_menu) {
mbar->removeAction (*a);
mbar->removeAction (a->second);
mbar->addAction (c->action ().qaction ());
}
present_actions.erase (*a);
@ -1086,17 +1108,19 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
}
// Remove all actions that have vanished
for (std::set<QAction *>::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
mbar->removeAction (*a);
for (std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
mbar->removeAction (a->second);
}
}
void
void
AbstractMenu::build (QMenu *m, std::list<AbstractMenuItem> &items)
{
std::set<QAction *> present_actions;
std::set<std::pair<size_t, QAction *> > present_actions;
QList<QAction *> a = m->actions ();
present_actions.insert (a.begin (), a.end ());
for (QList<QAction *>::const_iterator i = a.begin (); i != a.end (); ++i) {
present_actions.insert (std::make_pair (id_from_action (*i), *i));
}
for (std::list<AbstractMenuItem>::iterator c = items.begin (); c != items.end (); ++c) {
@ -1111,10 +1135,10 @@ AbstractMenu::build (QMenu *m, std::list<AbstractMenuItem> &items)
c->set_action (Action (new ActionHandle (menu)), true);
} else {
// Move the action to the end if present in the menu already
std::set<QAction *>::iterator a = present_actions.find (c->menu ()->menuAction ());
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ()));
if (a != present_actions.end ()) {
if (s_can_move_menu) {
m->removeAction (*a);
m->removeAction (a->second);
m->addMenu (c->menu ());
}
present_actions.erase (*a);
@ -1128,10 +1152,10 @@ AbstractMenu::build (QMenu *m, std::list<AbstractMenuItem> &items)
} else {
// Move the action to the end if present in the menu already
std::set<QAction *>::iterator a = present_actions.find (c->action ().qaction ());
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ().qaction ()), c->action ().qaction ()));
if (a != present_actions.end ()) {
if (s_can_move_menu) {
m->removeAction (*a);
m->removeAction (a->second);
m->addAction (c->action ().qaction ());
}
present_actions.erase (*a);
@ -1143,26 +1167,26 @@ AbstractMenu::build (QMenu *m, std::list<AbstractMenuItem> &items)
}
// Remove all actions that have vanished
for (std::set<QAction *>::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
m->removeAction (*a);
for (std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
m->removeAction (a->second);
}
}
void
void
AbstractMenu::build (QToolBar *t, std::list<AbstractMenuItem> &items)
{
for (std::list<AbstractMenuItem>::iterator c = items.begin (); c != items.end (); ++c) {
if (! c->children.empty ()) {
// To support tool buttons with menu we have to attach a helper menu
// item to the QAction object.
// To support tool buttons with menu we have to attach a helper menu
// item to the QAction object.
// TODO: this hurts if we use this QAction otherwise. In this case, this
// QAction would get a menu too. However, hopefully this usage is constrained
// to special toolbar buttons only.
// In order to be able to manage the QMenu ourselves, we must not give it a parent.
QMenu *menu = new QMenu (0);
m_helper_menu_items.push_back (menu); // will be owned by the stable vector
m_helper_menu_items.push_back (menu); // will be owned by the stable vector
c->action ().qaction ()->setMenu (menu);
t->addAction (c->action ().qaction ());
build (menu, c->children);
@ -1183,7 +1207,7 @@ AbstractMenu::detached_menu (const std::string &name)
}
QMenu *
AbstractMenu::menu (const std::string &path)
AbstractMenu::menu (const std::string &path)
{
AbstractMenuItem *item = find_item_exact (path);
if (item) {
@ -1214,7 +1238,7 @@ AbstractMenu::is_separator (const std::string &path) const
return item != 0 && item->action ().is_separator ();
}
Action
Action
AbstractMenu::action (const std::string &path) const
{
const AbstractMenuItem *item = find_item_exact (path);
@ -1224,7 +1248,7 @@ AbstractMenu::action (const std::string &path) const
return item->action ();
}
std::vector<std::string>
std::vector<std::string>
AbstractMenu::items (const std::string &path) const
{
std::vector<std::string> res;
@ -1240,7 +1264,7 @@ AbstractMenu::items (const std::string &path) const
return res;
}
void
void
AbstractMenu::insert_item (const std::string &p, const std::string &name, const Action &action)
{
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
@ -1271,7 +1295,7 @@ AbstractMenu::insert_item (const std::string &p, const std::string &name, const
emit changed ();
}
void
void
AbstractMenu::insert_separator (const std::string &p, const std::string &name)
{
tl_assert (mp_provider != 0); // required to get the parent for the new QAction (via ActionHandle)
@ -1294,7 +1318,7 @@ AbstractMenu::insert_separator (const std::string &p, const std::string &name)
emit changed ();
}
void
void
AbstractMenu::insert_menu (const std::string &p, const std::string &name, const Action &action)
{
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
@ -1324,13 +1348,13 @@ AbstractMenu::insert_menu (const std::string &p, const std::string &name, const
emit changed ();
}
void
void
AbstractMenu::insert_menu (const std::string &path, const std::string &name, const std::string &title)
{
insert_menu (path, name, create_action (title));
}
void
void
AbstractMenu::delete_item (const std::string &p)
{
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
@ -1387,7 +1411,7 @@ AbstractMenu::find_item_exact (const std::string &path) const
}
AbstractMenuItem *
AbstractMenu::find_item_exact (const std::string &path)
AbstractMenu::find_item_exact (const std::string &path)
{
tl::Extractor extr (path.c_str ());
AbstractMenuItem *item = &m_root;
@ -1406,7 +1430,7 @@ AbstractMenu::find_item_exact (const std::string &path)
}
if (n > 0) {
return 0;
}
}
item = &*c;
@ -1576,7 +1600,7 @@ AbstractMenu::find_item (const std::string &p)
return path;
}
void
void
AbstractMenu::transfer (const MenuLayoutEntry *layout, AbstractMenuItem &item)
{
tl_assert (mp_provider != 0);
@ -1609,7 +1633,7 @@ AbstractMenu::transfer (const MenuLayoutEntry *layout, AbstractMenuItem &item)
std::string tool_tip;
parse_menu_title (layout->title, title, shortcut, res, tool_tip);
a.set_separator (false);
a.set_title (title);
@ -1641,7 +1665,7 @@ AbstractMenu::transfer (const MenuLayoutEntry *layout, AbstractMenuItem &item)
}
}
std::vector<std::string>
std::vector<std::string>
AbstractMenu::group (const std::string &name) const
{
std::vector<std::string> grp;
@ -1649,7 +1673,7 @@ AbstractMenu::group (const std::string &name) const
return grp;
}
void
void
AbstractMenu::collect_group (std::vector<std::string> &grp, const std::string &name, const AbstractMenuItem &item) const
{
for (std::list<AbstractMenuItem>::const_iterator c = item.children.begin (); c != item.children.end (); ++c) {