add_library(dolphinstatic STATIC)
target_sources(dolphinstatic PRIVATE
+ animatedheightwidget.cpp
dolphinbookmarkhandler.cpp
dolphindockwidget.cpp
dolphinmainwindow.cpp
global.cpp
dolphin.qrc
+ animatedheightwidget.h
dolphinbookmarkhandler.h
dolphindockwidget.h
dolphinmainwindow.h
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
+
+#include "animatedheightwidget.h"
+
+#include <QGridLayout>
+#include <QPropertyAnimation>
+#include <QScrollArea>
+#include <QStyle>
+
+AnimatedHeightWidget::AnimatedHeightWidget(QWidget *parent)
+ : QWidget{parent}
+{
+ // Showing of this widget is normally animated. We hide it for now and make it small.
+ hide();
+ setMaximumHeight(0);
+
+ auto fillParentLayout = new QGridLayout(this);
+ fillParentLayout->setContentsMargins(0, 0, 0, 0);
+
+ // Put the contents into a QScrollArea. This prevents increasing the view width
+ // in case there is not enough available width for the contents.
+ m_contentsContainerParent = new QScrollArea(this);
+ fillParentLayout->addWidget(m_contentsContainerParent);
+ m_contentsContainerParent->setFrameShape(QFrame::NoFrame);
+ m_contentsContainerParent->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_contentsContainerParent->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_contentsContainerParent->setWidgetResizable(true);
+
+ setMinimumWidth(0);
+}
+
+QSize AnimatedHeightWidget::sizeHint() const
+{
+ return QSize{1, preferredHeight()};
+ // 1 as width because this widget should never be the reason the DolphinViewContainer is made wider.
+}
+
+void AnimatedHeightWidget::setVisible(bool visible, Animated animated)
+{
+ setEnabled(visible);
+ if (m_heightAnimation) {
+ m_heightAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
+ }
+
+ if (animated == WithAnimation
+ && (style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) < 1 || GlobalConfig::animationDurationFactor() <= 0.0)) {
+ animated = WithoutAnimation;
+ }
+
+ if (animated == WithoutAnimation) {
+ setMaximumHeight(visible ? preferredHeight() : 0);
+ setVisible(visible);
+ return;
+ }
+
+ m_heightAnimation = new QPropertyAnimation(this, "maximumHeight");
+ m_heightAnimation->setDuration(2 * style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) * GlobalConfig::animationDurationFactor());
+
+ m_heightAnimation->setStartValue(height());
+ m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
+ if (visible) {
+ show();
+ m_heightAnimation->setEndValue(preferredHeight());
+ } else {
+ m_heightAnimation->setEndValue(0);
+ connect(m_heightAnimation, &QAbstractAnimation::finished, this, &QWidget::hide);
+ }
+
+ m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+QWidget *AnimatedHeightWidget::prepareContentsContainer(QWidget *contentsContainer)
+{
+ Q_ASSERT_X(!m_contentsContainerParent->widget(),
+ "AnimatedHeightWidget::prepareContentsContainer",
+ "Another contentsContainer has already been prepared. There can only be one.");
+ contentsContainer->setParent(m_contentsContainerParent);
+ m_contentsContainerParent->setWidget(contentsContainer);
+ return contentsContainer;
+}
+
+bool AnimatedHeightWidget::isAnimationRunning() const
+{
+ return m_heightAnimation && m_heightAnimation->state() == QAbstractAnimation::Running;
+}
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2024 Felix Ernst <felixernst@kde.org>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
+
+#ifndef ANIMATEDHEIGHTWIDGET_H
+#define ANIMATEDHEIGHTWIDGET_H
+
+#include "global.h"
+
+#include <QPointer>
+#include <QWidget>
+
+class QPropertyAnimation;
+class QScrollArea;
+
+/**
+ * @brief An abstract base class which facilitates animated showing and hiding of sub-classes
+ */
+class AnimatedHeightWidget : public QWidget
+{
+public:
+ AnimatedHeightWidget(QWidget *parent);
+
+ /**
+ * Plays a show or hide animation while changing visibility.
+ * Therefore, if this method is used to hide this widget, the actual hiding will be delayed until the animation finished.
+ *
+ * @param visible Whether this bar is supposed to be visible long-term
+ * @param animated Whether this should be animated. The animation is skipped if the users' settings are configured that way.
+ *
+ * @see QWidget::setVisible()
+ */
+ void setVisible(bool visible, Animated animated);
+
+ /**
+ * @returns a QSize with a width of 1 to make sure that this bar never causes side panels to shrink.
+ * The returned height equals preferredHeight().
+ */
+ QSize sizeHint() const override;
+
+protected:
+ /**
+ * AnimatedHeightWidget always requires a singular main child which we call the "contentsContainer".
+ * Use this method to register such an object.
+ *
+ * @returns a "contentsContainer" which is a QWidget that consists of/contains all visible contents of this AnimatedHeightWidget.
+ * It will be the only grandchild of this AnimatedHeightWidget.
+ * @param contentsContainer The object that should be used as the "contentsContainer".
+ */
+ QWidget *prepareContentsContainer(QWidget *contentsContainer = new QWidget);
+
+ /** @returns whether this object is currently animating a visibility change. */
+ bool isAnimationRunning() const;
+
+private:
+ using QWidget::hide; // Use QAbstractAnimation::setVisible() instead.
+ using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't fully hide the one from QWidget so we can still use it privately.
+ using QWidget::show; // Use QAbstractAnimation::setVisible() instead.
+
+ /** @returns the full preferred height this widget should have when it is done animating and visible. */
+ virtual int preferredHeight() const = 0;
+
+private:
+ /** @see contentsContainerParent() */
+ QScrollArea *m_contentsContainerParent = nullptr;
+
+ /** @see AnimatedHeightWidget::setVisible() */
+ QPointer<QPropertyAnimation> m_heightAnimation;
+};
+
+#endif // ANIMATEDHEIGHTWIDGET_H
m_topLayout->setContentsMargins(0, 0, 0, 0);
m_searchBox = new DolphinSearchBox(this);
- m_searchBox->hide();
+ m_searchBox->setVisible(false, WithoutAnimation);
connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate);
connect(m_searchBox, &DolphinSearchBox::openRequest, this, &DolphinViewContainer::openSearchBox);
connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox);
// Initialize filter bar
m_filterBar = new FilterBar(this);
- m_filterBar->setVisible(GeneralSettings::filterBar());
+ m_filterBar->setVisible(GeneralSettings::filterBar(), WithoutAnimation);
connect(m_filterBar, &FilterBar::filterChanged, this, &DolphinViewContainer::setNameFilter);
connect(m_filterBar, &FilterBar::closeRequest, this, &DolphinViewContainer::closeFilterBar);
m_selectionModeBottomBar->setVisible(false, WithAnimation);
Q_EMIT selectionModeChanged(false);
- if (m_selectionModeTopBar->isAncestorOf(QApplication::focusWidget()) || m_selectionModeBottomBar->isAncestorOf(QApplication::focusWidget())) {
+ if (!QApplication::focusWidget() || m_selectionModeTopBar->isAncestorOf(QApplication::focusWidget())
+ || m_selectionModeBottomBar->isAncestorOf(QApplication::focusWidget())) {
m_view->setFocus();
}
return;
bool DolphinViewContainer::isFilterBarVisible() const
{
- return m_filterBar->isVisible();
+ return m_filterBar->isEnabled(); // Gets disabled in AnimatedHeightWidget while animating towards a hidden state.
}
void DolphinViewContainer::setSearchModeEnabled(bool enabled)
{
- m_searchBox->setVisible(enabled);
+ m_searchBox->setVisible(enabled, WithAnimation);
if (enabled) {
const QUrl &locationUrl = m_urlNavigator->locationUrl();
Q_ASSERT(m_filterBar);
if (visible) {
m_view->hideToolTip(ToolTipManager::HideBehavior::Instantly);
- m_filterBar->show();
+ m_filterBar->setVisible(true, WithAnimation);
m_filterBar->setFocus();
m_filterBar->selectAll();
} else {
*/
void readSettings();
- /** Returns true, if the filter bar is visible. */
+ /** @returns true, if the filter bar is visible.
+ * false, if it is hidden or currently animating towards a hidden state. */
bool isFilterBarVisible() const;
/** Returns true if the search mode is enabled. */
#include <QToolButton>
FilterBar::FilterBar(QWidget *parent)
- : QWidget(parent)
+ : AnimatedHeightWidget{parent}
{
+ QWidget *contentsContainer = prepareContentsContainer();
+
// Create button to lock text when changing folders
- m_lockButton = new QToolButton(this);
+ m_lockButton = new QToolButton(contentsContainer);
m_lockButton->setAutoRaise(true);
m_lockButton->setCheckable(true);
m_lockButton->setIcon(QIcon::fromTheme(QStringLiteral("object-unlocked")));
connect(m_lockButton, &QToolButton::toggled, this, &FilterBar::slotToggleLockButton);
// Create filter editor
- m_filterInput = new QLineEdit(this);
+ m_filterInput = new QLineEdit(contentsContainer);
m_filterInput->setLayoutDirection(Qt::LeftToRight);
m_filterInput->setClearButtonEnabled(true);
m_filterInput->setPlaceholderText(i18n("Filter…"));
setFocusProxy(m_filterInput);
// Create close button
- QToolButton *closeButton = new QToolButton(this);
+ QToolButton *closeButton = new QToolButton(contentsContainer);
closeButton->setAutoRaise(true);
closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
closeButton->setToolTip(i18nc("@info:tooltip", "Hide Filter Bar"));
connect(closeButton, &QToolButton::clicked, this, &FilterBar::closeRequest);
// Apply layout
- QHBoxLayout *hLayout = new QHBoxLayout(this);
+ QHBoxLayout *hLayout = new QHBoxLayout(contentsContainer);
hLayout->setContentsMargins(0, 0, 0, 0);
hLayout->addWidget(m_lockButton);
hLayout->addWidget(m_filterInput);
void FilterBar::closeFilterBar()
{
- hide();
+ setVisible(false, WithAnimation);
clear();
if (m_lockButton) {
m_lockButton->setChecked(false);
QWidget::keyPressEvent(event);
}
+int FilterBar::preferredHeight() const
+{
+ return std::max(m_filterInput->sizeHint().height(), m_lockButton->sizeHint().height());
+}
+
#include "moc_filterbar.cpp"
#ifndef FILTERBAR_H
#define FILTERBAR_H
-#include <QWidget>
+#include "animatedheightwidget.h"
class QLineEdit;
class QToolButton;
*
* @author Gregor Kališnik <gregor@podnapisi.net>
*/
-class FilterBar : public QWidget
+class FilterBar : public AnimatedHeightWidget
{
Q_OBJECT
void showEvent(QShowEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
+ /** @see AnimatedHeightWidget::preferredHeight() */
+ int preferredHeight() const override;
+
private:
QLineEdit *m_filterInput;
QToolButton *m_lockButton;
#include <QUrlQuery>
DolphinSearchBox::DolphinSearchBox(QWidget *parent)
- : QWidget(parent)
+ : AnimatedHeightWidget(parent)
, m_startedSearching(false)
, m_active(true)
, m_topLayout(nullptr)
, m_facetsWidget(nullptr)
, m_searchPath()
, m_startSearchTimer(nullptr)
+ , m_initialized(false)
{
}
return m_active;
}
-bool DolphinSearchBox::event(QEvent *event)
+void DolphinSearchBox::setVisible(bool visible, Animated animated)
{
- if (event->type() == QEvent::Polish) {
+ if (visible) {
init();
}
- return QWidget::event(event);
+ AnimatedHeightWidget::setVisible(visible, animated);
}
void DolphinSearchBox::showEvent(QShowEvent *event)
void DolphinSearchBox::saveSettings()
{
- SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere"));
- SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content"));
- SearchSettings::self()->save();
+ if (m_initialized) {
+ SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere"));
+ SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content"));
+ SearchSettings::self()->save();
+ }
}
void DolphinSearchBox::init()
{
+ if (m_initialized) {
+ return; // This object is already initialised.
+ }
+
+ QWidget *contentsContainer = prepareContentsContainer();
+
// Create search box
- m_searchInput = new QLineEdit(this);
+ m_searchInput = new QLineEdit(contentsContainer);
m_searchInput->setPlaceholderText(i18n("Search…"));
m_searchInput->installEventFilter(this);
m_searchInput->setClearButtonEnabled(true);
connect(m_saveSearchAction, &QAction::triggered, this, &DolphinSearchBox::slotSearchSaved);
// Create close button
- QToolButton *closeButton = new QToolButton(this);
+ QToolButton *closeButton = new QToolButton(contentsContainer);
closeButton->setAutoRaise(true);
closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching"));
searchInputLayout->addWidget(closeButton);
// Create "Filename" and "Content" button
- m_fileNameButton = new QToolButton(this);
+ m_fileNameButton = new QToolButton(contentsContainer);
m_fileNameButton->setText(i18nc("action:button", "Filename"));
initButton(m_fileNameButton);
m_contentButton->setText(i18nc("action:button", "Content"));
initButton(m_contentButton);
- QButtonGroup *searchWhatGroup = new QButtonGroup(this);
+ QButtonGroup *searchWhatGroup = new QButtonGroup(contentsContainer);
searchWhatGroup->addButton(m_fileNameButton);
searchWhatGroup->addButton(m_contentButton);
- m_separator = new KSeparator(Qt::Vertical, this);
+ m_separator = new KSeparator(Qt::Vertical, contentsContainer);
// Create "From Here" and "Your files" buttons
- m_fromHereButton = new QToolButton(this);
+ m_fromHereButton = new QToolButton(contentsContainer);
m_fromHereButton->setText(i18nc("action:button", "From Here"));
initButton(m_fromHereButton);
- m_everywhereButton = new QToolButton(this);
+ m_everywhereButton = new QToolButton(contentsContainer);
m_everywhereButton->setText(i18nc("action:button", "Your files"));
m_everywhereButton->setToolTip(i18nc("action:button", "Search in your home directory"));
m_everywhereButton->setIcon(QIcon::fromTheme(QStringLiteral("user-home")));
m_everywhereButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
initButton(m_everywhereButton);
- QButtonGroup *searchLocationGroup = new QButtonGroup(this);
+ QButtonGroup *searchLocationGroup = new QButtonGroup(contentsContainer);
searchLocationGroup->addButton(m_fromHereButton);
searchLocationGroup->addButton(m_everywhereButton);
QToolButton *kfindToolsButton = nullptr;
if (kfind) {
- kfindToolsButton = new QToolButton(this);
+ kfindToolsButton = new QToolButton(contentsContainer);
kfindToolsButton->setAutoRaise(true);
kfindToolsButton->setPopupMode(QToolButton::InstantPopup);
kfindToolsButton->setIcon(QIcon::fromTheme("arrow-down-double"));
}
// Create "Facets" widget
- m_facetsWidget = new DolphinFacetsWidget(this);
+ m_facetsWidget = new DolphinFacetsWidget(contentsContainer);
m_facetsWidget->installEventFilter(this);
m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
m_facetsWidget->layout()->setSpacing(Dolphin::LAYOUT_SPACING_SMALL);
// Put the options into a QScrollArea. This prevents increasing the view width
// in case that not enough width for the options is available.
- QWidget *optionsContainer = new QWidget(this);
+ QWidget *optionsContainer = new QWidget(contentsContainer);
// Apply layout for the options
QHBoxLayout *optionsLayout = new QHBoxLayout(optionsContainer);
optionsLayout->addWidget(m_separator);
optionsLayout->addWidget(m_fromHereButton);
optionsLayout->addWidget(m_everywhereButton);
- optionsLayout->addWidget(new KSeparator(Qt::Vertical, this));
+ optionsLayout->addWidget(new KSeparator(Qt::Vertical, contentsContainer));
if (kfindToolsButton) {
optionsLayout->addWidget(kfindToolsButton);
}
optionsLayout->addStretch(1);
- m_optionsScrollArea = new QScrollArea(this);
+ m_optionsScrollArea = new QScrollArea(contentsContainer);
m_optionsScrollArea->setFrameShape(QFrame::NoFrame);
m_optionsScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_optionsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_optionsScrollArea->setWidget(optionsContainer);
m_optionsScrollArea->setWidgetResizable(true);
- m_topLayout = new QVBoxLayout(this);
+ m_topLayout = new QVBoxLayout(contentsContainer);
m_topLayout->setContentsMargins(0, Dolphin::LAYOUT_SPACING_SMALL, 0, 0);
m_topLayout->setSpacing(Dolphin::LAYOUT_SPACING_SMALL);
m_topLayout->addLayout(searchInputLayout);
m_startSearchTimer->setSingleShot(true);
m_startSearchTimer->setInterval(500);
connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest);
+
+ m_initialized = true;
}
QString DolphinSearchBox::queryTitle(const QString &text) const
const bool indexingEnabled = isIndexingEnabled();
m_facetsWidget->setEnabled(indexingEnabled);
m_facetsWidget->setVisible(indexingEnabled);
+
+ // The m_facetsWidget might have changed visibility. We smoothly animate towards the updated height.
+ if (isVisible() && isEnabled()) {
+ setVisible(true, WithAnimation);
+ }
}
bool DolphinSearchBox::isIndexingEnabled() const
#endif
}
+int DolphinSearchBox::preferredHeight() const
+{
+ return m_initialized ? m_topLayout->sizeHint().height() : 0;
+}
+
#include "moc_dolphinsearchbox.cpp"
#ifndef DOLPHINSEARCHBOX_H
#define DOLPHINSEARCHBOX_H
+#include "animatedheightwidget.h"
+
#include <QUrl>
-#include <QWidget>
class DolphinFacetsWidget;
class DolphinQuery;
* If Baloo is available and the current folder is indexed, further
* options are offered.
*/
-class DolphinSearchBox : public QWidget
+class DolphinSearchBox : public AnimatedHeightWidget
{
Q_OBJECT
*/
bool isActive() const;
+ /*
+ * @see AnimatedHeightWidget::setVisible()
+ * @see QWidget::setVisible()
+ */
+ void setVisible(bool visible, Animated animated);
+
protected:
- bool event(QEvent *event) override;
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
bool isIndexingEnabled() const;
+ /** @see AnimatedHeightWidget::preferredHeight() */
+ int preferredHeight() const override;
+
private:
QString queryTitle(const QString &text) const;
QUrl m_searchPath;
QTimer *m_startSearchTimer;
+
+ bool m_initialized;
};
#endif
#include <QGridLayout>
#include <QResizeEvent>
-#include <QScrollArea>
#include <QStyle>
#include <QTimer>
using namespace SelectionMode;
BottomBar::BottomBar(KActionCollection *actionCollection, QWidget *parent)
- : QWidget{parent}
+ : AnimatedHeightWidget{parent}
{
- // Showing of this widget is normally animated. We hide it for now and make it small.
- hide();
- setMaximumHeight(0);
-
- setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
- setMinimumWidth(0);
-
- auto fillParentLayout = new QGridLayout(this);
- fillParentLayout->setContentsMargins(0, 0, 0, 0);
-
- // Put the contents into a QScrollArea. This prevents increasing the view width
- // in case that not enough width for the contents is available. (this trick is also used in dolphinsearchbox.cpp.)
- m_scrollArea = new QScrollArea(this);
- fillParentLayout->addWidget(m_scrollArea);
- m_scrollArea->setFrameShape(QFrame::NoFrame);
- m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_scrollArea->setWidgetResizable(true);
-
- m_contentsContainer = new BottomBarContentsContainer(actionCollection, m_scrollArea);
- m_scrollArea->setWidget(m_contentsContainer);
+ m_contentsContainer = new BottomBarContentsContainer(actionCollection, nullptr);
+ prepareContentsContainer(m_contentsContainer);
m_contentsContainer->installEventFilter(this); // Adjusts the height of this bar to the height of the contentsContainer
connect(m_contentsContainer, &BottomBarContentsContainer::error, this, &BottomBar::error);
connect(m_contentsContainer, &BottomBarContentsContainer::barVisibilityChangeRequested, this, [this](bool visible) {
});
connect(m_contentsContainer, &BottomBarContentsContainer::selectionModeLeavingRequested, this, &BottomBar::selectionModeLeavingRequested);
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
BackgroundColorHelper::instance()->controlBackgroundColor(this);
}
void BottomBar::setVisibleInternal(bool visible, Animated animated)
{
- Q_ASSERT_X(animated == WithAnimation, "SelectionModeBottomBar::setVisible", "This wasn't implemented.");
if (!visible && contents() == PasteContents) {
return; // The bar with PasteContents should not be hidden or users might not know how to paste what they just copied.
// Set contents to anything else to circumvent this prevention mechanism.
return; // There is nothing on the bar that we want to show. We keep it invisible and only show it when the selection or the contents change.
}
- setEnabled(visible);
- if (m_heightAnimation) {
- m_heightAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
- }
- m_heightAnimation = new QPropertyAnimation(this, "maximumHeight");
- m_heightAnimation->setDuration(2 * style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) * GlobalConfig::animationDurationFactor());
- m_heightAnimation->setStartValue(height());
- m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
- if (visible) {
- show();
- m_heightAnimation->setEndValue(sizeHint().height());
- connect(m_heightAnimation, &QAbstractAnimation::finished, this, [this]() {
- setMaximumHeight(sizeHint().height());
- });
- } else {
- m_heightAnimation->setEndValue(0);
- connect(m_heightAnimation, &QAbstractAnimation::finished, this, &QWidget::hide);
- }
-
- m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
-}
-
-QSize BottomBar::sizeHint() const
-{
- return QSize{1, m_contentsContainer->sizeHint().height()};
- // 1 as width because this widget should never be the reason the DolphinViewContainer is made wider.
+ AnimatedHeightWidget::setVisible(visible, animated);
}
void BottomBar::slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl)
case QEvent::ChildRemoved:
QTimer::singleShot(0, this, [this]() {
// The necessary height might have changed because of the added/removed child so we change the height manually.
- if (isVisibleTo(parentWidget()) && isEnabled() && (!m_heightAnimation || m_heightAnimation->state() != QAbstractAnimation::Running)) {
+ if (isVisibleTo(parentWidget()) && isEnabled() && !isAnimationRunning()) {
setMaximumHeight(sizeHint().height());
}
});
{
if (resizeEvent->oldSize().width() == resizeEvent->size().width()) {
// The width() didn't change so our custom override isn't needed.
- return QWidget::resizeEvent(resizeEvent);
+ return AnimatedHeightWidget::resizeEvent(resizeEvent);
}
m_contentsContainer->adaptToNewBarWidth(width());
- return QWidget::resizeEvent(resizeEvent);
+ return AnimatedHeightWidget::resizeEvent(resizeEvent);
+}
+
+int BottomBar::preferredHeight() const
+{
+ return m_contentsContainer->sizeHint().height();
}
#include "moc_bottombar.cpp"
#ifndef BOTTOMBAR_H
#define BOTTOMBAR_H
+#include "animatedheightwidget.h"
#include "global.h"
#include <QAction>
#include <QPointer>
-#include <QPropertyAnimation>
-#include <QWidget>
class KActionCollection;
class KFileItemList;
class QPushButton;
class QResizeEvent;
-class QScrollArea;
class QUrl;
namespace SelectionMode
*
* The visible contents of the bar are managed in BottomBarContentsContainer. This class serves as a wrapper around it.
*/
-class BottomBar : public QWidget
+class BottomBar : public AnimatedHeightWidget
{
Q_OBJECT
* @param visible Whether this bar is supposed to be visible long term
* @param animated Whether this should be animated. The animation is skipped if the users' settings are configured that way.
*
+ * @see AnimatedHeightWidget::setVisible()
* @see QWidget::setVisible()
*/
void setVisible(bool visible, Animated animated);
void resetContents(Contents contents);
Contents contents() const;
- /** @returns a width of 1 to make sure that this bar never causes side panels to shrink. */
- QSize sizeHint() const override;
-
public Q_SLOTS:
/** Adapts the contents based on the selection in the related view. */
void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl);
void resizeEvent(QResizeEvent *resizeEvent) override;
private:
- using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget so we can still use it privately.
-
/**
* Identical to SelectionModeBottomBar::setVisible() but doesn't change m_allowedToBeVisible.
* @see SelectionModeBottomBar::setVisible()
*/
void setVisibleInternal(bool visible, Animated animated);
+ /** @see AnimatedHeightWidget::preferredHeight() */
+ int preferredHeight() const override;
+
private:
- /** The only direct child widget of this bar. */
- QScrollArea *m_scrollArea;
/** The only direct grandchild of this bar. */
BottomBarContentsContainer *m_contentsContainer;
* This is necessary because this bar might have been setVisible(true) but there is no reason to show the bar currently so it was kept hidden.
* @see SelectionModeBottomBar::setVisible() */
bool m_allowedToBeVisible = false;
- /** @see SelectionModeBottomBar::setVisible() */
- QPointer<QPropertyAnimation> m_heightAnimation;
};
}
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
-#include <QScrollArea>
#include <QStyle>
using namespace SelectionMode;
TopBar::TopBar(QWidget *parent)
- : QWidget{parent}
+ : AnimatedHeightWidget{parent}
{
- // Showing of this widget is normally animated. We hide it for now and make it small.
- hide();
- setMaximumHeight(0);
-
setToolTip(KToolTipHelper::whatsThisHintOnly());
setWhatsThis(xi18nc("@info:whatsthis",
"<title>Selection Mode</title><para>Select files or folders to manage or manipulate them."
"<item>Selection rectangles (created by dragging from an empty area) invert the selection status of items within.</item></list></para>"
"<para>The available action buttons at the bottom change depending on the current selection.</para>"));
- auto fillParentLayout = new QGridLayout(this);
- fillParentLayout->setContentsMargins(0, 0, 0, 0);
-
- // Put the contents into a QScrollArea. This prevents increasing the view width
- // in case that not enough width for the contents is available. (this trick is also used in bottombar.cpp.)
- auto scrollArea = new QScrollArea(this);
- fillParentLayout->addWidget(scrollArea);
- scrollArea->setFrameShape(QFrame::NoFrame);
- scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- scrollArea->setWidgetResizable(true);
-
- auto contentsContainer = new QWidget(scrollArea);
- scrollArea->setWidget(contentsContainer);
+ QWidget *contentsContainer = prepareContentsContainer();
BackgroundColorHelper::instance()->controlBackgroundColor(this);
- setMinimumWidth(0);
-
m_fullLabelString = i18nc("@info label above the view explaining the state", "Selection Mode: Click on files or folders to select or deselect them.");
m_shortLabelString = i18nc("@info label above the view explaining the state", "Selection Mode");
m_label = new QLabel(contentsContainer);
QHBoxLayout *layout = new QHBoxLayout(contentsContainer);
auto contentsMargins = layout->contentsMargins();
m_preferredHeight = contentsMargins.top() + m_label->sizeHint().height() + contentsMargins.bottom();
- scrollArea->setMaximumHeight(m_preferredHeight);
m_closeButton->setFixedHeight(m_preferredHeight);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_closeButton);
}
-void TopBar::setVisible(bool visible, Animated animated)
-{
- Q_ASSERT_X(animated == WithAnimation, "SelectionModeTopBar::setVisible", "This wasn't implemented.");
-
- if (m_heightAnimation) {
- m_heightAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
- }
- m_heightAnimation = new QPropertyAnimation(this, "maximumHeight");
- m_heightAnimation->setDuration(2 * style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) * GlobalConfig::animationDurationFactor());
-
- m_heightAnimation->setStartValue(height());
- m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
- if (visible) {
- show();
- m_heightAnimation->setEndValue(m_preferredHeight);
- } else {
- m_heightAnimation->setEndValue(0);
- connect(m_heightAnimation, &QAbstractAnimation::finished, this, &QWidget::hide);
- }
-
- m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
-}
-
void TopBar::resizeEvent(QResizeEvent *resizeEvent)
{
updateLabelString();
- return QWidget::resizeEvent(resizeEvent);
+ return AnimatedHeightWidget::resizeEvent(resizeEvent);
}
void TopBar::updateLabelString()
#ifndef SELECTIONMODETOPBAR_H
#define SELECTIONMODETOPBAR_H
-#include "global.h"
+#include "animatedheightwidget.h"
-#include <QPointer>
-#include <QPropertyAnimation>
-#include <QWidget>
-
-class QHideEvent;
class QLabel;
class QPushButton;
class QResizeEvent;
-class QShowEvent;
namespace SelectionMode
{
/**
* @brief A bar appearing at the top of the view when in selection mode to make users aware of the selection mode state of the application.
*/
-class TopBar : public QWidget
+class TopBar : public AnimatedHeightWidget
{
Q_OBJECT
public:
TopBar(QWidget *parent);
- /**
- * Plays a show or hide animation while changing visibility.
- * Therefore, if this method is used to hide this widget, the actual hiding will be postponed until the animation finished.
- * @see QWidget::setVisible()
- */
- void setVisible(bool visible, Animated animated);
-
Q_SIGNALS:
void selectionModeLeavingRequested();
void resizeEvent(QResizeEvent *resizeEvent) override;
private:
- using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget so we can still use it privately.
-
/** Decides whether the m_fullLabelString or m_shortLabelString should be used based on available width. */
void updateLabelString();
+ /** @see AnimatedHeightWidget::preferredHeight() */
+ inline int preferredHeight() const override
+ {
+ return m_preferredHeight;
+ };
+
private:
QLabel *m_label;
QPushButton *m_closeButton;
QString m_shortLabelString;
int m_preferredHeight;
-
- QPointer<QPropertyAnimation> m_heightAnimation;
};
}
}
DolphinStatusBar::DolphinStatusBar(QWidget *parent)
- : QWidget(parent)
+ : AnimatedHeightWidget(parent)
, m_text()
, m_defaultText()
, m_label(nullptr)
, m_textTimestamp()
{
setProperty("_breeze_statusbar_separator", true);
+
+ QWidget *contentsContainer = prepareContentsContainer();
+
// Initialize text label
- m_label = new KSqueezedTextLabel(m_text, this);
+ m_label = new KSqueezedTextLabel(m_text, contentsContainer);
m_label->setWordWrap(true);
m_label->setTextFormat(Qt::PlainText);
// Initialize zoom slider's explanatory label
- m_zoomLabel = new KSqueezedTextLabel(i18nc("Used as a noun, i.e. 'Here is the zoom level:'", "Zoom:"), this);
+ m_zoomLabel = new KSqueezedTextLabel(i18nc("Used as a noun, i.e. 'Here is the zoom level:'", "Zoom:"), contentsContainer);
// Initialize zoom widget
- m_zoomSlider = new QSlider(Qt::Horizontal, this);
+ m_zoomSlider = new QSlider(Qt::Horizontal, contentsContainer);
m_zoomSlider->setAccessibleName(i18n("Zoom"));
m_zoomSlider->setAccessibleDescription(i18nc("Description for zoom-slider (accessibility)", "Sets the size of the file icons."));
m_zoomSlider->setPageStep(1);
connect(m_zoomSlider, &QSlider::sliderMoved, this, &DolphinStatusBar::showZoomSliderToolTip);
// Initialize space information
- m_spaceInfo = new StatusBarSpaceInfo(this);
+ m_spaceInfo = new StatusBarSpaceInfo(contentsContainer);
// Initialize progress information
- m_stopButton = new QToolButton(this);
+ m_stopButton = new QToolButton(contentsContainer);
m_stopButton->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
m_stopButton->setAccessibleName(i18n("Stop"));
m_stopButton->setAutoRaise(true);
m_stopButton->hide();
connect(m_stopButton, &QToolButton::clicked, this, &DolphinStatusBar::stopPressed);
- m_progressTextLabel = new QLabel(this);
+ m_progressTextLabel = new QLabel(contentsContainer);
m_progressTextLabel->hide();
- m_progressBar = new QProgressBar(this);
+ m_progressBar = new QProgressBar(contentsContainer);
m_progressBar->hide();
m_showProgressBarTimer = new QTimer(this);
m_progressBar->setFixedHeight(zoomSliderHeight);
m_progressBar->setMaximumWidth(fontMetrics.averageCharWidth() * 20);
- QHBoxLayout *topLayout = new QHBoxLayout(this);
+ m_topLayout = new QHBoxLayout(contentsContainer);
updateContentsMargins();
- topLayout->setSpacing(4);
- topLayout->addWidget(m_label, 1);
- topLayout->addWidget(m_zoomLabel);
- topLayout->addWidget(m_zoomSlider, 1);
- topLayout->addWidget(m_spaceInfo, 1);
- topLayout->addWidget(m_stopButton);
- topLayout->addWidget(m_progressTextLabel);
- topLayout->addWidget(m_progressBar);
-
- setVisible(GeneralSettings::showStatusBar());
+ m_topLayout->setSpacing(4);
+ m_topLayout->addWidget(m_label, 1);
+ m_topLayout->addWidget(m_zoomLabel);
+ m_topLayout->addWidget(m_zoomSlider, 1);
+ m_topLayout->addWidget(m_spaceInfo, 1);
+ m_topLayout->addWidget(m_stopButton);
+ m_topLayout->addWidget(m_progressTextLabel);
+ m_topLayout->addWidget(m_progressBar);
+
+ setVisible(GeneralSettings::showStatusBar(), WithoutAnimation);
setExtensionsVisible(true);
setWhatsThis(xi18nc("@info:whatsthis Statusbar",
"<para>This is "
void DolphinStatusBar::readSettings()
{
- setVisible(GeneralSettings::showStatusBar());
+ setVisible(GeneralSettings::showStatusBar(), WithAnimation);
setExtensionsVisible(true);
}
{
if (GeneralSettings::showSpaceInfo()) {
// We reduce the outside margin for the flat button so it visually has the same margin as the status bar text label on the other end of the bar.
- layout()->setContentsMargins(6, 0, 2, 0);
+ m_topLayout->setContentsMargins(6, 0, 2, 0);
} else {
- layout()->setContentsMargins(6, 0, 6, 0);
+ m_topLayout->setContentsMargins(6, 0, 6, 0);
}
}
style()->drawPrimitive(QStyle::PE_PanelStatusBar, &opt, &p, this);
}
+int DolphinStatusBar::preferredHeight() const
+{
+ return m_spaceInfo->height();
+}
+
#include "moc_dolphinstatusbar.cpp"
#ifndef DOLPHINSTATUSBAR_H
#define DOLPHINSTATUSBAR_H
+#include "animatedheightwidget.h"
+
#include <QTime>
-#include <QWidget>
class QUrl;
class StatusBarSpaceInfo;
class QSlider;
class QTimer;
class KSqueezedTextLabel;
+class QHBoxLayout;
/**
* @brief Represents the statusbar of a Dolphin view.
* The statusbar allows to show messages, progress
* information and space-information of a disk.
*/
-class DolphinStatusBar : public QWidget
+class DolphinStatusBar : public AnimatedHeightWidget
{
Q_OBJECT
void updateContentsMargins();
+ /** @see AnimatedHeightWidget::preferredHeight() */
+ int preferredHeight() const override;
+
private:
QString m_text;
QString m_defaultText;
QTimer *m_delayUpdateTimer;
QTime m_textTimestamp;
+
+ QHBoxLayout *m_topLayout;
};
#endif
*/
void DolphinSearchBoxTest::testTextClearing()
{
- m_searchBox->show();
+ m_searchBox->setVisible(true, WithoutAnimation);
QVERIFY(m_searchBox->text().isEmpty());
m_searchBox->setText("xyz");
- m_searchBox->hide();
- m_searchBox->show();
+ m_searchBox->setVisible(false, WithoutAnimation);
+ m_searchBox->setVisible(true, WithoutAnimation);
QCOMPARE(m_searchBox->text(), QString("xyz"));
QTest::keyClick(m_searchBox, Qt::Key_Escape);