]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Better separation of classes
authorFelix Ernst <fe.a.ernst@gmail.com>
Sun, 24 Apr 2022 11:18:30 +0000 (13:18 +0200)
committerFelix Ernst <fe.a.ernst@gmail.com>
Sun, 14 Aug 2022 14:42:40 +0000 (14:42 +0000)
Make obvious when actions trigger selection mode.

22 files changed:
src/CMakeLists.txt
src/dolphinmainwindow.cpp
src/dolphinmainwindow.h
src/dolphinpart.cpp
src/dolphinviewcontainer.cpp
src/dolphinviewcontainer.h
src/selectionmode/actiontexthelper.cpp [new file with mode: 0644]
src/selectionmode/actiontexthelper.h [new file with mode: 0644]
src/selectionmode/actionwithwidget.cpp
src/selectionmode/actionwithwidget.h
src/selectionmode/backgroundcolorhelper.cpp
src/selectionmode/backgroundcolorhelper.h
src/selectionmode/bottombar.cpp [new file with mode: 0644]
src/selectionmode/bottombar.h [new file with mode: 0644]
src/selectionmode/bottombarcontentscontainer.cpp [moved from src/selectionmode/selectionmodebottombar.cpp with 73% similarity]
src/selectionmode/bottombarcontentscontainer.h [moved from src/selectionmode/selectionmodebottombar.h with 57% similarity]
src/selectionmode/singleclickselectionproxystyle.h
src/selectionmode/topbar.cpp [moved from src/selectionmode/selectionmodetopbar.cpp with 93% similarity]
src/selectionmode/topbar.h [moved from src/selectionmode/selectionmodetopbar.h with 86% similarity]
src/views/dolphinview.cpp
src/views/dolphinviewactionhandler.cpp
src/views/dolphinviewactionhandler.h

index 504e3729c313a6547b73965e0f2d3dc4189e821f..4b4b5d87b8fc4ac89a30c5e81a2ee8f8dfd85dcc 100644 (file)
@@ -87,6 +87,7 @@ target_sources(dolphinprivate PRIVATE
     settings/viewmodes/viewmodesettings.cpp
     settings/viewpropertiesdialog.cpp
     settings/viewpropsprogressinfo.cpp
+    selectionmode/actiontexthelper.cpp
     views/dolphinfileitemlistwidget.cpp
     views/dolphinitemlistview.cpp
     views/dolphinnewfilemenuobserver.cpp
@@ -219,10 +220,12 @@ target_sources(dolphinstatic PRIVATE
     search/dolphinfacetswidget.cpp
     search/dolphinquery.cpp
     search/dolphinsearchbox.cpp
+    selectionmode/actiontexthelper.cpp
     selectionmode/actionwithwidget.cpp
     selectionmode/backgroundcolorhelper.cpp
-    selectionmode/selectionmodebottombar.cpp
-    selectionmode/selectionmodetopbar.cpp
+    selectionmode/bottombar.cpp
+    selectionmode/bottombarcontentscontainer.cpp
+    selectionmode/topbar.cpp
     settings/general/behaviorsettingspage.cpp
     settings/general/configurepreviewplugindialog.cpp
     settings/general/confirmationssettingspage.cpp
index fae30761e927e2b345f9b5a742f909c3c5ef712c..d8d30448321c3d87a52fc5753a9055feb6982b1c 100644 (file)
@@ -24,6 +24,7 @@
 #include "panels/folders/folderspanel.h"
 #include "panels/places/placespanel.h"
 #include "panels/terminal/terminalpanel.h"
+#include "selectionmode/actiontexthelper.h"
 #include "settings/dolphinsettingsdialog.h"
 #include "statusbar/dolphinstatusbar.h"
 #include "views/dolphinviewactionhandler.h"
@@ -124,7 +125,7 @@ DolphinMainWindow::DolphinMainWindow() :
     setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
     setObjectName(QStringLiteral("Dolphin#"));
 
-    setStateConfigGroup("State");
+   // setStateConfigGroup("State");
 
     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
             this, &DolphinMainWindow::showErrorMessage);
@@ -160,9 +161,10 @@ DolphinMainWindow::DolphinMainWindow() :
             this, &DolphinMainWindow::updateWindowTitle);
     setCentralWidget(m_tabWidget);
 
+    m_actionTextHelper = new SelectionMode::ActionTextHelper(this);
     setupActions();
 
