////////////////////////////////////////////////////////////////////////////// // oxygenframeshadow.h // handle sunken frames' shadows // ------------------- // // Copyright (c) 2010 Hugo Pereira Da Costa // // Largely inspired from skulpture widget style // Copyright (c) 2007-2009 Christoph Feck // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////// #include "oxygenframeshadow.h" #include "oxygenframeshadow.moc.cpp" #include #include #include #include #include #include #include #include namespace Oxygen { //____________________________________________________________________________________ bool FrameShadowFactory::registerWidget( QWidget* widget, StyleHelper& helper ) { if( !widget ) return false; if( isRegistered( widget ) ) return false; // check whether widget is a frame, and has the proper shape bool accepted = false; bool flat = false; // cast to frame and check QFrame* frame( qobject_cast( widget ) ); if( !frame ) return false; // also do not install on QSplitter /* due to Qt, splitters are set with a frame style that matches the condition below, though no shadow should be installed, obviously */ if( qobject_cast( widget ) ) return false; // further checks on frame shape, and parent if( frame->frameStyle() == (QFrame::StyledPanel | QFrame::Sunken) ) accepted = true; else if( widget->parent() && widget->parent()->inherits( "QComboBoxPrivateContainer" ) ) { accepted = true; flat = true; } if( !accepted ) return false; // make sure that the widget is not embedded into a KHTMLView QWidget* parent( widget->parentWidget() ); while( parent && !parent->isTopLevel() ) { if( parent->inherits( "KHTMLView" ) ) return false; parent = parent->parentWidget(); } // store in set _registeredWidgets.insert( widget ); // catch object destruction connect( widget, SIGNAL( destroyed( QObject* ) ), SLOT( widgetDestroyed( QObject* ) ) ); // install shadow installShadows( widget, helper, flat ); return true; } //____________________________________________________________________________________ void FrameShadowFactory::unregisterWidget( QWidget* widget ) { if( !isRegistered( widget ) ) return; _registeredWidgets.remove( widget ); removeShadows( widget ); } //____________________________________________________________________________________ void FrameShadowFactory::widgetDestroyed( QObject* o ) { _registeredWidgets.remove( o ); } //____________________________________________________________________________________ void FrameShadowFactory::installShadows( QWidget* widget, StyleHelper& helper, bool flat ) { removeShadows(widget); widget->installEventFilter(this); if( !flat ) { installShadow( widget, helper, Left ); installShadow( widget, helper, Right ); } installShadow( widget, helper, Top, flat ); installShadow( widget, helper, Bottom, flat ); } //____________________________________________________________________________________ void FrameShadowFactory::removeShadows( QWidget* widget ) { widget->removeEventFilter( this ); const QList children = widget->children(); foreach( QObject *child, children ) { if( FrameShadowBase* shadow = qobject_cast(child) ) { shadow->hide(); shadow->setParent(0); shadow->deleteLater(); } } } //____________________________________________________________________________________ bool FrameShadowFactory::eventFilter( QObject* object, QEvent* event ) { switch( event->type() ) { // TODO: possibly implement ZOrderChange event, to make sure that // the shadow is always painted on top case QEvent::ZOrderChange: { raiseShadows( object ); break; } case QEvent::Show: updateShadowsGeometry( object ); update( object ); break; case QEvent::Resize: updateShadowsGeometry( object ); break; default: break; } return QObject::eventFilter( object, event ); } //____________________________________________________________________________________ void FrameShadowFactory::updateShadowsGeometry( QObject* object ) const { const QList children = object->children(); foreach( QObject *child, children ) { if( FrameShadowBase* shadow = qobject_cast(child) ) { shadow->updateGeometry(); } } } //____________________________________________________________________________________ void FrameShadowFactory::raiseShadows( QObject* object ) const { const QList children = object->children(); foreach( QObject *child, children ) { if( FrameShadowBase* shadow = qobject_cast(child) ) { shadow->raise(); } } } //____________________________________________________________________________________ void FrameShadowFactory::update( QObject* object ) const { const QList children = object->children(); foreach( QObject *child, children ) { if( FrameShadowBase* shadow = qobject_cast(child) ) { shadow->update();} } } //____________________________________________________________________________________ void FrameShadowFactory::updateState( const QWidget* widget, bool focus, bool hover, qreal opacity, AnimationMode mode ) const { const QList children = widget->children(); foreach( QObject *child, children ) { if( FrameShadowBase* shadow = qobject_cast(child) ) { shadow->updateState( focus, hover, opacity, mode ); } } } //____________________________________________________________________________________ void FrameShadowFactory::installShadow( QWidget* widget, StyleHelper& helper, ShadowArea area, bool flat ) const { FrameShadowBase *shadow(0); if( flat ) shadow = new FlatFrameShadow( area, helper ); else shadow = new SunkenFrameShadow( area, helper ); shadow->setParent(widget); shadow->updateGeometry(); shadow->show(); } //____________________________________________________________________________________ void FrameShadowBase::init() { setAttribute(Qt::WA_OpaquePaintEvent, false); setFocusPolicy(Qt::NoFocus); setAttribute(Qt::WA_TransparentForMouseEvents, true); setContextMenuPolicy(Qt::NoContextMenu); // grab viewport widget QWidget *viewport( FrameShadowBase::viewport() ); if( !viewport && parentWidget() && parentWidget()->inherits( "Q3ListView" ) ) { viewport = parentWidget(); } // set cursor from viewport if (viewport) setCursor(viewport->cursor()); } //____________________________________________________________________________________ QWidget* FrameShadowBase::viewport( void ) const { if( !parentWidget() ) return NULL; if( QAbstractScrollArea *widget = qobject_cast(parentWidget()) ) { return widget->viewport(); } else return NULL; } //____________________________________________________________________________________ bool FrameShadowBase::event(QEvent *e) { // paintEvents are handled separately if (e->type() == QEvent::Paint) return QWidget::event(e); QWidget *viewport( FrameShadowBase::viewport() ); switch (e->type()) { case QEvent::DragEnter: case QEvent::DragMove: case QEvent::DragLeave: case QEvent::Drop: if( viewport ) { setAcceptDrops(viewport->acceptDrops()); return viewport->QObject::event(e); } break; case QEvent::Enter: if( viewport ) { setCursor(viewport->cursor()); setAcceptDrops(viewport->acceptDrops()); } break; case QEvent::ContextMenu: if( viewport ) { QContextMenuEvent *me = static_cast(e); QContextMenuEvent *ne = new QContextMenuEvent(me->reason(), parentWidget()->mapFromGlobal(me->globalPos()), me->globalPos()); QApplication::sendEvent(viewport, ne); e->accept(); return true; } break; case QEvent::MouseButtonPress: releaseMouse(); case QEvent::MouseMove: case QEvent::MouseButtonRelease: if( viewport ) { QMouseEvent *me = static_cast(e); QMouseEvent *ne = new QMouseEvent(e->type(), parentWidget()->mapFromGlobal(me->globalPos()), me->globalPos(), me->button(), me->buttons(), me->modifiers()); QApplication::sendEvent(viewport, ne); e->accept(); return true; } break; default: break; } e->ignore(); return false; } //____________________________________________________________________________________ void SunkenFrameShadow::updateGeometry() { QWidget *widget = parentWidget(); if( !widget ) return; QRect cr = widget->contentsRect(); switch (shadowArea()) { case Top: cr.setHeight( SHADOW_SIZE_TOP ); cr.adjust( -1, -1, 1, 0 ); break; case Left: cr.setWidth(SHADOW_SIZE_LEFT); cr.adjust(-1, SHADOW_SIZE_TOP, 0, -SHADOW_SIZE_BOTTOM); break; case Bottom: cr.setTop(cr.bottom() - SHADOW_SIZE_BOTTOM + 1); cr.adjust( -1, 0, 1, 1 ); break; case Right: cr.setLeft(cr.right() - SHADOW_SIZE_RIGHT + 1); cr.adjust(0, SHADOW_SIZE_TOP, 1, -SHADOW_SIZE_BOTTOM); break; case Unknown: default: return; } setGeometry(cr); } //____________________________________________________________________________________ void SunkenFrameShadow::updateState( bool focus, bool hover, qreal opacity, AnimationMode mode ) { bool changed( false ); if( _focus != focus ) { _focus = focus; changed |= true; } if( _hover != hover ) { _hover = hover; changed |= !_focus; } if( _mode != mode ) { _mode = mode; changed |= (_mode == AnimationNone) || (_mode == AnimationFocus) || (_mode == AnimationHover && !_focus ); } if( _opacity != opacity ) { _opacity = opacity; changed |= (_mode != AnimationNone ); } if( changed ) { if( QWidget* viewport = this->viewport() ) { // need to disable viewport updates to avoid some redundant painting // besides it fixes one visual glitch (from Qt) in QTableViews viewport->setUpdatesEnabled( false ); update() ; viewport->setUpdatesEnabled( true ); } else update(); } } //____________________________________________________________________________________ void SunkenFrameShadow::paintEvent(QPaintEvent *event ) { // this fixes shadows in frames that change frameStyle() after polish() if (QFrame *frame = qobject_cast(parentWidget())) { if (frame->frameStyle() != (QFrame::StyledPanel | QFrame::Sunken)) return; } QWidget *parent = parentWidget(); QRect r = parent->contentsRect(); r.translate(mapFromParent(QPoint(0, 0))); QColor base( palette().color(QPalette::Window) ); TileSet::Tiles tiles; switch( shadowArea() ) { case Top: { tiles = TileSet::Left|TileSet::Top|TileSet::Right; r.adjust( -2, -2, 2, -1 ); break; } case Bottom: { tiles = TileSet::Left|TileSet::Bottom|TileSet::Right; r.adjust( -2, 1, 2, 2 ); break; } case Left: { tiles = TileSet::Left; r.adjust( -2, -3, -1, 3 ); break; } case Right: { tiles = TileSet::Right; r.adjust( -1, -3, 2, 3 ); break; } default: return; } QPainter painter(this); painter.setClipRegion( event->region() ); _helper.renderHole( &painter, palette().color( QPalette::Window ), r, _focus, _hover, _opacity, _mode, tiles, true ); return; } //____________________________________________________________________________________ void FlatFrameShadow::updateGeometry() { QWidget *widget = parentWidget(); if( !widget ) return; QRect cr = widget->contentsRect(); switch (shadowArea()) { case Top: cr.setHeight( SHADOW_SIZE_TOP-3 ); break; case Bottom: cr.setTop(cr.bottom() - SHADOW_SIZE_BOTTOM + 4); break; case Unknown: default: return; } setGeometry(cr); } //____________________________________________________________________________________ void FlatFrameShadow::paintEvent(QPaintEvent *event ) { // this fixes shadows in frames that change frameStyle() after polish() if (QFrame *frame = qobject_cast(parentWidget())) { if (frame->frameStyle() != (QFrame::NoFrame)) return; } QWidget *parent = parentWidget(); QPixmap pm( size() ); { pm.fill( Qt::transparent ); QPainter p( &pm ); p.setClipRegion( event->region() ); p.setRenderHints( QPainter::Antialiasing ); p.translate( -geometry().topLeft() ); p.setCompositionMode(QPainter::CompositionMode_DestinationOver); p.setPen( Qt::NoPen ); _helper.renderMenuBackground( &p, geometry(), parent, parent->palette() ); // mask p.setCompositionMode(QPainter::CompositionMode_DestinationOut); p.setBrush( Qt::black ); p.drawRoundedRect( QRectF(parent->contentsRect()), 2.5, 2.5 ); } QPainter p( this ); p.setClipRegion( event->region() ); p.fillRect( rect(), Qt::transparent ); p.drawPixmap( QPoint(0,0), pm ); return; } }