Merge branch 'graesslin/kdecoration2'

Brings the new kdecoration2 based window decoration.
wilder-pre-rebase
Martin Gräßlin 11 years ago
commit 6e7059c9a8
  1. 3
      windec/CMakeLists.txt
  2. 22
      windec/kdecoration2/CMakeLists.txt
  3. 12
      windec/kdecoration2/breeze.json
  4. 434
      windec/kdecoration2/breezebuttons.cpp
  5. 102
      windec/kdecoration2/breezebuttons.h
  6. 352
      windec/kdecoration2/breezedeco.cpp
  7. 123
      windec/kdecoration2/breezedeco.h

@ -13,12 +13,11 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(KDEInstallDirs)
include(KDECompilerSettings)
include(KDECMakeSettings)
#find_package(KDecorations REQUIRED)
#find_package(KF5ConfigWidgets REQUIRED)
#find_package(Qt5 CONFIG REQUIRED COMPONENTS Core DBus Quick Widgets Concurrent X11Extras Test UiTools)
#add_subdirectory(code)
add_subdirectory(kdecoration2)
install(DIRECTORY package/
DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/decorations/kwin4_decoration_qml_breeze)

@ -0,0 +1,22 @@
find_package(KDecoration2 REQUIRED)
find_package(KF5 REQUIRED COMPONENTS CoreAddons Config)
set(breezedecoration_SRCS
breezedeco.cpp
breezebuttons.cpp
)
add_library(breezedecoration MODULE ${breezedecoration_SRCS})
target_link_libraries(breezedecoration
PUBLIC
Qt5::Core
Qt5::Gui
PRIVATE
KDecoration2::KDecoration
KF5::ConfigCore
KF5::CoreAddons
)
install(TARGETS breezedecoration DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2)

@ -0,0 +1,12 @@
{
"Type": "Service",
"X-KDE-Library": "breezedecoration",
"X-KDE-PluginInfo-EnabledByDefault": true,
"X-KDE-PluginInfo-Name": "org.kde.breeze",
"X-KDE-ServiceTypes": [
"org.kde.kdecoration2"
],
"org.kde.kdecoration2": {
"blur": false
}
}

