kwin/clients/aurorae/src/aurorae.cpp
Martin Gräßlin 6353d6ff57 Don't blur behind opaque window decorations.
Adding a new AbilityUsesBlurBehind which can be set by decorations with
alpha channel to request that the background can be blurred. This improves
the situation for our completely opaque default Oxygen decoration.

This change is a behavior change in comparison to 4.5! Translucent decorations do
not get blurred by default any more.
CCMAIL: craig.p.drummond@googlemail.com

svn path=/trunk/KDE/kdebase/workspace/; revision=1195274
2010-11-10 18:33:07 +00:00

495 lines
16 KiB
C++

/********************************************************************
Copyright (C) 2009, 2010 Martin Gräßlin <kde@martin-graesslin.com>
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, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "aurorae.h"
#include "auroraescene.h"
#include "auroraetheme.h"
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsSceneMouseEvent>
#include <KConfig>
#include <KConfigGroup>
#include <Plasma/FrameSvg>
namespace Aurorae
{
AuroraeFactory::AuroraeFactory()
: QObject()
, KDecorationFactoryUnstable()
, m_theme(new AuroraeTheme(this))
{
init();
}
void AuroraeFactory::init()
{
KConfig conf("auroraerc");
KConfigGroup group(&conf, "Engine");
const QString themeName = group.readEntry("ThemeName", "example-deco");
KConfig config("aurorae/themes/" + themeName + '/' + themeName + "rc", KConfig::FullConfig, "data");
KConfigGroup themeGroup(&conf, themeName);
m_theme->loadTheme(themeName, config);
m_theme->setBorderSize((KDecorationDefines::BorderSize)themeGroup.readEntry<int>("BorderSize", KDecorationDefines::BorderNormal));
m_theme->setButtonSize((KDecorationDefines::BorderSize)themeGroup.readEntry<int>("ButtonSize", KDecorationDefines::BorderNormal));
m_theme->setShowTooltips(options()->showTooltips());
m_theme->setTabDragMimeType(clientGroupItemDragMimeType());
}
AuroraeFactory::~AuroraeFactory()
{
s_instance = NULL;
}
AuroraeFactory *AuroraeFactory::instance()
{
if (!s_instance) {
s_instance = new AuroraeFactory;
}
return s_instance;
}
bool AuroraeFactory::reset(unsigned long changed)
{
init();
resetDecorations(changed);
return false; // need hard reset
}
bool AuroraeFactory::supports(Ability ability) const
{
switch (ability) {
case AbilityAnnounceButtons:
case AbilityUsesAlphaChannel:
case AbilityButtonMenu:
case AbilityButtonSpacer:
case AbilityExtendIntoClientArea:
return true;
case AbilityButtonMinimize:
return m_theme->hasButton(MinimizeButton);
case AbilityButtonMaximize:
return m_theme->hasButton(MaximizeButton) || m_theme->hasButton(RestoreButton);
case AbilityButtonClose:
return m_theme->hasButton(CloseButton);
case AbilityButtonAboveOthers:
return m_theme->hasButton(KeepAboveButton);
case AbilityButtonBelowOthers:
return m_theme->hasButton(KeepBelowButton);
case AbilityButtonShade:
return m_theme->hasButton(ShadeButton);
case AbilityButtonOnAllDesktops:
return m_theme->hasButton(AllDesktopsButton);
case AbilityButtonHelp:
return m_theme->hasButton(HelpButton);
case AbilityProvidesShadow:
return true; // TODO: correct value from theme
case AbilityClientGrouping:
return true;
case AbilityUsesBlurBehind:
return true;
default:
return false;
}
}
KDecoration *AuroraeFactory::createDecoration(KDecorationBridge *bridge)
{
AuroraeClient *client = new AuroraeClient(bridge, this);
return client;
}
QList< KDecorationDefines::BorderSize > AuroraeFactory::borderSizes() const
{
return QList< BorderSize >() << BorderTiny << BorderNormal <<
BorderLarge << BorderVeryLarge << BorderHuge <<
BorderVeryHuge << BorderOversized;
}
AuroraeFactory *AuroraeFactory::s_instance = NULL;
/*******************************************************
* Client
*******************************************************/
AuroraeClient::AuroraeClient(KDecorationBridge *bridge, KDecorationFactory *factory)
: KDecorationUnstable(bridge, factory)
, m_clickInProgress(false)
{
m_scene = new AuroraeScene(AuroraeFactory::instance()->theme(),
options()->customButtonPositions() ? options()->titleButtonsLeft() : AuroraeFactory::instance()->theme()->defaultButtonsLeft(),
options()->customButtonPositions() ? options()->titleButtonsRight() : AuroraeFactory::instance()->theme()->defaultButtonsRight(),
providesContextHelp(), NULL);
connect(m_scene, SIGNAL(closeWindow()), SLOT(closeWindow()));
connect(m_scene, SIGNAL(maximize(Qt::MouseButtons)), SLOT(maximize(Qt::MouseButtons)));
connect(m_scene, SIGNAL(showContextHelp()), SLOT(showContextHelp()));
connect(m_scene, SIGNAL(minimizeWindow()), SLOT(minimize()));
connect(m_scene, SIGNAL(menuClicked()), SLOT(menuClicked()));
connect(m_scene, SIGNAL(menuDblClicked()), SLOT(closeWindow()));
connect(m_scene, SIGNAL(toggleOnAllDesktops()), SLOT(toggleOnAllDesktops()));
connect(m_scene, SIGNAL(toggleShade()), SLOT(toggleShade()));
connect(m_scene, SIGNAL(toggleKeepAbove()), SLOT(toggleKeepAbove()));
connect(m_scene, SIGNAL(toggleKeepBelow()), SLOT(toggleKeepBelow()));
connect(m_scene, SIGNAL(titlePressed(Qt::MouseButton,Qt::MouseButtons)),
SLOT(titlePressed(Qt::MouseButton,Qt::MouseButtons)));
connect(m_scene, SIGNAL(titleReleased(Qt::MouseButton,Qt::MouseButtons)),
SLOT(titleReleased(Qt::MouseButton,Qt::MouseButtons)));
connect(m_scene, SIGNAL(titleDoubleClicked()), SLOT(titlebarDblClickOperation()));
connect(m_scene, SIGNAL(titleMouseMoved(Qt::MouseButton,Qt::MouseButtons)),
SLOT(titleMouseMoved(Qt::MouseButton,Qt::MouseButtons)));
connect(m_scene, SIGNAL(wheelEvent(int)), SLOT(titlebarMouseWheelOperation(int)));
connect(m_scene, SIGNAL(tabMouseButtonPress(QGraphicsSceneMouseEvent*,int)),
SLOT(tabMouseButtonPress(QGraphicsSceneMouseEvent*,int)));
connect(m_scene, SIGNAL(tabMouseButtonRelease(QGraphicsSceneMouseEvent*,int)),
SLOT(tabMouseButtonRelease(QGraphicsSceneMouseEvent*,int)));
connect(m_scene, SIGNAL(tabRemoved(int)), SLOT(tabRemoved(int)));
connect(m_scene, SIGNAL(tabMoved(int,int)), SLOT(tabMoved(int,int)));
connect(m_scene, SIGNAL(tabMovedToGroup(long int,int)), SLOT(tabMovedToGroup(long int,int)));
connect(this, SIGNAL(keepAboveChanged(bool)), SLOT(keepAboveChanged(bool)));
connect(this, SIGNAL(keepBelowChanged(bool)), SLOT(keepBelowChanged(bool)));
}
AuroraeClient::~AuroraeClient()
{
m_view->setParent(NULL);
m_view->deleteLater();
m_scene->deleteLater();
}
void AuroraeClient::init()
{
// HACK: we need to add the GraphicsView as a child widget to a normal widget
// the GraphicsView eats the mouse release event and by that kwin core starts to move
// the decoration each time the decoration is clicked
// therefore we use two widgets and inject an own mouse release event to the parent widget
// when the graphics view eats a mouse event
createMainWidget();
widget()->setAttribute(Qt::WA_TranslucentBackground);
widget()->setAttribute(Qt::WA_NoSystemBackground);
widget()->installEventFilter(this);
m_view = new QGraphicsView(m_scene, widget());
m_view->setAttribute(Qt::WA_TranslucentBackground);
m_view->setFrameShape(QFrame::NoFrame);
QPalette pal = m_view->palette();
pal.setColor(m_view->backgroundRole(), Qt::transparent);
m_view->setPalette(pal);
QPalette pal2 = widget()->palette();
pal2.setColor(widget()->backgroundRole(), Qt::transparent);
widget()->setPalette(pal2);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// scene initialisation
m_scene->setActive(isActive(), false);
m_scene->setIcon(icon());
m_scene->setAllDesktops(isOnAllDesktops());
m_scene->setMaximizeMode(options()->moveResizeMaximizedWindows() ? MaximizeRestore : maximizeMode());
m_scene->setShade(isShade());
m_scene->setKeepAbove(keepAbove());
m_scene->setKeepBelow(keepBelow());
AuroraeFactory::instance()->theme()->setCompositingActive(compositingActive());
}
void AuroraeClient::activeChange()
{
if (m_scene->isActive() != isActive()) {
m_scene->setActive(isActive());
}
}
void AuroraeClient::captionChange()
{
checkTabs(true);
}
void AuroraeClient::iconChange()
{
m_scene->setIcon(icon());
}
void AuroraeClient::desktopChange()
{
m_scene->setAllDesktops(isOnAllDesktops());
}
void AuroraeClient::maximizeChange()
{
if (!options()->moveResizeMaximizedWindows()) {
m_scene->setMaximizeMode(maximizeMode());
}
}
void AuroraeClient::resize(const QSize &s)
{
m_scene->setSceneRect(QRectF(QPoint(0, 0), s));
m_scene->updateLayout();
m_view->resize(s);
widget()->resize(s);
updateWindowShape();
}
void AuroraeClient::shadeChange()
{
m_scene->setShade(isShade());
}
void AuroraeClient::borders(int &left, int &right, int &top, int &bottom) const
{
const bool maximized = maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows();
AuroraeFactory::instance()->theme()->borders(left, top, right, bottom, maximized);
}
void AuroraeClient::padding(int &left, int &right, int &top, int &bottom) const
{
AuroraeFactory::instance()->theme()->padding(left, top, right, bottom);
}
QSize AuroraeClient::minimumSize() const
{
return widget()->minimumSize();
}
KDecorationDefines::Position AuroraeClient::mousePosition(const QPoint &point) const
{
// based on the code from deKorator
int pos = PositionCenter;
if (isShade()) {
return Position(pos);
}
int borderLeft, borderTop, borderRight, borderBottom;
borders(borderLeft, borderRight, borderTop, borderBottom);
int paddingLeft, paddingTop, paddingRight, paddingBottom;
padding(paddingLeft, paddingRight, paddingTop, paddingBottom);
const bool maximized = maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows();
int titleEdgeLeft, titleEdgeRight, titleEdgeTop, titleEdgeBottom;
AuroraeFactory::instance()->theme()->titleEdges(titleEdgeLeft, titleEdgeTop, titleEdgeRight, titleEdgeBottom, maximized);
switch (AuroraeFactory::instance()->theme()->decorationPosition()) {
case DecorationTop:
borderTop = titleEdgeTop;
break;
case DecorationLeft:
borderLeft = titleEdgeLeft;
break;
case DecorationRight:
borderRight = titleEdgeRight;
break;
case DecorationBottom:
borderBottom = titleEdgeBottom;
break;
default:
break; // nothing
}
if (point.x() >= (m_view->width() - borderRight - paddingRight)) {
pos |= PositionRight;
} else if (point.x() <= borderLeft + paddingLeft) {
pos |= PositionLeft;
}
if (point.y() >= m_view->height() - borderBottom - paddingBottom) {
pos |= PositionBottom;
} else if (point.y() <= borderTop + paddingTop ) {
pos |= PositionTop;
}
return Position(pos);
}
void AuroraeClient::reset(long unsigned int changed)
{
if (changed & SettingCompositing) {
updateWindowShape();
AuroraeFactory::instance()->theme()->setCompositingActive(compositingActive());
}
if (changed & SettingButtons) {
m_scene->setButtons(options()->customButtonPositions() ? options()->titleButtonsLeft() : AuroraeFactory::instance()->theme()->defaultButtonsLeft(),
options()->customButtonPositions() ? options()->titleButtonsRight() : AuroraeFactory::instance()->theme()->defaultButtonsRight());
}
KDecoration::reset(changed);
}
void AuroraeClient::menuClicked()
{
showWindowMenu(QCursor::pos());
}
void AuroraeClient::toggleShade()
{
setShade(!isShade());
}
void AuroraeClient::keepAboveChanged(bool above)
{
if (above && m_scene->isKeepBelow()) {
m_scene->setKeepBelow(false);
}
m_scene->setKeepAbove(above);
}
void AuroraeClient::keepBelowChanged(bool below)
{
if (below && m_scene->isKeepAbove()) {
m_scene->setKeepAbove(false);
}
m_scene->setKeepBelow(below);
}
void AuroraeClient::toggleKeepAbove()
{
setKeepAbove(!keepAbove());
}
void AuroraeClient::toggleKeepBelow()
{
setKeepBelow(!keepBelow());
}
void AuroraeClient::updateWindowShape()
{
bool maximized = maximizeMode()==KDecorationDefines::MaximizeFull && !options()->moveResizeMaximizedWindows();
int w=widget()->width();
int h=widget()->height();
if (maximized || compositingActive()) {
QRegion mask(0,0,w,h);
setMask(mask);
return;
}
int pl, pt, pr, pb;
padding(pl, pr, pt, pb);
Plasma::FrameSvg *deco = AuroraeFactory::instance()->theme()->decoration();
if (!deco->hasElementPrefix("decoration-opaque")) {
// opaque element is missing: set generic mask
w = w - pl - pr;
h = h - pt - pb;
QRegion mask(pl, pt, w, h);
setMask(mask);
return;
}
deco->setElementPrefix("decoration-opaque");
deco->resizeFrame(QSize(w-pl-pr, h-pt-pb));
QRegion mask = deco->mask().translated(pl, pt);
setMask(mask);
}
void AuroraeClient::titlePressed(Qt::MouseButton button, Qt::MouseButtons buttons)
{
QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, widget()->mapFromGlobal(QCursor::pos()),
QCursor::pos(), button, buttons, Qt::NoModifier);
processMousePressEvent(event);
delete event;
event = 0;
}
void AuroraeClient::titleReleased(Qt::MouseButton button, Qt::MouseButtons buttons)
{
QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonRelease, widget()->mapFromGlobal(QCursor::pos()),
QCursor::pos(), button, buttons, Qt::NoModifier);
QApplication::sendEvent(widget(), event);
delete event;
event = 0;
}
void AuroraeClient::titleMouseMoved(Qt::MouseButton button, Qt::MouseButtons buttons)
{
QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()),
QCursor::pos(), button, buttons, Qt::NoModifier);
QApplication::sendEvent(widget(), event);
delete event;
event = 0;
}
void AuroraeClient::checkTabs(bool force)
{
if (m_scene->tabCount() == 1 && clientGroupItems().count() == 1 && !force) {
return;
}
while (m_scene->tabCount() < clientGroupItems().count()) {
m_scene->addTab(QString());
}
while (m_scene->tabCount() > clientGroupItems().count()) {
m_scene->removeLastTab();
}
QList<AuroraeTabData> data;
foreach (const ClientGroupItem &item, clientGroupItems()) {
data << AuroraeTabData(item.title(), item.icon());
}
m_scene->setAllTabData(data);
m_scene->setFocusedTab(visibleClientGroupItem());
}
bool AuroraeClient::eventFilter(QObject *o, QEvent *e)
{
if (o != widget()) {
return false;
}
if (e->type() == QEvent::Paint) {
checkTabs();
}
return false;
}
void AuroraeClient::tabMouseButtonPress(QGraphicsSceneMouseEvent *e, int index)
{
if (buttonToWindowOperation(e->buttons()) == OperationsOp) {
displayClientMenu(index, e->screenPos());
return;
} else if (buttonToWindowOperation(e->buttons()) == ClientGroupDragOp) {
m_scene->setUniqueTabDragId(index, itemId(index));
}
titlePressed(e->button(), e->buttons());
m_clickInProgress = true;
}
void AuroraeClient::tabMouseButtonRelease(QGraphicsSceneMouseEvent *e, int index)
{
if (m_clickInProgress) {
setVisibleClientGroupItem(index);
}
titleReleased(e->button(), e->buttons());
m_clickInProgress = false;
}
void AuroraeClient::tabRemoved(int index)
{
removeFromClientGroup(index);
}
void AuroraeClient::tabMoved(int index, int before)
{
moveItemInClientGroup(index, before);
}
void AuroraeClient::tabMovedToGroup(long int uid, int before)
{
moveItemToClientGroup(uid, before);
}
} // namespace Aurorae
extern "C"
{
KDE_EXPORT KDecorationFactory *create_factory() {
return Aurorae::AuroraeFactory::instance();
}
}
#include "aurorae.moc"