-    m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
+    m_actionHandler = new DolphinViewActionHandler(actionCollection(), m_actionTextHelper, this);
     connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
     connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory);
     connect(m_actionHandler, &DolphinViewActionHandler::setSelectionMode, this, &DolphinMainWindow::slotSetSelectionMode);
@@ -715,7 +717,7 @@ void DolphinMainWindow::undo()
 void DolphinMainWindow::cut()
 {
     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
-        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionModeBottomBar::Contents::CutContents);
+        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CutContents);
     } else {
         m_activeViewContainer->view()->cutSelectedItemsToClipboard();
         m_activeViewContainer->setSelectionModeEnabled(false);
@@ -725,7 +727,7 @@ void DolphinMainWindow::cut()
 void DolphinMainWindow::copy()
 {
     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
-        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionModeBottomBar::Contents::CopyContents);
+        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CopyContents);
     } else {
         m_activeViewContainer->view()->copySelectedItemsToClipboard();
         m_activeViewContainer->setSelectionModeEnabled(false);
@@ -856,7 +858,7 @@ void DolphinMainWindow::slotGoForward(QAction* action)
     }
 }
 
-void DolphinMainWindow::slotSetSelectionMode(bool enabled, SelectionModeBottomBar::Contents bottomBarContents)
+void DolphinMainWindow::slotSetSelectionMode(bool enabled, SelectionMode::BottomBar::Contents bottomBarContents)
 {
     m_activeViewContainer->setSelectionModeEnabled(enabled, actionCollection(), bottomBarContents);
 }
@@ -903,7 +905,7 @@ void DolphinMainWindow::toggleSplitStash()
 void DolphinMainWindow::copyToInactiveSplitView()
 {
     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
-        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionModeBottomBar::Contents::CopyToOtherViewContents);
+        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CopyToOtherViewContents);
     } else {
         m_tabWidget->copyToInactiveSplitView();
         m_activeViewContainer->setSelectionModeEnabled(false);
@@ -913,7 +915,7 @@ void DolphinMainWindow::copyToInactiveSplitView()
 void DolphinMainWindow::moveToInactiveSplitView()
 {
     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
-        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionModeBottomBar::Contents::MoveToOtherViewContents);
+        m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::MoveToOtherViewContents);
     } else {
         m_tabWidget->moveToInactiveSplitView();
         m_activeViewContainer->setSelectionModeEnabled(false);
@@ -946,7 +948,7 @@ void DolphinMainWindow::toggleSelectionMode()
 {
     const bool checked = !m_activeViewContainer->isSelectionModeEnabled();
 
-    m_activeViewContainer->setSelectionModeEnabled(checked, actionCollection(), SelectionModeBottomBar::Contents::GeneralContents);
+    m_activeViewContainer->setSelectionModeEnabled(checked, actionCollection(), SelectionMode::BottomBar::Contents::GeneralContents);
     actionCollection()->action(QStringLiteral("toggle_selection_mode"))->setChecked(checked);
 }
 
@@ -1584,12 +1586,14 @@ void DolphinMainWindow::setupActions()
         "next to each other on the keyboard: <shortcut>Ctrl+X</shortcut>, "
         "<shortcut>Ctrl+C</shortcut> and <shortcut>Ctrl+V</shortcut>.</para>");
     QAction* cutAction = KStandardAction::cut(this, &DolphinMainWindow::cut, actionCollection());
+    m_actionTextHelper->registerTextWhenNothingIsSelected(cutAction, i18nc("@action", "Cut…"));
     cutAction->setWhatsThis(xi18nc("@info:whatsthis cut", "This copies the items "
         "in your current selection to the <emphasis>clipboard</emphasis>.<nl/>"
         "Use the <emphasis>Paste</emphasis> action afterwards to copy them from "
         "the clipboard to a new location. The items will be removed from their "
         "initial location.") + cutCopyPastePara);
     QAction* copyAction = KStandardAction::copy(this, &DolphinMainWindow::copy, actionCollection());
