diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dolphinmainwindow.cpp | 8 | ||||
| -rw-r--r-- | src/dolphintabpage.cpp | 144 | ||||
| -rw-r--r-- | src/dolphintabpage.h | 41 | ||||
| -rw-r--r-- | src/global.cpp | 27 | ||||
| -rw-r--r-- | src/global.h | 23 |
5 files changed, 230 insertions, 13 deletions
diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 321670b58..3377918ce 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -807,7 +807,7 @@ void DolphinMainWindow::invertSelection() void DolphinMainWindow::toggleSplitView() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); - tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled()); + tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation); updateViewActions(); } @@ -815,8 +815,8 @@ void DolphinMainWindow::toggleSplitView() void DolphinMainWindow::toggleSplitStash() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); - tabPage->setSplitViewEnabled(false); - tabPage->setSplitViewEnabled(true, QUrl("stash:/")); + tabPage->setSplitViewEnabled(false, WithAnimation); + tabPage->setSplitViewEnabled(true, WithAnimation, QUrl("stash:/")); } void DolphinMainWindow::reloadView() @@ -2143,7 +2143,7 @@ void DolphinMainWindow::refreshViews() // The startup settings have been changed by the user (see bug #254947). // Synchronize the split-view setting with the active view: const bool splitView = GeneralSettings::splitView(); - m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView); + m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView, WithAnimation); updateSplitAction(); updateWindowTitle(); } diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 138822cfd..a90e8e7f0 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <[email protected]> + * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -8,13 +9,17 @@ #include "dolphin_generalsettings.h" #include "dolphinviewcontainer.h" +#include "global.h" +#include <QVariantAnimation> #include <QSplitter> #include <QGridLayout> #include <QWidgetAction> +#include <QStyle> DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, QWidget* parent) : QWidget(parent), + m_expandingContainer{nullptr}, m_primaryViewActive(true), m_splitViewEnabled(false), m_active(true) @@ -65,12 +70,24 @@ bool DolphinTabPage::splitViewEnabled() const return m_splitViewEnabled; } -void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) +void DolphinTabPage::setSplitViewEnabled(bool enabled, Animated animated, const QUrl &secondaryUrl) { if (m_splitViewEnabled != enabled) { m_splitViewEnabled = enabled; + if (animated == WithAnimation && ( + style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) < 1 || + GlobalConfig::animationDurationFactor() <= 0.0)) { + animated = WithoutAnimation; + } + if (m_expandViewAnimation) { + m_expandViewAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped. + if (animated == WithoutAnimation) { + slotAnimationFinished(); + } + } if (enabled) { + QList<int> splitterSizes = m_splitter->sizes(); const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl; m_secondaryViewContainer = createViewContainer(url); @@ -84,8 +101,15 @@ void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) m_splitter->addWidget(m_secondaryViewContainer); m_secondaryViewContainer->installEventFilter(this); - m_secondaryViewContainer->show(); m_secondaryViewContainer->setActive(true); + + if (animated == WithAnimation) { + m_secondaryViewContainer->setMinimumWidth(1); + splitterSizes.append(1); + m_splitter->setSizes(splitterSizes); + startExpandViewAnimation(m_secondaryViewContainer); + } + m_secondaryViewContainer->show(); } else { m_navigatorsWidget->setSecondaryNavigatorVisible(false); m_secondaryViewContainer->disconnectUrlNavigator(); @@ -117,8 +141,18 @@ void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) } } m_primaryViewContainer->setActive(true); - view->close(); - view->deleteLater(); + + if (animated == WithoutAnimation) { + view->close(); + view->deleteLater(); + } else { + // Kill it but keep it as a zombie for the closing animation. + m_secondaryViewContainer = nullptr; + view->blockSignals(true); + view->view()->blockSignals(true); + view->setDisabled(true); + startExpandViewAnimation(m_primaryViewContainer); + } } } } @@ -204,7 +238,7 @@ void DolphinTabPage::insertNavigatorsWidget(DolphinNavigatorsWidgetAction* navig void DolphinTabPage::resizeNavigators() const { - if (!m_splitViewEnabled) { + if (!m_secondaryViewContainer) { m_navigatorsWidget->followViewContainerGeometry( m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(), m_primaryViewContainer->width()); @@ -285,7 +319,7 @@ void DolphinTabPage::restoreState(const QByteArray& state) bool isSplitViewEnabled = false; stream >> isSplitViewEnabled; - setSplitViewEnabled(isSplitViewEnabled); + setSplitViewEnabled(isSplitViewEnabled, WithoutAnimation); QUrl primaryUrl; stream >> primaryUrl; @@ -329,7 +363,7 @@ void DolphinTabPage::restoreStateV1(const QByteArray& state) bool isSplitViewEnabled = false; stream >> isSplitViewEnabled; - setSplitViewEnabled(isSplitViewEnabled); + setSplitViewEnabled(isSplitViewEnabled, WithoutAnimation); QUrl primaryUrl; stream >> primaryUrl; @@ -372,6 +406,72 @@ void DolphinTabPage::setActive(bool active) activeViewContainer()->setActive(active); } +void DolphinTabPage::slotAnimationFinished() +{ + for (int i = 0; i < m_splitter->count(); ++i) { + QWidget *viewContainer = m_splitter->widget(i); + if (viewContainer != m_primaryViewContainer && + viewContainer != m_secondaryViewContainer) { + viewContainer->close(); + viewContainer->deleteLater(); + } + } + for (int i = 0; i < m_splitter->count(); ++i) { + QWidget *viewContainer = m_splitter->widget(i); + viewContainer->setMinimumWidth(viewContainer->minimumSizeHint().width()); + } + m_expandingContainer = nullptr; +} + +void DolphinTabPage::slotAnimationValueChanged(const QVariant& value) +{ + Q_CHECK_PTR(m_expandingContainer); + const int indexOfExpandingContainer = m_splitter->indexOf(m_expandingContainer); + int indexOfNonExpandingContainer = -1; + if (m_expandingContainer == m_primaryViewContainer) { + indexOfNonExpandingContainer = m_splitter->indexOf(m_secondaryViewContainer); + } else { + indexOfNonExpandingContainer = m_splitter->indexOf(m_primaryViewContainer); + } + std::vector<QWidget *> widgetsToRemove; + const QList<int> oldSplitterSizes = m_splitter->sizes(); + QList<int> newSplitterSizes{oldSplitterSizes}; + int expansionWidthNeeded = value.toInt() - oldSplitterSizes.at(indexOfExpandingContainer); + + // Reduce the size of the other widgets to make space for the expandingContainer. + for (int i = m_splitter->count() - 1; i >= 0; --i) { + if (m_splitter->widget(i) == m_primaryViewContainer || + m_splitter->widget(i) == m_secondaryViewContainer) { + continue; + } + newSplitterSizes[i] = oldSplitterSizes.at(i) - expansionWidthNeeded; + expansionWidthNeeded = 0; + if (indexOfNonExpandingContainer != -1) { + // Make sure every zombie container is at least slightly reduced in size + // so it doesn't seem like they are here to stay. + newSplitterSizes[i]--; + newSplitterSizes[indexOfNonExpandingContainer]++; + } + if (newSplitterSizes.at(i) <= 0) { + expansionWidthNeeded -= newSplitterSizes.at(i); + newSplitterSizes[i] = 0; + widgetsToRemove.emplace_back(m_splitter->widget(i)); + } + } + if (expansionWidthNeeded > 1 && indexOfNonExpandingContainer != -1) { + Q_ASSERT(m_splitViewEnabled); + newSplitterSizes[indexOfNonExpandingContainer] -= expansionWidthNeeded; + } + newSplitterSizes[indexOfExpandingContainer] = value.toInt(); + m_splitter->setSizes(newSplitterSizes); + while (!widgetsToRemove.empty()) { + widgetsToRemove.back()->close(); + widgetsToRemove.back()->deleteLater(); + widgetsToRemove.pop_back(); + } +} + + void DolphinTabPage::slotViewActivated() { const DolphinView* oldActiveView = activeViewContainer()->view(); @@ -441,3 +541,33 @@ DolphinViewContainer* DolphinTabPage::createViewContainer(const QUrl& url) const return container; } + +void DolphinTabPage::startExpandViewAnimation(DolphinViewContainer *expandingContainer) +{ + Q_CHECK_PTR(expandingContainer); + Q_ASSERT(expandingContainer == m_primaryViewContainer || + expandingContainer == m_secondaryViewContainer); + m_expandingContainer = expandingContainer; + + m_expandViewAnimation = new QVariantAnimation(m_splitter); + m_expandViewAnimation->setDuration(2 * + style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) * + GlobalConfig::animationDurationFactor()); + for (int i = 0; i < m_splitter->count(); ++i) { + m_splitter->widget(i)->setMinimumWidth(1); + } + connect(m_expandViewAnimation, &QAbstractAnimation::finished, + this, &DolphinTabPage::slotAnimationFinished); + connect(m_expandViewAnimation, &QVariantAnimation::valueChanged, + this, &DolphinTabPage::slotAnimationValueChanged); + + m_expandViewAnimation->setStartValue(expandingContainer->width()); + if (m_splitViewEnabled) { // A new viewContainer is being opened. + m_expandViewAnimation->setEndValue(m_splitter->width() / 2); + m_expandViewAnimation->setEasingCurve(QEasingCurve::OutCubic); + } else { // A viewContainer is being closed. + m_expandViewAnimation->setEndValue(m_splitter->width()); + m_expandViewAnimation->setEasingCurve(QEasingCurve::InCubic); + } + m_expandViewAnimation->start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 63a246328..e90bf99bf 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <[email protected]> + * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -14,8 +15,14 @@ class DolphinNavigatorsWidgetAction; class DolphinViewContainer; class QSplitter; +class QVariantAnimation; class KFileItemList; +enum Animated { + WithAnimation, + WithoutAnimation +}; + class DolphinTabPage : public QWidget { Q_OBJECT @@ -36,9 +43,15 @@ public: /** * Enables or disables the split view mode. * - * If \a enabled is true, it creates a secondary view with the url of the primary view. + * @param enabled If true, creates a secondary viewContainer in this tab. + * Otherwise deletes it. + * @param animated Decides wether the effects of this method call should + * happen instantly or be transitioned to smoothly. + * @param secondaryUrl If \p enabled is true, the new viewContainer will be opened at this + * parameter. The default value will set the Url of the new viewContainer + * to be the same as the existing one. */ - void setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl = QUrl()); + void setSplitViewEnabled(bool enabled, Animated animated, const QUrl &secondaryUrl = QUrl()); /** * @return The primary view container. @@ -148,6 +161,17 @@ signals: private slots: /** + * Deletes all zombie viewContainers that were used for the animation + * and resets the minimum size of the others to a sane value. + */ + void slotAnimationFinished(); + + /** + * This method is called for every frame of the m_expandViewAnimation. + */ + void slotAnimationValueChanged(const QVariant &value); + + /** * Handles the view activated event. * * It sets the previous active view to inactive, updates the current @@ -170,6 +194,16 @@ private: */ DolphinViewContainer* createViewContainer(const QUrl& url) const; + /** + * Starts an animation that transitions between split view mode states. + * + * One of the viewContainers is always being expanded when toggling so + * this method can animate both opening and closing of viewContainers. + * @param expandingContainer The container that will increase in size + * over the course of the animation. + */ + void startExpandViewAnimation(DolphinViewContainer *expandingContainer); + private: QSplitter* m_splitter; @@ -177,6 +211,9 @@ private: QPointer<DolphinViewContainer> m_primaryViewContainer; QPointer<DolphinViewContainer> m_secondaryViewContainer; + DolphinViewContainer *m_expandingContainer; + QPointer<QVariantAnimation> m_expandViewAnimation; + bool m_primaryViewActive; bool m_splitViewEnabled; bool m_active; diff --git a/src/global.cpp b/src/global.cpp index 1018c7d4c..3d17a733b 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -10,6 +10,7 @@ #include "dolphindebug.h" #include "dolphinmainwindowinterface.h" +#include <KConfigWatcher> #include <KDialogJobUiDelegate> #include <KIO/ApplicationLauncherJob> #include <KService> @@ -138,3 +139,29 @@ QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Do return dolphinInterfaces; } + +double GlobalConfig::animationDurationFactor() +{ + if (s_animationDurationFactor >= 0.0) { + return s_animationDurationFactor; + } + // This is the first time this method is called. + auto kdeGlobalsConfig = KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("KDE")); + updateAnimationDurationFactor(kdeGlobalsConfig, {"AnimationDurationFactor"}); + + KConfigWatcher::Ptr configWatcher = KConfigWatcher::create(KSharedConfig::openConfig()); + connect(configWatcher.data(), &KConfigWatcher::configChanged, + &GlobalConfig::updateAnimationDurationFactor); + return s_animationDurationFactor; +} + +void GlobalConfig::updateAnimationDurationFactor(const KConfigGroup &group, const QByteArrayList &names) +{ + if (group.name() == QLatin1String("KDE") && + names.contains(QByteArrayLiteral("AnimationDurationFactor"))) { + s_animationDurationFactor = std::max(0.0, + group.readEntry("AnimationDurationFactor", 1.0)); + } +} + +double GlobalConfig::s_animationDurationFactor = -1.0; diff --git a/src/global.h b/src/global.h index 65247351a..088e9c5b6 100644 --- a/src/global.h +++ b/src/global.h @@ -11,6 +11,7 @@ #include <QUrl> #include <QWidget> +class KConfigGroup; class OrgKdeDolphinMainWindowInterface; namespace Dolphin { @@ -52,4 +53,26 @@ namespace Dolphin { const int LAYOUT_SPACING_SMALL = 2; } +class GlobalConfig : public QObject +{ + Q_OBJECT + +public: + GlobalConfig() = delete; + + /** + * @return a value from the global KDE config that should be + * multiplied with every animation duration once. + * 0.0 is returned if animations are globally turned off. + * 1.0 is the default value. + */ + static double animationDurationFactor(); + +private: + static void updateAnimationDurationFactor(const KConfigGroup &group, const QByteArrayList &names); + +private: + static double s_animationDurationFactor; +}; + #endif //GLOBAL_H |
