diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index e41d9a42c6a..75be2220adc 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -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; } diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 8891d9166cd..0eaaf72a9a2 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -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); diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.cpp b/src/plugins/coreplugin/progressmanager/futureprogress.cpp index d0e78fece90..a0644021738 100644 --- a/src/plugins/coreplugin/progressmanager/futureprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/futureprogress.cpp @@ -30,10 +30,13 @@ #include "futureprogress.h" #include "progressbar.h" +#include + #include #include #include #include +#include #include #include @@ -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 diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.h b/src/plugins/coreplugin/progressmanager/futureprogress.h index b8cfec78ca2..cbc3a25ba3c 100644 --- a/src/plugins/coreplugin/progressmanager/futureprogress.h +++ b/src/plugins/coreplugin/progressmanager/futureprogress.h @@ -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; }; diff --git a/src/plugins/coreplugin/progressmanager/progressbar.cpp b/src/plugins/coreplugin/progressmanager/progressbar.cpp index d61b0e09a5f..6a2c72be471 100644 --- a/src/plugins/coreplugin/progressmanager/progressbar.cpp +++ b/src/plugins/coreplugin/progressmanager/progressbar.cpp @@ -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 (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; + 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); + int titleHeight = m_titleVisible ? fm.height() : 0; + // Draw separator - int h = fm.height(); - p.setPen(Utils::StyleHelper::sidebarShadow()); - p.drawLine(0,0, size().width(), 0); + int separatorHeight = m_separatorVisible ? 2 : 0; + if (m_separatorVisible) { + p.setPen(Utils::StyleHelper::sidebarShadow()); + p.drawLine(0,0, size().width(), 0); - p.setPen(Utils::StyleHelper::sidebarHighlight()); - p.drawLine(1, 1, size().width(), 1); + 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; + 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); + 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, 1, -3, 0); - textRect.setHeight(h+5); + 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); + 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 (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)); + 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)); + 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)); + } } } diff --git a/src/plugins/coreplugin/progressmanager/progressbar.h b/src/plugins/coreplugin/progressmanager/progressbar.h index bbc5789fcc0..ef88731a95a 100644 --- a/src/plugins/coreplugin/progressmanager/progressbar.h +++ b/src/plugins/coreplugin/progressmanager/progressbar.h @@ -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; diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp index 94ee59406b8..07c155a4c8f 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp @@ -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 +#include #include +#include + +#include +#include +#include +#include +#include +#include +#include +#include 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 &future, const QString &title, @@ -294,6 +381,8 @@ FutureProgress *ProgressManagerPrivate::addTask(const QFuture &future, con { QFutureWatcher *watcher = new QFutureWatcher(); m_runningTasks.insert(watcher, type); + connect(watcher, SIGNAL(progressRangeChanged(int,int)), this, SLOT(updateSummaryProgressBar())); + connect(watcher, SIGNAL(progressValueChanged(int)), this, SLOT(updateSummaryProgressBar())); connect(watcher, SIGNAL(finished()), this, SLOT(taskFinished())); if (flags & ShowInApplicationIcon) { if (m_applicationTask) @@ -312,7 +401,7 @@ FutureProgress *ProgressManagerPrivate::addTask(const QFuture &future, con return m_progressView->addTask(future, title, type, flags); } -QWidget *ProgressManagerPrivate::progressView() +ProgressView *ProgressManagerPrivate::progressView() { return m_progressView; } @@ -327,6 +416,7 @@ void ProgressManagerPrivate::taskFinished() QString type = m_runningTasks.value(task); m_runningTasks.remove(task); delete task; + updateSummaryProgressBar(); if (!m_runningTasks.key(type, 0)) emit allTasksFinished(type); @@ -341,3 +431,96 @@ void ProgressManagerPrivate::disconnectApplicationTask() setApplicationProgressVisible(false); m_applicationTask = 0; } + +void ProgressManagerPrivate::updateSummaryProgressBar() +{ + m_summaryProgressBar->setError(m_progressView->hasError()); + updateVisibility(); + if (m_runningTasks.isEmpty()) { + m_summaryProgressBar->setFinished(true); + if (m_progressView->isEmpty() || m_progressView->isFading()) + fadeAway(); + return; + } + + stopFade(); + + m_summaryProgressBar->setFinished(false); + QMapIterator *, QString> it(m_runningTasks); + int range = 0; + int value = 0; + while (it.hasNext()) { + it.next(); + QFutureWatcher *watcher = it.key(); + int min = watcher->progressMinimum(); + range += watcher->progressMaximum() - min; + value += watcher->progressValue() - min; + } + m_summaryProgressBar->setRange(0, range); + m_summaryProgressBar->setValue(value); +} + +void ProgressManagerPrivate::fadeAway() +{ + stopFade(); + m_opacityAnimation = new QPropertyAnimation(m_opacityEffect, "opacity"); + m_opacityAnimation->setDuration(Utils::StyleHelper::progressFadeAnimationDuration); + m_opacityAnimation->setEndValue(0.); + connect(m_opacityAnimation, SIGNAL(finished()), this, SLOT(fadeFinished())); + m_opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ProgressManagerPrivate::stopFade() +{ + if (m_opacityAnimation) { + m_opacityAnimation->stop(); + m_opacityEffect->setOpacity(1.); + delete m_opacityAnimation; + } +} + +void ProgressManagerPrivate::updateVisibility() +{ + m_progressView->setVisible(m_progressViewPinned || m_hovered || m_progressView->isHovered()); + m_summaryProgressBar->setVisible((!m_runningTasks.isEmpty() || !m_progressView->isEmpty()) + && !m_progressViewPinned); +} + +void ProgressManagerPrivate::updateVisibilityWithDelay() +{ + QTimer::singleShot(150, this, SLOT(updateVisibility())); +} + +void ProgressManagerPrivate::fadeFinished() +{ + m_summaryProgressBar->setVisible(false); + m_opacityEffect->setOpacity(1.); +} + +void ProgressManagerPrivate::progressDetailsToggled(bool checked) +{ + m_progressViewPinned = checked; + updateVisibility(); +} + +ToggleButton::ToggleButton(QWidget *parent) + : QToolButton(parent) +{ + setToolButtonStyle(Qt::ToolButtonIconOnly); +} + +QSize ToggleButton::sizeHint() const +{ + return QSize(12, 12); +} + +void ToggleButton::paintEvent(QPaintEvent *event) +{ + QToolButton::paintEvent(event); + QPainter p(this); + QStyle *s = style(); + QStyleOption arrowOpt; + arrowOpt.initFrom(this); + arrowOpt.rect = QRect(rect().center().x() - 3, rect().center().y() - 6, 9, 9); + s->drawPrimitive(QStyle::PE_IndicatorArrowUp, &arrowOpt, &p, this); +} diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_mac.mm b/src/plugins/coreplugin/progressmanager/progressmanager_mac.mm index 0aa0bc849e2..a511fc8d70f 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_mac.mm +++ b/src/plugins/coreplugin/progressmanager/progressmanager_mac.mm @@ -29,7 +29,7 @@ #include "progressmanager_p.h" -void Core::Internal::ProgressManagerPrivate::init() +void Core::Internal::ProgressManagerPrivate::initInternal() { } diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_p.h b/src/plugins/coreplugin/progressmanager/progressmanager_p.h index edba64e40b8..873789008cb 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_p.h +++ b/src/plugins/coreplugin/progressmanager/progressmanager_p.h @@ -32,13 +32,20 @@ #include "progressmanager.h" -#include -#include #include +#include +#include +#include +#include +#include namespace Core { + +class StatusBarWidget; + namespace Internal { +class ProgressBar; class ProgressView; class ProgressManagerPrivate : public Core::ProgressManager @@ -54,11 +61,14 @@ public: ProgressFlags flags); void setApplicationLabel(const QString &text); - QWidget *progressView(); + ProgressView *progressView(); public slots: void cancelTasks(const QString &type); +protected: + bool eventFilter(QObject *obj, QEvent *event); + private slots: void taskFinished(); void cancelAllRunningTasks(); @@ -66,11 +76,36 @@ private slots: void setApplicationProgressValue(int value); void setApplicationProgressVisible(bool visible); void disconnectApplicationTask(); + void updateSummaryProgressBar(); + void fadeAway(); + void fadeFinished(); + void progressDetailsToggled(bool checked); + void updateVisibility(); + void updateVisibilityWithDelay(); private: + void initInternal(); + void stopFade(); + QPointer m_progressView; QMap *, QString> m_runningTasks; QFutureWatcher *m_applicationTask; + Core::StatusBarWidget *m_statusBarWidgetContainer; + QWidget *m_statusBarWidget; + ProgressBar *m_summaryProgressBar; + QGraphicsOpacityEffect *m_opacityEffect; + QPointer m_opacityAnimation; + bool m_progressViewPinned; + bool m_hovered; +}; + +class ToggleButton : public QToolButton +{ + Q_OBJECT +public: + ToggleButton(QWidget *parent); + QSize sizeHint() const; + void paintEvent(QPaintEvent *event); }; } // namespace Internal diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp index 1fef8712ec6..be858eb3945 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp @@ -84,7 +84,7 @@ static inline HWND hwndOfWidget(const QWidget *w) } #endif -void Core::Internal::ProgressManagerPrivate::init() +void Core::Internal::ProgressManagerPrivate::initInternal() { CoInitialize(NULL); HRESULT hRes = CoCreateInstance(CLSID_TaskbarList, @@ -164,7 +164,7 @@ void Core::Internal::ProgressManagerPrivate::setApplicationProgressVisible(bool #else -void Core::Internal::ProgressManagerPrivate::init() +void Core::Internal::ProgressManagerPrivate::initInternal() { } diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_x11.cpp b/src/plugins/coreplugin/progressmanager/progressmanager_x11.cpp index 337f0033f0f..2650f5d1cf6 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_x11.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager_x11.cpp @@ -29,7 +29,7 @@ #include "progressmanager_p.h" -void Core::Internal::ProgressManagerPrivate::init() +void Core::Internal::ProgressManagerPrivate::initInternal() { } diff --git a/src/plugins/coreplugin/progressmanager/progressview.cpp b/src/plugins/coreplugin/progressmanager/progressview.cpp index f2a4a9e31c0..695037a799c 100644 --- a/src/plugins/coreplugin/progressmanager/progressview.cpp +++ b/src/plugins/coreplugin/progressmanager/progressview.cpp @@ -32,18 +32,22 @@ #include +#include #include using namespace Core; using namespace Core::Internal; +static const int PROGRESS_WIDTH = 100; + ProgressView::ProgressView(QWidget *parent) - : QWidget(parent) + : QWidget(parent), m_referenceWidget(0), m_hovered(false) { m_layout = new QVBoxLayout; setLayout(m_layout); - m_layout->setMargin(0); + m_layout->setContentsMargins(0, 0, 0, 1); m_layout->setSpacing(0); + m_layout->setSizeConstraint(QLayout::SetFixedSize); setWindowTitle(tr("Processes")); } @@ -59,7 +63,7 @@ FutureProgress *ProgressView::addTask(const QFuture &future, ProgressManager::ProgressFlags flags) { removeOldTasks(type); - if (m_taskList.size() == 3) + if (m_taskList.size() == 10) removeOneOldTask(); FutureProgress *progress = new FutureProgress(this); progress->setTitle(title); @@ -72,10 +76,76 @@ FutureProgress *ProgressView::addTask(const QFuture &future, progress->setKeepOnFinish(FutureProgress::KeepOnFinishTillUserInteraction); else progress->setKeepOnFinish(FutureProgress::HideOnFinish); + connect(progress, SIGNAL(hasErrorChanged()), this, SIGNAL(hasErrorChanged())); connect(progress, SIGNAL(removeMe()), this, SLOT(slotRemoveTask())); + connect(progress, SIGNAL(fadeStarted()), this, SLOT(checkForLastProgressFading())); return progress; } +bool ProgressView::hasError() const +{ + foreach (FutureProgress *progress, m_taskList) + if (progress->hasError()) + return true; + return false; +} + +bool ProgressView::isFading() const +{ + if (m_taskList.isEmpty()) + return false; + foreach (FutureProgress *progress, m_taskList) { + if (!progress->isFading()) // we still have progress bars that are not fading, do nothing + return false; + } + return true; +} + +bool ProgressView::isEmpty() const +{ + return m_taskList.isEmpty(); +} + +bool ProgressView::isHovered() const +{ + return m_hovered; +} + +void ProgressView::setReferenceWidget(QWidget *widget) +{ + if (m_referenceWidget) + removeEventFilter(this); + m_referenceWidget = widget; + if (m_referenceWidget) + installEventFilter(this); + reposition(); +} + +bool ProgressView::event(QEvent *event) +{ + if (event->type() == QEvent::ParentAboutToChange && parentWidget()) { + parentWidget()->removeEventFilter(this); + } else if (event->type() == QEvent::ParentChange && parentWidget()) { + parentWidget()->installEventFilter(this); + } else if (event->type() == QEvent::Resize) { + reposition(); + } else if (event->type() == QEvent::Enter) { + m_hovered = true; + emit hoveredChanged(m_hovered); + } else if (event->type() == QEvent::Leave) { + m_hovered = false; + emit hoveredChanged(m_hovered); + } + return QWidget::event(event); +} + +bool ProgressView::eventFilter(QObject *obj, QEvent *event) +{ + if ((obj == parentWidget() || obj == m_referenceWidget) && event->type() == QEvent::Resize) + reposition(); + return false; +} + void ProgressView::removeOldTasks(const QString &type, bool keepOne) { bool firstFound = !keepOne; // start with false if we want to keep one @@ -99,6 +169,15 @@ void ProgressView::deleteTask(FutureProgress *progress) progress->deleteLater(); } +void ProgressView::reposition() +{ + if (!parentWidget() || !m_referenceWidget) + return; + QPoint topRightReferenceInParent = + m_referenceWidget->mapTo(parentWidget(), m_referenceWidget->rect().topRight()); + move(topRightReferenceInParent - rect().bottomRight()); +} + void ProgressView::removeOneOldTask() { if (m_taskList.isEmpty()) @@ -146,3 +225,9 @@ void ProgressView::slotRemoveTask() removeTask(progress); removeOldTasks(type, true); } + +void ProgressView::checkForLastProgressFading() +{ + if (isEmpty() || isFading()) + emit fadeOfLastProgressStarted(); +} diff --git a/src/plugins/coreplugin/progressmanager/progressview.h b/src/plugins/coreplugin/progressmanager/progressview.h index ca5204b5648..1cff2dcd17c 100644 --- a/src/plugins/coreplugin/progressmanager/progressview.h +++ b/src/plugins/coreplugin/progressmanager/progressview.h @@ -35,6 +35,7 @@ #include #include + QT_BEGIN_NAMESPACE class QVBoxLayout; QT_END_NAMESPACE @@ -59,17 +60,37 @@ public: const QString &type, ProgressManager::ProgressFlags flags); + bool hasError() const; + bool isFading() const; + bool isEmpty() const; + bool isHovered() const; + + void setReferenceWidget(QWidget *widget); + +protected: + bool event(QEvent *event); + bool eventFilter(QObject *obj, QEvent *event); + +signals: + void hasErrorChanged(); + void fadeOfLastProgressStarted(); + void hoveredChanged(bool hovered); + private slots: void slotRemoveTask(); + void checkForLastProgressFading(); private: void removeOldTasks(const QString &type, bool keepOne = false); void removeOneOldTask(); void removeTask(FutureProgress *task); void deleteTask(FutureProgress *task); + void reposition(); QVBoxLayout *m_layout; QList m_taskList; + QWidget *m_referenceWidget; + bool m_hovered; }; } // namespace Internal