From b0da2594e91ad2b0b8111c91971b7090ff8ef5cf Mon Sep 17 00:00:00 2001 From: Akseli Lahtinen Date: Thu, 8 Sep 2022 00:13:30 +0300 Subject: [PATCH 1/3] add window outline draw outline for breeze window decoration add setting for enabling/disabling decoration outlines remove outline when using no borders fix outline color values, draw outline on "no borders" setting add breezedecoration.moc to breezedecoration.cpp Apply 1 suggestion(s) to 1 file(s) lower intensity, try to update outline when borders are changed clean up unnecessary blocks try compositionmode_source instead of source_over, remove unnecessary function check if border size has changed and update shadow if needed add breezedecoration.moc again because git loves to drop it for some reason update shadow if decoration settings are changed check for color change and update outline if changed update both active and inactive shadows, give bg color to shadow as parameter Fix top outline having 2px width change valueF to lightnessF fix 2px bottom line in no-borders setting remove unnecessary outline toggle fix accidental removes fix newlines fix more missing newlines, move QPainterPath to cpp file more newline fixes more newline fixes fix newlines, move QPainterPath to cpp file fix newlines fix more missing newlines, move QPainterPath to cpp file more newline fixes more newline fixes Bind the lightness value of the outline Fix drawing the outlines for windows without borders Remove top outline from titlebar, use the new outline instead Change decorationBackgroundColor name to outlineColor Update breezedecoration.cpp and breezedecoration.h Use titlebar color instead of frame color add light transparency to outline --- kdecoration/breezedecoration.cpp | 79 ++++++++++++++++++++++++++------ kdecoration/breezedecoration.h | 2 +- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp index d16dea9d..bcdd3d1d 100644 --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -134,6 +135,8 @@ static int g_shadowStrength = 255; static QColor g_shadowColor = Qt::black; static QSharedPointer g_sShadow; static QSharedPointer 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,20 @@ QPair 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); + // 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); + + 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 +776,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 Decoration::createShadowObject(const float strengthScale) +QSharedPointer 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 +836,40 @@ QSharedPointer 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)); + // Invert background color for outline + QRectF outlineRect = innerRect; + // Titlebar already has an outline, so move the top of the outline on the same level to avoid 2px width on top outline. + outlineRect.setTop(outlineRect.top() + 1); + QPainterPath outlinePath; + + // Paint the outline + const qreal outlineWidth = 2; + + const QPen outlinePen(outlineColor, outlineWidth); + painter.setPen(outlinePen); 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); + + // Draw window outline + // Check if border size is "no borders" or "no side-borders" + outlinePath.addRoundedRect(outlineRect, m_scaledCornerRadius, m_scaledCornerRadius); + if (borderSize(true) == 0) { + // If there is no borders, create bottom rectangle for sharp bottom edges + outlinePath.setFillRule(Qt::WindingFill); + + // Bottom rectangle + const QRectF bottomRect(outlineRect.left(), outlineRect.top() + outlineRect.height() / 2, outlineRect.width(), outlineRect.height() / 2); + + outlinePath.addRect(bottomRect); + + // draw it with simplified so the intersections are removed + painter.drawPath(outlinePath.simplified()); + + } else { + // Draw normal rounded rectangle around the window without simplification + painter.drawPath(outlinePath); + } painter.end(); diff --git a/kdecoration/breezedecoration.h b/kdecoration/breezedecoration.h index 0abc3ac2..f331e2ae 100644 --- a/kdecoration/breezedecoration.h +++ b/kdecoration/breezedecoration.h @@ -112,7 +112,7 @@ private: void createButtons(); void paintTitleBar(QPainter *painter, const QRect &repaintRegion); void updateShadow(); - QSharedPointer createShadowObject(const float strengthScale); + QSharedPointer createShadowObject(const float strengthScale, const QColor &outlineColor); void setScaledCornerRadius(); //*@name border size -- GitLab From 76ee0fac422e0d1f970aa1c344dd54c004115b76 Mon Sep 17 00:00:00 2001 From: Akseli Lahtinen Date: Sun, 23 Oct 2022 02:37:34 +0300 Subject: [PATCH 2/3] fix border thickness and bottom corners (thanks Noah!) --- kdecoration/breezedecoration.cpp | 50 +++++++++++++++----------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp index bcdd3d1d..6c15226f 100644 --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -836,40 +836,38 @@ QSharedPointer Decoration::createShadowObject(co painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); painter.drawRoundedRect(innerRect, m_scaledCornerRadius + 0.5, m_scaledCornerRadius + 0.5); - // Invert background color for outline - QRectF outlineRect = innerRect; + // 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. - outlineRect.setTop(outlineRect.top() + 1); + QRectF outlineRect = innerRect + QMarginsF(penOffset, -penOffset, penOffset, penOffset); + qreal cornerSize = m_scaledCornerRadius * 2; + QRectF cornerRect(outlineRect.x(), outlineRect.y(), cornerSize, cornerSize); QPainterPath outlinePath; - // Paint the outline - const qreal outlineWidth = 2; - - const QPen outlinePen(outlineColor, outlineWidth); - painter.setPen(outlinePen); - painter.setBrush(Qt::NoBrush); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.setRenderHint(QPainter::Antialiasing); + outlinePath.arcMoveTo(cornerRect, 180); + outlinePath.arcTo(cornerRect, 180, -90); + cornerRect.moveTopRight(outlineRect.topRight()); + outlinePath.arcTo(cornerRect, 90, -90); - // Draw window outline // Check if border size is "no borders" or "no side-borders" - outlinePath.addRoundedRect(outlineRect, m_scaledCornerRadius, m_scaledCornerRadius); if (borderSize(true) == 0) { - // If there is no borders, create bottom rectangle for sharp bottom edges - outlinePath.setFillRule(Qt::WindingFill); - - // Bottom rectangle - const QRectF bottomRect(outlineRect.left(), outlineRect.top() + outlineRect.height() / 2, outlineRect.width(), outlineRect.height() / 2); - - outlinePath.addRect(bottomRect); - - // draw it with simplified so the intersections are removed - painter.drawPath(outlinePath.simplified()); - + outlinePath.lineTo(outlineRect.bottomRight()); + outlinePath.lineTo(outlineRect.bottomLeft()); } else { - // Draw normal rounded rectangle around the window without simplification - painter.drawPath(outlinePath); + 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_Source); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPath(outlinePath); painter.end(); -- GitLab From f060aaa7c2f2336313811e92e7213b765731bd00 Mon Sep 17 00:00:00 2001 From: Akseli Lahtinen Date: Sun, 23 Oct 2022 02:59:20 +0300 Subject: [PATCH 3/3] do not draw outlines on very light backgrounds to avoid "blur" effect --- kdecoration/breezedecoration.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp index 6c15226f..36e8beff 100644 --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -752,14 +752,23 @@ 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); - - outlineColor.lightnessF() >= 0.5 ? outlineColor = outlineColor.darker(130) : outlineColor = outlineColor.lighter(130); + // 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)) { -- GitLab