1 /***************************************************************************
2 * Copyright (c) 2008 Jeff Mitchell <mitchell@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
19
20 #include "PopupDropper.h"
21 #include "PopupDropper_p.h"
22 #include "PopupDropperItem.h"
23
24 #include <QtDebug>
25 #include <QGraphicsItem>
26 #include <QGraphicsScene>
27 #include <QSvgRenderer>
28 #include <QAction>
29 #include <QMenu>
30 #include <QPalette>
31 #include <QTimeLine>
32 #include <QWidget>
33
PopupDropperPrivate(PopupDropper * parent,bool sa,QWidget * widget)34 PopupDropperPrivate::PopupDropperPrivate( PopupDropper* parent, bool sa, QWidget* widget )
35 : QObject( parent )
36 , standalone( sa )
37 , widget( widget )
38 , scene( 0 )
39 , view( 0 )
40 , fade( PopupDropper::FadeInOut )
41 , fadeHideTimer()
42 , fadeShowTimer()
43 , fadeInTime( 800 )
44 , fadeOutTime( 300 )
45 , deleteTimer()
46 , deleteTimeout( 1000 )
47 , frameMax( 30 )
48 , windowColor( 0, 0, 0, 64 )
49 , windowBackgroundBrush()
50 , baseTextColor( Qt::white )
51 , hoveredTextColor( Qt::blue )
52 , hoveredBorderPen()
53 , hoveredFillBrush()
54 , file()
55 , sharedRenderer( 0 )
56 , horizontalOffset( 30 )
57 , pdiItems()
58 , overlayLevel( 1 )
59 , entered( false )
60 , submenuMap()
61 , submenu( false )
62 , allItems()
63 , quitOnDragLeave( false )
64 , onTop( true )
65 , widgetRect()
66 , queuedHide( false )
67 , q( parent )
68 {
69 if( widget )
70 widgetRect = widget->rect();
71
72 windowBackgroundBrush.setColor( windowColor );
73 hoveredBorderPen.setColor( Qt::blue );
74 hoveredBorderPen.setWidth( 2 );
75 hoveredBorderPen.setStyle( Qt::SolidLine );
76 QColor hoveredFillColor = QColor( Qt::blue );
77 hoveredFillColor.setAlpha( 32 );
78 hoveredFillBrush.setColor( hoveredFillColor );
79 hoveredFillBrush.setStyle( Qt::SolidPattern );
80 scene = new QGraphicsScene( ( sa ? 0 : parent ) );
81 view = new PopupDropperView( parent, scene, ( sa ? 0 : widget ) );
82 //qDebug() << "on create, view size = " << view->size();
83 deleteTimer.setSingleShot( true );
84 fadeHideTimer.setDirection( QTimeLine::Backward );
85 connect( &fadeHideTimer, &QTimeLine::frameChanged, this, &PopupDropperPrivate::fadeHideTimerFrameChanged );
86 connect( &fadeShowTimer, &QTimeLine::frameChanged, this, &PopupDropperPrivate::fadeShowTimerFrameChanged );
87 connect( &fadeHideTimer, &QTimeLine::finished, this, &PopupDropperPrivate::fadeHideTimerFinished );
88 connect( &fadeShowTimer, &QTimeLine::finished, this, &PopupDropperPrivate::fadeShowTimerFinished );
89 connect( &deleteTimer, &QTimer::timeout, this, &PopupDropperPrivate::deleteTimerFinished );
90 }
91
~PopupDropperPrivate()92 PopupDropperPrivate::~PopupDropperPrivate()
93 {
94 }
95
newSceneView(PopupDropper * pud)96 void PopupDropperPrivate::newSceneView( PopupDropper* pud )
97 {
98 scene->deleteLater();
99 scene = new QGraphicsScene( pud );
100 //qDebug() << "new scene width in newSceneView = " << scene->width();
101 view = new PopupDropperView( pud, scene, widget );
102 //qDebug() << "on create, view size = " << view->size();
103 }
104
setParent(QObject * parent)105 void PopupDropperPrivate::setParent( QObject* parent )
106 {
107 QObject::setParent( parent );
108 q = static_cast<PopupDropper*>( parent );
109 }
110
fadeHideTimerFrameChanged(int frame)111 void PopupDropperPrivate::fadeHideTimerFrameChanged( int frame ) //SLOT
112 {
113 if( fadeHideTimer.state() == QTimeLine::Running )
114 {
115 qreal val = ( frame * 1.0 ) / frameMax;
116 QColor color = windowColor;
117 int alpha = (int)( color.alpha() * val );
118 color.setAlpha( alpha );
119 q->setPalette( color );
120 foreach( PopupDropperItem* pdi, pdiItems )
121 pdi->setSubitemOpacity( val );
122 }
123 }
124
fadeShowTimerFrameChanged(int frame)125 void PopupDropperPrivate::fadeShowTimerFrameChanged( int frame ) //SLOT
126 {
127 if( fadeShowTimer.state() == QTimeLine::Running )
128 {
129 qreal val = ( frame * 1.0 ) / frameMax;
130 QColor color = windowColor;
131 int alpha = (int)( color.alpha() * val );
132 color.setAlpha( alpha );
133 q->setPalette( color );
134 foreach( PopupDropperItem* pdi, pdiItems )
135 pdi->setSubitemOpacity( val );
136 }
137 }
138
fadeHideTimerFinished()139 void PopupDropperPrivate::fadeHideTimerFinished() //SLOT
140 {
141 view->hide();
142 //qDebug() << "Emitting fadeHideFinished in d pointer " << this;
143 Q_EMIT q->fadeHideFinished();
144 }
145
fadeShowTimerFinished()146 void PopupDropperPrivate::fadeShowTimerFinished() //SLOT
147 {
148 q->setPalette( windowColor );
149 queuedHide = false;
150 foreach( PopupDropperItem* pdi, pdiItems )
151 pdi->setSubitemOpacity( 1.0 );
152 }
153
dragEntered()154 void PopupDropperPrivate::dragEntered()
155 {
156 //qDebug() << "PopupDropperPrivate::dragEntered";
157 q->updateAllOverlays();
158 }
159
dragLeft()160 void PopupDropperPrivate::dragLeft()
161 {
162 //qDebug() << "PopupDropperPrivate::dragLeft()";
163 //qDebug() << "PUD to be hidden or not hidden = " << q;
164 if( view->entered() && quitOnDragLeave )
165 {
166 view->setAcceptDrops( false );
167 //qDebug() << "View entered, hiding";
168 connect( q, &PopupDropper::fadeHideFinished, q, &PopupDropper::subtractOverlay );
169 q->hide();
170 }
171 q->updateAllOverlays();
172 }
173
startDeleteTimer()174 void PopupDropperPrivate::startDeleteTimer()
175 {
176 if( deleteTimeout == 0 )
177 return;
178 view->setEntered( false );
179 //qDebug() << "Starting delete timer";
180 deleteTimer.start( deleteTimeout );
181 }
182
deleteTimerFinished()183 void PopupDropperPrivate::deleteTimerFinished() //SLOT
184 {
185 //qDebug() << "Delete Timer Finished";
186 if( !view->entered() && quitOnDragLeave )
187 {
188 connect( q, &PopupDropper::fadeHideFinished, q, &PopupDropper::subtractOverlay );
189 q->hide();
190 }
191 }
192
reposItems()193 void PopupDropperPrivate::reposItems()
194 {
195 qreal partitionsize, my_min, my_max;
196 //qDebug() << "allItems.size() = " << allItems.size();
197 int counter = 0;
198 for( int i = 0; i < allItems.size(); i++ )
199 {
200 //qDebug() << "item " << i;
201 int verticalmargin = 5;
202 partitionsize = scene->height() / pdiItems.size(); //gives partition size...now center in this area
203 my_min = ( counter * partitionsize ) + verticalmargin;
204 my_max = ( ( counter + 1 ) * partitionsize ) - verticalmargin;
205 //qDebug() << "my_min = " << my_min << ", my_max = " << my_max;
206 PopupDropperItem* pItem = dynamic_cast<PopupDropperItem*>( allItems.at( i ) );
207 QGraphicsLineItem* qglItem = 0;
208 if( pItem )
209 {
210 pItem->setPopupDropper( q ); //safety
211 //qDebug() << "item " << i << " is a PDI ";
212 //If the svgElementRect is too high, resize it to fit
213 pItem->svgElementRect().setHeight( my_max - my_min - ( 2 * verticalmargin ) );
214 pItem->setPos( 0, my_min );
215 pItem->borderRectItem()->setRect( 0 - pItem->borderWidth(), 0, scene->width() + 2*pItem->borderWidth(), my_max - my_min );
216 pItem->scaleAndReposSvgItem();
217 pItem->reposTextItem();
218 pItem->reposHoverFillRects();
219 pItem->update();
220 //qDebug() << "size of view frame = " << view->size();
221 ++counter;
222 }
223 else if( ( qglItem = dynamic_cast<QGraphicsLineItem*>( allItems.at( i ) ) ) )
224 {
225 //qDebug() << "item " << i << " is a QGLI";
226 qglItem->setLine( horizontalOffset, (my_max-partitionsize), scene->width() - horizontalOffset, (my_max-partitionsize) );
227 }
228 }
229 }
230
amIOnTop(PopupDropperView * pdv)231 bool PopupDropperPrivate::amIOnTop( PopupDropperView* pdv )
232 {
233 if( onTop && pdv == view )
234 return true;
235 return false;
236 }
237
238 //////////////////////////////////////////////////////////////
239
PopupDropper(QWidget * parent,bool standalone)240 PopupDropper::PopupDropper( QWidget *parent, bool standalone )
241 : QObject( parent )
242 , d( new PopupDropperPrivate( this, standalone, parent ) )
243 {
244 if( !parent )
245 {
246 parent = d->view;
247 d->widget = parent;
248 }
249 QObject::setParent( parent );
250 initOverlay( parent );
251 setColors( d->windowColor, d->baseTextColor, d->hoveredTextColor, d->hoveredBorderPen.color(), d->hoveredFillBrush.color() );
252 d->sharedRenderer = new QSvgRenderer( this );
253 d->overlayLevel = 1;
254 //qDebug() << "Popup Dropper created!";
255 }
256
~PopupDropper()257 PopupDropper::~PopupDropper()
258 {
259 //qDebug() << "Popup Dropper destroyed!";
260 }
261
overlayLevel() const262 int PopupDropper::overlayLevel() const
263 {
264 return d->overlayLevel;
265 }
266
initOverlay(QWidget * parent,PopupDropperPrivate * priv)267 void PopupDropper::initOverlay( QWidget* parent, PopupDropperPrivate* priv )
268 {
269 PopupDropperPrivate *pdp = priv ? priv : d;
270 //qDebug() << "PUD Overlay being created, d pointer is " << d;
271 pdp->scene->setSceneRect( QRectF( parent->rect() ) );
272 //qDebug() << "Scene width = " << pdp->scene->width();
273 pdp->scene->setItemIndexMethod( QGraphicsScene::NoIndex );
274 pdp->view->setFixedSize( parent->size() );
275 pdp->view->setLineWidth( 0 );
276 pdp->view->setFrameStyle( QFrame::NoFrame );
277 pdp->view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
278 pdp->view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
279 pdp->view->setBackgroundRole( QPalette::Window );
280 pdp->view->setAutoFillBackground( true );
281 pdp->fadeHideTimer.setFrameRange( 0, pdp->frameMax );
282 pdp->fadeHideTimer.setUpdateInterval( 20 ); // 50 fps
283 pdp->fadeShowTimer.setFrameRange( 0, pdp->frameMax );
284 pdp->fadeShowTimer.setUpdateInterval( 20 ); // 50 fps
285 }
286
addOverlay()287 void PopupDropper::addOverlay()
288 {
289 d->onTop = false;
290 m_viewStack.push( d );
291 PopupDropperPrivate* old_d = d;
292 d = new PopupDropperPrivate( this, false, old_d->view );
293 d->sharedRenderer = old_d->sharedRenderer;
294 //qDebug() << "Adding overlay: ";
295 initOverlay( old_d->view );
296 setColors( d->windowColor, d->baseTextColor, d->hoveredTextColor, d->hoveredBorderPen.color(), d->hoveredFillBrush.color() );
297 d->quitOnDragLeave = true;
298 d->overlayLevel = old_d->overlayLevel + 1;
299 old_d->view->deactivateHover();
300 }
301
302 //note: Used when activating a submenu; does not set default colors, should have done that when creating submenu
addOverlay(PopupDropperPrivate * newD)303 void PopupDropper::addOverlay( PopupDropperPrivate* newD )
304 {
305 d->onTop = false;
306 //qDebug() << "right before push, m_viewStack.size() is " << m_viewStack.size();
307 m_viewStack.push( d );
308 //qDebug() << "right after push, m_viewStack.size() is " << m_viewStack.size();
309 PopupDropperPrivate* old_d = d;
310 d = newD;
311 d->onTop = true;
312 d->sharedRenderer = old_d->sharedRenderer;
313 d->quitOnDragLeave = true;
314 d->overlayLevel = old_d->overlayLevel + 1;
315 //qDebug() << "new d d->overlayLevel = " << d->overlayLevel;
316 //qDebug() << "in PopupDropper " << this;
317 }
318
319 //NOTE: just like you have to show the overlay when adding, this function does not hide the overlay; you must do that yourself
320 //with hide() (which will hide just one level)
subtractOverlay()321 bool PopupDropper::subtractOverlay()
322 {
323 //qDebug() << "subtractOverlay, with d pointer " << d;
324 disconnect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::subtractOverlay );
325 while( !isHidden() && d->fadeHideTimer.state() == QTimeLine::Running )
326 {
327 //qDebug() << "singleshotting subtractOverlay";
328 QTimer::singleShot( 0, this, &PopupDropper::subtractOverlay );
329 return false;
330 }
331 //qDebug() << "in PopupDropper " << this;
332 //qDebug() << "overlay d->overlayLevel = " << d->overlayLevel;
333 if( d->overlayLevel == 1 )
334 return false;
335 PopupDropper::Fading currFadeValue = d->fade;
336 d->fade = PopupDropper::NoFade;
337 d->onTop = false;
338 PopupDropperPrivate* old_d = d;
339 //qDebug() << "right before pop, m_viewStack.size() is " << m_viewStack.size();
340 d = m_viewStack.pop();
341 d->onTop = true;
342 if( !old_d->submenu )
343 {
344 //qDebug() << "not submenu, deleting";
345 old_d->deleteLater();
346 }
347 else
348 {
349 foreach( QGraphicsItem* item, old_d->pdiItems )
350 old_d->scene->removeItem( item );
351 //qDebug() << "not deleting, submenu";
352 old_d->fade = currFadeValue;
353 old_d->view->resetView();
354 }
355 d->startDeleteTimer();
356 return true;
357 }
358
addSubmenu(PopupDropper ** pd,const QString & text)359 PopupDropperItem* PopupDropper::addSubmenu( PopupDropper** pd, const QString &text )
360 {
361 //qDebug() << "addSubmenu, this is " << this << " and passed-in PopupDropper is " << (*pd);
362 if( !(*pd) )
363 {
364 qWarning() << "Did not pass in a valid PUD!";
365 return 0;
366 }
367 PopupDropperPrivate* newD = (*pd)->d;
368 newD->submenu = true;
369 newD->widget = d->widget;
370 newD->setParent( this );
371 foreach( PopupDropperItem* item, newD->pdiItems )
372 newD->scene->removeItem( item );
373 newD->newSceneView( this );
374 initOverlay( d->widget, newD );
375 PopupDropperItem* pdi = new PopupDropperItem();
376
377 QAction* action = new QAction( text, this );
378
379 connect( action, &QAction::hovered, this, &PopupDropper::activateSubmenu );
380 pdi->setAction( action );
381 pdi->setSubmenuTrigger( true );
382 pdi->setHoverIndicatorShowStyle( PopupDropperItem::OnHover );
383 d->submenuMap[action] = newD;
384 delete (*pd);
385 (*pd) = 0;
386 foreach( PopupDropperItem* item, newD->pdiItems )
387 item->setPopupDropper( this );
388 //qDebug() << "d->submenuMap[pda] = " << d->submenuMap[pda];
389 addItem( pdi );
390 return pdi;
391 }
392
activateSubmenu()393 void PopupDropper::activateSubmenu()
394 {
395 //qDebug() << "Sender is " << QObject::sender();
396 if( isHidden() || d->fadeHideTimer.state() == QTimeLine::Running )
397 return;
398 PopupDropperPrivate* oldd = d;
399 addOverlay( d->submenuMap[static_cast<QAction*>(QObject::sender())] );
400 //qDebug() << "d->pdiItems.size() = " << d->pdiItems.size() << " for " << d;
401 foreach( PopupDropperItem* item, d->pdiItems )
402 addItem( item, false, false );
403 oldd->view->deactivateHover();
404 show();
405 }
406
addMenu(const QMenu * menu)407 bool PopupDropper::addMenu( const QMenu *menu )
408 {
409 Q_UNUSED(menu)
410 if( !menu )
411 return false;
412
413 if( menu->actions().isEmpty() )
414 return true;
415
416 PopupDropperItem *pdi = nullptr;
417 foreach( QAction *action, menu->actions() )
418 {
419 if( !action->menu() )
420 {
421 pdi = new PopupDropperItem();
422 pdi->setAction( action );
423 addItem( pdi );
424 }
425 else
426 {
427 PopupDropper *pd = new PopupDropper( nullptr );
428 bool success = pd->addMenu( action->menu() );
429 if( success )
430 pdi = addSubmenu( &pd, action->text() );
431 }
432 pdi = nullptr;
433 }
434
435 return true;
436 }
437
standalone() const438 bool PopupDropper::standalone() const
439 {
440 return d->standalone;
441 }
442
show()443 void PopupDropper::show()
444 {
445 if( !d->sharedRenderer )
446 {
447 //qDebug() << "No shared renderer set!";
448 return;
449 }
450 if( d->widget && d->widget->rect() != d->widgetRect )
451 {
452 d->widgetRect = d->widget->rect();
453 d->scene->setSceneRect( d->widget->rect() );
454 d->view->setFixedSize( d->widget->size() );
455 update();
456 }
457 //qDebug() << "Showing PopupDropper";
458 d->fadeShowTimer.stop();
459 if( ( d->fade == PopupDropper::FadeIn || d->fade == PopupDropper::FadeInOut ) && d->fadeInTime > 0 )
460 {
461 //qDebug() << "Animating!";
462 d->fadeShowTimer.setDuration( d->fadeInTime );
463 d->fadeShowTimer.setCurrentTime( 0 );
464 d->fadeShowTimer.setCurveShape( QTimeLine::EaseOutCurve );
465 QColor color = d->windowColor;
466 color.setAlpha( 0 );
467 setPalette( color );
468 foreach( PopupDropperItem* pdi, d->pdiItems )
469 pdi->setSubitemOpacity( 0.0 );
470 d->fadeShowTimer.start();
471 //qDebug() << "Timer started";
472 }
473 d->view->show();
474 }
475
showAllOverlays()476 void PopupDropper::showAllOverlays()
477 {
478 show();
479 for( int i = m_viewStack.size() - 1; i >= 0; --i )
480 {
481 PopupDropperPrivate* pdi = m_viewStack.at( i );
482 if( pdi != d )
483 d->view->show();
484 }
485 }
486
487 //returns immediately!
hide()488 void PopupDropper::hide()
489 {
490 //qDebug() << "PopupDropper::hide entered, d pointer is = " << d;
491
492 //if hide is called and the view is already hidden, it's likely spurious
493 if( isHidden() )
494 {
495 //qDebug() << "ishidden, returning";
496 return;
497 }
498
499 //queuedHide is to make sure that fadeShowTimerFinished executes before this next hide()
500 if( d->fadeShowTimer.state() == QTimeLine::Running )
501 {
502 //qDebug() << "show timer running, queueing hide";
503 d->fadeShowTimer.stop();
504 d->queuedHide = true;
505 QTimer::singleShot( 0, d, &PopupDropperPrivate::fadeShowTimerFinished );
506 QTimer::singleShot( 0, this, &PopupDropper::hide );
507 return;
508 }
509
510 //queuedHide will be set to false from fadeShowTimerFinished...so if this came up first,
511 //then wait
512 if( d->fadeHideTimer.state() == QTimeLine::Running || d->queuedHide )
513 {
514 //qDebug() << "hide timer running or queued hide";
515 QTimer::singleShot( 0, this, &PopupDropper::hide );
516 return;
517 }
518
519 if( ( d->fade == PopupDropper::FadeOut || d->fade == PopupDropper::FadeInOut ) && d->fadeOutTime > 0 )
520 {
521 //qDebug() << "Starting fade out";
522 d->fadeHideTimer.setDuration( d->fadeOutTime );
523 d->fadeHideTimer.setCurveShape( QTimeLine::LinearCurve );
524 d->fadeHideTimer.start();
525 //qDebug() << "Timer started";
526 return;
527 }
528 else //time is zero, or no fade
529 {
530 //qDebug() << "time is zero, or no fade";
531 QTimer::singleShot( 0, d, &PopupDropperPrivate::fadeHideTimerFinished );
532 return;
533 }
534 }
535
hideAllOverlays()536 void PopupDropper::hideAllOverlays()
537 {
538 //qDebug() << "Entered hideAllOverlays";
539 connect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::slotHideAllOverlays );
540 hide();
541 //qDebug() << "Leaving hideAllOverlays";
542 }
543
slotHideAllOverlays()544 void PopupDropper::slotHideAllOverlays()
545 {
546 //qDebug() << "Entered slotHideAllOverlays()";
547 disconnect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::slotHideAllOverlays );
548 //qDebug() << "m_viewStack.size() = " << m_viewStack.size();
549 for( int i = m_viewStack.size() - 1; i >= 0; --i )
550 {
551 PopupDropperPrivate* pdp = m_viewStack.at( i );
552 //qDebug() << "checking pdp = " << (QObject*)pdp << ", d is " << (QObject*)d;
553 if( pdp != d )
554 pdp->view->hide();
555 }
556 //qDebug() << "Leaving slotHideAllOverlays";
557 }
558
update()559 void PopupDropper::update()
560 {
561 d->reposItems();
562 d->view->update();
563 }
564
updateAllOverlays()565 void PopupDropper::updateAllOverlays()
566 {
567 for( int i = m_viewStack.size() - 1; i >= 0; --i )
568 {
569 PopupDropperPrivate* pdp = m_viewStack.at( i );
570 pdp->view->update();
571 }
572 d->view->update();
573 }
574
isHidden() const575 bool PopupDropper::isHidden() const
576 {
577 return d->view->isHidden();
578 }
579
clear()580 void PopupDropper::clear()
581 {
582 while( !isHidden() && d->fadeHideTimer.state() == QTimeLine::Running )
583 {
584 QTimer::singleShot(0, this, &PopupDropper::clear );
585 return;
586 }
587 //qDebug() << "Clear happening!";
588 // disconnect( this, 0, this, &PopupDropper::clear ); Unused at this point.
589 do
590 {
591 foreach( QGraphicsItem* item, d->allItems )
592 {
593 if( dynamic_cast<PopupDropperItem*>(item) )
594 {
595 if( dynamic_cast<PopupDropperItem*>(item)->isSubmenuTrigger() )
596 {
597 //qDebug() << "Disconnecting action";
598 disconnect( dynamic_cast<PopupDropperItem*>(item)->action(), &QAction::hovered, this, &PopupDropper::activateSubmenu );
599 }
600 dynamic_cast<PopupDropperItem*>(item)->deleteLater();
601 }
602 else
603 delete item;
604 }
605 d->pdiItems.clear();
606 d->allItems.clear();
607 //qDebug() << "Size of pdiItems is now " << d->pdiItems.size();
608 //qDebug() << "Size of allItems is now " << d->allItems.size();
609 d->view->hide();
610 d->view->resetView();
611 } while ( subtractOverlay() );
612 }
613
isEmpty(bool allItems) const614 bool PopupDropper::isEmpty( bool allItems ) const
615 {
616 if( allItems )
617 return d->allItems.empty();
618 else
619 return d->pdiItems.empty();
620 }
621
quitOnDragLeave() const622 bool PopupDropper::quitOnDragLeave() const
623 {
624 return d->quitOnDragLeave;
625 }
626
setQuitOnDragLeave(bool quit)627 void PopupDropper::setQuitOnDragLeave( bool quit )
628 {
629 d->quitOnDragLeave = quit;
630 }
631
fadeInTime() const632 int PopupDropper::fadeInTime() const
633 {
634 return d->fadeInTime;
635 }
636
setFadeInTime(const int msecs)637 void PopupDropper::setFadeInTime( const int msecs )
638 {
639 d->fadeInTime = msecs;
640 }
641
fadeOutTime() const642 int PopupDropper::fadeOutTime() const
643 {
644 return d->fadeOutTime;
645 }
646
setFadeOutTime(const int msecs)647 void PopupDropper::setFadeOutTime( const int msecs )
648 {
649 d->fadeOutTime = msecs;
650 }
651
fading() const652 PopupDropper::Fading PopupDropper::fading() const
653 {
654 return d->fade;
655 }
656
setFading(PopupDropper::Fading fade)657 void PopupDropper::setFading( PopupDropper::Fading fade )
658 {
659 d->fade = fade;
660 }
661
fadeHideTimer() const662 const QTimeLine* PopupDropper::fadeHideTimer() const
663 {
664 return &d->fadeHideTimer;
665 }
666
fadeShowTimer() const667 const QTimeLine* PopupDropper::fadeShowTimer() const
668 {
669 return &d->fadeShowTimer;
670 }
671
deleteTimeout() const672 int PopupDropper::deleteTimeout() const
673 {
674 return d->deleteTimeout;
675 }
676
setDeleteTimeout(int msecs)677 void PopupDropper::setDeleteTimeout( int msecs )
678 {
679 d->deleteTimeout = msecs;
680 }
681
windowColor() const682 QColor PopupDropper::windowColor() const
683 {
684 return d->windowColor;
685 }
686
setWindowColor(const QColor & window)687 void PopupDropper::setWindowColor( const QColor &window )
688 {
689 d->windowColor = window;
690 setPalette( window );
691 }
692
windowBackgroundBrush() const693 QBrush PopupDropper::windowBackgroundBrush() const
694 {
695 return d->windowBackgroundBrush;
696 }
697
setWindowBackgroundBrush(const QBrush & window)698 void PopupDropper::setWindowBackgroundBrush( const QBrush &window )
699 {
700 d->windowBackgroundBrush = window;
701 d->view->setBackgroundBrush( window );
702 }
703
baseTextColor() const704 QColor PopupDropper::baseTextColor() const
705 {
706 return d->baseTextColor;
707 }
708
setBaseTextColor(const QColor & text)709 void PopupDropper::setBaseTextColor( const QColor &text )
710 {
711 d->baseTextColor = text;
712 foreach( PopupDropperItem *item, d->pdiItems )
713 item->setBaseTextColor( text );
714 }
715
hoveredTextColor() const716 QColor PopupDropper::hoveredTextColor() const
717 {
718 return d->hoveredTextColor;
719 }
720
setHoveredTextColor(const QColor & text)721 void PopupDropper::setHoveredTextColor( const QColor &text )
722 {
723 d->hoveredTextColor = text;
724 foreach( PopupDropperItem *item, d->pdiItems )
725 item->setHoveredTextColor( text );
726 }
727
hoveredBorderPen() const728 QPen PopupDropper::hoveredBorderPen() const
729 {
730 return d->hoveredBorderPen;
731 }
732
setHoveredBorderPen(const QPen & border)733 void PopupDropper::setHoveredBorderPen( const QPen &border )
734 {
735 d->hoveredBorderPen = border;
736 foreach( PopupDropperItem *item, d->pdiItems )
737 item->setHoveredBorderPen( border );
738 }
739
hoveredFillBrush() const740 QBrush PopupDropper::hoveredFillBrush() const
741 {
742 return d->hoveredFillBrush;
743 }
744
setHoveredFillBrush(const QBrush & fill)745 void PopupDropper::setHoveredFillBrush( const QBrush &fill )
746 {
747 d->hoveredFillBrush = fill;
748 foreach( PopupDropperItem *item, d->pdiItems )
749 item->setHoveredFillBrush( fill );
750 }
751
setColors(const QColor & window,const QColor & baseText,const QColor & hoveredText,const QColor & hoveredBorder,const QColor & hoveredFill)752 void PopupDropper::setColors( const QColor &window, const QColor &baseText, const QColor &hoveredText, const QColor &hoveredBorder, const QColor &hoveredFill )
753 {
754 d->windowColor = window;
755 d->baseTextColor = baseText;
756 d->hoveredTextColor = hoveredText;
757 d->hoveredBorderPen.setColor( hoveredBorder );
758 d->hoveredFillBrush.setColor( hoveredFill );
759 setPalette( window, baseText, hoveredText, hoveredBorder, hoveredFill );
760 }
761
setPalette(const QColor & window)762 void PopupDropper::setPalette( const QColor &window )
763 {
764 QPalette p = d->view->palette();
765 p.setColor( QPalette::Window, window );
766 d->view->setPalette( p );
767 updateAllOverlays();
768 }
769
setPalette(const QColor & window,const QColor & baseText,const QColor & hoveredText,const QColor & hoveredBorder,const QColor & hoveredFill)770 void PopupDropper::setPalette( const QColor &window, const QColor &baseText, const QColor &hoveredText, const QColor &hoveredBorder, const QColor &hoveredFill )
771 {
772 QPalette p = d->view->palette();
773 p.setColor( QPalette::Window, window );
774 d->view->setPalette( p );
775 QPen pen;
776 QBrush brush;
777 foreach( PopupDropperItem *item, d->pdiItems )
778 {
779 item->setBaseTextColor( baseText );
780 item->setHoveredTextColor( hoveredText );
781 pen = item->hoveredBorderPen();
782 pen.setColor( hoveredBorder );
783 item->setHoveredBorderPen( pen );
784 brush = item->hoveredFillBrush();
785 brush.setColor( hoveredFill );
786 item->setHoveredFillBrush( brush );
787 }
788 updateAllOverlays();
789 }
790
windowTitle() const791 QString PopupDropper::windowTitle() const
792 {
793 return d->view->windowTitle();
794 }
795
setWindowTitle(const QString & title)796 void PopupDropper::setWindowTitle( const QString &title )
797 {
798 d->view->setWindowTitle( title );
799 d->view->update();
800 }
801
svgFile() const802 QString PopupDropper::svgFile() const
803 {
804 return d->file;
805 }
806
setSvgFile(const QString & file)807 void PopupDropper::setSvgFile( const QString &file )
808 {
809 if( d->sharedRenderer )
810 {
811 if( !d->sharedRenderer->load( file ) )
812 qWarning() << "Could not load SVG file " << file;
813 else
814 {
815 d->file = file;
816 //qDebug() << "Loaded SVG file!";
817 }
818 }
819 else
820 qWarning() << "No shared renderer!";
821 }
822
svgRenderer()823 QSvgRenderer* PopupDropper::svgRenderer()
824 {
825 return d->sharedRenderer;
826 }
827
setSvgRenderer(QSvgRenderer * renderer)828 void PopupDropper::setSvgRenderer( QSvgRenderer *renderer )
829 {
830 d->sharedRenderer = renderer;
831 }
832
horizontalOffset() const833 int PopupDropper::horizontalOffset() const
834 {
835 return d->horizontalOffset;
836 }
837
setHorizontalOffset(int pixels)838 void PopupDropper::setHorizontalOffset( int pixels )
839 {
840 d->horizontalOffset = pixels;
841 }
842
viewSize() const843 const QSize PopupDropper::viewSize() const
844 {
845 if( d && d->view )
846 return d->view->size();
847 else
848 return QSize( 0, 0 );
849 }
850
addItem(PopupDropperItem * item,bool useSharedRenderer)851 void PopupDropper::addItem( PopupDropperItem *item, bool useSharedRenderer )
852 {
853 addItem( item, useSharedRenderer, true );
854 }
855
addItem(PopupDropperItem * item,bool useSharedRenderer,bool appendToList)856 void PopupDropper::addItem( PopupDropperItem *item, bool useSharedRenderer, bool appendToList )
857 {
858 //qDebug() << "adding item";
859 //FIXME: Make separators do something graphical instead of just ignoring them
860 PopupDropperItem *pItem = static_cast<PopupDropperItem*>( item );
861 if( pItem->isSeparator() )
862 return;
863 if( useSharedRenderer )
864 pItem->setSharedRenderer( d->sharedRenderer );
865 //qDebug() << "Checking appendToList";
866 if( appendToList )
867 {
868 d->pdiItems.append( pItem );
869 d->allItems.append( pItem );
870 //qDebug() << "pdiItems list is now size " << d->pdiItems.size() << " for " << d;
871 //qDebug() << "allItems list is now size " << d->allItems.size() << " for " << d;
872 }
873 if( !pItem->textItem() )
874 {
875 QGraphicsTextItem *textItem = new QGraphicsTextItem( pItem->text(), pItem );
876 pItem->setTextItem( textItem );
877 if( !pItem->customBaseTextColor() || !pItem->baseTextColor().isValid() )
878 {
879 pItem->setBaseTextColor( d->baseTextColor );
880 //qDebug() << "Using PD's base text color";
881 }
882 else
883 {
884 //qDebug() << "Using the item's base text color";
885 pItem->textItem()->setDefaultTextColor( pItem->baseTextColor() );
886 }
887 if( !pItem->customHoveredTextColor() )
888 pItem->setHoveredTextColor( d->hoveredTextColor );
889 }
890 if( !pItem->borderRectItem() )
891 {
892 QGraphicsRectItem *borderRectItem = new QGraphicsRectItem( pItem );
893 borderRectItem->setZValue( -5 );
894 pItem->setBorderRectItem( borderRectItem );
895 if( !pItem->customHoveredBorderPen() )
896 pItem->setHoveredBorderPen( d->hoveredBorderPen );
897 if( !pItem->customHoveredFillBrush() )
898 pItem->setHoveredFillBrush( d->hoveredFillBrush );
899 }
900 d->reposItems();
901 pItem->setPopupDropper( this );
902 d->scene->addItem( pItem );
903 }
904
items() const905 QList<PopupDropperItem*> PopupDropper::items() const
906 {
907 QList<PopupDropperItem*> list;
908 foreach( PopupDropperItem *item, d->pdiItems )
909 list.append( item );
910
911 return list;
912 }
913
914 //Won't currently work for > 1 level of submenu!
915 //TODO: Figure out a better way. (Does anything else work > 1 level?)
submenuItems(const PopupDropperItem * item) const916 QList<PopupDropperItem*> PopupDropper::submenuItems( const PopupDropperItem *item ) const
917 {
918 QList<PopupDropperItem*> list;
919 if( !item || !item->isSubmenuTrigger() || !d->submenuMap.contains( item->action() ) )
920 return list;
921
922 PopupDropperPrivate *pdp = d->submenuMap[item->action()];
923 list.reserve(pdp->pdiItems.count());
924 foreach( PopupDropperItem *pdi, pdp->pdiItems )
925 list.append( pdi );
926
927 return list;
928 }
929
930 //Goes through and calls the callback on all items, including submenuItems
931 //which can be adjusted differently by checking isSubmenuTrigger()
forEachItem(void callback (void *))932 void PopupDropper::forEachItem( void callback(void*) )
933 {
934 forEachItemPrivate( d, callback );
935 }
936
forEachItemPrivate(PopupDropperPrivate * pdp,void callback (void * item))937 void PopupDropper::forEachItemPrivate( PopupDropperPrivate *pdp, void callback(void* item) )
938 {
939 foreach( PopupDropperItem *item, pdp->pdiItems )
940 callback( item );
941 foreach( QAction *action, pdp->submenuMap.keys() )
942 forEachItemPrivate( pdp->submenuMap[action], callback );
943 }
944
addSeparator(PopupDropperItem * separator)945 void PopupDropper::addSeparator( PopupDropperItem* separator )
946 {
947
948 if( !separator )
949 {
950 //qDebug() << "Action is not a separator!";
951 return;
952 }
953
954 separator->setSeparator( true );
955
956 if( separator->separatorStyle() == PopupDropperItem::TextSeparator )
957 {
958 //qDebug() << "Separator style is text";
959 addItem( separator );
960 }
961
962 //qDebug() << "Separator style is line";
963 QPen linePen;
964 if( separator && separator->hasLineSeparatorPen() )
965 linePen = separator->lineSeparatorPen();
966 else
967 {
968 linePen.setWidth( 2 );
969 linePen.setCapStyle( Qt::RoundCap );
970 linePen.setStyle( Qt::DotLine );
971 linePen.setColor( QColor( 255, 255, 255 ) );
972 }
973
974 //qDebug() << "scene width = " << d->scene->width() << ", horizontalOffset = " << d->horizontalOffset;
975 //qDebug() << "right side = " << qreal(d->scene->width() - d->horizontalOffset);
976 QGraphicsLineItem* lineItem = new QGraphicsLineItem( 0, 0, 0, 0 );
977 d->allItems.append( lineItem );
978 lineItem->setPen( linePen );
979 d->reposItems();
980 d->scene->addItem( lineItem );
981 }
982