]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Keep working towards a reviewable state
authorFelix Ernst <fe.a.ernst@gmail.com>
Sun, 24 Apr 2022 00:34:43 +0000 (02:34 +0200)
committerFelix Ernst <fe.a.ernst@gmail.com>
Sun, 14 Aug 2022 14:42:40 +0000 (14:42 +0000)
- Various code improvements
- Smoother animations
- The bottom bar in General Mode only becomes visible if items are
    currently selected
- Removed the selection mode action from the default toolbar since
    it can already be toggled in various ways
- More documentation
- Some cleaning

14 files changed:
src/dolphinmainwindow.cpp
src/dolphinui.rc
src/dolphinviewcontainer.cpp
src/kitemviews/kitemlistcontroller.cpp
src/selectionmode/actionwithwidget.cpp
src/selectionmode/actionwithwidget.h
src/selectionmode/backgroundcolorhelper.cpp
src/selectionmode/backgroundcolorhelper.h
src/selectionmode/selectionmodebottombar.cpp
src/selectionmode/selectionmodebottombar.h
src/selectionmode/selectionmodetopbar.cpp
src/selectionmode/selectionmodetopbar.h
src/selectionmode/singleclickselectionproxystyle.h
src/views/dolphinviewactionhandler.cpp

index aea0d31ad585c1dafeceeca6458dae0087fb4bdf..fae30761e927e2b345f9b5a742f909c3c5ef712c 100644 (file)
@@ -83,8 +83,6 @@
 #include <QToolButton>
 #include <QWhatsThisClickedEvent>
 
-#include <iostream>
-
 namespace {
     // Used for GeneralSettings::version() to determine whether
     // an updated version of Dolphin is running, so as to migrate
@@ -126,7 +124,7 @@ DolphinMainWindow::DolphinMainWindow() :
     setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
     setObjectName(QStringLiteral("Dolphin#"));
 
-    //setStateConfigGroup("State"); // TODO: Don't leave this as a comment.
+    setStateConfigGroup("State");
 
     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
             this, &DolphinMainWindow::showErrorMessage);
@@ -195,7 +193,6 @@ DolphinMainWindow::DolphinMainWindow() :
 
     auto hamburgerMenu = static_cast<KHamburgerMenu *>(actionCollection()->action(
                                     KStandardAction::name(KStandardAction::HamburgerMenu)));
-    hamburgerMenu->icon();
     hamburgerMenu->setMenuBar(menuBar());
     hamburgerMenu->setShowMenuBarAction(showMenuBarAction);
     connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu,
@@ -1331,7 +1328,7 @@ void DolphinMainWindow::updateHamburgerMenu()
 
     menu->addMenu(m_newFileMenu->menu());
     if (!toolBar()->isVisible()
-        || !toolbarActions.contains(ac->action(QStringLiteral("toggle_selection_mode_with_popup")))
+        || !toolbarActions.contains(ac->action(QStringLiteral("toggle_selection_mode_tool_bar")))
     ) {
         menu->addAction(ac->action(QStringLiteral("toggle_selection_mode")));
     }
@@ -1676,10 +1673,10 @@ void DolphinMainWindow::setupActions()
     // So in a way "Select" here is used to mean both "Select files" and also "Select what to do" but mostly the first.
     // The text is kept so unspecific because it will be shown on the toolbar where space is at a premium.
     toggleSelectionModeAction->setIconText(i18nc("@action:intoolbar", "Select"));
-    toggleSelectionModeAction->setWhatsThis(xi18nc("@info:whatsthis", "<para>This application doesn't know which files or folders should be acted on, "
-        "unless they are <emphasis>selected</emphasis> first. Press this to toggle the <emphasis>Selection Mode</emphasis> which makes selecting and deselecting as "
-        "easy as pressing an item once.</para><para>While in this mode, a quick access bar at the bottom shows all the available actions for the current "
-        "selection of items.</para>"));
+    toggleSelectionModeAction->setWhatsThis(xi18nc("@info:whatsthis", "<para>This application only knows which files or folders should be acted on if they are"
+        " <emphasis>selected</emphasis> first. Press this to toggle a <emphasis>Selection Mode</emphasis> which makes selecting and deselecting as easy as "
+        "pressing an item once.</para><para>While in this mode, a quick access bar at the bottom shows available actions for the currently selected items."
+        "</para>"));
     toggleSelectionModeAction->setIcon(QIcon::fromTheme(QStringLiteral("quickwizard")));
     toggleSelectionModeAction->setCheckable(true);
     actionCollection()->setDefaultShortcut(toggleSelectionModeAction, Qt::Key_Space );
