Commit 5a3dfb76 authored by Eike Ziller's avatar Eike Ziller
Browse files

Move progress indicators out of mode bar.



This both allows the mode bar to be hidden optionally, and can give the
progress information a bit more room (e.g. for titles).

Progress information can either be shown in "pop up" windows in the
lower left corner of the main window (hiding window contents below),
or in a summary progress bar in the bottom right corner of the status bar.
Hovering the summary progress bar temporarily pops up the detailed
progress information. Keyboard can be used to switch between the two
views.

Change-Id: Ic6d6ab4fd43906e84b480c8ddf8eae5f5852e1f3
Reviewed-by: default avatarErik Verbruggen <erik.verbruggen@digia.com>
parent c5f7cc62
......@@ -50,6 +50,7 @@ class QTCREATOR_UTILS_EXPORT StyleHelper
{
public:
static const unsigned int DEFAULT_BASE_COLOR = 0x666666;
static const int progressFadeAnimationDuration = 600;
// Height of the project explorer navigation bar
static int navigationWidgetHeight() { return 24; }
......
......@@ -51,6 +51,7 @@
#include "outputpane.h"
#include "plugindialog.h"
#include "progressmanager_p.h"
#include "progressview.h"
#include "shortcutsettings.h"
#include "vcsmanager.h"
#include "scriptmanager_p.h"
......@@ -202,7 +203,6 @@ MainWindow::MainWindow() :
m_modeStack = new FancyTabWidget(this);
m_modeManager = new ModeManager(this, m_modeStack);
m_modeManager->addWidget(m_progressManager->progressView());
m_statusBarManager = new StatusBarManager(this);
m_messageManager = new MessageManager;
m_editorManager = new EditorManager(this);
......@@ -210,6 +210,9 @@ MainWindow::MainWindow() :
m_externalToolManager = new ExternalToolManager();
setCentralWidget(m_modeStack);
m_progressManager->progressView()->setParent(this);
m_progressManager->progressView()->setReferenceWidget(m_modeStack->statusBar());
connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(updateFocusWidget(QWidget*,QWidget*)));
// Add a small Toolbutton for toggling the navigation widget
......@@ -297,10 +300,10 @@ MainWindow::~MainWindow()
delete m_editorManager;
m_editorManager = 0;
delete m_statusBarManager;
m_statusBarManager = 0;
delete m_progressManager;
m_progressManager = 0;
delete m_statusBarManager;
m_statusBarManager = 0;
ExtensionSystem::PluginManager::removeObject(m_coreImpl);
delete m_coreImpl;
m_coreImpl = 0;
......@@ -327,7 +330,7 @@ bool MainWindow::init(QString *errorMessage)
ExtensionSystem::PluginManager::addObject(m_coreImpl);
m_statusBarManager->init();
m_modeManager->init();
m_progressManager->init();
m_progressManager->init(); // needs the status bar manager
ExtensionSystem::PluginManager::addObject(m_generalSettings);
ExtensionSystem::PluginManager::addObject(m_shortcutSettings);
......
......@@ -30,10 +30,13 @@
#include "futureprogress.h"
#include "progressbar.h"
#include <utils/stylehelper.h>
#include <QCoreApplication>
#include <QFutureWatcher>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QPainter>
#include <QSequentialAnimationGroup>
#include <QTimer>
......@@ -66,13 +69,14 @@ public:
FutureProgress::KeepOnFinishType m_keep;
bool m_waitingForUserInteraction;
FutureProgress *m_q;
bool m_fadeStarting;
bool m_isFading;
};
FutureProgressPrivate::FutureProgressPrivate(FutureProgress *q) :
m_progress(new Internal::ProgressBar), m_widget(0), m_widgetLayout(new QHBoxLayout),
m_keep(FutureProgress::HideOnFinish), m_waitingForUserInteraction(false),
m_q(q), m_isFading(false)
m_q(q), m_fadeStarting(false), m_isFading(false)
{
}
......@@ -218,17 +222,19 @@ void FutureProgress::setFinished()
d->m_progress->setFinished(true);
if (d->m_watcher.future().isCanceled())
if (d->m_watcher.future().isCanceled()) {
d->m_progress->setError(true);
else
emit hasErrorChanged();
} else {
d->m_progress->setError(false);
}
emit finished();
d->tryToFadeAway();
}
void FutureProgressPrivate::tryToFadeAway()
{
if (m_isFading)
if (m_fadeStarting)
return;
if (m_keep == FutureProgress::KeepOnFinishTillUserInteraction
|| (m_keep == FutureProgress::HideOnFinish && m_progress->hasError())) {
......@@ -236,10 +242,10 @@ void FutureProgressPrivate::tryToFadeAway()
//eventfilter is needed to get user interaction
//events to start QTimer::singleShot later
qApp->installEventFilter(m_q);
m_isFading = true;
m_fadeStarting = true;
} else if (m_keep == FutureProgress::HideOnFinish) {
QTimer::singleShot(shortNotificationTimeout, this, SLOT(fadeAway()));
m_isFading = true;
m_fadeStarting = true;
}
}
......@@ -288,6 +294,13 @@ void FutureProgress::mousePressEvent(QMouseEvent *event)
QWidget::mousePressEvent(event);
}
void FutureProgress::paintEvent(QPaintEvent *)
{
QPainter p(this);
QLinearGradient grad = Utils::StyleHelper::statusBarGradient(rect());
p.fillRect(rect(), grad);
}
/*!
\fn bool FutureProgress::hasError() const
Returns the error state of this progress indicator.
......@@ -328,15 +341,27 @@ QWidget *FutureProgress::widget() const
return d->m_widget;
}
bool FutureProgress::isFading() const
{
return d->m_isFading;
}
QSize FutureProgress::sizeHint() const
{
return QSize(100, minimumHeight());
}
void FutureProgressPrivate::fadeAway()
{
m_isFading = true;
QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect;
opacityEffect->setOpacity(1.);
m_q->setGraphicsEffect(opacityEffect);
QSequentialAnimationGroup *group = new QSequentialAnimationGroup(this);
QPropertyAnimation *animation = new QPropertyAnimation(opacityEffect, "opacity");
animation->setDuration(600);
animation->setDuration(Utils::StyleHelper::progressFadeAnimationDuration);
animation->setEndValue(0.);
group->addAnimation(animation);
animation = new QPropertyAnimation(m_q, "maximumHeight");
......@@ -345,9 +370,10 @@ void FutureProgressPrivate::fadeAway()
animation->setStartValue(m_q->sizeHint().height());
animation->setEndValue(0.0);
group->addAnimation(animation);
group->start(QAbstractAnimation::DeleteWhenStopped);
connect(group, SIGNAL(finished()), m_q, SIGNAL(removeMe()));
group->start(QAbstractAnimation::DeleteWhenStopped);
emit m_q->fadeStarted();
}
} // namespace Core
......
......@@ -71,14 +71,21 @@ public:
void setWidget(QWidget *widget);
QWidget *widget() const;
bool isFading() const;
QSize sizeHint() const;
signals:
void clicked();
void finished();
void canceled();
void removeMe();
void hasErrorChanged();
void fadeStarted();
protected:
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *);
private slots:
void updateToolTip(const QString &);
......@@ -90,6 +97,7 @@ private slots:
void setProgressText(const QString &text);
private:
friend class FutureProgressPrivate; // for sending signal
FutureProgressPrivate *d;
};
......
......@@ -44,8 +44,9 @@ using namespace Core::Internal;
#define CANCELBUTTON_SIZE 15
ProgressBar::ProgressBar(QWidget *parent)
: QWidget(parent), m_minimum(1), m_maximum(100), m_value(1), m_cancelButtonFader(0),
m_finished(false), m_error(false)
: QWidget(parent), m_titleVisible(true), m_separatorVisible(true), m_progressHeight(0),
m_minimum(1), m_maximum(100), m_value(1), m_cancelButtonFader(0), m_finished(false),
m_error(false)
{
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
setMouseTracking(true);
......@@ -131,6 +132,45 @@ void ProgressBar::setTitle(const QString &title)
update();
}
void ProgressBar::setTitleVisible(bool visible)
{
if (m_titleVisible == visible)
return;
m_titleVisible = visible;
update();
}
bool ProgressBar::isTitleVisible() const
{
return m_titleVisible;
}
void ProgressBar::setSeparatorVisible(bool visible)
{
if (m_separatorVisible == visible)
return;
m_separatorVisible = visible;
update();
}
bool ProgressBar::isSeparatorVisible() const
{
return m_separatorVisible;
}
void ProgressBar::setCancelEnabled(bool enabled)
{
if (m_cancelEnabled == enabled)
return;
m_cancelEnabled = enabled;
update();
}
bool ProgressBar::isCancelEnabled() const
{
return m_cancelEnabled;
}
void ProgressBar::setError(bool on)
{
m_error = on;
......@@ -149,19 +189,22 @@ namespace { const int INDENT = 6; }
void ProgressBar::mousePressEvent(QMouseEvent *event)
{
QFont boldFont(font());
boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
boldFont.setBold(true);
QFontMetrics fm(boldFont);
int h = fm.height();
QRect rect(INDENT - 1, h+8, size().width()-2*INDENT + 1, m_progressHeight-1);
QRect cancelRect(rect.adjusted(rect.width() - CANCELBUTTON_SIZE, 1, -1, 0));
if (event->modifiers() == Qt::NoModifier
&& cancelRect.contains(event->pos())) {
event->accept();
emit clicked();
return;
if (m_cancelEnabled) {
QFont boldFont(font());
boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
boldFont.setBold(true);
QFontMetrics fm(boldFont);
int titleHeight = m_titleVisible ? fm.height() : 0;
int separatorHeight = m_separatorVisible ? 2 : 0;
QRect rect(INDENT - 1, titleHeight + separatorHeight + 6, size().width()-2*INDENT + 1, m_progressHeight-1);
QRect cancelRect(rect.adjusted(rect.width() - CANCELBUTTON_SIZE, 1, -1, 0));
if (event->modifiers() == Qt::NoModifier
&& cancelRect.contains(event->pos())) {
event->accept();
emit clicked();
return;
}
}
QWidget::mousePressEvent(event);
}
......@@ -198,37 +241,43 @@ void ProgressBar::paintEvent(QPaintEvent *)
p.setFont(boldFont);
QFontMetrics fm(boldFont);
// Draw separator
int h = fm.height();
p.setPen(Utils::StyleHelper::sidebarShadow());
p.drawLine(0,0, size().width(), 0);
p.setPen(Utils::StyleHelper::sidebarHighlight());
p.drawLine(1, 1, size().width(), 1);
QRect textBounds = fm.boundingRect(m_title);
textBounds.moveCenter(rect().center());
int alignment = Qt::AlignHCenter;
int titleHeight = m_titleVisible ? fm.height() : 0;
int textSpace = rect().width() - 8;
// If there is not enough room when centered, we left align and
// elide the text
QString elidedtitle = fm.elidedText(m_title, Qt::ElideRight, textSpace);
// Draw separator
int separatorHeight = m_separatorVisible ? 2 : 0;
if (m_separatorVisible) {
p.setPen(Utils::StyleHelper::sidebarShadow());
p.drawLine(0,0, size().width(), 0);
QRect textRect = rect().adjusted(3, 1, -3, 0);
textRect.setHeight(h+5);
p.setPen(Utils::StyleHelper::sidebarHighlight());
p.drawLine(1, 1, size().width(), 1);
}
p.setPen(QColor(0, 0, 0, 120));
p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle);
p.translate(0, -1);
p.setPen(Utils::StyleHelper::panelTextColor());
p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle);
p.translate(0, 1);
if (m_titleVisible) {
QRect textBounds = fm.boundingRect(m_title);
textBounds.moveCenter(rect().center());
int alignment = Qt::AlignHCenter;
int textSpace = rect().width() - 8;
// If there is not enough room when centered, we left align and
// elide the text
QString elidedtitle = fm.elidedText(m_title, Qt::ElideRight, textSpace);
QRect textRect = rect().adjusted(3, separatorHeight - 1, -3, 0);
textRect.setHeight(titleHeight+5);
p.setPen(QColor(0, 0, 0, 120));
p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle);
p.translate(0, -1);
p.setPen(Utils::StyleHelper::panelTextColor());
p.drawText(textRect, alignment | Qt::AlignBottom, elidedtitle);
p.translate(0, 1);
}
m_progressHeight = PROGRESSBAR_HEIGHT;
m_progressHeight += ((m_progressHeight % 2) + 1) % 2; // make odd
// draw outer rect
QRect rect(INDENT - 1, h+6, size().width()-2*INDENT + 1, m_progressHeight-1);
QRect rect(INDENT - 1, titleHeight + separatorHeight + 4, size().width()-2*INDENT + 1, m_progressHeight-1);
p.setPen(Utils::StyleHelper::panelTextColor());
Utils::StyleHelper::drawCornerImage(bar, &p, rect, 4, 2, 3, 2);
......@@ -272,31 +321,33 @@ void ProgressBar::paintEvent(QPaintEvent *)
p.drawPoint(inner.bottomLeft());
p.drawPoint(inner.bottomRight());
// Draw cancel button
p.setOpacity(m_cancelButtonFader);
if (value() < maximum() && !m_error) {
QRect cancelRect(rect.adjusted(rect.width() - CANCELBUTTON_SIZE, 1, -1, 0));
bool hover = cancelRect.contains(mapFromGlobal(QCursor::pos()));
QLinearGradient grad(cancelRect.topLeft(), cancelRect.bottomLeft());
int intensity = hover ? 90 : 70;
QColor buttonColor(intensity, intensity, intensity, 255);
grad.setColorAt(0, buttonColor.lighter(130));
grad.setColorAt(1, buttonColor.darker(130));
p.setPen(Qt::NoPen);
p.setBrush(grad);
p.drawRect(cancelRect.adjusted(1, 1, -1, -1));
p.setPen(QPen(QColor(0, 0, 0, 30)));
p.drawLine(cancelRect.topLeft() + QPoint(0,1), cancelRect.bottomLeft() + QPoint(0,-1));
p.setPen(QPen(QColor(0, 0, 0, 120)));
p.drawLine(cancelRect.topLeft() + QPoint(1,1), cancelRect.bottomLeft() + QPoint(1,-1));
p.setPen(QPen(QColor(255, 255, 255, 30)));
p.drawLine(cancelRect.topLeft() + QPoint(2,1), cancelRect.bottomLeft() + QPoint(2,-1));
p.setPen(QPen(hover ? Utils::StyleHelper::panelTextColor() : QColor(180, 180, 180), 1));
p.setRenderHint(QPainter::Antialiasing);
p.translate(0.5, 0.5);
p.drawLine(cancelRect.center()+QPoint(-1,-2), cancelRect.center()+QPoint(+3,+2));
p.drawLine(cancelRect.center()+QPoint(+3,-2), cancelRect.center()+QPoint(-1,+2));
if (m_cancelEnabled) {
// Draw cancel button
p.setOpacity(m_cancelButtonFader);
if (value() < maximum() && !m_error) {
QRect cancelRect(rect.adjusted(rect.width() - CANCELBUTTON_SIZE, 1, -1, 0));
bool hover = cancelRect.contains(mapFromGlobal(QCursor::pos()));
QLinearGradient grad(cancelRect.topLeft(), cancelRect.bottomLeft());
int intensity = hover ? 90 : 70;
QColor buttonColor(intensity, intensity, intensity, 255);
grad.setColorAt(0, buttonColor.lighter(130));
grad.setColorAt(1, buttonColor.darker(130));
p.setPen(Qt::NoPen);
p.setBrush(grad);
p.drawRect(cancelRect.adjusted(1, 1, -1, -1));
p.setPen(QPen(QColor(0, 0, 0, 30)));
p.drawLine(cancelRect.topLeft() + QPoint(0,1), cancelRect.bottomLeft() + QPoint(0,-1));
p.setPen(QPen(QColor(0, 0, 0, 120)));
p.drawLine(cancelRect.topLeft() + QPoint(1,1), cancelRect.bottomLeft() + QPoint(1,-1));
p.setPen(QPen(QColor(255, 255, 255, 30)));
p.drawLine(cancelRect.topLeft() + QPoint(2,1), cancelRect.bottomLeft() + QPoint(2,-1));
p.setPen(QPen(hover ? Utils::StyleHelper::panelTextColor() : QColor(180, 180, 180), 1));
p.setRenderHint(QPainter::Antialiasing);
p.translate(0.5, 0.5);
p.drawLine(cancelRect.center()+QPoint(-1,-2), cancelRect.center()+QPoint(+3,+2));
p.drawLine(cancelRect.center()+QPoint(+3,-2), cancelRect.center()+QPoint(-1,+2));
}
}
}
......@@ -48,6 +48,12 @@ public:
QString title() const;
void setTitle(const QString &title);
void setTitleVisible(bool visible);
bool isTitleVisible() const;
void setSeparatorVisible(bool visible);
bool isSeparatorVisible() const;
void setCancelEnabled(bool enabled);
bool isCancelEnabled() const;
void setError(bool on);
bool hasError() const;
QSize sizeHint() const;
......@@ -75,6 +81,9 @@ private:
QImage bar;
QString m_text;
QString m_title;
bool m_titleVisible;
bool m_separatorVisible;
bool m_cancelEnabled;
int m_progressHeight;
int m_minimum;
int m_maximum;
......
......@@ -28,10 +28,28 @@
****************************************************************************/
#include "progressmanager_p.h"
#include "progressbar.h"
#include "progressview.h"
#include "../actionmanager/actionmanager.h"
#include "../icontext.h"
#include "../coreconstants.h"
#include "../icore.h"
#include "../statusbarwidget.h"
#include <extensionsystem/pluginmanager.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <QAction>
#include <QEvent>
#include <QHBoxLayout>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
#include <QStyleOption>
#include <QTimer>
using namespace Core;
using namespace Core::Internal;
......@@ -243,17 +261,70 @@ using namespace Core::Internal;
ProgressManagerPrivate::ProgressManagerPrivate(QObject *parent)
: ProgressManager(parent),
m_applicationTask(0)
m_applicationTask(0),
m_opacityEffect(new QGraphicsOpacityEffect(this)),
m_progressViewPinned(false),
m_hovered(false)
{
m_progressView = new ProgressView;
connect(m_progressView, SIGNAL(hasErrorChanged()), this, SLOT(updateSummaryProgressBar()));
connect(m_progressView, SIGNAL(fadeOfLastProgressStarted()), this, SLOT(updateSummaryProgressBar()));
// withDelay, so the statusBarWidget has the chance to get the enter event
connect(m_progressView, SIGNAL(hoveredChanged(bool)), this, SLOT(updateVisibilityWithDelay()));
connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(cancelAllRunningTasks()));
}
ProgressManagerPrivate::~ProgressManagerPrivate()
{
ExtensionSystem::PluginManager::removeObject(m_statusBarWidgetContainer);
delete m_statusBarWidgetContainer;
cleanup();
}
void ProgressManagerPrivate::init()
{
m_statusBarWidgetContainer = new Core::StatusBarWidget;
m_statusBarWidget = new QWidget;
QHBoxLayout *layout = new QHBoxLayout(m_statusBarWidget);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
m_statusBarWidget->setLayout(layout);
m_summaryProgressBar = new ProgressBar(m_statusBarWidget);
m_summaryProgressBar->setMinimumWidth(70);
m_summaryProgressBar->setTitleVisible(false);
m_summaryProgressBar->setSeparatorVisible(false);
m_summaryProgressBar->setCancelEnabled(false);
m_summaryProgressBar->setGraphicsEffect(m_opacityEffect);
m_summaryProgressBar->setVisible(!m_progressViewPinned);
layout->addWidget(m_summaryProgressBar);
ToggleButton *toggleButton = new ToggleButton(m_statusBarWidget);
layout->addWidget(toggleButton);
m_statusBarWidgetContainer->setWidget(m_statusBarWidget);
m_statusBarWidgetContainer->setPosition(Core::StatusBarWidget::RightCorner);
ExtensionSystem::PluginManager::addObject(m_statusBarWidgetContainer);
m_statusBarWidget->installEventFilter(this);
QAction *toggleProgressView = new QAction(tr("Toggle progress details"), this);
toggleProgressView->setCheckable(true);
toggleProgressView->setChecked(m_progressViewPinned);
// we have to set an transparent icon to prevent the tool button to show text
QPixmap p(1, 1);
p.fill(Qt::transparent);
toggleProgressView->setIcon(QIcon(p));
Command *cmd = ActionManager::registerAction(toggleProgressView,
Id("QtCreator.ToggleProgressDetails"),
Context(Constants::C_GLOBAL));
cmd->setDefaultKeySequence(QKeySequence(Utils::HostOsInfo::isMacHost()
? tr("Ctrl+Shift+0")
: tr("Alt+Shift+0")));
connect(toggleProgressView, SIGNAL(toggled(bool)), this, SLOT(progressDetailsToggled(bool)));
toggleButton->setDefaultAction(cmd->action());
m_progressView->setVisible(m_progressViewPinned);
initInternal();
}
void ProgressManagerPrivate::cancelTasks(const QString &type)
{
bool found = false;
......@@ -271,8 +342,23 @@ void ProgressManagerPrivate::cancelTasks(const QString &type)
delete task.key();
task = m_runningTasks.erase(task);
}
if (found)
if (found) {
updateSummaryProgressBar();
emit allTasksFinished(type);
}
}
bool ProgressManagerPrivate::eventFilter(QObject *obj, QEvent *event)
{
if (obj == m_statusBarWidget && event->type() == QEvent::Enter) {
m_hovered = true;
updateVisibility();
} else if (obj == m_statusBarWidget && event->type() == QEvent::Leave) {
m_hovered = false;
// give the progress view the chance to get the mouse enter event
updateVisibilityWithDelay();
}
return false;
}
void ProgressManagerPrivate::cancelAllRunningTasks()
......@@ -287,6 +373,7 @@ void ProgressManagerPrivate::cancelAllRunningTasks()
++task;
}
m_runningTasks.clear();
updateSummaryProgressBar();
}
FutureProgress *ProgressManagerPrivate::addTask(const QFuture<void> &future, const QString &title,
......@@ -294,6 +381,8 @@ FutureProgress *ProgressManagerPrivate::addTask(const QFuture<void> &future, con
{
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();