File indexing completed on 2024-05-05 04:19:13

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2021 Felix Ernst <fe.a.ernst@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "alignwithsidebarwidgetaction.h"
0009 
0010 #include <lib/gwenviewconfig.h>
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QApplication>
0015 #include <QEvent>
0016 #include <QStyle>
0017 
0018 #include <cmath>
0019 
0020 AlignWithSideBarWidgetAction::AlignWithSideBarWidgetAction(QObject *parent)
0021     : QWidgetAction(parent)
0022 {
0023     setText(i18nc("@action:inmenu a spacer that aligns toolbar buttons with the sidebar", "Sidebar Alignment Spacer"));
0024 }
0025 
0026 void AlignWithSideBarWidgetAction::setSideBar(QWidget *sideBar)
0027 {
0028     mSideBar = sideBar;
0029     const QList<QWidget *> widgets = createdWidgets();
0030     for (auto widget : widgets) {
0031         static_cast<AligningSpacer *>(widget)->setSideBar(sideBar);
0032     }
0033 }
0034 
0035 QWidget *AlignWithSideBarWidgetAction::createWidget(QWidget *parent)
0036 {
0037     auto aligningSpacer = new AligningSpacer(parent);
0038     aligningSpacer->setSideBar(mSideBar);
0039     return aligningSpacer;
0040 }
0041 
0042 AligningSpacer::AligningSpacer(QWidget *parent)
0043     : QWidget{parent}
0044 {
0045     if ((mToolbar = qobject_cast<QToolBar *>(parent))) {
0046         connect(mToolbar, &QToolBar::orientationChanged, this, &AligningSpacer::update);
0047     }
0048 }
0049 
0050 void AligningSpacer::setSideBar(QWidget *sideBar)
0051 {
0052     if (mSideBar) {
0053         mSideBar->removeEventFilter(this);
0054     }
0055     mSideBar = sideBar;
0056     if (mSideBar) {
0057         mSideBar->installEventFilter(this);
0058     }
0059     update();
0060 }
0061 
0062 bool AligningSpacer::eventFilter(QObject * /* watched */, QEvent *event)
0063 {
0064     switch (event->type()) {
0065     case QEvent::Show:
0066     case QEvent::Hide:
0067     case QEvent::Resize:
0068         update();
0069         return false;
0070     default:
0071         return false;
0072     }
0073 }
0074 
0075 void AligningSpacer::moveEvent(QMoveEvent * /* moved */)
0076 {
0077     update();
0078 }
0079 
0080 void AligningSpacer::setFollowingSeparatorVisible(bool visible)
0081 {
0082     if (!mToolbar) {
0083         return;
0084     }
0085     if (mToolbar->orientation() == Qt::Vertical) {
0086         visible = true;
0087     }
0088 
0089     const QList<QAction *> toolbarActions = mToolbar->actions();
0090     bool actionForThisSpacerFound = false; // If this is true, the next action in the iteration
0091                                            // is what we are interested in.
0092     for (auto action : toolbarActions) {
0093         if (actionForThisSpacerFound) {
0094             if (visible && mWasSeparatorRemoved) {
0095                 mToolbar->insertSeparator(action);
0096                 mWasSeparatorRemoved = false;
0097             } else if (!visible && action->isSeparator()) {
0098                 mToolbar->removeAction(action);
0099                 mWasSeparatorRemoved = true;
0100             }
0101             return;
0102         }
0103         if (mToolbar->widgetForAction(action) == this) {
0104             actionForThisSpacerFound = true;
0105         }
0106     }
0107 }
0108 
0109 void AligningSpacer::update()
0110 {
0111     const bool oldWasSeparatorRemoved = mWasSeparatorRemoved;
0112     if (updateWidth() < 8) {
0113         // Because the spacer is so small the separator should be visible to serve its purpose.
0114         if (mWasSeparatorRemoved) {
0115             setFollowingSeparatorVisible(true);
0116         }
0117     } else if (mSideBar) {
0118         setFollowingSeparatorVisible(mSideBar->isVisible());
0119     }
0120 
0121     if (oldWasSeparatorRemoved != mWasSeparatorRemoved) { // One more updateWidth() is needed.
0122         updateWidth();
0123     }
0124 }
0125 
0126 int AligningSpacer::updateWidth()
0127 {
0128     if (!mSideBar || (mToolbar && mToolbar->orientation() == Qt::Vertical)) {
0129         setFixedWidth(0);
0130         return 0;
0131     }
0132 
0133     const auto separatorWidth = static_cast<float>(style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, nullptr, this));
0134     int sideBarWidth = mSideBar->geometry().width();
0135     if (sideBarWidth <= 0) {
0136         if (!Gwenview::GwenviewConfig::sideBarSplitterSizes().isEmpty()) {
0137             sideBarWidth = Gwenview::GwenviewConfig::sideBarSplitterSizes().constFirst();
0138         } else {
0139             // We get to this code when gwenview.rc was deleted or when this is the first run.
0140             // There doesn't seem to be an easy way to get the width the sideBar is going
0141             // to have at this point in time so we set it to some value that leads to
0142             // a nice default appearance on the first run.
0143             if (QApplication::layoutDirection() != Qt::RightToLeft) {
0144                 sideBarWidth = x() + separatorWidth * 2; // Leads to a nice default spacing.
0145             } else {
0146                 sideBarWidth = mToolbar->width() - x() + separatorWidth * 2;
0147             }
0148             mSideBar->resize(sideBarWidth, mSideBar->height()); // Make sure it aligns.
0149         }
0150     }
0151 
0152     int newWidth;
0153     if (QApplication::layoutDirection() != Qt::RightToLeft) {
0154         newWidth = sideBarWidth - mapTo(window(), QPoint(0, 0)).x();
0155     } else {
0156         newWidth = sideBarWidth - window()->width() + mapTo(window(), QPoint(width(), 0)).x();
0157     }
0158     if (!mWasSeparatorRemoved) {
0159         // Make it so a potentially following separator looks aligned with the sidebar.
0160         newWidth -= std::ceil(separatorWidth * 0.3);
0161     } else {
0162         // Make it so removing the separator doesn't change the toolbutton positions.
0163         newWidth += std::floor(separatorWidth * 0.7);
0164     }
0165 
0166     // Make sure nothing weird can happen.
0167     if (newWidth < 0 || newWidth > sideBarWidth) {
0168         newWidth = 0;
0169     }
0170     setFixedWidth(newWidth);
0171     return newWidth;
0172 }
0173 
0174 #include "moc_alignwithsidebarwidgetaction.cpp"