+    m_actionTextHelper->registerTextWhenNothingIsSelected(copyAction, i18nc("@action", "Copy…"));
     copyAction->setWhatsThis(xi18nc("@info:whatsthis copy", "This copies the "
         "items in your current selection to the <emphasis>clipboard</emphasis>."
         "<nl/>Use the <emphasis>Paste</emphasis> action afterwards to copy them "
@@ -1606,6 +1610,7 @@ void DolphinMainWindow::setupActions()
 
     QAction* copyToOtherViewAction = actionCollection()->addAction(QStringLiteral("copy_to_inactive_split_view"));
     copyToOtherViewAction->setText(i18nc("@action:inmenu", "Copy to Inactive Split View"));
+    m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Inactive Split View…"));
     copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy", "This copies the selected items from "
         "the <emphasis>active</emphasis> view to the inactive split view."));
     copyToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
@@ -1615,6 +1620,7 @@ void DolphinMainWindow::setupActions()
 
     QAction* moveToOtherViewAction = actionCollection()->addAction(QStringLiteral("move_to_inactive_split_view"));
     moveToOtherViewAction->setText(i18nc("@action:inmenu", "Move to Inactive Split View"));
+    m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Inactive Split View…"));
     moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move", "This moves the selected items from "
         "the <emphasis>active</emphasis> view to the inactive split view."));
     moveToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut")));
@@ -2229,7 +2235,7 @@ void DolphinMainWindow::updateFileAndEditActions()
     QAction* deleteAction            = col->action(KStandardAction::name(KStandardAction::DeleteFile));
     QAction* cutAction               = col->action(KStandardAction::name(KStandardAction::Cut));
     QAction* duplicateAction         = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler
-    QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places"));
+    QAction* addToPlacesAction       = col->action(QStringLiteral("add_to_places"));
     QAction* copyToOtherViewAction   = col->action(QStringLiteral("copy_to_inactive_split_view"));
     QAction* moveToOtherViewAction   = col->action(QStringLiteral("move_to_inactive_split_view"));
     QAction* copyLocation            = col->action(QString("copy_location"));
@@ -2245,7 +2251,11 @@ void DolphinMainWindow::updateFileAndEditActions()
         duplicateAction->setEnabled(true);
         addToPlacesAction->setEnabled(true);
         copyLocation->setEnabled(true);
+        // Them triggering selection mode and not directly acting on selected items is signified by adding "…" to their text.
+        m_actionTextHelper->textsWhenNothingIsSelectedEnabled(true);
+
     } else {
+        m_actionTextHelper->textsWhenNothingIsSelectedEnabled(false);
         stateChanged(QStringLiteral("has_selection"));
 
         QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler
index 0c9c762d63593271d16288e26013ea5933647736..fe844ad1b26062a1bed8532294f5372da2ed9d45 100644 (file)
@@ -10,7 +10,7 @@
 #define DOLPHIN_MAINWINDOW_H
 
 #include "dolphintabwidget.h"
-#include "selectionmode/selectionmodebottombar.h"
+#include "selectionmode/bottombar.h"
 #include "config-dolphin.h"
 #include <KFileItemActions>
 #include <kio/fileundomanager.h>
@@ -48,6 +48,9 @@ class TerminalPanel;
 namespace KIO {
     class OpenUrlJob;
 }
+namespace SelectionMode {
+    class ActionTextHelper;
+}
 
 /**
  * @short Main window for Dolphin.
@@ -315,7 +318,7 @@ private Q_SLOTS:
     void updatePasteAction();
 
     /** Calls DolphinViewContainer::setSelectionMode() for m_activeViewContainer. */
-    void slotSetSelectionMode(bool enabled, SelectionModeBottomBar::Contents bottomBarContents);
+    void slotSetSelectionMode(bool enabled, SelectionMode::BottomBar::Contents bottomBarContents);
 
     /** Selects all items from the active view. */
     void selectAll();
@@ -705,6 +708,7 @@ private:
     DolphinRemoteEncoding* m_remoteEncoding;
     QPointer<DolphinSettingsDialog> m_settingsDialog;
     DolphinBookmarkHandler* m_bookmarkHandler;
+    SelectionMode::ActionTextHelper* m_actionTextHelper;
 
     // Members for the toolbar menu that is shown when the menubar is hidden:
     QToolButton* m_controlButton;
index 8f2279d45f8abd92d05c12eb2f7b3adae34dce18..575c304178e8b066982e704cfa0027da03f7a971 100644 (file)
@@ -110,7 +110,7 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent,
     connect(m_view, &DolphinView::itemCountChanged, this, &DolphinPart::updateStatusBar);
     connect(m_view,  &DolphinView::selectionChanged, this, &DolphinPart::updateStatusBar);
 
-    m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
+    m_actionHandler = new DolphinViewActionHandler(actionCollection(), nullptr, this);
     m_actionHandler->setCurrentView(m_view);
     connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinPart::createDirectory);
 
