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.
 
 
 
 

361 lines
12 KiB

/*************************************************************************
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
*************************************************************************/
#include "breezeframeshadow.h"
#include "breeze.h"
#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 )
{
// 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->inherits( "KTextEditor::View" ) ) 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;
}
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, SideTop );
installShadow( widget, helper, SideBottom );
widget->removeEventFilter( &_addEventFilter );
}
//____________________________________________________________________________________
void FrameShadowFactory::removeShadows( QWidget* widget )
{
widget->removeEventFilter( this );
const QList<QObject* > children = widget->children();
foreach( QObject *child, children )
{
if( FrameShadow* shadow = qobject_cast<FrameShadow*>(child) )
{
shadow->hide();
shadow->setParent(0);
shadow->deleteLater();
}
}
}
//____________________________________________________________________________________
void FrameShadowFactory::updateShadowsGeometry( const QObject* object, QRect rect ) const
{
const QList<QObject *> children = object->children();
foreach( QObject *child, children )
{
if( FrameShadow* shadow = qobject_cast<FrameShadow *>(child) )
{ shadow->updateGeometry( rect ); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::raiseShadows( QObject* object ) const
{
const QList<QObject *> children = object->children();
foreach( QObject *child, children )
{
if( FrameShadow* shadow = qobject_cast<FrameShadow *>(child) )
{ shadow->raise(); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::update( QObject* object ) const
{
const QList<QObject* > children = object->children();
foreach( QObject *child, children )
{
if( FrameShadow* shadow = qobject_cast<FrameShadow *>(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( FrameShadow* shadow = qobject_cast<FrameShadow *>(child) )
{ shadow->updateState( focus, hover, opacity, mode ); }
}
}
//____________________________________________________________________________________
void FrameShadowFactory::installShadow( QWidget* widget, Helper& helper, Side area ) const
{
FrameShadow *shadow(0);
shadow = new FrameShadow( area, helper );
shadow->setParent(widget);
shadow->hide();
}
//____________________________________________________________________________________
void FrameShadowFactory::widgetDestroyed( QObject* object )
{ _registeredWidgets.remove( object ); }
//____________________________________________________________________________________
FrameShadow::FrameShadow( Side area, Helper& helper ):
_helper( helper ),
_area( area ),
_hasFocus( false ),
_mouseOver( false ),
_opacity( -1 ),
_mode( AnimationNone )
{
setAttribute(Qt::WA_OpaquePaintEvent, false);
setFocusPolicy(Qt::NoFocus);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
setContextMenuPolicy(Qt::NoContextMenu);
// grab viewport widget
QWidget *viewport( this->viewport() );
// set cursor from viewport
if (viewport) setCursor(viewport->cursor());
}
//____________________________________________________________________________________
void FrameShadow::updateGeometry( QRect rect )
{
// show on first call
if( isHidden() ) show();
// store offsets between passed rect and parent widget rect
QRect parentRect( parentWidget()->contentsRect() );
_margins = QMargins(
rect.left() - parentRect.left(),
rect.top() - parentRect.top(),
rect.right() - parentRect.right(),
rect.bottom() - parentRect.bottom() );
// for efficiency, take out the part for which nothing is rendered
rect.adjust( 1, 1, -1, -1 );
// adjust geometry
const int shadowSize( Metrics::Frame_FrameRadius );
switch( _area )
{
case SideTop:
rect.setHeight( shadowSize );
break;
case SideBottom:
rect.setTop( rect.bottom() - shadowSize + 1 );
break;
case SideLeft:
rect.setWidth(shadowSize);
rect.adjust(0, shadowSize, 0, -shadowSize );
break;
case SideRight:
rect.setLeft(rect.right() - shadowSize + 1 );
rect.adjust(0, shadowSize, 0, -shadowSize );
break;
default:
return;
}
setGeometry(rect);
}
//____________________________________________________________________________________
void FrameShadow::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 FrameShadow::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; }
const QRect parentRect( parentWidget()->contentsRect().translated( mapFromParent( QPoint( 0, 0 ) ) ) );
const QRect rect( parentRect.adjusted( _margins.left(), _margins.top(), _margins.right(), _margins.bottom() ) );
// render
QPainter painter(this);
painter.setClipRegion( event->region() );
painter.setRenderHint( QPainter::Antialiasing );
const QColor outline( _helper.frameOutlineColor( palette(), _mouseOver, _hasFocus, _opacity, _mode ) );
painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
_helper.renderFrame( &painter, rect, QColor(), outline );
return;
}
//____________________________________________________________________________________
QWidget* FrameShadow::viewport( void ) const
{
if( !parentWidget() ) return nullptr;
else if( QAbstractScrollArea *widget = qobject_cast<QAbstractScrollArea *>(parentWidget()) ) {
return widget->viewport();
} else return nullptr;
}
}