File indexing completed on 2024-05-12 16:02:09

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003    SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0004    SPDX-FileCopyrightText: 2021 Alvin Wong <alvin@alvinhc.com>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "KoDockWidgetTitleBar.h"
0010 #include "KoDockWidgetTitleBar_p.h"
0011 #include "KoDockWidgetTitleBarButton.h"
0012 
0013 #include <KoIcon.h>
0014 #include <kis_icon_utils.h>
0015 
0016 #include <WidgetsDebug.h>
0017 #include <klocalizedstring.h>
0018 
0019 #include <QAbstractButton>
0020 #include <QAction>
0021 #include <QHBoxLayout>
0022 #include <QLabel>
0023 #include <QStyle>
0024 #include <QStylePainter>
0025 #include <QStyleOptionFrame>
0026 
0027 #include <KSqueezedTextLabel>
0028 
0029 static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
0030 {
0031     return (dockwidget->features() & feature) == feature;
0032 }
0033 
0034 constexpr int SPACING = 6;
0035 
0036 KoDockWidgetTitleBar::KoDockWidgetTitleBar(QDockWidget* dockWidget)
0037         : QWidget(dockWidget), d(new Private(this))
0038 {
0039     d->floatIcon = kisIcon("docker_float");
0040     d->floatButton = new KoDockWidgetTitleBarButton(this);
0041     d->floatButton->setIcon(d->floatIcon);
0042     connect(d->floatButton, SIGNAL(clicked()), SLOT(toggleFloating()));
0043     d->floatButton->setVisible(true);
0044     d->floatButton->setToolTip(i18nc("@info:tooltip", "Float Docker"));
0045     d->floatButton->setStyleSheet("border: 0");
0046 
0047     d->removeIcon = kisIcon("docker_close");
0048     d->closeButton = new KoDockWidgetTitleBarButton(this);
0049     d->closeButton->setIcon(d->removeIcon);
0050     connect(d->closeButton, SIGNAL(clicked()), dockWidget, SLOT(close()));
0051     d->closeButton->setVisible(true);
0052     d->closeButton->setToolTip(i18nc("@info:tooltip", "Close Docker"));   
0053     d->closeButton->setStyleSheet("border: 0"); // border makes the header busy looking (appears on some OSs)
0054 
0055     d->lockIcon = kisIcon("docker_lock_a");
0056     d->lockButton = new KoDockWidgetTitleBarButton(this);
0057     d->lockButton->setCheckable(true);
0058     d->lockButton->setIcon(d->lockIcon);
0059     connect(d->lockButton, SIGNAL(toggled(bool)), SLOT(setLocked(bool)));
0060     d->lockButton->setVisible(true);
0061     d->lockButton->setToolTip(i18nc("@info:tooltip", "Lock Docker"));
0062     d->lockButton->setStyleSheet("border: 0");
0063 
0064     d->updateButtonSizes();
0065 
0066     d->titleLabel = new KSqueezedTextLabel(this);
0067     d->titleLabel->setTextElideMode(Qt::ElideRight);
0068     d->titleLabel->setText(dockWidget->windowTitle());
0069 
0070     QHBoxLayout *layout = new QHBoxLayout(this);
0071     layout->setContentsMargins(2, 0, 2, 0);
0072     layout->setSpacing(SPACING);
0073     layout->addWidget(d->lockButton);
0074     layout->addWidget(d->titleLabel, 1);
0075     layout->addWidget(d->floatButton);
0076     layout->addWidget(d->closeButton);
0077 
0078     connect(dockWidget, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), SLOT(featuresChanged(QDockWidget::DockWidgetFeatures)));
0079     connect(dockWidget, SIGNAL(topLevelChanged(bool)), SLOT(topLevelChanged(bool)));
0080     connect(dockWidget, SIGNAL(windowTitleChanged(const QString &)), SLOT(dockWidgetTitleChanged(const QString &)));
0081 
0082     d->featuresChanged(QDockWidget::NoDockWidgetFeatures);
0083 }
0084 
0085 KoDockWidgetTitleBar::~KoDockWidgetTitleBar()
0086 {
0087     delete d;
0088 }
0089 
0090 void KoDockWidgetTitleBar::paintEvent(QPaintEvent*)
0091 {
0092     QStylePainter p(this);
0093 
0094     QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
0095 
0096     int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0;
0097     int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
0098 
0099     QStyleOptionDockWidget titleOpt;
0100     titleOpt.initFrom(q);
0101 
0102     QSize lockButtonSize(0,0);
0103     if (d->lockButton->isVisible()) {
0104         lockButtonSize = d->lockButton->size();
0105     }
0106 
0107     // To improve the look with Fusion which has weird 13x15 button sizes
0108     int fusionTextOffset = 0;
0109     QRect styleTestRect = q->style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &titleOpt, q);
0110     if (styleTestRect.width() < 16) {
0111         fusionTextOffset = d->lockButton->x();
0112     }
0113 
0114     titleOpt.rect = QRect(QPoint(fw + mw + lockButtonSize.width() + fusionTextOffset, 0),
0115                           QSize(geometry().width() - (fw * 2) -  mw - lockButtonSize.width(), geometry().height()));
0116     // We don't print the title text here. Instead, we have a QLabel.
0117     //   titleOpt.title = q->windowTitle();
0118     // FIXME: Maybe we just shouldn't use a QStylePainter at all?
0119     titleOpt.title = QString();
0120     titleOpt.closable = hasFeature(q, QDockWidget::DockWidgetClosable);
0121     titleOpt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable);
0122     p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
0123 }
0124 
0125 void KoDockWidgetTitleBar::resizeEvent(QResizeEvent*)
0126 {
0127     QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
0128 
0129     if (q->isFloating() || (width() < (d->closeButton->width() + d->floatButton->width() + d->lockButton->width()) + 32)) {
0130         d->lockButton->setVisible(false);
0131     } else {
0132         d->lockButton->setVisible(true);
0133     }
0134 }
0135 
0136 void KoDockWidgetTitleBar::setLocked(bool locked)
0137 {
0138     QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
0139 
0140     d->locked = locked;
0141     d->lockButton->blockSignals(true);
0142     d->lockButton->setChecked(locked);
0143     d->lockButton->blockSignals(false);
0144 
0145     if (locked) {
0146         d->features = q->features();
0147         q->setFeatures(QDockWidget::NoDockWidgetFeatures);
0148     }
0149     else {
0150         q->setFeatures(d->features);
0151     }
0152 
0153     q->toggleViewAction()->setEnabled(!locked);
0154     d->closeButton->setEnabled(!locked);
0155     d->floatButton->setEnabled(!locked);
0156     d->floatButton->setVisible(!locked);
0157 
0158     d->updateIcons();
0159     q->setProperty("Locked", locked);
0160 }
0161 
0162 void KoDockWidgetTitleBar::updateIcons()
0163 {
0164     d->updateIcons();
0165 }
0166 
0167 void KoDockWidgetTitleBar::Private::toggleFloating()
0168 {
0169     QDockWidget *q = qobject_cast<QDockWidget*>(thePublic->parentWidget());
0170 
0171     q->setFloating(!q->isFloating());
0172     updateIcons();
0173 }
0174 
0175 void KoDockWidgetTitleBar::Private::topLevelChanged(bool topLevel)
0176 {
0177     lockButton->setEnabled(!topLevel);
0178     updateIcons();
0179 }
0180 
0181 void KoDockWidgetTitleBar::Private::featuresChanged(QDockWidget::DockWidgetFeatures)
0182 {
0183     QDockWidget *q = qobject_cast<QDockWidget*>(thePublic->parentWidget());
0184 
0185     closeButton->setVisible(hasFeature(q, QDockWidget::DockWidgetClosable));
0186     floatButton->setVisible(hasFeature(q, QDockWidget::DockWidgetFloatable));
0187 
0188     updateButtonSizes();
0189     thePublic->resizeEvent(0);
0190 }
0191 
0192 void KoDockWidgetTitleBar::Private::dockWidgetTitleChanged(const QString &title)
0193 {
0194     titleLabel->setText(title);
0195 }
0196 
0197 
0198 void KoDockWidgetTitleBar::Private::updateIcons()
0199 {
0200     lockIcon = (!locked) ? kisIcon("docker_lock_a") : kisIcon("docker_lock_b");
0201     lockButton->setIcon(lockIcon);
0202 
0203     // this method gets called when switching themes, so update all of the themed icons now
0204     floatButton->setIcon(kisIcon("docker_float"));
0205     closeButton->setIcon(kisIcon("docker_close"));
0206 
0207     thePublic->resizeEvent(0);
0208 }
0209 
0210 void KoDockWidgetTitleBar::Private::updateButtonSizes()
0211 {
0212     const QDockWidget *q = qobject_cast<QDockWidget*>(thePublic->parentWidget());
0213 
0214     const int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0;
0215 
0216     QStyleOptionDockWidget opt;
0217     opt.initFrom(q);
0218     opt.rect = QRect(QPoint(fw, fw), QSize(thePublic->geometry().width() - (fw * 2), thePublic->geometry().height() - (fw * 2)));
0219     opt.title = q->windowTitle();
0220     // Originally it was:
0221     //   opt.closable = hasFeature(q, QDockWidget::DockWidgetClosable);
0222     // but I think we better just always pretend the close button is visible to
0223     // get the button size.
0224     opt.closable = true;
0225     opt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable);
0226 
0227     // Again, we just always use the size of the close button, so we don't
0228     // need to get the size of the float button...
0229     //   QRect floatRect = q->style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &opt, q);
0230     const QRect closeRect = q->style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &opt, q);
0231     QSize buttonSize = closeRect.size();
0232     if (buttonSize.width() < 16) {
0233         // To improve the look with Fusion which has weird 13x15 button sizes
0234         buttonSize = QSize(16, 16);
0235     } else if (buttonSize.width() != buttonSize.height()) {
0236         // Just make sure the button is square...
0237         buttonSize.setHeight(buttonSize.width());
0238     }
0239 
0240     floatButton->setFixedSize(buttonSize);
0241     closeButton->setFixedSize(buttonSize);
0242     lockButton->setFixedSize(buttonSize);
0243 }
0244 
0245 //have to include this because of Q_PRIVATE_SLOT
0246 #include "moc_KoDockWidgetTitleBar.cpp"