File indexing completed on 2025-04-27 03:58:31

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-15-08
0007  * Description : A floatable/dockable widget for thumbnail bars,
0008  *               providing i drag handle similar to the
0009  *               one on toolbars and a standard QAction to show/hide the
0010  *               thumbnail bar. It inherits QDockWidget and can be used in
0011  *               the dock area's of a QMainWindow.
0012  *
0013  * SPDX-FileCopyrightText: 2009 by Pieter Edelman <p dot edelman at gmx dot net>
0014  *
0015  * SPDX-License-Identifier: GPL-2.0-or-later
0016  *
0017  * ============================================================ */
0018 
0019 #include "thumbbardock.h"
0020 
0021 // Qt includes
0022 
0023 #include <QKeySequence>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 namespace Digikam
0030 {
0031 
0032 class Q_DECL_HIDDEN DragHandle::Private
0033 {
0034 
0035 public:
0036 
0037     explicit Private()
0038       : parent     (nullptr),
0039         currentArea(Qt::LeftDockWidgetArea)
0040     {
0041     }
0042 
0043     QDockWidget*       parent;
0044     Qt::DockWidgetArea currentArea;
0045 };
0046 
0047 DragHandle::DragHandle(QDockWidget* const parent)
0048     : QWidget(),
0049       d      (new Private)
0050 {
0051     d->parent = parent;
0052 
0053     setCursor(Qt::PointingHandCursor);
0054     setToolTip(i18n("Drag to reposition"));
0055 
0056     // When the dock location changes, check if the orientation has changed.
0057 
0058     connect(d->parent, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
0059             this, SLOT(dockLocationChanged(Qt::DockWidgetArea)));
0060 }
0061 
0062 DragHandle::~DragHandle()
0063 {
0064     delete d;
0065 }
0066 
0067 void DragHandle::paintEvent(QPaintEvent*)
0068 {
0069     QPainter p(this);
0070     QStyle* const style = d->parent->style();
0071 
0072     // The QStyleOptionToolBar contains every parameter needed to draw the
0073     // handle.
0074 
0075     QStyleOptionToolBar opt;
0076     opt.initFrom(d->parent);
0077     opt.features = QStyleOptionToolBar::Movable;
0078 
0079     // If the thumbnail bar is laid out horizontally, the state should be set
0080     // to horizontal to draw the handle in the proper orientation.
0081 
0082     if ((d->currentArea == Qt::LeftDockWidgetArea) || (d->currentArea == Qt::RightDockWidgetArea))
0083     {
0084         opt.rect = QRect(opt.rect.x(), opt.rect.y(),
0085                          d->parent->width(),
0086                          style->pixelMetric(QStyle::PM_ToolBarHandleExtent));
0087     }
0088     else
0089     {
0090         opt.state |= QStyle::State_Horizontal;
0091         opt.rect   = QRect(opt.rect.x(), opt.rect.y(),
0092                            style->pixelMetric(QStyle::PM_ToolBarHandleExtent),
0093                            d->parent->height());
0094     }
0095 
0096     // Draw the toolbar handle.
0097 
0098     style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
0099 }
0100 
0101 void DragHandle::dockLocationChanged(Qt::DockWidgetArea area)
0102 {
0103     d->currentArea = area;
0104 
0105     // When the dock widget that contains this handle changes to a different
0106     // orientation, the DockWidgetVerticalTitleBar feature needs to be adjusted:
0107     // present when the thumbbar orientation is horizontal, absent when it is
0108     // vertical(!)
0109 
0110     if ((d->currentArea == Qt::LeftDockWidgetArea) || (d->currentArea == Qt::RightDockWidgetArea))
0111     {
0112         d->parent->setFeatures(d->parent->features() & ~QDockWidget::DockWidgetVerticalTitleBar);
0113     }
0114     else
0115     {
0116         d->parent->setFeatures(d->parent->features() | QDockWidget::DockWidgetVerticalTitleBar);
0117     }
0118 }
0119 
0120 QSize DragHandle::sizeHint() const
0121 {
0122     // Size is the sum of the margin, frame width and the handle itself.
0123 
0124     QStyle* const style = d->parent->style();
0125     int handleWidth     = style->pixelMetric(QStyle::PM_ToolBarHandleExtent);
0126     int margin          = style->pixelMetric(QStyle::PM_ToolBarItemMargin) +
0127                           style->pixelMetric(QStyle::PM_ToolBarFrameWidth);
0128 
0129     if ((d->currentArea == Qt::LeftDockWidgetArea) || (d->currentArea == Qt::RightDockWidgetArea))
0130     {
0131         return QSize(d->parent->width(), handleWidth + 2*margin);
0132     }
0133     else
0134     {
0135         return QSize(handleWidth + 2*margin, d->parent->height());
0136     }
0137 }
0138 
0139 QSize DragHandle::minimumSizeHint() const
0140 {
0141     return QSize(0, 0);
0142 }
0143 
0144 // ----------------------------------------------------------------------------
0145 
0146 ThumbBarDock::ThumbBarDock(QWidget* const parent, Qt::WindowFlags flags)
0147     : QDockWidget(parent, flags),
0148       m_visible  (SHOULD_BE_SHOWN)
0149 {
0150     // Use a DragHandle as title bar widget.
0151 
0152     setTitleBarWidget(new DragHandle(this));
0153     setContextMenuPolicy(Qt::PreventContextMenu);
0154 }
0155 
0156 ThumbBarDock::~ThumbBarDock()
0157 {
0158 }
0159 
0160 void ThumbBarDock::reInitialize()
0161 {
0162     // Measure orientation of the widget and adjust the child thumbbar to this
0163     // orientation and size.
0164 
0165     QMainWindow* const parent = qobject_cast<QMainWindow*>(parentWidget());
0166     Q_EMIT dockLocationChanged(parent->dockWidgetArea(this));
0167     widget()->resize(size());
0168     update();
0169 }
0170 
0171 QAction* ThumbBarDock::getToggleAction(QObject* const parent, const QString& caption) const
0172 {
0173     QAction* const action = new QAction(QIcon::fromTheme(QLatin1String("view-choose")),
0174                                         caption.isNull() ? i18n("Show Thumbbar") : caption,
0175                                         parent);
0176 
0177     action->setCheckable(true);
0178 
0179     // Connect the triggered signal, which is only emitted after a user action
0180     // and not programmatically, to the show/hide method.
0181 
0182     connect(action, SIGNAL(triggered(bool)),
0183             this, SLOT(showThumbBar(bool)));
0184 
0185     // Connect the show/hide signal to the state of the toggle action.
0186 
0187     connect(this, SIGNAL(visibilityChanged(bool)),
0188             action, SLOT(setChecked(bool)));
0189 
0190     return action;
0191 }
0192 
0193 void ThumbBarDock::restoreVisibility()
0194 {
0195     // Set the visibility to what it should be or to what it was. Reset
0196     // SHOULD_BE_ values to their WAS_ values, to implement correct behavior
0197     // on subsequent calls.
0198 
0199     if      (m_visible == SHOULD_BE_SHOWN)
0200     {
0201         m_visible = WAS_SHOWN;
0202     }
0203     else if (m_visible == SHOULD_BE_HIDDEN)
0204     {
0205         m_visible = WAS_HIDDEN;
0206     }
0207 
0208     setVisible(m_visible == WAS_SHOWN);
0209 }
0210 
0211 bool ThumbBarDock::shouldBeVisible() const
0212 {
0213     if ((m_visible == WAS_SHOWN) || (m_visible == SHOULD_BE_SHOWN))
0214     {
0215         return true;
0216     }
0217 
0218     return false;
0219 }
0220 
0221 void ThumbBarDock::setShouldBeVisible(bool status)
0222 {
0223     if (status)
0224     {
0225         m_visible = SHOULD_BE_SHOWN;
0226     }
0227     else
0228     {
0229         m_visible = SHOULD_BE_HIDDEN;
0230     }
0231 }
0232 
0233 void ThumbBarDock::showThumbBar(bool status)
0234 {
0235     if (status)
0236     {
0237         m_visible = WAS_SHOWN;
0238     }
0239     else
0240     {
0241         m_visible = WAS_HIDDEN;
0242     }
0243 
0244     setVisible(status);
0245 }
0246 
0247 QPixmap ThumbBarDock::generateFuzzyRect(const QSize& size, const QColor& color, int radius, const QColor& fillColor)
0248 {
0249     QPixmap pix(size);
0250     pix.fill(fillColor);
0251 
0252     QPainter painter(&pix);
0253     painter.setRenderHint(QPainter::Antialiasing, true);
0254 
0255     // Draw corners ----------------------------------
0256 
0257     QRadialGradient gradient;
0258     gradient.setColorAt(1, Qt::transparent);
0259     gradient.setColorAt(0, color);
0260     gradient.setRadius(radius);
0261     QPoint center;
0262 
0263     // Top Left
0264 
0265     center = QPoint(radius, radius);
0266     gradient.setCenter(center);
0267     gradient.setFocalPoint(center);
0268     painter.fillRect(0, 0, radius, radius, gradient);
0269 
0270     // Top right
0271 
0272     center = QPoint(size.width() - radius, radius);
0273     gradient.setCenter(center);
0274     gradient.setFocalPoint(center);
0275     painter.fillRect(center.x(), 0, radius, radius, gradient);
0276 
0277     // Bottom left
0278 
0279     center = QPoint(radius, size.height() - radius);
0280     gradient.setCenter(center);
0281     gradient.setFocalPoint(center);
0282     painter.fillRect(0, center.y(), radius, radius, gradient);
0283 
0284     // Bottom right
0285 
0286     center = QPoint(size.width() - radius, size.height() - radius);
0287     gradient.setCenter(center);
0288     gradient.setFocalPoint(center);
0289     painter.fillRect(center.x(), center.y(), radius, radius, gradient);
0290 
0291     // Draw borders ----------------------------------
0292 
0293     QLinearGradient linearGradient;
0294     linearGradient.setColorAt(1, Qt::transparent);
0295     linearGradient.setColorAt(0, color);
0296 
0297     // Top
0298 
0299     linearGradient.setStart(0, radius);
0300     linearGradient.setFinalStop(0, 0);
0301     painter.fillRect(radius, 0, size.width() - 2*radius, radius, linearGradient);
0302 
0303     // Bottom
0304 
0305     linearGradient.setStart(0, size.height() - radius);
0306     linearGradient.setFinalStop(0, size.height());
0307     painter.fillRect(radius, int(linearGradient.start().y()), size.width() - 2*radius, radius, linearGradient);
0308 
0309     // Left
0310 
0311     linearGradient.setStart(radius, 0);
0312     linearGradient.setFinalStop(0, 0);
0313     painter.fillRect(0, radius, radius, size.height() - 2*radius, linearGradient);
0314 
0315     // Right
0316 
0317     linearGradient.setStart(size.width() - radius, 0);
0318     linearGradient.setFinalStop(size.width(), 0);
0319     painter.fillRect(int(linearGradient.start().x()), radius, radius, size.height() - 2*radius, linearGradient);
0320 
0321     return pix;
0322 }
0323 
0324 QPixmap ThumbBarDock::generateFuzzyRectForGroup(const QSize& size, const QColor& color, int radius)
0325 {
0326     // Create two normal borders
0327 
0328     QSize groupSize = size.scaled(size.width()-10, size.height()-10, Qt::KeepAspectRatio);
0329     QPixmap border1 = generateFuzzyRect(groupSize, color, radius, Qt::white);
0330     QPixmap border2 = border1.copy();
0331 
0332     QTransform rm;
0333 
0334     // Rotate first border right by 4 degrees
0335 
0336     rm.rotate(4);
0337     border1 = border1.transformed(rm, Qt::SmoothTransformation);
0338 
0339     // Rotate second border left by 4 degrees
0340 
0341     rm.rotate(-8);
0342     border2 = border2.transformed(rm, Qt::SmoothTransformation);
0343 
0344     // Combine both borders
0345 
0346     int width  = qMax(border1.size().width(), border2.size().width());
0347     int height = qMax(border1.size().height(), border2.size().height());
0348 
0349     QPixmap result(QSize(width, height));
0350     result.fill(Qt::transparent); // force alpha channel
0351 
0352     {
0353         QPainter painter(&result);
0354         painter.setRenderHints(QPainter::Antialiasing, true);
0355         painter.drawPixmap(0, 0, border1);
0356         painter.drawPixmap(0, 0, border2);
0357     }
0358 
0359     return result;
0360 }
0361 
0362 } // namespace Digikam
0363 
0364 #include "moc_thumbbardock.cpp"