From: Alessio Bonfiglio Date: Fri, 17 Dec 2021 14:11:46 +0000 (+0000) Subject: Enable Ctrl/Shift-Click to open folder in a new tab/window X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/65b18bf935faad814b9ab3b318fdbfb4772d2051 Enable Ctrl/Shift-Click to open folder in a new tab/window and more --- diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index e034e58f8..11338ec1f 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -445,6 +445,16 @@ void DolphinMainWindow::openNewTab(const QUrl& url) m_tabWidget->openNewTab(url, QUrl()); } +void DolphinMainWindow::openNewTabAndActivate(const QUrl &url) +{ + m_tabWidget->openNewActivatedTab(url, QUrl()); +} + +void DolphinMainWindow::openNewWindow(const QUrl &url) +{ + Dolphin::openNewWindow({url}, this); +} + void DolphinMainWindow::slotSplitViewChanged() { m_tabWidget->currentTabPage()->setSplitViewEnabled(GeneralSettings::splitView(), WithAnimation); @@ -1859,8 +1869,10 @@ void DolphinMainWindow::setupDockWidgets() foldersPanel, &FoldersPanel::setUrl); connect(foldersPanel, &FoldersPanel::folderActivated, this, &DolphinMainWindow::changeUrl); - connect(foldersPanel, &FoldersPanel::folderMiddleClicked, + connect(foldersPanel, &FoldersPanel::folderInNewTab, this, &DolphinMainWindow::openNewTab); + connect(foldersPanel, &FoldersPanel::folderInNewActiveTab, + this, &DolphinMainWindow::openNewTabAndActivate); connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); @@ -1942,8 +1954,10 @@ void DolphinMainWindow::setupDockWidgets() addDockWidget(Qt::LeftDockWidgetArea, placesDock); connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated); - connect(m_placesPanel, &PlacesPanel::placeMiddleClicked, + connect(m_placesPanel, &PlacesPanel::placeActivatedInNewTab, this, &DolphinMainWindow::openNewTab); + connect(m_placesPanel, &PlacesPanel::placeActivatedInNewActiveTab, + this, &DolphinMainWindow::openNewTabAndActivate); connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); connect(this, &DolphinMainWindow::urlChanged, @@ -2134,6 +2148,10 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) this, &DolphinMainWindow::updateSearchAction); connect(container, &DolphinViewContainer::captionChanged, this, &DolphinMainWindow::updateWindowTitle); + connect(container, &DolphinViewContainer::tabRequested, + this, &DolphinMainWindow::openNewTab); + connect(container, &DolphinViewContainer::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled); @@ -2147,6 +2165,10 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) this, &DolphinMainWindow::fileItemsChanged); connect(view, &DolphinView::tabRequested, this, &DolphinMainWindow::openNewTab); + connect(view, &DolphinView::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); + connect(view, &DolphinView::windowRequested, + this, &DolphinMainWindow::openNewWindow); connect(view, &DolphinView::requestContextMenu, this, &DolphinMainWindow::openContextMenu); connect(view, &DolphinView::directoryLoadingStarted, @@ -2181,6 +2203,10 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) this, &DolphinMainWindow::slotEditableStateChanged); connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTab); + connect(navigator, &KUrlNavigator::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); + connect(navigator, &KUrlNavigator::newWindowRequested, + this, &DolphinMainWindow::openNewWindow); } diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 46515cc8b..761766df8 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -171,6 +171,16 @@ public Q_SLOTS: */ void openNewTab(const QUrl& url); + /** + * Opens a new tab showing the URL \a url and activate it. + */ + void openNewTabAndActivate(const QUrl &url); + + /** + * Opens a new window showing the URL \a url. + */ + void openNewWindow(const QUrl &url); + /** @see GeneralSettings::splitViewChanged() */ void slotSplitViewChanged(); diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index a647b6f43..48e73ca89 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -642,7 +643,7 @@ void DolphinViewContainer::slotUrlIsFileError(const QUrl& url) } } -void DolphinViewContainer::slotItemActivated(const KFileItem& item) +void DolphinViewContainer::slotItemActivated(const KFileItem &item) { // It is possible to activate items on inactive views by // drag & drop operations. Assure that activating an item always @@ -651,7 +652,17 @@ void DolphinViewContainer::slotItemActivated(const KFileItem& item) const QUrl& url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives()); if (!url.isEmpty()) { - setUrl(url); + const auto modifiers = QGuiApplication::keyboardModifiers(); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) { + Q_EMIT activeTabRequested(url); + } else if (modifiers & Qt::ControlModifier) { + Q_EMIT tabRequested(url); + } else if (modifiers & Qt::ShiftModifier) { + Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(url)}, this); + } else { + setUrl(url); + } return; } diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index 5d2321952..f78f85e55 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -229,6 +229,16 @@ Q_SIGNALS: */ void captionChanged(); + /** + * Is emitted if a new tab should be opened in the background for the URL \a url. + */ + void tabRequested(const QUrl &url); + + /** + * Is emitted if a new tab should be opened for the URL \a url and set as active. + */ + void activeTabRequested(const QUrl &url); + private Q_SLOTS: /** * Updates the number of items (= number of files + number of @@ -281,7 +291,7 @@ private Q_SLOTS: * directory is opened in the view. If the item is a file, the file * gets started by the corresponding application. */ - void slotItemActivated(const KFileItem& item); + void slotItemActivated(const KFileItem &item); /** * Handles activation of multiple files. The files get started by diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 8687872ee..d0bcd6ceb 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -1583,14 +1583,10 @@ bool KItemListController::onRelease(const QPointF& pos, const Qt::KeyboardModifi Q_EMIT itemExpansionToggleClicked(index); emitItemActivated = false; - } else if (shiftOrControlPressed) { - // The mouse click should only update the selection, not trigger the item + } else if (shiftOrControlPressed && m_selectionBehavior != SingleSelection) { + // The mouse click should only update the selection, not trigger the item, except when + // we are in single selection mode emitItemActivated = false; - // When Ctrl-clicking an item when in single selection mode - // i.e. where Ctrl won't change the selection, pretend it was middle clicked - if (controlPressed && m_selectionBehavior == SingleSelection) { - Q_EMIT itemMiddleClicked(index); - } } else if (!(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)) { if (touch) { emitItemActivated = true; diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h index 24339134e..f4092576a 100644 --- a/src/kitemviews/kitemlistcontroller.h +++ b/src/kitemviews/kitemlistcontroller.h @@ -137,7 +137,7 @@ Q_SIGNALS: * Is emitted if more than one item has been activated by pressing Return/Enter * when having a selection. */ - void itemsActivated(const KItemSet& indexes); + void itemsActivated(const KItemSet &indexes); void itemMiddleClicked(int index); diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 19a05d2b6..d3d8b81f1 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -189,7 +189,19 @@ void FoldersPanel::slotItemActivated(int index) { const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { - Q_EMIT folderActivated(item.url()); + const auto modifiers = QGuiApplication::keyboardModifiers(); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) { + Q_EMIT folderInNewActiveTab(item.url()); + } else if (modifiers & Qt::ControlModifier) { + Q_EMIT folderInNewTab(item.url()); + } else if (modifiers & Qt::ShiftModifier) { + // The shift modifier is not considered because it is used to expand the tree view without actually + // opening the folder + return; + } else { + Q_EMIT folderActivated(item.url()); + } } } @@ -197,7 +209,13 @@ void FoldersPanel::slotItemMiddleClicked(int index) { const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { - Q_EMIT folderMiddleClicked(item.url()); + const auto modifiers = QGuiApplication::keyboardModifiers(); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ShiftModifier) { + Q_EMIT folderInNewActiveTab(item.url()); + } else { + Q_EMIT folderInNewTab(item.url()); + } } } diff --git a/src/panels/folders/folderspanel.h b/src/panels/folders/folderspanel.h index 26c8e4cb9..3ce7870ff 100644 --- a/src/panels/folders/folderspanel.h +++ b/src/panels/folders/folderspanel.h @@ -42,7 +42,8 @@ public: Q_SIGNALS: void folderActivated(const QUrl& url); - void folderMiddleClicked(const QUrl& url); + void folderInNewTab(const QUrl &url); + void folderInNewActiveTab(const QUrl &url); void errorMessage(const QString& error); protected: diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 83e014a82..b9fc4a897 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -44,16 +45,16 @@ #include PlacesPanel::PlacesPanel(QWidget* parent) : - Panel(parent), - m_controller(nullptr), - m_model(nullptr), - m_view(nullptr), - m_storageSetupFailedUrl(), - m_triggerStorageSetupButton(), - m_itemDropEventIndex(-1), - m_itemDropEventMimeData(nullptr), - m_itemDropEvent(nullptr), - m_tooltipTimer() + Panel(parent), + m_controller(nullptr), + m_model(nullptr), + m_view(nullptr), + m_storageSetupFailedUrl(), + m_triggerStorageSetupModifier(), + m_itemDropEventIndex(-1), + m_itemDropEventMimeData(nullptr), + m_itemDropEvent(nullptr), + m_tooltipTimer() { m_tooltipTimer.setInterval(500); m_tooltipTimer.setSingleShot(true); @@ -163,12 +164,28 @@ bool PlacesPanel::eventFilter(QObject * /* obj */, QEvent *event) void PlacesPanel::slotItemActivated(int index) { - triggerItem(index, Qt::LeftButton); + const auto modifiers = QGuiApplication::keyboardModifiers(); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) { + triggerItem(index, TriggerItemModifier::ToNewActiveTab); + } else if (modifiers & Qt::ControlModifier) { + triggerItem(index, TriggerItemModifier::ToNewTab); + } else if (modifiers & Qt::ShiftModifier) { + triggerItem(index, TriggerItemModifier::ToNewWindow); + } else { + triggerItem(index, TriggerItemModifier::None); + } } void PlacesPanel::slotItemMiddleClicked(int index) { - triggerItem(index, Qt::MiddleButton); + const auto modifiers = QGuiApplication::keyboardModifiers(); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ShiftModifier) { + triggerItem(index, TriggerItemModifier::ToNewActiveTab); + } else { + triggerItem(index, TriggerItemModifier::ToNewTab); + } } void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) @@ -287,9 +304,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) } else if (action == openInNewWindowAction) { Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this); } else if (action == openInNewTabAction) { - // TriggerItem does set up the storage first and then it will - // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton. - triggerItem(index, Qt::MiddleButton); + triggerItem(index, TriggerItemModifier::ToNewTab); } else if (action == mountAction) { m_model->requestStorageSetup(index); } else if (action == teardownAction) { @@ -480,14 +495,14 @@ void PlacesPanel::slotStorageSetupDone(int index, bool success) disconnect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotStorageSetupDone); - if (m_triggerStorageSetupButton == Qt::NoButton) { + if (m_triggerStorageSetupModifier == TriggerItemModifier::None) { return; } if (success) { Q_ASSERT(!m_model->storageSetupNeeded(index)); - triggerItem(index, m_triggerStorageSetupButton); - m_triggerStorageSetupButton = Qt::NoButton; + triggerItem(index, m_triggerStorageSetupModifier); + m_triggerStorageSetupModifier = TriggerItemModifier::None; } else { setUrl(m_storageSetupFailedUrl); m_storageSetupFailedUrl = QUrl(); @@ -553,7 +568,7 @@ void PlacesPanel::selectItem() } } -void PlacesPanel::triggerItem(int index, Qt::MouseButton button) +void PlacesPanel::triggerItem(int index, TriggerItemModifier modifier) { const PlacesItem* item = m_model->placesItem(index); if (!item) { @@ -561,7 +576,7 @@ void PlacesPanel::triggerItem(int index, Qt::MouseButton button) } if (m_model->storageSetupNeeded(index)) { - m_triggerStorageSetupButton = button; + m_triggerStorageSetupModifier = modifier; m_storageSetupFailedUrl = url(); connect(m_model, &PlacesItemModel::storageSetupDone, @@ -569,14 +584,23 @@ void PlacesPanel::triggerItem(int index, Qt::MouseButton button) m_model->requestStorageSetup(index); } else { - m_triggerStorageSetupButton = Qt::NoButton; + m_triggerStorageSetupModifier = TriggerItemModifier::None; const QUrl url = m_model->data(index).value("url").toUrl(); if (!url.isEmpty()) { - if (button == Qt::MiddleButton) { - Q_EMIT placeMiddleClicked(KFilePlacesModel::convertedUrl(url)); - } else { - Q_EMIT placeActivated(KFilePlacesModel::convertedUrl(url)); + switch (modifier) { + case TriggerItemModifier::ToNewTab: + Q_EMIT placeActivatedInNewTab(KFilePlacesModel::convertedUrl(url)); + break; + case TriggerItemModifier::ToNewActiveTab: + Q_EMIT placeActivatedInNewActiveTab(KFilePlacesModel::convertedUrl(url)); + break; + case TriggerItemModifier::ToNewWindow: + Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(url)}, this); + break; + case TriggerItemModifier::None: + Q_EMIT placeActivated(KFilePlacesModel::convertedUrl(url)); + break; } } } diff --git a/src/panels/places/placespanel.h b/src/panels/places/placespanel.h index 39f8da365..ce28c8c08 100644 --- a/src/panels/places/placespanel.h +++ b/src/panels/places/placespanel.h @@ -35,7 +35,8 @@ public: Q_SIGNALS: void placeActivated(const QUrl& url); - void placeMiddleClicked(const QUrl& url); + void placeActivatedInNewTab(const QUrl &url); + void placeActivatedInNewActiveTab(const QUrl &url); void errorMessage(const QString& error); void storageTearDownRequested(const QString& mountPath); void storageTearDownExternallyRequested(const QString& mountPath); @@ -63,6 +64,9 @@ private Q_SLOTS: void slotStorageSetupDone(int index, bool success); void slotShowTooltip(); +private: + enum class TriggerItemModifier { None, ToNewTab, ToNewActiveTab, ToNewWindow }; + private: void addEntry(); void editEntry(int index); @@ -73,7 +77,7 @@ private: */ void selectItem(); - void triggerItem(int index, Qt::MouseButton button); + void triggerItem(int index, TriggerItemModifier modifier); QAction* buildGroupContextMenu(QMenu* menu, int index); @@ -83,7 +87,7 @@ private: PlacesView* m_view; QUrl m_storageSetupFailedUrl; - Qt::MouseButton m_triggerStorageSetupButton; + TriggerItemModifier m_triggerStorageSetupModifier; int m_itemDropEventIndex; QMimeData* m_itemDropEventMimeData; diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 9a063d857..bb537b982 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -974,12 +974,14 @@ void DolphinView::slotItemActivated(int index) } } -void DolphinView::slotItemsActivated(const KItemSet& indexes) +void DolphinView::slotItemsActivated(const KItemSet &indexes) { Q_ASSERT(indexes.count() >= 2); abortTwoClicksRenaming(); + const auto modifiers = QGuiApplication::keyboardModifiers(); + if (indexes.count() > 5) { QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count()); const int answer = KMessageBox::warningYesNo(this, question); @@ -995,8 +997,15 @@ void DolphinView::slotItemsActivated(const KItemSet& indexes) KFileItem item = m_model->fileItem(index); const QUrl& url = openItemAsFolderUrl(item); - if (!url.isEmpty()) { // Open folders in new tabs - Q_EMIT tabRequested(url); + if (!url.isEmpty()) { + // Open folders in new tabs or in new windows depending on the modifier + // The ctrl+shift behavior is ignored because we are handling multiple items + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ShiftModifier && !(modifiers & Qt::ControlModifier)) { + Q_EMIT windowRequested(url); + } else { + Q_EMIT tabRequested(url); + } } else { items.append(item); } @@ -1013,10 +1022,21 @@ void DolphinView::slotItemMiddleClicked(int index) { const KFileItem& item = m_model->fileItem(index); const QUrl& url = openItemAsFolderUrl(item); + const auto modifiers = QGuiApplication::keyboardModifiers(); if (!url.isEmpty()) { - Q_EMIT tabRequested(url); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ShiftModifier) { + Q_EMIT activeTabRequested(url); + } else { + Q_EMIT tabRequested(url); + } } else if (isTabsForFilesEnabled()) { - Q_EMIT tabRequested(item.url()); + // keep in sync with KUrlNavigator::slotNavigatorButtonClicked + if (modifiers & Qt::ShiftModifier) { + Q_EMIT activeTabRequested(item.url()); + } else { + Q_EMIT tabRequested(item.url()); + } } } diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 11926d558..0f288f942 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -425,13 +425,13 @@ Q_SIGNALS: /** * Is emitted when clicking on an item with the left mouse button. */ - void itemActivated(const KFileItem& item); + void itemActivated(const KFileItem &item); /** * Is emitted when multiple items have been activated by e. g. * context menu open with. */ - void itemsActivated(const KFileItemList& items); + void itemsActivated(const KFileItemList &items); /** * Is emitted if items have been added or deleted. @@ -443,6 +443,16 @@ Q_SIGNALS: */ void tabRequested(const QUrl& url); + /** + * Is emitted if a new tab should be opened for the URL \a url and set as active. + */ + void activeTabRequested(const QUrl &url); + + /** + * Is emitted if a new window should be opened for the URL \a url. + */ + void windowRequested(const QUrl &url); + /** * Is emitted if the view mode (IconsView, DetailsView, * PreviewsView) has been changed. @@ -619,7 +629,7 @@ private Q_SLOTS: void activate(); void slotItemActivated(int index); - void slotItemsActivated(const KItemSet& indexes); + void slotItemsActivated(const KItemSet &indexes); void slotItemMiddleClicked(int index); void slotItemContextMenuRequested(int index, const QPointF& pos); void slotViewContextMenuRequested(const QPointF& pos);