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