Various improvements and fixes:
[kdenetwork.git] / krdc / floatingtoolbar.cpp
bloba7906f89c0d51db8a329a5abd3e60d17888d0cda
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
4 ** Parts of this file have been take from okular:
5 ** Copyright (C) 2004-2005 Enrico Ros <eros.kde@email.it>
6 **
7 ** This file is part of KDE.
8 **
9 ** This program is free software; you can redistribute it and/or modify
10 ** it under the terms of the GNU General Public License as published by
11 ** the Free Software Foundation; either version 2 of the License, or
12 ** (at your option) any later version.
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; see the file COPYING. If not, write to
21 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ** Boston, MA 02110-1301, USA.
24 ****************************************************************************/
26 #include "floatingtoolbar.h"
28 #include <KDebug>
30 #include <QApplication>
31 #include <QBitmap>
32 #include <QMouseEvent>
33 #include <QPainter>
34 #include <QTimer>
36 #define iconSize 22
37 #define toolBarRBMargin 2
38 #define toolBarOpacity 0.8
39 #define visiblePixelWhenAutoHidden 6
40 #define autoHideTimeout 2000
42 class FloatingToolBarPrivate
44 public:
45 FloatingToolBarPrivate(FloatingToolBar *qq)
46 : q(qq) {
49 // rebuild contents and reposition then widget
50 void buildToolBar();
51 void reposition();
52 // compute the visible and hidden positions along current side
53 QPoint getInnerPoint() const;
54 QPoint getOuterPoint() const;
56 FloatingToolBar *q;
58 QWidget *anchorWidget;
59 FloatingToolBar::Side anchorSide;
60 QWidget *offsetPlaceHolder;
62 QTimer *animTimer;
63 QTimer *autoHideTimer;
64 QPoint currentPosition;
65 QPoint endPosition;
66 bool hiding;
67 bool toDelete;
68 bool visible;
69 bool sticky;
70 qreal opacity;
72 QPixmap backgroundPixmap;
75 FloatingToolBar::FloatingToolBar(QWidget *parent, QWidget *anchorWidget)
76 : QToolBar(parent), d(new FloatingToolBarPrivate(this))
78 d->offsetPlaceHolder = new QWidget(this);
79 addWidget(d->offsetPlaceHolder);
81 setMouseTracking(true);
82 setIconSize(QSize(iconSize, iconSize));
83 d->anchorWidget = anchorWidget;
84 d->anchorSide = Left;
85 d->hiding = false;
86 d->toDelete = false;
87 d->visible = false;
88 d->sticky = false;
89 d->opacity = toolBarOpacity;
91 d->animTimer = new QTimer(this);
92 connect(d->animTimer, SIGNAL(timeout()), this, SLOT(animate()));
94 d->autoHideTimer = new QTimer(this);
95 connect(d->autoHideTimer, SIGNAL(timeout()), this, SLOT(hide()));
96 d->autoHideTimer->start(autoHideTimeout);
98 // apply a filter to get notified when anchor changes geometry
99 d->anchorWidget->installEventFilter(this);
102 FloatingToolBar::~FloatingToolBar()
104 delete d;
107 void FloatingToolBar::addAction(QAction *action)
109 QToolBar::addAction(action);
111 // rebuild toolbar shape and contents only if the toolbar is already visible,
112 // otherwise it will be done in showAndAnimate()
113 if (isVisible())
114 d->reposition();
117 void FloatingToolBar::setSide(Side side)
119 d->anchorSide = side;
121 if (isVisible())
122 d->reposition();
125 void FloatingToolBar::setSticky(bool sticky)
127 d->sticky = sticky;
129 if (sticky)
130 d->autoHideTimer->stop();
131 else
132 d->autoHideTimer->start(autoHideTimeout);
135 void FloatingToolBar::showAndAnimate()
137 d->hiding = false;
139 show();
141 // force update for case when toolbar has not been built yet
142 d->reposition();
144 // start scrolling in
145 d->animTimer->start(20);
148 void FloatingToolBar::hideAndDestroy()
150 // set parameters for sliding out
151 d->hiding = true;
152 d->toDelete = true;
153 d->endPosition = d->getOuterPoint();
155 // start scrolling out
156 d->animTimer->start(20);
159 void FloatingToolBar::hide()
161 if (d->visible) {
162 QPoint diff;
163 switch (d->anchorSide) {
164 case Left:
165 diff = QPoint(visiblePixelWhenAutoHidden, 0);
166 break;
167 case Right:
168 diff = QPoint(-visiblePixelWhenAutoHidden, 0);
169 break;
170 case Top:
171 diff = QPoint(0, visiblePixelWhenAutoHidden);
172 break;
173 case Bottom:
174 diff = QPoint(0, -visiblePixelWhenAutoHidden);
175 break;
177 d->hiding = true;
178 d->endPosition = d->getOuterPoint() + diff;
181 // start scrolling out
182 d->animTimer->start(20);
185 bool FloatingToolBar::eventFilter(QObject *obj, QEvent *e)
187 // if anchorWidget changed geometry reposition toolbar
188 if (obj == d->anchorWidget && e->type() == QEvent::Resize) {
189 d->animTimer->stop();
190 if (d->hiding && d->toDelete)
191 deleteLater();
192 else
193 d->reposition();
196 // keep toolbar visible when mouse is on it
197 if (e->type() == QEvent::MouseMove && d->autoHideTimer->isActive())
198 d->autoHideTimer->start(autoHideTimeout);
200 return false;
203 void FloatingToolBar::paintEvent(QPaintEvent *e)
205 QToolBar::paintEvent(e);
207 // paint the internal pixmap over the widget
208 QPainter p(this);
209 p.setOpacity(d->opacity);
210 p.drawImage(e->rect().topLeft(), d->backgroundPixmap.toImage(), e->rect());
213 void FloatingToolBar::mousePressEvent(QMouseEvent *e)
215 if (e->button() == Qt::LeftButton)
216 setCursor(Qt::SizeAllCursor);
218 QToolBar::mousePressEvent(e);
221 void FloatingToolBar::mouseMoveEvent(QMouseEvent *e)
223 // keep toolbar visible when mouse is on it
224 if (d->autoHideTimer->isActive())
225 d->autoHideTimer->start(autoHideTimeout);
227 // show the toolbar again when it is auto-hidden
228 if (!d->visible && !d->animTimer->isActive()) {
229 d->hiding = false;
230 show();
231 d->endPosition = QPoint();
232 d->reposition();
234 d->animTimer->start(20);
236 return;
239 if ((QApplication::mouseButtons() & Qt::LeftButton) != Qt::LeftButton)
240 return;
242 // compute the nearest side to attach the widget to
243 QPoint parentPos = mapToParent(e->pos());
244 float nX = (float)parentPos.x() / (float)d->anchorWidget->width();
245 float nY = (float)parentPos.y() / (float)d->anchorWidget->height();
246 if (nX > 0.3 && nX < 0.7 && nY > 0.3 && nY < 0.7)
247 return;
248 bool LT = nX < (1.0 - nY);
249 bool LB = nX < (nY);
250 Side side = LT ? (LB ? Left : Top) : (LB ? Bottom : Right);
252 // check if side changed
253 if (side == d->anchorSide)
254 return;
256 d->anchorSide = side;
257 d->reposition();
258 emit orientationChanged((int)side);
260 QToolBar::mouseMoveEvent(e);
263 void FloatingToolBar::mouseReleaseEvent(QMouseEvent *e)
265 if (e->button() == Qt::LeftButton)
266 setCursor(Qt::ArrowCursor);
268 QToolBar::mouseReleaseEvent(e);
271 void FloatingToolBar::wheelEvent(QWheelEvent *e)
273 e->accept();
275 qreal diff = e->delta() / 100.0 / 15.0;
276 // kDebug(5010) << diff;
277 if (((d->opacity <= 1) && (diff > 0)) || ((d->opacity >= 0) && (diff < 0)))
278 d->opacity += diff;
280 update();
282 QToolBar::wheelEvent(e);
285 void FloatingToolBarPrivate::buildToolBar()
287 bool prevUpdates = q->updatesEnabled();
288 q->setUpdatesEnabled(false);
290 // 1. init numbers we are going to use
291 bool topLeft = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Top;
292 bool vertical = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Right;
294 if (anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Right) {
295 offsetPlaceHolder->setFixedSize(1, 7);
296 q->setOrientation(Qt::Vertical);
297 } else {
298 offsetPlaceHolder->setFixedSize(7, 1);
299 q->setOrientation(Qt::Horizontal);
302 // 2. compute widget size
303 int myWidth = q->sizeHint().width() - 1;
304 int myHeight = q->sizeHint().height() - 1;
306 // 3. resize pixmap, mask and widget
307 QBitmap mask(myWidth + 1, myHeight + 1);
308 backgroundPixmap = QPixmap(myWidth + 1, myHeight + 1);
309 backgroundPixmap.fill(Qt::transparent);
311 q->resize(myWidth + 1, myHeight + 1);
313 // 4. create and set transparency mask
314 QPainter maskPainter(&mask);
315 mask.fill(Qt::white);
316 maskPainter.setBrush(Qt::black);
317 if (vertical)
318 maskPainter.drawRoundRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight);
319 else
320 maskPainter.drawRoundRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10));
321 maskPainter.end();
322 q->setMask(mask);
324 // 5. draw background
325 QPainter bufferPainter(&backgroundPixmap);
326 bufferPainter.translate(0.5, 0.5);
327 QPalette pal = q->palette();
328 // 5.1. draw horizontal/vertical gradient
329 QLinearGradient grad;
330 switch (anchorSide) {
331 case FloatingToolBar::Left:
332 grad = QLinearGradient(0, 1, myWidth + 1, 1);
333 break;
334 case FloatingToolBar::Right:
335 grad = QLinearGradient(myWidth + 1, 1, 0, 1);
336 break;
337 case FloatingToolBar::Top:
338 grad = QLinearGradient(1, 0, 1, myHeight + 1);
339 break;
340 case FloatingToolBar::Bottom:
341 grad = QLinearGradient(1, myHeight + 1, 0, 1);
342 break;
344 grad.setColorAt(0, pal.color(QPalette::Active, QPalette::Button));
345 grad.setColorAt(1, pal.color(QPalette::Active, QPalette::Light));
346 bufferPainter.setBrush(QBrush(grad));
347 // 5.2. draw rounded border
348 bufferPainter.setPen( pal.color(QPalette::Active, QPalette::Dark).lighter(40));
349 bufferPainter.setRenderHints(QPainter::Antialiasing);
350 if (vertical)
351 bufferPainter.drawRoundRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight);
352 else
353 bufferPainter.drawRoundRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10));
354 // 5.3. draw handle
355 bufferPainter.translate(-0.5, -0.5);
356 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Mid));
357 if (vertical) {
358 int dx = anchorSide == FloatingToolBar::Left ? 2 : 4;
359 bufferPainter.drawLine(dx, 6, dx + myWidth - 8, 6);
360 bufferPainter.drawLine(dx, 9, dx + myWidth - 8, 9);
361 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light));
362 bufferPainter.drawLine(dx + 1, 7, dx + myWidth - 7, 7);
363 bufferPainter.drawLine(dx + 1, 10, dx + myWidth - 7, 10);
364 } else {
365 int dy = anchorSide == FloatingToolBar::Top ? 2 : 4;
366 bufferPainter.drawLine(6, dy, 6, dy + myHeight - 8);
367 bufferPainter.drawLine(9, dy, 9, dy + myHeight - 8);
368 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light));
369 bufferPainter.drawLine(7, dy + 1, 7, dy + myHeight - 7);
370 bufferPainter.drawLine(10, dy + 1, 10, dy + myHeight - 7);
373 q->setUpdatesEnabled(prevUpdates);
376 void FloatingToolBarPrivate::reposition()
378 // note: hiding widget here will gives better gfx, but ends drag operation
379 // rebuild widget and move it to its final place
380 buildToolBar();
381 if (!visible) {
382 currentPosition = getOuterPoint();
383 endPosition = getInnerPoint();
384 } else {
385 currentPosition = getInnerPoint();
386 endPosition = getOuterPoint();
388 q->move(currentPosition);
391 QPoint FloatingToolBarPrivate::getInnerPoint() const
393 // returns the final position of the widget
394 if (anchorSide == FloatingToolBar::Left)
395 return QPoint(0, (anchorWidget->height() - q->height()) / 2);
396 if (anchorSide == FloatingToolBar::Top)
397 return QPoint((anchorWidget->width() - q->width()) / 2, 0);
398 if (anchorSide == FloatingToolBar::Right)
399 return QPoint(anchorWidget->width() - q->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2);
400 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() - q->height() + toolBarRBMargin);
403 QPoint FloatingToolBarPrivate::getOuterPoint() const
405 // returns the point from which the transition starts
406 if (anchorSide == FloatingToolBar::Left)
407 return QPoint(-q->width(), (anchorWidget->height() - q->height()) / 2);
408 if (anchorSide == FloatingToolBar::Top)
409 return QPoint((anchorWidget->width() - q->width()) / 2, -q->height());
410 if (anchorSide == FloatingToolBar::Right)
411 return QPoint(anchorWidget->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2);
412 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() + toolBarRBMargin);
415 void FloatingToolBar::animate()
417 // move currentPosition towards endPosition
418 int dX = d->endPosition.x() - d->currentPosition.x();
419 int dY = d->endPosition.y() - d->currentPosition.y();
420 dX = dX / 6 + qMax(-1, qMin(1, dX));
421 dY = dY / 6 + qMax(-1, qMin(1, dY));
422 d->currentPosition.setX(d->currentPosition.x() + dX);
423 d->currentPosition.setY(d->currentPosition.y() + dY);
425 move(d->currentPosition);
427 // handle arrival to the end
428 if (d->currentPosition == d->endPosition) {
429 d->animTimer->stop();
430 if (d->hiding) {
431 d->visible = false;
432 if (d->toDelete)
433 deleteLater();
434 } else
435 d->visible = true;
439 #include "floatingtoolbar.moc"