@@ -1690,7 +1687,7 @@ void DolphinMainWindow::setupActions()
     auto *toggleSelectionModeToolBarAction = new KToolBarPopupAction(toggleSelectionModeAction->icon(), toggleSelectionModeAction->iconText(), actionCollection());
     toggleSelectionModeToolBarAction->setToolTip(toggleSelectionModeAction->text());
     toggleSelectionModeToolBarAction->setWhatsThis(toggleSelectionModeAction->whatsThis());
-    actionCollection()->addAction(QStringLiteral("toggle_selection_mode_with_popup"), toggleSelectionModeToolBarAction);
+    actionCollection()->addAction(QStringLiteral("toggle_selection_mode_tool_bar"), toggleSelectionModeToolBarAction);
     toggleSelectionModeToolBarAction->setCheckable(true);
     toggleSelectionModeToolBarAction->setPopupMode(QToolButton::DelayedPopup);
     connect(toggleSelectionModeToolBarAction, &QAction::triggered, toggleSelectionModeAction, &QAction::trigger);
index a23912608a768ffef2c8ca466d81dd89efe39075..0eca6b8fe3fb46312ea3c2a9cca008f64081cee7 100644 (file)
         <Action name="split_view" />
         <Action name="split_stash" />
         <Action name="toggle_search" />
-        <Action name="toggle_selection_mode_with_popup" />
         <Action name="hamburger_menu" />
     </ToolBar>
     <ActionProperties scheme="Default">
index dd72a6d6671be86c8eb5b1a03d83c5fd8077a9f5..d45096d0b860ed7e081bf5ef1938b81ee609eca9 100644 (file)
@@ -42,8 +42,6 @@
 #include <QUrl>
 #include <QDesktopServices>
 
-#include <iostream>
-
 // An overview of the widgets contained by this ViewContainer
 struct LayoutStructure {
     int searchBox               = 0;
@@ -377,7 +375,6 @@ void DolphinViewContainer::disconnectUrlNavigator()
 
 void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection, SelectionModeBottomBar::Contents bottomBarContents)
 {
-    std::cout << "DolphinViewContainer::setSelectionModeEnabled(" << enabled << ", " << bottomBarContents << ")\n";
     const bool wasEnabled = m_view->selectionMode();
     m_view->setSelectionMode(enabled);
 
@@ -410,7 +407,6 @@ void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollecti
         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) {
             showErrorMessage(errorMessage);
         });
@@ -434,8 +430,15 @@ void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollecti
 bool DolphinViewContainer::isSelectionModeEnabled() const
 {
     const bool isEnabled = m_view->selectionMode();
-    Q_ASSERT( !isEnabled // We cannot assert the invisibility of the bars because of the hide animation.
-          || ( isEnabled && m_selectionModeTopBar && m_selectionModeTopBar->isVisible() && m_selectionModeBottomBar && m_selectionModeBottomBar->isVisible()));
+    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))
+          || ( 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)));
     return isEnabled;
 }
 
index 03ee5cfe62eed09996db3c60cbec5ceac6907655..cc58ed06c24f71e89a2fd63452320a7d83dd9c48 100644 (file)
@@ -1546,7 +1546,8 @@ bool KItemListController::onPress(const QPoint& screenPos, const QPointF& pos, c
     }
 
     const bool shiftPressed = modifiers & Qt::ShiftModifier;
-    const bool controlPressed = (modifiers & Qt::ControlModifier) || m_selectionMode;
+    const bool controlPressed = (modifiers & Qt::ControlModifier) || m_selectionMode; // Keeping selectionMode similar to pressing control will hopefully
+                                                                                      // simplify the overall logic and possibilities both for users and devs.
     const bool leftClick = buttons & Qt::LeftButton;
     const bool rightClick = buttons & Qt::RightButton;
 
index 8e82a37bf14311640375c5835d2817106d7d1ca5..e9823af7c983eec49b3b89242e4106e8d86dfcf1 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index 722fdf2843c1352e41c6bd0c8fc0845269e6025f..62dad0fc1df38659952a88a0924126e38c40f75a 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index 8a7d69758b200d9adae2e55090bc75fe4a960c8a..ca110e7627867b38a7067c6e6fa7f8da870c4a11 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index 0e8a61b34825932879f66501cc86d0ea7bf62773..013d3368540725cdeaa01202b2a27bffb99195dc 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index 5bf19295a48d1a40ba8e46f27b84de7990a2a3cd..d10a8581bab025604e6c468e4f33cdf578668a43 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
@@ -37,7 +37,7 @@
 #include <QVBoxLayout>
 
 #include <unordered_set>
