Support modes with context menus.

If modes provide a QMenu, a little arrow is shown next to the mode icon.
If the user clicks there, the menu is shown instead of changing the
mode.

Limitations: Modes need to provide some QMenu instance already when they
are added to the object pool. Setting or removing the menu later will
not update the UI.

Change-Id: Ic4ef709e6200afcff14f41054a5dd98c37b0b849
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
Eike Ziller
2016-05-19 11:50:45 +02:00
parent d984a83611
commit 685bc2cca1
5 changed files with 63 additions and 35 deletions

View File

@@ -41,6 +41,7 @@
#include <QPixmapCache> #include <QPixmapCache>
#include <QStackedLayout> #include <QStackedLayout>
#include <QStatusBar> #include <QStatusBar>
#include <QStyleOption>
#include <QToolTip> #include <QToolTip>
using namespace Core; using namespace Core;
@@ -50,6 +51,8 @@ using namespace Utils;
const int FancyTabBar::m_rounding = 22; const int FancyTabBar::m_rounding = 22;
const int FancyTabBar::m_textPadding = 4; const int FancyTabBar::m_textPadding = 4;
static const int kMenuButtonWidth = 16;
void FancyTab::fadeIn() void FancyTab::fadeIn()
{ {
animator.stop(); animator.stop();
@@ -83,10 +86,6 @@ FancyTabBar::FancyTabBar(QWidget *parent)
setAttribute(Qt::WA_Hover, true); setAttribute(Qt::WA_Hover, true);
setFocusPolicy(Qt::NoFocus); setFocusPolicy(Qt::NoFocus);
setMouseTracking(true); // Needed for hover events setMouseTracking(true); // Needed for hover events
m_triggerTimer.setSingleShot(true);
// We use a zerotimer to keep the sidebar responsive
connect(&m_triggerTimer, &QTimer::timeout, this, &FancyTabBar::emitCurrentIndex);
} }
FancyTabBar::~FancyTabBar() FancyTabBar::~FancyTabBar()
@@ -104,7 +103,7 @@ QSize FancyTabBar::tabSizeHint(bool minimum) const
int width = 60 + spacing + 2; int width = 60 + spacing + 2;
int maxLabelwidth = 0; int maxLabelwidth = 0;
for (int tab=0 ; tab<count() ;++tab) { for (int tab=0 ; tab<count() ;++tab) {
int width = fm.width(tabText(tab)); int width = fm.width(m_tabs.at(tab)->text);
if (width > maxLabelwidth) if (width > maxLabelwidth)
maxLabelwidth = width; maxLabelwidth = width;
} }
@@ -211,24 +210,22 @@ QRect FancyTabBar::tabRect(int index) const
} }
// This keeps the sidebar responsive since
// we get a repaint before loading the
// mode itself
void FancyTabBar::emitCurrentIndex()
{
emit currentChanged(m_currentIndex);
}
void FancyTabBar::mousePressEvent(QMouseEvent *e) void FancyTabBar::mousePressEvent(QMouseEvent *e)
{ {
e->accept(); e->accept();
for (int index = 0; index < m_tabs.count(); ++index) { for (int index = 0; index < m_tabs.count(); ++index) {
if (tabRect(index).contains(e->pos())) { const QRect rect = tabRect(index);
if (rect.contains(e->pos())) {
if (isTabEnabled(index)) { if (isTabEnabled(index)) {
m_currentIndex = index; if (m_tabs.at(index)->hasMenu && rect.right() - e->pos().x() <= kMenuButtonWidth) {
update(); // menu arrow clicked
m_triggerTimer.start(0); emit menuTriggered(index, e);
} else {
m_currentIndex = index;
update();
// update tab bar before showing widget
QTimer::singleShot(0, this, [this]() { emit currentChanged(m_currentIndex); });
}
} }
break; break;
} }
@@ -290,6 +287,7 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
} }
painter->save(); painter->save();
FancyTab *tab = m_tabs.at(tabIndex);
QRect rect = tabRect(tabIndex); QRect rect = tabRect(tabIndex);
bool selected = (tabIndex == m_currentIndex); bool selected = (tabIndex == m_currentIndex);
bool enabled = isTabEnabled(tabIndex); bool enabled = isTabEnabled(tabIndex);
@@ -303,7 +301,7 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
} }
} }
QString tabText(this->tabText(tabIndex)); QString tabText(tab->text);
QRect tabTextRect(rect); QRect tabTextRect(rect);
const bool drawIcon = rect.height() > 36; const bool drawIcon = rect.height() > 36;
QRect tabIconRect(tabTextRect); QRect tabIconRect(tabTextRect);
@@ -334,7 +332,7 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
tabIconRect.adjust(0, 4, 0, -textHeight); tabIconRect.adjust(0, 4, 0, -textHeight);
const QIcon::Mode iconMode = enabled ? (selected ? QIcon::Active : QIcon::Normal) const QIcon::Mode iconMode = enabled ? (selected ? QIcon::Active : QIcon::Normal)
: QIcon::Disabled; : QIcon::Disabled;
StyleHelper::drawIconWithShadow(tabIcon(tabIndex), tabIconRect, painter, iconMode); StyleHelper::drawIconWithShadow(tab->icon, tabIconRect, painter, iconMode);
} }
painter->setOpacity(1.0); //FIXME: was 0.7 before? painter->setOpacity(1.0); //FIXME: was 0.7 before?
@@ -350,6 +348,13 @@ void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
painter->translate(0, -1); painter->translate(0, -1);
painter->drawText(tabTextRect, textFlags, tabText); painter->drawText(tabTextRect, textFlags, tabText);
// menu arrow
if (tab->hasMenu) {
QStyleOption opt;
opt.initFrom(this);
opt.rect = rect.adjusted(rect.width() - kMenuButtonWidth, 0, -8, 0);
StyleHelper::drawArrow(QStyle::PE_IndicatorArrowRight, painter, &opt);
}
painter->restore(); painter->restore();
} }
@@ -479,6 +484,7 @@ FancyTabWidget::FancyTabWidget(QWidget *parent)
setLayout(mainLayout); setLayout(mainLayout);
connect(m_tabBar, &FancyTabBar::currentChanged, this, &FancyTabWidget::showWidget); connect(m_tabBar, &FancyTabBar::currentChanged, this, &FancyTabWidget::showWidget);
connect(m_tabBar, &FancyTabBar::menuTriggered, this, &FancyTabWidget::menuTriggered);
} }
void FancyTabWidget::setSelectionWidgetVisible(bool visible) void FancyTabWidget::setSelectionWidgetVisible(bool visible)
@@ -491,10 +497,11 @@ bool FancyTabWidget::isSelectionWidgetVisible() const
return m_selectionWidget->isVisible(); return m_selectionWidget->isVisible();
} }
void FancyTabWidget::insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label) void FancyTabWidget::insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label,
bool hasMenu)
{ {
m_modesStack->insertWidget(index, tab); m_modesStack->insertWidget(index, tab);
m_tabBar->insertTab(index, icon, label); m_tabBar->insertTab(index, icon, label, hasMenu);
} }
void FancyTabWidget::removeTab(int index) void FancyTabWidget::removeTab(int index)

