Warning, file /network/krdc/floatingtoolbar.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Urs Wolfer <uwolfer@kde.org> 0003 Parts of this file have been take from okular: 0004 SPDX-FileCopyrightText: 2004-2005 Enrico Ros <eros.kde@email.it> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "floatingtoolbar.h" 0010 #include "krdc_debug.h" 0011 0012 #include <QApplication> 0013 #include <QBitmap> 0014 #include <QMouseEvent> 0015 #include <QPainter> 0016 #include <QTimer> 0017 #include <QStyle> 0018 0019 static const int actionIconSize = 22; 0020 static const int toolBarRBMargin = 2; 0021 static const double toolBarOpacity = 0.8; 0022 static const int visiblePixelWhenAutoHidden = 6; 0023 static const int autoHideTimeout = 500; 0024 static const int initialAutoHideTimeout = 2000; 0025 0026 /** 0027 * Denotes the various states of the animation. 0028 */ 0029 enum AnimState { 0030 Hiding, 0031 Showing, 0032 Still 0033 }; 0034 0035 class FloatingToolBarPrivate 0036 { 0037 public: 0038 FloatingToolBarPrivate(FloatingToolBar *qq) 0039 : q(qq) 0040 , anchorSide(FloatingToolBar::Left) 0041 , offsetPlaceHolder(new QWidget(qq)) 0042 , animState(Still) 0043 , toDelete(false) 0044 , visible(false) 0045 , sticky(false) 0046 , opacity(toolBarOpacity) 0047 // set queuedShow to true so we show the toolbar if we get a resize event on the anchorWidget 0048 , queuedShow(true) { 0049 } 0050 0051 // rebuild contents and reposition then widget 0052 void buildToolBar(); 0053 void reposition(); 0054 // compute the visible and hidden positions along current side 0055 QPoint getInnerPoint() const; 0056 QPoint getOuterPoint() const; 0057 0058 FloatingToolBar *q; 0059 0060 QWidget *anchorWidget; 0061 FloatingToolBar::Side anchorSide; 0062 QWidget *offsetPlaceHolder; 0063 0064 QTimer *animTimer; 0065 QTimer *autoHideTimer; 0066 QPoint currentPosition; 0067 QPoint endPosition; 0068 AnimState animState; 0069 bool toDelete; 0070 bool visible; 0071 bool sticky; 0072 qreal opacity; 0073 bool queuedShow; 0074 0075 QPixmap backgroundPixmap; 0076 }; 0077 0078 FloatingToolBar::FloatingToolBar(QWidget *parent, QWidget *anchorWidget) 0079 : QToolBar(parent), d(new FloatingToolBarPrivate(this)) 0080 { 0081 ; 0082 addWidget(d->offsetPlaceHolder); 0083 0084 setMouseTracking(true); 0085 setIconSize(QSize(actionIconSize, actionIconSize)); 0086 d->anchorWidget = anchorWidget; 0087 0088 d->animTimer = new QTimer(this); 0089 connect(d->animTimer, SIGNAL(timeout()), this, SLOT(animate())); 0090 0091 d->autoHideTimer = new QTimer(this); 0092 connect(d->autoHideTimer, SIGNAL(timeout()), this, SLOT(hide())); 0093 0094 // apply a filter to get notified when anchor changes geometry 0095 d->anchorWidget->installEventFilter(this); 0096 } 0097 0098 FloatingToolBar::~FloatingToolBar() 0099 { 0100 delete d; 0101 } 0102 0103 void FloatingToolBar::addAction(QAction *action) 0104 { 0105 QToolBar::addAction(action); 0106 0107 // rebuild toolbar shape and contents only if the toolbar is already visible, 0108 // otherwise it will be done in showAndAnimate() 0109 if (isVisible()) 0110 d->reposition(); 0111 } 0112 0113 void FloatingToolBar::setSide(Side side) 0114 { 0115 d->anchorSide = side; 0116 0117 if (isVisible()) 0118 d->reposition(); 0119 } 0120 0121 void FloatingToolBar::setSticky(bool sticky) 0122 { 0123 d->sticky = sticky; 0124 0125 if (sticky) 0126 d->autoHideTimer->stop(); 0127 } 0128 0129 void FloatingToolBar::showAndAnimate() 0130 { 0131 if (d->animState == Showing) 0132 return; 0133 0134 d->animState = Showing; 0135 0136 show(); 0137 0138 // force update for case when toolbar has not been built yet 0139 d->reposition(); 0140 0141 // start scrolling in 0142 d->animTimer->start(20); 0143 0144 // This permits to show the toolbar for a while when going full screen. 0145 if (!d->sticky) 0146 d->autoHideTimer->start(initialAutoHideTimeout); 0147 } 0148 0149 void FloatingToolBar::hideAndDestroy() 0150 { 0151 if (d->animState == Hiding) 0152 return; 0153 0154 // set parameters for sliding out 0155 d->animState = Hiding; 0156 d->toDelete = true; 0157 d->endPosition = d->getOuterPoint(); 0158 0159 // start scrolling out 0160 d->animTimer->start(20); 0161 } 0162 0163 void FloatingToolBar::hide() 0164 { 0165 if (underMouse()) 0166 return; 0167 0168 if (d->visible) { 0169 QPoint diff; 0170 switch (d->anchorSide) { 0171 case Left: 0172 diff = QPoint(visiblePixelWhenAutoHidden, 0); 0173 break; 0174 case Right: 0175 diff = QPoint(-visiblePixelWhenAutoHidden, 0); 0176 break; 0177 case Top: 0178 diff = QPoint(0, visiblePixelWhenAutoHidden); 0179 break; 0180 case Bottom: 0181 diff = QPoint(0, -visiblePixelWhenAutoHidden); 0182 break; 0183 } 0184 d->animState = Hiding; 0185 d->endPosition = d->getOuterPoint() + diff; 0186 0187 // start scrolling out 0188 d->animTimer->start(20); 0189 } 0190 } 0191 0192 bool FloatingToolBar::eventFilter(QObject *obj, QEvent *e) 0193 { 0194 if (obj == d->anchorWidget && e->type() == QEvent::Resize) { 0195 if (d->queuedShow) { // if the toolbar is not visible yet, try to show it if the anchor widget is in fullscreen already 0196 d->queuedShow = false; 0197 showAndAnimate(); 0198 return true; 0199 } 0200 0201 // if anchorWidget changed geometry reposition toolbar 0202 d->animTimer->stop(); 0203 if ((d->animState == Hiding || !d->visible) && d->toDelete) 0204 deleteLater(); 0205 else 0206 d->reposition(); 0207 } 0208 0209 return QToolBar::eventFilter(obj, e); 0210 } 0211 0212 void FloatingToolBar::paintEvent(QPaintEvent *e) 0213 { 0214 QToolBar::paintEvent(e); 0215 0216 // paint the internal pixmap over the widget 0217 QPainter p(this); 0218 p.setOpacity(d->opacity); 0219 p.drawImage(e->rect().topLeft(), d->backgroundPixmap.toImage(), e->rect()); 0220 } 0221 0222 void FloatingToolBar::mousePressEvent(QMouseEvent *e) 0223 { 0224 if (e->button() == Qt::LeftButton) 0225 setCursor(Qt::SizeAllCursor); 0226 0227 QToolBar::mousePressEvent(e); 0228 } 0229 0230 void FloatingToolBar::mouseMoveEvent(QMouseEvent *e) 0231 { 0232 // show the toolbar again when it is auto-hidden 0233 if (!d->visible) { 0234 showAndAnimate(); 0235 return; 0236 } 0237 0238 if ((QApplication::mouseButtons() & Qt::LeftButton) != Qt::LeftButton) 0239 return; 0240 0241 // compute the nearest side to attach the widget to 0242 const QPoint parentPos = mapToParent(e->pos()); 0243 const float nX = (float)parentPos.x() / (float)d->anchorWidget->width(); 0244 const float nY = (float)parentPos.y() / (float)d->anchorWidget->height(); 0245 if (nX > 0.3 && nX < 0.7 && nY > 0.3 && nY < 0.7) 0246 return; 0247 bool LT = nX < (1.0 - nY); 0248 bool LB = nX < (nY); 0249 Side side = LT ? (LB ? Left : Top) : (LB ? Bottom : Right); 0250 0251 // check if side changed 0252 if (side == d->anchorSide) 0253 return; 0254 0255 d->anchorSide = side; 0256 d->reposition(); 0257 Q_EMIT orientationChanged((int)side); 0258 0259 QToolBar::mouseMoveEvent(e); 0260 } 0261 0262 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0263 void FloatingToolBar::enterEvent(QEnterEvent *e) 0264 #else 0265 void FloatingToolBar::enterEvent(QEvent *e) 0266 #endif 0267 { 0268 // Stop the autohide timer while the mouse is inside 0269 d->autoHideTimer->stop(); 0270 0271 if (!d->visible) 0272 showAndAnimate(); 0273 QToolBar::enterEvent(e); 0274 } 0275 0276 void FloatingToolBar::leaveEvent(QEvent *e) 0277 { 0278 if (!d->sticky) 0279 d->autoHideTimer->start(autoHideTimeout); 0280 QToolBar::leaveEvent(e); 0281 } 0282 0283 void FloatingToolBar::mouseReleaseEvent(QMouseEvent *e) 0284 { 0285 if (e->button() == Qt::LeftButton) 0286 setCursor(Qt::ArrowCursor); 0287 0288 QToolBar::mouseReleaseEvent(e); 0289 } 0290 0291 void FloatingToolBar::wheelEvent(QWheelEvent *e) 0292 { 0293 e->accept(); 0294 0295 const qreal diff = e->angleDelta().y() / 100.0 / 15.0; 0296 // qCDebug(KRDC) << diff; 0297 if (((d->opacity <= 1) && (diff > 0)) || ((d->opacity >= 0) && (diff < 0))) 0298 d->opacity += diff; 0299 0300 update(); 0301 0302 QToolBar::wheelEvent(e); 0303 } 0304 0305 void FloatingToolBarPrivate::buildToolBar() 0306 { 0307 const bool prevUpdates = q->updatesEnabled(); 0308 q->setUpdatesEnabled(false); 0309 0310 // 1. init numbers we are going to use 0311 const bool topLeft = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Top; 0312 const bool vertical = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Right; 0313 0314 if (vertical) { 0315 offsetPlaceHolder->setFixedSize(1, 7); 0316 q->setOrientation(Qt::Vertical); 0317 } else { 0318 offsetPlaceHolder->setFixedSize(7, 1); 0319 q->setOrientation(Qt::Horizontal); 0320 } 0321 0322 // 2. compute widget size 0323 const int myWidth = q->sizeHint().width() - 1; 0324 const int myHeight = q->sizeHint().height() - 1; 0325 0326 // 3. resize pixmap, mask and widget 0327 QBitmap mask(myWidth + 1, myHeight + 1); 0328 backgroundPixmap = QPixmap(myWidth + 1, myHeight + 1); 0329 backgroundPixmap.fill(Qt::transparent); 0330 0331 q->resize(myWidth + 1, myHeight + 1); 0332 0333 // 4. create and set transparency mask 0334 QPainter maskPainter(&mask); 0335 mask.fill(Qt::white); 0336 maskPainter.setBrush(Qt::black); 0337 if (vertical) 0338 maskPainter.drawRoundedRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight, Qt::RelativeSize); 0339 else 0340 maskPainter.drawRoundedRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10), Qt::RelativeSize); 0341 maskPainter.end(); 0342 q->setMask(mask); 0343 0344 // 5. draw background 0345 QPainter bufferPainter(&backgroundPixmap); 0346 bufferPainter.translate(0.5, 0.5); 0347 QPalette pal = q->palette(); 0348 // 5.1. draw horizontal/vertical gradient 0349 QLinearGradient grad; 0350 switch (anchorSide) { 0351 case FloatingToolBar::Left: 0352 grad = QLinearGradient(0, 1, myWidth + 1, 1); 0353 break; 0354 case FloatingToolBar::Right: 0355 grad = QLinearGradient(myWidth + 1, 1, 0, 1); 0356 break; 0357 case FloatingToolBar::Top: 0358 grad = QLinearGradient(1, 0, 1, myHeight + 1); 0359 break; 0360 case FloatingToolBar::Bottom: 0361 grad = QLinearGradient(1, myHeight + 1, 0, 1); 0362 break; 0363 } 0364 grad.setColorAt(0, pal.color(QPalette::Active, QPalette::Button)); 0365 grad.setColorAt(1, pal.color(QPalette::Active, QPalette::Light)); 0366 bufferPainter.setBrush(QBrush(grad)); 0367 // 5.2. draw rounded border 0368 bufferPainter.setPen( pal.color(QPalette::Active, QPalette::Dark).lighter(40)); 0369 bufferPainter.setRenderHints(QPainter::Antialiasing); 0370 if (vertical) 0371 bufferPainter.drawRoundedRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight, Qt::RelativeSize); 0372 else 0373 bufferPainter.drawRoundedRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10), Qt::RelativeSize); 0374 // 5.3. draw handle 0375 bufferPainter.translate(-0.5, -0.5); 0376 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Mid)); 0377 if (vertical) { 0378 int dx = anchorSide == FloatingToolBar::Left ? 2 : 4; 0379 bufferPainter.drawLine(dx, 6, dx + myWidth - 8, 6); 0380 bufferPainter.drawLine(dx, 9, dx + myWidth - 8, 9); 0381 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light)); 0382 bufferPainter.drawLine(dx + 1, 7, dx + myWidth - 7, 7); 0383 bufferPainter.drawLine(dx + 1, 10, dx + myWidth - 7, 10); 0384 } else { 0385 int dy = anchorSide == FloatingToolBar::Top ? 2 : 4; 0386 bufferPainter.drawLine(6, dy, 6, dy + myHeight - 8); 0387 bufferPainter.drawLine(9, dy, 9, dy + myHeight - 8); 0388 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light)); 0389 bufferPainter.drawLine(7, dy + 1, 7, dy + myHeight - 7); 0390 bufferPainter.drawLine(10, dy + 1, 10, dy + myHeight - 7); 0391 } 0392 0393 q->setUpdatesEnabled(prevUpdates); 0394 } 0395 0396 void FloatingToolBarPrivate::reposition() 0397 { 0398 // note: hiding widget here will gives better gfx, but ends drag operation 0399 // rebuild widget and move it to its final place 0400 buildToolBar(); 0401 if (!visible) { 0402 currentPosition = getOuterPoint(); 0403 endPosition = getInnerPoint(); 0404 } else { 0405 currentPosition = getInnerPoint(); 0406 endPosition = getOuterPoint(); 0407 } 0408 q->move(currentPosition); 0409 } 0410 0411 QPoint FloatingToolBarPrivate::getInnerPoint() const 0412 { 0413 // returns the final position of the widget 0414 if (anchorSide == FloatingToolBar::Left) 0415 return QPoint(0, (anchorWidget->height() - q->height()) / 2); 0416 if (anchorSide == FloatingToolBar::Top) 0417 return QPoint((anchorWidget->width() - q->width()) / 2, 0); 0418 if (anchorSide == FloatingToolBar::Right) 0419 return QPoint(anchorWidget->width() - q->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2); 0420 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() - q->height() + toolBarRBMargin); 0421 } 0422 0423 QPoint FloatingToolBarPrivate::getOuterPoint() const 0424 { 0425 // returns the point from which the transition starts 0426 if (anchorSide == FloatingToolBar::Left) 0427 return QPoint(-q->width(), (anchorWidget->height() - q->height()) / 2); 0428 if (anchorSide == FloatingToolBar::Top) 0429 return QPoint((anchorWidget->width() - q->width()) / 2, -q->height()); 0430 if (anchorSide == FloatingToolBar::Right) 0431 return QPoint(anchorWidget->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2); 0432 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() + toolBarRBMargin); 0433 } 0434 0435 void FloatingToolBar::animate() 0436 { 0437 if (style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { 0438 // move currentPosition towards endPosition 0439 int dX = d->endPosition.x() - d->currentPosition.x(); 0440 int dY = d->endPosition.y() - d->currentPosition.y(); 0441 dX = dX / 6 + qMax(-1, qMin(1, dX)); 0442 dY = dY / 6 + qMax(-1, qMin(1, dY)); 0443 d->currentPosition.setX(d->currentPosition.x() + dX); 0444 d->currentPosition.setY(d->currentPosition.y() + dY); 0445 } else { 0446 d->currentPosition = d->endPosition; 0447 } 0448 0449 move(d->currentPosition); 0450 0451 // handle arrival to the end 0452 if (d->currentPosition == d->endPosition) { 0453 d->animTimer->stop(); 0454 switch (d->animState) { 0455 case Hiding: 0456 d->visible = false; 0457 d->animState = Still; 0458 if (d->toDelete) 0459 deleteLater(); 0460 break; 0461 case Showing: 0462 d->visible = true; 0463 d->animState = Still; 0464 break; 0465 default: 0466 qCDebug(KRDC) << "Illegal state"; 0467 } 0468 } 0469 } 0470