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.
 
 
 
 

358 lines
14 KiB

/*
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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 "breezedeco.h"
#include "breezebuttons.h"
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/DecorationButtonGroup>
#include <KDecoration2/DecorationSettings>
#include <KDecoration2/DecorationShadow>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KPluginFactory>
#include <QPainter>
K_PLUGIN_FACTORY_WITH_JSON(BreezeDecoFactory,
"breeze.json",
registerPlugin<Breeze::Decoration>();)
namespace Breeze
{
ColorSettings::ColorSettings(const QPalette &pal)
{
init(pal);
}
void ColorSettings::update(const QPalette &pal)
{
init(pal);
}
void ColorSettings::init(const QPalette &pal)
{
m_palette = pal;
KConfigGroup wmConfig(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("WM"));
m_activeFrameColor = wmConfig.readEntry("frame", pal.color(QPalette::Active, QPalette::Background));
m_inactiveFrameColor = wmConfig.readEntry("inactiveFrame", m_activeFrameColor);
m_activeTitleBarColor = wmConfig.readEntry("activeBackground", pal.color(QPalette::Active, QPalette::Highlight));
m_inactiveTitleBarColor = wmConfig.readEntry("inactiveBackground", m_inactiveFrameColor);
m_activeFontColor = wmConfig.readEntry("activeForeground", pal.color(QPalette::Active, QPalette::HighlightedText));
m_inactiveFontColor = wmConfig.readEntry("inactiveForeground", m_activeFontColor.dark());
}
Decoration::Decoration(QObject *parent, const QVariantList &args)
: KDecoration2::Decoration(parent, args)
, m_colorSettings(client()->palette())
, m_leftButtons(nullptr)
, m_rightButtons(nullptr)
{
}
Decoration::~Decoration() = default;
void Decoration::init()
{
recalculateBorders();
updateTitleRect();
auto s = settings();
connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, this, &Decoration::recalculateBorders);
// a change in font might cause the borders to change
connect(s.data(), &KDecoration2::DecorationSettings::fontChanged, this, &Decoration::recalculateBorders);
connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::recalculateBorders);
connect(client().data(), &KDecoration2::DecoratedClient::borderingScreenEdgesChanged, this, &Decoration::recalculateBorders);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedHorizontallyChanged, this, &Decoration::recalculateBorders);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedVerticallyChanged, this, &Decoration::recalculateBorders);
connect(client().data(), &KDecoration2::DecoratedClient::captionChanged, this,
[this]() {
// update the caption area
update(captionRect());
}
);
connect(client().data(), &KDecoration2::DecoratedClient::activeChanged, this, [this]() { update(); });
connect(client().data(), &KDecoration2::DecoratedClient::paletteChanged, this,
[this]() {
m_colorSettings.update(client()->palette());
update();
}
);
connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleRect);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleRect);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::setOpaque);
connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateButtonPositions);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateButtonPositions);
connect(client().data(), &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateButtonPositions);
createButtons();
createShadow();
}
void Decoration::updateTitleRect()
{
auto s = settings();
const bool maximized = client()->isMaximized();
const int width = client()->width();
const int height = maximized ? borderTop() : borderTop() - s->smallSpacing();
const int x = maximized ? 0 : s->largeSpacing() / 2;
const int y = maximized ? 0 : s->smallSpacing();
setTitleRect(QRect(x, y, width, height));
}
static int borderSize(const QSharedPointer<KDecoration2::DecorationSettings> &settings, bool bottom) {
const int baseSize = settings->largeSpacing() / 2;
switch (settings->borderSize()) {
case KDecoration2::BorderSize::None:
return 0;
case KDecoration2::BorderSize::NoSides:
return bottom ? baseSize : 0;
case KDecoration2::BorderSize::Tiny:
return baseSize / 2;
case KDecoration2::BorderSize::Normal:
return baseSize;
case KDecoration2::BorderSize::Large:
return baseSize * 1.5;
case KDecoration2::BorderSize::VeryLarge:
return baseSize * 2;
case KDecoration2::BorderSize::Huge:
return baseSize * 2.5;
case KDecoration2::BorderSize::VeryHuge:
return baseSize * 3;
case KDecoration2::BorderSize::Oversized:
return baseSize * 5;
default:
return baseSize;
}
}
static int borderSize(const QSharedPointer<KDecoration2::DecorationSettings> &settings) {
return borderSize(settings, false);
}
void Decoration::recalculateBorders()
{
auto s = settings();
const Qt::Edges edges = client()->borderingScreenEdges();
int left = client()->isMaximizedHorizontally() || edges.testFlag(Qt::LeftEdge) ? 0 : borderSize(s);
int right = client()->isMaximizedHorizontally() || edges.testFlag(Qt::RightEdge) ? 0 : borderSize(s);
QFontMetrics fm(s->font());
int top = qMax(fm.boundingRect(client()->caption()).height(), s->gridUnit() * 2);
// padding below
top += s->smallSpacing() * 2 + 1;
if (!client()->isMaximized()) {
// padding above only on maximized
top += s->smallSpacing();
}
int bottom = client()->isMaximizedVertically() || edges.testFlag(Qt::BottomEdge) ? 0 : borderSize(s, true);
setBorders(left, right, top, bottom);
const int extSize = s->largeSpacing() / 2;
int extSides = 0;
int extBottom = 0;
if (s->borderSize() == KDecoration2::BorderSize::None) {
extSides = extSize;
extBottom = extSize;
} else if (s->borderSize() == KDecoration2::BorderSize::NoSides) {
extSides = extSize;
}
setExtendedBorders(extSides, extSides, 0, extBottom);
}
void Decoration::createButtons()
{
m_leftButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Left, this, &Button::create);
m_rightButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Right, this, &Button::create);
updateButtonPositions();
}
void Decoration::updateButtonPositions()
{
auto s = settings();
const int padding = client()->isMaximized() ? 0 : s->smallSpacing();
m_rightButtons->setSpacing(s->smallSpacing());
m_leftButtons->setSpacing(s->smallSpacing());
m_leftButtons->setPos(QPointF(padding, padding));
m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width() - padding, padding));
}
void Decoration::paint(QPainter *painter)
{
// paint background
painter->fillRect(rect(), Qt::transparent);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
painter->setBrush(m_colorSettings.frame(client()->isActive()));
// clip away the top part
painter->save();
painter->setClipRect(0, borderTop(), size().width(), size().height() - borderTop(), Qt::IntersectClip);
painter->drawRoundedRect(rect(), 5.0, 5.0);
painter->restore();
paintTitleBar(painter);
painter->restore();
}
void Decoration::paintTitleBar(QPainter *painter)
{
const bool active = client()->isActive();
const QRect titleRect(QPoint(0, 0), QSize(size().width(), borderTop()));
const QColor titleBarColor(m_colorSettings.titleBarColor(client()->isActive()));
// render a linear gradient on title area
QLinearGradient gradient;
gradient.setStart(0.0, 0.0);
gradient.setFinalStop(0.0, titleRect.height());
gradient.setColorAt(0.0, titleBarColor.lighter(client()->isActive() ? 120.0 : 100.0));
gradient.setColorAt(0.8, titleBarColor);
gradient.setColorAt(1.0, titleBarColor);
gradient.setFinalStop(0.0, titleRect.height());
painter->save();
painter->setBrush(gradient);
painter->setPen(Qt::NoPen);
if (client()->isMaximized()) {
painter->drawRect(titleRect);
} else {
painter->setClipRect(titleRect, Qt::IntersectClip);
// we make the rect a little bit larger to be able to clip away the rounded corners on bottom
painter->drawRoundedRect(titleRect.adjusted(0, 0, 0, 5), 5.0, 5.0);
}
painter->restore();
auto s = settings();
const int titleBarSpacer = s->smallSpacing();
if (true) {
// TODO: should be config option
painter->fillRect(0, borderTop() - titleBarSpacer - 1,
size().width(), 1,
client()->palette().color(active ? QPalette::Highlight: QPalette::Background));
}
// draw title bar spacer
painter->fillRect(0, borderTop() - titleBarSpacer, size().width(), titleBarSpacer, client()->palette().color(QPalette::Background));
// draw caption
painter->setFont(s->font());
const QRect cR = captionRect();
const QString caption = painter->fontMetrics().elidedText(client()->caption(), Qt::ElideMiddle, cR.width());
painter->setPen(m_colorSettings.font(client()->isActive()));
painter->drawText(cR, Qt::AlignCenter | Qt::TextSingleLine, caption);
// draw all buttons
m_leftButtons->paint(painter);
m_rightButtons->paint(painter);
}
int Decoration::captionHeight() const
{
return borderTop() - settings()->smallSpacing() * (client()->isMaximized() ? 2 : 3) - 1;
}
QRect Decoration::captionRect() const
{
const int leftOffset = m_leftButtons->geometry().x() + m_leftButtons->geometry().width();
const int rightOffset = size().width() - m_rightButtons->geometry().x();
const int offset = qMax(leftOffset, rightOffset);
const int yOffset = client()->isMaximized() ? 0 : settings()->smallSpacing();
// below is the spacer
return QRect(offset, yOffset, size().width() - offset * 2, captionHeight());
}
void Decoration::createShadow()
{
KDecoration2::DecorationShadow *decorationShadow = new KDecoration2::DecorationShadow(this);
decorationShadow->setPaddingBottom(20);
decorationShadow->setPaddingRight(20);
decorationShadow->setPaddingTop(10);
decorationShadow->setPaddingLeft(10);
decorationShadow->setTopLeft(QSize(20, 20));
decorationShadow->setTop(QSize(20, 20));
decorationShadow->setTopRight(QSize(20, 20));
decorationShadow->setRight(QSize(20, 20));
decorationShadow->setBottomRight(QSize(20, 20));
decorationShadow->setBottom(QSize(20, 20));
decorationShadow->setBottomLeft(QSize(20, 20));
decorationShadow->setLeft(QSize(20, 20));
QImage image(60, 60, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
auto gradientStopColor = [](qreal alpha) {
QColor color(35, 38, 41);
color.setAlphaF(alpha);
return color;
};
QRadialGradient radialGradient(20, 20, 20);
radialGradient.setColorAt(0.0, gradientStopColor(0.35));
radialGradient.setColorAt(0.25, gradientStopColor(0.25));
radialGradient.setColorAt(0.5, gradientStopColor(0.13));
radialGradient.setColorAt(0.75, gradientStopColor(0.04));
radialGradient.setColorAt(1.0, gradientStopColor(0.0));
QLinearGradient linearGradient;
linearGradient.setColorAt(0.0, gradientStopColor(0.35));
linearGradient.setColorAt(0.25, gradientStopColor(0.25));
linearGradient.setColorAt(0.5, gradientStopColor(0.13));
linearGradient.setColorAt(0.75, gradientStopColor(0.04));
linearGradient.setColorAt(1.0, gradientStopColor(0.0));
QPainter p(&image);
p.setCompositionMode(QPainter::CompositionMode_Source);
// topLeft
p.fillRect(QRect(0, 0, 20, 20), radialGradient);
// top
linearGradient.setStart(20, 20);
linearGradient.setFinalStop(20, 0);
p.fillRect(QRect(20, 0, 20, 20), linearGradient);
// topRight
radialGradient.setCenter(40.0, 20.0);
radialGradient.setFocalPoint(40.0, 20.0);
p.fillRect(QRect(40, 0, 20, 20), radialGradient);
// left
linearGradient.setStart(20, 20);
linearGradient.setFinalStop(0, 20);
p.fillRect(QRect(0, 20, 20, 20), linearGradient);
// bottom left
radialGradient.setCenter(20.0, 40.0);
radialGradient.setFocalPoint(20.0, 40.0);
p.fillRect(QRect(0, 40, 20, 20), radialGradient);
// bottom
linearGradient.setStart(20, 40);
linearGradient.setFinalStop(20, 60);
p.fillRect(QRect(20, 40, 20, 20), linearGradient);
// bottom right
radialGradient.setCenter(40.0, 40.0);
radialGradient.setFocalPoint(40.0, 40.0);
p.fillRect(QRect(40, 40, 20, 20), radialGradient);
// right
linearGradient.setStart(40, 20);
linearGradient.setFinalStop(60, 20);
p.fillRect(QRect(40, 20, 20, 20), linearGradient);
decorationShadow->setShadow(image);
setShadow(decorationShadow);
}
} // namespace
#include "breezedeco.moc"