1 /****************************************************************************
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>
7 ** This file is part of KDE.
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"
30 #include <QApplication>
32 #include <QMouseEvent>
33 #include <QLinkedList>
35 #include <QPaintEvent>
37 #include <QToolButton>
38 #include <QWheelEvent>
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
53 FloatingToolBarPrivate(FloatingToolBar
*qq
)
57 // rebuild contents and reposition then widget
60 // compute the visible and hidden positions along current side
61 QPoint
getInnerPoint() const;
62 QPoint
getOuterPoint() const;
66 QWidget
*anchorWidget
;
67 FloatingToolBar::Side anchorSide
;
70 QTimer
*autoHideTimer
;
71 QPoint currentPosition
;
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
;
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()
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
130 void FloatingToolBar::setSide(Side side
)
132 d
->anchorSide
= side
;
137 void FloatingToolBar::setSticky(bool sticky
)
142 d
->autoHideTimer
->stop();
144 d
->autoHideTimer
->start(autoHideTimeout
);
147 void FloatingToolBar::showAndAnimate()
153 // start scrolling in
154 d
->animTimer
->start(20);
157 void FloatingToolBar::hideAndDestroy()
159 // set parameters for sliding out
162 d
->endPosition
= d
->getOuterPoint();
164 // start scrolling out
165 d
->animTimer
->start(20);
168 void FloatingToolBar::hide()
172 switch (d
->anchorSide
) {
174 diff
= QPoint(visiblePixelWhenAutoHidden
, 0);
177 diff
= QPoint(-visiblePixelWhenAutoHidden
, 0);
180 diff
= QPoint(0, visiblePixelWhenAutoHidden
);
183 diff
= QPoint(0, -visiblePixelWhenAutoHidden
);
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
)
205 // keep toolbar visible when mouse is on it
206 if (e
->type() == QEvent::MouseMove
&& d
->autoHideTimer
->isActive())
207 d
->autoHideTimer
->start(autoHideTimeout
);
212 void FloatingToolBar::paintEvent(QPaintEvent
*e
)
214 // paint the internal pixmap over the widget
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()) {
236 d
->endPosition
= QPoint();
239 d
->animTimer
->start(20);
244 if ((QApplication::mouseButtons() & Qt::LeftButton
) != Qt::LeftButton
)
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)
253 bool LT
= nX
< (1.0 - nY
);
255 Side side
= LT
? (LB
? Left
: Top
) : (LB
? Bottom
: Right
);
257 // check if side changed
258 if (side
== d
->anchorSide
)
261 d
->anchorSide
= side
;
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
)
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)))
284 void FloatingToolBarPrivate::buildToolBar()
286 int buttonsNumber
= buttons
.count();
287 int parentWidth
= anchorWidget
->width();
288 int parentHeight
= anchorWidget
->height();
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
;
296 myCols
= 1 + (buttonsNumber
* toolBarGridSize
) /
297 (parentHeight
- toolBarGridSize
);
298 myRows
= (int)ceil((float)buttonsNumber
/ (float)myCols
);
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;
315 if (anchorSide
== FloatingToolBar::Right
)
321 if (anchorSide
== FloatingToolBar::Bottom
)
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
);
338 maskPainter
.drawRoundRect(topLeft
? -10 : 0, 0, myWidth
+ 10, myHeight
, 2000 / (myWidth
+ 10), 2000 / myHeight
);
340 maskPainter
.drawRoundRect(0, topLeft
? -10 : 0, myWidth
, myHeight
+ 10, 2000 / myWidth
, 2000 / (myHeight
+ 10));
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);
353 case FloatingToolBar::Right
:
354 grad
= QLinearGradient(myWidth
+ 1, 1, 0, 1);
356 case FloatingToolBar::Top
:
357 grad
= QLinearGradient(1, 0, 1, myHeight
+ 1);
359 case FloatingToolBar::Bottom
:
360 grad
= QLinearGradient(1, myHeight
+ 1, 0, 1);
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
);
370 bufferPainter
.drawRoundRect(topLeft
? -10 : 0, 0, myWidth
+ 10, myHeight
, 2000 / (myWidth
+ 10), 2000 / myHeight
);
372 bufferPainter
.drawRoundRect(0, topLeft
? -10 : 0, myWidth
, myHeight
+ 10, 2000 / myWidth
, 2000 / (myHeight
+ 10));
374 bufferPainter
.setPen(pal
.color(QPalette::Active
, QPalette::Mid
));
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);
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)
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
);
400 if (++gridX
== myCols
) {
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
415 currentPosition
= getOuterPoint();
416 endPosition
= getInnerPoint();
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
)
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();
477 #include "floatingtoolbar.moc"