Brings the new kdecoration2 based window decoration.wilder-pre-rebase
commit
6e7059c9a8
7 changed files with 1046 additions and 2 deletions
@ -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…
Reference in new issue