diff options
| author | Elvis Angelaccio <[email protected]> | 2020-11-10 00:05:27 +0100 |
|---|---|---|
| committer | Elvis Angelaccio <[email protected]> | 2020-11-10 00:05:27 +0100 |
| commit | 6719072837f30c1822768da65e6ea222e987e32f (patch) | |
| tree | b61a3c588561946e9d462e23379d44fbdeed185b /src | |
| parent | e5d137b81debbbfe51c18d16d361fd28a3448416 (diff) | |
| parent | 63f4981fe01d88b2ef1b27e0577d7f5d4c8cc485 (diff) | |
Merge branch 'release/20.12'
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/dolphinmainwindow.cpp | 69 | ||||
| -rw-r--r-- | src/dolphinmainwindow.h | 3 | ||||
| -rw-r--r-- | src/dolphinnavigatorswidgetaction.cpp | 259 | ||||
| -rw-r--r-- | src/dolphinnavigatorswidgetaction.h | 159 | ||||
| -rw-r--r-- | src/dolphintabpage.cpp | 90 | ||||
| -rw-r--r-- | src/dolphintabpage.h | 37 | ||||
| -rw-r--r-- | src/dolphintabwidget.cpp | 50 | ||||
| -rw-r--r-- | src/dolphintabwidget.h | 24 | ||||
| -rw-r--r-- | src/dolphinui.rc | 4 | ||||
| -rw-r--r-- | src/dolphinurlnavigator.cpp | 109 | ||||
| -rw-r--r-- | src/dolphinurlnavigator.h | 77 | ||||
| -rw-r--r-- | src/dolphinurlnavigatorscontroller.cpp | 69 | ||||
| -rw-r--r-- | src/dolphinurlnavigatorscontroller.h | 76 | ||||
| -rw-r--r-- | src/dolphinviewcontainer.cpp | 187 | ||||
| -rw-r--r-- | src/dolphinviewcontainer.h | 77 | ||||
| -rw-r--r-- | src/main.cpp | 7 | ||||
| -rw-r--r-- | src/panels/places/placesitemlistwidget.cpp | 15 |
18 files changed, 1114 insertions, 201 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bff52cf0f..1955ce558 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -203,11 +203,14 @@ set(dolphinstatic_SRCS dolphinmainwindow.cpp dolphinviewcontainer.cpp dolphincontextmenu.cpp + dolphinnavigatorswidgetaction.cpp dolphintabbar.cpp dolphinplacesmodelsingleton.cpp dolphinrecenttabsmenu.cpp dolphintabpage.cpp dolphintabwidget.cpp + dolphinurlnavigator.cpp + dolphinurlnavigatorscontroller.cpp trash/dolphintrash.cpp filterbar/filterbar.cpp panels/places/placespanel.cpp diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 97ced73b4..0c4c3c881 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -14,8 +14,10 @@ #include "dolphinbookmarkhandler.h" #include "dolphindockwidget.h" #include "dolphincontextmenu.h" +#include "dolphinnavigatorswidgetaction.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.h" +#include "dolphinurlnavigatorscontroller.h" #include "dolphinviewcontainer.h" #include "dolphintabpage.h" #include "middleclickactioneventfilter.h" @@ -140,7 +142,9 @@ DolphinMainWindow::DolphinMainWindow() : setAcceptDrops(true); - m_tabWidget = new DolphinTabWidget(this); + auto *navigatorsWidgetAction = new DolphinNavigatorsWidgetAction(this); + actionCollection()->addAction(QStringLiteral("url_navigators"), navigatorsWidgetAction); + m_tabWidget = new DolphinTabWidget(navigatorsWidgetAction, this); m_tabWidget->setObjectName("tabWidget"); connect(m_tabWidget, &DolphinTabWidget::activeViewChanged, this, &DolphinMainWindow::activeViewChanged); @@ -165,6 +169,11 @@ DolphinMainWindow::DolphinMainWindow() : setupGUI(Keys | Save | Create | ToolBar); stateChanged(QStringLiteral("new_file")); + toolBar()->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); + toolBar()->setFloatable(false); + if (!toolBar()->actions().contains(navigatorsWidgetAction)) { + navigatorsWidgetAction->addToToolbarAndSave(this); + } QClipboard* clipboard = QApplication::clipboard(); connect(clipboard, &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction); @@ -356,7 +365,7 @@ void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) void DolphinMainWindow::updateHistory() { - const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); const int index = urlNavigator->historyIndex(); QAction* backAction = actionCollection()->action(KStandardAction::name(KStandardAction::Back)); @@ -727,7 +736,7 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) void DolphinMainWindow::slotAboutToShowBackPopupMenu() { - KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); int entries = 0; m_backAction->menu()->clear(); for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) { @@ -740,7 +749,7 @@ void DolphinMainWindow::slotAboutToShowBackPopupMenu() void DolphinMainWindow::slotGoBack(QAction* action) { int gotoIndex = action->data().value<int>(); - KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); for (int i = gotoIndex - urlNavigator->historyIndex(); i > 0; --i) { goBack(); } @@ -749,14 +758,14 @@ void DolphinMainWindow::slotGoBack(QAction* action) void DolphinMainWindow::slotBackForwardActionMiddleClicked(QAction* action) { if (action) { - KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + const KUrlNavigator *urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory(); openNewTabAfterCurrentTab(urlNavigator->locationUrl(action->data().value<int>())); } } void DolphinMainWindow::slotAboutToShowForwardPopupMenu() { - KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); int entries = 0; m_forwardAction->menu()->clear(); for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { @@ -769,7 +778,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu() void DolphinMainWindow::slotGoForward(QAction* action) { int gotoIndex = action->data().value<int>(); - KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); for (int i = urlNavigator->historyIndex() - gotoIndex; i > 0; --i) { goForward(); } @@ -891,7 +900,7 @@ void DolphinMainWindow::slotTerminalPanelVisibilityChanged() void DolphinMainWindow::goBack() { - KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + DolphinUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); urlNavigator->goBack(); if (urlNavigator->locationState().isEmpty()) { @@ -903,29 +912,29 @@ void DolphinMainWindow::goBack() void DolphinMainWindow::goForward() { - m_activeViewContainer->urlNavigator()->goForward(); + m_activeViewContainer->urlNavigatorInternalWithHistory()->goForward(); } void DolphinMainWindow::goUp() { - m_activeViewContainer->urlNavigator()->goUp(); + m_activeViewContainer->urlNavigatorInternalWithHistory()->goUp(); } void DolphinMainWindow::goHome() { - m_activeViewContainer->urlNavigator()->goHome(); + m_activeViewContainer->urlNavigatorInternalWithHistory()->goHome(); } void DolphinMainWindow::goBackInNewTab() { - KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + const KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory(); const int index = urlNavigator->historyIndex() + 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } void DolphinMainWindow::goForwardInNewTab() { - KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + const KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory(); const int index = urlNavigator->historyIndex() - 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } @@ -1057,6 +1066,8 @@ void DolphinMainWindow::editSettings() const QUrl url = container->url(); DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this); connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews); + connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, + &DolphinUrlNavigatorsController::slotReadSettings); settingsDialog->setAttribute(Qt::WA_DeleteOnClose); settingsDialog->show(); m_settingsDialog = settingsDialog; @@ -1253,7 +1264,12 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) // view and url navigator) and main window. oldViewContainer->disconnect(this); oldViewContainer->view()->disconnect(this); - oldViewContainer->urlNavigator()->disconnect(this); + auto navigators = static_cast<DolphinNavigatorsWidgetAction *> + (actionCollection()->action(QStringLiteral("url_navigators"))); + navigators->primaryUrlNavigator()->disconnect(this); + if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) { + secondaryUrlNavigator->disconnect(this); + } // except the requestItemInfo so that on hover the information panel can still be updated connect(oldViewContainer->view(), &DolphinView::requestItemInfo, @@ -1883,14 +1899,14 @@ void DolphinMainWindow::setupDockWidgets() connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl); connect(placesDock, &DolphinDockWidget::visibilityChanged, - m_tabWidget, &DolphinTabWidget::slotPlacesPanelVisibilityChanged); + &DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged); connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings); connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested); connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested); - m_tabWidget->slotPlacesPanelVisibilityChanged(m_placesPanel->isVisible()); + DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged(m_placesPanel->isVisible()); auto actionShowAllPlaces = new QAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Show Hidden Places"), this); actionShowAllPlaces->setCheckable(true); @@ -2023,10 +2039,6 @@ void DolphinMainWindow::updateViewActions() showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible()); updateSplitAction(); - - QAction* editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location")); - const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); - editableLocactionAction->setChecked(urlNavigator->isUrlEditable()); } void DolphinMainWindow::updateGoActions() @@ -2162,15 +2174,26 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp); - const KUrlNavigator* navigator = container->urlNavigator(); + auto navigators = static_cast<DolphinNavigatorsWidgetAction *> + (actionCollection()->action(QStringLiteral("url_navigators"))); + + const KUrlNavigator *navigator = m_tabWidget->currentTabPage()->primaryViewActive() ? + navigators->primaryUrlNavigator() : + navigators->secondaryUrlNavigator(); + connect(navigator, &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl); - connect(navigator, &KUrlNavigator::historyChanged, - this, &DolphinMainWindow::updateHistory); + QAction *editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location")); + editableLocactionAction->setChecked(navigator->isUrlEditable()); connect(navigator, &KUrlNavigator::editableStateChanged, this, &DolphinMainWindow::slotEditableStateChanged); connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTabAfterLastTab); + + disconnect(m_updateHistoryConnection); + m_updateHistoryConnection = connect( + container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, + this, &DolphinMainWindow::updateHistory); } void DolphinMainWindow::updateSplitAction() diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index a56215fa7..29ab6326d 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -649,6 +649,9 @@ private: KToolBarPopupAction* m_backAction; KToolBarPopupAction* m_forwardAction; + /** Makes sure that only one object is ever connected to the history. */ + QMetaObject::Connection m_updateHistoryConnection; + QMenu m_searchTools; }; diff --git a/src/dolphinnavigatorswidgetaction.cpp b/src/dolphinnavigatorswidgetaction.cpp new file mode 100644 index 000000000..984e1f35c --- /dev/null +++ b/src/dolphinnavigatorswidgetaction.cpp @@ -0,0 +1,259 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "dolphinnavigatorswidgetaction.h" + +#include "trash/dolphintrash.h" + +#include <KLocalizedString> +#include <KXMLGUIFactory> +#include <KXmlGuiWindow> + +#include <QDomDocument> +#include <QHBoxLayout> +#include <QPushButton> +#include <QSplitter> + +#include <limits> + +DolphinNavigatorsWidgetAction::DolphinNavigatorsWidgetAction(QWidget *parent) : + QWidgetAction{parent}, + m_splitter{new QSplitter(Qt::Horizontal)}, + m_adjustSpacingTimer{new QTimer(this)}, + m_globalXOfSplitter{INT_MIN}, + m_globalXOfPrimary{INT_MIN}, + m_widthOfPrimary{INT_MIN}, + m_globalXOfSecondary{INT_MIN}, + m_widthOfSecondary{INT_MIN} +{ + updateText(); + setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); + + m_splitter->setChildrenCollapsible(false); + setDefaultWidget(m_splitter.get()); + + m_splitter->addWidget(createNavigatorWidget(Primary)); + + m_adjustSpacingTimer->setInterval(100); + m_adjustSpacingTimer->setSingleShot(true); + connect(m_adjustSpacingTimer.get(), &QTimer::timeout, + this, &DolphinNavigatorsWidgetAction::adjustSpacing); +} + +bool DolphinNavigatorsWidgetAction::addToToolbarAndSave(KXmlGuiWindow *mainWindow) +{ + const QString rawXml = KXMLGUIFactory::readConfigFile(mainWindow->xmlFile()); + QDomDocument domDocument; + if (rawXml.isEmpty() || !domDocument.setContent(rawXml) || domDocument.isNull()) { + return false; + } + QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0); + if (toolbar.isNull()) { + return false; + } + + QDomElement urlNavigatorElement = domDocument.createElement(QStringLiteral("Action")); + urlNavigatorElement.setAttribute(QStringLiteral("name"), QStringLiteral("url_navigators")); + + QDomNode position = toolbar.firstChildElement(QStringLiteral("Spacer")); + if (position.isNull()) { + toolbar.appendChild(urlNavigatorElement); + } else { + toolbar.replaceChild(urlNavigatorElement, position); + } + + KXMLGUIFactory::saveConfigFile(domDocument, mainWindow->xmlFile()); + mainWindow->reloadXML(); + mainWindow->createGUI(); + return true; +} + +void DolphinNavigatorsWidgetAction::createSecondaryUrlNavigator() +{ + Q_ASSERT(m_splitter->count() == 1); + m_splitter->addWidget(createNavigatorWidget(Secondary)); + Q_ASSERT(m_splitter->count() == 2); + updateText(); +} + +void DolphinNavigatorsWidgetAction::followViewContainerGeometry( + int globalXOfPrimary, int widthOfPrimary) +{ + followViewContainersGeometry(globalXOfPrimary, widthOfPrimary, INT_MIN, INT_MIN); +} + +void DolphinNavigatorsWidgetAction::followViewContainersGeometry( + int globalXOfPrimary, int widthOfPrimary, + int globalXOfSecondary, int widthOfSecondary) +{ + m_globalXOfSplitter = m_splitter->mapToGlobal(QPoint(0,0)).x(); + m_globalXOfPrimary = globalXOfPrimary; + m_widthOfPrimary = widthOfPrimary; + m_globalXOfSecondary = globalXOfSecondary; + m_widthOfSecondary = widthOfSecondary; + adjustSpacing(); +} + +DolphinUrlNavigator* DolphinNavigatorsWidgetAction::primaryUrlNavigator() const +{ + Q_ASSERT(m_splitter); + return m_splitter->widget(0)->findChild<DolphinUrlNavigator *>(); +} + +DolphinUrlNavigator* DolphinNavigatorsWidgetAction::secondaryUrlNavigator() const +{ + Q_ASSERT(m_splitter); + if (m_splitter->count() < 2) { + return nullptr; + } + return m_splitter->widget(1)->findChild<DolphinUrlNavigator *>(); +} + +void DolphinNavigatorsWidgetAction::setSecondaryNavigatorVisible(bool visible) +{ + if (visible) { + Q_ASSERT(m_splitter->count() == 2); + m_splitter->widget(1)->setVisible(true); + } else if (m_splitter->count() > 1) { + m_splitter->widget(1)->setVisible(false); + // Fix an unlikely event of wrong trash button visibility. + emptyTrashButton(Secondary)->setVisible(false); + } + updateText(); +} + +void DolphinNavigatorsWidgetAction::adjustSpacing() +{ + Q_ASSERT(m_globalXOfSplitter != INT_MIN); + Q_ASSERT(m_globalXOfPrimary != INT_MIN); + Q_ASSERT(m_widthOfPrimary != INT_MIN); + const int widthOfSplitterPrimary = m_globalXOfPrimary + m_widthOfPrimary - m_globalXOfSplitter; + const QList<int> splitterSizes = {widthOfSplitterPrimary, + m_splitter->width() - widthOfSplitterPrimary}; + m_splitter->setSizes(splitterSizes); + + // primary side of m_splitter + int leadingSpacing = m_globalXOfPrimary - m_globalXOfSplitter; + if (leadingSpacing < 0) { + leadingSpacing = 0; + } + int trailingSpacing = (m_globalXOfSplitter + m_splitter->width()) + - (m_globalXOfPrimary + m_widthOfPrimary); + if (trailingSpacing < 0 || emptyTrashButton(Primary)->isVisible()) { + trailingSpacing = 0; + } + const int widthLeftForUrlNavigator = m_splitter->widget(0)->width() - leadingSpacing - trailingSpacing; + const int widthNeededForUrlNavigator = primaryUrlNavigator()->sizeHint().width() - widthLeftForUrlNavigator; + if (widthNeededForUrlNavigator > 0) { + trailingSpacing -= widthNeededForUrlNavigator; + if (trailingSpacing < 0) { + leadingSpacing += trailingSpacing; + trailingSpacing = 0; + } + if (leadingSpacing < 0) { + leadingSpacing = 0; + } + } + spacing(Primary, Leading)->setMinimumWidth(leadingSpacing); + spacing(Primary, Trailing)->setFixedWidth(trailingSpacing); + + // secondary side of m_splitter + if (m_globalXOfSecondary == INT_MIN) { + Q_ASSERT(m_widthOfSecondary == INT_MIN); + return; + } + spacing(Primary, Trailing)->setFixedWidth(0); + + trailingSpacing = (m_globalXOfSplitter + m_splitter->width()) + - (m_globalXOfSecondary + m_widthOfSecondary); + if (trailingSpacing < 0 || emptyTrashButton(Secondary)->isVisible()) { + trailingSpacing = 0; + } else { + const int widthLeftForUrlNavigator2 = m_splitter->widget(1)->width() - trailingSpacing; + const int widthNeededForUrlNavigator2 = secondaryUrlNavigator()->sizeHint().width() - widthLeftForUrlNavigator2; + if (widthNeededForUrlNavigator2 > 0) { + trailingSpacing -= widthNeededForUrlNavigator2; + if (trailingSpacing < 0) { + trailingSpacing = 0; + } + } + } + spacing(Secondary, Trailing)->setMinimumWidth(trailingSpacing); +} + +QWidget *DolphinNavigatorsWidgetAction::createNavigatorWidget(Side side) const +{ + auto navigatorWidget = new QWidget(m_splitter.get()); + auto layout = new QHBoxLayout{navigatorWidget}; + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + if (side == Primary) { + auto leadingSpacing = new QWidget{navigatorWidget}; + layout->addWidget(leadingSpacing); + } + auto urlNavigator = new DolphinUrlNavigator(navigatorWidget); + layout->addWidget(urlNavigator); + + auto emptyTrashButton = newEmptyTrashButton(urlNavigator, navigatorWidget); + layout->addWidget(emptyTrashButton); + + connect(urlNavigator, &KUrlNavigator::urlChanged, this, [this]() { + // We have to wait for DolphinUrlNavigator::sizeHint() to update which + // happens a little bit later than when urlChanged is emitted. + this->m_adjustSpacingTimer->start(); + }); + + auto trailingSpacing = new QWidget{navigatorWidget}; + layout->addWidget(trailingSpacing); + return navigatorWidget; +} + +QPushButton * DolphinNavigatorsWidgetAction::emptyTrashButton(DolphinNavigatorsWidgetAction::Side side) +{ + int sideIndex = (side == Primary ? 0 : 1); + if (side == Primary) { + return static_cast<QPushButton *>(m_splitter->widget(sideIndex)->layout()->itemAt(2)->widget()); + } + return static_cast<QPushButton *>(m_splitter->widget(sideIndex)->layout()->itemAt(1)->widget()); +} + +QPushButton *DolphinNavigatorsWidgetAction::newEmptyTrashButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const +{ + auto emptyTrashButton = new QPushButton(QIcon::fromTheme(QStringLiteral("user-trash")), + i18nc("@action:button", "Empty Trash"), parent); + emptyTrashButton->setFlat(true); + connect(emptyTrashButton, &QPushButton::clicked, + this, [parent]() { Trash::empty(parent); }); + connect(&Trash::instance(), &Trash::emptinessChanged, + emptyTrashButton, &QPushButton::setDisabled); + emptyTrashButton->hide(); + connect(urlNavigator, &KUrlNavigator::urlChanged, this, [emptyTrashButton, urlNavigator]() { + emptyTrashButton->setVisible(urlNavigator->locationUrl().scheme() == QLatin1String("trash")); + }); + emptyTrashButton->setDisabled(Trash::isEmpty()); + return emptyTrashButton; +} + +QWidget *DolphinNavigatorsWidgetAction::spacing(Side side, Position position) const +{ + int sideIndex = (side == Primary ? 0 : 1); + if (position == Leading) { + Q_ASSERT(side == Primary); // The secondary side of the splitter has no leading spacing. + return m_splitter->widget(sideIndex)->layout()->itemAt(0)->widget(); + } + if (side == Primary) { + return m_splitter->widget(sideIndex)->layout()->itemAt(3)->widget(); + } + return m_splitter->widget(sideIndex)->layout()->itemAt(2)->widget(); +} + +void DolphinNavigatorsWidgetAction::updateText() +{ + const int urlNavigatorsAmount = m_splitter->count() > 1 && m_splitter->widget(1)->isVisible() ? + 2 : 1; + setText(i18ncp("@action:inmenu", "Url Navigator", "Url Navigators", urlNavigatorsAmount)); +} diff --git a/src/dolphinnavigatorswidgetaction.h b/src/dolphinnavigatorswidgetaction.h new file mode 100644 index 000000000..f343e6a1c --- /dev/null +++ b/src/dolphinnavigatorswidgetaction.h @@ -0,0 +1,159 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef DOLPHINNAVIGATORSWIDGETACTION_H +#define DOLPHINNAVIGATORSWIDGETACTION_H + +#include "dolphinurlnavigator.h" + +#include <QSplitter> +#include <QTimer> +#include <QWidgetAction> + +#include <memory> + +class KXmlGuiWindow; +class QPushButton; + +/** + * @brief QWidgetAction that allows to use DolphinUrlNavigators in a toolbar. + * + * This class is mainly a container that manages up to two DolphinUrlNavigator objects so they + * can be added to a toolbar. It also deals with alignment. + * + * The structure of the defaultWidget() of this QWidgetAction is as follows: + * - A QSplitter manages up to two sides which each correspond to one DolphinViewContainer. + * The secondary side only exists for split view and is created by + * createSecondaryUrlNavigator() when necessary. + * - Each side is a QWidget which I call NavigatorWidget with a QHBoxLayout. + * - Each NavigatorWidget consists an UrlNavigator, an emptyTrashButton and spacing. + * - Only the primary navigatorWidget has leading spacing. Both have trailing spacing. + * The spacing is there to align the UrlNavigator with its DolphinViewContainer. + */ +class DolphinNavigatorsWidgetAction : public QWidgetAction +{ + Q_OBJECT + +public: + DolphinNavigatorsWidgetAction(QWidget *parent = nullptr); + + /** + * Adds this action to the mainWindow's toolbar and saves the change + * in the users ui configuration file. + * @return true if successful. Otherwise false. + */ + bool addToToolbarAndSave(KXmlGuiWindow *mainWindow); + + /** + * The secondary UrlNavigator is only created on-demand. Such an action is not necessary + * for the primary UrlNavigator which is created preemptively. + * + * This method should preferably only be called when: + * - Split view is activated in the active tab + * OR + * - A switch to a tab that is already in split view mode is occuring + */ + void createSecondaryUrlNavigator(); + + /** + * Notify the primary UrlNavigator of changes in geometry of the ViewContainer it tries to be + * aligned with. Only call this method if there is no secondary UrlNavigator. + */ + void followViewContainerGeometry(int globalXOfPrimary, int widthOfPrimary); + /** + * Notify this widget of changes in geometry of the ViewContainers it tries to be + * aligned with. + */ + void followViewContainersGeometry(int globalXOfPrimary, int widthOfPrimary, + int globalXOfSecondary, int widthOfSecondary); + + /** + * @return the primary UrlNavigator. + */ + DolphinUrlNavigator *primaryUrlNavigator() const; + /** + * @return the secondary UrlNavigator and nullptr if it doesn't exist. + */ + DolphinUrlNavigator *secondaryUrlNavigator() const; + + /** + * Change the visibility of the secondary UrlNavigator including spacing. + * @param visible Setting this to false will completely hide the secondary side of this + * WidgetAction's QSplitter making the QSplitter effectively disappear. + */ + void setSecondaryNavigatorVisible(bool visible); + +private: + /** + * Adjusts the width of the spacings used to align the UrlNavigators with ViewContainers. + * This can only work nicely if up-to-date geometry of ViewContainers is cached so + * followViewContainersGeometry() has to have been called at least once before. + */ + void adjustSpacing(); + + /** + * In Left-to-right languages the Primary side will be the left one. + */ + enum Side { + Primary, + Secondary + }; + /** + * Used to create the navigatorWidgets for both sides of the QSplitter. + */ + QWidget *createNavigatorWidget(Side side) const; + + /** + * Used to retrieve the emptyTrashButtons for the navigatorWidgets on both sides. + */ + QPushButton *emptyTrashButton(Side side); + + /** + * Creates a new empty trash button. + * @param urlNavigator Only when this UrlNavigator shows the trash directory + * will the the button be visible. + * @param parent Aside from the usual QObject deletion mechanisms, + * this parameter influences the positioning of dialog windows + * pertaining to this trash button. + */ + QPushButton *newEmptyTrashButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const; + + enum Position { + Leading, + Trailing + }; + /** + * Used to retrieve both the leading and trailing spacing for the navigatorWidgets + * on both sides. A secondary leading spacing does not exist. + */ + QWidget *spacing(Side side, Position position) const; + + /** + * Sets this action's text depending on the amount of visible UrlNavigators. + */ + void updateText(); + + /** + * The defaultWidget() of this QWidgetAction. + */ + std::unique_ptr<QSplitter> m_splitter; + + /** + * adjustSpacing() has to be called slightly later than when urlChanged is emitted. + * This timer bridges that time. + */ + std::unique_ptr<QTimer> m_adjustSpacingTimer; + + // cached values + int m_globalXOfSplitter; + int m_globalXOfPrimary; + int m_widthOfPrimary; + int m_globalXOfSecondary; + int m_widthOfSecondary; +}; + +#endif // DOLPHINNAVIGATORSWIDGETACTION_H diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 837793b10..7f945dce2 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -24,6 +24,8 @@ DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, m_splitter = new QSplitter(Qt::Horizontal, this); m_splitter->setChildrenCollapsible(false); + connect(m_splitter, &QSplitter::splitterMoved, + this, &DolphinTabPage::splitterMoved); layout->addWidget(m_splitter); // Create a new primary view @@ -34,6 +36,7 @@ DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, this, &DolphinTabPage::slotViewUrlRedirection); m_splitter->addWidget(m_primaryViewContainer); + m_primaryViewContainer->installEventFilter(this); m_primaryViewContainer->show(); if (secondaryUrl.isValid() || GeneralSettings::splitView()) { @@ -43,6 +46,7 @@ DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl; m_secondaryViewContainer = createViewContainer(url); m_splitter->addWidget(m_secondaryViewContainer); + m_secondaryViewContainer->installEventFilter(this); m_secondaryViewContainer->show(); } @@ -68,17 +72,30 @@ void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl; m_secondaryViewContainer = createViewContainer(url); - const bool placesSelectorVisible = m_primaryViewContainer->urlNavigator()->isPlacesSelectorVisible(); - m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(placesSelectorVisible); + auto secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator(); + if (!secondaryNavigator) { + m_navigatorsWidget->createSecondaryUrlNavigator(); + secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator(); + } + m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator); + m_navigatorsWidget->setSecondaryNavigatorVisible(true); m_splitter->addWidget(m_secondaryViewContainer); + m_secondaryViewContainer->installEventFilter(this); m_secondaryViewContainer->show(); m_secondaryViewContainer->setActive(true); } else { + m_navigatorsWidget->setSecondaryNavigatorVisible(false); + m_secondaryViewContainer->disconnectUrlNavigator(); + DolphinViewContainer* view; if (GeneralSettings::closeActiveSplitView()) { view = activeViewContainer(); if (m_primaryViewActive) { + m_primaryViewContainer->disconnectUrlNavigator(); + m_secondaryViewContainer->connectUrlNavigator( + m_navigatorsWidget->primaryUrlNavigator()); + // If the primary view is active, we have to swap the pointers // because the secondary view will be the new primary view. qSwap(m_primaryViewContainer, m_secondaryViewContainer); @@ -87,6 +104,10 @@ void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl) } else { view = m_primaryViewActive ? m_secondaryViewContainer : m_primaryViewContainer; if (!m_primaryViewActive) { + m_primaryViewContainer->disconnectUrlNavigator(); + m_secondaryViewContainer->connectUrlNavigator( + m_navigatorsWidget->primaryUrlNavigator()); + // If the secondary view is active, we have to swap the pointers // because the secondary view will be the new primary view. qSwap(m_primaryViewContainer, m_secondaryViewContainer); @@ -134,6 +155,51 @@ int DolphinTabPage::selectedItemsCount() const return selectedItemsCount; } +void DolphinTabPage::connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget) +{ + m_navigatorsWidget = navigatorsWidget; + auto primaryNavigator = navigatorsWidget->primaryUrlNavigator(); + m_primaryViewContainer->connectUrlNavigator(primaryNavigator); + if (m_splitViewEnabled) { + auto secondaryNavigator = navigatorsWidget->secondaryUrlNavigator(); + m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator); + } + resizeNavigators(); +} + +void DolphinTabPage::disconnectNavigators() +{ + m_navigatorsWidget = nullptr; + m_primaryViewContainer->disconnectUrlNavigator(); + if (m_splitViewEnabled) { + m_secondaryViewContainer->disconnectUrlNavigator(); + } +} + +bool DolphinTabPage::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::Resize && m_navigatorsWidget) { + resizeNavigators(); + return false; + } + return QWidget::eventFilter(watched, event); +} + +void DolphinTabPage::resizeNavigators() const +{ + if (!m_splitViewEnabled) { + m_navigatorsWidget->followViewContainerGeometry( + m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(), + m_primaryViewContainer->width()); + } else { + m_navigatorsWidget->followViewContainersGeometry( + m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(), + m_primaryViewContainer->width(), + m_secondaryViewContainer->mapToGlobal(QPoint(0,0)).x(), + m_secondaryViewContainer->width()); + } +} + void DolphinTabPage::markUrlsAsSelected(const QList<QUrl>& urls) { m_primaryViewContainer->view()->markUrlsAsSelected(urls); @@ -150,14 +216,6 @@ void DolphinTabPage::markUrlAsCurrent(const QUrl& url) } } -void DolphinTabPage::setPlacesSelectorVisible(bool visible) -{ - m_primaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); - if (m_splitViewEnabled) { - m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); - } -} - void DolphinTabPage::refreshViews() { m_primaryViewContainer->readSettings(); @@ -176,12 +234,12 @@ QByteArray DolphinTabPage::saveState() const stream << m_splitViewEnabled; stream << m_primaryViewContainer->url(); - stream << m_primaryViewContainer->urlNavigator()->isUrlEditable(); + stream << m_primaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable(); m_primaryViewContainer->view()->saveState(stream); if (m_splitViewEnabled) { stream << m_secondaryViewContainer->url(); - stream << m_secondaryViewContainer->urlNavigator()->isUrlEditable(); + stream << m_secondaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable(); m_secondaryViewContainer->view()->saveState(stream); } @@ -217,7 +275,7 @@ void DolphinTabPage::restoreState(const QByteArray& state) m_primaryViewContainer->setUrl(primaryUrl); bool primaryUrlEditable; stream >> primaryUrlEditable; - m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); + m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable); m_primaryViewContainer->view()->restoreState(stream); if (isSplitViewEnabled) { @@ -226,7 +284,7 @@ void DolphinTabPage::restoreState(const QByteArray& state) m_secondaryViewContainer->setUrl(secondaryUrl); bool secondaryUrlEditable; stream >> secondaryUrlEditable; - m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); + m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable); m_secondaryViewContainer->view()->restoreState(stream); } @@ -261,7 +319,7 @@ void DolphinTabPage::restoreStateV1(const QByteArray& state) m_primaryViewContainer->setUrl(primaryUrl); bool primaryUrlEditable; stream >> primaryUrlEditable; - m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); + m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable); if (isSplitViewEnabled) { QUrl secondaryUrl; @@ -269,7 +327,7 @@ void DolphinTabPage::restoreStateV1(const QByteArray& state) m_secondaryViewContainer->setUrl(secondaryUrl); bool secondaryUrlEditable; stream >> secondaryUrlEditable; - m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); + m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable); } stream >> m_primaryViewActive; diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 74344acd1..b874d128f 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -11,8 +11,9 @@ #include <QUrl> #include <QWidget> -class QSplitter; +class DolphinNavigatorsWidgetAction; class DolphinViewContainer; +class QSplitter; class KFileItemList; class DolphinTabPage : public QWidget @@ -67,6 +68,30 @@ public: int selectedItemsCount() const; /** + * Connects a navigatorsWidget to this. It will be connected to the DolphinViewContainers + * managed by this tab. For alignment purposes this will from now on notify the + * navigatorsWidget when this tab or its viewContainers are resized. + */ + void connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget); + + /** + * Makes it so this tab and its DolphinViewContainers aren't controlled by any + * UrlNavigators anymore. + */ + void disconnectNavigators(); + + /** + * Calls resizeNavigators() when a watched object is resized. + */ + bool eventFilter(QObject *watched, QEvent *event) override; + + /** + * Notify the connected DolphinNavigatorsWidgetAction of geometry changes which it + * needs for visual alignment. + */ + void resizeNavigators() const; + + /** * Marks the items indicated by \p urls to get selected after the * directory DolphinView::url() has been loaded. Note that nothing * gets selected if no loading of a directory has been triggered @@ -81,14 +106,6 @@ public: void markUrlAsCurrent(const QUrl& url); /** - * Sets the places selector visible, if \a visible is true. - * The places selector allows to select the places provided - * by the places model passed in the constructor. Per default - * the places selector is visible. - */ - void setPlacesSelectorVisible(bool visible); - - /** * Refreshes the views of the main window by recreating them according to * the given Dolphin settings. */ @@ -125,6 +142,7 @@ public: signals: void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const QUrl& url); + void splitterMoved(int pos, int index); private slots: /** @@ -153,6 +171,7 @@ private: private: QSplitter* m_splitter; + QPointer<DolphinNavigatorsWidgetAction> m_navigatorsWidget; QPointer<DolphinViewContainer> m_primaryViewContainer; QPointer<DolphinViewContainer> m_secondaryViewContainer; diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index 5ef39dac6..da8f76d7c 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -8,7 +8,6 @@ #include "dolphin_generalsettings.h" #include "dolphintabbar.h" -#include "dolphintabpage.h" #include "dolphinviewcontainer.h" #include <KConfigGroup> @@ -20,10 +19,10 @@ #include <QApplication> #include <QDropEvent> -DolphinTabWidget::DolphinTabWidget(QWidget* parent) : +DolphinTabWidget::DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget* parent) : QTabWidget(parent), - m_placesSelectorVisible(true), - m_lastViewedTab(0) + m_lastViewedTab(nullptr), + m_navigatorsWidget{navigatorsWidget} { KAcceleratorManager::setNoAccel(this); @@ -127,11 +126,16 @@ bool DolphinTabWidget::isUrlOpen(const QUrl &url) const void DolphinTabWidget::openNewActivatedTab() { + std::unique_ptr<DolphinUrlNavigator::VisualState> oldNavigatorState; + if (currentTabPage()->primaryViewActive() || !m_navigatorsWidget->secondaryUrlNavigator()) { + oldNavigatorState = m_navigatorsWidget->primaryUrlNavigator()->visualState(); + } else { + oldNavigatorState = m_navigatorsWidget->secondaryUrlNavigator()->visualState(); + } + const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer(); Q_ASSERT(oldActiveViewContainer); - const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable(); - openNewActivatedTab(oldActiveViewContainer->url()); DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer(); @@ -139,7 +143,7 @@ void DolphinTabWidget::openNewActivatedTab() // The URL navigator of the new tab should have the same editable state // as the current tab - newActiveViewContainer->urlNavigator()->setUrlEditable(isUrlEditable); + newActiveViewContainer->urlNavigator()->setVisualState(*oldNavigatorState.get()); // Always focus the new tab's view newActiveViewContainer->view()->setFocus(); @@ -157,7 +161,6 @@ void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryU DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); tabPage->setActive(false); - tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged); connect(tabPage, &DolphinTabPage::activeViewUrlChanged, @@ -288,19 +291,6 @@ void DolphinTabWidget::activatePrevTab() setCurrentIndex(index >= 0 ? index : (count() - 1)); } -void DolphinTabWidget::slotPlacesPanelVisibilityChanged(bool visible) -{ - // The places-selector from the URL navigator should only be shown - // if the places dock is invisible - m_placesSelectorVisible = !visible; - - const int tabCount = count(); - for (int i = 0; i < tabCount; ++i) { - DolphinTabPage* tabPage = tabPageAt(i); - tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); - } -} - void DolphinTabWidget::restoreClosedTab(const QByteArray& state) { openNewActivatedTab(); @@ -399,16 +389,24 @@ void DolphinTabWidget::tabUrlChanged(const QUrl& url) void DolphinTabWidget::currentTabChanged(int index) { - // last-viewed tab deactivation - if (DolphinTabPage* tabPage = tabPageAt(m_lastViewedTab)) { - tabPage->setActive(false); + DolphinTabPage *tabPage = tabPageAt(index); + if (tabPage == m_lastViewedTab) { + return; + } + if (m_lastViewedTab) { + m_lastViewedTab->disconnectNavigators(); + m_lastViewedTab->setActive(false); + } + if (tabPage->splitViewEnabled() && !m_navigatorsWidget->secondaryUrlNavigator()) { + m_navigatorsWidget->createSecondaryUrlNavigator(); } - DolphinTabPage* tabPage = tabPageAt(index); DolphinViewContainer* viewContainer = tabPage->activeViewContainer(); Q_EMIT activeViewChanged(viewContainer); Q_EMIT currentUrlChanged(viewContainer->url()); tabPage->setActive(true); - m_lastViewedTab = index; + tabPage->connectNavigators(m_navigatorsWidget); + m_navigatorsWidget->setSecondaryNavigatorVisible(tabPage->splitViewEnabled()); + m_lastViewedTab = tabPage; } void DolphinTabWidget::tabInserted(int index) diff --git a/src/dolphintabwidget.h b/src/dolphintabwidget.h index 1eca71c5d..4a1b9d99c 100644 --- a/src/dolphintabwidget.h +++ b/src/dolphintabwidget.h @@ -7,11 +7,13 @@ #ifndef DOLPHIN_TAB_WIDGET_H #define DOLPHIN_TAB_WIDGET_H +#include "dolphinnavigatorswidgetaction.h" +#include "dolphintabpage.h" + #include <QTabWidget> #include <QUrl> class DolphinViewContainer; -class DolphinTabPage; class KConfigGroup; class DolphinTabWidget : public QTabWidget @@ -32,7 +34,12 @@ public: */ AfterLastTab }; - explicit DolphinTabWidget(QWidget* parent); + + /** + * @param navigatorsWidget The navigatorsWidget which is always going to be connected + * to the active tabPage. + */ + explicit DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget *parent); /** * @return Tab page at the current index (can be 0 if tabs count is smaller than 1) @@ -164,13 +171,6 @@ public slots: void activatePrevTab(); /** - * Is invoked if the Places panel got visible/invisible and takes care - * that the places-selector of all views is only shown if the Places panel - * is invisible. - */ - void slotPlacesPanelVisibilityChanged(bool visible); - - /** * Is called when the user wants to reopen a previously closed tab from * the recent tabs menu. */ @@ -231,10 +231,8 @@ private: QPair<int, bool> indexByUrl(const QUrl& url) const; private: - /** Caches the (negated) places panel visibility */ - bool m_placesSelectorVisible; - - int m_lastViewedTab; + QPointer<DolphinTabPage> m_lastViewedTab; + QPointer<DolphinNavigatorsWidgetAction> m_navigatorsWidget; }; #endif diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 46a4bac51..01a3778f9 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -119,9 +119,7 @@ <Action name="icons" /> <Action name="compact" /> <Action name="details" /> - <Separator name="separator_0" /> - <Action name="sort" /> - <Spacer name="spacer_0" /> + <Action name="url_navigators" /> <Action name="split_view" /> <Action name="split_stash" /> <Action name="toggle_search" /> diff --git a/src/dolphinurlnavigator.cpp b/src/dolphinurlnavigator.cpp new file mode 100644 index 000000000..1dfe5420f --- /dev/null +++ b/src/dolphinurlnavigator.cpp @@ -0,0 +1,109 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "dolphinurlnavigator.h" + +#include "dolphin_generalsettings.h" +#include "dolphinplacesmodelsingleton.h" +#include "dolphinurlnavigatorscontroller.h" +#include "global.h" + +#include <KUrlComboBox> +#include <KLocalizedString> + +#include <QAbstractButton> +#include <QLayout> +#include <QLineEdit> + +DolphinUrlNavigator::DolphinUrlNavigator(QWidget *parent) : + DolphinUrlNavigator(QUrl(), parent) +{} + +DolphinUrlNavigator::DolphinUrlNavigator(const QUrl &url, QWidget *parent) : + KUrlNavigator(DolphinPlacesModelSingleton::instance().placesModel(), url, parent) +{ + const GeneralSettings* settings = GeneralSettings::self(); + setUrlEditable(settings->editableUrl()); + setShowFullPath(settings->showFullPath()); + setHomeUrl(Dolphin::homeUrl()); + setPlacesSelectorVisible(DolphinUrlNavigatorsController::placesSelectorVisible()); + editor()->setCompletionMode(KCompletion::CompletionMode(settings->urlCompletionMode())); + setWhatsThis(xi18nc("@info:whatsthis location bar", + "<para>This describes the location of the files and folders " + "displayed below.</para><para>The name of the currently viewed " + "folder can be read at the very right. To the left of it is the " + "name of the folder that contains it. The whole line is called " + "the <emphasis>path</emphasis> to the current location because " + "following these folders from left to right leads here.</para>" + "<para>This interactive path " + "is more powerful than one would expect. To learn more " + "about the basic and advanced features of the location bar " + "<link url='help:/dolphin/location-bar.html'>click here</link>. " + "This will open the dedicated page in the Handbook.</para>")); + + DolphinUrlNavigatorsController::registerDolphinUrlNavigator(this); + + connect(this, &KUrlNavigator::returnPressed, + this, &DolphinUrlNavigator::slotReturnPressed); +} + +DolphinUrlNavigator::~DolphinUrlNavigator() +{ + DolphinUrlNavigatorsController::unregisterDolphinUrlNavigator(this); +} + +QSize DolphinUrlNavigator::sizeHint() const +{ + if (isUrlEditable()) { + return editor()->lineEdit()->sizeHint(); + } + int widthHint = 0; + for (int i = 0; i < layout()->count(); ++i) { + QWidget *widget = layout()->itemAt(i)->widget(); + const QAbstractButton *button = qobject_cast<QAbstractButton *>(widget); + if (button && button->icon().isNull()) { + widthHint += widget->minimumSizeHint().width(); + } + } + return QSize(widthHint, KUrlNavigator::sizeHint().height()); +} + +std::unique_ptr<DolphinUrlNavigator::VisualState> DolphinUrlNavigator::visualState() const +{ + std::unique_ptr<VisualState> visualState{new VisualState}; + visualState->isUrlEditable = (isUrlEditable()); + const QLineEdit *lineEdit = editor()->lineEdit(); + visualState->hasFocus = lineEdit->hasFocus(); + visualState->text = lineEdit->text(); + visualState->cursorPosition = lineEdit->cursorPosition(); + visualState->selectionStart = lineEdit->selectionStart(); + visualState->selectionLength = lineEdit->selectionLength(); + return visualState; +} + +void DolphinUrlNavigator::setVisualState(const VisualState& visualState) +{ + setUrlEditable(visualState.isUrlEditable); + if (!visualState.isUrlEditable) { + return; + } + editor()->lineEdit()->setText(visualState.text); + if (visualState.hasFocus) { + editor()->lineEdit()->setFocus(); + editor()->lineEdit()->setCursorPosition(visualState.cursorPosition); + if (visualState.selectionStart != -1) { + editor()->lineEdit()->setSelection(visualState.selectionStart, visualState.selectionLength); + } + } +} + +void DolphinUrlNavigator::slotReturnPressed() +{ + if (!GeneralSettings::editableUrl()) { + setUrlEditable(false); + } +} diff --git a/src/dolphinurlnavigator.h b/src/dolphinurlnavigator.h new file mode 100644 index 000000000..9bcc32b4d --- /dev/null +++ b/src/dolphinurlnavigator.h @@ -0,0 +1,77 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef DOLPHINURLNAVIGATOR_H +#define DOLPHINURLNAVIGATOR_H + +#include <KUrlNavigator> + +/** + * @brief Extends KUrlNavigator in a Dolphin-specific way. + * + * Makes sure that Dolphin preferences and settings are + * applied to all constructed DolphinUrlNavigators. + * @see KUrlNavigator + * + * To apply changes to all instances of this class @see DolphinUrlNavigatorsController. + */ +class DolphinUrlNavigator : public KUrlNavigator +{ + Q_OBJECT + +public: + /** + * Applies all Dolphin-specific settings to a KUrlNavigator + * @see KUrlNavigator::KurlNavigator() + */ + DolphinUrlNavigator(QWidget *parent = nullptr); + + /** + * Applies all Dolphin-specific settings to a KUrlNavigator + * @see KUrlNavigator::KurlNavigator() + */ + DolphinUrlNavigator(const QUrl &url, QWidget *parent = nullptr); + + virtual ~DolphinUrlNavigator(); + + // TODO: Fix KUrlNavigator::sizeHint() instead. + QSize sizeHint() const override; + + /** + * Wraps the visual state of a DolphinUrlNavigator so it can be passed around. + * This notably doesn't involve the locationUrl or history. + */ + struct VisualState { + bool isUrlEditable; + bool hasFocus; + QString text; + int cursorPosition; + int selectionStart; + int selectionLength; + }; + /** + * Retrieve the visual state of this DolphinUrlNavigator. + * If two DolphinUrlNavigators have the same visual state they should look identical. + * + * @return a copy of the visualState of this object. Ownership of this copy is transferred + * to the caller via std::unique_ptr. + */ + std::unique_ptr<VisualState> visualState() const; + /** + * @param visualState A struct describing the new visual state of this object. + */ + void setVisualState(const VisualState &visualState); + +public slots: + /** + * Switches to "breadcrumb" mode if the editable mode is not set to be + * preferred in the Dolphin settings. + */ + void slotReturnPressed(); +}; + +#endif // DOLPHINURLNAVIGATOR_H diff --git a/src/dolphinurlnavigatorscontroller.cpp b/src/dolphinurlnavigatorscontroller.cpp new file mode 100644 index 000000000..59e1d6356 --- /dev/null +++ b/src/dolphinurlnavigatorscontroller.cpp @@ -0,0 +1,69 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "dolphinurlnavigatorscontroller.h" + +#include "dolphin_generalsettings.h" +#include "dolphinurlnavigator.h" +#include "global.h" + +#include <KUrlComboBox> + +void DolphinUrlNavigatorsController::slotReadSettings() +{ + // The startup settings should (only) get applied if they have been + // modified by the user. Otherwise keep the (possibly) different current + // settings of the URL navigators and split view. + if (GeneralSettings::modifiedStartupSettings()) { + for (DolphinUrlNavigator *urlNavigator : s_instances) { + urlNavigator->setUrlEditable(GeneralSettings::editableUrl()); + urlNavigator->setShowFullPath(GeneralSettings::showFullPath()); + urlNavigator->setHomeUrl(Dolphin::homeUrl()); + } + } +} + +void DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged(bool visible) +{ + // The places-selector from the URL navigator should only be shown + // if the places dock is invisible + s_placesSelectorVisible = !visible; + + for (DolphinUrlNavigator *urlNavigator : s_instances) { + urlNavigator->setPlacesSelectorVisible(s_placesSelectorVisible); + } +} + +bool DolphinUrlNavigatorsController::placesSelectorVisible() +{ + return s_placesSelectorVisible; +} + +void DolphinUrlNavigatorsController::registerDolphinUrlNavigator(DolphinUrlNavigator *dolphinUrlNavigator) +{ + s_instances.push_front(dolphinUrlNavigator); + connect(dolphinUrlNavigator->editor(), &KUrlComboBox::completionModeChanged, + DolphinUrlNavigatorsController::setCompletionMode); +} + +void DolphinUrlNavigatorsController::unregisterDolphinUrlNavigator(DolphinUrlNavigator *dolphinUrlNavigator) +{ + s_instances.remove(dolphinUrlNavigator); +} + +void DolphinUrlNavigatorsController::setCompletionMode(const KCompletion::CompletionMode completionMode) +{ + if (completionMode != GeneralSettings::urlCompletionMode()) { + GeneralSettings::setUrlCompletionMode(completionMode); + for (const DolphinUrlNavigator *urlNavigator : s_instances) { + urlNavigator->editor()->setCompletionMode(completionMode); + } + } +} + +std::forward_list<DolphinUrlNavigator *> DolphinUrlNavigatorsController::s_instances; +bool DolphinUrlNavigatorsController::s_placesSelectorVisible = true; diff --git a/src/dolphinurlnavigatorscontroller.h b/src/dolphinurlnavigatorscontroller.h new file mode 100644 index 000000000..797cbf4f9 --- /dev/null +++ b/src/dolphinurlnavigatorscontroller.h @@ -0,0 +1,76 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef DOLPHINURLNAVIGATORSCONTROLLER_H +#define DOLPHINURLNAVIGATORSCONTROLLER_H + +#include <KCompletion> + +#include <QObject> + +#include <forward_list> + +class DolphinUrlNavigator; + +/** + * @brief A controller managing all DolphinUrlNavigators. + * + * This class is used to apply settings changes to all constructed DolphinUrlNavigators. + * + * @see DolphinUrlNavigator + */ +class DolphinUrlNavigatorsController : public QObject +{ + Q_OBJECT + +public: + DolphinUrlNavigatorsController() = delete; + +public slots: + /** + * Refreshes all DolphinUrlNavigators to get synchronized with the + * Dolphin settings if they were changed. + */ + static void slotReadSettings(); + + static void slotPlacesPanelVisibilityChanged(bool visible); + +private: + /** + * @return wether the places selector of DolphinUrlNavigators should be visible. + */ + static bool placesSelectorVisible(); + + /** + * Adds \p dolphinUrlNavigator to the list of DolphinUrlNavigators + * controlled by this class. + */ + static void registerDolphinUrlNavigator(DolphinUrlNavigator *dolphinUrlNavigator); + + /** + * Removes \p dolphinUrlNavigator from the list of DolphinUrlNavigators + * controlled by this class. + */ + static void unregisterDolphinUrlNavigator(DolphinUrlNavigator *dolphinUrlNavigator); + +private slots: + /** + * Sets the completion mode for all DolphinUrlNavigators and saves it in settings. + */ + static void setCompletionMode(const KCompletion::CompletionMode completionMode); + +private: + /** Contains all currently constructed DolphinUrlNavigators */ + static std::forward_list<DolphinUrlNavigator *> s_instances; + + /** Caches the (negated) places panel visibility */ + static bool s_placesSelectorVisible; + + friend class DolphinUrlNavigator; +}; + +#endif // DOLPHINURLNAVIGATORSCONTROLLER_H diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index b1b67447f..0fe8ee9d3 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -7,13 +7,12 @@ #include "dolphinviewcontainer.h" #include "dolphin_generalsettings.h" -#include "dolphinplacesmodelsingleton.h" #include "dolphindebug.h" +#include "dolphinplacesmodelsingleton.h" #include "filterbar/filterbar.h" #include "global.h" #include "search/dolphinsearchbox.h" #include "statusbar/dolphinstatusbar.h" -#include "trash/dolphintrash.h" #include "views/viewmodecontroller.h" #include "views/viewproperties.h" #include "dolphin_detailsmodesettings.h" @@ -31,6 +30,7 @@ #include <KProtocolManager> #include <KShell> #include <KUrlComboBox> +#include <KUrlNavigator> #include <QDropEvent> #include <QLoggingCategory> @@ -43,9 +43,8 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : QWidget(parent), m_topLayout(nullptr), - m_navigatorWidget(nullptr), - m_urlNavigator(nullptr), - m_emptyTrashButton(nullptr), + m_urlNavigator{new DolphinUrlNavigator(url)}, + m_urlNavigatorConnected{nullptr}, m_searchBox(nullptr), m_searchModeEnabled(false), m_messageWidget(nullptr), @@ -65,43 +64,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : m_topLayout->setSpacing(0); m_topLayout->setContentsMargins(0, 0, 0, 0); - m_navigatorWidget = new QWidget(this); - QHBoxLayout* navigatorLayout = new QHBoxLayout(m_navigatorWidget); - navigatorLayout->setSpacing(0); - navigatorLayout->setContentsMargins(0, 0, 0, 0); - m_navigatorWidget->setWhatsThis(xi18nc("@info:whatsthis location bar", - "<para>This line describes the location of the files and folders " - "displayed below.</para><para>The name of the currently viewed " - "folder can be read at the very right. To the left of it is the " - "name of the folder that contains it. The whole line is called " - "the <emphasis>path</emphasis> to the current location because " - "following these folders from left to right leads here.</para>" - "<para>The path is displayed on the <emphasis>location bar</emphasis> " - "which is more powerful than one would expect. To learn more " - "about the basic and advanced features of the location bar " - "<link url='help:/dolphin/location-bar.html'>click here</link>. " - "This will open the dedicated page in the Handbook.</para>")); - - m_urlNavigator = new KUrlNavigator(DolphinPlacesModelSingleton::instance().placesModel(), url, this); - connect(m_urlNavigator, &KUrlNavigator::activated, - this, &DolphinViewContainer::activate); - connect(m_urlNavigator->editor(), &KUrlComboBox::completionModeChanged, - this, &DolphinViewContainer::saveUrlCompletionMode); - - const GeneralSettings* settings = GeneralSettings::self(); - m_urlNavigator->setUrlEditable(settings->editableUrl()); - m_urlNavigator->setShowFullPath(settings->showFullPath()); - m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); - KUrlComboBox* editor = m_urlNavigator->editor(); - editor->setCompletionMode(KCompletion::CompletionMode(settings->urlCompletionMode())); - - m_emptyTrashButton = new QPushButton(QIcon::fromTheme(QStringLiteral("user-trash")), i18nc("@action:button", "Empty Trash"), this); - m_emptyTrashButton->setFlat(true); - connect(m_emptyTrashButton, &QPushButton::clicked, this, [this]() { Trash::empty(this); }); - connect(&Trash::instance(), &Trash::emptinessChanged, m_emptyTrashButton, &QPushButton::setDisabled); - m_emptyTrashButton->setDisabled(Trash::isEmpty()); - m_emptyTrashButton->hide(); - m_searchBox = new DolphinSearchBox(this); m_searchBox->hide(); connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate); @@ -135,7 +97,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : // Initialize filter bar m_filterBar = new FilterBar(this); - m_filterBar->setVisible(settings->filterBar()); + m_filterBar->setVisible(GeneralSettings::filterBar()); connect(m_filterBar, &FilterBar::filterChanged, this, &DolphinViewContainer::setNameFilter); @@ -149,9 +111,13 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : connect(m_view, &DolphinView::urlChanged, m_filterBar, &FilterBar::slotUrlChanged); connect(m_view, &DolphinView::urlChanged, - m_urlNavigator, &KUrlNavigator::setLocationUrl); - connect(m_view, &DolphinView::urlChanged, m_messageWidget, &KMessageWidget::hide); + // m_urlNavigator stays in sync with m_view's location changes and + // keeps track of them so going back and forth in the history works. + connect(m_view, &DolphinView::urlChanged, + m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl); + connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlChanged, + this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); connect(m_view, &DolphinView::writeStateChanged, this, &DolphinViewContainer::writeStateChanged); connect(m_view, &DolphinView::requestItemInfo, @@ -183,22 +149,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate); - connect(m_urlNavigator, &KUrlNavigator::urlAboutToBeChanged, - this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); - connect(m_urlNavigator, &KUrlNavigator::urlChanged, - this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); - connect(m_urlNavigator, &KUrlNavigator::urlSelectionRequested, - this, &DolphinViewContainer::slotUrlSelectionRequested); - connect(m_urlNavigator, &KUrlNavigator::returnPressed, - this, &DolphinViewContainer::slotReturnPressed); - connect(m_urlNavigator, &KUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) { - m_view->dropUrls(destination, event, m_urlNavigator->dropWidget()); - }); - - connect(m_view, &DolphinView::directoryLoadingCompleted, this, [this]() { - m_emptyTrashButton->setVisible(m_view->url().scheme() == QLatin1String("trash")); - }); - // Initialize status bar m_statusBar = new DolphinStatusBar(this); m_statusBar->setUrl(m_view->url()); @@ -225,10 +175,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinViewContainer::delayedStatusBarUpdate); - navigatorLayout->addWidget(m_urlNavigator); - navigatorLayout->addWidget(m_emptyTrashButton); - - m_topLayout->addWidget(m_navigatorWidget); m_topLayout->addWidget(m_searchBox); m_topLayout->addWidget(m_messageWidget); m_topLayout->addWidget(m_view); @@ -263,7 +209,9 @@ QUrl DolphinViewContainer::url() const void DolphinViewContainer::setActive(bool active) { m_searchBox->setActive(active); - m_urlNavigator->setActive(active); + if (m_urlNavigatorConnected) { + m_urlNavigatorConnected->setActive(active); + } m_view->setActive(active); #ifdef HAVE_KACTIVITIES @@ -277,7 +225,6 @@ void DolphinViewContainer::setActive(bool active) bool DolphinViewContainer::isActive() const { - Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive()); return m_view->isActive(); } @@ -306,14 +253,24 @@ DolphinStatusBar* DolphinViewContainer::statusBar() return m_statusBar; } -const KUrlNavigator* DolphinViewContainer::urlNavigator() const +const DolphinUrlNavigator* DolphinViewContainer::urlNavigator() const { - return m_urlNavigator; + return m_urlNavigatorConnected; } -KUrlNavigator* DolphinViewContainer::urlNavigator() +DolphinUrlNavigator* DolphinViewContainer::urlNavigator() { - return m_urlNavigator; + return m_urlNavigatorConnected; +} + +const DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory() const +{ + return m_urlNavigator.get(); +} + +DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory() +{ + return m_urlNavigator.get(); } const DolphinView* DolphinViewContainer::view() const @@ -326,6 +283,61 @@ DolphinView* DolphinViewContainer::view() return m_view; } +void DolphinViewContainer::connectUrlNavigator(DolphinUrlNavigator *urlNavigator) +{ + Q_CHECK_PTR(urlNavigator); + Q_ASSERT(!m_urlNavigatorConnected); + Q_ASSERT(m_urlNavigator.get() != urlNavigator); + Q_CHECK_PTR(m_view); + + urlNavigator->setLocationUrl(m_view->url()); + if (m_urlNavigatorVisualState) { + urlNavigator->setVisualState(*m_urlNavigatorVisualState.get()); + m_urlNavigatorVisualState.reset(); + } + urlNavigator->setActive(isActive()); + + connect(m_view, &DolphinView::urlChanged, + urlNavigator, &DolphinUrlNavigator::setLocationUrl); + connect(urlNavigator, &DolphinUrlNavigator::urlChanged, + this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); + connect(urlNavigator, &DolphinUrlNavigator::activated, + this, &DolphinViewContainer::activate); + connect(urlNavigator, &DolphinUrlNavigator::urlAboutToBeChanged, + this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); + connect(urlNavigator, &DolphinUrlNavigator::urlSelectionRequested, + this, &DolphinViewContainer::slotUrlSelectionRequested); + connect(urlNavigator, &DolphinUrlNavigator::urlsDropped, + this, [=](const QUrl &destination, QDropEvent *event) { + m_view->dropUrls(destination, event, urlNavigator->dropWidget()); + }); + + m_urlNavigatorConnected = urlNavigator; +} + +void DolphinViewContainer::disconnectUrlNavigator() +{ + if (!m_urlNavigatorConnected) { + return; + } + + disconnect(m_view, &DolphinView::urlChanged, + m_urlNavigatorConnected, &DolphinUrlNavigator::setLocationUrl); + disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlChanged, + this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); + disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::activated, + this, &DolphinViewContainer::activate); + disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlAboutToBeChanged, + this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); + disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlSelectionRequested, + this, &DolphinViewContainer::slotUrlSelectionRequested); + disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlsDropped, + this, nullptr); + + m_urlNavigatorVisualState = m_urlNavigatorConnected->visualState(); + m_urlNavigatorConnected = nullptr; +} + void DolphinViewContainer::showMessage(const QString& msg, MessageType type) { if (msg.isEmpty()) { @@ -359,13 +371,10 @@ void DolphinViewContainer::showMessage(const QString& msg, MessageType type) void DolphinViewContainer::readSettings() { + // The startup settings should (only) get applied if they have been + // modified by the user. Otherwise keep the (possibly) different current + // setting of the filterbar. if (GeneralSettings::modifiedStartupSettings()) { - // The startup settings should only get applied if they have been - // modified by the user. Otherwise keep the (possibly) different current - // settings of the URL navigator and the filterbar. - m_urlNavigator->setUrlEditable(GeneralSettings::editableUrl()); - m_urlNavigator->setShowFullPath(GeneralSettings::showFullPath()); - m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); setFilterBarVisible(GeneralSettings::filterBar()); } @@ -381,10 +390,9 @@ bool DolphinViewContainer::isFilterBarVisible() const void DolphinViewContainer::setSearchModeEnabled(bool enabled) { m_searchBox->setVisible(enabled); - m_navigatorWidget->setVisible(!enabled); if (enabled) { - const QUrl& locationUrl = m_urlNavigator->locationUrl(); + const QUrl& locationUrl = m_urlNavigatorConnected->locationUrl(); m_searchBox->fromSearchUrl(locationUrl); } @@ -405,7 +413,7 @@ void DolphinViewContainer::setSearchModeEnabled(bool enabled) if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) { url = Dolphin::homeUrl(); } - m_urlNavigator->setLocationUrl(url); + m_urlNavigatorConnected->setLocationUrl(url); } m_searchModeEnabled = enabled; @@ -680,7 +688,9 @@ void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const QUrl&) void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl& url) { - slotReturnPressed(); + if (m_urlNavigatorConnected) { + m_urlNavigatorConnected->slotReturnPressed(); + } if (KProtocolManager::supportsListing(url)) { setSearchModeEnabled(isSearchUrl(url)); @@ -739,24 +749,13 @@ void DolphinViewContainer::requestFocus() m_view->setFocus(); } -void DolphinViewContainer::saveUrlCompletionMode(KCompletion::CompletionMode completion) -{ - GeneralSettings::setUrlCompletionMode(completion); -} - -void DolphinViewContainer::slotReturnPressed() -{ - if (!GeneralSettings::editableUrl()) { - m_urlNavigator->setUrlEditable(false); - } -} - void DolphinViewContainer::startSearching() { + Q_CHECK_PTR(m_urlNavigatorConnected); const QUrl url = m_searchBox->urlForSearching(); if (url.isValid() && !url.isEmpty()) { m_view->setViewPropertiesContext(QStringLiteral("search")); - m_urlNavigator->setLocationUrl(url); + m_urlNavigatorConnected->setLocationUrl(url); } } diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index ee1193f19..77b74d189 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -8,9 +8,9 @@ #define DOLPHINVIEWCONTAINER_H #include "config-kactivities.h" +#include "dolphinurlnavigator.h" #include "views/dolphinview.h" -#include <KCompletion> #include <KFileItem> #include <KIO/Job> #include <KUrlNavigator> @@ -85,13 +85,54 @@ public: const DolphinStatusBar* statusBar() const; DolphinStatusBar* statusBar(); - const KUrlNavigator* urlNavigator() const; - KUrlNavigator* urlNavigator(); + /** + * @return An UrlNavigator that is controlling this view + * or nullptr if there is none. + * @see connectUrlNavigator() + * @see disconnectUrlNavigator() + * + * Use urlNavigatorInternalWithHistory() if you want to access the history. + * @see urlNavigatorInternalWithHistory() + */ + const DolphinUrlNavigator *urlNavigator() const; + /** + * @return An UrlNavigator that is controlling this view + * or nullptr if there is none. + * @see connectUrlNavigator() + * @see disconnectUrlNavigator() + * + * Use urlNavigatorInternalWithHistory() if you want to access the history. + * @see urlNavigatorInternalWithHistory() + */ + DolphinUrlNavigator *urlNavigator(); + + /** + * @return An UrlNavigator that contains this view's history. + * Use urlNavigator() instead when not accessing the history. + */ + const DolphinUrlNavigator *urlNavigatorInternalWithHistory() const; + /** + * @return An UrlNavigator that contains this view's history. + * Use urlNavigator() instead when not accessing the history. + */ + DolphinUrlNavigator *urlNavigatorInternalWithHistory(); const DolphinView* view() const; DolphinView* view(); /** + * @param urlNavigator The UrlNavigator that is supposed to control + * this view. + */ + void connectUrlNavigator(DolphinUrlNavigator *urlNavigator); + + /** + * Disconnects the navigator that is currently controling the view. + * This method completely reverses connectUrlNavigator(). + */ + void disconnectUrlNavigator(); + + /** * Shows the message \msg with the given type non-modal above * the view-content. */ @@ -282,14 +323,6 @@ private slots: void requestFocus(); /** - * Saves the currently used URL completion mode of - * the URL navigator. - */ - void saveUrlCompletionMode(KCompletion::CompletionMode completion); - - void slotReturnPressed(); - - /** * Gets the search URL from the searchbox and starts searching. */ void startSearching(); @@ -328,9 +361,20 @@ private: private: QVBoxLayout* m_topLayout; - QWidget* m_navigatorWidget; - KUrlNavigator* m_urlNavigator; - QPushButton* m_emptyTrashButton; + + /** + * The internal UrlNavigator which is never visible to the user. + * m_urlNavigator is used even when another UrlNavigator is controlling + * the view to keep track of this object's history. + */ + std::unique_ptr<DolphinUrlNavigator> m_urlNavigator; + + /** + * The UrlNavigator that is currently connected to the view. + * This is a nullptr if no UrlNavigator is connected. + * Otherwise it's one of the UrlNavigators visible in the toolbar. + */ + QPointer<DolphinUrlNavigator> m_urlNavigatorConnected; DolphinSearchBox* m_searchBox; bool m_searchModeEnabled; KMessageWidget* m_messageWidget; @@ -343,6 +387,11 @@ private: QTimer* m_statusBarTimer; // Triggers a delayed update QElapsedTimer m_statusBarTimestamp; // Time in ms since last update bool m_autoGrabFocus; + /** + * The visual state to be applied to the next UrlNavigator that gets + * connected to this ViewContainer. + */ + std::unique_ptr<DolphinUrlNavigator::VisualState> m_urlNavigatorVisualState; #ifdef HAVE_KACTIVITIES private: diff --git a/src/main.cpp b/src/main.cpp index ef2905d77..a4b1b1963 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include <KCrash> #include <KDBusService> #include <KLocalizedString> +#include <KToolBar> #include <Kdelibs4ConfigMigrator> #include <KConfigGui> @@ -213,6 +214,12 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) } } } + Qt::ToolBarArea area = mainWindow->toolBarArea(mainWindow->toolBar()); + if (area != Qt::TopToolBarArea && area != Qt::BottomToolBarArea) { + // Migrate users with disabled tool bar positions. + // Remove this a few years from now (2020). + mainWindow->addToolBar(Qt::TopToolBarArea, mainWindow->toolBar()); + } #ifdef HAVE_KUSERFEEDBACK auto feedbackProvider = DolphinFeedbackProvider::instance(); diff --git a/src/panels/places/placesitemlistwidget.cpp b/src/panels/places/placesitemlistwidget.cpp index 745a6ae1b..38bc13438 100644 --- a/src/panels/places/placesitemlistwidget.cpp +++ b/src/panels/places/placesitemlistwidget.cpp @@ -12,6 +12,8 @@ #include <KColorScheme> #include <KIO/FileSystemFreeSpaceJob> +#include <Solid/Device> +#include <Solid/NetworkShare> #define CAPACITYBAR_HEIGHT 2 #define CAPACITYBAR_MARGIN 2 @@ -41,12 +43,19 @@ QPalette::ColorRole PlacesItemListWidget::normalTextColorRole() const void PlacesItemListWidget::updateCapacityBar() { - const bool isDevice = !data().value("udi").toString().isEmpty(); - const QUrl url = data().value("url").toUrl(); - if (!(isDevice && url.isLocalFile())) { + const QString udi = data().value("udi").toString(); + if (udi.isEmpty()) { resetCapacityBar(); return; } + const Solid::Device device = Solid::Device(udi); + if (device.isDeviceInterface(Solid::DeviceInterface::NetworkShare) + || device.isDeviceInterface(Solid::DeviceInterface::OpticalDrive) + || device.isDeviceInterface(Solid::DeviceInterface::OpticalDisc)) { + resetCapacityBar(); + return; + } + const QUrl url = data().value("url").toUrl(); if (m_freeSpaceInfo.job || !m_freeSpaceInfo.lastUpdated.hasExpired()) { // Job running or cache is still valid. |
