diff options
Diffstat (limited to 'src/selectionmode')
| -rw-r--r-- | src/selectionmode/actiontexthelper.cpp | 41 | ||||
| -rw-r--r-- | src/selectionmode/actiontexthelper.h | 71 | ||||
| -rw-r--r-- | src/selectionmode/actionwithwidget.cpp | 6 | ||||
| -rw-r--r-- | src/selectionmode/actionwithwidget.h | 5 | ||||
| -rw-r--r-- | src/selectionmode/backgroundcolorhelper.cpp | 2 | ||||
| -rw-r--r-- | src/selectionmode/backgroundcolorhelper.h | 5 | ||||
| -rw-r--r-- | src/selectionmode/bottombar.cpp | 172 | ||||
| -rw-r--r-- | src/selectionmode/bottombar.h | 128 | ||||
| -rw-r--r-- | src/selectionmode/bottombarcontentscontainer.cpp (renamed from src/selectionmode/selectionmodebottombar.cpp) | 321 | ||||
| -rw-r--r-- | src/selectionmode/bottombarcontentscontainer.h (renamed from src/selectionmode/selectionmodebottombar.h) | 106 | ||||
| -rw-r--r-- | src/selectionmode/singleclickselectionproxystyle.h | 9 | ||||
| -rw-r--r-- | src/selectionmode/topbar.cpp (renamed from src/selectionmode/selectionmodetopbar.cpp) | 14 | ||||
| -rw-r--r-- | src/selectionmode/topbar.h (renamed from src/selectionmode/selectionmodetopbar.h) | 11 |
13 files changed, 576 insertions, 315 deletions
diff --git a/src/selectionmode/actiontexthelper.cpp b/src/selectionmode/actiontexthelper.cpp new file mode 100644 index 000000000..3eb868a8c --- /dev/null +++ b/src/selectionmode/actiontexthelper.cpp @@ -0,0 +1,41 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "actiontexthelper.h" + +using namespace SelectionMode; + +ActionTextHelper::ActionTextHelper(QObject *parent) : + QObject(parent) +{ } + +void ActionTextHelper::registerTextWhenNothingIsSelected(QAction *action, QString registeredText) +{ + Q_CHECK_PTR(action); + m_registeredActionTextChanges.emplace_back(action, registeredText, TextWhenNothingIsSelected); +} + +void ActionTextHelper::textsWhenNothingIsSelectedEnabled(bool enabled) +{ + for (auto i = m_registeredActionTextChanges.begin(); i != m_registeredActionTextChanges.end(); ++i) { + if (!i->action) { + i = m_registeredActionTextChanges.erase(i); + continue; + } + if (enabled && i->textStateOfRegisteredText == TextWhenNothingIsSelected) { + QString textWhenSomethingIsSelected = i->action->text(); + i->action->setText(i->registeredText); + i->registeredText = textWhenSomethingIsSelected; + i->textStateOfRegisteredText = TextWhenSomethingIsSelected; + } else if (!enabled && i->textStateOfRegisteredText == TextWhenSomethingIsSelected) { + QString textWhenNothingIsSelected = i->action->text(); + i->action->setText(i->registeredText); + i->registeredText = textWhenNothingIsSelected; + i->textStateOfRegisteredText = TextWhenNothingIsSelected; + } + } +} diff --git a/src/selectionmode/actiontexthelper.h b/src/selectionmode/actiontexthelper.h new file mode 100644 index 000000000..8f7501fa4 --- /dev/null +++ b/src/selectionmode/actiontexthelper.h @@ -0,0 +1,71 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef ACTIONTEXTHELPER_H +#define ACTIONTEXTHELPER_H + +#include <QAction> +#include <QPointer> +#include <QString> + +namespace SelectionMode +{ + +/** + * @brief Helps changing the texts of actions depending on the current selection. + */ +class ActionTextHelper : QObject +{ +public: + explicit ActionTextHelper(QObject *parent); + + /** + * Changes the text of \a action to \a text whenever textsWhenNothingIsSelectedEnabled(true) is called. + * The texts can be changed back by calling textsWhenNothingIsSelectedEnabled(false) is called. + * @see textsWhenNothingIsSelectedEnabled() + */ + void registerTextWhenNothingIsSelected(QAction *action, QString registeredText); + + /** + * Changes all texts that were registered previously using registerTextWhenNothingIsSelected() to those + * registered texts if called with \a enabled == true. Otherwise resets the texts to the original one. + */ + void textsWhenNothingIsSelectedEnabled(bool enabled); + +private: + enum TextState { + TextWhenNothingIsSelected, + TextWhenSomethingIsSelected + }; + + /** + * Utility struct to allow switching back and forth between registered actions showing their + * distinct texts for when no items are selected or when items are selected. + * An example is "Copy" or "Copy…". The latter one is used when nothing is selected and signifies + * that it will trigger SelectionMode so items can be selected and then copied. + */ + struct RegisteredActionTextChange { + QPointer<QAction> action; + QString registeredText; + TextState textStateOfRegisteredText; + + RegisteredActionTextChange(QAction *action, QString registeredText, TextState state) : + action{action}, + registeredText{registeredText}, + textStateOfRegisteredText{state} + { }; + }; + + /** + * @see RegisteredActionTextChange + */ + std::vector<RegisteredActionTextChange> m_registeredActionTextChanges; +}; + +} + +#endif // ACTIONTEXTHELPER_H diff --git a/src/selectionmode/actionwithwidget.cpp b/src/selectionmode/actionwithwidget.cpp index e9823af7c..82ce045db 100644 --- a/src/selectionmode/actionwithwidget.cpp +++ b/src/selectionmode/actionwithwidget.cpp @@ -12,6 +12,8 @@ #include <QPushButton> #include <QToolButton> +using namespace SelectionMode; + ActionWithWidget::ActionWithWidget(QAction *action) : m_action{action} { } @@ -40,7 +42,7 @@ QWidget *ActionWithWidget::newWidget(QWidget *parent) return m_widget; } -QAbstractButton *newButtonForAction(QAction *action, QWidget *parent) +QAbstractButton *SelectionMode::newButtonForAction(QAction *action, QWidget *parent) { Q_CHECK_PTR(action); Q_ASSERT(!action->isSeparator()); @@ -62,7 +64,7 @@ QAbstractButton *newButtonForAction(QAction *action, QWidget *parent) return toolButton; } -void copyActionDataToButton(QAbstractButton *button, QAction *action) +void SelectionMode::copyActionDataToButton(QAbstractButton *button, QAction *action) { button->setText(action->text()); button->setIcon(action->icon()); diff --git a/src/selectionmode/actionwithwidget.h b/src/selectionmode/actionwithwidget.h index 62dad0fc1..cf7b8bc35 100644 --- a/src/selectionmode/actionwithwidget.h +++ b/src/selectionmode/actionwithwidget.h @@ -14,6 +14,9 @@ class QAbstractButton; +namespace SelectionMode +{ + /** * @brief Small wrapper/helper class that contains an action and its widget. * @@ -83,4 +86,6 @@ QAbstractButton *newButtonForAction(QAction *action, QWidget *parent); */ void copyActionDataToButton(QAbstractButton *button, QAction *action); +} + #endif // ACTIONWITHWIDGET_H diff --git a/src/selectionmode/backgroundcolorhelper.cpp b/src/selectionmode/backgroundcolorhelper.cpp index ca110e762..4477d0f2c 100644 --- a/src/selectionmode/backgroundcolorhelper.cpp +++ b/src/selectionmode/backgroundcolorhelper.cpp @@ -14,6 +14,8 @@ #include <QtGlobal> #include <QWidget> +using namespace SelectionMode; + BackgroundColorHelper *BackgroundColorHelper::instance() { if (!s_instance) { diff --git a/src/selectionmode/backgroundcolorhelper.h b/src/selectionmode/backgroundcolorhelper.h index 013d33685..8d2730fcf 100644 --- a/src/selectionmode/backgroundcolorhelper.h +++ b/src/selectionmode/backgroundcolorhelper.h @@ -15,6 +15,9 @@ class QWidget; +namespace SelectionMode +{ + /** * @brief A Singleton class for managing the colors of selection mode widgets. */ @@ -42,4 +45,6 @@ private: static BackgroundColorHelper *s_instance; }; +} + #endif // BACKGROUNDCOLORHELPER_H diff --git a/src/selectionmode/bottombar.cpp b/src/selectionmode/bottombar.cpp new file mode 100644 index 000000000..999b24ae4 --- /dev/null +++ b/src/selectionmode/bottombar.cpp @@ -0,0 +1,172 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "bottombar.h" + +#include "bottombarcontentscontainer.h" +#include "backgroundcolorhelper.h" +#include "global.h" + +#include <KActionCollection> +#include <KFileItem> +#include <KFileItemListProperties> +#include <KStandardAction> + +#include <QGridLayout> +#include <QResizeEvent> +#include <QScrollArea> +#include <QStyle> +#include <QtGlobal> +#include <QTimer> + +using namespace SelectionMode; + +BottomBar::BottomBar(KActionCollection *actionCollection, QWidget *parent) : + QWidget{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->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){ + if (!m_allowedToBeVisible && visible) { + return; + } + setVisibleInternal(visible, WithAnimation); + }); + connect(m_contentsContainer, &BottomBarContentsContainer::leaveSelectionModeRequested, this, &BottomBar::leaveSelectionModeRequested); + + BackgroundColorHelper::instance()->controlBackgroundColor(this); +} + +void BottomBar::setVisible(bool visible, Animated animated) +{ + m_allowedToBeVisible = visible; + setVisibleInternal(visible, animated); +} + +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. + } + if (visible && !m_contentsContainer->hasSomethingToShow()) { + 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. +} + +void BottomBar::slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl) +{ + m_contentsContainer->slotSelectionChanged(selection, baseUrl); +} + +void BottomBar::slotSplitTabDisabled() +{ + switch (contents()) { + case CopyToOtherViewContents: + case MoveToOtherViewContents: + Q_EMIT leaveSelectionModeRequested(); + default: + return; + } +} + +void BottomBar::resetContents(BottomBar::Contents contents) +{ + m_contentsContainer->resetContents(contents); + + if (m_allowedToBeVisible) { + setVisibleInternal(true, WithAnimation); + } +} + +BottomBar::Contents BottomBar::contents() const +{ + return m_contentsContainer->contents(); +} + +bool BottomBar::eventFilter(QObject *watched, QEvent *event) +{ + Q_ASSERT(qobject_cast<QWidget *>(watched)); // This evenfFilter is only implemented for QWidgets. + + switch (event->type()) { + case QEvent::ChildAdded: + 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)) { + setMaximumHeight(sizeHint().height()); + } + }); + // Fall through. + default: + return false; + } +} + +void BottomBar::resizeEvent(QResizeEvent *resizeEvent) +{ + if (resizeEvent->oldSize().width() == resizeEvent->size().width()) { + // The width() didn't change so our custom override isn't needed. + return QWidget::resizeEvent(resizeEvent); + } + + m_contentsContainer->updateForNewWidth(); + + return QWidget::resizeEvent(resizeEvent); +} diff --git a/src/selectionmode/bottombar.h b/src/selectionmode/bottombar.h new file mode 100644 index 000000000..ff23b6699 --- /dev/null +++ b/src/selectionmode/bottombar.h @@ -0,0 +1,128 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef BOTTOMBAR_H +#define BOTTOMBAR_H + +#include "global.h" + +#include <QAction> +#include <QPointer> +#include <QPropertyAnimation> +#include <QWidget> + +#include <memory> + +class KActionCollection; +class KFileItemList; +class QAction; +class QPushButton; +class QResizeEvent; +class QScrollArea; +class QUrl; + +namespace SelectionMode +{ + class BottomBarContentsContainer; + +/** + * A bar used in selection mode that serves various purposes depending on what the user is currently trying to do. + * + * The Contents enum below gives a rough idea about the different states this bar might have. + * The bar is notified of various changes that make changing or updating the content worthwhile. + * + * The visible contents of the bar are managed in BottomBarContentsContainer. This class serves as a wrapper around it. + */ +class BottomBar : public QWidget +{ + Q_OBJECT + +public: + /** The different contents this bar can have. */ + enum Contents{ + CopyContents, + CopyLocationContents, + CopyToOtherViewContents, + CutContents, + DeleteContents, + DuplicateContents, + GeneralContents, + MoveToOtherViewContents, + MoveToTrashContents, + PasteContents, + RenameContents + }; + + /** + * @param actionCollection the collection this bar retrieves its actions from + * @param parent the parent widget. Typically a DolphinViewContainer + */ + explicit BottomBar(KActionCollection *actionCollection, 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. + * + * This bar might also not show itself when setVisible(true), when context menu actions are supposed to be shown + * for the selected items but no items have been selected yet. In that case it will only show itself once items were selected. + * @see QWidget::setVisible() + */ + void setVisible(bool visible, Animated animated); + using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget. + + 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: + void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl); + + /** Used to notify the m_selectionModeBottomBar that there is no other ViewContainer in the tab. */ + void slotSplitTabDisabled(); + +Q_SIGNALS: + /** + * Forwards the errors from the KFileItemAction::error() used for contextual actions. + */ + void error(const QString &errorMessage); + + void leaveSelectionModeRequested(); + +protected: + /** Is installed on an internal widget to make sure that the height of the bar is adjusted to its contents. */ + bool eventFilter(QObject *watched, QEvent *event) override; + + /** Adapts the way the contents of this bar are displayed based on the available width. */ + void resizeEvent(QResizeEvent *resizeEvent) override; + +private: + /** + * Identical to SelectionModeBottomBar::setVisible() but doesn't change m_allowedToBeVisible. + * @see SelectionModeBottomBar::setVisible() + * @see m_allowedToBeVisible + */ + void setVisibleInternal(bool visible, Animated animated); + +private: + /** The only direct child widget of this bar. */ + QScrollArea *m_scrollArea; + /** The only direct grandchild of this bar. */ + BottomBarContentsContainer *m_contentsContainer; + + /** Remembers if this bar was setVisible(true) or setVisible(false) the last time. + * 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; +}; + +} + +#endif // BOTTOMBAR_H diff --git a/src/selectionmode/selectionmodebottombar.cpp b/src/selectionmode/bottombarcontentscontainer.cpp index d10a8581b..04b52a60f 100644 --- a/src/selectionmode/selectionmodebottombar.cpp +++ b/src/selectionmode/bottombarcontentscontainer.cpp @@ -5,155 +5,36 @@ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ -#include "selectionmodebottombar.h" +#include "bottombarcontentscontainer.h" -#include "backgroundcolorhelper.h" #include "dolphin_generalsettings.h" #include "dolphincontextmenu.h" #include "dolphinmainwindow.h" #include "dolphinremoveaction.h" -#include "global.h" #include "kitemviews/kfileitemlisttostring.h" -#include <KActionCollection> -#include <KColorScheme> -#include <KFileItem> -#include <KFileItemListProperties> #include <KLocalizedString> -#include <KStandardAction> -#include <QFontMetrics> -#include <QGuiApplication> #include <QHBoxLayout> #include <QLabel> -#include <QLayout> #include <QMenu> #include <QPushButton> -#include <QResizeEvent> -#include <QScrollArea> -#include <QStyle> #include <QToolButton> -#include <QtGlobal> #include <QVBoxLayout> #include <unordered_set> -SelectionModeBottomBar::SelectionModeBottomBar(KActionCollection *actionCollection, QWidget *parent) : +using namespace SelectionMode; + +BottomBarContentsContainer::BottomBarContentsContainer(KActionCollection *actionCollection, QWidget *parent) : QWidget{parent}, m_actionCollection{actionCollection} { - // 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.) - 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); - contentsContainer->installEventFilter(this); // Adjusts the height of this bar to the height of the contentsContainer - - BackgroundColorHelper::instance()->controlBackgroundColor(this); - // We will mostly interact with m_layout when changing the contents and not care about the other internal hierarchy. - m_layout = new QHBoxLayout(contentsContainer); -} - -void SelectionModeBottomBar::setVisible(bool visible, Animated animated) -{ - m_allowedToBeVisible = visible; - setVisibleInternal(visible, animated); -} - -void SelectionModeBottomBar::setVisibleInternal(bool visible, Animated animated) -{ - Q_ASSERT_X(animated == WithAnimation, "SelectionModeBottomBar::setVisible", "This wasn't implemented."); - if (!visible && m_contents == PasteContents) { - return; // The bar with PasteContents should not be hidden or users might not know how to paste what they just copied. - // Set m_contents to anything else to circumvent this prevention mechanism. - } - if (visible && m_contents == GeneralContents && !m_internalContextMenu) { - 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 SelectionModeBottomBar::sizeHint() const -{ - return QSize{1, m_layout->parentWidget()->sizeHint().height()}; - // 1 as width because this widget should never be the reason the DolphinViewContainer is made wider. -} - -void SelectionModeBottomBar::slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl) -{ - if (m_contents == GeneralContents) { - auto contextActions = contextActionsFor(selection, baseUrl); - m_generalBarActions.clear(); - if (contextActions.empty()) { - if (isVisibleTo(parentWidget())) { - setVisibleInternal(false, WithAnimation); - } - } else { - for (auto i = contextActions.begin(); i != contextActions.end(); ++i) { - m_generalBarActions.emplace_back(ActionWithWidget{*i}); - } - resetContents(GeneralContents); - - if (m_allowedToBeVisible) { - setVisibleInternal(true, WithAnimation); - } - } - } - updateMainActionButton(selection); -} - -void SelectionModeBottomBar::slotSplitTabDisabled() -{ - switch (m_contents) { - case CopyToOtherViewContents: - case MoveToOtherViewContents: - Q_EMIT leaveSelectionModeRequested(); - default: - return; - } + m_layout = new QHBoxLayout(this); } -void SelectionModeBottomBar::resetContents(SelectionModeBottomBar::Contents contents) +void BottomBarContentsContainer::resetContents(BottomBar::Contents contents) { emptyBarContents(); @@ -163,73 +44,34 @@ void SelectionModeBottomBar::resetContents(SelectionModeBottomBar::Contents cont Q_CHECK_PTR(m_actionCollection); m_contents = contents; switch (contents) { - case CopyContents: - addCopyContents(); - break; - case CopyLocationContents: - addCopyLocationContents(); - break; - case CopyToOtherViewContents: - addCopyToOtherViewContents(); - break; - case CutContents: - addCutContents(); - break; - case DeleteContents: - addDeleteContents(); - break; - case DuplicateContents: - addDuplicateContents(); - break; - case GeneralContents: - addGeneralContents(); - break; - case PasteContents: - addPasteContents(); - break; - case MoveToOtherViewContents: - addMoveToOtherViewContents(); - break; - case MoveToTrashContents: - addMoveToTrashContents(); - break; - case RenameContents: + case BottomBar::CopyContents: + return addCopyContents(); + case BottomBar::CopyLocationContents: + return addCopyLocationContents(); + case BottomBar::CopyToOtherViewContents: + return addCopyToOtherViewContents(); + case BottomBar::CutContents: + return addCutContents(); + case BottomBar::DeleteContents: + return addDeleteContents(); + case BottomBar::DuplicateContents: + return addDuplicateContents(); + case BottomBar::GeneralContents: + return addGeneralContents(); + case BottomBar::PasteContents: + return addPasteContents(); + case BottomBar::MoveToOtherViewContents: + return addMoveToOtherViewContents(); + case BottomBar::MoveToTrashContents: + return addMoveToTrashContents(); + case BottomBar::RenameContents: return addRenameContents(); } - - if (m_allowedToBeVisible) { - setVisibleInternal(true, WithAnimation); - } -} - -bool SelectionModeBottomBar::eventFilter(QObject *watched, QEvent *event) -{ - Q_ASSERT(qobject_cast<QWidget *>(watched)); // This evenfFilter is only implemented for QWidgets. - - switch (event->type()) { - case QEvent::ChildAdded: - 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)) { - setMaximumHeight(sizeHint().height()); - } - }); - // Fall through. - default: - return false; - } } -void SelectionModeBottomBar::resizeEvent(QResizeEvent *resizeEvent) +void BottomBarContentsContainer::updateForNewWidth() { - if (resizeEvent->oldSize().width() == resizeEvent->size().width()) { - // The width() didn't change so our custom override isn't needed. - return QWidget::resizeEvent(resizeEvent); - } - m_layout->parentWidget()->setFixedWidth(resizeEvent->size().width()); - - if (m_contents == GeneralContents) { + if (m_contents == BottomBar::GeneralContents) { Q_ASSERT(m_overflowButton); if (unusedSpace() < 0) { // The bottom bar is overflowing! We need to hide some of the widgets. @@ -280,10 +122,31 @@ void SelectionModeBottomBar::resizeEvent(QResizeEvent *resizeEvent) // Hide the leading explanation if it doesn't fit. The buttons are labeled clear enough that this shouldn't be a big UX problem. updateExplanatoryLabelVisibility(); - return QWidget::resizeEvent(resizeEvent); } -void SelectionModeBottomBar::addCopyContents() +void BottomBarContentsContainer::slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl) +{ + if (m_contents == BottomBar::GeneralContents) { + auto contextActions = contextActionsFor(selection, baseUrl); + m_generalBarActions.clear(); + if (contextActions.empty()) { + Q_ASSERT(qobject_cast<BottomBar *>(parentWidget()->parentWidget()->parentWidget())); + if (isVisibleTo(parentWidget()->parentWidget()->parentWidget()->parentWidget())) { // is the bar visible + Q_EMIT barVisibilityChangeRequested(false); + } + } else { + for (auto i = contextActions.begin(); i != contextActions.end(); ++i) { + m_generalBarActions.emplace_back(ActionWithWidget{*i}); + } + resetContents(BottomBar::GeneralContents); + + Q_EMIT barVisibilityChangeRequested(true); + } + } + updateMainActionButton(selection); +} + +void BottomBarContentsContainer::addCopyContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be copied."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -292,14 +155,14 @@ void SelectionModeBottomBar::addCopyContents() // i18n: Aborts the current step-by-step process to copy files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Copying"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *copyButton = new QPushButton(this); // We claim to have PasteContents already so triggering the copy action next won't instantly hide the bottom bar. connect(copyButton, &QAbstractButton::clicked, [this]() { if (GeneralSettings::showPasteBarAfterCopying()) { - m_contents = Contents::PasteContents; + m_contents = BottomBar::Contents::PasteContents; } }); // Connect the copy action as a second step. @@ -307,7 +170,7 @@ void SelectionModeBottomBar::addCopyContents() // Finally connect the lambda that actually changes the contents to the PasteContents. connect(copyButton, &QAbstractButton::clicked, [this]() { if (GeneralSettings::showPasteBarAfterCopying()) { - resetContents(Contents::PasteContents); // resetContents() needs to be connected last because + resetContents(BottomBar::Contents::PasteContents); // resetContents() needs to be connected last because // it instantly deletes the button and then the other slots won't be called. } Q_EMIT leaveSelectionModeRequested(); @@ -316,7 +179,7 @@ void SelectionModeBottomBar::addCopyContents() m_layout->addWidget(copyButton); } -void SelectionModeBottomBar::addCopyLocationContents() +void BottomBarContentsContainer::addCopyLocationContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select one file or folder whose location should be copied."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -325,7 +188,7 @@ void SelectionModeBottomBar::addCopyLocationContents() // i18n: Aborts the current step-by-step process to copy the location of files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Copying"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *copyLocationButton = new QPushButton(this); @@ -334,7 +197,7 @@ void SelectionModeBottomBar::addCopyLocationContents() m_layout->addWidget(copyLocationButton); } -void SelectionModeBottomBar::addCopyToOtherViewContents() +void BottomBarContentsContainer::addCopyToOtherViewContents() { // i18n: "Copy over" refers to copying to the other split view area that is currently visible to the user. m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be copied over."), this); @@ -344,7 +207,7 @@ void SelectionModeBottomBar::addCopyToOtherViewContents() // i18n: Aborts the current step-by-step process to copy the location of files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Copying"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *copyToOtherViewButton = new QPushButton(this); @@ -353,7 +216,7 @@ void SelectionModeBottomBar::addCopyToOtherViewContents() m_layout->addWidget(copyToOtherViewButton); } -void SelectionModeBottomBar::addCutContents() +void BottomBarContentsContainer::addCutContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be cut."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -362,14 +225,14 @@ void SelectionModeBottomBar::addCutContents() // i18n: Aborts the current step-by-step process to cut files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Cutting"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *cutButton = new QPushButton(this); // We claim to have PasteContents already so triggering the cut action next won't instantly hide the bottom bar. connect(cutButton, &QAbstractButton::clicked, [this]() { if (GeneralSettings::showPasteBarAfterCopying()) { - m_contents = Contents::PasteContents; + m_contents = BottomBar::Contents::PasteContents; } }); // Connect the cut action as a second step. @@ -377,7 +240,7 @@ void SelectionModeBottomBar::addCutContents() // Finally connect the lambda that actually changes the contents to the PasteContents. connect(cutButton, &QAbstractButton::clicked, [this](){ if (GeneralSettings::showPasteBarAfterCopying()) { - resetContents(Contents::PasteContents); // resetContents() needs to be connected last because + resetContents(BottomBar::Contents::PasteContents); // resetContents() needs to be connected last because // it instantly deletes the button and then the other slots won't be called. } Q_EMIT leaveSelectionModeRequested(); @@ -386,7 +249,7 @@ void SelectionModeBottomBar::addCutContents() m_layout->addWidget(cutButton); } -void SelectionModeBottomBar::addDeleteContents() +void BottomBarContentsContainer::addDeleteContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be permanently deleted."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -395,7 +258,7 @@ void SelectionModeBottomBar::addDeleteContents() // i18n: Aborts the current step-by-step process to delete files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *deleteButton = new QPushButton(this); @@ -404,7 +267,7 @@ void SelectionModeBottomBar::addDeleteContents() m_layout->addWidget(deleteButton); } -void SelectionModeBottomBar::addDuplicateContents() +void BottomBarContentsContainer::addDuplicateContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be duplicated here."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -413,7 +276,7 @@ void SelectionModeBottomBar::addDuplicateContents() // i18n: Aborts the current step-by-step process to duplicate files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Duplicating"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *duplicateButton = new QPushButton(this); @@ -422,7 +285,7 @@ void SelectionModeBottomBar::addDuplicateContents() m_layout->addWidget(duplicateButton); } -void SelectionModeBottomBar::addGeneralContents() +void BottomBarContentsContainer::addGeneralContents() { if (!m_overflowButton) { m_overflowButton = new QToolButton{this}; @@ -473,7 +336,7 @@ void SelectionModeBottomBar::addGeneralContents() } } -void SelectionModeBottomBar::addMoveToOtherViewContents() +void BottomBarContentsContainer::addMoveToOtherViewContents() { // i18n: "Move over" refers to moving to the other split view area that is currently visible to the user. m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be moved over."), this); @@ -483,7 +346,7 @@ void SelectionModeBottomBar::addMoveToOtherViewContents() // i18n: Aborts the current step-by-step process to copy the location of files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort Moving"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *moveToOtherViewButton = new QPushButton(this); @@ -492,7 +355,7 @@ void SelectionModeBottomBar::addMoveToOtherViewContents() m_layout->addWidget(moveToOtherViewButton); } -void SelectionModeBottomBar::addMoveToTrashContents() +void BottomBarContentsContainer::addMoveToTrashContents() { m_explanatoryLabel = new QLabel(i18nc("@info explaining the next step in a process", "Select the files and folders that should be moved to the Trash."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -501,7 +364,7 @@ void SelectionModeBottomBar::addMoveToTrashContents() // i18n: Aborts the current step-by-step process of moving files to the trash by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Abort"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *moveToTrashButton = new QPushButton(this); @@ -510,7 +373,7 @@ void SelectionModeBottomBar::addMoveToTrashContents() m_layout->addWidget(moveToTrashButton); } -void SelectionModeBottomBar::addPasteContents() +void BottomBarContentsContainer::addPasteContents() { m_explanatoryLabel = new QLabel(xi18n("<para>The selected files and folders were added to the Clipboard. " "Now the <emphasis>Paste</emphasis> action can be used to transfer them from the Clipboard " @@ -526,7 +389,7 @@ void SelectionModeBottomBar::addPasteContents() /** We are in "PasteContents" mode which means hiding the bottom bar is impossible. * So we first have to claim that we have different contents before requesting to leave selection mode. */ auto actuallyLeaveSelectionMode = [this]() { - m_contents = Contents::CopyLocationContents; + m_contents = BottomBar::Contents::CopyLocationContents; Q_EMIT leaveSelectionModeRequested(); }; @@ -553,7 +416,7 @@ void SelectionModeBottomBar::addPasteContents() m_explanatoryLabel->setMaximumHeight(pasteButton->sizeHint().height() + dismissButton->sizeHint().height() + m_explanatoryLabel->fontMetrics().height()); } -void SelectionModeBottomBar::addRenameContents() +void BottomBarContentsContainer::addRenameContents() { m_explanatoryLabel = new QLabel(i18nc("@info explains the next step in a process", "Select the file or folder that should be renamed.\nBulk renaming is possible when multiple items are selected."), this); m_explanatoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); @@ -562,7 +425,7 @@ void SelectionModeBottomBar::addRenameContents() // i18n: Aborts the current step-by-step process to delete files by leaving the selection mode. auto *cancelButton = new QPushButton(i18nc("@action:button", "Stop Renaming"), this); - connect(cancelButton, &QAbstractButton::clicked, this, &SelectionModeBottomBar::leaveSelectionModeRequested); + connect(cancelButton, &QAbstractButton::clicked, this, &BottomBarContentsContainer::leaveSelectionModeRequested); m_layout->addWidget(cancelButton); auto *renameButton = new QPushButton(this); @@ -571,7 +434,7 @@ void SelectionModeBottomBar::addRenameContents() m_layout->addWidget(renameButton); } -void SelectionModeBottomBar::emptyBarContents() +void BottomBarContentsContainer::emptyBarContents() { QLayoutItem *child; while ((child = m_layout->takeAt(0)) != nullptr) { @@ -587,7 +450,7 @@ void SelectionModeBottomBar::emptyBarContents() } } -std::vector<QAction *> SelectionModeBottomBar::contextActionsFor(const KFileItemList& selectedItems, const QUrl& baseUrl) +std::vector<QAction *> BottomBarContentsContainer::contextActionsFor(const KFileItemList& selectedItems, const QUrl& baseUrl) { if (selectedItems.isEmpty()) { // There are no contextual actions to show for these items. @@ -610,7 +473,7 @@ std::vector<QAction *> SelectionModeBottomBar::contextActionsFor(const KFileItem if (!m_fileItemActions) { m_fileItemActions = new KFileItemActions(this); m_fileItemActions->setParentWidget(dolphinMainWindow); - connect(m_fileItemActions, &KFileItemActions::error, this, &SelectionModeBottomBar::error); + connect(m_fileItemActions, &KFileItemActions::error, this, &BottomBarContentsContainer::error); } m_internalContextMenu = std::make_unique<DolphinContextMenu>(dolphinMainWindow, selectedItems.constFirst(), selectedItems, baseUrl, m_fileItemActions); auto internalContextMenuActions = m_internalContextMenu->actions(); @@ -640,7 +503,7 @@ std::vector<QAction *> SelectionModeBottomBar::contextActionsFor(const KFileItem return contextActions; } -int SelectionModeBottomBar::unusedSpace() const +int BottomBarContentsContainer::unusedSpace() const { int sumOfPreferredWidths = m_layout->contentsMargins().left() + m_layout->contentsMargins().right(); if (m_overflowButton) { @@ -653,11 +516,13 @@ int SelectionModeBottomBar::unusedSpace() const } sumOfPreferredWidths += m_layout->itemAt(i)->sizeHint().width() + m_layout->spacing(); } - return width() - sumOfPreferredWidths - 20; // We consider all space used when there are only 20 pixels left - // so there is some room to breath and not too much wonkyness while resizing. + Q_ASSERT(qobject_cast<BottomBar *>(parentWidget()->parentWidget()->parentWidget())); + const int totalBarWidth = parentWidget()->parentWidget()->parentWidget()->width(); + return totalBarWidth - sumOfPreferredWidths - 20; // We consider all space used when there are only 20 pixels left + // so there is some room to breath and not too much wonkyness while resizing. } -void SelectionModeBottomBar::updateExplanatoryLabelVisibility() +void BottomBarContentsContainer::updateExplanatoryLabelVisibility() { if (!m_explanatoryLabel) { return; @@ -670,7 +535,7 @@ void SelectionModeBottomBar::updateExplanatoryLabelVisibility() } } -void SelectionModeBottomBar::updateMainActionButton(const KFileItemList& selection) +void BottomBarContentsContainer::updateMainActionButton(const KFileItemList& selection) { if (!m_mainAction.widget()) { return; @@ -683,37 +548,37 @@ void SelectionModeBottomBar::updateMainActionButton(const KFileItemList& selecti QString buttonText; switch (m_contents) { - case CopyContents: + case BottomBar::CopyContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Copy action", "Copy %2 to the Clipboard", "Copy %2 to the Clipboard", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case CopyLocationContents: + case BottomBar::CopyLocationContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Copy Location action", "Copy the Location of %2 to the Clipboard", "Copy the Location of %2 to the Clipboard", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case CutContents: + case BottomBar::CutContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Cut action", "Cut %2 to the Clipboard", "Cut %2 to the Clipboard", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case DeleteContents: + case BottomBar::DeleteContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Delete action", "Permanently Delete %2", "Permanently Delete %2", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case DuplicateContents: + case BottomBar::DuplicateContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Duplicate action", "Duplicate %2", "Duplicate %2", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case MoveToTrashContents: + case BottomBar::MoveToTrashContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Trash action", "Move %2 to the Trash", "Move %2 to the Trash", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); break; - case RenameContents: + case BottomBar::RenameContents: buttonText = i18ncp("@action A more elaborate and clearly worded version of the Rename action", "Rename %2", "Rename %2", selection.count(), fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); diff --git a/src/selectionmode/selectionmodebottombar.h b/src/selectionmode/bottombarcontentscontainer.h index 89fd3c3a3..6255ff987 100644 --- a/src/selectionmode/selectionmodebottombar.h +++ b/src/selectionmode/bottombarcontentscontainer.h @@ -5,113 +5,74 @@ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ -#ifndef SELECTIONMODEBOTTOMBAR_H -#define SELECTIONMODEBOTTOMBAR_H +#ifndef BOTTOMBARCONTENTSCONTAINER_H +#define BOTTOMBARCONTENTSCONTAINER_H #include "actionwithwidget.h" -#include "global.h" +#include "bottombar.h" -#include <QAction> #include <QPointer> -#include <QPropertyAnimation> +#include <QToolButton> #include <QWidget> -#include <memory> - class DolphinContextMenu; class KActionCollection; class KFileItemActions; class KFileItemList; -class QAbstractButton; -class QAction; -class QFontMetrics; class QHBoxLayout; class QLabel; -class QPushButton; -class QResizeEvent; -class QToolButton; class QUrl; +namespace SelectionMode +{ + /** - * A bar mainly used in selection mode that serves various purposes depending on what the user is currently trying to do. + * @brief An internal widget of BottomBar that controls the visible contents/widgets on it. * - * The Contents enum below gives a rough idea about the different states this bar might have. - * The bar is notified of various changes that make changing or updating the content worthwhile. + * This class should only be interacted with from the BottomBar class. + * @see BottomBar */ -class SelectionModeBottomBar : public QWidget +class BottomBarContentsContainer : public QWidget { Q_OBJECT public: - /** The different contents this bar can have. */ - enum Contents{ - CopyContents, - CopyLocationContents, - CopyToOtherViewContents, - CutContents, - DeleteContents, - DuplicateContents, - GeneralContents, - MoveToOtherViewContents, - MoveToTrashContents, - PasteContents, - RenameContents - }; - /** - * Default constructor + * @param actionCollection the collection where the actions for the contents are retrieved from + * @param parent the parent widget. Typically a ScrollView within the BottomBar */ - explicit SelectionModeBottomBar(KActionCollection *actionCollection, QWidget *parent); + explicit BottomBarContentsContainer(KActionCollection *actionCollection, 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. - * - * This bar might also not show itself when setVisible(true), when context menu actions are supposed to be shown - * for the selected items but no items have been selected yet. In that case it will only show itself once items were selected. - * @see QWidget::setVisible() - */ - void setVisible(bool visible, Animated animated); - using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget. - - void resetContents(Contents contents); - inline Contents contents() const + void resetContents(BottomBar::Contents contents); + inline BottomBar::Contents contents() const { return m_contents; }; - /** @returns a width of 1 to make sure that this bar never causes side panels to shrink. */ - QSize sizeHint() const override; + inline bool hasSomethingToShow() { + return contents() != BottomBar::GeneralContents || m_internalContextMenu; + } + + void updateForNewWidth(); public Q_SLOTS: void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl); - /** Used to notify the m_selectionModeBottomBar that there is no other ViewContainer in the tab. */ - void slotSplitTabDisabled(); - Q_SIGNALS: /** * Forwards the errors from the KFileItemAction::error() used for contextual actions. */ void error(const QString &errorMessage); - void leaveSelectionModeRequested(); - -protected: - /** Is installed on an internal widget to make sure that the height of the bar is adjusted to its contents. */ - bool eventFilter(QObject *watched, QEvent *event) override; - - /** Adapts the way the contents of this bar are displayed based on the available width. */ - void resizeEvent(QResizeEvent *resizeEvent) override; - -private: /** - * Identical to SelectionModeBottomBar::setVisible() but doesn't change m_allowedToBeVisible. - * @see SelectionModeBottomBar::setVisible() - * @see m_allowedToBeVisible + * Sometimes the contents see no reason to be visible and request the bar to be hidden instead which emits this signal. + * This can later change e.g. because the user selected items. Then this signal is used to request showing of the bar. */ - void setVisibleInternal(bool visible, Animated animated); + void barVisibilityChangeRequested(bool visible); + void leaveSelectionModeRequested(); + +private: void addCopyContents(); void addCopyLocationContents(); void addCopyToOtherViewContents(); @@ -179,20 +140,15 @@ private: /// The actionCollection from which the actions for this bar are retrieved. KActionCollection *m_actionCollection; /// Describes the current contents of the bar. - Contents m_contents; + BottomBar::Contents m_contents; /** The layout all the buttons and labels are added to. * Do not confuse this with layout() because we do have a QScrollView in between this widget and m_layout. */ QHBoxLayout *m_layout; - /** Remembers if this bar was setVisible(true) or setVisible(false) the last time. - * 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; - /// The info label used for some of the BarContents. Is hidden for narrow widths. QPointer<QLabel> m_explanatoryLabel; }; -#endif // SELECTIONMODEBOTTOMBAR_H +} + +#endif // BOTTOMBARCONTENTSCONTAINER_H diff --git a/src/selectionmode/singleclickselectionproxystyle.h b/src/selectionmode/singleclickselectionproxystyle.h index 65117c56c..ece46cce6 100644 --- a/src/selectionmode/singleclickselectionproxystyle.h +++ b/src/selectionmode/singleclickselectionproxystyle.h @@ -10,8 +10,13 @@ #include <QProxyStyle> +namespace SelectionMode +{ + /** - * @todo write docs + * @brief A simple proxy style to temporarily make single click select and not activate + * + * @see QProxyStyle */ class SingleClickSelectionProxyStyle : public QProxyStyle { @@ -26,4 +31,6 @@ public: } }; +} + #endif // SINGLECLICKSELECTIONPROXYSTYLE_H diff --git a/src/selectionmode/selectionmodetopbar.cpp b/src/selectionmode/topbar.cpp index 83aa8e849..d783c76ca 100644 --- a/src/selectionmode/selectionmodetopbar.cpp +++ b/src/selectionmode/topbar.cpp @@ -5,7 +5,7 @@ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ -#include "selectionmodetopbar.h" +#include "topbar.h" #include "backgroundcolorhelper.h" @@ -22,7 +22,9 @@ #include <QStyle> #include <QtGlobal> -SelectionModeTopBar::SelectionModeTopBar(QWidget *parent) : +using namespace SelectionMode; + +TopBar::TopBar(QWidget *parent) : QWidget{parent} { // Showing of this widget is normally animated. We hide it for now and make it small. @@ -68,7 +70,7 @@ SelectionModeTopBar::SelectionModeTopBar(QWidget *parent) : m_closeButton->setAccessibleName(m_closeButton->toolTip()); m_closeButton->setFlat(true); connect(m_closeButton, &QAbstractButton::pressed, - this, &SelectionModeTopBar::leaveSelectionModeRequested); + this, &TopBar::leaveSelectionModeRequested); QHBoxLayout *layout = new QHBoxLayout(contentsContainer); auto contentsMargins = layout->contentsMargins(); @@ -83,7 +85,7 @@ SelectionModeTopBar::SelectionModeTopBar(QWidget *parent) : layout->addWidget(m_closeButton); } -void SelectionModeTopBar::setVisible(bool visible, Animated animated) +void TopBar::setVisible(bool visible, Animated animated) { Q_ASSERT_X(animated == WithAnimation, "SelectionModeTopBar::setVisible", "This wasn't implemented."); @@ -109,12 +111,12 @@ void SelectionModeTopBar::setVisible(bool visible, Animated animated) m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped); } -void SelectionModeTopBar::resizeEvent(QResizeEvent */* resizeEvent */) +void TopBar::resizeEvent(QResizeEvent */* resizeEvent */) { updateLabelString(); } -void SelectionModeTopBar::updateLabelString() +void TopBar::updateLabelString() { QFontMetrics fontMetrics = m_label->fontMetrics(); if (fontMetrics.horizontalAdvance(m_fullLabelString) + m_closeButton->sizeHint().width() + style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 2 + style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 2 < width()) { diff --git a/src/selectionmode/selectionmodetopbar.h b/src/selectionmode/topbar.h index eb26a5c26..e0cd34935 100644 --- a/src/selectionmode/selectionmodetopbar.h +++ b/src/selectionmode/topbar.h @@ -21,15 +21,18 @@ class QPushButton; class QResizeEvent; class QShowEvent; +namespace SelectionMode +{ + /** - * @todo write docs + * @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 SelectionModeTopBar : public QWidget +class TopBar : public QWidget { Q_OBJECT public: - SelectionModeTopBar(QWidget *parent); + TopBar(QWidget *parent); /** * Plays a show or hide animation while changing visibility. @@ -63,4 +66,6 @@ private: QPointer<QPropertyAnimation> m_heightAnimation; }; +} + #endif // SELECTIONMODETOPBAR_H |
