You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

462 lines
14 KiB

//////////////////////////////////////////////////////////////////////////////
// breezeframeshadow.h
// handle sunken frames' shadows
// -------------------
//
// Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Largely inspired from skulpture widget style
// Copyright (c) 2007-2009 Christoph Feck <christoph@maxiom.de>
//
// 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 "breezeframeshadow.h"
#include "breezeframeshadow.moc"
#include <QDebug>
#include <QAbstractScrollArea>
#include <QApplication>
#include <QFrame>
#include <QMouseEvent>
#include <QPainter>
#include <QSplitter>
#include <KColorUtils>
namespace Breeze
{
//____________________________________________________________________________________
bool FrameShadowFactory::registerWidget( QWidget* widget, Helper& helper )
{
if( !widget ) return false;
if( isRegistered( widget ) ) return false;
// check whether widget is a frame, and has the proper shape
bool accepted = false;
// cast to frame and check
QFrame* frame( qobject_cast<QFrame*>( 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<QSplitter*>( 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;
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 );
return true;
}
//____________________________________________________________________________________
void FrameShadowFactory::unregisterWidget( QWidget* widget )
{
if( !isRegistered( widget ) ) return;
_registeredWidgets.remove( widget );
removeShadows( widget );
}
//____________________________________________________________________________________
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::installShadows( QWidget* widget, Helper& helper )
{
removeShadows(widget);
widget->installEventFilter(this);
widget->installEventFilter( &_addEventFilter );
installShadow( widget, helper, Top );
installShadow( widget, helper, Bottom );
widget->removeEventFilter( &_addEventFilter );
}
//____________________________________________________________________________________
void FrameShadowFactory::removeShadows( QWidget* widget )
{
widget->removeEventFilter( this );
const QList<QObject* > children = widget->children();
foreach( QObject *child, children )
{
if( FrameShadowBase* shadow = qobject_cast<FrameShadowBase*>(child) )
{
shadow->hide();
shadow->setParent(0);
shadow->deleteLater();
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::updateShadowsGeometry( QObject* object ) const
{
const QList<QObject *> children = object->children();
foreach( QObject *child, children )
{
if( FrameShadowBase* shadow = qobject_cast<FrameShadowBase *>(child) )
{ shadow->updateGeometry(); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::raiseShadows( QObject* object ) const
{
const QList<QObject *> children = object->children();
foreach( QObject *child, children )
{
if( FrameShadowBase* shadow = qobject_cast<FrameShadowBase *>(child) )
{ shadow->raise(); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::update( QObject* object ) const
{
const QList<QObject* > children = object->children();
foreach( QObject *child, children )
{
if( FrameShadowBase* shadow = qobject_cast<FrameShadowBase *>(child) )
{ shadow->update();}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::updateState( const QWidget* widget, bool focus, bool hover, qreal opacity, AnimationMode mode ) const
{
const QList<QObject *> children = widget->children();
foreach( QObject *child, children )
{
if( FrameShadowBase* shadow = qobject_cast<FrameShadowBase *>(child) )
{ shadow->updateState( focus, hover, opacity, mode ); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::installShadow( QWidget* widget, Helper& helper, ShadowArea area ) const
{
FrameShadowBase *shadow(0);
shadow = new SunkenFrameShadow( area, helper );
shadow->setParent(widget);
shadow->updateGeometry();
shadow->show();
}
//____________________________________________________________________________________
void FrameShadowFactory::widgetDestroyed( QObject* object )
{ _registeredWidgets.remove( object ); }
//____________________________________________________________________________________
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<QAbstractScrollArea *>(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<QContextMenuEvent *>(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<QMouseEvent *>(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( _hasFocus != focus ) { _hasFocus = focus; changed |= true; }
if( _mouseOver != hover ) { _mouseOver = hover; changed |= !_hasFocus; }
if( _mode != mode )
{
_mode = mode;
changed |=
(_mode == AnimationNone) ||
(_mode == AnimationFocus) ||
(_mode == AnimationHover && !_hasFocus );
}
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<QFrame *>(parentWidget()))
{ if (frame->frameStyle() != (QFrame::StyledPanel | QFrame::Sunken)) return; }
QWidget *parent = parentWidget();
if(!parent) return;
QRect rect = parent->contentsRect();
rect.translate(mapFromParent(QPoint(0, 0)));
QColor base( palette().color(QPalette::Window) );
switch( shadowArea() )
{
case Top:
{
rect.adjust( -2, -2, 2, -1 );
break;
}
case Bottom:
{
rect.adjust( -2, 1, 2, 2 );
break;
}
case Left:
{
rect.adjust( -2, -4, -1, 4 );
break;
}
case Right:
{
rect.adjust( -1, -4, 2, 4 );
break;
}
default: return;
}
// render
QPainter painter(this);
painter.setClipRegion( event->region() );
painter.setRenderHint( QPainter::Antialiasing );
const QColor outline( _helper.frameOutlineColor( palette(), _mouseOver, _hasFocus, _opacity, _mode ) );
_helper.renderFrame( &painter, rect, QColor(), outline, _hasFocus );
return;
}
}