1 /****************************************************************************
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>
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>
37 #define toolBarRBMargin 2
38 #define toolBarOpacity 0.8
39 #define visiblePixelWhenAutoHidden 6
40 #define autoHideTimeout 2000
42 class FloatingToolBarPrivate
45 FloatingToolBarPrivate(FloatingToolBar
*qq
)
49 // rebuild contents and reposition then widget
52 // compute the visible and hidden positions along current side
53 QPoint
getInnerPoint() const;
54 QPoint
getOuterPoint() const;
58 QWidget
*anchorWidget
;
59 FloatingToolBar::Side anchorSide
;
60 QWidget
*offsetPlaceHolder
;
63 QTimer
*autoHideTimer
;
64 QPoint currentPosition
;
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
;
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()
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()
117 void FloatingToolBar::setSide(Side side
)
119 d
->anchorSide
= side
;
125 void FloatingToolBar::setSticky(bool sticky
)
130 d
->autoHideTimer
->stop();
132 d
->autoHideTimer
->start(autoHideTimeout
);
135 void FloatingToolBar::showAndAnimate()
141 // force update for case when toolbar has not been built yet
144 // start scrolling in
145 d
->animTimer
->start(20);
148 void FloatingToolBar::hideAndDestroy()
150 // set parameters for sliding out
153 d
->endPosition
= d
->getOuterPoint();
155 // start scrolling out
156 d
->animTimer
->start(20);
159 void FloatingToolBar::hide()
163 switch (d
->anchorSide
) {
165 diff
= QPoint(visiblePixelWhenAutoHidden
, 0);
168 diff
= QPoint(-visiblePixelWhenAutoHidden
, 0);
171 diff
= QPoint(0, visiblePixelWhenAutoHidden
);
174 diff
= QPoint(0, -visiblePixelWhenAutoHidden
);
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
)
196 // keep toolbar visible when mouse is on it
197 if (e
->type() == QEvent::MouseMove
&& d
->autoHideTimer
->isActive())
198 d
->autoHideTimer
->start(autoHideTimeout
);
203 void FloatingToolBar::paintEvent(QPaintEvent
*e
)
205 QToolBar::paintEvent(e
);
207 // paint the internal pixmap over the widget
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()) {
231 d
->endPosition
= QPoint();
234 d
->animTimer
->start(20);
239 if ((QApplication::mouseButtons() & Qt::LeftButton
) != Qt::LeftButton
)
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)
248 bool LT
= nX
< (1.0 - nY
);
250 Side side
= LT
? (LB
? Left
: Top
) : (LB
? Bottom
: Right
);
252 // check if side changed
253 if (side
== d
->anchorSide
)
256 d
->anchorSide
= side
;
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
)
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)))
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
);
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
);
318 maskPainter
.drawRoundRect(topLeft
? -10 : 0, 0, myWidth
+ 10, myHeight
, 2000 / (myWidth
+ 10), 2000 / myHeight
);
320 maskPainter
.drawRoundRect(0, topLeft
? -10 : 0, myWidth
, myHeight
+ 10, 2000 / myWidth
, 2000 / (myHeight
+ 10));
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);
334 case FloatingToolBar::Right
:
335 grad
= QLinearGradient(myWidth
+ 1, 1, 0, 1);
337 case FloatingToolBar::Top
:
338 grad
= QLinearGradient(1, 0, 1, myHeight
+ 1);
340 case FloatingToolBar::Bottom
:
341 grad
= QLinearGradient(1, myHeight
+ 1, 0, 1);
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
);
351 bufferPainter
.drawRoundRect(topLeft
? -10 : 0, 0, myWidth
+ 10, myHeight
, 2000 / (myWidth
+ 10), 2000 / myHeight
);
353 bufferPainter
.drawRoundRect(0, topLeft
? -10 : 0, myWidth
, myHeight
+ 10, 2000 / myWidth
, 2000 / (myHeight
+ 10));
355 bufferPainter
.translate(-0.5, -0.5);
356 bufferPainter
.setPen(pal
.color(QPalette::Active
, QPalette::Mid
));
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);
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
382 currentPosition
= getOuterPoint();
383 endPosition
= getInnerPoint();
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();
439 #include "floatingtoolbar.moc"