index d45096d0b860ed7e081bf5ef1938b81ee609eca9..c801d095d95e04a819b85389d026f11958a4c065 100644 (file)
@@ -12,7 +12,7 @@
 #include "filterbar/filterbar.h"
 #include "global.h"
 #include "search/dolphinsearchbox.h"
-#include "selectionmode/selectionmodetopbar.h"
+#include "selectionmode/topbar.h"
 #include "statusbar/dolphinstatusbar.h"
 #include "views/viewmodecontroller.h"
 #include "views/viewproperties.h"
@@ -373,7 +373,7 @@ void DolphinViewContainer::disconnectUrlNavigator()
     m_urlNavigatorConnected = nullptr;
 }
 
-void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection, SelectionModeBottomBar::Contents bottomBarContents)
+void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection, SelectionMode::BottomBar::Contents bottomBarContents)
 {
     const bool wasEnabled = m_view->selectionMode();
     m_view->setSelectionMode(enabled);
@@ -395,28 +395,28 @@ void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollecti
             setSelectionModeEnabled(false);
         });
 
-        m_selectionModeTopBar = new SelectionModeTopBar(this); // will be created hidden
-        connect(m_selectionModeTopBar, &SelectionModeTopBar::leaveSelectionModeRequested, this, [this]() {
+        m_selectionModeTopBar = new SelectionMode::TopBar(this); // will be created hidden
+        connect(m_selectionModeTopBar, &SelectionMode::TopBar::leaveSelectionModeRequested, this, [this]() {
             setSelectionModeEnabled(false);
         });
         m_topLayout->addWidget(m_selectionModeTopBar, positionFor.selectionModeTopBar, 0);
     }
 
     if (!m_selectionModeBottomBar) {
-        m_selectionModeBottomBar = new SelectionModeBottomBar(actionCollection, this);
+        m_selectionModeBottomBar = new SelectionMode::BottomBar(actionCollection, this);
         connect(m_view, &DolphinView::selectionChanged, this, [this](const KFileItemList &selection) {
             m_selectionModeBottomBar->slotSelectionChanged(selection, m_view->url());
         });
-        connect(m_selectionModeBottomBar, &SelectionModeBottomBar::error, this, [this](const QString &errorMessage) {
+        connect(m_selectionModeBottomBar, &SelectionMode::BottomBar::error, this, [this](const QString &errorMessage) {
             showErrorMessage(errorMessage);
         });
-        connect(m_selectionModeBottomBar, &SelectionModeBottomBar::leaveSelectionModeRequested, this, [this]() {
+        connect(m_selectionModeBottomBar, &SelectionMode::BottomBar::leaveSelectionModeRequested, this, [this]() {
             setSelectionModeEnabled(false);
         });
         m_topLayout->addWidget(m_selectionModeBottomBar, positionFor.selectionModeBottomBar, 0);
     }
     m_selectionModeBottomBar->resetContents(bottomBarContents);
-    if (bottomBarContents == SelectionModeBottomBar::GeneralContents) {
+    if (bottomBarContents == SelectionMode::BottomBar::GeneralContents) {
         m_selectionModeBottomBar->slotSelectionChanged(m_view->selectedItems(), m_view->url());
     }
 
@@ -433,12 +433,12 @@ bool DolphinViewContainer::isSelectionModeEnabled() const
     Q_ASSERT((!isEnabled
               // We can't assert that the bars are invisible only because the selection mode is disabled because the hide animation might still be playing.
               && (!m_selectionModeBottomBar || !m_selectionModeBottomBar->isEnabled() ||
-                  !m_selectionModeBottomBar->isVisible() || m_selectionModeBottomBar->contents() == SelectionModeBottomBar::PasteContents))
+                  !m_selectionModeBottomBar->isVisible() || m_selectionModeBottomBar->contents() == SelectionMode::BottomBar::PasteContents))
           || ( isEnabled
               && m_selectionModeTopBar && m_selectionModeTopBar->isVisible()
               // The bottom bar is either visible or was hidden because it has nothing to show in GeneralContents mode e.g. because no items are selected.
               && m_selectionModeBottomBar
-              && (m_selectionModeBottomBar->isVisible() || m_selectionModeBottomBar->contents() == SelectionModeBottomBar::GeneralContents)));
+              && (m_selectionModeBottomBar->isVisible() || m_selectionModeBottomBar->contents() == SelectionMode::BottomBar::GeneralContents)));
     return isEnabled;
 }
 
index a0936efd3da083ad8df758490e0b67e4f14c00e6..9d5cec11f1e8a51c76e1ab5f0cee996db3fca7e5 100644 (file)
@@ -9,7 +9,7 @@
 
 #include "config-dolphin.h"
 #include "dolphinurlnavigator.h"
-#include "selectionmode/selectionmodebottombar.h"
+#include "selectionmode/bottombar.h"
 #include "views/dolphinview.h"
 
 #include <KFileItem>
@@ -33,7 +33,9 @@ class QGridLayout;
 class QUrl;
 class DolphinSearchBox;
 class DolphinStatusBar;
-class SelectionModeTopBar;
+namespace SelectionMode {
+    class TopBar;
+}
 
 /**
  * @short Represents a view for the directory content
@@ -135,7 +137,7 @@ public:
      */
     void disconnectUrlNavigator();
 
-    void setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection = nullptr, SelectionModeBottomBar::Contents bottomBarContents = SelectionModeBottomBar::Contents::GeneralContents);
+    void setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection = nullptr, SelectionMode::BottomBar::Contents bottomBarContents = SelectionMode::BottomBar::Contents::GeneralContents);
     bool isSelectionModeEnabled() const;
 
     /**
@@ -429,14 +431,14 @@ private:
     KMessageWidget* m_messageWidget;
 
     /// A bar shown at the top of the view to signify that selection mode is currently active.
-    SelectionModeTopBar *m_selectionModeTopBar;
+    SelectionMode::TopBar *m_selectionModeTopBar;
 
     DolphinView* m_view;
 
     FilterBar* m_filterBar;
 
     /// A bar shown at the bottom of the view whose contents depend on what the user is currently doing.
-    SelectionModeBottomBar *m_selectionModeBottomBar;
+    SelectionMode::BottomBar *m_selectionModeBottomBar;
 
     DolphinStatusBar* m_statusBar;
     QTimer* m_statusBarTimer;            // Triggers a delayed update
diff --git a/src/selectionmode/actiontexthelper.cpp b/src/selectionmode/actiontexthelper.cpp
new file mode 100644 (file)
index 0000000..3eb868a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    This file is part of the KDE project
+    SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+    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 (file)
index 0000000..8f7501f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    This file is part of the KDE project
+    SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+    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
index e9823af7c983eec49b3b89242e4106e8d86dfcf1..82ce045dbfce2d9a7501c73878d745649143cb93 100644 (file)
@@ -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());
index 62dad0fc1df38659952a88a0924126e38c40f75a..cf7b8bc35151eab2d25d5cf8cfa3385e12cd5f10 100644 (file)
@@ -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
index ca110e7627867b38a7067c6e6fa7f8da870c4a11..4477d0f2c1dedb4376067a934032f344acd47f0c 100644 (file)
@@ -14,6 +14,8 @@
 #include <QtGlobal>
 #include <QWidget>
 
+using namespace SelectionMode;
+
 BackgroundColorHelper *BackgroundColorHelper::instance()
 {
     if (!s_instance) {
index 013d3368540725cdeaa01202b2a27bffb99195dc..8d2730fcf1165e8291b96056465fb5ce06ea615a 100644 (file)
@@ -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 (file)
index 0000000..999b24a
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+    This file is part of the KDE project
+    SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+    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 (file)
index 0000000..ff23b66
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    This file is part of the KDE project
+    SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+    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
similarity index 73%
rename from src/selectionmode/selectionmodebottombar.cpp
rename to src/selectionmode/bottombarcontentscontainer.cpp
index d10a8581bab025604e6c468e4f33cdf578668a43..04b52a60f1e4bdd40bd358e3dfc7ccf58c27ad73 100644 (file)
     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)
+void BottomBarContentsContainer::updateForNewWidth()
 {
-    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)
-{
-    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));
similarity index 57%
rename from src/selectionmode/selectionmodebottombar.h
rename to src/selectionmode/bottombarcontentscontainer.h
index 89fd3c3a374bfc251a181a350bda30d7b0cfe88f..6255ff987cdb49159b0130a42dd93d5f54b073be 100644 (file)
     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
index 65117c56c015bed27df8c3f74063b7357e118c2a..ece46cce6d2f16e639072316282bb75022b5761d 100644 (file)
 
 #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
similarity index 93%
rename from src/selectionmode/selectionmodetopbar.cpp
rename to src/selectionmode/topbar.cpp
index 83aa8e849404c52e9dbb8458c0e20086fcee49a8..d783c76caaa017754a05acbb1eeb7e70425b9fcb 100644 (file)
@@ -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()) {
similarity index 86%
rename from src/selectionmode/selectionmodetopbar.h
rename to src/selectionmode/topbar.h
index eb26a5c26ad03f3c4c4ea08b4e38dc76b7924b07..e0cd34935dd5a8e32fda14ac92e72cb9b77a5926 100644 (file)
@@ -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
index 56867dd13bf95b58f2ac6b96d301462679e2a8d8..f235efffe2cdd391b20ccb794778d246f1c39a2a 100644 (file)
@@ -286,7 +286,7 @@ DolphinView::Mode DolphinView::viewMode() const
 void DolphinView::setSelectionMode(const bool enabled)
 {
     if (enabled) {
-        m_proxyStyle = std::make_unique<SingleClickSelectionProxyStyle>();
+        m_proxyStyle = std::make_unique<SelectionMode::SingleClickSelectionProxyStyle>();
         setStyle(m_proxyStyle.get());
         m_view->setStyle(m_proxyStyle.get());
     } else {
index b2e45a5f68f370bcd5a31bf037636bfafb37eb0f..ef30e91c9f46fd9b97102ab40d34383de44a1050 100644 (file)
@@ -10,6 +10,7 @@
 #include "dolphindebug.h"
 #include "kitemviews/kfileitemlisttostring.h"
 #include "kitemviews/kfileitemmodel.h"
+#include "selectionmode/actiontexthelper.h"
 #include "settings/viewpropertiesdialog.h"
 #include "views/zoomlevelinfo.h"
 #include "kconfig_version.h"
@@ -29,7 +30,7 @@
 #include <QMenu>
 #include <QPointer>
 
-DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) :
+DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, SelectionMode::ActionTextHelper* actionTextHelper, QObject* parent) :
     QObject(parent),
     m_actionCollection(collection),
     m_currentView(nullptr),
@@ -37,7 +38,7 @@ DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection
     m_visibleRoles()
 {
     Q_ASSERT(m_actionCollection);
-    createActions();
+    createActions(actionTextHelper);
 }
 
 void DolphinViewActionHandler::setCurrentView(DolphinView* view)
@@ -84,7 +85,7 @@ DolphinView* DolphinViewActionHandler::currentView()
     return m_currentView;
 }
 
-void DolphinViewActionHandler::createActions()
+void DolphinViewActionHandler::createActions(SelectionMode::ActionTextHelper *actionTextHelper)
 {
     // This action doesn't appear in the GUI, it's for the shortcut only.
     // KNewFileMenu takes care of the GUI stuff.
@@ -164,6 +165,13 @@ void DolphinViewActionHandler::createActions()
     m_actionCollection->setDefaultShortcuts(copyPathAction, {Qt::CTRL | Qt::ALT | Qt::Key_C});
     connect(copyPathAction, &QAction::triggered, this, &DolphinViewActionHandler::slotCopyPath);
 
+    if (actionTextHelper) {
+        actionTextHelper->registerTextWhenNothingIsSelected(trashAction, i18nc("@action:inmenu File", "Move to Trash…"));
+        actionTextHelper->registerTextWhenNothingIsSelected(deleteAction, i18nc("@action:inmenu File", "Delete…"));
+        actionTextHelper->registerTextWhenNothingIsSelected(duplicateAction, i18nc("@action:inmenu File", "Duplicate Here…"));
+        actionTextHelper->registerTextWhenNothingIsSelected(copyPathAction, i18nc("@action:incontextmenu", "Copy Location…"));
+    }
+
     // This menu makes sure that users who don't know how to open a context menu and haven't
     // figured out how to enable the menu bar can still perform basic file manipulation.
     // This only works if they know how to select a file.
@@ -426,7 +434,7 @@ void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action)
 void DolphinViewActionHandler::slotRename()
 {
     if (m_currentView->selectedItemsCount() == 0) {
-        Q_EMIT setSelectionMode(true, SelectionModeBottomBar::Contents::RenameContents);
+        Q_EMIT setSelectionMode(true, SelectionMode::BottomBar::Contents::RenameContents);
     } else {
         Q_EMIT actionBeingHandled();
         m_currentView->renameSelectedItems();
@@ -436,7 +444,7 @@ void DolphinViewActionHandler::slotRename()
 void DolphinViewActionHandler::slotTrashActivated()
 {
     if (m_currentView->selectedItemsCount() == 0) {
-        Q_EMIT setSelectionMode(true, SelectionModeBottomBar::Contents::MoveToTrashContents);
+        Q_EMIT setSelectionMode(true, SelectionMode::BottomBar::Contents::MoveToTrashContents);
     } else {
         Q_EMIT actionBeingHandled();
         m_currentView->trashSelectedItems();
@@ -447,7 +455,7 @@ void DolphinViewActionHandler::slotTrashActivated()
 void DolphinViewActionHandler::slotDeleteItems()
 {
     if (m_currentView->selectedItemsCount() == 0) {
-        Q_EMIT setSelectionMode(true, SelectionModeBottomBar::Contents::DeleteContents);
+        Q_EMIT setSelectionMode(true, SelectionMode::BottomBar::Contents::DeleteContents);
     } else {
         Q_EMIT actionBeingHandled();
         m_currentView->deleteSelectedItems();
@@ -752,7 +760,7 @@ void DolphinViewActionHandler::slotAdjustViewProperties()
 void DolphinViewActionHandler::slotDuplicate()
 {
     if (m_currentView->selectedItemsCount() == 0) {
-        Q_EMIT setSelectionMode(true, SelectionModeBottomBar::Contents::DuplicateContents);
+        Q_EMIT setSelectionMode(true, SelectionMode::BottomBar::Contents::DuplicateContents);
     } else {
         Q_EMIT actionBeingHandled();
         m_currentView->duplicateSelectedItems();
@@ -780,7 +788,7 @@ void DolphinViewActionHandler::slotProperties()
 void DolphinViewActionHandler::slotCopyPath()
 {
     if (m_currentView->selectedItemsCount() == 0) {
-        Q_EMIT setSelectionMode(true, SelectionModeBottomBar::Contents::CopyLocationContents);
+        Q_EMIT setSelectionMode(true, SelectionMode::BottomBar::Contents::CopyLocationContents);
     } else {
         m_currentView->copyPathToClipboard();
         Q_EMIT setSelectionMode(false);
index f35512a5ff78fee8540a5013aaa1085ea525211e..5c7475fdb8ec0fd6d143d3fe5b5b78e150f73c53 100644 (file)
@@ -10,7 +10,7 @@
 #define DOLPHINVIEWACTIONHANDLER_H
 
 #include "dolphin_export.h"
-#include "selectionmode/selectionmodebottombar.h"
+#include "selectionmode/bottombar.h"
 #include "views/dolphinview.h"
 
 #include <QObject>
@@ -21,6 +21,9 @@ class QActionGroup;
 class DolphinView;
 class KActionCollection;
 class KFileItemList;
+namespace SelectionMode {
+    class ActionTextHelper;
+}
 
 /**
  * @short Handles all actions for DolphinView
@@ -41,7 +44,7 @@ class DOLPHIN_EXPORT DolphinViewActionHandler : public QObject
     Q_OBJECT
 
 public:
-    explicit DolphinViewActionHandler(KActionCollection* collection, QObject* parent);
+    explicit DolphinViewActionHandler(KActionCollection* collection, SelectionMode::ActionTextHelper* actionTextHelper, QObject* parent);
 
     /**
      * Sets the view that this action handler should work on.
@@ -85,7 +88,7 @@ Q_SIGNALS:
     void createDirectoryTriggered();
 
     /** Used to request selection mode */
-    void setSelectionMode(bool enabled, SelectionModeBottomBar::Contents bottomBarContents = SelectionModeBottomBar::Contents::GeneralContents);
+    void setSelectionMode(bool enabled, SelectionMode::BottomBar::Contents bottomBarContents = SelectionMode::BottomBar::Contents::GeneralContents);
 
 private Q_SLOTS:
     /**
@@ -238,7 +241,7 @@ private:
      * Create all the actions.
      * This is called only once (by the constructor)
      */
-    void createActions();
+    void createActions(SelectionMode::ActionTextHelper *actionTextHelper);
 
     /**
      * Creates an action-group out of all roles from KFileItemModel.