Add outlines to window decoration

This commit adds outlines to window decorations. This will help users who have
a hard time distinguishing overlapping windows from each other, especially with
dark themes that can be quite strong and make the shadows difficult to see.

The outline takes the background color of the window and inverts it, so it should match any background color pretty well.
wilder-reb-27
Akseli Lahtinen 3 years ago committed by Nate Graham
parent 46d194f6b7
commit 8a91ef78d5
  1. 86
      kdecoration/breezedecoration.cpp
  2. 2
      kdecoration/breezedecoration.h

@ -31,6 +31,7 @@
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#include <QPainter>
#include <QPainterPath>
#include <QTextStream>
#include <QTimer>
@ -134,6 +135,8 @@ static int g_shadowStrength = 255;
static QColor g_shadowColor = Qt::black;
static QSharedPointer<KDecoration2::DecorationShadow> g_sShadow;
static QSharedPointer<KDecoration2::DecorationShadow> g_sShadowInactive;
static int g_lastBorderSize;
static QColor g_lastOutlineColor;
//________________________________________________________________
Decoration::Decoration(QObject *parent, const QVariantList &args)
@ -464,6 +467,9 @@ void Decoration::recalculateBorders()
}
setResizeOnlyBorders(QMargins(extSides, 0, extSides, extBottom));
// Update shadows and clear outline to make sure outline changes when borders are changed
updateShadow();
}
//________________________________________________________________
@ -583,11 +589,11 @@ void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
void Decoration::paintTitleBar(QPainter *painter, const QRect &repaintRegion)
{
const auto c = client().toStrongRef();
const QRect frontRect(QPoint(0, 1), QSize(size().width(), borderTop() + 1));
const QRect backRect(QPoint(0, 0), QSize(size().width(), borderTop()));
const QRect frontRect(QPoint(0, 1), QSize(size().width(), borderTop()));
const QRect backRect(QPoint(0, 1), QSize(size().width(), borderTop() - 1));
QBrush frontBrush;
QBrush backBrush(this->titleBarColor().lighter(130));
QBrush backBrush(this->titleBarColor());
if (!backRect.intersects(repaintRegion))
return;
@ -744,9 +750,29 @@ QPair<QRect, Qt::Alignment> Decoration::captionRect() const
void Decoration::updateShadow()
{
auto s = settings();
auto c = client().toStrongRef();
auto outlineColor = c->color(c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::TitleBar);
auto backgroundColor = c->color(c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::Frame);
// Bind lightness between 0.1 and 1.0 so it can never be completely black.
// Outlines only have transparency if alpha channel is supported
outlineColor.setHslF(outlineColor.hslHueF(),
outlineColor.hslSaturationF(),
qBound(0.1, outlineColor.lightnessF(), 1.0),
s->isAlphaChannelSupported() ? 0.9 : 1.0);
// If outlinecolor is very close to the window backgroundcolor, the shadow coloring should be enough,
// so we use the background color as the outline as well.
// This is only checked for very light colors, like the default Breeze Light (#e5e6e7)
// If not, we just modify the lightness like usual
if (!lookupShadowParams(m_internalSettings->shadowSize()).isNone() && backgroundColor.lightnessF() >= 0.85
&& (backgroundColor.lightnessF() - outlineColor.lightnessF()) <= 0.05f) {
outlineColor = backgroundColor;
} else {
outlineColor.lightnessF() >= 0.5 ? outlineColor = outlineColor.darker(130) : outlineColor = outlineColor.lighter(130);
}
// Animated case, no cached shadow object
if ((m_shadowAnimation->state() == QAbstractAnimation::Running) && (m_shadowOpacity != 0.0) && (m_shadowOpacity != 1.0)) {
setShadow(createShadowObject(0.5 + m_shadowOpacity * 0.5));
setShadow(createShadowObject(0.5 + m_shadowOpacity * 0.5, outlineColor));
return;
}
@ -759,20 +785,25 @@ void Decoration::updateShadow()
g_shadowColor = m_internalSettings->shadowColor();
}
auto c = client().toStrongRef();
auto &shadow = (c->isActive()) ? g_sShadow : g_sShadowInactive;
if (!shadow) {
shadow = createShadowObject(c->isActive() ? 1.0 : 0.5);
if (!shadow || g_lastBorderSize != borderSize(true) || g_lastOutlineColor != outlineColor) {
// Update both active and inactive shadows so outline stays consistent between the two
g_sShadow = createShadowObject(1.0, outlineColor);
g_sShadowInactive = createShadowObject(0.5, outlineColor);
g_lastBorderSize = borderSize(true);
g_lastOutlineColor = outlineColor;
}
setShadow(shadow);
}
//________________________________________________________________
QSharedPointer<KDecoration2::DecorationShadow> Decoration::createShadowObject(const float strengthScale)
QSharedPointer<KDecoration2::DecorationShadow> Decoration::createShadowObject(const float strengthScale, const QColor &outlineColor)
{
const CompositeShadowParams params = lookupShadowParams(m_internalSettings->shadowSize());
CompositeShadowParams params = lookupShadowParams(m_internalSettings->shadowSize());
if (params.isNone()) {
return nullptr;
// If shadows are disabled, set shadow opacity to 0.
// This allows the outline effect to show up without the shadow effect.
params = CompositeShadowParams(QPoint(0, 4), ShadowParams(QPoint(0, 0), 16, 0), ShadowParams(QPoint(0, -2), 8, 0));
}
auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
@ -814,11 +845,38 @@ QSharedPointer<KDecoration2::DecorationShadow> Decoration::createShadowObject(co
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
painter.drawRoundedRect(innerRect, m_scaledCornerRadius + 0.5, m_scaledCornerRadius + 0.5);
// Draw outline.
painter.setPen(withOpacity(Qt::black, 0.1));
// Draw window outline
const qreal outlineWidth = 1.001;
const qreal penOffset = outlineWidth / 2;
// Titlebar already has an outline, so move the top of the outline on the same level to avoid 2px width on top outline.
QRectF outlineRect = innerRect + QMarginsF(penOffset, -penOffset, penOffset, penOffset);
qreal cornerSize = m_scaledCornerRadius * 2;
QRectF cornerRect(outlineRect.x(), outlineRect.y(), cornerSize, cornerSize);
QPainterPath outlinePath;
outlinePath.arcMoveTo(cornerRect, 180);
outlinePath.arcTo(cornerRect, 180, -90);
cornerRect.moveTopRight(outlineRect.topRight());
outlinePath.arcTo(cornerRect, 90, -90);
// Check if border size is "no borders" or "no side-borders"
if (borderSize(true) == 0) {
outlinePath.lineTo(outlineRect.bottomRight());
outlinePath.lineTo(outlineRect.bottomLeft());
} else {
cornerRect.moveBottomRight(outlineRect.bottomRight());
outlinePath.arcTo(cornerRect, 0, -90);
cornerRect.moveBottomLeft(outlineRect.bottomLeft());
outlinePath.arcTo(cornerRect, 270, -90);
}
outlinePath.closeSubpath();
painter.setPen(QPen(outlineColor, outlineWidth));
painter.setBrush(Qt::NoBrush);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawRoundedRect(innerRect, m_scaledCornerRadius - 0.5, m_scaledCornerRadius - 0.5);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setRenderHint(QPainter::Antialiasing);
painter.drawPath(outlinePath);
painter.end();

@ -112,7 +112,7 @@ private:
void createButtons();
void paintTitleBar(QPainter *painter, const QRect &repaintRegion);
void updateShadow();
QSharedPointer<KDecoration2::DecorationShadow> createShadowObject(const float strengthScale);
QSharedPointer<KDecoration2::DecorationShadow> createShadowObject(const float strengthScale, const QColor &outlineColor);
void setScaledCornerRadius();
//*@name border size

Loading…
Cancel
Save