SVN_SILENT
[kdenetwork.git] / krdc / floatingtoolbar.cpp
blob11b05c460ef082423201efbd5d99cba95b519ca4
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007 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 <QLinkedList>
34 #include <QPainter>
35 #include <QPaintEvent>
36 #include <QTimer>
37 #include <QToolButton>
38 #include <QWheelEvent>
40 #include <math.h>
42 static const int iconSize = 22;
43 static const int buttonSize = 28;
44 static const int toolBarGridSize = 28;
45 static const int toolBarRBMargin = 2;
46 static const qreal toolBarOpacity = 0.8;
47 static const int visiblePixelWhenAutoHidden = 3;
48 static const int autoHideTimeout = 2000;
50 class FloatingToolBarPrivate
52 public:
53 FloatingToolBarPrivate(FloatingToolBar *qq)
54 : q(qq) {
57 // rebuild contents and reposition then widget
58 void buildToolBar();
59 void reposition();
60 // compute the visible and hidden positions along current side
61 QPoint getInnerPoint() const;
62 QPoint getOuterPoint() const;
64 FloatingToolBar *q;
66 QWidget *anchorWidget;
67 FloatingToolBar::Side anchorSide;
69 QTimer *animTimer;
70 QTimer *autoHideTimer;
71 QPoint currentPosition;
72 QPoint endPosition;
73 bool hiding;
74 bool toDelete;
75 bool visible;
76 bool sticky;
77 qreal opacity;
79 QPixmap backgroundPixmap;
80 QLinkedList<QToolButton*> buttons;
83 FloatingToolBar::FloatingToolBar(QWidget *parent, QWidget *anchorWidget)
84 : QWidget(parent), d(new FloatingToolBarPrivate(this))
86 setMouseTracking(true);
88 d->anchorWidget = anchorWidget;
89 d->anchorSide = Left;
90 d->hiding = false;
91 d->toDelete = false;
92 d->visible = false;
93 d->sticky = false;
94 d->opacity = toolBarOpacity;
96 d->animTimer = new QTimer(this);
97 connect(d->animTimer, SIGNAL(timeout()), this, SLOT(slotAnimate()));
99 d->autoHideTimer = new QTimer(this);
100 connect(d->autoHideTimer, SIGNAL(timeout()), this, SLOT(hide()));
101 d->autoHideTimer->start(autoHideTimeout);
103 // apply a filter to get notified when anchor changes geometry
104 d->anchorWidget->installEventFilter(this);
107 FloatingToolBar::~FloatingToolBar()
109 delete d;
112 void FloatingToolBar::addAction(QAction *action)
114 QToolButton *button = new QToolButton(this);
115 button->setDefaultAction(action);
116 button->setCheckable(true);
117 button->setAutoRaise(true);
118 button->resize(buttonSize, buttonSize);
119 button->setIconSize(QSize(iconSize, iconSize));
121 d->buttons.append(button);
123 button->setMouseTracking(true);
124 button->installEventFilter(this);
126 // rebuild toolbar shape and contents
127 d->reposition();
130 void FloatingToolBar::setSide(Side side)
132 d->anchorSide = side;
134 d->reposition();
137 void FloatingToolBar::setSticky(bool sticky)
139 d->sticky = sticky;
141 if (sticky)
142 d->autoHideTimer->stop();
143 else
144 d->autoHideTimer->start(autoHideTimeout);
147 void FloatingToolBar::showAndAnimate()
149 d->hiding = false;
151 show();
153 // start scrolling in
154 d->animTimer->start(20);
157 void FloatingToolBar::hideAndDestroy()
159 // set parameters for sliding out
160 d->hiding = true;
161 d->toDelete = true;
162 d->endPosition = d->getOuterPoint();
164 // start scrolling out
165 d->animTimer->start(20);
168 void FloatingToolBar::hide()
170 if (d->visible) {
171 QPoint diff;
172 switch (d->anchorSide) {
173 case Left:
174 diff = QPoint(visiblePixelWhenAutoHidden, 0);
175 break;
176 case Right:
177 diff = QPoint(-visiblePixelWhenAutoHidden, 0);
178 break;
179 case Top:
180 diff = QPoint(0, visiblePixelWhenAutoHidden);
181 break;
182 case Bottom:
183 diff = QPoint(0, -visiblePixelWhenAutoHidden);
184 break;
186 d->hiding = true;
187 d->endPosition = d->getOuterPoint() + diff;
190 // start scrolling out
191 d->animTimer->start(20);
194 bool FloatingToolBar::eventFilter(QObject *obj, QEvent *e)
196 // if anchorWidget changed geometry reposition toolbar
197 if (obj == d->anchorWidget && e->type() == QEvent::Resize) {
198 d->animTimer->stop();
199 if (d->hiding && d->toDelete)
200 deleteLater();
201 else
202 d->reposition();
205 // keep toolbar visible when mouse is on it
206 if (e->type() == QEvent::MouseMove && d->autoHideTimer->isActive())
207 d->autoHideTimer->start(autoHideTimeout);
209 return false;
212 void FloatingToolBar::paintEvent(QPaintEvent *e)
214 // paint the internal pixmap over the widget
215 QPainter p(this);
216 p.setOpacity(d->opacity);
217 p.drawPixmap(e->rect().topLeft(), d->backgroundPixmap, e->rect());
220 void FloatingToolBar::mousePressEvent(QMouseEvent *e)
222 if (e->button() == Qt::LeftButton)
223 setCursor(Qt::SizeAllCursor);
226 void FloatingToolBar::mouseMoveEvent(QMouseEvent *e)
228 // keep toolbar visible when mouse is on it
229 if (d->autoHideTimer->isActive())
230 d->autoHideTimer->start(autoHideTimeout);
232 // show the toolbar again when it is auto-hidden
233 if (!d->visible && !d->animTimer->isActive()) {
234 d->hiding = false;
235 show();
236 d->endPosition = QPoint();
237 d->reposition();
239 d->animTimer->start(20);
241 return;
244 if ((QApplication::mouseButtons() & Qt::LeftButton) != Qt::LeftButton)
245 return;
247 // compute the nearest side to attach the widget to
248 QPoint parentPos = mapToParent(e->pos());
249 float nX = (float)parentPos.x() / (float)d->anchorWidget->width();
250 float nY = (float)parentPos.y() / (float)d->anchorWidget->height();
251 if (nX > 0.3 && nX < 0.7 && nY > 0.3 && nY < 0.7)
252 return;
253 bool LT = nX < (1.0 - nY);
254 bool LB = nX < (nY);
255 Side side = LT ? (LB ? Left : Top) : (LB ? Bottom : Right);
257 // check if side changed
258 if (side == d->anchorSide)
259 return;
261 d->anchorSide = side;
262 d->reposition();
263 emit orientationChanged((int)side);
266 void FloatingToolBar::mouseReleaseEvent(QMouseEvent *e)
268 if (e->button() == Qt::LeftButton)
269 setCursor(Qt::ArrowCursor);
272 void FloatingToolBar::wheelEvent(QWheelEvent *e)
274 e->accept();
276 qreal diff = e->delta() / 100.0 / 15.0;
277 // kDebug(5010) << diff;
278 if (((d->opacity <= 1) && (diff > 0)) || ((d->opacity >= 0) && (diff < 0)))
279 d->opacity += diff;
281 update();
284 void FloatingToolBarPrivate::buildToolBar()
286 int buttonsNumber = buttons.count();
287 int parentWidth = anchorWidget->width();
288 int parentHeight = anchorWidget->height();
289 int myCols = 1;
290 int myRows = 1;
292 // 1. find out columns and rows we're going to use
293 bool topLeft = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Top;
294 bool vertical = anchorSide == FloatingToolBar::Left || anchorSide == FloatingToolBar::Right;
295 if (vertical) {
296 myCols = 1 + (buttonsNumber * toolBarGridSize) /
297 (parentHeight - toolBarGridSize);
298 myRows = (int)ceil((float)buttonsNumber / (float)myCols);
299 } else {
300 myRows = 1 + (buttonsNumber * toolBarGridSize) /
301 (parentWidth - toolBarGridSize);
302 myCols = (int)ceil((float)buttonsNumber / (float)myRows);
305 // 2. compute widget size (from rows/cols)
306 int myWidth = myCols * toolBarGridSize;
307 int myHeight = myRows * toolBarGridSize;
308 int xOffset = (toolBarGridSize - buttonSize) / 2;
309 int yOffset = (toolBarGridSize - buttonSize) / 2;
311 if (vertical) {
312 myHeight += 16;
313 myWidth += 4;
314 yOffset += 12;
315 if (anchorSide == FloatingToolBar::Right)
316 xOffset += 4;
317 } else {
318 myWidth += 16;
319 myHeight += 4;
320 xOffset += 12;
321 if (anchorSide == FloatingToolBar::Bottom)
322 yOffset += 4;
325 bool prevUpdates = q->updatesEnabled();
326 q->setUpdatesEnabled(false);
328 // 3. resize pixmap, mask and widget
329 QBitmap mask(myWidth + 1, myHeight + 1);
330 backgroundPixmap = QPixmap(myWidth + 1, myHeight + 1);
331 q->resize(myWidth + 1, myHeight + 1);
333 // 4. create and set transparency mask
334 QPainter maskPainter(&mask);
335 mask.fill(Qt::white);
336 maskPainter.setBrush(Qt::black);
337 if (vertical)
338 maskPainter.drawRoundRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight);
339 else
340 maskPainter.drawRoundRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10));
341 maskPainter.end();
342 q->setMask(mask);
344 // 5. draw background
345 QPainter bufferPainter(&backgroundPixmap);
346 QPalette pal = q->palette();
347 // 5.1. draw horizontal/vertical gradient
348 QLinearGradient grad;
349 switch (anchorSide) {
350 case FloatingToolBar::Left:
351 grad = QLinearGradient(0, 1, myWidth + 1, 1);
352 break;
353 case FloatingToolBar::Right:
354 grad = QLinearGradient(myWidth + 1, 1, 0, 1);
355 break;
356 case FloatingToolBar::Top:
357 grad = QLinearGradient(1, 0, 1, myHeight + 1);
358 break;
359 case FloatingToolBar::Bottom:
360 grad = QLinearGradient(1, myHeight + 1, 0, 1);
361 break;
363 grad.setColorAt(0, pal.color(QPalette::Active, QPalette::Button));
364 grad.setColorAt(1, pal.color(QPalette::Active, QPalette::Light));
365 bufferPainter.fillRect(0, 0, myWidth + 1, myHeight + 1, grad);
366 // 5.2. draw rounded border
367 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Dark));
368 bufferPainter.setRenderHints(QPainter::Antialiasing);
369 if (vertical)
370 bufferPainter.drawRoundRect(topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight);
371 else
372 bufferPainter.drawRoundRect(0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10));
373 // 5.3. draw handle
374 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Mid));
375 if (vertical) {
376 int dx = anchorSide == FloatingToolBar::Left ? 2 : 4;
377 bufferPainter.drawLine(dx, 6, dx + myWidth - 8, 6);
378 bufferPainter.drawLine(dx, 9, dx + myWidth - 8, 9);
379 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light));
380 bufferPainter.drawLine(dx + 1, 7, dx + myWidth - 7, 7);
381 bufferPainter.drawLine(dx + 1, 10, dx + myWidth - 7, 10);
382 } else {
383 int dy = anchorSide == FloatingToolBar::Top ? 2 : 4;
384 bufferPainter.drawLine(6, dy, 6, dy + myHeight - 8);
385 bufferPainter.drawLine(9, dy, 9, dy + myHeight - 8);
386 bufferPainter.setPen(pal.color(QPalette::Active, QPalette::Light));
387 bufferPainter.drawLine(7, dy + 1, 7, dy + myHeight - 7);
388 bufferPainter.drawLine(10, dy + 1, 10, dy + myHeight - 7);
391 // 6. reposition buttons (in rows/col grid)
392 int gridX = 0;
393 int gridY = 0;
394 QLinkedList<QToolButton*>::const_iterator it = buttons.begin(), end = buttons.end();
395 for (; it != end; ++it) {
396 QToolButton *button = *it;
397 button->move(gridX * toolBarGridSize + xOffset,
398 gridY * toolBarGridSize + yOffset);
399 button->show();
400 if (++gridX == myCols) {
401 gridX = 0;
402 gridY++;
406 q->setUpdatesEnabled(prevUpdates);
409 void FloatingToolBarPrivate::reposition()
411 // note: hiding widget here will gives better gfx, but ends drag operation
412 // rebuild widget and move it to its final place
413 buildToolBar();
414 if (!visible) {
415 currentPosition = getOuterPoint();
416 endPosition = getInnerPoint();
417 } else {
418 currentPosition = getInnerPoint();
419 endPosition = getOuterPoint();
421 q->move(currentPosition);
423 // repaint all buttons (to update background)
424 QLinkedList<QToolButton*>::const_iterator it = buttons.begin(), end = buttons.end();
425 for (; it != end; ++it)
426 (*it)->update();
429 QPoint FloatingToolBarPrivate::getInnerPoint() const
431 // returns the final position of the widget
432 if (anchorSide == FloatingToolBar::Left)
433 return QPoint(0, (anchorWidget->height() - q->height()) / 2);
434 if (anchorSide == FloatingToolBar::Top)
435 return QPoint((anchorWidget->width() - q->width()) / 2, 0);
436 if (anchorSide == FloatingToolBar::Right)
437 return QPoint(anchorWidget->width() - q->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2);
438 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() - q->height() + toolBarRBMargin);
441 QPoint FloatingToolBarPrivate::getOuterPoint() const
443 // returns the point from which the transition starts
444 if (anchorSide == FloatingToolBar::Left)
445 return QPoint(-q->width(), (anchorWidget->height() - q->height()) / 2);
446 if (anchorSide == FloatingToolBar::Top)
447 return QPoint((anchorWidget->width() - q->width()) / 2, -q->height());
448 if (anchorSide == FloatingToolBar::Right)
449 return QPoint(anchorWidget->width() + toolBarRBMargin, (anchorWidget->height() - q->height()) / 2);
450 return QPoint((anchorWidget->width() - q->width()) / 2, anchorWidget->height() + toolBarRBMargin);
453 void FloatingToolBar::slotAnimate()
455 // move currentPosition towards endPosition
456 int dX = d->endPosition.x() - d->currentPosition.x();
457 int dY = d->endPosition.y() - d->currentPosition.y();
458 dX = dX / 6 + qMax(-1, qMin(1, dX));
459 dY = dY / 6 + qMax(-1, qMin(1, dY));
460 d->currentPosition.setX(d->currentPosition.x() + dX);
461 d->currentPosition.setY(d->currentPosition.y() + dY);
463 move(d->currentPosition);
465 // handle arrival to the end
466 if (d->currentPosition == d->endPosition) {
467 d->animTimer->stop();
468 if (d->hiding) {
469 d->visible = false;
470 if (d->toDelete)
471 deleteLater();
472 } else
473 d->visible = true;
477 #include "floatingtoolbar.moc"