View File

@@ -46,7 +46,7 @@ class FancyTab : public QObject
Q_PROPERTY(float fader READ fader WRITE setFader) Q_PROPERTY(float fader READ fader WRITE setFader)
public: public:
FancyTab(QWidget *tabbar) : enabled(false), tabbar(tabbar), m_fader(0) { FancyTab(QWidget *tabbar) : tabbar(tabbar){
animator.setPropertyName("fader"); animator.setPropertyName("fader");
animator.setTargetObject(this); animator.setTargetObject(this);
} }
@@ -59,12 +59,13 @@ public:
QIcon icon; QIcon icon;
QString text; QString text;
QString toolTip; QString toolTip;
bool enabled; bool enabled = false;
bool hasMenu = false;
private: private:
QPropertyAnimation animator; QPropertyAnimation animator;
QWidget *tabbar; QWidget *tabbar;
float m_fader; float m_fader = 0;
}; };
class FancyTabBar : public QWidget class FancyTabBar : public QWidget
@@ -91,10 +92,11 @@ public:
void setTabEnabled(int index, bool enable); void setTabEnabled(int index, bool enable);
bool isTabEnabled(int index) const; bool isTabEnabled(int index) const;
void insertTab(int index, const QIcon &icon, const QString &label) { void insertTab(int index, const QIcon &icon, const QString &label, bool hasMenu) {
FancyTab *tab = new FancyTab(this); FancyTab *tab = new FancyTab(this);
tab->icon = icon; tab->icon = icon;
tab->text = label; tab->text = label;
tab->hasMenu = hasMenu;
m_tabs.insert(index, tab); m_tabs.insert(index, tab);
updateGeometry(); updateGeometry();
} }
@@ -110,16 +112,12 @@ public:
void setTabToolTip(int index, QString toolTip) { m_tabs[index]->toolTip = toolTip; } void setTabToolTip(int index, QString toolTip) { m_tabs[index]->toolTip = toolTip; }
QString tabToolTip(int index) const { return m_tabs.at(index)->toolTip; } QString tabToolTip(int index) const { return m_tabs.at(index)->toolTip; }
QIcon tabIcon(int index) const { return m_tabs.at(index)->icon; }
QString tabText(int index) const { return m_tabs.at(index)->text; }
int count() const {return m_tabs.count(); } int count() const {return m_tabs.count(); }
QRect tabRect(int index) const; QRect tabRect(int index) const;
signals: signals:
void currentChanged(int); void currentChanged(int index);
void menuTriggered(int index, QMouseEvent *event);
public slots:
void emitCurrentIndex();
private: private:
static const int m_rounding; static const int m_rounding;
@@ -128,7 +126,6 @@ private:
int m_hoverIndex; int m_hoverIndex;
int m_currentIndex; int m_currentIndex;
QList<FancyTab*> m_tabs; QList<FancyTab*> m_tabs;
QTimer m_triggerTimer;
QSize tabSizeHint(bool minimum = false) const; QSize tabSizeHint(bool minimum = false) const;
}; };
@@ -140,7 +137,7 @@ class FancyTabWidget : public QWidget
public: public:
FancyTabWidget(QWidget *parent = 0); FancyTabWidget(QWidget *parent = 0);
void insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label); void insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label, bool hasMenu);
void removeTab(int index); void removeTab(int index);
void setBackgroundBrush(const QBrush &brush); void setBackgroundBrush(const QBrush &brush);
void addCornerWidget(QWidget *widget); void addCornerWidget(QWidget *widget);
@@ -161,6 +158,7 @@ public:
signals: signals:
void currentAboutToShow(int index); void currentAboutToShow(int index);
void currentChanged(int index); void currentChanged(int index);
void menuTriggered(int index, QMouseEvent *event);
void topAreaClicked(Qt::MouseButton button, Qt::KeyboardModifiers modifiers); void topAreaClicked(Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
public slots: public slots:

View File

@@ -32,6 +32,12 @@ IMode::IMode(QObject *parent)
{ {
} }
IMode::~IMode()
{
if (m_menu)
delete m_menu;
}
void IMode::setEnabled(bool enabled) void IMode::setEnabled(bool enabled)
{ {
if (m_isEnabled == enabled) if (m_isEnabled == enabled)

View File

@@ -29,6 +29,7 @@
#include "id.h" #include "id.h"
#include <QIcon> #include <QIcon>
#include <QMenu>
namespace Core { namespace Core {
@@ -39,18 +40,21 @@ class CORE_EXPORT IMode : public IContext
public: public:
IMode(QObject *parent = 0); IMode(QObject *parent = 0);
~IMode();
QString displayName() const { return m_displayName; } QString displayName() const { return m_displayName; }
QIcon icon() const { return m_icon; } QIcon icon() const { return m_icon; }
int priority() const { return m_priority; } int priority() const { return m_priority; }
Id id() const { return m_id; } Id id() const { return m_id; }
bool isEnabled() const; bool isEnabled() const;
QMenu *menu() const { return m_menu; }
void setEnabled(bool enabled); void setEnabled(bool enabled);
void setDisplayName(const QString &displayName) { m_displayName = displayName; } void setDisplayName(const QString &displayName) { m_displayName = displayName; }
void setIcon(const QIcon &icon) { m_icon = icon; } void setIcon(const QIcon &icon) { m_icon = icon; }
void setPriority(int priority) { m_priority = priority; } void setPriority(int priority) { m_priority = priority; }
void setId(Id id) { m_id = id; } void setId(Id id) { m_id = id; }
void setMenu(QMenu *menu) { m_menu = menu; }
signals: signals:
void enabledStateChanged(bool enabled); void enabledStateChanged(bool enabled);
@@ -58,6 +62,7 @@ signals:
private: private:
QString m_displayName; QString m_displayName;
QIcon m_icon; QIcon m_icon;
QMenu *m_menu = nullptr;
int m_priority; int m_priority;
Id m_id; Id m_id;
bool m_isEnabled; bool m_isEnabled;

View File

@@ -42,6 +42,7 @@
#include <QAction> #include <QAction>
#include <QDebug> #include <QDebug>
#include <QMap> #include <QMap>
#include <QMouseEvent>
#include <QVector> #include <QVector>
namespace Core { namespace Core {
@@ -57,6 +58,8 @@ namespace Core {
struct ModeManagerPrivate struct ModeManagerPrivate
{ {
void showMenu(int index, QMouseEvent *event);
Internal::MainWindow *m_mainWindow; Internal::MainWindow *m_mainWindow;
Internal::FancyTabWidget *m_modeStack; Internal::FancyTabWidget *m_modeStack;
Internal::FancyActionBar *m_actionBar; Internal::FancyActionBar *m_actionBar;
@@ -81,6 +84,12 @@ static int indexOf(Id id)
return -1; return -1;
} }
void ModeManagerPrivate::showMenu(int index, QMouseEvent *event)
{
QTC_ASSERT(m_modes.at(index)->menu(), return);
m_modes.at(index)->menu()->popup(event->globalPos());
}
ModeManager::ModeManager(Internal::MainWindow *mainWindow, ModeManager::ModeManager(Internal::MainWindow *mainWindow,
Internal::FancyTabWidget *modeStack) Internal::FancyTabWidget *modeStack)
{ {
@@ -98,6 +107,8 @@ ModeManager::ModeManager(Internal::MainWindow *mainWindow,
this, &ModeManager::currentTabAboutToChange); this, &ModeManager::currentTabAboutToChange);
connect(d->m_modeStack, &Internal::FancyTabWidget::currentChanged, connect(d->m_modeStack, &Internal::FancyTabWidget::currentChanged,
this, &ModeManager::currentTabChanged); this, &ModeManager::currentTabChanged);
connect(d->m_modeStack, &Internal::FancyTabWidget::menuTriggered,
this, [](int index, QMouseEvent *e) { d->showMenu(index, e); });
} }
void ModeManager::init() void ModeManager::init()
@@ -153,7 +164,8 @@ void ModeManager::objectAdded(QObject *obj)
++index; ++index;
d->m_modes.insert(index, mode); d->m_modes.insert(index, mode);
d->m_modeStack->insertTab(index, mode->widget(), mode->icon(), mode->displayName()); d->m_modeStack->insertTab(index, mode->widget(), mode->icon(), mode->displayName(),
mode->menu() != nullptr);
d->m_modeStack->setTabEnabled(index, mode->isEnabled()); d->m_modeStack->setTabEnabled(index, mode->isEnabled());
// Register mode shortcut // Register mode shortcut