From 499c8bfc244f631304fe0c15449a31b442ebe0d7 Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Fri, 16 Nov 2012 04:47:11 +0000 Subject: Improved: Popup menus: Auto-breakup too-wide menus in class PopupMenu. --- muse2/ChangeLog | 4 + muse2/muse/widgets/popupmenu.cpp | 160 ++++++++++++++++++++++++--------------- muse2/muse/widgets/popupmenu.h | 15 +++- 3 files changed, 119 insertions(+), 60 deletions(-) diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 0efcd0ca..30427798 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,7 @@ +15.11.2012: + * Improved: Popup menus: Auto-breakup too-wide menus in class PopupMenu. (Tim) + In some cases X/Qt was giving up if too wide AND/OR too many columns, menu would not appear. + Tested OK with Deicsonze track info patch popup, and a synth plugin with > 2000 automation controls. 13.11.2012: - Fix init of gain for duplicated audiotracks (rj) 12.11.2012: diff --git a/muse2/muse/widgets/popupmenu.cpp b/muse2/muse/widgets/popupmenu.cpp index a34418a4..6cda1987 100644 --- a/muse2/muse/widgets/popupmenu.cpp +++ b/muse2/muse/widgets/popupmenu.cpp @@ -24,17 +24,12 @@ // //========================================================= -//#include #include #include #include #include #include #include -//#include - -//#include -//#include #include "popupmenu.h" #include "gconfig.h" @@ -47,11 +42,6 @@ namespace MusEGui { // PopupMenu //====================== -//PopupMenu::PopupMenu() -//{ -// init(); -//} - PopupMenu::PopupMenu(bool stayOpen) : _stayOpen(stayOpen) { @@ -72,11 +62,14 @@ PopupMenu::PopupMenu(const QString& title, QWidget* parent, bool stayOpen) void PopupMenu::init() { - //printf("PopupMenu::init this:%p\n", this); - // Menus will trigger! Set to make sure our trigger handlers ignore menus. menuAction()->setData(-1); + _cur_menu = this; + _cur_menu_count = 1; + _cur_item_width = 0; + _cur_col_count = 0; + //_stayOpen = false; moveDelta = 0; @@ -91,7 +84,7 @@ void PopupMenu::init() // NOTE: Tested all RoutePopupMenu and PopupMenu dtors and a couple of action dtors from our // PixmapButtonsHeaderWidgetAction and PixmapButtonsWidgetAction: -// This does not appear to be required any more. All submenus and actions are being deleted now. p4.0.43 +// This does not appear to be required any more. All submenus and actions are being deleted now. /* void PopupMenu::clear() { @@ -171,8 +164,6 @@ QAction* PopupMenu::findActionFromData(const QVariant& v) const bool PopupMenu::event(QEvent* event) { - //printf("PopupMenu::event type:%d\n", event->type()); - switch(event->type()) { #ifndef POPUP_MENU_DISABLE_STAY_OPEN @@ -221,25 +212,7 @@ bool PopupMenu::event(QEvent* event) { QMouseEvent* e = static_cast(event); QPoint globPos = e->globalPos(); - //QPoint pos = e->pos(); int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - - //printf("PopupMenu::event MouseMove: pos x:%d y:%d globPos x:%d y:%d\n", - // pos.x(), pos.y(), globPos.x(), globPos.y()); - - /* - //QAction* action = actionAt(globPos); - QAction* action = actionAt(pos); - if(action) - { - QRect r = actionGeometry(action); - //printf(" act x:%d y:%d w:%d h:%d popup px:%d py:%d pw:%d ph:%d\n", - // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height()); - - //action->hover(); - } - */ - if(x() < 0 && globPos.x() <= 0) // If on the very first pixel (or beyond) { moveDelta = 32; @@ -280,8 +253,6 @@ bool PopupMenu::event(QEvent* event) #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::timerHandler() { - // printf("PopupMenu::timerHandler\n"); - //if(!isVisible() || !hasFocus()) if(!isVisible()) { @@ -308,39 +279,16 @@ void PopupMenu::timerHandler() void PopupMenu::popHovered(QAction* action) { - //timer->stop(); - - //moveDelta = 0; if(action) { int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - QRect r = actionGeometry(action); - //printf("PopupMenu::popHovered x:%d y:%d w:%d h:%d px:%d py:%d pw:%d ph:%d\n", - // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height()); - //printf("PopupMenu::popHovered x:%d y:%d w:%d h:%d px:%d py:%d pw:%d ph:%d dtw:%d\n", - // r.x(), r.y(), r.width(), r.height(), x(), y(), width(), height(), dw); - //int x = r.x() + ctrlSubPop->x(); if(x() + r.x() < 0) - //setGeometry(0, y(), width(), height()); - //scroll(-x, 0); - //move(-r.x() + 32, y()); // Allow some of left column to show so that mouse can move over it. - //move(-r.x() + r.width(), y()); // Allow some of left column to show so that mouse can move over it. - //moveDelta = x() - r.x() + 32; move(-r.x(), y()); else if(r.x() + r.width() + x() > dw) - //setGeometry(1200 - r.x() - r.width(), y(), width(), height()); - //scroll(-x + 1200, 0); - //move(dw - r.x() - r.width() - 32, y()); // Allow some of right column to show so that mouse can move over it. - //move(dw - r.x(), y()); // Allow some of right column to show so that mouse can move over it. - //moveDelta = x() + dw - r.x() - r.width() - 32; move(dw - r.x() - r.width(), y()); } - - //if(moveDelta == 0) - // timer->stop(); - } #endif // POPUP_MENU_DISABLE_AUTO_SCROLL @@ -376,7 +324,6 @@ void PopupMenu::mouseReleaseEvent(QMouseEvent *e) return; } - //printf("PopupMenu::mouseReleaseEvent\n"); if (action) action->activate(QAction::Trigger); else @@ -385,6 +332,101 @@ void PopupMenu::mouseReleaseEvent(QMouseEvent *e) #endif // POPUP_MENU_DISABLE_STAY_OPEN } +//----------------------------------------- +// getMenu +// Auto-breakup a too-wide menu. +// NOTE This is a necessary catch-all because X doesn't like too-wide menus, but risky because +// some callers might depend on all the items being in one menu (using ::actions or ::addActions). +// So try to avoid that. The overwhelming rule of thumb is that too-wide menus are bad design anyway. +//------------------------------------------ +PopupMenu* PopupMenu::getMenu() +{ + // We want the whole thing if multiple monitors. + // Resonable to assume if X can show this desktop, it can show a menu with the same width? + int dw = QApplication::desktop()->width(); + // If we're still only at one column, not much we can do - some item(s) must have had reeeeally long text. + // Not to worry. Hopefully the auto-scroll will handle it! + // Use columnCount() + 2 to catch well BEFORE it widens beyond the edge, and leave room for many + // TESTED: Not only does the system not appear to like too-wide menus, but also the column count was observed + // rolling over to zero repeatedly after it reached 15, simply when adding actions! The action width was 52 + // the number of items when it first rolled over was around 480 = 884, well below my desktop width of 1366. + // Apparently there is a limit on the number of columns - whatever, it made the col count limit necessary: + if((_cur_col_count > 1 && ((_cur_col_count + 2) * _cur_item_width) >= dw) || _cur_col_count >= 8) + { + // This menu is too wide. So make a new one... + _cur_item_width = 0; + _cur_col_count = 1; + QString s(tr(" %1").arg(_cur_menu_count)); + _cur_menu = new PopupMenu(s, this, _stayOpen); + ++_cur_menu_count; + addMenu(_cur_menu); + } + return _cur_menu; +} + +//---------------------------------------------------- +// Need to catch these to auto-breakup a too-big menu... +//---------------------------------------------------- + +QAction* PopupMenu::addAction(const QString& text) +{ + QAction* act = static_cast(getMenu())->addAction(text); + int w = _cur_menu->actionGeometry(act).width(); + if(w > _cur_item_width) + _cur_item_width = w; + int c = _cur_menu->columnCount(); + if(c > _cur_col_count) + _cur_col_count = c; + return act; +} + +QAction* PopupMenu::addAction(const QIcon& icon, const QString& text) +{ + QAction* act = static_cast(getMenu())->addAction(icon, text); + int w = _cur_menu->actionGeometry(act).width(); + if(w > _cur_item_width) + _cur_item_width = w; + int c = _cur_menu->columnCount(); + if(c > _cur_col_count) + _cur_col_count = c; + return act; +} + +QAction* PopupMenu::addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) +{ + QAction* act = static_cast(getMenu())->addAction(text, receiver, member, shortcut); + int w = _cur_menu->actionGeometry(act).width(); + if(w > _cur_item_width) + _cur_item_width = w; + int c = _cur_menu->columnCount(); + if(c > _cur_col_count) + _cur_col_count = c; + return act; +} + +QAction* PopupMenu::addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) +{ + QAction* act = static_cast(getMenu())->addAction(icon, text, receiver, member, shortcut); + int w = _cur_menu->actionGeometry(act).width(); + if(w > _cur_item_width) + _cur_item_width = w; + int c = _cur_menu->columnCount(); + if(c > _cur_col_count) + _cur_col_count = c; + return act; +} + +void PopupMenu::addAction(QAction* action) +{ + static_cast(getMenu())->addAction(action); + int w = _cur_menu->actionGeometry(action).width(); + if(w > _cur_item_width) + _cur_item_width = w; + int c = _cur_menu->columnCount(); + if(c > _cur_col_count) + _cur_col_count = c; +} + /* //====================== // PopupView diff --git a/muse2/muse/widgets/popupmenu.h b/muse2/muse/widgets/popupmenu.h index d1d863e9..4c764fa8 100644 --- a/muse2/muse/widgets/popupmenu.h +++ b/muse2/muse/widgets/popupmenu.h @@ -58,7 +58,14 @@ Q_OBJECT QTimer* timer; #endif int moveDelta; + PopupMenu* _cur_menu; // For auto-breakup. + int _cur_menu_count; + int _cur_item_width; + int _cur_col_count; + void init(); + // Auto-breakup a too-wide menu. + PopupMenu* getMenu(); private slots: #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL @@ -71,7 +78,6 @@ Q_OBJECT bool event(QEvent*); public: - //PopupMenu(); PopupMenu(bool stayOpen); PopupMenu(QWidget* parent=0, bool stayOpen = false); PopupMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false); @@ -79,6 +85,13 @@ Q_OBJECT QAction* findActionFromData(const QVariant&) const; bool stayOpen() const { return _stayOpen; } void clearAllChecks() const; + + // Need to catch these to auto-breakup a too-big menu. + QAction* addAction(const QString& text); + QAction* addAction(const QIcon& icon, const QString& text); + QAction* addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); + QAction* addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); + void addAction(QAction* action); }; -- cgit v1.2.3