@ -0,0 +1,434 @@
/*
* 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 "breezebuttons.h"
#include <KDecoration2/DecoratedClient>
#include <QPainter>
uint qHash(const QPalette &pal)
{
QByteArray byteArray;
QDataStream stream(&byteArray, QIODevice::WriteOnly);
stream << pal;
return byteArray.toBase64().toUInt();
}
namespace Breeze
{
ImageProvider *ImageProvider::s_self = nullptr;
ImageProvider::ImageProvider() = default;
ImageProvider::~ImageProvider()
{
s_self = nullptr;
}
ImageProvider *ImageProvider::self()
{
// TODO: this is not thread safe!
if (!s_self) {
s_self = new ImageProvider();
}
return s_self;
}
void ImageProvider::invalidate()
{
m_images.clear();
}
static ButtonState stateForButton(Button *decorationButton)
{
if (!decorationButton->isEnabled()) {
return ButtonState::Disabled;
}
if (decorationButton->isChecked()) {
if (decorationButton->isPressed()) {
return ButtonState::CheckedPressed;
}
if (decorationButton->isHovered()) {
return ButtonState::CheckedHovered;
}
return ButtonState::Checked;
}
if (decorationButton->isPressed()) {
return ButtonState::Pressed;
}
if (decorationButton->isHovered()) {
return ButtonState::Hovered;
}
if (decorationButton->isStandAlone()) {
return ButtonState::Preview;
}
return ButtonState::Normal;
}
QImage ImageProvider::button(Breeze::Button *decorationButton)
{
auto paletteIt = m_images.begin();
if (!decorationButton->decoration()) {
return QImage();
}
auto client = decorationButton->decoration()->client().data();
if (paletteIt == m_images.end() || paletteIt.key() != client->palette()) {
paletteIt = m_images.find(client->palette());
}
if (paletteIt == m_images.end()) {
const QPalette pal = client->palette();
m_images.insert(pal, ImagesForButton());
m_colorSettings.append(ColorSettings(pal));
paletteIt = m_images.find(client->palette());
}
Q_ASSERT(paletteIt != m_images.end());
auto it = paletteIt.value().find(decorationButton->type());
if (it == paletteIt.value().end()) {
auto hash = ImagesForDecoState();
hash.insert(true, ImagesForButtonState());
hash.insert(false, ImagesForButtonState());
paletteIt.value().insert(decorationButton->type(), hash);
it = paletteIt.value().find(decorationButton->type());
}
Q_ASSERT(it != paletteIt.value().end());
auto it2 = it.value().find(client->isActive());
Q_ASSERT(it2 != it.value().end());
const ButtonState state = stateForButton(decorationButton);
auto it3 = it2.value().find(state);
if (it3 == it2.value().end()) {
QImage image = renderButton(decorationButton);
it2.value().insert(state, image);
it3 = it2.value().find(state);
}
Q_ASSERT(it3 != it2.value().end());
return it3.value();
}
void ImageProvider::clearCache(Breeze::Button *decorationButton)
{
auto paletteIt = m_images.begin();
if (!decorationButton->decoration()) {
return;
}
const QPalette &palette = decorationButton->decoration()->client().data()->palette();
if (paletteIt == m_images.end() || paletteIt.key() != palette) {
paletteIt = m_images.find(palette);
}
if (paletteIt == m_images.end()) {
return;
}
auto it = paletteIt.value().find(decorationButton->type());
if (it == paletteIt.value().end()) {
return;
}
paletteIt.value().erase(it);
}
ColorSettings ImageProvider::colorSettings(const QPalette &pal) const
{
for (const ColorSettings &colorSettings : m_colorSettings) {
if (colorSettings.palette() == pal) {
return colorSettings;
}
}
Q_ASSERT(false);
return ColorSettings(pal);
}
ColorSettings ImageProvider::colorSettings(Breeze::Button *decorationButton) const
{
if (!decorationButton->decoration()) {
return colorSettings(QPalette());
}
return colorSettings(decorationButton->decoration()->client().data()->palette());
}
QImage ImageProvider::renderButton(Breeze::Button *decorationButton) const
{
QImage image(decorationButton->size().toSize(), QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
switch (decorationButton->type()) {
case KDecoration2::DecorationButtonType::Close:
renderCloseButton(&p, decorationButton);
break;
case KDecoration2::DecorationButtonType::Maximize:
renderMaximizeButton(&p, decorationButton);
break;
case KDecoration2::DecorationButtonType::OnAllDesktops:
renderOnAllDesktopsButton(&p, decorationButton);
break;
case KDecoration2::DecorationButtonType::Shade:
renderShadeButton(&p, decorationButton);
break;
case KDecoration2::DecorationButtonType::Minimize:
drawGenericButtonBackground(&p, decorationButton);
drawDownArrow(&p, decorationButton);
break;
case KDecoration2::DecorationButtonType::KeepBelow:
// TODO: Needs a checked state
drawGenericButtonBackground(&p, decorationButton);
drawDownArrow(&p, decorationButton, QPointF(0.0, -2.0));
drawDownArrow(&p, decorationButton, QPointF(0.0, 2.0));
break;
case KDecoration2::DecorationButtonType::KeepAbove:
// TODO: Needs a checked state
drawGenericButtonBackground(&p, decorationButton);
drawUpArrow(&p, decorationButton, QPointF(0.0, -2.0));
drawUpArrow(&p, decorationButton, QPointF(0.0, 2.0));
break;
default:
break;
}
return image;
}
void ImageProvider::renderCloseButton(QPainter *painter, Breeze::Button *decorationButton) const
{
if (!decorationButton->decoration()) {
return;
}
auto client = decorationButton->decoration()->client().data();
const bool active = client->isActive();
const QPalette &pal = client->palette();
const bool pressed = decorationButton->isPressed();
const bool hovered = decorationButton->isHovered();
const QSize &size = decorationButton->size().toSize();
const QColor pressedColor = QColor(237, 21, 21);
const QColor backgroundColor = pressed ? pressedColor : hovered ? pressedColor.lighter() : colorSettings(pal).font(active);
drawBackground(painter, decorationButton, backgroundColor);
// draw the X
QPen pen(hovered || pressed ? colorSettings(pal).font(active) : colorSettings(pal).titleBarColor(active));
pen.setWidth(2);
painter->setPen(pen);
painter->translate(size.width() / 2.0, size.height() / 2.0);
painter->rotate(45.0);
painter->drawLine(0, -size.height() / 4, 0, size.height() / 4);
painter->drawLine(-size.width() / 4, 0, size.width() / 4, 0);
}
void ImageProvider::renderMaximizeButton(QPainter *painter, Breeze::Button *decorationButton) const
{
painter->save();
drawGenericButtonBackground(painter, decorationButton);
QPen pen(foregroundColor(decorationButton));
if (decorationButton->isChecked()) {
// restore button
const qreal width = (decorationButton->size().height() - 5) / 4;
painter->translate(decorationButton->size().width() / 2.0, decorationButton->size().height() / 2.0);
painter->rotate(45.0);
pen.setWidth(2);
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
painter->drawRect(-width, -width, width * 2, width * 2);
} else {
// maximize button
drawUpArrow(painter, decorationButton);
}
painter->restore();
}
void ImageProvider::renderOnAllDesktopsButton(QPainter *painter, Breeze::Button *decorationButton) const
{
if (!decorationButton->decoration()) {
return;
}
const bool active = decorationButton->decoration()->client().data()->isActive();
painter->save();
drawGenericButtonBackground(painter, decorationButton);
if (decorationButton->isChecked()) {
// on all desktops
painter->setPen(Qt::NoPen);
painter->setBrush(foregroundColor(decorationButton));
painter->translate(decorationButton->size().width() / 2.0, decorationButton->size().height() / 2.0);
const int radius = decorationButton->size().width() / 2 - 3;
painter->drawEllipse(-radius, -radius, radius * 2, radius * 2);
painter->setBrush(decorationButton->isHovered() ? colorSettings(decorationButton).font(active) : colorSettings(decorationButton).titleBarColor(active));
painter->drawEllipse(-1, -1, 2, 2);
} else {
// not on all desktops
// TODO: implement the pin
}
painter->restore();
}
void ImageProvider::renderShadeButton(QPainter *painter, Breeze::Button *decorationButton) const
{
drawGenericButtonBackground(painter, decorationButton);
painter->save();
QPen pen(foregroundColor(decorationButton));
pen.setWidth(2);
painter->setPen(pen);
painter->translate(decorationButton->size().width() / 2.0, decorationButton->size().height() / 2.0);
const qreal width = decorationButton->size().width() /2.0 - 3.0;
painter->drawLine(-width, -4.0, width, -4.0);
painter->restore();
if (decorationButton->isChecked()) {
drawDownArrow(painter, decorationButton, QPointF(0.0, 2.0));
} else {
drawUpArrow(painter, decorationButton, QPointF(0.0, 2.0));
}
}
void ImageProvider::drawGenericButtonBackground(QPainter *painter, Breeze::Button *decorationButton) const
{
if (!decorationButton->decoration()) {
return;
}
const bool standAlone = decorationButton->isStandAlone();
if (!decorationButton->isPressed() && !decorationButton->isHovered() && !standAlone) {
return;
}
const QColor baseBackgroundColor = colorSettings(decorationButton).font(decorationButton->decoration()->client().data()->isActive());
drawBackground(painter, decorationButton, QColor(baseBackgroundColor.red(),
baseBackgroundColor.green(),
baseBackgroundColor.blue(),
decorationButton->isPressed() ? 50 : standAlone ? 255 : 127));
}
void ImageProvider::drawBackground(QPainter *painter, Breeze::Button *decorationButton, const QColor &color) const
{
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(decorationButton->isEnabled() ? color : QColor(color.red(), color.green(), color.blue(), color.alpha() * 0.6));
painter->drawEllipse(QRectF(QPointF(0, 0), decorationButton->size()));
painter->restore();
}
void ImageProvider::drawDownArrow(QPainter *painter, Breeze::Button *decorationButton, const QPointF &offset) const
{
painter->save();
QPen pen(foregroundColor(decorationButton));
// TODO: where do the magic values come from?
const qreal width = (decorationButton->size().height() - 5) / 2;
pen.setWidth(2);
painter->setPen(pen);
painter->translate(QPointF(decorationButton->size().width() / 2.0, decorationButton->size().height() / 2.0) + offset + QPointF(0.0, 0.707*width/2.0));
painter->rotate(45.0);
painter->drawLine(0, -width, 0, 0);
painter->rotate(-90.0);
painter->drawLine(0, -width, 0, 0);
painter->restore();
}
void ImageProvider::drawUpArrow(QPainter *painter, Breeze::Button *decorationButton, const QPointF &offset) const
{
painter->save();
QPen pen(foregroundColor(decorationButton));
// TODO: where do the magic values come from?
const qreal width = (decorationButton->size().height() - 5) / 2;
pen.setWidth(2);
painter->setPen(pen);
painter->translate(QPointF(decorationButton->size().width() / 2.0, decorationButton->size().height() / 2.0) + offset - QPointF(0.0, 0.707*width/2.0));
painter->rotate(225.0);
painter->drawLine(0, 0, 0, -width);
painter->rotate(-90.0);
painter->drawLine(0, 0, 0, -width);
painter->restore();
}
QColor ImageProvider::foregroundColor(Breeze::Button *decorationButton) const
{
if (!decorationButton->decoration()) {
return QColor();
}
const auto client = decorationButton->decoration()->client().data();
const ColorSettings &colors = colorSettings(client->palette());
const bool active = client->isActive();
if (decorationButton->isStandAlone()) {
return colors.titleBarColor(active);
}
if (decorationButton->isHovered()) {
return colors.titleBarColor(active);
}
QColor c = colors.font(active);
if (!decorationButton->isEnabled()) {
c.setAlphaF(c.alphaF() * 0.6);
}
return c;
}
Button::Button(KDecoration2::DecorationButtonType type, Decoration* decoration, QObject* parent)
: DecorationButton(type, decoration, parent)
{
const int height = decoration->captionHeight();
setGeometry(QRect(0, 0, height, height));
connect(decoration, &Decoration::bordersChanged, this, [this, decoration] {
const int height = decoration->captionHeight();
if (height == geometry().height()) {
return;
}
ImageProvider::self()->clearCache(this);
setGeometry(QRectF(geometry().topLeft(), QSizeF(height, height)));
});
}
Button::Button(QObject *parent, const QVariantList &args)
: DecorationButton(args.at(0).value<KDecoration2::DecorationButtonType>(), args.at(1).value<Decoration*>(), parent)
, m_standalone(true)
{
}
Button *Button::create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent)
{
if (Decoration *d = qobject_cast<Decoration*>(decoration)) {
Button *b = new Button(type, d, parent);
if (type == KDecoration2::DecorationButtonType::Menu) {
QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::iconChanged, b, [b]() { b->update(); });
}
return b;
}
return nullptr;
}
Button::~Button() = default;
void Button::paint(QPainter *painter, const QRect &repaintRegion)
{
Q_UNUSED(repaintRegion)
if (!decoration()) {
return;
}
// TODO: optimize based on repaintRegion
if (type() == KDecoration2::DecorationButtonType::Menu) {
const QPixmap pixmap = decoration()->client().data()->icon().pixmap(size().toSize());
painter->drawPixmap(geometry().center() - QPoint(pixmap.width()/2, pixmap.height()/2), pixmap);
} else {
painter->drawImage(geometry().topLeft(), ImageProvider::self()->button(this));
}
}
} // namespace

@ -0,0 +1,102 @@
/*
* 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/>.
*/
#ifndef BREEZE_BUTTONS_H
#define BREEZE_BUTTONS_H
#include <KDecoration2/DecorationButton>
#include "breezedeco.h"
#include <QHash>
#include <QImage>
uint qHash(const QPalette &pal);
namespace Breeze
{
class Button;
enum class ButtonState : uint {
Normal,
Disabled,
Hovered,
Pressed,
Checked,
CheckedHovered,
CheckedPressed,
Preview
};
inline uint qHash(const ButtonState &state) {
return static_cast<uint>(state);
}
class ImageProvider final
{
public:
~ImageProvider();
static ImageProvider *self();
QImage button(Button *decorationButton);
void clearCache(Button *decorationButton);
void invalidate();
private:
ImageProvider();
QImage renderButton(Button *decorationButton) const;
void renderCloseButton(QPainter *p, Button *decorationButton) const;
void renderShadeButton(QPainter *p, Button *decorationButton) const;
void renderMaximizeButton(QPainter *p, Button *decorationButton) const;
void renderOnAllDesktopsButton(QPainter *p, Button *decorationButton) const;
void drawGenericButtonBackground(QPainter *painter, Button *decorationButton) const;
void drawBackground(QPainter *painter, Button *decorationButton, const QColor &color) const;
void drawDownArrow(QPainter *painter, Button *decorationButton, const QPointF &offset = QPointF(0.0, 0.0)) const;
void drawUpArrow(QPainter *painter, Button *decorationButton, const QPointF &offset = QPointF(0.0, 0.0)) const;
QColor foregroundColor(Button *decorationButton) const;
ColorSettings colorSettings(Button *decorationButton) const;
ColorSettings colorSettings(const QPalette &pal) const;
static ImageProvider *s_self;
typedef QHash<ButtonState, QImage> ImagesForButtonState;
typedef QHash<bool, ImagesForButtonState> ImagesForDecoState;
typedef QHash<KDecoration2::DecorationButtonType, ImagesForDecoState> ImagesForButton;
QHash<QPalette, ImagesForButton> m_images;
QList<ColorSettings> m_colorSettings;
};
class Button : public KDecoration2::DecorationButton
{
Q_OBJECT
public:
explicit Button(QObject *parent, const QVariantList &args);
virtual ~Button();
void paint(QPainter *painter, const QRect &repaintRegion) override;
static Button *create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent);
bool isStandAlone() const {
return m_standalone;
}
private:
explicit Button(KDecoration2::DecorationButtonType type, Decoration *decoration, QObject *parent = nullptr);
bool m_standalone = false;
};
} // namespace
#endif

