You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1669 lines
57 KiB
1669 lines
57 KiB
/* |
|
* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr> |
|
* |
|
* SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "breezehelper.h" |
|
|
|
#include "breeze.h" |
|
#include "breezestyleconfigdata.h" |
|
|
|
#include <KColorScheme> |
|
#include <KColorUtils> |
|
#include <KIconLoader> |
|
#include <KWindowSystem> |
|
|
|
#include <QApplication> |
|
#include <QDBusConnection> |
|
#include <QDockWidget> |
|
#include <QFileInfo> |
|
#include <QMainWindow> |
|
#include <QMdiArea> |
|
#include <QMenuBar> |
|
#include <QPainter> |
|
#include <QWindow> |
|
|
|
#if BREEZE_HAVE_QTX11EXTRAS |
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
#include <private/qtx11extras_p.h> |
|
#else |
|
#include <QX11Info> |
|
#endif |
|
#endif |
|
|
|
#include <QDialog> |
|
#include <algorithm> |
|
|
|
namespace Breeze |
|
{ |
|
//* contrast for arrow and treeline rendering |
|
static const qreal arrowShade = 0.15; |
|
|
|
static const qreal highlightBackgroundAlpha = 0.33; |
|
|
|
static const auto radioCheckSunkenDarkeningFactor = 110; |
|
|
|
//____________________________________________________________________ |
|
Helper::Helper(KSharedConfig::Ptr config, QObject *parent) |
|
: QObject(parent) |
|
, _config(std::move(config)) |
|
, _kwinConfig(KSharedConfig::openConfig("kwinrc")) |
|
, _decorationConfig(new InternalSettings()) |
|
{ |
|
if (qApp) { |
|
connect(qApp, &QApplication::paletteChanged, this, [=]() { |
|
if (!qApp->property("KDE_COLOR_SCHEME_PATH").isValid()) { |
|
return; |
|
} |
|
const auto path = qApp->property("KDE_COLOR_SCHEME_PATH").toString(); |
|
if (!path.isEmpty()) { |
|
KConfig config(path, KConfig::SimpleConfig); |
|
KConfigGroup group(config.group("WM")); |
|
const QPalette palette(QApplication::palette()); |
|
_activeTitleBarColor = group.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight)); |
|
_activeTitleBarTextColor = group.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText)); |
|
_inactiveTitleBarColor = group.readEntry("inactiveBackground", palette.color(QPalette::Disabled, QPalette::Highlight)); |
|
_inactiveTitleBarTextColor = group.readEntry("inactiveForeground", palette.color(QPalette::Disabled, QPalette::HighlightedText)); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
//____________________________________________________________________ |
|
KSharedConfig::Ptr Helper::config() const |
|
{ |
|
return _config; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QSharedPointer<InternalSettings> Helper::decorationConfig() const |
|
{ |
|
return _decorationConfig; |
|
} |
|
|
|
//____________________________________________________________________ |
|
void Helper::loadConfig() |
|
{ |
|
_viewFocusBrush = KStatefulBrush(KColorScheme::View, KColorScheme::FocusColor); |
|
_viewHoverBrush = KStatefulBrush(KColorScheme::View, KColorScheme::HoverColor); |
|
_buttonFocusBrush = KStatefulBrush(KColorScheme::Button, KColorScheme::FocusColor); |
|
_buttonHoverBrush = KStatefulBrush(KColorScheme::Button, KColorScheme::HoverColor); |
|
_viewNegativeTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeText); |
|
_viewNeutralTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText); |
|
|
|
const QPalette palette(QApplication::palette()); |
|
_config->reparseConfiguration(); |
|
_kwinConfig->reparseConfiguration(); |
|
_cachedAutoValid = false; |
|
_decorationConfig->load(); |
|
|
|
KConfigGroup globalGroup(_config->group("WM")); |
|
_activeTitleBarColor = globalGroup.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight)); |
|
_activeTitleBarTextColor = globalGroup.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText)); |
|
_inactiveTitleBarColor = globalGroup.readEntry("inactiveBackground", palette.color(QPalette::Disabled, QPalette::Highlight)); |
|
_inactiveTitleBarTextColor = globalGroup.readEntry("inactiveForeground", palette.color(QPalette::Disabled, QPalette::HighlightedText)); |
|
|
|
if (const QString colorSchemePath = qApp->property("KDE_COLOR_SCHEME_PATH").toString(); !colorSchemePath.isEmpty()) { |
|
KConfig config(colorSchemePath, KConfig::SimpleConfig); |
|
KConfigGroup appGroup(config.group("WM")); |
|
_activeTitleBarColor = appGroup.readEntry("activeBackground", _activeTitleBarColor); |
|
_activeTitleBarTextColor = appGroup.readEntry("activeForeground", _activeTitleBarTextColor); |
|
_inactiveTitleBarColor = appGroup.readEntry("inactiveBackground", _inactiveTitleBarColor); |
|
_inactiveTitleBarTextColor = appGroup.readEntry("inactiveForeground", _inactiveTitleBarTextColor); |
|
} |
|
} |
|
|
|
QColor transparentize(const QColor &color, qreal amount) |
|
{ |
|
auto clone = color; |
|
clone.setAlphaF(amount); |
|
return clone; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::frameOutlineColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor outline(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.25)); |
|
|
|
// focus takes precedence over hover |
|
if (mode == AnimationFocus) { |
|
const QColor focus(focusColor(palette)); |
|
const QColor hover(hoverColor(palette)); |
|
|
|
if (mouseOver) { |
|
outline = KColorUtils::mix(hover, focus, opacity); |
|
} else { |
|
outline = KColorUtils::mix(outline, focus, opacity); |
|
} |
|
|
|
} else if (hasFocus) { |
|
outline = focusColor(palette); |
|
|
|
} else if (mode == AnimationHover) { |
|
const QColor hover(hoverColor(palette)); |
|
outline = KColorUtils::mix(outline, hover, opacity); |
|
|
|
} else if (mouseOver) { |
|
outline = hoverColor(palette); |
|
} |
|
|
|
return outline; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::focusOutlineColor(const QPalette &palette) const |
|
{ |
|
return KColorUtils::mix(focusColor(palette), palette.color(QPalette::WindowText), 0.15); |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::hoverOutlineColor(const QPalette &palette) const |
|
{ |
|
return KColorUtils::mix(hoverColor(palette), palette.color(QPalette::WindowText), 0.15); |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::buttonFocusOutlineColor(const QPalette &palette) const |
|
{ |
|
return KColorUtils::mix(buttonFocusColor(palette), palette.color(QPalette::ButtonText), 0.15); |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::buttonHoverOutlineColor(const QPalette &palette) const |
|
{ |
|
return KColorUtils::mix(buttonHoverColor(palette), palette.color(QPalette::ButtonText), 0.15); |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::sidePanelOutlineColor(const QPalette &palette, bool hasFocus, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor outline(palette.color(QPalette::Inactive, QPalette::Highlight)); |
|
const QColor &focus = palette.color(QPalette::Active, QPalette::Highlight); |
|
|
|
if (mode == AnimationFocus) { |
|
outline = KColorUtils::mix(outline, focus, opacity); |
|
|
|
} else if (hasFocus) { |
|
outline = focus; |
|
} |
|
|
|
return outline; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::frameBackgroundColor(const QPalette &palette, QPalette::ColorGroup group) const |
|
{ |
|
return KColorUtils::mix(palette.color(group, QPalette::Window), palette.color(group, QPalette::Base), 0.3); |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::arrowColor(const QPalette &palette, QPalette::ColorGroup group, QPalette::ColorRole role) const |
|
{ |
|
switch (role) { |
|
case QPalette::Text: |
|
return KColorUtils::mix(palette.color(group, QPalette::Text), palette.color(group, QPalette::Base), arrowShade); |
|
case QPalette::WindowText: |
|
return KColorUtils::mix(palette.color(group, QPalette::WindowText), palette.color(group, QPalette::Window), arrowShade); |
|
case QPalette::ButtonText: |
|
return KColorUtils::mix(palette.color(group, QPalette::ButtonText), palette.color(group, QPalette::Button), arrowShade); |
|
default: |
|
return palette.color(group, role); |
|
} |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::arrowColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor outline(arrowColor(palette, QPalette::WindowText)); |
|
if (mode == AnimationHover) { |
|
const QColor focus(focusColor(palette)); |
|
const QColor hover(hoverColor(palette)); |
|
if (hasFocus) { |
|
outline = KColorUtils::mix(focus, hover, opacity); |
|
} else { |
|
outline = KColorUtils::mix(outline, hover, opacity); |
|
} |
|
|
|
} else if (mouseOver) { |
|
outline = hoverColor(palette); |
|
|
|
} else if (mode == AnimationFocus) { |
|
const QColor focus(focusColor(palette)); |
|
outline = KColorUtils::mix(outline, focus, opacity); |
|
|
|
} else if (hasFocus) { |
|
outline = focusColor(palette); |
|
} |
|
|
|
return outline; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::sliderOutlineColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor outline(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.4)); |
|
|
|
// hover takes precedence over focus |
|
if (mode == AnimationHover) { |
|
const QColor hover(hoverColor(palette)); |
|
const QColor focus(focusColor(palette)); |
|
if (hasFocus) { |
|
outline = KColorUtils::mix(focus, hover, opacity); |
|
} else { |
|
outline = KColorUtils::mix(outline, hover, opacity); |
|
} |
|
|
|
} else if (mouseOver) { |
|
outline = hoverColor(palette); |
|
|
|
} else if (mode == AnimationFocus) { |
|
const QColor focus(focusColor(palette)); |
|
outline = KColorUtils::mix(outline, focus, opacity); |
|
|
|
} else if (hasFocus) { |
|
outline = focusColor(palette); |
|
} |
|
|
|
return outline; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::scrollBarHandleColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor color(alphaColor(palette.color(QPalette::WindowText), 0.5)); |
|
|
|
// hover takes precedence over focus |
|
if (mode == AnimationHover) { |
|
const QColor hover(hoverColor(palette)); |
|
const QColor focus(focusColor(palette)); |
|
if (hasFocus) { |
|
color = KColorUtils::mix(focus, hover, opacity); |
|
} else { |
|
color = KColorUtils::mix(color, hover, opacity); |
|
} |
|
|
|
} else if (mouseOver) { |
|
color = hoverColor(palette); |
|
|
|
} else if (mode == AnimationFocus) { |
|
const QColor focus(focusColor(palette)); |
|
color = KColorUtils::mix(color, focus, opacity); |
|
|
|
} else if (hasFocus) { |
|
color = focusColor(palette); |
|
} |
|
|
|
return color; |
|
} |
|
|
|
//______________________________________________________________________________ |
|
QColor Helper::checkBoxIndicatorColor(const QPalette &palette, bool mouseOver, bool active, qreal opacity, AnimationMode mode) const |
|
{ |
|
QColor color(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.6)); |
|
if (mode == AnimationHover) { |
|
const QColor focus(focusColor(palette)); |
|
const QColor hover(hoverColor(palette)); |
|
if (active) { |
|
color = KColorUtils::mix(focus, hover, opacity); |
|
} else { |
|
color = KColorUtils::mix(color, hover, opacity); |
|
} |
|
|
|
} else if (mouseOver) { |
|
color = hoverColor(palette); |
|
|
|
} else if (active) { |
|
color = focusColor(palette); |
|
} |
|
|
|
return color; |
|
} |
|
|
|
//______________________________________________________________________________ |
|
QColor Helper::separatorColor(const QPalette &palette) const |
|
{ |
|
return KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.25); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
QPalette Helper::disabledPalette(const QPalette &source, qreal ratio) const |
|
{ |
|
QPalette copy(source); |
|
|
|
const QList<QPalette::ColorRole> roles = |
|
{QPalette::Window, QPalette::Highlight, QPalette::WindowText, QPalette::ButtonText, QPalette::Text, QPalette::Button}; |
|
foreach (const QPalette::ColorRole &role, roles) { |
|
copy.setColor(role, KColorUtils::mix(source.color(QPalette::Active, role), source.color(QPalette::Disabled, role), 1.0 - ratio)); |
|
} |
|
|
|
return copy; |
|
} |
|
|
|
//____________________________________________________________________ |
|
QColor Helper::alphaColor(QColor color, qreal alpha) const |
|
{ |
|
if (alpha >= 0 && alpha < 1.0) { |
|
color.setAlphaF(alpha * color.alphaF()); |
|
} |
|
return color; |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderDebugFrame(QPainter *painter, const QRect &rect) const |
|
{ |
|
painter->save(); |
|
painter->setRenderHints(QPainter::Antialiasing); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->setPen(Qt::red); |
|
painter->drawRect(strokedRect(rect)); |
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderFocusRect(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, Sides sides) const |
|
{ |
|
if (!color.isValid()) { |
|
return; |
|
} |
|
|
|
painter->save(); |
|
painter->setRenderHints(QPainter::Antialiasing); |
|
painter->setBrush(color); |
|
|
|
if (!(outline.isValid() && sides)) { |
|
painter->setPen(Qt::NoPen); |
|
painter->drawRect(rect); |
|
|
|
} else { |
|
painter->setClipRect(rect); |
|
|
|
QRectF copy(strokedRect(rect)); |
|
|
|
const qreal radius(frameRadius(PenWidth::Frame)); |
|
if (!(sides & SideTop)) { |
|
copy.adjust(0, -radius, 0, 0); |
|
} |
|
if (!(sides & SideBottom)) { |
|
copy.adjust(0, 0, 0, radius); |
|
} |
|
if (!(sides & SideLeft)) { |
|
copy.adjust(-radius, 0, 0, 0); |
|
} |
|
if (!(sides & SideRight)) { |
|
copy.adjust(0, 0, radius, 0); |
|
} |
|
|
|
painter->setPen(outline); |
|
// painter->setBrush( Qt::NoBrush ); |
|
painter->drawRoundedRect(copy, radius, radius); |
|
} |
|
|
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderFocusLine(QPainter *painter, const QRect &rect, const QColor &color) const |
|
{ |
|
if (!color.isValid()) { |
|
return; |
|
} |
|
|
|
painter->save(); |
|
painter->setRenderHint(QPainter::Antialiasing, false); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->setPen(color); |
|
|
|
painter->translate(0, 2); |
|
painter->drawLine(rect.bottomLeft(), rect.bottomRight()); |
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderFrameWithSides(QPainter *painter, const QRect &rect, const QColor &color, Qt::Edges edges, const QColor &outline) const |
|
{ |
|
painter->save(); |
|
|
|
painter->setRenderHint(QPainter::Antialiasing); |
|
|
|
QRectF frameRect(rect); |
|
|
|
// set brush |
|
painter->setBrush(color); |
|
painter->setPen(Qt::NoPen); |
|
|
|
// render |
|
painter->drawRect(frameRect); |
|
|
|
// set brush again |
|
painter->setBrush(Qt::NoBrush); |
|
painter->setPen(outline); |
|
|
|
// manually apply the effects of StrokedRect here but only to the edges with a frame |
|
if (edges & Qt::LeftEdge) { |
|
frameRect.adjust(0.5, 0.0, 0.0, 0.0); |
|
} |
|
if (edges & Qt::RightEdge) { |
|
frameRect.adjust(0.0, 0, -0.5, 0.0); |
|
} |
|
if (edges & Qt::TopEdge) { |
|
frameRect.adjust(0.0, 0.5, 0.0, 0.0); |
|
} |
|
if (edges & Qt::BottomEdge) { |
|
frameRect.adjust(0.0, 0.0, 0.0, -0.5); |
|
} |
|
|
|
// draw lines |
|
if (edges & Qt::LeftEdge) { |
|
painter->drawLine(QLineF(frameRect.topLeft(), frameRect.bottomLeft())); |
|
} |
|
if (edges & Qt::RightEdge) { |
|
painter->drawLine(QLineF(frameRect.topRight(), frameRect.bottomRight())); |
|
} |
|
if (edges & Qt::TopEdge) { |
|
painter->drawLine(QLineF(frameRect.topLeft(), frameRect.topRight())); |
|
} |
|
if (edges & Qt::BottomEdge) { |
|
painter->drawLine(QLineF(frameRect.bottomLeft(), frameRect.bottomRight())); |
|
} |
|
|
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline) const |
|
{ |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
|
|
QRectF frameRect(rect.adjusted(1, 1, -1, -1)); |
|
qreal radius(frameRadius(PenWidth::NoPen)); |
|
|
|
// set pen |
|
if (outline.isValid()) { |
|
painter->setPen(outline); |
|
frameRect = strokedRect(frameRect); |
|
radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame); |
|
|
|
} else { |
|
painter->setPen(Qt::NoPen); |
|
} |
|
|
|
// set brush |
|
if (color.isValid()) { |
|
painter->setBrush(color); |
|
} else { |
|
painter->setBrush(Qt::NoBrush); |
|
} |
|
|
|
// render |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderSidePanelFrame(QPainter *painter, const QRect &rect, const QColor &outline, Side side) const |
|
{ |
|
// check color |
|
if (!outline.isValid()) { |
|
return; |
|
} |
|
|
|
// adjust rect |
|
QRectF frameRect(strokedRect(rect)); |
|
|
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
painter->setPen(outline); |
|
|
|
// render |
|
switch (side) { |
|
default: |
|
case SideLeft: |
|
painter->drawLine(frameRect.topRight(), frameRect.bottomRight()); |
|
break; |
|
|
|
case SideTop: |
|
painter->drawLine(frameRect.topLeft(), frameRect.topRight()); |
|
break; |
|
|
|
case SideRight: |
|
painter->drawLine(frameRect.topLeft(), frameRect.bottomLeft()); |
|
break; |
|
|
|
case SideBottom: |
|
painter->drawLine(frameRect.bottomLeft(), frameRect.bottomRight()); |
|
break; |
|
|
|
case AllSides: { |
|
const qreal radius(frameRadius(PenWidth::Frame)); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderMenuFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, bool roundCorners, bool isTopMenu) const |
|
{ |
|
painter->save(); |
|
|
|
// set brush |
|
if (color.isValid()) { |
|
painter->setBrush(color); |
|
} else { |
|
painter->setBrush(Qt::NoBrush); |
|
} |
|
|
|
// We simulate being able to independently adjust corner radii by |
|
// setting a clip region and then extending the rectangle beyond it. |
|
if (isTopMenu) { |
|
painter->setClipRect(rect); |
|
} |
|
|
|
if (roundCorners) { |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
QRectF frameRect(rect); |
|
qreal radius(frameRadius(PenWidth::NoPen)); |
|
|
|
if (isTopMenu) { |
|
frameRect.adjust(0, -radius, 0, 0); |
|
} |
|
|
|
// set pen |
|
if (outline.isValid()) { |
|
painter->setPen(outline); |
|
frameRect = strokedRect(frameRect); |
|
radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame); |
|
|
|
} else { |
|
painter->setPen(Qt::NoPen); |
|
} |
|
|
|
// render |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
|
|
} else { |
|
painter->setRenderHint(QPainter::Antialiasing, false); |
|
QRect frameRect(rect); |
|
if (isTopMenu) { |
|
frameRect.adjust(0, 1, 0, 0); |
|
} |
|
|
|
if (outline.isValid()) { |
|
painter->setPen(outline); |
|
frameRect.adjust(0, 0, -1, -1); |
|
|
|
} else { |
|
painter->setPen(Qt::NoPen); |
|
} |
|
|
|
painter->drawRect(frameRect); |
|
} |
|
|
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderButtonFrame(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
const QHash<QByteArray, bool> &stateProperties, |
|
qreal bgAnimation, |
|
qreal penAnimation) const |
|
{ |
|
bool enabled = stateProperties.value("enabled", true); |
|
bool visualFocus = stateProperties.value("visualFocus"); |
|
bool hovered = stateProperties.value("hovered"); |
|
bool down = stateProperties.value("down"); |
|
bool checked = stateProperties.value("checked"); |
|
bool flat = stateProperties.value("flat"); |
|
bool defaultButton = stateProperties.value("defaultButton"); |
|
bool hasNeutralHighlight = stateProperties.value("hasNeutralHighlight"); |
|
bool isActiveWindow = stateProperties.value("isActiveWindow"); |
|
|
|
// don't render background if flat and not hovered, down, checked, or given visual focus |
|
if (flat && !(hovered || down || checked || visualFocus) && bgAnimation == AnimationData::OpacityInvalid && penAnimation == AnimationData::OpacityInvalid) { |
|
return; |
|
} |
|
|
|
QRectF shadowedRect = this->shadowedRect(rect); |
|
QRectF frameRect = strokedRect(shadowedRect); |
|
qreal radius = frameRadius(PenWidth::Frame); |
|
// setting color group to work around KColorScheme feature |
|
const QColor &highlightColor = palette.color(!enabled ? QPalette::Disabled : QPalette::Active, QPalette::Highlight); |
|
QBrush bgBrush; |
|
QBrush penBrush; |
|
|
|
// Colors |
|
if (flat) { |
|
if (down && enabled) { |
|
bgBrush = alphaColor(highlightColor, highlightBackgroundAlpha); |
|
} else if (checked) { |
|
bgBrush = hasNeutralHighlight ? alphaColor(neutralText(palette), highlightBackgroundAlpha) : alphaColor(palette.buttonText().color(), 0.125); |
|
penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3); |
|
} else if (isActiveWindow && defaultButton) { |
|
bgBrush = alphaColor(highlightColor, 0.125); |
|
penBrush = KColorUtils::mix(highlightColor, KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.333), 0.5); |
|
} else { |
|
bgBrush = alphaColor(highlightColor, 0); |
|
penBrush = hasNeutralHighlight ? neutralText(palette) : bgBrush; |
|
} |
|
} else { |
|
if (down && enabled) { |
|
bgBrush = KColorUtils::mix(palette.button().color(), highlightColor, 0.333); |
|
} else if (checked) { |
|
bgBrush = hasNeutralHighlight ? KColorUtils::mix(palette.button().color(), neutralText(palette), 0.333) |
|
: KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.125); |
|
penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3); |
|
} else if (isActiveWindow && defaultButton) { |
|
bgBrush = KColorUtils::mix(palette.button().color(), highlightColor, 0.2); |
|
penBrush = KColorUtils::mix(highlightColor, KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.333), 0.5); |
|
} else { |
|
bgBrush = palette.button().color(); |
|
penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3); |
|
} |
|
} |
|
|
|
if ((hovered || visualFocus || down) && enabled) { |
|
penBrush = highlightColor; |
|
} |
|
|
|
// Animations |
|
if (bgAnimation != AnimationData::OpacityInvalid && enabled) { |
|
QColor color1 = bgBrush.color(); |
|
QColor color2 = flat ? alphaColor(highlightColor, highlightBackgroundAlpha) : KColorUtils::mix(palette.button().color(), highlightColor, 0.333); |
|
bgBrush = KColorUtils::mix(color1, color2, bgAnimation); |
|
} |
|
if (penAnimation != AnimationData::OpacityInvalid && enabled) { |
|
QColor color1 = penBrush.color(); |
|
QColor color2 = highlightColor; |
|
penBrush = KColorUtils::mix(color1, color2, penAnimation); |
|
} |
|
|
|
// Shadow |
|
if (isActiveWindow && !(flat || down || checked) && enabled) { |
|
renderRoundedRectShadow(painter, shadowedRect, shadowColor(palette)); |
|
} |
|
|
|
// Render button |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
painter->setBrush(bgBrush); |
|
painter->setPen(QPen(penBrush, PenWidth::Frame)); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderToolBoxFrame(QPainter *painter, const QRect &rect, int tabWidth, const QColor &outline) const |
|
{ |
|
if (!outline.isValid()) { |
|
return; |
|
} |
|
|
|
// round radius |
|
const qreal radius(frameRadius(PenWidth::Frame)); |
|
const QSizeF cornerSize(2 * radius, 2 * radius); |
|
|
|
// if rect - tabwidth is even, need to increase tabWidth by 1 unit |
|
// for anti aliasing |
|
if (!((rect.width() - tabWidth) % 2)) { |
|
++tabWidth; |
|
} |
|
|
|
// adjust rect for antialiasing |
|
QRectF baseRect(strokedRect(rect)); |
|
|
|
// create path |
|
QPainterPath path; |
|
path.moveTo(0, baseRect.height() - 1); |
|
path.lineTo((baseRect.width() - tabWidth) / 2 - radius, baseRect.height() - 1); |
|
path.arcTo(QRectF(QPointF((baseRect.width() - tabWidth) / 2 - 2 * radius, baseRect.height() - 1 - 2 * radius), cornerSize), 270, 90); |
|
path.lineTo((baseRect.width() - tabWidth) / 2, radius); |
|
path.arcTo(QRectF(QPointF((baseRect.width() - tabWidth) / 2, 0), cornerSize), 180, -90); |
|
path.lineTo((baseRect.width() + tabWidth) / 2 - 1 - radius, 0); |
|
path.arcTo(QRectF(QPointF((baseRect.width() + tabWidth) / 2 - 1 - 2 * radius, 0), cornerSize), 90, -90); |
|
path.lineTo((baseRect.width() + tabWidth) / 2 - 1, baseRect.height() - 1 - radius); |
|
path.arcTo(QRectF(QPointF((baseRect.width() + tabWidth) / 2 - 1, baseRect.height() - 1 - 2 * radius), cornerSize), 180, 90); |
|
path.lineTo(baseRect.width() - 1, baseRect.height() - 1); |
|
|
|
// render |
|
painter->setRenderHints(QPainter::Antialiasing); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->setPen(outline); |
|
painter->translate(baseRect.topLeft()); |
|
painter->drawPath(path); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderTabWidgetFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, Corners corners) const |
|
{ |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
|
|
QRectF frameRect(rect.adjusted(1, 1, -1, -1)); |
|
qreal radius(frameRadius(PenWidth::NoPen)); |
|
|
|
// set pen |
|
if (outline.isValid()) { |
|
painter->setPen(outline); |
|
frameRect = strokedRect(frameRect); |
|
radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame); |
|
|
|
} else { |
|
painter->setPen(Qt::NoPen); |
|
} |
|
|
|
// set brush |
|
if (color.isValid()) { |
|
painter->setBrush(color); |
|
} else { |
|
painter->setBrush(Qt::NoBrush); |
|
} |
|
|
|
// render |
|
QPainterPath path(roundedPath(frameRect, corners, radius)); |
|
painter->drawPath(path); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderSelection(QPainter *painter, const QRect &rect, const QColor &color) const |
|
{ |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
painter->setPen(Qt::NoPen); |
|
painter->setBrush(color); |
|
painter->drawRect(rect); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderSeparator(QPainter *painter, const QRect &rect, const QColor &color, bool vertical) const |
|
{ |
|
painter->setRenderHint(QPainter::Antialiasing, false); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->setPen(color); |
|
|
|
if (vertical) { |
|
painter->translate(rect.width() / 2, 0); |
|
painter->drawLine(rect.topLeft(), rect.bottomLeft()); |
|
|
|
} else { |
|
painter->translate(0, rect.height() / 2); |
|
painter->drawLine(rect.topLeft(), rect.topRight()); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderCheckBoxBackground(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
CheckBoxState state, |
|
bool neutalHighlight, |
|
bool sunken, |
|
qreal animation) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
// copy rect |
|
QRectF frameRect(rect); |
|
frameRect.adjust(2, 2, -2, -2); |
|
frameRect = strokedRect(frameRect); |
|
|
|
auto transparent = neutalHighlight ? neutralText(palette) : palette.highlight().color(); |
|
transparent.setAlphaF(highlightBackgroundAlpha); |
|
|
|
QBrush penBrush; |
|
if (neutalHighlight) { |
|
penBrush = neutralText(palette); |
|
} else if (state == CheckOn || state == CheckPartial) { |
|
penBrush = palette.highlight().color(); |
|
} else { |
|
penBrush = transparentize(palette.text().color(), highlightBackgroundAlpha); |
|
} |
|
painter->setPen(QPen(penBrush, PenWidth::Frame)); |
|
|
|
const auto radius = Metrics::CheckBox_Radius; |
|
|
|
switch (state) { |
|
case CheckOff: |
|
painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
break; |
|
|
|
case CheckPartial: |
|
case CheckOn: |
|
painter->setBrush(transparent.darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
break; |
|
|
|
case CheckAnimated: |
|
painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
painter->setBrush(transparent); |
|
painter->setOpacity(animation); |
|
painter->drawRoundedRect(frameRect, radius, radius); |
|
break; |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderCheckBox(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
bool mouseOver, |
|
CheckBoxState state, |
|
CheckBoxState target, |
|
bool neutalHighlight, |
|
bool sunken, |
|
qreal animation, |
|
qreal hoverAnimation) const |
|
{ |
|
Q_UNUSED(sunken) |
|
|
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
// copy rect and radius |
|
QRectF frameRect(rect); |
|
frameRect.adjust(2, 2, -2, -2); |
|
|
|
if (mouseOver) { |
|
painter->save(); |
|
|
|
if (hoverAnimation != AnimationData::OpacityInvalid) { |
|
painter->setOpacity(hoverAnimation); |
|
} |
|
|
|
painter->setPen(QPen(neutalHighlight ? neutralText(palette).lighter() : focusColor(palette), PenWidth::Frame)); |
|
painter->setBrush(Qt::NoBrush); |
|
|
|
painter->drawRoundedRect(frameRect.adjusted(0.5, 0.5, -0.5, -0.5), Metrics::CheckBox_Radius, Metrics::CheckBox_Radius); |
|
|
|
painter->restore(); |
|
} |
|
|
|
// check |
|
auto leftPoint = frameRect.center(); |
|
leftPoint.setX(frameRect.left() + 4); |
|
|
|
auto bottomPoint = frameRect.center(); |
|
bottomPoint.setX(bottomPoint.x() - 1); |
|
bottomPoint.setY(frameRect.bottom() - 5); |
|
|
|
auto rightPoint = frameRect.center(); |
|
rightPoint.setX(rightPoint.x() + 4.5); |
|
rightPoint.setY(frameRect.top() + 5.5); |
|
|
|
QPainterPath path; |
|
path.moveTo(leftPoint); |
|
path.lineTo(bottomPoint); |
|
path.lineTo(rightPoint); |
|
|
|
// dots |
|
auto centerDot = QRectF(frameRect.center(), QSize(2, 2)); |
|
centerDot.adjust(-1, -1, -1, -1); |
|
auto leftDot = centerDot.adjusted(-4, 0, -4, 0); |
|
auto rightDot = centerDot.adjusted(4, 0, 4, 0); |
|
|
|
painter->setPen(Qt::transparent); |
|
painter->setBrush(Qt::transparent); |
|
|
|
auto checkPen = QPen(palette.text(), PenWidth::Frame * 2); |
|
checkPen.setJoinStyle(Qt::MiterJoin); |
|
|
|
switch (state) { |
|
case CheckOff: |
|
break; |
|
case CheckOn: |
|
painter->setPen(checkPen); |
|
painter->drawPath(path); |
|
break; |
|
case CheckPartial: |
|
painter->setBrush(palette.text()); |
|
painter->drawRect(leftDot); |
|
painter->drawRect(centerDot); |
|
painter->drawRect(rightDot); |
|
break; |
|
case CheckAnimated: |
|
checkPen.setDashPattern({path.length() * animation, path.length()}); |
|
|
|
switch (target) { |
|
case CheckOff: |
|
break; |
|
case CheckOn: |
|
painter->setPen(checkPen); |
|
painter->drawPath(path); |
|
break; |
|
case CheckPartial: |
|
if (animation >= 3 / 3) { |
|
painter->drawRect(rightDot); |
|
} |
|
if (animation >= 2 / 3) { |
|
painter->drawRect(centerDot); |
|
} |
|
if (animation >= 1 / 3) { |
|
painter->drawRect(leftDot); |
|
} |
|
break; |
|
case CheckAnimated: |
|
break; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderRadioButtonBackground(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
RadioButtonState state, |
|
bool neutalHighlight, |
|
bool sunken, |
|
qreal animation) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
// copy rect |
|
QRectF frameRect(rect); |
|
frameRect.adjust(2, 2, -2, -2); |
|
frameRect.adjust(0.5, 0.5, -0.5, -0.5); |
|
|
|
auto transparent = neutalHighlight ? neutralText(palette) : palette.highlight().color(); |
|
transparent.setAlphaF(highlightBackgroundAlpha); |
|
|
|
QBrush penBrush; |
|
if (neutalHighlight) { |
|
penBrush = neutralText(palette); |
|
} else if (state == RadioOn) { |
|
penBrush = palette.highlight().color(); |
|
} else { |
|
penBrush = transparentize(palette.text().color(), highlightBackgroundAlpha); |
|
} |
|
painter->setPen(QPen(penBrush, PenWidth::Frame)); |
|
|
|
switch (state) { |
|
case RadioOff: |
|
painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawEllipse(frameRect); |
|
break; |
|
case RadioOn: |
|
painter->setBrush(transparent.darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawEllipse(frameRect); |
|
break; |
|
case RadioAnimated: |
|
painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100)); |
|
painter->drawEllipse(frameRect); |
|
painter->setBrush(transparent); |
|
painter->setOpacity(animation); |
|
painter->drawEllipse(frameRect); |
|
break; |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderRadioButton(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
bool mouseOver, |
|
RadioButtonState state, |
|
bool neutralHighlight, |
|
bool sunken, |
|
qreal animation, |
|
qreal animationHover) const |
|
{ |
|
Q_UNUSED(sunken) |
|
|
|
// copy rect |
|
QRectF frameRect(rect); |
|
frameRect.adjust(1, 1, -1, -1); |
|
|
|
if (mouseOver) { |
|
painter->save(); |
|
|
|
if (animationHover != AnimationData::OpacityInvalid) { |
|
painter->setOpacity(animationHover); |
|
} |
|
|
|
painter->setPen(QPen(neutralHighlight ? neutralText(palette).lighter() : focusColor(palette), PenWidth::Frame)); |
|
painter->setBrush(Qt::NoBrush); |
|
|
|
const QRectF contentRect(frameRect.adjusted(1, 1, -1, -1).adjusted(0.5, 0.5, -0.5, -0.5)); |
|
painter->drawEllipse(contentRect); |
|
|
|
painter->restore(); |
|
} |
|
|
|
painter->setBrush(palette.text()); |
|
painter->setPen(Qt::NoPen); |
|
|
|
QRectF markerRect; |
|
markerRect = frameRect.adjusted(6, 6, -6, -6); |
|
|
|
qreal adjustFactor; |
|
|
|
// mark |
|
switch (state) { |
|
case RadioOn: |
|
painter->drawEllipse(markerRect); |
|
|
|
break; |
|
case RadioAnimated: |
|
adjustFactor = markerRect.height() * (1 - animation); |
|
markerRect.adjust(adjustFactor, adjustFactor, -adjustFactor, -adjustFactor); |
|
painter->drawEllipse(markerRect); |
|
|
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderSliderGroove(QPainter *painter, const QRect &rect, const QColor &color) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
QRectF baseRect(rect); |
|
baseRect.adjust(0.5, 0.5, -0.5, -0.5); |
|
const qreal radius(0.5 * Metrics::Slider_GrooveThickness); |
|
|
|
// content |
|
if (color.isValid()) { |
|
painter->setPen(QPen(color, PenWidth::Frame)); |
|
auto bg = color; |
|
bg.setAlphaF(bg.alphaF() / 2); |
|
painter->setBrush(bg); |
|
painter->drawRoundedRect(baseRect, radius, radius); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderDialGroove(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg, qreal first, qreal last) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
const QRectF baseRect(rect); |
|
|
|
// content |
|
if (fg.isValid()) { |
|
const qreal penWidth(Metrics::Slider_GrooveThickness); |
|
const QRectF grooveRect(rect.adjusted(penWidth / 2, penWidth / 2, -penWidth / 2, -penWidth / 2)); |
|
|
|
// setup angles |
|
const int angleStart(first * 180 * 16 / M_PI); |
|
const int angleSpan((last - first) * 180 * 16 / M_PI); |
|
|
|
const QPen bgPen(fg, penWidth, Qt::SolidLine, Qt::RoundCap); |
|
const QPen fgPen(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5)), penWidth - 2, Qt::SolidLine, Qt::RoundCap); |
|
|
|
// setup pen |
|
if (angleSpan != 0) { |
|
painter->setPen(bgPen); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->drawArc(grooveRect, angleStart, angleSpan); |
|
painter->setPen(fgPen); |
|
painter->drawArc(grooveRect, angleStart, angleSpan); |
|
} |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderSliderHandle(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, const QColor &shadow, bool sunken) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
// copy rect |
|
QRectF frameRect(rect); |
|
frameRect.adjust(1, 1, -1, -1); |
|
|
|
// shadow |
|
if (!sunken) { |
|
renderEllipseShadow(painter, frameRect, shadow); |
|
} |
|
|
|
// set pen |
|
if (outline.isValid()) { |
|
painter->setPen(QPen(outline, PenWidth::Frame)); |
|
frameRect = strokedRect(frameRect); |
|
|
|
} else { |
|
painter->setPen(Qt::NoPen); |
|
} |
|
|
|
// set brush |
|
if (color.isValid()) { |
|
painter->setBrush(color); |
|
} else { |
|
painter->setBrush(Qt::NoBrush); |
|
} |
|
|
|
// render |
|
painter->drawEllipse(frameRect); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderProgressBarGroove(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
QRectF baseRect(rect); |
|
baseRect.adjust(0.5, 0.5, -0.5, -0.5); |
|
const qreal radius(0.5 * Metrics::ProgressBar_Thickness); |
|
|
|
// content |
|
if (fg.isValid()) { |
|
painter->setPen(QPen(fg, PenWidth::Frame)); |
|
painter->setBrush(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5))); |
|
painter->drawRoundedRect(baseRect, radius, radius); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderProgressBarBusyContents(QPainter *painter, |
|
const QRect &rect, |
|
const QColor &first, |
|
const QColor &second, |
|
bool horizontal, |
|
bool reverse, |
|
int progress) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
const QRectF baseRect(rect); |
|
const qreal radius(0.5 * Metrics::ProgressBar_Thickness); |
|
|
|
// setup brush |
|
QPixmap pixmap(horizontal ? 2 * Metrics::ProgressBar_BusyIndicatorSize : 1, horizontal ? 1 : 2 * Metrics::ProgressBar_BusyIndicatorSize); |
|
pixmap.fill(second); |
|
if (horizontal) { |
|
QPainter painter(&pixmap); |
|
painter.setBrush(first); |
|
painter.setPen(Qt::NoPen); |
|
|
|
progress %= 2 * Metrics::ProgressBar_BusyIndicatorSize; |
|
if (reverse) { |
|
progress = 2 * Metrics::ProgressBar_BusyIndicatorSize - progress - 1; |
|
} |
|
painter.drawRect(QRect(0, 0, Metrics::ProgressBar_BusyIndicatorSize, 1).translated(progress, 0)); |
|
|
|
if (progress > Metrics::ProgressBar_BusyIndicatorSize) { |
|
painter.drawRect(QRect(0, 0, Metrics::ProgressBar_BusyIndicatorSize, 1).translated(progress - 2 * Metrics::ProgressBar_BusyIndicatorSize, 0)); |
|
} |
|
|
|
} else { |
|
QPainter painter(&pixmap); |
|
painter.setBrush(first); |
|
painter.setPen(Qt::NoPen); |
|
|
|
progress %= 2 * Metrics::ProgressBar_BusyIndicatorSize; |
|
progress = 2 * Metrics::ProgressBar_BusyIndicatorSize - progress - 1; |
|
painter.drawRect(QRect(0, 0, 1, Metrics::ProgressBar_BusyIndicatorSize).translated(0, progress)); |
|
|
|
if (progress > Metrics::ProgressBar_BusyIndicatorSize) { |
|
painter.drawRect(QRect(0, 0, 1, Metrics::ProgressBar_BusyIndicatorSize).translated(0, progress - 2 * Metrics::ProgressBar_BusyIndicatorSize)); |
|
} |
|
} |
|
|
|
painter->setPen(Qt::NoPen); |
|
painter->setBrush(pixmap); |
|
painter->drawRoundedRect(baseRect, radius, radius); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderScrollBarHandle(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
const QRectF baseRect(rect); |
|
const qreal radius(0.5 * std::min({baseRect.width(), baseRect.height(), (qreal)Metrics::ScrollBar_SliderWidth})); |
|
|
|
painter->setPen(Qt::NoPen); |
|
painter->setPen(QPen(fg, 1.001)); |
|
painter->setBrush(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5))); |
|
painter->drawRoundedRect(strokedRect(baseRect), radius, radius); |
|
} |
|
|
|
void Helper::renderScrollBarGroove(QPainter *painter, const QRect &rect, const QColor &color) const |
|
{ |
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
const QRectF baseRect(rect); |
|
const qreal radius(0.5 * std::min({baseRect.width(), baseRect.height(), (qreal)Metrics::ScrollBar_SliderWidth})); |
|
|
|
// content |
|
if (color.isValid()) { |
|
painter->setPen(Qt::NoPen); |
|
auto bg = color; |
|
bg.setAlphaF(bg.alphaF() / 2.0); |
|
painter->setBrush(bg); |
|
painter->setPen(QPen(color, 1.001)); |
|
painter->drawRoundedRect(strokedRect(baseRect), radius, radius); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderScrollBarBorder(QPainter *painter, const QRect &rect, const QColor &color) const |
|
{ |
|
// content |
|
if (color.isValid()) { |
|
painter->setPen(Qt::NoPen); |
|
painter->setBrush(color); |
|
painter->drawRect(rect); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderTabBarTab(QPainter *painter, |
|
const QRect &rect, |
|
const QPalette &palette, |
|
const QHash<QByteArray, bool> &stateProperties, |
|
Corners corners, |
|
qreal animation) const |
|
{ |
|
bool enabled = stateProperties.value("enabled", true); |
|
bool visualFocus = stateProperties.value("visualFocus"); |
|
bool hovered = stateProperties.value("hovered"); |
|
bool down = stateProperties.value("down"); |
|
bool selected = stateProperties.value("selected"); |
|
bool documentMode = stateProperties.value("documentMode"); |
|
bool north = stateProperties.value("north"); |
|
bool south = stateProperties.value("south"); |
|
bool west = stateProperties.value("west"); |
|
bool east = stateProperties.value("east"); |
|
bool animated = animation != AnimationData::OpacityInvalid; |
|
bool isQtQuickControl = stateProperties.value("isQtQuickControl"); |
|
bool hasAlteredBackground = stateProperties.value("hasAlteredBackground"); |
|
|
|
// setup painter |
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
QRectF frameRect = rect; |
|
QColor bgBrush; |
|
|
|
if (selected) { |
|
if (north) { |
|
// overlap bottom border |
|
frameRect.adjust(0, 0, 0, 1); |
|
} else if (south) { |
|
// overlap top border |
|
frameRect.adjust(0, -1, 0, 0); |
|
} else if (west) { |
|
// overlap right border |
|
frameRect.adjust(0, 0, 1, 0); |
|
} else if (east) { |
|
// overlap left border |
|
frameRect.adjust(-1, 0, 0, 0); |
|
} |
|
if (documentMode && !isQtQuickControl && !hasAlteredBackground) { |
|
bgBrush = palette.color(QPalette::Window); |
|
} else { |
|
bgBrush = frameBackgroundColor(palette); |
|
} |
|
QColor penBrush = KColorUtils::mix(bgBrush, palette.color(QPalette::WindowText), 0.25); |
|
painter->setBrush(bgBrush); |
|
painter->setPen(QPen(penBrush, PenWidth::Frame)); |
|
QRectF highlightRect = frameRect; |
|
if (north || south) { |
|
highlightRect.setHeight(Metrics::Frame_FrameRadius); |
|
} else if (west || east) { |
|
highlightRect.setWidth(Metrics::Frame_FrameRadius); |
|
} |
|
if (south) { |
|
highlightRect.moveBottom(frameRect.bottom()); |
|
} else if (east) { |
|
highlightRect.moveRight(frameRect.right()); |
|
} |
|
QPainterPath path = roundedPath(strokedRect(frameRect), corners, frameRadius(PenWidth::Frame)); |
|
painter->drawPath(path); |
|
QPainterPath highlightPath = roundedPath(highlightRect, corners, Metrics::Frame_FrameRadius); |
|
painter->setBrush(palette.color(QPalette::Highlight)); |
|
painter->setPen(Qt::NoPen); |
|
painter->drawPath(highlightPath); |
|
} else { |
|
if (north) { |
|
// don't overlap bottom border |
|
frameRect.adjust(0, 0, 0, -1); |
|
} else if (south) { |
|
// don't overlap top border |
|
frameRect.adjust(0, 1, 0, 0); |
|
} else if (west) { |
|
// don't overlap right border |
|
frameRect.adjust(0, 0, -1, 0); |
|
} else if (east) { |
|
// don't overlap left border |
|
frameRect.adjust(1, 0, 0, 0); |
|
} |
|
const auto windowColor = palette.color(QPalette::Window); |
|
bgBrush = windowColor.darker(120); |
|
const auto hover = alphaColor(hoverColor(palette), 0.2); |
|
if (animated) { |
|
bgBrush = KColorUtils::mix(bgBrush, hover, animation); |
|
} else if (enabled && hovered && !selected) { |
|
bgBrush = hover; |
|
} |
|
painter->setBrush(bgBrush); |
|
painter->setPen(Qt::NoPen); |
|
QPainterPath path = roundedPath(frameRect, corners, Metrics::Frame_FrameRadius); |
|
painter->drawPath(path); |
|
} |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderArrow(QPainter *painter, const QRect &rect, const QColor &color, ArrowOrientation orientation) const |
|
{ |
|
// define polygon |
|
QPolygonF arrow; |
|
switch (orientation) { |
|
/* The inner points of the normal arrows are not on half pixels because |
|
* they need to have an even width (up/down) or height (left/right). |
|
* An even width/height makes them easier to align with other UI elements. |
|
*/ |
|
case ArrowUp: |
|
arrow = QVector<QPointF>{QPointF(-4.5, 1.5), QPointF(0, -3), QPointF(4.5, 1.5)}; |
|
break; |
|
case ArrowDown: |
|
arrow = QVector<QPointF>{QPointF(-4.5, -1.5), QPointF(0, 3), QPointF(4.5, -1.5)}; |
|
break; |
|
case ArrowLeft: |
|
arrow = QVector<QPointF>{QPointF(1.5, -4.5), QPointF(-3, 0), QPointF(1.5, 4.5)}; |
|
break; |
|
case ArrowRight: |
|
arrow = QVector<QPointF>{QPointF(-1.5, -4.5), QPointF(3, 0), QPointF(-1.5, 4.5)}; |
|
break; |
|
case ArrowDown_Small: |
|
arrow = QVector<QPointF>{QPointF(1.5, 3.5), QPointF(3.5, 5.5), QPointF(5.5, 3.5)}; |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
painter->save(); |
|
painter->setRenderHints(QPainter::Antialiasing); |
|
painter->translate(QRectF(rect).center()); |
|
painter->setBrush(Qt::NoBrush); |
|
QPen pen(color, PenWidth::Symbol); |
|
pen.setCapStyle(Qt::SquareCap); |
|
pen.setJoinStyle(Qt::MiterJoin); |
|
painter->setPen(pen); |
|
painter->drawPolyline(arrow); |
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderDecorationButton(QPainter *painter, const QRect &rect, const QColor &color, ButtonType buttonType, bool inverted) const |
|
{ |
|
painter->save(); |
|
painter->setViewport(rect); |
|
painter->setWindow(0, 0, 18, 18); |
|
painter->setRenderHints(QPainter::Antialiasing); |
|
|
|
// initialize pen |
|
QPen pen; |
|
pen.setCapStyle(Qt::RoundCap); |
|
pen.setJoinStyle(Qt::MiterJoin); |
|
|
|
if (inverted) { |
|
// render circle |
|
painter->setPen(Qt::NoPen); |
|
painter->setBrush(color); |
|
painter->drawEllipse(QRectF(0, 0, 18, 18)); |
|
|
|
// take out the inner part |
|
painter->setCompositionMode(QPainter::CompositionMode_DestinationOut); |
|
painter->setBrush(Qt::NoBrush); |
|
pen.setColor(Qt::black); |
|
|
|
} else { |
|
painter->setBrush(Qt::NoBrush); |
|
pen.setColor(color); |
|
} |
|
|
|
pen.setCapStyle(Qt::RoundCap); |
|
pen.setJoinStyle(Qt::MiterJoin); |
|
pen.setWidthF(PenWidth::Symbol * qMax(1.0, 18.0 / rect.width())); |
|
painter->setPen(pen); |
|
|
|
switch (buttonType) { |
|
case ButtonClose: { |
|
painter->drawLine(QPointF(5, 5), QPointF(13, 13)); |
|
painter->drawLine(13, 5, 5, 13); |
|
break; |
|
} |
|
|
|
case ButtonMaximize: { |
|
painter->drawPolyline(QVector<QPointF>{QPointF(4, 11), QPointF(9, 6), QPointF(14, 11)}); |
|
break; |
|
} |
|
|
|
case ButtonMinimize: { |
|
painter->drawPolyline(QVector<QPointF>{QPointF(4, 7), QPointF(9, 12), QPointF(14, 7)}); |
|
break; |
|
} |
|
|
|
case ButtonRestore: { |
|
pen.setJoinStyle(Qt::RoundJoin); |
|
painter->setPen(pen); |
|
painter->drawPolygon(QVector<QPointF>{QPointF(4.5, 9), QPointF(9, 4.5), QPointF(13.5, 9), QPointF(9, 13.5)}); |
|
break; |
|
} |
|
|
|
default: |
|
break; |
|
} |
|
|
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderRoundedRectShadow(QPainter *painter, const QRectF &rect, const QColor &color, qreal radius) const |
|
{ |
|
if (!color.isValid()) { |
|
return; |
|
} |
|
|
|
painter->setRenderHint(QPainter::Antialiasing, true); |
|
|
|
qreal adjustment = 0.5 * PenWidth::Shadow; // Translate for the pen |
|
QRectF shadowRect = rect.adjusted(adjustment, adjustment, -adjustment, adjustment); |
|
|
|
painter->setPen(QPen(color, PenWidth::Shadow)); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->drawRoundedRect(shadowRect, radius, radius); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
void Helper::renderEllipseShadow(QPainter *painter, const QRectF &rect, const QColor &color) const |
|
{ |
|
if (!color.isValid()) { |
|
return; |
|
} |
|
|
|
painter->save(); |
|
|
|
// Clipping does not improve performance here |
|
|
|
qreal adjustment = 0.5 * PenWidth::Shadow; // Adjust for the pen |
|
|
|
qreal radius = rect.width() / 2 - adjustment; |
|
|
|
/* The right side is offset by +0.5 for the visible part of the shadow. |
|
* The other sides are offset by +0.5 or -0.5 because of the pen. |
|
*/ |
|
QRectF shadowRect = rect.adjusted(adjustment, adjustment, adjustment, -adjustment); |
|
|
|
painter->translate(rect.center()); |
|
painter->rotate(45); |
|
painter->translate(-rect.center()); |
|
painter->setPen(color); |
|
painter->setBrush(Qt::NoBrush); |
|
painter->drawRoundedRect(shadowRect, radius, radius); |
|
|
|
painter->restore(); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
bool Helper::isX11() |
|
{ |
|
static const bool s_isX11 = KWindowSystem::isPlatformX11(); |
|
return s_isX11; |
|
} |
|
|
|
//______________________________________________________________________________ |
|
bool Helper::isWayland() |
|
{ |
|
static const bool s_isWayland = KWindowSystem::isPlatformWayland(); |
|
return s_isWayland; |
|
} |
|
|
|
//______________________________________________________________________________ |
|
QRectF Helper::strokedRect(const QRectF &rect, const qreal penWidth) const |
|
{ |
|
/* With a pen stroke width of 1, the rectangle should have each of its |
|
* sides moved inwards by half a pixel. This allows the stroke to be |
|
* pixel perfect instead of blurry from sitting between pixels and |
|
* prevents the rectangle with a stroke from becoming larger than the |
|
* original size of the rectangle. |
|
*/ |
|
qreal adjustment = 0.5 * penWidth; |
|
return rect.adjusted(adjustment, adjustment, -adjustment, -adjustment); |
|
} |
|
|
|
//______________________________________________________________________________ |
|
QPainterPath Helper::roundedPath(const QRectF &rect, Corners corners, qreal radius) const |
|
{ |
|
QPainterPath path; |
|
|
|
// simple cases |
|
if (corners == 0) { |
|
path.addRect(rect); |
|
return path; |
|
} |
|
|
|
if (corners == AllCorners) { |
|
path.addRoundedRect(rect, radius, radius); |
|
return path; |
|
} |
|
|
|
const QSizeF cornerSize(2 * radius, 2 * radius); |
|
|
|
// rotate counterclockwise |
|
// top left corner |
|
if (corners & CornerTopLeft) { |
|
path.moveTo(rect.topLeft() + QPointF(radius, 0)); |
|
path.arcTo(QRectF(rect.topLeft(), cornerSize), 90, 90); |
|
|
|
} else { |
|
path.moveTo(rect.topLeft()); |
|
} |
|
|
|
// bottom left corner |
|
if (corners & CornerBottomLeft) { |
|
path.lineTo(rect.bottomLeft() - QPointF(0, radius)); |
|
path.arcTo(QRectF(rect.bottomLeft() - QPointF(0, 2 * radius), cornerSize), 180, 90); |
|
|
|
} else { |
|
path.lineTo(rect.bottomLeft()); |
|
} |
|
|
|
// bottom right corner |
|
if (corners & CornerBottomRight) { |
|
path.lineTo(rect.bottomRight() - QPointF(radius, 0)); |
|
path.arcTo(QRectF(rect.bottomRight() - QPointF(2 * radius, 2 * radius), cornerSize), 270, 90); |
|
|
|
} else { |
|
path.lineTo(rect.bottomRight()); |
|
} |
|
|
|
// top right corner |
|
if (corners & CornerTopRight) { |
|
path.lineTo(rect.topRight() + QPointF(0, radius)); |
|
path.arcTo(QRectF(rect.topRight() - QPointF(2 * radius, 0), cornerSize), 0, 90); |
|
|
|
} else { |
|
path.lineTo(rect.topRight()); |
|
} |
|
|
|
path.closeSubpath(); |
|
return path; |
|
} |
|
|
|
//________________________________________________________________________________________________________ |
|
bool Helper::compositingActive() const |
|
{ |
|
#if BREEZE_HAVE_QTX11EXTRAS |
|
if (isX11()) { |
|
return QX11Info::isCompositingManagerRunning(QX11Info::appScreen()); |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
//____________________________________________________________________ |
|
bool Helper::hasAlphaChannel(const QWidget *widget) const |
|
{ |
|
return compositingActive() && widget && widget->testAttribute(Qt::WA_TranslucentBackground); |
|
} |
|
|
|
//______________________________________________________________________________________ |
|
|
|
QPixmap Helper::coloredIcon(const QIcon &icon, const QPalette &palette, const QSize &size, QIcon::Mode mode, QIcon::State state) |
|
{ |
|
const QPalette activePalette = KIconLoader::global()->customPalette(); |
|
const bool changePalette = activePalette != palette; |
|
if (changePalette) { |
|
KIconLoader::global()->setCustomPalette(palette); |
|
} |
|
const QPixmap pixmap = icon.pixmap(size, mode, state); |
|
if (changePalette) { |
|
if (activePalette == QPalette()) { |
|
KIconLoader::global()->resetPalette(); |
|
} else { |
|
KIconLoader::global()->setCustomPalette(activePalette); |
|
} |
|
} |
|
return pixmap; |
|
} |
|
|
|
bool Helper::shouldDrawToolsArea(const QWidget *widget) const |
|
{ |
|
if (!widget) { |
|
return false; |
|
} |
|
static bool isAuto = false; |
|
static QString borderSize; |
|
if (!_cachedAutoValid) { |
|
KConfigGroup kdecorationGroup(_kwinConfig->group("org.kde.kdecoration2")); |
|
isAuto = kdecorationGroup.readEntry("BorderSizeAuto", true); |
|
borderSize = kdecorationGroup.readEntry("BorderSize", "Normal"); |
|
_cachedAutoValid = true; |
|
} |
|
if (isAuto) { |
|
auto window = widget->window(); |
|
if (qobject_cast<const QDialog *>(widget)) { |
|
return true; |
|
} |
|
if (window) { |
|
auto handle = window->windowHandle(); |
|
if (handle) { |
|
auto toolbar = qobject_cast<const QToolBar *>(widget); |
|
if (toolbar) { |
|
if (toolbar->isFloating()) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
} else { |
|
return false; |
|
} |
|
} |
|
if (borderSize != "None" && borderSize != "NoSides") { |
|
return false; |
|
} |
|
return true; |
|
} |
|
}
|
|
|