-#include <iostream>
+
 SelectionModeBottomBar::SelectionModeBottomBar(KActionCollection *actionCollection, QWidget *parent) :
     QWidget{parent},
     m_actionCollection{actionCollection}
@@ -73,44 +73,50 @@ SelectionModeBottomBar::SelectionModeBottomBar(KActionCollection *actionCollecti
 
 void SelectionModeBottomBar::setVisible(bool visible, Animated animated)
 {
-    Q_ASSERT_X(animated == WithAnimation, "SelectionModeBottomBar::setVisible", "This wasn't implemented.");
+    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.
+    }
 
-    if (!m_heightAnimation) {
-        m_heightAnimation = new QPropertyAnimation(this, "maximumHeight");
+    setEnabled(visible);
+    if (m_heightAnimation) {
+        m_heightAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
     }
-    disconnect(m_heightAnimation, &QAbstractAnimation::finished,
-               this, nullptr);
+    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->setStartValue(0);
         m_heightAnimation->setEndValue(sizeHint().height());
-        m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
         connect(m_heightAnimation, &QAbstractAnimation::finished,
                 this, [this](){ setMaximumHeight(sizeHint().height()); });
     } else {
-        m_heightAnimation->setStartValue(height());
         m_heightAnimation->setEndValue(0);
-        m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
         connect(m_heightAnimation, &QAbstractAnimation::finished,
                 this, &QWidget::hide);
     }
 
-    m_heightAnimation->start();
+    m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
 }
 
 QSize SelectionModeBottomBar::sizeHint() const
 {
-    // 1 as width because this widget should never be the reason the DolphinViewContainer is made wider.
     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)
@@ -118,10 +124,20 @@ void SelectionModeBottomBar::slotSelectionChanged(const KFileItemList &selection
     if (m_contents == GeneralContents) {
         auto contextActions = contextActionsFor(selection, baseUrl);
         m_generalBarActions.clear();
-        for (auto i = contextActions.begin(); i != contextActions.end(); ++i) {
-            m_generalBarActions.emplace_back(ActionWithWidget{*i});
+        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);
+            }
         }
-        resetContents(GeneralContents);
     }
     updateMainActionButton(selection);
 }
@@ -148,28 +164,42 @@ void SelectionModeBottomBar::resetContents(SelectionModeBottomBar::Contents cont
     m_contents = contents;
     switch (contents) {
     case CopyContents:
-        return addCopyContents();
+        addCopyContents();
+        break;
     case CopyLocationContents:
-        return addCopyLocationContents();
+        addCopyLocationContents();
+        break;
     case CopyToOtherViewContents:
-        return addCopyToOtherViewContents();
+        addCopyToOtherViewContents();
+        break;
     case CutContents:
-        return addCutContents();
+        addCutContents();
+        break;
     case DeleteContents:
-        return addDeleteContents();
+        addDeleteContents();
+        break;
     case DuplicateContents:
-        return addDuplicateContents();
+        addDuplicateContents();
+        break;
     case GeneralContents:
-        return addGeneralContents();
+        addGeneralContents();
+        break;
     case PasteContents:
-        return addPasteContents();
+        addPasteContents();
+        break;
     case MoveToOtherViewContents:
-        return addMoveToOtherViewContents();
+        addMoveToOtherViewContents();
+        break;
     case MoveToTrashContents:
-        return addMoveToTrashContents();
+        addMoveToTrashContents();
+        break;
     case RenameContents:
         return addRenameContents();
     }
+
+    if (m_allowedToBeVisible) {
+        setVisibleInternal(true, WithAnimation);
+    }
 }
 
 bool SelectionModeBottomBar::eventFilter(QObject *watched, QEvent *event)
