Developer Documentation
QtSlideWindow.cc
1 /*===========================================================================*\
2 * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40  \*===========================================================================*/
41 
42 //=============================================================================
43 //
44 // CLASS QtSlideWindow
45 //
46 //=============================================================================
47 
48 
49 //== GLOBAL DEFINITIONS=========================================================
50 
51 #define BACKGROUND_RED 0xff
52 #define BACKGROUND_GREEN 0xff
53 #define BACKGROUND_BLUE 0xff
54 #define BACKGROUND_ALPHA 0xcf
55 
56 #define SLIDE_DURATION 300
57 #define WAIT_UNTIL_SLIDE_DOWN 500
58 
59 //== INCLUDES =================================================================
60 
62 
63 #include <QPainter>
64 #include <QGraphicsSceneMouseEvent>
65 #include <QGraphicsScene>
66 #include <QGraphicsView>
67 #include <QDialog>
68 #include <QVBoxLayout>
69 
70 #include "QtSlideWindow.hh"
71 #include "QtGraphicsButton.hh"
72 
73 //== IMPLEMENTATION ==========================================================
74 
75 QtSlideWindow::QtSlideWindow(QString _name, QGraphicsItem *_parent) :
76  QGraphicsProxyWidget(_parent),
77  name_(_name),
78  mainWidget_(0),
79  autohideButton_(0),
80  detachButton_(0),
81  dialog_(0),
82  down_(false),
83  animating_(false),
84  timer_(0),
85  fontHeight_(QFontMetrics(QFont()).height()){
86 
87  setCacheMode(QGraphicsItem::DeviceCoordinateCache);
88  setWindowFrameMargins(2, 15, 2, 2);
89  setZValue(2.0);
90 
91  QImage autohide(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "button-autohide.png");
92  QImage detach(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "button-detach.png");
93 
94  autohideButton_ = new QtGraphicsButton(autohide, this, 12, 12);
95  detachButton_ = new QtGraphicsButton(detach, this, 12, 12);
96 
99  autohideButton_->setPos(geometry().width() - 12, -13);
100  detachButton_->setPos(geometry().width() - 25, -13);
101 
102  connect(detachButton_, SIGNAL(pressed()), this, SLOT(detachPressed()));
103  connect(autohideButton_, SIGNAL(pressed()), this, SLOT(autohidePressed()));
104 
105  // Create animation object
106  animation_ = new QPropertyAnimation(this, "pos");
107  animation_->setDuration(SLIDE_DURATION);
108  connect(animation_, SIGNAL(finished()), this, SLOT(animationFinished()));
109 
110  // Create timer
111  timer_ = new QTimer();
112  timer_->setSingleShot(true);
113 
114  // Wait some milliseconds before starting the actual slide down animation
115  connect(timer_, SIGNAL(timeout()), this, SLOT(startSlideDownAnimation()));
116 
117  // Hide widget
118  hide();
119 
120  setAcceptHoverEvents(true);
121 
123 }
124 
125 //-----------------------------------------------------------------------------
126 
127 void QtSlideWindow::attachWidget(QWidget *_m) {
128 
129  if (!_m)
130  return;
131 
132  mainWidget_ = _m;
133  mainWidget_->setParent(0);
134 
135  if (autohideButton_->isChecked()) {
136  setWidget(mainWidget_);
137  setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
138 
139  down_ = true;
140 
141  show();
142  }
143  updateGeometry();
144 }
145 
146 //-----------------------------------------------------------------------------
147 
149 
150  setWidget(0);
151  hide();
152 
153  if (mainWidget_) {
154  mainWidget_->setParent(0);
155  mainWidget_ = 0;
156  }
157 
158  if (dialog_) {
159  disconnect (dialog_, SIGNAL(finished (int)), this, SLOT(dialogClosed ()));
160  dialog_->close();
161  delete dialog_;
162  dialog_ = 0;
163  }
164 }
165 
166 //-----------------------------------------------------------------------------
167 
168 void QtSlideWindow::paintWindowFrame(QPainter *_painter, const QStyleOptionGraphicsItem* /*_option*/, QWidget* /*_widget*/) {
169 
170  int w = geometry().width();
171  int h = geometry().height();
172 
173  _painter->setRenderHint(QPainter::Antialiasing, true);
174  _painter->setBrush(QPalette{}.window());
175  _painter->setPen(QPalette{}.windowText().color());
176  _painter->drawRoundedRect(-2, -4-fontHeight_, w + 4, h + 40, 4, 4);
177 
178  _painter->drawText(2, -2-fontHeight_, w - 4, fontHeight_, Qt::AlignCenter, name_);
179 }
180 
181 //-----------------------------------------------------------------------------
182 
184 
185  if(_e->type() == QEvent::GraphicsSceneMousePress || _e->type() == QEvent::GraphicsSceneMouseRelease) {
186  QGraphicsSceneMouseEvent *ge = dynamic_cast<QGraphicsSceneMouseEvent*> (_e);
187  if(windowFrameSectionAt(ge->pos()) != Qt::TopSection) {
188  _e->accept();
189  return false;
190  }
191  }
192 
193  return QGraphicsProxyWidget::windowFrameEvent(_e);
194 }
195 
196 //-----------------------------------------------------------------------------
197 
198 Qt::WindowFrameSection QtSlideWindow::windowFrameSectionAt(const QPointF &_pos) const {
199 
200  if (_pos.x() >= 2 && _pos.x() < geometry().width() - 2 - (13 * 2) && _pos.y() >= -15 && _pos.y() <= -10) {
201  return Qt::TopSection;
202  }
203 
204  return Qt::NoSection;
205 }
206 
207 //-----------------------------------------------------------------------------
208 
209 void QtSlideWindow::hoverEnterEvent(QGraphicsSceneHoverEvent *) {
210 
211  // Stop slide down action after timeout if the cursor
212  // re-enters the logger within the timeout interval
213  if(timer_->isActive()) {
214  timer_->stop();
215  return;
216  }
217 
218  // Don't do anything if animation is currently in progress
219  if(animating_) return;
220 
221  if (autohideButton_->isChecked() && down_) {
222  slideUp();
223  }
224  if (!mainWidget_->isVisible()) {
225  setWidget(mainWidget_);
226  setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
227  show();
228  }
229 }
230 
231 //-----------------------------------------------------------------------------
232 
233 void QtSlideWindow::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
234 
235  // Don't do anything if animation is currently in progress
236  if(animating_) return;
237 
238  if (autohideButton_->isChecked() && !down_) {
239  slideDown();
240  }
241 }
242 
243 //-----------------------------------------------------------------------------
244 
245 void QtSlideWindow::resizeEvent(QGraphicsSceneResizeEvent *_event) {
246 
247  QGraphicsProxyWidget::resizeEvent(_event);
248 }
249 
250 //-----------------------------------------------------------------------------
251 
252 void QtSlideWindow::moveEvent(QGraphicsSceneMoveEvent *_event) {
253 
254  QGraphicsProxyWidget::moveEvent(_event);
255 }
256 
257 //-----------------------------------------------------------------------------
258 
260 
261  setWidget(0);
262 
263  dialog_ = new QDialog(0, Qt::Window);
264  dialog_->setWindowTitle(name_);
265  dialog_->setLayout(new QVBoxLayout);
266  dialog_->resize(mainWidget_->size());
267  dialog_->layout()->addWidget(mainWidget_);
268 
269  if (scene() && scene()->views()[0] && scene()->views()[0]->window()) {
270  QWidget *w = scene()->views()[0]->window();
271  int x = (w->width() - mainWidget_->width()) / 2;
272  x += w->x();
273  x = qMax(0, x);
274  int y = (w->height() - mainWidget_->height()) / 2;
275  y += w->y();
276  y = qMax(0, y);
277  dialog_->move(x, y);
278  }
279 
280  hide();
281 
282  dialog_->show();
283  connect (dialog_, SIGNAL(finished (int)), this, SLOT(dialogClosed ()));
284 }
285 
286 //-----------------------------------------------------------------------------
287 
289 
290  dialog_ = 0;
291  mainWidget_->setParent(0);
292 
293  setWidget(mainWidget_);
294  setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
295 
296  show();
297 
298  updateGeometry();
299 }
300 
301 //-----------------------------------------------------------------------------
302 
304 
305  if(!autohideButton_->isChecked() && down_) {
306  slideUp();
307  }
308 }
309 
310 //-----------------------------------------------------------------------------
311 
313 
314  startP_.setX(10);
315  startP_.setY(parentWidget()->geometry().height() - geometry().height());
316 
317  endP_.setX(10);
318  endP_.setY(parentWidget()->geometry().height() - (size().height() - geometry().height()));
319 }
320 
321 //-----------------------------------------------------------------------------
322 
324 
325  animating_ = false;
326 
327  // If we reached up position,
328  // check if the mouse is still inside (could leave in between as we block the leave event
329  // when animating to avoid flickering
330  // If mouse is not inside anymore, we start the countdown for slideDown
331  if ( !down_ && !isUnderMouse() )
332  timer_->start(WAIT_UNTIL_SLIDE_DOWN);
333 }
334 
335 //-----------------------------------------------------------------------------
336 
338 
339  if(!widget()) return;
340 
342 
343  down_ = false;
344  animating_ = true;
345 
346  animation_->setStartValue(endP_);
347  animation_->setEndValue(startP_);
348 
349  animation_->start();
350 }
351 
352 //-----------------------------------------------------------------------------
353 
355 
356  if(!widget()) return;
357 
359 
360  timer_->start(WAIT_UNTIL_SLIDE_DOWN);
361 }
362 
363 //-----------------------------------------------------------------------------
364 
366 
367  down_ = true;
368  animating_ = true;
369 
370  animation_->setStartValue(startP_);
371  animation_->setEndValue(endP_);
372 
373  animation_->start();
374 }
375 
376 //-----------------------------------------------------------------------------
377 
379 
380  if (parentWidget() && widget()) {
381 
383 
384  resize(parentWidget()->geometry().width() - 20, widget()->size().height());
385 
386  if(down_) {
387  setPos(endP_);
388  } else {
389  setPos(startP_);
390  }
391 
392  if (autohideButton_) {
393  autohideButton_->setPos(geometry().width() - 12, -13);
394  }
395  if (detachButton_) {
396  detachButton_->setPos(geometry().width() - 25, -13);
397  }
398  }
399 }
400 
401 //-----------------------------------------------------------------------------
402 
403 void QtSlideWindow::saveState(QSettings &_settings) {
404 
405  _settings.setValue("AutoHide", autohideButton_->isChecked());
406  _settings.setValue("Detached", (dialog_ != 0));
407  _settings.setValue("WidgedGeometry", (mainWidget_) ? mainWidget_->saveGeometry() : QByteArray());
408  _settings.setValue("DialogGeometry", (dialog_) ? dialog_->saveGeometry() : QByteArray());
409 }
410 
411 //-----------------------------------------------------------------------------
412 
413 void QtSlideWindow::restoreState(QSettings &_settings) {
414 
415  autohideButton_->setChecked(_settings.value("AutoHide", autohideButton_->isChecked()).toBool());
416 
417  if (_settings.value("Detached", false).toBool() && !dialog_ && mainWidget_) {
418  detachPressed();
419  }
420 
421  if (mainWidget_) {
422  mainWidget_->restoreGeometry(_settings.value("WidgedGeometry").toByteArray());
423  }
424 
425  if (autohideButton_->isChecked()) {
426 
427  } else {
428  if (!mainWidget_->isVisible()) {
429  setWidget(mainWidget_);
430  setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
431  show();
432  }
433  }
434 
435  if (dialog_)
436  dialog_->restoreGeometry(_settings.value("DialogGeometry").toByteArray());
437 }
438 
439 //=============================================================================
440 
void detachPressed()
detach button pressed
void slideDown()
Slide widget down.
const int fontHeight_
height of the default font
void setChecked(bool _value)
sets button checked state
QPointF endP_
Ending position (for animation)
void saveState(QSettings &_settings)
saves the current state
bool animating_
Is widget animating in this moment?
QDialog * dialog_
detached dialog
bool down_
Track if widget is at bottom position.
void autohidePressed()
autohide button presed
QPropertyAnimation * animation_
Animation object.
void dialogClosed()
detached dialog closed
QtGraphicsButton * autohideButton_
buttons
virtual bool windowFrameEvent(QEvent *_e)
track frame events
QPointF startP_
Starting position (for animation)
virtual void resizeEvent(QGraphicsSceneResizeEvent *_event)
size & position event tracking
QString name_
name
virtual Qt::WindowFrameSection windowFrameSectionAt(const QPointF &_pos) const
categorize frame area
void detachWidget()
detach child widget
virtual void paintWindowFrame(QPainter *_painter, const QStyleOptionGraphicsItem *_option, QWidget *_widget=0)
paints decoration
void restoreState(QSettings &_settings)
restores the state
void animationFinished()
Slot is called whenever the animation is finished.
QtSlideWindow(QString _name=0, QGraphicsItem *_parent=0)
void updateGeometry()
recalculate geometry
bool isChecked() const
returns button checked state
void slideUp()
Slide widget up.
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *_event)
hove event tracking
void startSlideDownAnimation()
Start actual slide down.
void setCheckable(bool _value)
makes the button checkable
QWidget * mainWidget_
child widget
void updateParentGeometry()
Call this to correctly set start and ending positions.
void attachWidget(QWidget *_m)
attach a child widget
QTimer * timer_
Wait some milliseconds before sliding widget down.