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.
2045 lines
68 KiB
2045 lines
68 KiB
////////////////////////////////////////////////////////////////////////////// |
|
// oxygenclient.cpp |
|
// ------------------- |
|
// |
|
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr> |
|
// Copyright (c) 2006, 2007 Casper Boemann <cbr@boemann.dk> |
|
// Copyright (c) 2006, 2007 Riccardo Iaconelli <riccardo@kde.org> |
|
// |
|
// 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 "oxygenclient.h" |
|
#include "oxygenclient.moc" |
|
|
|
#include "oxygenbutton.h" |
|
#include "oxygensizegrip.h" |
|
|
|
#include <cassert> |
|
#include <cmath> |
|
|
|
#include <KLocalizedString> |
|
#include <KColorUtils> |
|
#include <KStyle> |
|
|
|
#include <QApplication> |
|
#include <QDrag> |
|
#include <QLabel> |
|
#include <QPainter> |
|
#include <QBitmap> |
|
#include <QX11Info> |
|
#include <QObjectList> |
|
#include <QMimeData> |
|
|
|
#include <X11/Xlib.h> |
|
#include <X11/Xatom.h> |
|
|
|
namespace Oxygen |
|
{ |
|
|
|
//___________________________________________ |
|
Client::Client(KDecorationBridge *b, Factory *f): |
|
KCommonDecorationUnstable(b, f), |
|
_factory( f ), |
|
_sizeGrip( 0 ), |
|
_glowAnimation( new Animation( 200, this ) ), |
|
_titleAnimationData( new TitleAnimationData( this ) ), |
|
_glowIntensity(0), |
|
_initialized( false ), |
|
_forceActive( false ), |
|
_mouseButton( Qt::NoButton ), |
|
_itemData( this ), |
|
_sourceItem( -1 ), |
|
_shadowAtom( 0 ) |
|
{} |
|
|
|
//___________________________________________ |
|
Client::~Client() |
|
{ |
|
|
|
// delete sizegrip if any |
|
if( hasSizeGrip() ) deleteSizeGrip(); |
|
|
|
} |
|
|
|
//___________________________________________ |
|
QString Client::visibleName() const |
|
{ return i18n("Oxygen"); } |
|
|
|
//___________________________________________ |
|
void Client::init() |
|
{ |
|
|
|
// make sure valid configuration is set |
|
if( !_configuration ) _configuration = _factory->configuration( *this ); |
|
|
|
KCommonDecoration::init(); |
|
|
|
widget()->setAttribute(Qt::WA_NoSystemBackground ); |
|
widget()->setAutoFillBackground( false ); |
|
widget()->setAcceptDrops( true ); |
|
|
|
// setup glow animation |
|
_glowAnimation->setStartValue( glowBias() ); |
|
_glowAnimation->setEndValue( 1.0 ); |
|
_glowAnimation->setTargetObject( this ); |
|
_glowAnimation->setPropertyName( "glowIntensity" ); |
|
_glowAnimation->setEasingCurve( QEasingCurve::InOutQuad ); |
|
connect( _glowAnimation, SIGNAL(finished()), this, SLOT(clearForceActive()) ); |
|
|
|
// title animation data |
|
_titleAnimationData->initialize(); |
|
connect( _titleAnimationData, SIGNAL(pixmapsChanged()), SLOT(updateTitleRect()) ); |
|
|
|
// lists |
|
connect( _itemData.animation().data(), SIGNAL(finished()), this, SLOT(clearTargetItem()) ); |
|
|
|
// in case of preview, one wants to make the label used |
|
// for the central widget transparent. This allows one to have |
|
// the correct background (with gradient) rendered |
|
// Remark: this is minor (and safe) a hack. |
|
// This should be moved upstream (into kwin/lib/kdecoration) |
|
if( isPreview() ) |
|
{ |
|
|
|
QList<QLabel*> children( widget()->findChildren<QLabel*>() ); |
|
foreach( QLabel* widget, children ) |
|
{ widget->setAutoFillBackground( false ); } |
|
|
|
// also change shadow configuration size to something that fits in the preview list |
|
shadowCache().setShadowSize( QPalette::Active, 15 ); |
|
shadowCache().setShadowSize( QPalette::Inactive, 15 ); |
|
|
|
} |
|
|
|
setAlphaEnabled(!isMaximized()); |
|
|
|
_initialized = true; |
|
|
|
// first reset is needed to store Oxygen configuration |
|
reset(0); |
|
|
|
} |
|
|
|
//___________________________________________ |
|
void Client::reset( unsigned long changed ) |
|
{ |
|
KCommonDecorationUnstable::reset( changed ); |
|
|
|
// update window mask when compositing is changed |
|
if( !_initialized ) return; |
|
if( changed & SettingCompositing ) |
|
{ |
|
updateWindowShape(); |
|
widget()->update(); |
|
} |
|
|
|
_configuration = _factory->configuration( *this ); |
|
|
|
// glow animations |
|
_glowAnimation->setDuration( _configuration->shadowAnimationsDuration() ); |
|
|
|
// title transitions |
|
_titleAnimationData->setDuration( _configuration->titleAnimationsDuration() ); |
|
|
|
// tabs |
|
_itemData.setAnimationsEnabled( animationsEnabled() && _configuration->tabAnimationsEnabled() ); |
|
_itemData.animation().data()->setDuration( _configuration->tabAnimationsDuration() ); |
|
|
|
// reset title transitions |
|
_titleAnimationData->reset(); |
|
|
|
// should also update animations for buttons |
|
resetButtons(); |
|
|
|
// also reset tab buttons |
|
for( int index = 0; index < _itemData.count(); index++ ) |
|
{ |
|
ClientGroupItemData& item( _itemData[index] ); |
|
if( item._closeButton ) { item._closeButton.data()->reset(0); } |
|
} |
|
|
|
// reset tab geometry |
|
_itemData.setDirty( true ); |
|
|
|
// handle size grip |
|
if( _configuration->drawSizeGrip() && _configuration->frameBorder() == Configuration::BorderNone ) |
|
{ |
|
|
|
if( !hasSizeGrip() ) createSizeGrip(); |
|
|
|
} else if( hasSizeGrip() ) deleteSizeGrip(); |
|
|
|
// needs to remove shadow property on window since shadows are handled by the decoration |
|
removeShadowHint(); |
|
|
|
} |
|
|
|
//___________________________________________ |
|
bool Client::decorationBehaviour(DecorationBehaviour behaviour) const |
|
{ |
|
switch (behaviour) |
|
{ |
|
|
|
case DB_MenuClose: |
|
return _configuration->closeWindowFromMenuButton(); |
|
|
|
case DB_WindowMask: |
|
return false; |
|
|
|
default: |
|
return KCommonDecoration::decorationBehaviour(behaviour); |
|
} |
|
} |
|
|
|
//_________________________________________________________ |
|
KCommonDecorationButton *Client::createButton(::ButtonType type) |
|
{ |
|
|
|
switch (type) { |
|
|
|
case MenuButton: |
|
return new Button(*this, i18n("Window Actions Menu"), ButtonMenu); |
|
|
|
case AppMenuButton: |
|
return new Button(*this, i18n("Application Menu"), ButtonApplicationMenu); |
|
|
|
case HelpButton: |
|
return new Button(*this, i18n("Help"), ButtonHelp); |
|
|
|
case MinButton: |
|
return new Button(*this, i18n("Minimize"), ButtonMin); |
|
|
|
case MaxButton: |
|
return new Button(*this, i18n("Maximize"), ButtonMax); |
|
|
|
case CloseButton: |
|
return new Button(*this, i18n("Close"), ButtonClose); |
|
|
|
case AboveButton: |
|
return new Button(*this, i18n("Keep Above Others"), ButtonAbove); |
|
|
|
case BelowButton: |
|
return new Button(*this, i18n("Keep Below Others"), ButtonBelow); |
|
|
|
case OnAllDesktopsButton: |
|
return new Button(*this, i18n("On All Desktops"), ButtonSticky); |
|
|
|
case ShadeButton: |
|
return new Button(*this, i18n("Shade Button"), ButtonShade); |
|
|
|
default: break; |
|
|
|
} |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QRegion Client::calcMask( void ) const |
|
{ |
|
|
|
if( isMaximized() ) { return widget()->rect(); } |
|
const QRect frame( widget()->rect().adjusted( |
|
layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop ), |
|
-layoutMetric( LM_OuterPaddingRight ), -layoutMetric( LM_OuterPaddingBottom ) ) ); |
|
|
|
QRegion mask; |
|
if( _configuration->frameBorder() == Configuration::BorderNone && !isShade() ) |
|
{ |
|
|
|
if( hideTitleBar() ) mask = QRegion(); |
|
else if( compositingActive() ) mask = QRegion(); |
|
else mask = helper().roundedMask( frame, 1, 1, 1, 0 ); |
|
|
|
} else { |
|
|
|
if( compositingActive() ) mask = QRegion(); |
|
else mask = helper().roundedMask( frame ); |
|
|
|
} |
|
|
|
return mask; |
|
|
|
} |
|
|
|
//___________________________________________ |
|
int Client::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const |
|
{ |
|
|
|
const bool maximized( isMaximized() ); |
|
const bool shaded( isShade() ); |
|
const bool narrowSpacing( _configuration->useNarrowButtonSpacing() ); |
|
const int frameBorder( this->frameBorder() ); |
|
const int buttonSize( hideTitleBar() ? 0 : this->buttonSize() ); |
|
|
|
switch (lm) |
|
{ |
|
case LM_BorderLeft: |
|
case LM_BorderRight: |
|
{ |
|
int border( frameBorder ); |
|
if( respectWindowState && maximized ) |
|
{ |
|
|
|
border = 0; |
|
|
|
} else if( _configuration->frameBorder() < Configuration::BorderTiny ) { |
|
|
|
border = 0; |
|
|
|
} else if( !compositingActive() && _configuration->frameBorder() == Configuration::BorderTiny ) { |
|
|
|
border = qMax( frameBorder, 3 ); |
|
|
|
} |
|
|
|
return border; |
|
} |
|
|
|
case LM_BorderBottom: |
|
{ |
|
int border( frameBorder ); |
|
if( (respectWindowState && maximized) || shaded ) |
|
{ |
|
|
|
border = 0; |
|
|
|
} else if( _configuration->frameBorder() >= Configuration::BorderNoSide ) { |
|
|
|
// for tiny border, the convention is to have a larger bottom area in order to |
|
// make resizing easier |
|
border = qMax(frameBorder, 4); |
|
|
|
} else if( _configuration->frameBorder() < Configuration::BorderTiny ) { |
|
|
|
border = 0; |
|
|
|
} else if( !compositingActive() && _configuration->frameBorder() == Configuration::BorderTiny ) { |
|
|
|
border = qMax( frameBorder, 3 ); |
|
|
|
} |
|
|
|
return border; |
|
} |
|
|
|
case LM_TitleEdgeTop: |
|
{ |
|
int border = 0; |
|
if( _configuration->frameBorder() == Configuration::BorderNone && hideTitleBar() ) |
|
{ |
|
|
|
border = 0; |
|
|
|
} else if( !( respectWindowState && maximized )) { |
|
|
|
border = TFRAMESIZE; |
|
|
|
} |
|
|
|
return border; |
|
|
|
} |
|
|
|
case LM_TitleEdgeBottom: |
|
{ |
|
return 0; |
|
} |
|
|
|
case LM_TitleEdgeLeft: |
|
case LM_TitleEdgeRight: |
|
{ |
|
int border = 0; |
|
if( !(respectWindowState && maximized) ) |
|
{ border = 4; } |
|
|
|
return border; |
|
|
|
} |
|
|
|
case LM_TitleBorderLeft: |
|
case LM_TitleBorderRight: |
|
{ |
|
int border = 5; |
|
|
|
// if title outline is to be drawn, one adds the space needed to |
|
// separate title and tab border. namely the same value |
|
if( _configuration->drawTitleOutline() ) border += border; |
|
|
|
return border; |
|
} |
|
|
|
case LM_ButtonWidth: |
|
case LM_ButtonHeight: |
|
case LM_TitleHeight: |
|
{ |
|
return buttonSize; |
|
} |
|
|
|
case LM_ButtonSpacing: |
|
return narrowSpacing ? 1:3; |
|
|
|
case LM_ButtonMarginTop: |
|
return 0; |
|
|
|
// outer margin for shadow/glow |
|
case LM_OuterPaddingLeft: |
|
case LM_OuterPaddingRight: |
|
case LM_OuterPaddingTop: |
|
case LM_OuterPaddingBottom: |
|
if( maximized ) return 0; |
|
else return shadowCache().shadowSize(); |
|
|
|
default: |
|
return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); |
|
} |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QRect Client::defaultTitleRect( bool active ) const |
|
{ |
|
|
|
QRect titleRect( this->titleRect().adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 ) ); |
|
|
|
// when drawing title outline, shrink the rect so that it matches the actual caption size |
|
if( active && _configuration->drawTitleOutline() && isActive() ) |
|
{ |
|
|
|
|
|
if( _configuration->titleAlignment() == Configuration::AlignCenterFullWidth ) |
|
{ |
|
titleRect.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) ); |
|
titleRect.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) ); |
|
} |
|
|
|
const QRect textRect( titleBoundingRect( options()->font( true, false), titleRect, caption() ) ); |
|
titleRect.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) ); |
|
titleRect.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) ); |
|
|
|
} else { |
|
|
|
// buttons are properly accounted for in titleBoundingRect method |
|
titleRect.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) ); |
|
titleRect.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) ); |
|
|
|
} |
|
|
|
return titleRect; |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QRect Client::titleBoundingRect( const QFont& font, QRect rect, const QString& caption ) const |
|
{ |
|
|
|
// get title bounding rect |
|
QRect boundingRect( QFontMetrics( font ).boundingRect( rect, titleAlignment() | Qt::AlignVCenter, caption ) ); |
|
|
|
// adjust to make sure bounding rect |
|
// 1/ has same vertical alignment as original titleRect |
|
// 2/ does not exceeds available horizontal space |
|
boundingRect.setTop( rect.top() ); |
|
boundingRect.setBottom( rect.bottom() ); |
|
|
|
// check bounding rect against input rect |
|
boundRectTo( boundingRect, rect ); |
|
|
|
if( _configuration->titleAlignment() == Configuration::AlignCenterFullWidth ) |
|
{ |
|
|
|
/* |
|
check bounding rect against max available space, for buttons |
|
this is not needed if centerTitleOnFullWidth flag is set to false, |
|
because it was already done before calling titleBoundingRect |
|
*/ |
|
boundRectTo( boundingRect, titleRect() ); |
|
|
|
} |
|
|
|
return boundingRect; |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::boundRectTo( QRect& rect, const QRect& bound ) const |
|
{ |
|
|
|
if( bound.left() > rect.left() ) |
|
{ |
|
rect.moveLeft( bound.left() ); |
|
if( bound.right() < rect.right() ) |
|
{ rect.setRight( bound.right() ); } |
|
|
|
} else if( bound.right() < rect.right() ) { |
|
|
|
rect.moveRight( bound.right() ); |
|
if( bound.left() > rect.left() ) |
|
{ rect.setLeft( bound.left() ); } |
|
|
|
} |
|
|
|
return; |
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::clearTargetItem( void ) |
|
{ |
|
|
|
if( _itemData.animationType() == AnimationLeave ) |
|
{ _itemData.setDirty( true ); } |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::updateItemBoundingRects( bool alsoUpdate ) |
|
{ |
|
|
|
// make sure items are not animated |
|
_itemData.animate( AnimationNone ); |
|
|
|
// maximum available space |
|
const QRect titleRect( this->titleRect() ); |
|
|
|
// get tabs |
|
const int items( tabCount() ); |
|
|
|
// make sure item data have the correct number of items |
|
while( _itemData.count() < items ) _itemData.push_back( ClientGroupItemData() ); |
|
while( _itemData.count() > items ) |
|
{ |
|
if( _itemData.back()._closeButton ) delete _itemData.back()._closeButton.data(); |
|
_itemData.pop_back(); |
|
} |
|
|
|
assert( !_itemData.isEmpty() ); |
|
|
|
// create buttons |
|
if( _itemData.count() == 1 ) |
|
{ |
|
|
|
// remove button |
|
if( _itemData.front()._closeButton ) |
|
{ delete _itemData.front()._closeButton.data(); } |
|
|
|
// set active rect |
|
_itemData.front()._activeRect = titleRect.adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 ); |
|
|
|
} else { |
|
|
|
int left( titleRect.left() ); |
|
const int width( titleRect.width()/items ); |
|
for( int index = 0; index < _itemData.count(); index++ ) |
|
{ |
|
|
|
ClientGroupItemData& item(_itemData[index]); |
|
|
|
// make sure button exists |
|
if( !item._closeButton ) |
|
{ |
|
item._closeButton = ClientGroupItemData::ButtonPointer( new Button( *this, QStringLiteral("Close this tab"), ButtonItemClose ) ); |
|
item._closeButton.data()->show(); |
|
item._closeButton.data()->installEventFilter( this ); |
|
} |
|
|
|
// set active rect |
|
QRect local( QPoint( left, titleRect.top() ), QSize( width, titleRect.height() ) ); |
|
local.adjust( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 ); |
|
item._activeRect = local; |
|
left += width; |
|
|
|
} |
|
|
|
} |
|
|
|
if( _itemData.count() == 1 ) |
|
{ |
|
|
|
_itemData.front().reset( defaultTitleRect() ); |
|
|
|
} else { |
|
|
|
for( int index = 0; index < _itemData.count(); index++ ) |
|
{ _itemData[index].reset( _itemData[index]._activeRect ); } |
|
|
|
} |
|
|
|
// button activity |
|
_itemData.updateButtonActivity( currentTabId() ); |
|
|
|
// reset buttons location |
|
_itemData.updateButtons( alsoUpdate ); |
|
_itemData.setDirty( false ); |
|
|
|
return; |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QColor Client::titlebarTextColor(const QPalette &palette) const |
|
{ |
|
if( glowIsAnimated() ) return KColorUtils::mix( |
|
titlebarTextColor( palette, false ), |
|
titlebarTextColor( palette, true ), |
|
glowIntensity() ); |
|
else return titlebarTextColor( palette, isActive() ); |
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::renderWindowBackground( QPainter* painter, const QRect& rect, const QWidget* widget, const QPalette& palette ) const |
|
{ |
|
|
|
// window background |
|
if( |
|
_configuration->blendStyle() == Configuration::BlendNone || |
|
( _configuration->blendStyle() == Configuration::BlendFromStyle && |
|
!helper().hasBackgroundGradient( windowId() ) |
|
) ) |
|
{ |
|
|
|
painter->fillRect( rect, palette.color( QPalette::Window ) ); |
|
|
|
} else { |
|
|
|
int offset = layoutMetric( LM_OuterPaddingTop ); |
|
|
|
// radial gradient positionning |
|
const int height = hideTitleBar() ? 0:buttonSize(); |
|
if( isMaximized() ) offset -= 3; |
|
|
|
const QWidget* window( isPreview() ? this->widget() : widget->window() ); |
|
helper().renderWindowBackground(painter, rect, widget, window, palette, offset, height ); |
|
|
|
} |
|
|
|
// background pixmap |
|
if( isPreview() || helper().hasBackgroundPixmap( windowId() ) ) |
|
{ |
|
int offset = layoutMetric( LM_OuterPaddingTop ); |
|
|
|
// radial gradient positionning |
|
const int height = hideTitleBar() ? 0:buttonSize(); |
|
if( isMaximized() ) offset -= 3; |
|
|
|
// background pixmap |
|
QPoint backgroundPixmapOffset( layoutMetric( LM_OuterPaddingLeft ) + layoutMetric( LM_BorderLeft ), 0 ); |
|
helper().setBackgroundPixmapOffset( backgroundPixmapOffset ); |
|
|
|
const QWidget* window( isPreview() ? this->widget() : widget->window() ); |
|
helper().renderBackgroundPixmap(painter, rect, widget, window, offset, height ); |
|
|
|
} |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::renderWindowBorder( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QPalette& palette ) const |
|
{ |
|
|
|
// get coordinates relative to the client area |
|
// this is annoying. One could use mapTo if this was taking const QWidget* and not |
|
// const QWidget* as argument. |
|
const QWidget* window = (isPreview()) ? this->widget() : widget->window(); |
|
const QWidget* w = widget; |
|
QPoint position( 0, 0 ); |
|
while ( w != window && !w->isWindow() && w != w->parentWidget() ) |
|
{ |
|
position += w->geometry().topLeft(); |
|
w = w->parentWidget(); |
|
} |
|
|
|
// save painter |
|
if( clipRect.isValid() ) |
|
{ |
|
painter->save(); |
|
painter->setClipRegion(clipRect,Qt::IntersectClip); |
|
} |
|
|
|
QRect r = (isPreview()) ? this->widget()->rect():window->rect(); |
|
r.adjust( layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop ), -layoutMetric( LM_OuterPaddingRight ), -layoutMetric( LM_OuterPaddingBottom ) ); |
|
r.adjust(0,0, 1, 1); |
|
|
|
// base color |
|
QColor color( palette.window().color() ); |
|
|
|
// add alpha channel |
|
if( _itemData.count() == 1 && glowIsAnimated() ) |
|
{ color = helper().alphaColor( color, glowIntensity() ); } |
|
|
|
// title height |
|
const int titleHeight( layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleEdgeBottom ) + layoutMetric( LM_TitleHeight ) ); |
|
|
|
// make titlebar background darker for tabbed, non-outline window |
|
if( ( tabCount() >= 2 || _itemData.isAnimated() ) && !(_configuration->drawTitleOutline() && isActive() ) ) |
|
{ |
|
|
|
const QPoint topLeft( r.topLeft()-position ); |
|
const QRect rect( topLeft, QSize( r.width(), titleHeight ) ); |
|
|
|
QLinearGradient lg( rect.topLeft(), rect.bottomLeft() ); |
|
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) ); |
|
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) ); |
|
painter->setBrush( lg ); |
|
painter->setPen( Qt::NoPen ); |
|
painter->drawRect( rect ); |
|
|
|
} |
|
|
|
// horizontal line |
|
{ |
|
const int shadowSize = 7; |
|
const int height = shadowSize-3; |
|
|
|
const QPoint topLeft( r.topLeft()+QPoint(0,titleHeight-height)-position ); |
|
QRect rect( topLeft, QSize( r.width(), height ) ); |
|
|
|
// adjustements to cope with shadow size and outline border. |
|
rect.adjust( -shadowSize, 0, shadowSize-1, 0 ); |
|
if( _configuration->drawTitleOutline() && ( isActive() || glowIsAnimated() ) && !isMaximized() ) |
|
{ |
|
if( _configuration->frameBorder() == Configuration::BorderTiny ) rect.adjust( 1, 0, -1, 0 ); |
|
else if( _configuration->frameBorder() > Configuration::BorderTiny ) rect.adjust( HFRAMESIZE-1, 0, -HFRAMESIZE+1, 0 ); |
|
} |
|
|
|
if( rect.isValid() ) |
|
{ helper().slab( color, 0, shadowSize )->render( rect, painter, TileSet::Top ); } |
|
|
|
} |
|
|
|
if( _configuration->drawTitleOutline() && ( isActive() || glowIsAnimated() ) ) |
|
{ |
|
|
|
// save old hints and turn off anti-aliasing |
|
const QPainter::RenderHints hints( painter->renderHints() ); |
|
painter->setRenderHint( QPainter::Antialiasing, false ); |
|
|
|
// save mask and frame to where |
|
// grey window background is to be rendered |
|
QRegion mask; |
|
QRect frame; |
|
|
|
// bottom line |
|
const int leftOffset = qMin( layoutMetric( LM_BorderLeft ), int(HFRAMESIZE) ); |
|
const int rightOffset = qMin( layoutMetric( LM_BorderRight ), int(HFRAMESIZE) ); |
|
if( _configuration->frameBorder() > Configuration::BorderNone ) |
|
{ |
|
|
|
const int height = qMax( 0, layoutMetric( LM_BorderBottom ) - HFRAMESIZE ); |
|
const int width = r.width() - leftOffset - rightOffset - 1; |
|
|
|
const QRect rect( r.bottomLeft()-position + QPoint( leftOffset, -layoutMetric( LM_BorderBottom ) ), QSize( width, height ) ); |
|
if( height > 0 ) { mask += rect; frame |= rect; } |
|
|
|
const QColor shadow( helper().calcDarkColor( color ) ); |
|
painter->setPen( shadow ); |
|
painter->drawLine( rect.bottomLeft()+QPoint(-1,1), rect.bottomRight()+QPoint(1,1) ); |
|
|
|
} |
|
|
|
// left and right |
|
const int topOffset = titleHeight; |
|
const int bottomOffset = qMin( layoutMetric( LM_BorderBottom ), int(HFRAMESIZE) ); |
|
const int height = r.height() - topOffset - bottomOffset - 1; |
|
|
|
if( _configuration->frameBorder() >= Configuration::BorderTiny ) |
|
{ |
|
|
|
const QColor shadow( helper().calcLightColor( color ) ); |
|
painter->setPen( shadow ); |
|
|
|
// left |
|
int width = qMax( 0, layoutMetric( LM_BorderLeft ) - HFRAMESIZE ); |
|
QRect rect( r.topLeft()-position + QPoint( layoutMetric( LM_BorderLeft ) - width, topOffset ), QSize( width, height ) ); |
|
if( width > 0 ) { mask += rect; frame |= rect; } |
|
|
|
painter->drawLine( rect.topLeft()-QPoint(1,0), rect.bottomLeft()-QPoint(1, 0) ); |
|
|
|
// right |
|
width = qMax( 0, layoutMetric( LM_BorderRight ) - HFRAMESIZE ); |
|
rect = QRect(r.topRight()-position + QPoint( -layoutMetric( LM_BorderRight ), topOffset ), QSize( width, height )); |
|
if( width > 0 ) { mask += rect; frame |= rect; } |
|
|
|
painter->drawLine( rect.topRight()+QPoint(1,0), rect.bottomRight()+QPoint(1, 0) ); |
|
} |
|
|
|
// restore old hints |
|
painter->setRenderHints( hints ); |
|
|
|
// in preview mode also adds center square |
|
if( isPreview() ) |
|
{ |
|
const QRect rect( r.topLeft()-position + QPoint( layoutMetric( LM_BorderLeft ), topOffset ), QSize(r.width()-layoutMetric( LM_BorderLeft )-layoutMetric( LM_BorderRight ),height) ); |
|
mask += rect; frame |= rect; |
|
} |
|
|
|
// paint |
|
if( !mask.isEmpty() ) |
|
{ |
|
painter->setClipRegion( mask, Qt::IntersectClip); |
|
renderWindowBackground(painter, frame, widget, palette ); |
|
} |
|
|
|
} |
|
|
|
// restore painter |
|
if( clipRect.isValid() ) |
|
{ painter->restore(); } |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::renderSeparator( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QColor& color ) const |
|
{ |
|
|
|
const QWidget* window = (isPreview()) ? this->widget() : widget->window(); |
|
|
|
// get coordinates relative to the client area |
|
// this is annoying. One could use mapTo if this was taking const QWidget* and not |
|
// const QWidget* as argument. |
|
QPoint position( 0, 0 ); |
|
{ |
|
const QWidget* w = widget; |
|
while ( w != window && !w->isWindow() && w != w->parentWidget() ) { |
|
position += w->geometry().topLeft(); |
|
w = w->parentWidget(); |
|
} |
|
} |
|
|
|
// setup painter |
|
if (clipRect.isValid()) |
|
{ |
|
painter->save(); |
|
painter->setClipRegion(clipRect,Qt::IntersectClip); |
|
} |
|
|
|
QRect r = (isPreview()) ? this->widget()->rect():window->rect(); |
|
r.adjust( layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop ), -layoutMetric( LM_OuterPaddingRight ), -layoutMetric( LM_OuterPaddingBottom ) ); |
|
|
|
// dimensions |
|
const int titleHeight = layoutMetric(LM_TitleHeight); |
|
const int titleTop = layoutMetric(LM_TitleEdgeTop) + r.top(); |
|
|
|
// set color |
|
QColor local( color ); |
|
if( glowIsAnimated() && _configuration->separatorMode() != Configuration::SeparatorAlways ) |
|
{ local = helper().alphaColor( color, glowIntensity() ); } |
|
|
|
// render |
|
helper().drawSeparator( painter, QRect(r.top(), titleTop+titleHeight-1.5, r.width(), 2).translated( -position ), local, Qt::Horizontal); |
|
|
|
if (clipRect.isValid()) { painter->restore(); } |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::renderTitleOutline( QPainter* painter, const QRect& rect, const QPalette& palette ) const |
|
{ |
|
|
|
// center (for active windows only) |
|
{ |
|
painter->save(); |
|
QRect adjustedRect( rect.adjusted( 1, 1, -1, 1 ) ); |
|
|
|
// prepare painter mask |
|
QRegion mask( adjustedRect.adjusted( 1, 0, -1, 0 ) ); |
|
mask += adjustedRect.adjusted( 0, 1, 0, 0 ); |
|
painter->setClipRegion( mask, Qt::IntersectClip ); |
|
|
|
// draw window background |
|
renderWindowBackground(painter, adjustedRect, widget(), palette ); |
|
painter->restore(); |
|
} |
|
|
|
// shadow |
|
const int shadowSize( 7 ); |
|
const int offset( -3 ); |
|
const int voffset( 5-shadowSize ); |
|
const QRect adjustedRect( rect.adjusted(offset, voffset, -offset, shadowSize) ); |
|
QColor color( palette.color( widget()->backgroundRole() ) ); |
|
|
|
// add alpha channel |
|
if( _itemData.count() == 1 && glowIsAnimated() ) |
|
{ color = helper().alphaColor( color, glowIntensity() ); } |
|
|
|
// render slab |
|
helper().slab( color, 0, shadowSize )->render( adjustedRect, painter, TileSet::Tiles(TileSet::Top|TileSet::Left|TileSet::Right) ); |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::renderTitleText( QPainter* painter, const QRect& rect, const QColor& color, const QColor& contrast ) const |
|
{ |
|
|
|
if( !_titleAnimationData->isValid() ) |
|
{ |
|
// contrast pixmap |
|
_titleAnimationData->reset( |
|
rect, |
|
renderTitleText( rect, caption(), color ), |
|
renderTitleText( rect, caption(), contrast ) ); |
|
} |
|
|
|
if( _titleAnimationData->isDirty() ) |
|
{ |
|
|
|
// clear dirty flags |
|
_titleAnimationData->setDirty( false ); |
|
|
|
// finish current animation if running |
|
if( _titleAnimationData->isAnimated() ) |
|
{ _titleAnimationData->finishAnimation(); } |
|
|
|
if( !_titleAnimationData->isLocked() ) |
|
{ |
|
|
|
// set pixmaps |
|
_titleAnimationData->setPixmaps( |
|
rect, |
|
renderTitleText( rect, caption(), color ), |
|
renderTitleText( rect, caption(), contrast ) ); |
|
|
|
_titleAnimationData->startAnimation(); |
|
renderTitleText( painter, rect, color, contrast ); |
|
|
|
} else if( !caption().isEmpty() ) { |
|
|
|
renderTitleText( painter, rect, caption(), color, contrast ); |
|
|
|
} |
|
|
|
// lock animations (this must be done whether or not |
|
// animation was actually started, in order to extend locking |
|
// every time title get changed too rapidly |
|
_titleAnimationData->lockAnimations(); |
|
|
|
} else if( _titleAnimationData->isAnimated() ) { |
|
|
|
if( isMaximized() ) painter->translate( 0, 2 ); |
|
if( !_titleAnimationData->contrastPixmap().isNull() ) |
|
{ |
|
painter->translate( 0, 1 ); |
|
painter->drawPixmap( rect.topLeft(), _titleAnimationData->contrastPixmap() ); |
|
painter->translate( 0, -1 ); |
|
} |
|
|
|
painter->drawPixmap( rect.topLeft(), _titleAnimationData->pixmap() ); |
|
|
|
if( isMaximized() ) painter->translate( 0, -2 ); |
|
|
|
} else if( !caption().isEmpty() ) { |
|
renderTitleText( painter, rect, caption(), color, contrast ); |
|
|
|
} |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
void Client::renderTitleText( QPainter* painter, const QRect& rect, const QString& caption, const QColor& color, const QColor& contrast, bool elide ) const |
|
{ |
|
|
|
const Qt::Alignment alignment( titleAlignment() | Qt::AlignVCenter ); |
|
const QString local( elide ? QFontMetrics( painter->font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption ); |
|
|
|
// translate title down in case of maximized window |
|
if( isMaximized() ) painter->translate( 0, 2 ); |
|
|
|
if( contrast.isValid() ) |
|
{ |
|
painter->setPen( contrast ); |
|
painter->translate( 0, 1 ); |
|
painter->drawText( rect, alignment, local ); |
|
painter->translate( 0, -1 ); |
|
} |
|
|
|
painter->setPen( color ); |
|
painter->drawText( rect, alignment, local ); |
|
|
|
// translate back |
|
if( isMaximized() ) painter->translate( 0, -2 ); |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
QPixmap Client::renderTitleText( const QRect& rect, const QString& caption, const QColor& color, bool elide ) const |
|
{ |
|
|
|
if( !rect.isValid() ) return QPixmap(); |
|
|
|
QPixmap out( rect.size() ); |
|
out.fill( Qt::transparent ); |
|
if( caption.isEmpty() || !color.isValid() ) return out; |
|
|
|
QPainter painter( &out ); |
|
painter.setFont( options()->font(isActive(), false) ); |
|
const Qt::Alignment alignment( titleAlignment() | Qt::AlignVCenter ); |
|
const QString local( elide ? QFontMetrics( painter.font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption ); |
|
|
|
painter.setPen( color ); |
|
painter.drawText( out.rect(), alignment, local ); |
|
painter.end(); |
|
return out; |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
void Client::renderItem( QPainter* painter, int index, const QPalette& palette ) |
|
{ |
|
|
|
const ClientGroupItemData& item( _itemData[index] ); |
|
|
|
// see if tag is active |
|
const int itemCount( _itemData.count() ); |
|
|
|
// check item bounding rect |
|
if( !item._boundingRect.isValid() ) return; |
|
|
|
// create rect in which text is to be drawn |
|
QRect textRect( item._boundingRect.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) ); |
|
|
|
// add extra space needed for title outline |
|
if( itemCount > 1 || _itemData.isAnimated() ) |
|
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); } |
|
|
|
// add extra space for the button |
|
if( itemCount > 1 && item._closeButton && item._closeButton.data()->isVisible() ) |
|
{ textRect.adjust( 0, 0, - buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); } |
|
|
|
// check if current item is active |
|
const bool active( tabId(index) == currentTabId() ); |
|
|
|
// get current item caption and update text rect |
|
const QString caption( itemCount == 1 ? this->caption() : this->caption(index) ); |
|
|
|
if( _configuration->titleAlignment() != Configuration::AlignCenterFullWidth ) |
|
{ boundRectTo( textRect, titleRect() ); } |
|
|
|
// adjust textRect |
|
textRect = titleBoundingRect( painter->font(), textRect, caption ); |
|
|
|
// title outline |
|
if( itemCount == 1 ) |
|
{ |
|
|
|
// no title outline if the window caption is empty |
|
if( !caption.trimmed().isEmpty() ) |
|
{ |
|
if( _itemData.isAnimated() ) { |
|
|
|
renderTitleOutline( painter, item._boundingRect, palette ); |
|
|
|
} else if( (isActive()||glowIsAnimated()) && _configuration->drawTitleOutline() ) { |
|
|
|
// adjusts boundingRect accordingly |
|
QRect boundingRect( item._boundingRect ); |
|
boundingRect.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) ); |
|
boundingRect.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) ); |
|
|
|
// render bounding rect around it with extra margins |
|
renderTitleOutline( painter, boundingRect, palette ); |
|
|
|
} |
|
|
|
} |
|
|
|
} else if( active ) { |
|
|
|
// in multiple tabs render title outline in all cases |
|
renderTitleOutline( painter, item._boundingRect, palette ); |
|
|
|
} |
|
|
|
// render text |
|
if( active || itemCount == 1 ) |
|
{ |
|
|
|
// for active tab, current caption is "merged" with old caption, if any |
|
renderTitleText( |
|
painter, textRect, |
|
titlebarTextColor( palette ), |
|
titlebarContrastColor( palette ) ); |
|
|
|
|
|
} else { |
|
|
|
QColor background( backgroundPalette( widget(), palette ).color( widget()->window()->backgroundRole() ) ); |
|
|
|
// add extra shade (as used in renderWindowBorder |
|
if( !( isActive() && _configuration->drawTitleOutline() ) ) |
|
{ background = KColorUtils::mix( background, Qt::black, 0.10 ); } |
|
|
|
// otherwise current caption is rendered directly |
|
renderTitleText( |
|
painter, textRect, caption, |
|
titlebarTextColor( backgroundPalette( widget(), palette ), false ), |
|
titlebarContrastColor( background ) ); |
|
|
|
} |
|
|
|
// render separators between inactive tabs |
|
if( !( active || itemCount == 1 ) && item._closeButton && item._closeButton.data()->isVisible() ) |
|
{ |
|
|
|
// separators |
|
// draw Left separator |
|
const QColor color( backgroundPalette( widget(), palette ).window().color() ); |
|
const bool isFirstItem( index == 0 || (index == 1 && !_itemData[0]._boundingRect.isValid() ) ); |
|
if( !active && ( ( isFirstItem && buttonsLeftWidth() > 0 ) || _itemData.isTarget( index ) ) ) |
|
{ |
|
|
|
const QRect local( item._boundingRect.topLeft()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) ); |
|
helper().drawSeparator( painter, local, color, Qt::Vertical); |
|
|
|
} |
|
|
|
// draw right separator |
|
if( |
|
( index == itemCount-1 && buttonsRightWidth() > 0 ) || |
|
( index+1 < itemCount && ( _itemData.isTarget( index+1 ) || |
|
!( tabId(index+1) == currentTabId() && _itemData[index+1]._boundingRect.isValid() ) ) ) ) |
|
{ |
|
|
|
const QRect local( item._boundingRect.topRight()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) ); |
|
helper().drawSeparator( painter, local, color, Qt::Vertical); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
void Client::renderTargetRect( QPainter* p, const QPalette& palette ) |
|
{ |
|
if( _itemData.targetRect().isNull() || _itemData.isAnimationRunning() ) return; |
|
|
|
const QColor color = palette.color(QPalette::Highlight); |
|
p->setPen(KColorUtils::mix(color, palette.color(QPalette::Active, QPalette::WindowText))); |
|
p->setBrush( helper().alphaColor( color, 0.5 ) ); |
|
p->drawRect( QRectF(_itemData.targetRect()).adjusted( 4.5, 2.5, -4.5, -2.5 ) ); |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
void Client::renderCorners( QPainter* painter, const QRect& frame, const QPalette& palette ) const |
|
{ |
|
|
|
const QColor color( backgroundColor( widget(), palette ) ); |
|
|
|
QLinearGradient lg = QLinearGradient(0, -0.5, 0, qreal( frame.height() )+0.5); |
|
lg.setColorAt(0.0, helper().calcLightColor( helper().backgroundTopColor(color) )); |
|
lg.setColorAt(0.51, helper().backgroundBottomColor(color) ); |
|
lg.setColorAt(1.0, helper().backgroundBottomColor(color) ); |
|
|
|
painter->setPen( QPen( lg, 1 ) ); |
|
painter->setBrush( Qt::NoBrush ); |
|
painter->drawRoundedRect( QRectF( frame ).adjusted( 0.5, 0.5, -0.5, -0.5 ), 3.5, 3.5 ); |
|
|
|
} |
|
|
|
//_______________________________________________________________________ |
|
void Client::renderFloatFrame( QPainter* painter, const QRect& frame, const QPalette& palette ) const |
|
{ |
|
|
|
// shadow and resize handles |
|
if( !isMaximized() ) |
|
{ |
|
|
|
if( _configuration->frameBorder() >= Configuration::BorderTiny ) |
|
{ |
|
|
|
helper().drawFloatFrame( |
|
painter, frame, backgroundColor( widget(), palette ), |
|
!compositingActive(), isActive() && shadowCache().isEnabled( QPalette::Active ), |
|
KDecoration::options()->color(ColorTitleBar) |
|
); |
|
|
|
} else { |
|
|
|
// for small borders, use a frame that matches the titlebar only |
|
const QRect local( frame.topLeft(), QSize( frame.width(), layoutMetric(LM_TitleHeight) + layoutMetric(LM_TitleEdgeTop) ) ); |
|
helper().drawFloatFrame( |
|
painter, local, backgroundColor( widget(), palette ), |
|
false, isActive() && shadowCache().isEnabled( QPalette::Active ), |
|
KDecoration::options()->color(ColorTitleBar) |
|
); |
|
} |
|
|
|
} else if( isShade() ) { |
|
|
|
// for shaded maximized windows adjust frame and draw the bottom part of it |
|
helper().drawFloatFrame( |
|
painter, frame, backgroundColor( widget(), palette ), |
|
!( compositingActive() || _configuration->frameBorder() == Configuration::BorderNone ), isActive(), |
|
KDecoration::options()->color(ColorTitleBar), |
|
TileSet::Bottom |
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
//____________________________________________________________________________ |
|
void Client::renderDots( QPainter* painter, const QRect& frame, const QColor& color ) const |
|
{ |
|
|
|
if( _configuration->frameBorder() >= Configuration::BorderTiny ) |
|
{ |
|
|
|
// dimensions |
|
int x,y,w,h; |
|
frame.getRect(&x, &y, &w, &h); |
|
|
|
if( isResizable() && !isShade() && !isMaximized() ) |
|
{ |
|
|
|
// Draw right side 3-dots resize handles |
|
const int cenY = (h / 2 + y) ; |
|
const int posX = (w + x - 3); |
|
|
|
helper().renderDot( painter, QPoint(posX, cenY - 3), color); |
|
helper().renderDot( painter, QPoint(posX, cenY), color); |
|
helper().renderDot( painter, QPoint(posX, cenY + 3), color); |
|
|
|
} |
|
|
|
// Draw bottom-right cornet 3-dots resize handles |
|
if( isResizable() && !isShade() && !_configuration->drawSizeGrip() ) |
|
{ |
|
|
|
painter->save(); |
|
painter->translate(x + w-9, y + h-9); |
|
helper().renderDot( painter, QPoint(2, 6), color); |
|
helper().renderDot( painter, QPoint(5, 5), color); |
|
helper().renderDot( painter, QPoint(6, 2), color); |
|
painter->restore(); |
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::activeChange( void ) |
|
{ |
|
|
|
KCommonDecorationUnstable::activeChange(); |
|
_itemData.setDirty( true ); |
|
|
|
// reset animation |
|
if( shadowAnimationsEnabled() ) |
|
{ |
|
_glowAnimation->setDirection( isActive() ? Animation::Forward : Animation::Backward ); |
|
if(!glowIsAnimated()) { _glowAnimation->start(); } |
|
} |
|
|
|
// update size grip so that it gets the right color |
|
// also make sure it is remaped to from z stack, |
|
// unless hidden |
|
if( hasSizeGrip() && !(isShade() || isMaximized() )) |
|
{ |
|
sizeGrip().activeChange(); |
|
sizeGrip().update(); |
|
} |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::maximizeChange( void ) |
|
{ |
|
if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) ); |
|
setAlphaEnabled(!isMaximized()); |
|
KCommonDecorationUnstable::maximizeChange(); |
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::shadeChange( void ) |
|
{ |
|
if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) ); |
|
KCommonDecorationUnstable::shadeChange(); |
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::captionChange( void ) |
|
{ |
|
|
|
KCommonDecorationUnstable::captionChange(); |
|
_itemData.setDirty( true ); |
|
if( titleAnimationsEnabled() ) |
|
{ _titleAnimationData->setDirty( true ); } |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QPalette Client::backgroundPalette( const QWidget* widget, QPalette palette ) const |
|
{ |
|
|
|
if( _configuration->drawTitleOutline() ) |
|
{ |
|
if( glowIsAnimated() && !isForcedActive() ) |
|
{ |
|
|
|
const QColor inactiveColor( backgroundColor( widget, palette, false ) ); |
|
const QColor activeColor( backgroundColor( widget, palette, true ) ); |
|
const QColor mixed( KColorUtils::mix( inactiveColor, activeColor, glowIntensity() ) ); |
|
palette.setColor( QPalette::Window, mixed ); |
|
palette.setColor( QPalette::Button, mixed ); |
|
|
|
} else if( isActive() || isForcedActive() ) { |
|
|
|
const QColor color = options()->color( KDecorationDefines::ColorTitleBar, true ); |
|
palette.setColor( QPalette::Window, color ); |
|
palette.setColor( QPalette::Button, color ); |
|
|
|
} |
|
|
|
} |
|
|
|
return palette; |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QColor Client::backgroundColor( const QWidget*, QPalette palette, bool active ) const |
|
{ |
|
|
|
return ( _configuration->drawTitleOutline() && active ) ? |
|
options()->color( KDecorationDefines::ColorTitleBar, true ): |
|
palette.color( QPalette::Window ); |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
QString Client::defaultButtonsLeft() const |
|
{ return KCommonDecoration::defaultButtonsLeft(); } |
|
|
|
//_________________________________________________________ |
|
QString Client::defaultButtonsRight() const |
|
{ return QStringLiteral("HIAX"); } |
|
|
|
//________________________________________________________________ |
|
void Client::updateWindowShape() |
|
{ |
|
|
|
if(isMaximized()) clearMask(); |
|
else setMask( calcMask() ); |
|
|
|
} |
|
|
|
//______________________________________________________________________________ |
|
bool Client::eventFilter( QObject* object, QEvent* event ) |
|
{ |
|
|
|
// all dedicated event filtering is here to handle multiple tabs. |
|
|
|
bool state = false; |
|
switch( event->type() ) |
|
{ |
|
|
|
case QEvent::Show: |
|
if( widget() == object ) |
|
{ _itemData.setDirty( true ); } |
|
break; |
|
|
|
case QEvent::MouseButtonPress: |
|
if( widget() == object ) |
|
{ state = mousePressEvent( static_cast< QMouseEvent* >( event ) ); } |
|
break; |
|
|
|
case QEvent::MouseButtonRelease: |
|
if( widget() == object ) state = mouseReleaseEvent( static_cast< QMouseEvent* >( event ) ); |
|
else if( Button *btn = qobject_cast< Button* >( object ) ) |
|
{ |
|
QMouseEvent* mouseEvent( static_cast< QMouseEvent* >( event ) ); |
|
if( mouseEvent->button() == Qt::LeftButton && btn->rect().contains( mouseEvent->pos() ) ) |
|
{ state = closeItem( btn ); } |
|
} |
|
|
|
break; |
|
|
|
case QEvent::MouseMove: |
|
state = mouseMoveEvent( static_cast< QMouseEvent* >( event ) ); |
|
break; |
|
|
|
case QEvent::DragEnter: |
|
if( widget() == object ) |
|
{ state = dragEnterEvent( static_cast< QDragEnterEvent* >( event ) ); } |
|
break; |
|
|
|
case QEvent::DragMove: |
|
if( widget() == object ) |
|
{ state = dragMoveEvent( static_cast< QDragMoveEvent* >( event ) ); } |
|
break; |
|
|
|
case QEvent::DragLeave: |
|
if( widget() == object ) |
|
{ state = dragLeaveEvent( static_cast< QDragLeaveEvent* >( event ) ); } |
|
break; |
|
|
|
case QEvent::Drop: |
|
if( widget() == object ) |
|
{ state = dropEvent( static_cast< QDropEvent* >( event ) ); } |
|
break; |
|
|
|
default: break; |
|
|
|
} |
|
return state || KCommonDecorationUnstable::eventFilter( object, event ); |
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::resizeEvent( QResizeEvent* event ) |
|
{ |
|
|
|
// prepare item data updates |
|
_itemData.setDirty( true ); |
|
|
|
// mark title animation as dirty |
|
if( event->oldSize().width() != event->size().width() ) |
|
{ _titleAnimationData->setDirty( true ); } |
|
|
|
// resize backing store pixmap |
|
if( !compositingActive() ) |
|
{ _pixmap = QPixmap( event->size() ); } |
|
|
|
// base class implementation |
|
KCommonDecorationUnstable::resizeEvent( event ); |
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::paintBackground( QPainter& painter ) const |
|
{ |
|
if( !compositingActive() ) |
|
{ painter.drawPixmap( QPoint(), _pixmap ); } |
|
} |
|
|
|
//_________________________________________________________ |
|
QRegion Client::region( KDecorationDefines::Region r ) |
|
{ |
|
|
|
// return empty region for anything but extended borders, when enabled |
|
if( !( r == KDecorationDefines::ExtendedBorderRegion && configuration()->useExtendedWindowBorders() ) ) |
|
{ return QRegion(); } |
|
|
|
// return empty region for maximized windows |
|
if( isMaximized() ) return QRegion(); |
|
|
|
// return 3 pixels extended borders for sides that have no visible borders |
|
// also add the invisible pixels at the masked rounded corners, in non compositing mode |
|
if( configuration()->frameBorder() <= Configuration::BorderNoSide || !compositingActive() ) |
|
{ |
|
|
|
QRect rect = widget()->rect().adjusted( |
|
layoutMetric( LM_OuterPaddingLeft ), |
|
layoutMetric( LM_OuterPaddingTop ), |
|
- layoutMetric( LM_OuterPaddingRight ), |
|
- layoutMetric( LM_OuterPaddingBottom ) ); |
|
|
|
rect.translate( -layoutMetric( LM_OuterPaddingLeft ), -layoutMetric( LM_OuterPaddingTop ) ); |
|
|
|
// mask |
|
QRegion mask( calcMask() ); |
|
if( mask.isEmpty() ) mask = rect; |
|
else mask.translate( -layoutMetric( LM_OuterPaddingLeft ), -layoutMetric( LM_OuterPaddingTop ) ); |
|
|
|
// only return non-empty region on the sides for which there is no border |
|
if( configuration()->frameBorder() == Configuration::BorderNone ) return QRegion( rect.adjusted( -3, 0, 3, 3 ) ) - mask; |
|
else if( configuration()->frameBorder() == Configuration::BorderNoSide ) return QRegion( rect.adjusted( -3, 0, 3, 0 ) ) - mask; |
|
else if( !compositingActive() ) return QRegion( rect ) - mask; |
|
|
|
} |
|
|
|
// fall back |
|
return QRegion(); |
|
|
|
} |
|
|
|
|
|
//_________________________________________________________ |
|
void Client::paintEvent( QPaintEvent* event ) |
|
{ |
|
|
|
// factory |
|
if(!( _initialized && _factory->initialized() ) ) return; |
|
|
|
if( compositingActive() ) |
|
{ |
|
|
|
QPainter painter(widget()); |
|
painter.setRenderHint(QPainter::Antialiasing); |
|
painter.setClipRegion( event->region() ); |
|
paint( painter ); |
|
|
|
// update buttons |
|
QList<Button*> buttons( widget()->findChildren<Button*>() ); |
|
foreach( Button* button, buttons ) |
|
{ |
|
if( ( button->isVisible() || isPreview() ) && event->rect().intersects( button->geometry() ) ) |
|
{ |
|
|
|
painter.save(); |
|
painter.setViewport( button->geometry() ); |
|
painter.setWindow( button->rect() ); |
|
button->paint( painter ); |
|
painter.restore(); |
|
|
|
} |
|
} |
|
|
|
|
|
} else { |
|
|
|
{ |
|
// update backing store pixmap |
|
QPainter painter( &_pixmap ); |
|
painter.setRenderHint(QPainter::Antialiasing); |
|
painter.setClipRegion( event->region() ); |
|
paint( painter ); |
|
} |
|
|
|
QPainter painter( widget() ); |
|
painter.setClipRegion( event->region() ); |
|
painter.drawPixmap( QPoint(), _pixmap ); |
|
|
|
// update buttons |
|
QList<Button*> buttons( widget()->findChildren<Button*>() ); |
|
foreach( Button* button, buttons ) |
|
{ |
|
if( event->rect().intersects( button->geometry() ) ) |
|
{ button->update(); } |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
//_________________________________________________________ |
|
void Client::paint( QPainter& painter ) |
|
{ |
|
|
|
// palette |
|
QPalette palette = widget()->palette(); |
|
palette.setCurrentColorGroup( (isActive() ) ? QPalette::Active : QPalette::Inactive ); |
|
|
|
// define frame |
|
QRect frame = widget()->rect(); |
|
|
|
// base color |
|
QColor color = palette.window().color(); |
|
|
|
// draw shadows |
|
if( compositingActive() && shadowCache().shadowSize() > 0 && !isMaximized() ) |
|
{ |
|
|
|
TileSet *tileSet( 0 ); |
|
const ShadowCache::Key key( this->key() ); |
|
if( shadowCache().isEnabled( QPalette::Active ) && glowIsAnimated() && !isForcedActive() ) |
|
{ |
|
|
|
tileSet = shadowCache().tileSet( key, glowIntensity() ); |
|
|
|
} else { |
|
|
|
tileSet = shadowCache().tileSet( key ); |
|
|
|
} |
|
|
|
tileSet->render( frame, &painter, TileSet::Ring); |
|
|
|
} |
|
|
|
// adjust frame |
|
frame.adjust( |
|
layoutMetric(LM_OuterPaddingLeft), |
|
layoutMetric(LM_OuterPaddingTop), |
|
-layoutMetric(LM_OuterPaddingRight), |
|
-layoutMetric(LM_OuterPaddingBottom) ); |
|
|
|
// adjust mask |
|
if( compositingActive() || isPreview() ) |
|
{ |
|
|
|
if( isMaximized() ) { |
|
|
|
painter.setClipRect( frame, Qt::IntersectClip ); |
|
|
|
} else { |
|
|
|
// multipliers |
|
const int left = 1; |
|
const int right = 1; |
|
const int top = 1; |
|
int bottom = 1; |
|
|
|
// disable bottom corners when border frame is too small and window is not shaded |
|
if( _configuration->frameBorder() == Configuration::BorderNone && !isShade() ) bottom = 0; |
|
QRegion mask( helper().roundedMask( frame, left, right, top, bottom ) ); |
|
|
|
renderCorners( &painter, frame, palette ); |
|
painter.setClipRegion( mask, Qt::IntersectClip ); |
|
|
|
} |
|
|
|
} |
|
|
|
// make sure ItemData and tabList are synchronized |
|
/* |
|
this needs to be done before calling RenderWindowBorder |
|
since some painting in there depend on the clientGroups state |
|
*/ |
|
if( _itemData.isDirty() || _itemData.count() != tabCount() ) |
|
{ updateItemBoundingRects( false ); } |
|
|
|
// window background |
|
renderWindowBackground( &painter, frame, widget(), backgroundPalette( widget(), palette ) ); |
|
|
|
// window border (for title outline) |
|
if( hasTitleOutline() ) renderWindowBorder( &painter, frame, widget(), palette ); |
|
|
|
// clipping |
|
if( compositingActive() ) |
|
{ |
|
painter.setClipping(false); |
|
frame.adjust(-1,-1, 1, 1); |
|
} |
|
|
|
// float frame |
|
renderFloatFrame( &painter, frame, palette ); |
|
|
|
// resize handles |
|
renderDots( &painter, frame, backgroundColor( widget(), palette ) ); |
|
|
|
if( !hideTitleBar() ) |
|
{ |
|
|
|
// title bounding rect |
|
painter.setFont( options()->font(isActive(), false) ); |
|
|
|
// draw ClientGroupItems |
|
const int itemCount( _itemData.count() ); |
|
for( int i = 0; i < itemCount; i++ ) renderItem( &painter, i, palette ); |
|
|
|
// draw target rect |
|
renderTargetRect( &painter, widget()->palette() ); |
|
|
|
// separator |
|
if( itemCount == 1 && !_itemData.isAnimated() && drawSeparator() ) |
|
{ renderSeparator(&painter, frame, widget(), color ); } |
|
|
|
} |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::mousePressEvent( QMouseEvent* event ) |
|
{ |
|
|
|
const QPoint point = event->pos(); |
|
if( tabIndexAt( point ) < 0 ) return false; |
|
_dragPoint = point; |
|
|
|
_mouseButton = event->button(); |
|
bool accepted( false ); |
|
if( buttonToWindowOperation( _mouseButton ) == TabDragOp ) |
|
{ |
|
|
|
accepted = true; |
|
|
|
} else if( buttonToWindowOperation( _mouseButton ) == OperationsOp ) { |
|
|
|
QPoint point = event->pos(); |
|
const int clickedIndex( tabIndexAt( point ) ); |
|
_mouseButton = Qt::NoButton; |
|
if ( tabIndexAt( point ) > -1) |
|
{ showWindowMenu( widget()->mapToGlobal( event->pos() ), tabId(clickedIndex) ); } |
|
|
|
accepted = true; // displayClientMenu can possibly destroy the deco... |
|
|
|
} |
|
return accepted; |
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::mouseReleaseEvent( QMouseEvent* event ) |
|
{ |
|
|
|
bool accepted( false ); |
|
if( _mouseButton == event->button() && buttonToWindowOperation( _mouseButton ) != OperationsOp ) |
|
{ |
|
|
|
const QPoint point = event->pos(); |
|
|
|
const long visibleItem = currentTabId(); |
|
const int clickedIndex( tabIndexAt( point ) ); |
|
if( clickedIndex >= 0 && visibleItem != tabId(clickedIndex) ) |
|
{ |
|
setCurrentTab( tabId(clickedIndex) ); |
|
setForceActive( true ); |
|
accepted = true; |
|
} |
|
|
|
} |
|
|
|
_mouseButton = Qt::NoButton; |
|
return accepted; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::mouseMoveEvent( QMouseEvent* event ) |
|
{ |
|
|
|
// check button and distance to drag point |
|
if( hideTitleBar() || _mouseButton == Qt::NoButton || ( event->pos() - _dragPoint ).manhattanLength() <= QApplication::startDragDistance() ) |
|
{ return false; } |
|
|
|
bool accepted( false ); |
|
if( buttonToWindowOperation( _mouseButton ) == TabDragOp ) |
|
{ |
|
|
|
const QPoint point = event->pos(); |
|
const int clickedIndex( tabIndexAt( point ) ); |
|
if( clickedIndex < 0 ) return false; |
|
|
|
_titleAnimationData->reset(); |
|
|
|
QDrag *drag = new QDrag( widget() ); |
|
QMimeData *groupData = new QMimeData(); |
|
groupData->setData( tabDragMimeType(), QByteArray().setNum( (qint64) tabId(clickedIndex) ) ); |
|
drag->setMimeData( groupData ); |
|
_sourceItem = tabIndexAt( _dragPoint ); |
|
|
|
// get tab geometry |
|
QRect geometry( _itemData[clickedIndex]._boundingRect ); |
|
|
|
// remove space used for buttons |
|
if( _itemData.count() > 1 ) |
|
{ |
|
|
|
geometry.adjust( 0, 0, - buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); |
|
|
|
} else if( !( isActive() && _configuration->drawTitleOutline() ) ) { |
|
|
|
geometry.adjust( |
|
buttonsLeftWidth() + layoutMetric( LM_TitleEdgeLeft ) , 0, |
|
-( buttonsRightWidth() + layoutMetric( LM_TitleEdgeRight )), 0 ); |
|
|
|
} |
|
|
|
// adjust geometry to include shadow size |
|
const int shadowSize( shadowCache().shadowSize() ); |
|
const bool drawShadow( |
|
compositingActive() && |
|
KStyle::customStyleHint( QStringLiteral("SH_ArgbDndWindow"), widget() ) && |
|
shadowSize > 0 ); |
|
|
|
if( drawShadow ) |
|
{ geometry.adjust( -shadowSize, -shadowSize, shadowSize, shadowSize ); } |
|
|
|
// compute pixmap and assign |
|
drag->setPixmap( itemDragPixmap( clickedIndex, geometry, drawShadow ) ); |
|
|
|
// note: the pixmap is moved just above the pointer on purpose |
|
// because overlapping pixmap and pointer slows down the pixmap a lot. |
|
QPoint hotSpot( QPoint( event->pos().x() - geometry.left(), -1 ) ); |
|
if( drawShadow ) hotSpot += QPoint( 0, shadowSize ); |
|
|
|
// make sure the horizontal hotspot position is not too far away (more than 1px) |
|
// from the pixmap |
|
if( hotSpot.x() < -1 ) hotSpot.setX(-1); |
|
if( hotSpot.x() > geometry.width() ) hotSpot.setX( geometry.width() ); |
|
|
|
drag->setHotSpot( hotSpot ); |
|
|
|
_dragStartTimer.start( 50, this ); |
|
drag->exec( Qt::MoveAction ); |
|
|
|
// detach tab from window |
|
if( drag->target() == 0 && _itemData.count() > 1 ) |
|
{ |
|
_itemData.setDirty( true ); |
|
untab( tabId(_sourceItem), |
|
widget()->frameGeometry().adjusted( |
|
layoutMetric( LM_OuterPaddingLeft ), |
|
layoutMetric( LM_OuterPaddingTop ), |
|
-layoutMetric( LM_OuterPaddingRight ), |
|
-layoutMetric( LM_OuterPaddingBottom ) |
|
).translated( QCursor::pos() - event->pos() + |
|
QPoint( layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop ))) |
|
); |
|
} |
|
|
|
// reset button |
|
_mouseButton = Qt::NoButton; |
|
accepted = true; |
|
|
|
} |
|
|
|
return accepted; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::dragEnterEvent( QDragEnterEvent* event ) |
|
{ |
|
|
|
// check if drag enter is allowed |
|
if( !event->mimeData()->hasFormat( tabDragMimeType() ) || hideTitleBar() ) return false; |
|
|
|
// accept event |
|
event->acceptProposedAction(); |
|
|
|
// animate |
|
if( event->source() != widget() ) |
|
{ |
|
|
|
_itemData.animate( AnimationEnter, tabIndexAt( event->pos(), true ) ); |
|
|
|
} else if( _itemData.count() > 1 ) { |
|
|
|
_itemData.animate( AnimationEnter|AnimationSameTarget, tabIndexAt( event->pos(), true ) ); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::dragLeaveEvent( QDragLeaveEvent* ) |
|
{ |
|
|
|
if( _itemData.animationType() & AnimationSameTarget ) |
|
{ |
|
|
|
if( _dragStartTimer.isActive() ) _dragStartTimer.stop(); |
|
_itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem ); |
|
|
|
} else if( _itemData.isAnimated() ) { |
|
|
|
_itemData.animate( AnimationLeave ); |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::dragMoveEvent( QDragMoveEvent* event ) |
|
{ |
|
|
|
// check format |
|
if( !event->mimeData()->hasFormat( tabDragMimeType() ) ) return false; |
|
|
|
// animate |
|
if( event->source() != widget() ) |
|
{ |
|
|
|
_itemData.animate( AnimationMove, tabIndexAt( event->pos(), true ) ); |
|
|
|
} else if( _itemData.count() > 1 ) { |
|
|
|
if( _dragStartTimer.isActive() ) _dragStartTimer.stop(); |
|
_itemData.animate( AnimationMove|AnimationSameTarget, tabIndexAt( event->pos(), true ) ); |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::dropEvent( QDropEvent* event ) |
|
{ |
|
|
|
const QPoint point = event->pos(); |
|
_itemData.animate( AnimationNone ); |
|
|
|
const QMimeData *groupData = event->mimeData(); |
|
if( !groupData->hasFormat( tabDragMimeType() ) ) return false; |
|
|
|
_itemData.setDirty( true ); |
|
|
|
if( widget() != event->source() ) setForceActive( true ); |
|
|
|
const long source = QString::fromUtf8( groupData->data( tabDragMimeType() ) ).toLong(); |
|
const int clickedIndex( tabIndexAt( point, true ) ); |
|
if( clickedIndex < 0 ) tab_A_behind_B( source, tabId(_itemData.count()-1) ); |
|
else tab_A_before_B( source, tabId(clickedIndex) ); |
|
|
|
// update title |
|
if( widget() == event->source() ) updateTitleRect(); |
|
|
|
_titleAnimationData->reset(); |
|
return true; |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
void Client::timerEvent( QTimerEvent* event ) |
|
{ |
|
|
|
if( event->timerId() != _dragStartTimer.timerId() ) |
|
{ return KCommonDecorationUnstable::timerEvent( event ); } |
|
|
|
_dragStartTimer.stop(); |
|
|
|
// do nothing if there is only one tab |
|
if( _itemData.count() > 1 ) |
|
{ |
|
_itemData.animate( AnimationMove|AnimationSameTarget, _sourceItem ); |
|
_itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem ); |
|
} |
|
|
|
} |
|
|
|
//_____________________________________________________________ |
|
bool Client::closeItem( const Button* button ) |
|
{ |
|
|
|
for( int i=0; i < _itemData.count(); i++ ) |
|
{ |
|
if( button == _itemData[i]._closeButton.data() ) |
|
{ |
|
_itemData.setDirty( true ); |
|
closeTab( tabId(i) ); |
|
return true; |
|
} |
|
} |
|
return false; |
|
|
|
} |
|
|
|
//________________________________________________________________ |
|
QPixmap Client::itemDragPixmap( int index, QRect geometry, bool drawShadow ) |
|
{ |
|
const bool itemValid( index >= 0 && index < tabCount() ); |
|
|
|
QPixmap pixmap( geometry.size() ); |
|
pixmap.fill( Qt::transparent ); |
|
QPainter painter( &pixmap ); |
|
painter.setRenderHints(QPainter::SmoothPixmapTransform|QPainter::Antialiasing); |
|
|
|
painter.translate( -geometry.topLeft() ); |
|
|
|
// draw shadows |
|
if( drawShadow ) |
|
{ |
|
|
|
// shadow |
|
const int shadowSize( shadowCache().shadowSize() ); |
|
TileSet *tileSet( shadowCache().tileSet( ShadowCache::Key() ) ); |
|
tileSet->render( geometry, &painter, TileSet::Ring); |
|
geometry.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize ); |
|
|
|
renderCorners( &painter, geometry, widget()->palette() ); |
|
|
|
} |
|
|
|
// mask |
|
painter.setClipRegion( helper().roundedMask( geometry ), Qt::IntersectClip ); |
|
|
|
// render window background |
|
renderWindowBackground( &painter, geometry, widget(), widget()->palette() ); |
|
|
|
// darken background if item is inactive |
|
const bool itemActive = (tabCount() <= 1) || !( itemValid && tabId(index) != currentTabId() ); |
|
if( !itemActive ) |
|
{ |
|
|
|
QLinearGradient lg( geometry.topLeft(), geometry.bottomLeft() ); |
|
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) ); |
|
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) ); |
|
painter.setBrush( lg ); |
|
painter.setPen( Qt::NoPen ); |
|
painter.drawRect( geometry ); |
|
|
|
} |
|
|
|
// render title text |
|
painter.setFont( options()->font(isActive(), false) ); |
|
QRect textRect( geometry.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) ); |
|
|
|
if( itemValid ) |
|
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); } |
|
|
|
const QString caption( itemValid ? this->caption(index) : this->caption() ); |
|
|
|
renderTitleText( |
|
&painter, textRect, caption, |
|
titlebarTextColor( widget()->palette(), isActive() && itemActive ), |
|
titlebarContrastColor( widget()->palette() ) ); |
|
|
|
// adjust geometry for floatFrame when compositing is on. |
|
if( drawShadow ) |
|
{ geometry.adjust(-1, -1, 1, 1 ); } |
|
|
|
// floating frame |
|
helper().drawFloatFrame( |
|
&painter, geometry, widget()->palette().window().color(), |
|
!drawShadow, false, |
|
KDecoration::options()->color(ColorTitleBar) |
|
); |
|
|
|
painter.end(); |
|
return pixmap; |
|
|
|
} |
|
|
|
//_________________________________________________________________ |
|
void Client::createSizeGrip( void ) |
|
{ |
|
|
|
assert( !hasSizeGrip() ); |
|
if( ( isResizable() && windowId() != 0 ) || isPreview() ) |
|
{ |
|
_sizeGrip = new SizeGrip( this ); |
|
sizeGrip().setVisible( !( isMaximized() || isShade() ) ); |
|
} |
|
|
|
} |
|
|
|
//_________________________________________________________________ |
|
void Client::deleteSizeGrip( void ) |
|
{ |
|
assert( hasSizeGrip() ); |
|
_sizeGrip->deleteLater(); |
|
_sizeGrip = 0; |
|
} |
|
|
|
//_________________________________________________________________ |
|
void Client::removeShadowHint( void ) |
|
{ |
|
|
|
// do nothing if no window id |
|
if( !windowId() ) return; |
|
|
|
// create atom |
|
if( !_shadowAtom ) |
|
{ _shadowAtom = XInternAtom( QX11Info::display(), "_KDE_NET_WM_SHADOW", False); } |
|
|
|
XDeleteProperty(QX11Info::display(), windowId(), _shadowAtom); |
|
} |
|
|
|
}
|
|
|