@@ -179,7 +209,12 @@ bool SelectionModeBottomBar::eventFilter(QObject *watched, QEvent *event)
     switch (event->type()) {
     case QEvent::ChildAdded:
     case QEvent::ChildRemoved:
-        QTimer::singleShot(0, this, [this](){ setMaximumHeight(sizeHint().height()); });
+        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;
@@ -205,14 +240,12 @@ void SelectionModeBottomBar::resizeEvent(QResizeEvent *resizeEvent)
                 i->widget()->setVisible(false);
 
                 // Add the action to the overflow.
-                std::cout << "An Action is added to the m_overflowButton because of a resize: " << qPrintable(i->action()->text()) << "\n";
                 auto overflowMenu = m_overflowButton->menu();
                 if (overflowMenu->actions().isEmpty()) {
                     overflowMenu->addAction(i->action());
                 } else {
                     overflowMenu->insertAction(overflowMenu->actions().at(0), i->action());
                 }
-                std::cout << "The number of actions in the menu is now " << m_overflowButton->menu()->actions().count() << "\n.";
                 m_overflowButton->setVisible(true);
                 if (unusedSpace() >= 0) {
                     break; // All widgets fit now.
@@ -236,10 +269,8 @@ void SelectionModeBottomBar::resizeEvent(QResizeEvent *resizeEvent)
                 i->widget()->setVisible(true);
 
                 // Remove the action from the overflow.
-                std::cout << "An Action is removed from the m_overflowButton because of a resize: " << qPrintable(i->action()->text()) << "\n";
                 auto overflowMenu = m_overflowButton->menu();
                 overflowMenu->removeAction(i->action());
-                std::cout << "The number of actions in the menu is now " << m_overflowButton->menu()->actions().count() << "\n.";
                 if (overflowMenu->isEmpty()) {
                     m_overflowButton->setVisible(false);
                 }
@@ -423,10 +454,8 @@ void SelectionModeBottomBar::addGeneralContents()
                 m_layout->insertWidget(m_layout->count() - 1, i->widget()); // Insert before m_overflowButton
             }
             if (unusedSpace() < i->widget()->sizeHint().width()) {
-                std::cout << "The " << unusedSpace() << " is smaller than the button->sizeHint().width() of " << i->widget()->sizeHint().width() << " plus the m_layout->spacing() of " << m_layout->spacing() << " so the action " << qPrintable(i->action()->text()) << " doesn't get its own button.\n";
                 break; // The bar is too full already. We keep it invisible.
             } else {
-                std::cout << "The " << unusedSpace() << " is bigger than the button->sizeHint().width() of " << i->widget()->sizeHint().width() << " plus the m_layout->spacing() of " << m_layout->spacing() << " so the action " << qPrintable(i->action()->text()) << " was added as its own button/widget.\n";
                 i->widget()->setVisible(true);
             }
         }
