File indexing completed on 2024-04-14 15:49:27

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2020 Felix Ernst <felixernst@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #ifndef DOLPHINNAVIGATORSWIDGETACTION_H
0009 #define DOLPHINNAVIGATORSWIDGETACTION_H
0010 
0011 #include "dolphinurlnavigator.h"
0012 
0013 #include <QPointer>
0014 #include <QSplitter>
0015 #include <QTimer>
0016 #include <QWidgetAction>
0017 
0018 #include <memory>
0019 
0020 class KXmlGuiWindow;
0021 class QPushButton;
0022 
0023 /**
0024  * @brief QWidgetAction that allows to use DolphinUrlNavigators in a toolbar.
0025  *
0026  * This class is mainly a container that manages up to two DolphinUrlNavigator objects so they
0027  * can be added to a toolbar. It also deals with alignment.
0028  *
0029  * The structure of the defaultWidget() of this QWidgetAction is as follows:
0030  * - A QSplitter manages up to two sides which each correspond to one DolphinViewContainer.
0031  *      The secondary side only exists for split view and is created by
0032  *      createSecondaryUrlNavigator() when necessary.
0033  * - Each side is a QWidget which I call NavigatorWidget with a QHBoxLayout.
0034  * - Each NavigatorWidget consists an UrlNavigator, an emptyTrashButton, a
0035  *   networkFolderButton, and spacing.
0036  * - Only the primary navigatorWidget has leading spacing. Both have trailing spacing.
0037  *      The spacing is there to align the UrlNavigator with its DolphinViewContainer.
0038  */
0039 class DolphinNavigatorsWidgetAction : public QWidgetAction
0040 {
0041     Q_OBJECT
0042 
0043 public:
0044     DolphinNavigatorsWidgetAction(QWidget *parent = nullptr);
0045 
0046     /**
0047      * Adjusts the width of the spacings used to align the UrlNavigators with ViewContainers.
0048      * This can only work nicely if up-to-date geometry of ViewContainers is cached so
0049      * followViewContainersGeometry() has to have been called at least once before.
0050      */
0051     void adjustSpacing();
0052 
0053     /**
0054      * The secondary UrlNavigator is only created on-demand. Such an action is not necessary
0055      * for the primary UrlNavigator which is created preemptively.
0056      *
0057      * This method should preferably only be called when:
0058      * - Split view is activated in the active tab
0059      * OR
0060      * - A switch to a tab that is already in split view mode is occurring
0061      */
0062     void createSecondaryUrlNavigator();
0063 
0064     /**
0065      * Notify this widget of changes in geometry of the ViewContainers it tries to be
0066      * aligned with.
0067      */
0068     void followViewContainersGeometry(QWidget *primaryViewContainer, QWidget *secondaryViewContainer = nullptr);
0069 
0070     bool isInToolbar() const;
0071 
0072     /**
0073      * @return the primary UrlNavigator.
0074      */
0075     DolphinUrlNavigator *primaryUrlNavigator() const;
0076     /**
0077      * @return the secondary UrlNavigator and nullptr if it doesn't exist.
0078      */
0079     DolphinUrlNavigator *secondaryUrlNavigator() const;
0080 
0081     /**
0082      * Change the visibility of the secondary UrlNavigator including spacing.
0083      * @param visible Setting this to false will completely hide the secondary side of this
0084      *                WidgetAction's QSplitter making the QSplitter effectively disappear.
0085      */
0086     void setSecondaryNavigatorVisible(bool visible);
0087 
0088 protected:
0089     /**
0090      * There should always ever be one navigatorsWidget for this action so
0091      * this method always returns the same widget and reparents it.
0092      * You normally don't have to use this method directly because
0093      * QWidgetAction::requestWidget() is used to obtain the navigatorsWidget
0094      * and to steal it from wherever it was prior.
0095      * @param parent the new parent of the navigatorsWidget.
0096      */
0097     QWidget *createWidget(QWidget *parent) override;
0098 
0099     /** @see QWidgetAction::deleteWidget() */
0100     void deleteWidget(QWidget *widget) override;
0101 
0102 private:
0103     /**
0104      * In Left-to-right languages the Primary side will be the left one.
0105      */
0106     enum Side { Primary, Secondary };
0107     /**
0108      * Used to create the navigatorWidgets for both sides of the QSplitter.
0109      */
0110     QWidget *createNavigatorWidget(Side side) const;
0111 
0112     /**
0113      * Used to retrieve the emptyTrashButtons for the navigatorWidgets on both sides.
0114      */
0115     QPushButton *emptyTrashButton(Side side);
0116 
0117     /**
0118      * Creates a new empty trash button.
0119      * @param urlNavigator Only when this UrlNavigator shows the trash directory
0120      *                     will the button be visible.
0121      * @param parent       Aside from the usual QObject deletion mechanisms,
0122      *                     this parameter influences the positioning of dialog windows
0123      *                     pertaining to this trash button.
0124      */
0125     QPushButton *newEmptyTrashButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const;
0126 
0127     /**
0128      * Used to retrieve the networkFolderButtons for the navigatorWidgets on
0129      * both sides.
0130      */
0131     QPushButton *networkFolderButton(Side side);
0132 
0133     /**
0134      * Creates a new add "network folder" button.
0135      * @param urlNavigator Only when this UrlNavigator shows the remote directory
0136      *                     will the button be visible.
0137      * @param parent       The object that should be the button's parent.
0138      */
0139     QPushButton *newNetworkFolderButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const;
0140 
0141     enum Position { Leading, Trailing };
0142     /**
0143      * Used to retrieve both the leading and trailing spacing for the navigatorWidgets
0144      * on both sides. A secondary leading spacing does not exist.
0145      */
0146     QWidget *spacing(Side side, Position position) const;
0147 
0148     /**
0149      * Sets this action's text depending on the amount of visible UrlNavigators.
0150      */
0151     void updateText();
0152 
0153     /**
0154      * The defaultWidget() of this QWidgetAction.
0155      */
0156     std::unique_ptr<QSplitter> m_splitter;
0157 
0158     /**
0159      * adjustSpacing() has to be called slightly later than when urlChanged is emitted.
0160      * This timer bridges that time.
0161      */
0162     std::unique_ptr<QTimer> m_adjustSpacingTimer;
0163 
0164     /**
0165      * Extracts the geometry information needed by adjustSpacing() from
0166      * ViewContainers. They are also monitored for size changes which
0167      * will lead to adjustSpacing() calls.
0168      */
0169     class ViewGeometriesHelper : public QObject
0170     {
0171     public:
0172         /**
0173          * @param navigatorsWidget       The QWidget of the navigatorsWidgetAction.
0174          * @param navigatorsWidgetAction is only used to call adjustSpacing() whenever that is
0175          *                               deemed necessary.
0176          */
0177         ViewGeometriesHelper(QWidget *navigatorsWidget, DolphinNavigatorsWidgetAction *navigatorsWidgetAction);
0178 
0179         /**
0180          * Calls m_navigatorsWidgetAction::adjustSpacing() when a watched object is resized.
0181          */
0182         bool eventFilter(QObject *watched, QEvent *event) override;
0183 
0184         /**
0185          * Sets the ViewContainers whose geometry is obtained when viewGeometries() is called.
0186          */
0187         void setViewContainers(QWidget *primaryViewContainer, QWidget *secondaryViewContainer = nullptr);
0188 
0189         struct Geometries {
0190             int globalXOfNavigatorsWidget;
0191             int globalXOfPrimary;
0192             int widthOfPrimary;
0193             int globalXOfSecondary;
0194             int widthOfSecondary;
0195         };
0196         /**
0197          * @return a Geometries struct that contains values adjustSpacing() requires.
0198          */
0199         Geometries viewGeometries();
0200 
0201     private:
0202         QWidget *m_navigatorsWidget;
0203         /** Is only used to call adjustSpacing() whenever that is deemed necessary. */
0204         DolphinNavigatorsWidgetAction *m_navigatorsWidgetAction;
0205 
0206         QPointer<QWidget> m_primaryViewContainer;
0207         QPointer<QWidget> m_secondaryViewContainer;
0208     };
0209 
0210     ViewGeometriesHelper m_viewGeometriesHelper;
0211 
0212     /**
0213      * Used to check if the window has been resized.
0214      * @see ViewGeometriesHelper::eventFilter() for why this is needed.
0215      */
0216     int m_previousWindowWidth = -1;
0217 };
0218 
0219 #endif // DOLPHINNAVIGATORSWIDGETACTION_H