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        : 2005-03-22
0007  * Description : a widget to manage sidebar in GUI - Multi-tab bar tab implementation.
0008  *
0009  * SPDX-FileCopyrightText: 2005-2006 by Joern Ahrens <joern dot ahrens at kdemail dot net>
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0012  * SPDX-FileCopyrightText: 2001-2003 by Joseph Wenninger <jowenn at kde dot org>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "sidebar_p.h"
0019 
0020 namespace Digikam
0021 {
0022 
0023 DMultiTabBarTab::DMultiTabBarTab(const QIcon& pic, const QString& text,
0024                                        int id, QWidget* const parent,
0025                                        Qt::Edge pos,
0026                                        DMultiTabBar::TextStyle style)
0027     : DMultiTabBarButton(pic, text, id, parent),
0028       d                 (new Private)
0029 {
0030     d->style    = style;
0031     d->position = pos;
0032     setToolTip(text);
0033     setCheckable(true);
0034     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
0035 
0036      // shrink down to icon only, but prefer to show text if it's there
0037 }
0038 
0039 DMultiTabBarTab::~DMultiTabBarTab()
0040 {
0041     delete d;
0042 }
0043 
0044 void DMultiTabBarTab::setPosition(Qt::Edge pos)
0045 {
0046     d->position = pos;
0047     updateGeometry();
0048 }
0049 
0050 void DMultiTabBarTab::setStyle(DMultiTabBar::TextStyle style)
0051 {
0052     d->style = style;
0053     updateGeometry();
0054 }
0055 
0056 QPixmap DMultiTabBarTab::iconPixmap() const
0057 {
0058     int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
0059 
0060     return icon().pixmap(iconSize);
0061 }
0062 
0063 void DMultiTabBarTab::initButtonStyleOption(QStyleOptionToolButton* opt) const
0064 {
0065     opt->initFrom(this);
0066 
0067     // Setup icon
0068 
0069     if (!icon().isNull())
0070     {
0071         opt->iconSize = iconSize();
0072         opt->icon     = icon();
0073     }
0074 
0075     // Should we draw text?
0076 
0077     if (shouldDrawText())
0078     {
0079         opt->text = text();
0080     }
0081 
0082     opt->state |= QStyle::State_AutoRaise;
0083 
0084     if (!isChecked() && !isDown())
0085     {
0086         opt->state |= QStyle::State_Raised;
0087     }
0088 
0089     if (isDown())
0090     {
0091         opt->state |= QStyle::State_Sunken;
0092     }
0093 
0094     if (isChecked())
0095     {
0096         opt->state |= QStyle::State_On;
0097     }
0098 
0099     opt->font            = font();
0100     opt->toolButtonStyle = shouldDrawText() ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly;
0101     opt->subControls     = QStyle::SC_ToolButton;
0102 }
0103 
0104 QSize DMultiTabBarTab::sizeHint() const
0105 {
0106     return computeSizeHint(shouldDrawText());
0107 }
0108 
0109 QSize DMultiTabBarTab::minimumSizeHint() const
0110 {
0111     return computeSizeHint(false);
0112 }
0113 
0114 void DMultiTabBarTab::computeMargins(int* hMargin, int* vMargin) const
0115 {
0116     // Unfortunately, QStyle does not give us enough information to figure out
0117     // where to place things, so we try to reverse-engineer it
0118 
0119     QStyleOptionToolButton opt;
0120     initButtonStyleOption(&opt);
0121 
0122     QPixmap iconPix  = iconPixmap();
0123     QSize trialSize  = iconPix.size();
0124     QSize expandSize = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, trialSize, this);
0125 
0126     *hMargin         = (expandSize.width()  - trialSize.width())/2;
0127     *vMargin         = (expandSize.height() - trialSize.height())/2;
0128 }
0129 
0130 QSize DMultiTabBarTab::computeSizeHint(bool withText) const
0131 {
0132     // Compute as horizontal first, then flip around if need be.
0133 
0134     QStyleOptionToolButton opt;
0135     initButtonStyleOption(&opt);
0136 
0137     int hMargin, vMargin;
0138     computeMargins(&hMargin, &vMargin);
0139 
0140     // Compute interior size, starting from pixmap..
0141 
0142     QPixmap iconPix = iconPixmap();
0143     QSize size      = iconPix.size();
0144 
0145     // Always include text height in computation, to avoid resizing the minor direction
0146     // when expanding text..
0147 
0148     QSize textSize  = fontMetrics().size(0, text());
0149     size.setHeight(qMax(size.height(), textSize.height()));
0150 
0151     // Pick margins for major/minor direction, depending on orientation
0152 
0153     int majorMargin = isVertical() ? vMargin : hMargin;
0154     int minorMargin = isVertical() ? hMargin : vMargin;
0155 
0156     size.setWidth (size.width()  + 2*majorMargin);
0157     size.setHeight(size.height() + 2*minorMargin);
0158 
0159     if (withText)
0160     {
0161         // Add enough room for the text, and an extra major margin.
0162 
0163         size.setWidth(size.width() + textSize.width() + majorMargin);
0164     }
0165 
0166     if (isVertical())
0167     {
0168         return QSize(size.height(), size.width());
0169     }
0170 
0171     return size;
0172 }
0173 
0174 void DMultiTabBarTab::setState(bool newState)
0175 {
0176     setChecked(newState);
0177     updateGeometry();
0178 }
0179 
0180 void DMultiTabBarTab::setIcon(const QString& icon)
0181 {
0182     setIcon(QIcon::fromTheme(icon));
0183 }
0184 
0185 void DMultiTabBarTab::setIcon(const QIcon& icon)
0186 {
0187     QPushButton::setIcon(icon);
0188 }
0189 
0190 bool DMultiTabBarTab::shouldDrawText() const
0191 {
0192     return ((d->style == DMultiTabBar::AllIconsText) || isChecked());
0193 }
0194 
0195 bool DMultiTabBarTab::isVertical() const
0196 {
0197     return ((d->position == Qt::RightEdge) || (d->position == Qt::LeftEdge));
0198 }
0199 
0200 void DMultiTabBarTab::paintEvent(QPaintEvent*)
0201 {
0202     QPainter painter(this);
0203 
0204     QStyleOptionToolButton opt;
0205     initButtonStyleOption(&opt);
0206 
0207     // Paint bevel..
0208 
0209     if (underMouse() || isChecked())
0210     {
0211         opt.text.clear();
0212         opt.icon = QIcon();
0213         style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &painter, this);
0214     }
0215 
0216     int hMargin, vMargin;
0217     computeMargins(&hMargin, &vMargin);
0218 
0219     // We first figure out how much room we have for the text, based on
0220     // icon size and margin, try to fit in by eliding, and perhaps
0221     // give up on drawing the text entirely if we're too short on room
0222 
0223     QPixmap icon = iconPixmap();
0224     int textRoom = 0;
0225     int iconRoom = 0;
0226 
0227     QString t;
0228 
0229     if (shouldDrawText())
0230     {
0231         if (isVertical())
0232         {
0233             iconRoom = icon.height() + 2*vMargin;
0234             textRoom = height() - iconRoom - vMargin;
0235         }
0236         else
0237         {
0238             iconRoom = icon.width() + 2*hMargin;
0239             textRoom = width() - iconRoom - hMargin;
0240         }
0241 
0242         t = painter.fontMetrics().elidedText(text(), Qt::ElideRight, textRoom);
0243 
0244         // See whether anything is left. Qt will return either
0245         // ... or the ellipsis unicode character, 0x2026
0246 
0247         if ((t == QLatin1String("...")) || (t == QString(QChar(0x2026))))
0248         {
0249             t.clear();
0250         }
0251     }
0252 
0253     // Label time.... Simple case: no text, so just plop down the icon right in the center
0254     // We only do this when the button never draws the text, to avoid jumps in icon position
0255     // when resizing
0256 
0257     if (!shouldDrawText())
0258     {
0259         style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter | Qt::AlignVCenter, icon);
0260         return;
0261     }
0262 
0263     // Now where the icon/text goes depends on text direction and tab position
0264 
0265     QRect iconArea;
0266     QRect labelArea;
0267 
0268     bool bottomIcon = false;
0269     bool rtl        = layoutDirection() == Qt::RightToLeft;
0270 
0271     if (isVertical())
0272     {
0273         if ((d->position == Qt::LeftEdge) && !rtl)
0274         {
0275             bottomIcon = true;
0276         }
0277 
0278         if ((d->position == Qt::RightEdge) && rtl)
0279         {
0280            bottomIcon = true;
0281         }
0282 
0283         if (bottomIcon)
0284         {
0285             labelArea = QRect(0, vMargin, width(), textRoom);
0286             iconArea  = QRect(0, vMargin + textRoom, width(), iconRoom);
0287         }
0288         else
0289         {
0290             labelArea = QRect(0, iconRoom, width(), textRoom);
0291             iconArea  = QRect(0, 0, width(), iconRoom);
0292         }
0293     }
0294     else
0295     {
0296         // Pretty simple --- depends only on RTL/LTR
0297 
0298         if (rtl)
0299         {
0300             labelArea = QRect(hMargin, 0, textRoom, height());
0301             iconArea  = QRect(hMargin + textRoom, 0, iconRoom, height());
0302         }
0303         else
0304         {
0305             labelArea = QRect(iconRoom, 0, textRoom, height());
0306             iconArea  = QRect(0, 0, iconRoom, height());
0307         }
0308     }
0309 
0310     style()->drawItemPixmap(&painter, iconArea, Qt::AlignCenter | Qt::AlignVCenter, icon);
0311 
0312     if (t.isEmpty())
0313     {
0314         return;
0315     }
0316 
0317     QRect labelPaintArea = labelArea;
0318 
0319     if (isVertical())
0320     {
0321         // If we're vertical, we paint to a simple 0,0 origin rect,
0322         // and get the transformations to get us in the right place
0323 
0324         labelPaintArea = QRect(0, 0, labelArea.height(), labelArea.width());
0325 
0326         QTransform tr;
0327 
0328         if (bottomIcon)
0329         {
0330             tr.translate(labelArea.x(), labelPaintArea.width() + labelArea.y());
0331             tr.rotate(-90);
0332         }
0333         else
0334         {
0335             tr.translate(labelPaintArea.height() + labelArea.x(), labelArea.y());
0336             tr.rotate(90);
0337         }
0338 
0339         painter.setTransform(tr);
0340     }
0341 
0342     style()->drawItemText(&painter, labelPaintArea, Qt::AlignLeading | Qt::AlignVCenter,
0343                           palette(), true, t, QPalette::ButtonText);
0344 }
0345 
0346 } // namespace Digikam