@@ -560,46 +589,51 @@ void SelectionModeBottomBar::emptyBarContents()
 
 std::vector<QAction *> SelectionModeBottomBar::contextActionsFor(const KFileItemList& selectedItems, const QUrl& baseUrl)
 {
+    if (selectedItems.isEmpty()) {
+        // There are no contextual actions to show for these items.
+        // We might even want to hide this bar in this case. To make this clear, we reset m_internalContextMenu.
+        m_internalContextMenu.release()->deleteLater();
+        return std::vector<QAction *>{};
+    }
+
     std::vector<QAction *> contextActions;
+
+    // We always want to show the most important actions at the beginning
     contextActions.emplace_back(m_actionCollection->action(KStandardAction::name(KStandardAction::Copy)));
     contextActions.emplace_back(m_actionCollection->action(KStandardAction::name(KStandardAction::Cut)));
     contextActions.emplace_back(m_actionCollection->action(KStandardAction::name(KStandardAction::RenameFile)));
     contextActions.emplace_back(m_actionCollection->action(KStandardAction::name(KStandardAction::MoveToTrash)));
 
-    if (!selectedItems.isEmpty()) {
-        // We are going to add the actions from the right-click context menu for the selected items.
-        auto *dolphinMainWindow = qobject_cast<DolphinMainWindow *>(window());
-        Q_CHECK_PTR(dolphinMainWindow);
-        if (!m_fileItemActions) {
-            m_fileItemActions = new KFileItemActions(this);
-            m_fileItemActions->setParentWidget(dolphinMainWindow);
-            connect(m_fileItemActions, &KFileItemActions::error, this, &SelectionModeBottomBar::error);
+    // We are going to add the actions from the right-click context menu for the selected items.
+    auto *dolphinMainWindow = qobject_cast<DolphinMainWindow *>(window());
+    Q_CHECK_PTR(dolphinMainWindow);
+    if (!m_fileItemActions) {
+        m_fileItemActions = new KFileItemActions(this);
+        m_fileItemActions->setParentWidget(dolphinMainWindow);
+        connect(m_fileItemActions, &KFileItemActions::error, this, &SelectionModeBottomBar::error);
+    }
+    m_internalContextMenu = std::make_unique<DolphinContextMenu>(dolphinMainWindow, selectedItems.constFirst(), selectedItems, baseUrl, m_fileItemActions);
+    auto internalContextMenuActions = m_internalContextMenu->actions();
+
+    // There are some actions which we wouldn't want to add. We remember them in the actionsThatShouldntBeAdded set.
+    // We don't want to add the four basic actions again which were already added to the top.
+    std::unordered_set<QAction *> actionsThatShouldntBeAdded{contextActions.begin(), contextActions.end()};
+    // "Delete" isn't really necessary to add because we have "Move to Trash" already. It is also more dangerous so let's exclude it.
+    actionsThatShouldntBeAdded.insert(m_actionCollection->action(KStandardAction::name(KStandardAction::DeleteFile)));
+
+    // KHamburgerMenu would only be visible if there is no menu available anywhere on the user interface. This might be useful for recovery from
+    // such a situation in theory but a bar with context dependent actions doesn't really seem like the right place for it.
+    Q_ASSERT(internalContextMenuActions.first()->icon().name() == m_actionCollection->action(KStandardAction::name(KStandardAction::HamburgerMenu))->icon().name());
+    internalContextMenuActions.removeFirst();
+
+    for (auto it = internalContextMenuActions.constBegin(); it != internalContextMenuActions.constEnd(); ++it) {
+        if (actionsThatShouldntBeAdded.count(*it)) {
+            continue; // Skip this action.
         }
-        m_internalContextMenu = std::make_unique<DolphinContextMenu>(dolphinMainWindow, selectedItems.constFirst(), selectedItems, baseUrl, m_fileItemActions);
-        auto internalContextMenuActions = m_internalContextMenu->actions();
-
-        // There are some actions which we wouldn't want to add. We remember them in the actionsThatShouldntBeAdded set.
-        // We don't want to add the four basic actions again which were already added to the top.
-        std::unordered_set<QAction *> actionsThatShouldntBeAdded{contextActions.begin(), contextActions.end()};
-        // "Delete" isn't really necessary to add because we have "Move to Trash" already. It is also more dangerous so let's exclude it.
-        actionsThatShouldntBeAdded.insert(m_actionCollection->action(KStandardAction::name(KStandardAction::DeleteFile)));
-        // "Open Terminal" isn't really context dependent and can therefore be opened from elsewhere instead.
-        actionsThatShouldntBeAdded.insert(m_actionCollection->action(QStringLiteral("open_terminal")));
-
-        // KHamburgerMenu would only be visible if there is no menu available anywhere on the user interface. This might be useful for recovery from
-        // such a situation in theory but a bar with context dependent actions doesn't really seem like the right place for it.
-        Q_ASSERT(internalContextMenuActions.first()->icon().name() == m_actionCollection->action(KStandardAction::name(KStandardAction::HamburgerMenu))->icon().name());
-        internalContextMenuActions.removeFirst();
-
-        for (auto it = internalContextMenuActions.constBegin(); it != internalContextMenuActions.constEnd(); ++it) {
-            if (actionsThatShouldntBeAdded.count(*it)) {
-                continue; // Skip this action.
-            }
-            if (!qobject_cast<DolphinRemoveAction *>(*it)) { // We already have a "Move to Trash" action so we don't want a DolphinRemoveAction.
-                // We filter duplicate separators here so we won't have to deal with them later.
-                if (!contextActions.back()->isSeparator() || !(*it)->isSeparator()) {
-                    contextActions.emplace_back((*it));
-                }
+        if (!qobject_cast<DolphinRemoveAction *>(*it)) { // We already have a "Move to Trash" action so we don't want a DolphinRemoveAction.
+            // We filter duplicate separators here so we won't have to deal with them later.
+            if (!contextActions.back()->isSeparator() || !(*it)->isSeparator()) {
+                contextActions.emplace_back((*it));
             }
         }
     }
@@ -612,21 +646,13 @@ int SelectionModeBottomBar::unusedSpace() const
     if (m_overflowButton) {
         sumOfPreferredWidths += m_overflowButton->sizeHint().width();
     }
-    std::cout << "These layout items should have sane width: ";
     for (int i = 0; i < m_layout->count(); ++i) {
         auto widget = m_layout->itemAt(i)->widget();
         if (widget && !widget->isVisibleTo(widget->parentWidget())) {
             continue; // We don't count invisible widgets.
         }
-        std::cout << m_layout->itemAt(i)->sizeHint().width() << ", ";
-        if (m_layout->itemAt(i)->sizeHint().width() == 0) {
-            // One of the items reports an invalid width. We can't work with this so we report an unused space of 0 which should lead to as few changes to the
-            // layout as possible until the next resize event happens at a later point in time.
-            //return 0;
-        }
         sumOfPreferredWidths += m_layout->itemAt(i)->sizeHint().width() + m_layout->spacing();
     }
-    std::cout << "leads to unusedSpace = " << width() << " - " << sumOfPreferredWidths - 20 << " = " << width() - sumOfPreferredWidths - 20 << "\n";
     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.
 }
@@ -636,7 +662,6 @@ void SelectionModeBottomBar::updateExplanatoryLabelVisibility()
     if (!m_explanatoryLabel) {
         return;
     }
-    std::cout << "label minimumSizeHint compared to width() :" << m_explanatoryLabel->sizeHint().width() << "/" << m_explanatoryLabel->width() << "; unusedSpace: " << unusedSpace() << "\n";
     if (m_explanatoryLabel->isVisible()) {
         m_explanatoryLabel->setVisible(unusedSpace() > 0);
     } else {
@@ -660,17 +685,17 @@ void SelectionModeBottomBar::updateMainActionButton(const KFileItemList& selecti
     switch (m_contents) {
     case 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(), 
+                            "Copy %2 to the Clipboard", "Copy %2 to the Clipboard", selection.count(),
                             fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics));
         break;
     case 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(), 
+                            "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:
         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(), 
+                            "Cut %2 to the Clipboard", "Cut %2 to the Clipboard", selection.count(),
                             fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics));
         break;
     case DeleteContents:
index 61cb903344733478a0a46309b0f14fc0e324b7b1..89fd3c3a374bfc251a181a350bda30d7b0cfe88f 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
@@ -66,6 +66,9 @@ public:
     /**
      * 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);
@@ -77,6 +80,7 @@ public:
         return m_contents;
     };
 
+    /** @returns a width of 1 to make sure that this bar never causes side panels to shrink. */
     QSize sizeHint() const override;
 
 public Q_SLOTS:
@@ -96,9 +100,18 @@ Q_SIGNALS:
 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);
+
     void addCopyContents();
     void addCopyLocationContents();
     void addCopyToOtherViewContents();
@@ -171,6 +184,10 @@ private:
      * 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;
 
index 89a4aa03a05b84a5edfc09934bf91dc8ba6f7dc1..83aa8e849404c52e9dbb8458c0e20086fcee49a8 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
@@ -22,9 +22,6 @@
 #include <QStyle>
 #include <QtGlobal>
 
-#include <type_traits>
-#include <iostream>
-
 SelectionModeTopBar::SelectionModeTopBar(QWidget *parent) :
     QWidget{parent}
 {
@@ -90,29 +87,26 @@ void SelectionModeTopBar::setVisible(bool visible, Animated animated)
 {
     Q_ASSERT_X(animated == WithAnimation, "SelectionModeTopBar::setVisible", "This wasn't implemented.");
 
-    if (!m_heightAnimation) {
-        m_heightAnimation = new QPropertyAnimation(this, "maximumHeight");
+    if (m_heightAnimation) {
+        m_heightAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
     }
-    disconnect(m_heightAnimation, &QAbstractAnimation::finished,
-               this, &QWidget::hide);
+    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->setStartValue(0);
         m_heightAnimation->setEndValue(m_preferredHeight);
-        m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
     } else {
-        m_heightAnimation->setStartValue(height());
         m_heightAnimation->setEndValue(0);
-        m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
         connect(m_heightAnimation, &QAbstractAnimation::finished,
                 this, &QWidget::hide);
     }
 
-    m_heightAnimation->start();
+    m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
 }
 
 void SelectionModeTopBar::resizeEvent(QResizeEvent */* resizeEvent */)
index fa829aef5241b7715a6ba58f458ade65a47f333f..eb26a5c26ad03f3c4c4ea08b4e38dc76b7924b07 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2022 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index 9c185a85a22b3b98caab4f0a5bde2f35c65d1dd0..65117c56c015bed27df8c3f74063b7357e118c2a 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file is part of the KDE project
-    SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
+    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
 */
index a66d1f6dda76abf97c08775b9623fbe4e26b8ec2..b2e45a5f68f370bcd5a31bf037636bfafb37eb0f 100644 (file)
@@ -29,8 +29,6 @@
 #include <QMenu>
 #include <QPointer>
 
-#include <iostream>
-
 DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) :
     QObject(parent),
     m_actionCollection(collection),