@ -0,0 +1,352 @@
/*
* 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>();
registerPlugin<Breeze::Button>(QStringLiteral("button"));
)
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().data()->palette())
, m_leftButtons(nullptr)
, m_rightButtons(nullptr)
{
}
Decoration::~Decoration() = default;
void Decoration::init()
{
recalculateBorders();
updateTitleBar();
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::adjacentScreenEdgesChanged, 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().data()->palette());
update();
}
);
connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleBar);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleBar);
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::updateTitleBar()
{
auto s = settings();
const bool maximized = client().data()->isMaximized();
const int width = client().data()->width();
const int height = maximized ? borderTop() : borderTop() - s->smallSpacing();
const int x = maximized ? 0 : s->largeSpacing() / 2;
const int y = maximized ? 0 : s->smallSpacing();
setTitleBar(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 auto c = client().data();
const Qt::Edges edges = c->adjacentScreenEdges();
int left = c->isMaximizedHorizontally() || edges.testFlag(Qt::LeftEdge) ? 0 : borderSize(s);
int right = c->isMaximizedHorizontally() || edges.testFlag(Qt::RightEdge) ? 0 : borderSize(s);
QFontMetrics fm(s->font());
int top = qMax(fm.boundingRect(c->caption()).height(), s->gridUnit() * 2);
// padding below
top += s->smallSpacing() * 2 + 1;
if (!c->isMaximized()) {
// padding above only on maximized
top += s->smallSpacing();
}
int bottom = c->isMaximizedVertically() || edges.testFlag(Qt::BottomEdge) ? 0 : borderSize(s, true);
setBorders(QMargins(left, top, right, 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;
}
setResizeOnlyBorders(QMargins(extSides, 0, extSides, 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().data()->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, const QRect &repaintRegion)
{
// TODO: optimize based on repaintRegion
// paint background
painter->fillRect(rect(), Qt::transparent);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::NoPen);
painter->setBrush(m_colorSettings.frame(client().data()->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, repaintRegion);
painter->restore();
}
void Decoration::paintTitleBar(QPainter *painter, const QRect &repaintRegion)
{
const auto c = client().data();
const bool active = c->isActive();
const QRect titleRect(QPoint(0, 0), QSize(size().width(), borderTop()));
const QColor titleBarColor(m_colorSettings.titleBarColor(c->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(c->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 (c->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,
c->palette().color(active ? QPalette::Highlight: QPalette::Background));
}
// draw title bar spacer
painter->fillRect(0, borderTop() - titleBarSpacer, size().width(), titleBarSpacer, c->palette().color(QPalette::Background));
// draw caption
painter->setFont(s->font());
const QRect cR = captionRect();
const QString caption = painter->fontMetrics().elidedText(c->caption(), Qt::ElideMiddle, cR.width());
painter->setPen(m_colorSettings.font(c->isActive()));
painter->drawText(cR, Qt::AlignCenter | Qt::TextSingleLine, caption);
// draw all buttons
m_leftButtons->paint(painter, repaintRegion);
m_rightButtons->paint(painter, repaintRegion);
}
int Decoration::captionHeight() const
{
return borderTop() - settings()->smallSpacing() * (client().data()->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().data()->isMaximized() ? 0 : settings()->smallSpacing();
// below is the spacer
return QRect(offset, yOffset, size().width() - offset * 2, captionHeight());
}
void Decoration::createShadow()
{
auto decorationShadow = QSharedPointer<KDecoration2::DecorationShadow>::create();
decorationShadow->setPadding(QMargins(10, 10, 20, 20));
decorationShadow->setInnerShadowRect(QRect(20, 20, 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"

@ -0,0 +1,123 @@
/*
* 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/>.
*/
#ifndef BREEZE_DECORATION_H
#define BREEZE_DECORATION_H
#include <KDecoration2/Decoration>
#include <QColor>
#include <QPalette>
#include <QVariant>
namespace KDecoration2
{
class DecorationButton;
class DecorationButtonGroup;
}
namespace Breeze
{
// TODO: move to deco API
class ColorSettings
{
public:
ColorSettings(const QPalette &pal);
void update(const QPalette &pal);
const QColor &titleBarColor(bool active) const {
return active ? m_activeTitleBarColor : m_inactiveTitleBarColor;
}
const QColor &activeTitleBarColor() const {
return m_activeTitleBarColor;
}
const QColor &inactiveTitleBarColor() const {
return m_inactiveTitleBarColor;
}
const QColor &frame(bool active) const {
return active ? m_activeFrameColor : m_inactiveFrameColor;
}
const QColor &activeFrame() const {
return m_activeFrameColor;
}
const QColor &inactiveFrame() const {
return m_inactiveFrameColor;
}
const QColor &font(bool active) const {
return active ? m_activeFontColor : m_inactiveFontColor;
}
const QColor &activeFont() const {
return m_activeFontColor;
}
const QColor &inactiveFont() const {
return m_inactiveFontColor;
}
const QPalette &palette() const {
return m_palette;
}
private:
void init(const QPalette &pal);
QColor m_activeTitleBarColor;
QColor m_inactiveTitleBarColor;
QColor m_activeFrameColor;
QColor m_inactiveFrameColor;
QColor m_activeFontColor;
QColor m_inactiveFontColor;
QPalette m_palette;
};
class Decoration : public KDecoration2::Decoration
{
Q_OBJECT
public:
explicit Decoration(QObject *parent = nullptr, const QVariantList &args = QVariantList());
virtual ~Decoration();
void paint(QPainter *painter, const QRect &repaintRegion) override;
const ColorSettings &colorSettings() {
return m_colorSettings;
}
int captionHeight() const;
public Q_SLOTS:
void init() override;
private Q_SLOTS:
void recalculateBorders();
void updateButtonPositions();
void updateTitleBar();
private:
QRect captionRect() const;
void createButtons();
void paintTitleBar(QPainter *painter, const QRect &repaintRegion);
void createShadow();
ColorSettings m_colorSettings;
QList<KDecoration2::DecorationButton*> m_buttons;
KDecoration2::DecorationButtonGroup *m_leftButtons;
KDecoration2::DecorationButtonGroup *m_rightButtons;
};
} // namespace
#endif
Loading…
Cancel
Save