Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,83 @@
add_definitions(-DTRANSLATION_DOMAIN=\"kdecoration\")
add_subdirectory(private)
set(libkdecoration3_SRCS
decoratedwindow.cpp
decoratedwindow.h
decoration.cpp
decoration.h
decoration_p.h
decorationbutton.cpp
decorationbutton.h
decorationbutton_p.h
decorationbuttongroup.cpp
decorationbuttongroup.h
decorationbuttongroup_p.h
decorationdefines.h
decorationsettings.cpp
decorationsettings.h
decorationshadow.cpp
decorationshadow.h
decorationshadow_p.h
decorationthemeprovider.cpp
decorationthemeprovider.h
)
add_library(kdecorations3 SHARED ${libkdecoration3_SRCS})
ecm_generate_export_header(kdecorations3
VERSION ${PROJECT_VERSION}
EXPORT_FILE_NAME kdecoration3/kdecoration3_export.h
DEPRECATION_VERSIONS 5.21
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
add_library(KDecoration3::KDecoration ALIAS kdecorations3)
target_link_libraries(kdecorations3
PUBLIC
Qt::Core
Qt::Gui
PRIVATE
kdecorations3private
KF6::I18n
)
target_include_directories(kdecorations3 INTERFACE "$<INSTALL_INTERFACE:${KDECORATION3_INCLUDEDIR}>" )
set_target_properties(kdecorations3 PROPERTIES VERSION ${KDECORATION3_VERSION}
SOVERSION ${KDECORATION3_SOVERSION}
EXPORT_NAME KDecoration
)
ecm_generate_headers(KDecoration3_CamelCase_HEADERS
HEADER_NAMES
DecoratedWindow
Decoration
DecorationButton
DecorationButtonGroup
DecorationSettings
DecorationShadow
DecorationThemeProvider
ScaleHelpers
PREFIX
KDecoration3
REQUIRED_HEADERS KDecoration3_HEADERS
)
install(FILES ${KDecoration3_CamelCase_HEADERS}
DESTINATION ${KDECORATION3_INCLUDEDIR}/KDecoration3
COMPONENT Devel)
install(TARGETS kdecorations3 EXPORT KDecoration3Targets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/kdecoration3/kdecoration3_export.h
${KDecoration3_HEADERS}
decorationdefines.h
DESTINATION
${KDECORATION3_INCLUDEDIR}/kdecoration3
COMPONENT
Devel
)
@@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/kdecoration.pot
@@ -0,0 +1,226 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decoratedwindow.h"
#include "private/decoratedwindowprivate.h"
#include "private/decorationbridge.h"
#include "scalehelpers.h"
#include <QColor>
namespace KDecoration3
{
DecoratedWindow::DecoratedWindow(Decoration *parent, DecorationBridge *bridge)
: QObject()
, d(bridge->createClient(this, parent))
{
}
DecoratedWindow::~DecoratedWindow() = default;
bool DecoratedWindow::isActive() const
{
return d->isActive();
}
QString DecoratedWindow::caption() const
{
return d->caption();
}
bool DecoratedWindow::isOnAllDesktops() const
{
return d->isOnAllDesktops();
}
bool DecoratedWindow::isShaded() const
{
return d->isShaded();
}
QIcon DecoratedWindow::icon() const
{
return d->icon();
}
bool DecoratedWindow::isMaximized() const
{
return d->isMaximized();
}
bool DecoratedWindow::isMaximizedHorizontally() const
{
return d->isMaximizedHorizontally();
}
bool DecoratedWindow::isMaximizedVertically() const
{
return d->isMaximizedVertically();
}
bool DecoratedWindow::isKeepAbove() const
{
return d->isKeepAbove();
}
bool DecoratedWindow::isKeepBelow() const
{
return d->isKeepBelow();
}
bool DecoratedWindow::isCloseable() const
{
return d->isCloseable();
}
bool DecoratedWindow::isMaximizeable() const
{
return d->isMaximizeable();
}
bool DecoratedWindow::isMinimizeable() const
{
return d->isMinimizeable();
}
bool DecoratedWindow::providesContextHelp() const
{
return d->providesContextHelp();
}
bool DecoratedWindow::isModal() const
{
return d->isModal();
}
bool DecoratedWindow::isShadeable() const
{
return d->isShadeable();
}
bool DecoratedWindow::isMoveable() const
{
return d->isMoveable();
}
bool DecoratedWindow::isResizeable() const
{
return d->isResizeable();
}
qreal DecoratedWindow::width() const
{
return d->width();
}
qreal DecoratedWindow::height() const
{
return d->height();
}
QSizeF DecoratedWindow::size() const
{
return d->size();
}
QPalette DecoratedWindow::palette() const
{
return d->palette();
}
Qt::Edges DecoratedWindow::adjacentScreenEdges() const
{
return d->adjacentScreenEdges();
}
QString DecoratedWindow::windowClass() const
{
return d->windowClass();
}
bool DecoratedWindow::hasApplicationMenu() const
{
return d->hasApplicationMenu();
}
bool DecoratedWindow::isApplicationMenuActive() const
{
return d->isApplicationMenuActive();
}
Decoration *DecoratedWindow::decoration() const
{
return d->decoration();
}
QColor DecoratedWindow::color(QPalette::ColorGroup group, QPalette::ColorRole role) const
{
return d->palette().color(group, role);
}
QColor DecoratedWindow::color(ColorGroup group, ColorRole role) const
{
return d->color(group, role);
}
void DecoratedWindow::showApplicationMenu(int actionId)
{
d->showApplicationMenu(actionId);
}
qreal DecoratedWindow::scale() const
{
return d->scale();
}
qreal DecoratedWindow::nextScale() const
{
return d->nextScale();
}
qreal DecoratedWindow::pixelSize() const
{
return ::KDecoration3::pixelSize(scale());
}
qreal DecoratedWindow::snapToPixelGrid(qreal value) const
{
return ::KDecoration3::snapToPixelGrid(value, d->scale());
}
QPointF DecoratedWindow::snapToPixelGrid(const QPointF &value) const
{
return ::KDecoration3::snapToPixelGrid(value, d->scale());
}
QSizeF DecoratedWindow::snapToPixelGrid(const QSizeF &value) const
{
return ::KDecoration3::snapToPixelGrid(value, d->scale());
}
QRectF DecoratedWindow::snapToPixelGrid(const QRectF &value) const
{
return ::KDecoration3::snapToPixelGrid(value, d->scale());
}
QString DecoratedWindow::applicationMenuServiceName() const
{
if (auto impl = dynamic_cast<DecoratedWindowPrivateV2 *>(d.get())) {
return impl->applicationMenuServiceName();
}
return QString();
}
QString DecoratedWindow::applicationMenuObjectPath() const
{
if (auto impl = dynamic_cast<DecoratedWindowPrivateV2 *>(d.get())) {
return impl->applicationMenuObjectPath();
}
return QString();
}
} // namespace
#include "moc_decoratedwindow.cpp"
@@ -0,0 +1,350 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decoration.h"
#include "decorationdefines.h"
#include <kdecoration3/kdecoration3_export.h>
#include <QFont>
#include <QIcon>
#include <QObject>
#include <QPalette>
#include <QPointer>
#include <QtGui/qwindowdefs.h>
#include <memory>
namespace KDecoration3
{
class DecorationBridge;
class DecoratedWindowPrivate;
/**
* @brief The Client which gets decorated.
*
* The DecoratedWindow provides access to all the properties relevant for decorating the Client.
* Each DecoratedWindow is bound to one Decoration and each Decoration is bound to this one
* DecoratedWindow.
*
* The DecoratedWindow only exports properties, it does not provide any means to change the state.
* To change state one needs to call the methods on Decoration. This is as the backend might
* disallow state changes. Therefore any changes should be bound to the change signals of the
* DecoratedWindow and not be bound to state changes of input elements (such as a button).
*/
class KDECORATIONS3_EXPORT DecoratedWindow : public QObject
{
Q_OBJECT
/**
* The Decoration of this DecoratedWindow
**/
Q_PROPERTY(KDecoration3::Decoration *decoration READ decoration CONSTANT)
/**
* Whether the DecoratedWindow is active (has focus) or is inactive.
**/
Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
/**
* The caption of the DecoratedWindow.
**/
Q_PROPERTY(QString caption READ caption NOTIFY captionChanged)
/**
* Whether the DecoratedWindow is on all desktops or on just one.
**/
Q_PROPERTY(bool onAllDesktops READ isOnAllDesktops NOTIFY onAllDesktopsChanged)
/**
* Whether the DecoratedWindow is shaded. Shaded means that the actual content is
* not visible, only the Decoration is visible.
**/
Q_PROPERTY(bool shaded READ isShaded NOTIFY shadedChanged)
/**
* The icon of the DecoratedWindow. This can be used as the icon for the window menu button.
**/
Q_PROPERTY(QIcon icon READ icon NOTIFY iconChanged)
/**
* Whether the DecoratedWindow is maximized. A DecoratedWindow is maximized if it is both
* maximizedHorizontally and maximizedVertically. The Decoration of a maximized DecoratedWindow
* should only consist of the title bar area.
**/
Q_PROPERTY(bool maximized READ isMaximized NOTIFY maximizedChanged)
/**
* Whether the DecoratedWindow is maximized horizontally. A horizontally maximized DecoratedWindow
* uses the maximal possible width.
**/
Q_PROPERTY(bool maximizedHorizontally READ isMaximizedHorizontally NOTIFY maximizedHorizontallyChanged)
/**
* Whether the DecoratedWindow is maximized vertically. A vertically maximized DecoratedWindow
* uses the maximal possible height.
**/
Q_PROPERTY(bool maximizedVertically READ isMaximizedVertically NOTIFY maximizedVerticallyChanged)
/**
* Whether the DecoratedWindow is set to be kept above other DecoratedWindows. There can be multiple
* DecoratedWindows which are set to be kept above.
**/
Q_PROPERTY(bool keepAbove READ isKeepAbove NOTIFY keepAboveChanged)
/**
* Whether the DecoratedWindow is set to be kept below other DecoratedWindows. There can be multiple
* DecoratedWindows which are set to be kept below.
**/
Q_PROPERTY(bool keepBelow READ isKeepBelow NOTIFY keepBelowChanged)
/**
* Whether the DecoratedWindow can be closed. If this property is @c false a DecorationButton
* for closing the DecoratedWindow should be disabled.
**/
Q_PROPERTY(bool closeable READ isCloseable NOTIFY closeableChanged)
/**
* Whether the DecoratedWindow can be maximized. If this property is @c false a DecorationButton
* for maximizing the DecoratedWindow should be disabled.
**/
Q_PROPERTY(bool maximizeable READ isMaximizeable NOTIFY maximizeableChanged)
/**
* Whether the DecoratedWindow can be minimized. If this property is @c false a DecorationButton
* for minimizing the DecoratedWindow should be disabled.
**/
Q_PROPERTY(bool minimizeable READ isMinimizeable NOTIFY minimizeableChanged)
/**
* Whether the DecoratedWindow provides context help.
* The Decoration should only show a context help button if this property is @c true.
**/
Q_PROPERTY(bool providesContextHelp READ providesContextHelp NOTIFY providesContextHelpChanged)
/**
* Whether the DecoratedWindow is a modal dialog.
**/
Q_PROPERTY(bool modal READ isModal CONSTANT)
/**
* Whether the DecoratedWindow can be shaded. If this property is @c false a DecorationButton
* for shading the DecoratedWindow should be disabled.
**/
Q_PROPERTY(bool shadeable READ isShadeable NOTIFY shadeableChanged)
/**
* Whether the DecoratedWindow can be moved.
**/
Q_PROPERTY(bool moveable READ isMoveable NOTIFY moveableChanged)
/**
* Whether the DecoratedWindow can be resized.
**/
Q_PROPERTY(bool resizeable READ isResizeable NOTIFY resizeableChanged)
/**
* The width of the DecoratedWindow.
**/
Q_PROPERTY(qreal width READ width NOTIFY widthChanged)
/**
* The height of the DecoratedWindow.
**/
Q_PROPERTY(qreal height READ height NOTIFY heightChanged)
/**
* The size of the DecoratedWindow.
**/
Q_PROPERTY(QSizeF size READ size NOTIFY sizeChanged)
/**
* The palette this DecoratedWindow uses. The palette might be different for each
* DecoratedWindow and the Decoration should honor the palette.
**/
Q_PROPERTY(QPalette palette READ palette NOTIFY paletteChanged)
/**
* The Edges which are adjacent to a screen edge. E.g. for a maximized DecoratedWindow this
* will include all Edges. The Decoration can use this information to hide borders.
**/
Q_PROPERTY(Qt::Edges adjacentScreenEdges READ adjacentScreenEdges NOTIFY adjacentScreenEdgesChanged)
/**
* Whether the DecoratedWindow has an application menu
* @since 5.9
*/
Q_PROPERTY(bool hasApplicationMenu READ hasApplicationMenu NOTIFY hasApplicationMenuChanged)
/**
* Whether the application menu for this DecoratedWindow is currently shown to the user
* The Decoration can use this information to highlight the respective button.
* @since 5.9
*/
Q_PROPERTY(bool applicationMenuActive READ isApplicationMenuActive NOTIFY applicationMenuActiveChanged)
/**
* Returns the DBus service name of the application menu. If the window has no application
* menu associated with it, an empty string will be returned.
*
* @since 6.3.2
*/
Q_PROPERTY(QString applicationMenuServiceName READ applicationMenuServiceName NOTIFY applicationMenuChanged)
/**
* Returns the DBus object path of the application menu. If the window has no application
* menu associated with it, an empty string will be returned.
*
* @since 6.3.2
*/
Q_PROPERTY(QString applicationMenuObjectPath READ applicationMenuObjectPath NOTIFY applicationMenuChanged)
/**
* The current scale this decorated window is targeting.
* @since 6.3
*/
Q_PROPERTY(qreal scale READ scale NOTIFY scaleChanged);
/**
* The next scale this decorated window is going to target.
* @since 6.3
*/
Q_PROPERTY(qreal nextScale READ nextScale NOTIFY nextScaleChanged)
public:
DecoratedWindow() = delete;
~DecoratedWindow() override;
bool isActive() const;
QString caption() const;
bool isOnAllDesktops() const;
bool isShaded() const;
QIcon icon() const;
bool isMaximized() const;
bool isMaximizedHorizontally() const;
bool isMaximizedVertically() const;
bool isKeepAbove() const;
bool isKeepBelow() const;
bool isCloseable() const;
bool isMaximizeable() const;
bool isMinimizeable() const;
bool providesContextHelp() const;
bool isModal() const;
bool isShadeable() const;
bool isMoveable() const;
bool isResizeable() const;
Qt::Edges adjacentScreenEdges() const;
QString windowClass() const;
qreal width() const;
qreal height() const;
QSizeF size() const;
Decoration *decoration() const;
QPalette palette() const;
/**
* Used to get colors in QPalette.
* @param group The color group
* @param role The color role
* @return palette().color(group, role)
* @since 5.3
**/
QColor color(QPalette::ColorGroup group, QPalette::ColorRole role) const;
/**
* Used to get additional colors that are not in QPalette.
* @param group The color group
* @param role The color role
* @return The color if provided for combination of group and role, otherwise invalid QColor.
* @since 5.3
**/
QColor color(ColorGroup group, ColorRole role) const;
/**
* Whether the DecoratedWindow has an application menu
* @since 5.9
*/
bool hasApplicationMenu() const;
/**
* Returns the DBus service name of the application menu. If the window has no application
* menu associated with it, an empty string will be returned.
*
* @since 6.3.2
*/
QString applicationMenuServiceName() const;
/**
* Returns the DBus object path of the application menu. If the window has no application
* menu associated with it, an empty string will be returned.
*
* @since 6.3.2
*/
QString applicationMenuObjectPath() const;
/**
* Whether the application menu for this DecoratedWindow is currently shown to the user
* The Decoration can use this information to highlight the respective button.
* @since 5.9
*/
bool isApplicationMenuActive() const;
/**
* Request the application menu to be shown to the user
* @param actionId The DBus menu ID of the action that should be highlighted, 0 for none.
*/
void showApplicationMenu(int actionId);
/**
* Returns the current scale this decorated window is targeting
* @since 6.3
*/
qreal scale() const;
/**
* Returns the next scale this decorated window is going to target.
*/
qreal nextScale() const;
/**
* @returns the logical size of a device pixel with the current scale
* @since 6.3
*/
qreal pixelSize() const;
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid with the current scale factor
* @since 6.3
*/
qreal snapToPixelGrid(qreal value) const;
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid with the current scale factor
* @since 6.3
*/
QPointF snapToPixelGrid(const QPointF &value) const;
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid with the current scale factor
* @since 6.3
*/
QSizeF snapToPixelGrid(const QSizeF &value) const;
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid with the current scale factor
* @since 6.3
*/
QRectF snapToPixelGrid(const QRectF &value) const;
Q_SIGNALS:
void activeChanged(bool);
void captionChanged(QString);
void onAllDesktopsChanged(bool);
void shadedChanged(bool);
void iconChanged(QIcon);
void maximizedChanged(bool);
void maximizedHorizontallyChanged(bool);
void maximizedVerticallyChanged(bool);
void keepAboveChanged(bool);
void keepBelowChanged(bool);
void closeableChanged(bool);
void maximizeableChanged(bool);
void minimizeableChanged(bool);
void providesContextHelpChanged(bool);
void shadeableChanged(bool);
void moveableChanged(bool);
void resizeableChanged(bool);
void widthChanged(qreal);
void heightChanged(qreal);
void sizeChanged(const QSizeF &size);
void paletteChanged(const QPalette &palette);
void adjacentScreenEdgesChanged(Qt::Edges edges);
void hasApplicationMenuChanged(bool);
void applicationMenuActiveChanged(bool);
void scaleChanged();
void nextScaleChanged();
void applicationMenuChanged();
private:
friend class Decoration;
DecoratedWindow(Decoration *parent, DecorationBridge *bridge);
const std::unique_ptr<DecoratedWindowPrivate> d;
};
} // namespace
@@ -0,0 +1,572 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decoration.h"
#include "decoratedwindow.h"
#include "decoration_p.h"
#include "decorationbutton.h"
#include "decorationsettings.h"
#include "private/decoratedwindowprivate.h"
#include "private/decorationbridge.h"
#include <QCoreApplication>
#include <QHoverEvent>
#include <cmath>
namespace KDecoration3
{
namespace
{
DecorationBridge *findBridge(const QVariantList &args)
{
for (const auto &arg : args) {
if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value<DecorationBridge *>()) {
return bridge;
}
}
Q_UNREACHABLE();
}
}
class DecorationStateData : public QSharedData
{
public:
QMarginsF borders;
};
DecorationState::DecorationState()
: d(new DecorationStateData)
{
}
DecorationState::DecorationState(const DecorationState &other)
: d(other.d)
{
}
DecorationState::~DecorationState()
{
}
std::shared_ptr<DecorationState> DecorationState::clone() const
{
return std::make_shared<DecorationState>(*this);
}
QMarginsF DecorationState::borders() const
{
return d->borders;
}
void DecorationState::setBorders(const QMarginsF &borders)
{
d->borders = borders;
}
Decoration::Private::Private(Decoration *deco, const QVariantList &args)
: sectionUnderMouse(Qt::NoSection)
, bridge(findBridge(args))
, client(std::shared_ptr<DecoratedWindow>(new DecoratedWindow(deco, bridge)))
, opaque(false)
, q(deco)
{
}
void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
{
if (sectionUnderMouse == section) {
return;
}
sectionUnderMouse = section;
Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
}
void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
{
if (titleBar.toRect().contains(mousePosition)) {
setSectionUnderMouse(Qt::TitleBarArea);
return;
}
const QSizeF size = q->size();
const QMarginsF borders = current->borders();
const int corner = 2 * settings->largeSpacing();
const bool left = mousePosition.x() < borders.left();
const bool top = mousePosition.y() < borders.top();
const bool bottom = mousePosition.y() >= size.height() - borders.bottom();
const bool right = mousePosition.x() >= size.width() - borders.right();
if (left) {
if (top && mousePosition.y() < titleBar.top() + corner) {
setSectionUnderMouse(Qt::TopLeftSection);
} else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
setSectionUnderMouse(Qt::BottomLeftSection);
} else {
setSectionUnderMouse(Qt::LeftSection);
}
return;
}
if (right) {
if (top && mousePosition.y() < titleBar.top() + corner) {
setSectionUnderMouse(Qt::TopRightSection);
} else if (mousePosition.y() >= size.height() - borders.bottom() - corner && mousePosition.y() >= titleBar.bottom()) {
setSectionUnderMouse(Qt::BottomRightSection);
} else {
setSectionUnderMouse(Qt::RightSection);
}
return;
}
if (bottom) {
if (mousePosition.y() >= titleBar.bottom()) {
if (mousePosition.x() < borders.left() + corner) {
setSectionUnderMouse(Qt::BottomLeftSection);
} else if (mousePosition.x() >= size.width() - borders.right() - corner) {
setSectionUnderMouse(Qt::BottomRightSection);
} else {
setSectionUnderMouse(Qt::BottomSection);
}
} else {
setSectionUnderMouse(Qt::TitleBarArea);
}
return;
}
if (top) {
if (mousePosition.y() < titleBar.top()) {
if (mousePosition.x() < borders.left() + corner) {
setSectionUnderMouse(Qt::TopLeftSection);
} else if (mousePosition.x() >= size.width() - borders.right() - corner) {
setSectionUnderMouse(Qt::TopRightSection);
} else {
setSectionUnderMouse(Qt::TopSection);
}
} else {
setSectionUnderMouse(Qt::TitleBarArea);
}
return;
}
setSectionUnderMouse(Qt::NoSection);
}
void Decoration::Private::addButton(DecorationButton *button)
{
Q_ASSERT(!buttons.contains(button));
buttons << button;
QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
auto it = buttons.begin();
while (it != buttons.end()) {
if (*it == static_cast<DecorationButton *>(o)) {
it = buttons.erase(it);
} else {
it++;
}
}
});
}
Decoration::Decoration(QObject *parent, const QVariantList &args)
: QObject(parent)
, d(new Private(this, args))
{
}
Decoration::~Decoration() = default;
DecoratedWindow *Decoration::window() const
{
return d->client.get();
}
void Decoration::requestClose()
{
d->client->d->requestClose();
}
void Decoration::requestContextHelp()
{
d->client->d->requestContextHelp();
}
void Decoration::requestMinimize()
{
d->client->d->requestMinimize();
}
void Decoration::requestToggleOnAllDesktops()
{
d->client->d->requestToggleOnAllDesktops();
}
void Decoration::requestToggleShade()
{
d->client->d->requestToggleShade();
}
void Decoration::requestToggleKeepAbove()
{
d->client->d->requestToggleKeepAbove();
}
void Decoration::requestToggleKeepBelow()
{
d->client->d->requestToggleKeepBelow();
}
#if KDECORATIONS3_ENABLE_DEPRECATED_SINCE(5, 21)
void Decoration::requestShowWindowMenu()
{
requestShowWindowMenu(QRect());
}
#endif
void Decoration::requestShowWindowMenu(const QRect &rect)
{
d->client->d->requestShowWindowMenu(rect);
}
void Decoration::requestShowToolTip(const QString &text)
{
d->client->d->requestShowToolTip(text);
}
void Decoration::requestHideToolTip()
{
d->client->d->requestHideToolTip();
}
void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
{
d->client->d->requestToggleMaximization(buttons);
}
void Decoration::showApplicationMenu(int actionId)
{
const auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
return button->type() == DecorationButtonType::ApplicationMenu;
});
if (it != d->buttons.constEnd()) {
requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
}
}
void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
{
d->client->d->requestShowApplicationMenu(rect, actionId);
}
void Decoration::setBlurRegion(const QRegion &region)
{
if (d->blurRegion != region) {
d->blurRegion = region;
Q_EMIT blurRegionChanged();
}
}
void Decoration::setBorders(const QMarginsF &borders)
{
if (d->next->borders() != borders) {
setState([borders](DecorationState *state) {
state->setBorders(borders);
});
}
}
void Decoration::setResizeOnlyBorders(const QMarginsF &borders)
{
if (d->resizeOnlyBorders != borders) {
d->resizeOnlyBorders = borders;
Q_EMIT resizeOnlyBordersChanged();
}
}
void Decoration::setTitleBar(const QRectF &rect)
{
if (d->titleBar != rect) {
d->titleBar = rect;
Q_EMIT titleBarChanged();
}
}
void Decoration::setOpaque(bool opaque)
{
if (d->opaque != opaque) {
d->opaque = opaque;
Q_EMIT opaqueChanged(opaque);
}
}
void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow)
{
if (d->shadow != shadow) {
d->shadow = shadow;
Q_EMIT shadowChanged(shadow);
}
}
QRegion Decoration::blurRegion() const
{
return d->blurRegion;
}
QMarginsF Decoration::borders() const
{
return d->current->borders();
}
QMarginsF Decoration::resizeOnlyBorders() const
{
return d->resizeOnlyBorders;
}
QRectF Decoration::titleBar() const
{
return d->titleBar;
}
Qt::WindowFrameSection Decoration::sectionUnderMouse() const
{
return d->sectionUnderMouse;
}
std::shared_ptr<DecorationShadow> Decoration::shadow() const
{
return d->shadow;
}
bool Decoration::isOpaque() const
{
return d->opaque;
}
qreal Decoration::borderLeft() const
{
return d->current->borders().left();
}
qreal Decoration::resizeOnlyBorderLeft() const
{
return d->resizeOnlyBorders.left();
}
qreal Decoration::borderRight() const
{
return d->current->borders().right();
}
qreal Decoration::resizeOnlyBorderRight() const
{
return d->resizeOnlyBorders.right();
}
qreal Decoration::borderTop() const
{
return d->current->borders().top();
}
qreal Decoration::resizeOnlyBorderTop() const
{
return d->resizeOnlyBorders.top();
}
qreal Decoration::borderBottom() const
{
return d->current->borders().bottom();
}
qreal Decoration::resizeOnlyBorderBottom() const
{
return d->resizeOnlyBorders.bottom();
}
QSizeF Decoration::size() const
{
const QMarginsF b = d->current->borders();
return QSizeF(d->client->width() + b.left() + b.right(), (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
}
QRectF Decoration::rect() const
{
return QRectF(QPointF(0, 0), size());
}
bool Decoration::event(QEvent *event)
{
switch (event->type()) {
case QEvent::HoverEnter:
hoverEnterEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::HoverLeave:
hoverLeaveEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::HoverMove:
hoverMoveEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::MouseButtonPress:
mousePressEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent *>(event));
return true;
default:
return QObject::event(event);
}
}
void Decoration::hoverEnterEvent(QHoverEvent *event)
{
for (DecorationButton *button : d->buttons) {
QCoreApplication::instance()->sendEvent(button, event);
}
auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
d->updateSectionUnderMouse(flooredPos);
}
void Decoration::hoverLeaveEvent(QHoverEvent *event)
{
for (DecorationButton *button : d->buttons) {
QCoreApplication::instance()->sendEvent(button, event);
}
d->setSectionUnderMouse(Qt::NoSection);
}
void Decoration::hoverMoveEvent(QHoverEvent *event)
{
for (DecorationButton *button : d->buttons) {
if (!button->isEnabled() || !button->isVisible()) {
continue;
}
const bool hovered = button->isHovered();
const bool contains = button->contains(event->position());
if (!hovered && contains) {
QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers());
QCoreApplication::instance()->sendEvent(button, &e);
} else if (hovered && !contains) {
QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers());
QCoreApplication::instance()->sendEvent(button, &e);
} else if (hovered && contains) {
QCoreApplication::instance()->sendEvent(button, event);
}
}
auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y()));
d->updateSectionUnderMouse(flooredPos);
}
void Decoration::mouseMoveEvent(QMouseEvent *event)
{
for (DecorationButton *button : d->buttons) {
if (button->isPressed()) {
QCoreApplication::instance()->sendEvent(button, event);
return;
}
}
// not handled, take care ourselves
}
void Decoration::mousePressEvent(QMouseEvent *event)
{
for (DecorationButton *button : d->buttons) {
if (button->isHovered()) {
if (button->acceptedButtons().testFlag(event->button())) {
QCoreApplication::instance()->sendEvent(button, event);
}
event->setAccepted(true);
return;
}
}
}
void Decoration::mouseReleaseEvent(QMouseEvent *event)
{
for (DecorationButton *button : d->buttons) {
if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
QCoreApplication::instance()->sendEvent(button, event);
return;
}
}
// not handled, take care ourselves
d->updateSectionUnderMouse(event->pos());
}
void Decoration::wheelEvent(QWheelEvent *event)
{
for (DecorationButton *button : d->buttons) {
if (button->contains(event->position())) {
QCoreApplication::instance()->sendEvent(button, event);
event->setAccepted(true);
}
}
}
void Decoration::update(const QRectF &r)
{
Q_EMIT damaged(r.isNull() ? rect().toAlignedRect() : r.toAlignedRect());
}
void Decoration::update()
{
update(QRect());
}
void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
{
d->settings = settings;
}
std::shared_ptr<DecorationSettings> Decoration::settings() const
{
return d->settings;
}
std::shared_ptr<DecorationState> Decoration::createState()
{
return std::make_shared<DecorationState>();
}
std::shared_ptr<DecorationState> Decoration::currentState() const
{
return d->current;
}
std::shared_ptr<DecorationState> Decoration::nextState() const
{
return d->next;
}
void Decoration::create()
{
d->next = createState();
d->current = createState();
}
void Decoration::setState(std::function<void(DecorationState *state)> callback)
{
callback(d->next.get());
Q_EMIT nextStateChanged(d->next);
}
void Decoration::apply(std::shared_ptr<DecorationState> state)
{
if (d->current == state) {
return;
}
const auto previous = d->current;
d->current = state;
update();
if (previous->borders() != state->borders()) {
Q_EMIT bordersChanged();
}
Q_EMIT currentStateChanged(state);
}
} // namespace
#include "moc_decoration.cpp"
@@ -0,0 +1,333 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationshadow.h"
#include <kdecoration3/kdecoration3_export.h>
#include <QMarginsF>
#include <QObject>
#include <QRectF>
#include <QSharedDataPointer>
class QHoverEvent;
class QMouseEvent;
class QPainter;
class QWheelEvent;
/**
* @brief Framework for creating window decorations.
*
**/
namespace KDecoration3
{
class DecorationPrivate;
class DecoratedWindow;
class DecorationButton;
class DecorationSettings;
class DecorationStateData;
/**
* \brief Decoration state.
*
* The DecorationState type represents double bufferred state associated with a decoration.
*
* \note Sub-classes of DecorationState must override the clone() function.
*/
class KDECORATIONS3_EXPORT DecorationState
{
public:
DecorationState();
DecorationState(const DecorationState &other);
virtual ~DecorationState();
virtual std::shared_ptr<DecorationState> clone() const;
QMarginsF borders() const;
void setBorders(const QMarginsF &margins);
private:
QSharedDataPointer<DecorationStateData> d;
};
/**
* @brief Base class for the Decoration.
*
* To provide a Decoration one needs to inherit from this class. The framework will instantiate
* an instance of the inherited class for each DecoratedWindow.
*
* The main tasks of the Decoration is to provide borders around the DecoratedWindow. For this
* the Deocration provides border sizes: those should be adjusted depending on the state of the
* DecoratedWindow. E.g. commonly a maximized DecoratedWindow does not have borders on the side,
* only the title bar.
*
* Whenever the visual representation of the Decoration changes the slot @link Decoration::update @endlink
* should be invoked to request a repaint. The framework will in return invoke the
* @link Decoration::paint @endlink method. This method needs to be implemented by inheriting
* classes.
*
* A Decoration commonly provides buttons for interaction. E.g. a close button to close the
* DecoratedWindow. For such actions the Decoration provides slots which should be connected to
* the clicked signals of the buttons. For convenience the framework provides the @link DecorationButton @endlink
* and the @link DecorationButtonGroup @endlink for easier layout. It is not required to use those,
* if one uses different ways to represent the actions one needs to filter the events accordingly.
*
* @see DecoratedWindow
* @see DecorationButton
* @see DecorationButtonGroup
**/
class KDECORATIONS3_EXPORT Decoration : public QObject
{
Q_OBJECT
Q_PROPERTY(QMarginsF borders READ borders NOTIFY bordersChanged)
Q_PROPERTY(qreal borderLeft READ borderLeft NOTIFY bordersChanged)
Q_PROPERTY(qreal borderRight READ borderRight NOTIFY bordersChanged)
Q_PROPERTY(qreal borderTop READ borderTop NOTIFY bordersChanged)
Q_PROPERTY(qreal borderBottom READ borderBottom NOTIFY bordersChanged)
Q_PROPERTY(QMarginsF resizeOnlyBorders READ resizeOnlyBorders NOTIFY resizeOnlyBordersChanged)
Q_PROPERTY(qreal resizeOnlyBorderLeft READ resizeOnlyBorderLeft NOTIFY resizeOnlyBordersChanged)
Q_PROPERTY(qreal resizeOnlyBorderRight READ resizeOnlyBorderRight NOTIFY resizeOnlyBordersChanged)
Q_PROPERTY(qreal resizeOnlyBorderTop READ resizeOnlyBorderTop NOTIFY resizeOnlyBordersChanged)
Q_PROPERTY(qreal resizeOnlyBorderBottom READ resizeOnlyBorderBottom NOTIFY resizeOnlyBordersChanged)
/**
* This property denotes the part of the Decoration which is currently under the mouse pointer.
* It gets automatically updated whenever a QMouseEvent or QHoverEvent gets processed.
**/
Q_PROPERTY(Qt::WindowFrameSection sectionUnderMouse READ sectionUnderMouse NOTIFY sectionUnderMouseChanged)
/**
* The titleBar is the area inside the Decoration containing all controls (e.g. Buttons)
* and the caption. The titleBar is the main interaction area, while all other areas of the
* Decoration are normally used as resize areas.
**/
Q_PROPERTY(QRectF titleBar READ titleBar NOTIFY titleBarChanged)
/**
* Whether the Decoration is fully opaque. By default a Decoration is considered to
* use the alpha channel and this property has the value @c false. But for e.g. a maximized
* DecoratedWindow it is possible that the Decoration is fully opaque. In this case the
* Decoration should set this property to @c true.
**/
Q_PROPERTY(bool opaque READ isOpaque NOTIFY opaqueChanged)
public:
~Decoration() override;
/**
* The DecoratedWindow for this Decoration.
**/
DecoratedWindow *window() const;
QMarginsF borders() const;
qreal borderLeft() const;
qreal borderRight() const;
qreal borderTop() const;
qreal borderBottom() const;
QMarginsF resizeOnlyBorders() const;
qreal resizeOnlyBorderLeft() const;
qreal resizeOnlyBorderRight() const;
qreal resizeOnlyBorderTop() const;
qreal resizeOnlyBorderBottom() const;
Qt::WindowFrameSection sectionUnderMouse() const;
QRectF titleBar() const;
bool isOpaque() const;
/**
* DecorationShadow for this Decoration. It is recommended that multiple Decorations share
* the same DecorationShadow. E.g one DecorationShadow for all inactive Decorations and one
* for the active Decoration.
**/
std::shared_ptr<DecorationShadow> shadow() const;
/**
* The decoration's geometry in local coordinates.
*
* Basically the size of the DecoratedWindow combined with the borders.
**/
QRectF rect() const;
QSizeF size() const;
/**
* The decoration's blur region in local coordinates
*/
QRegion blurRegion() const;
/**
* Invoked by the framework to set the Settings for this Decoration before
* init is invoked.
* @internal
**/
void setSettings(const std::shared_ptr<DecorationSettings> &settings);
/**
* @returns The DecorationSettings used for this Decoration.
**/
std::shared_ptr<DecorationSettings> settings() const;
/**
* Implement this method in inheriting classes to provide the rendering.
*
* The @p painter is set up to paint on an internal QPaintDevice. The painting is
* implicitly double buffered.
*
* @param painter The painter which needs to be used for rendering
* @param repaintArea The region which needs to be repainted.
**/
virtual void paint(QPainter *painter, const QRectF &repaintArea) = 0;
bool event(QEvent *event) override;
/**
* \internal
*
* Allocates the resources associated with the decoration, for example state containers.
*
* \note This method gets invoked by the compositor before init(), the decoration implementation
* must not call it.
*/
void create();
/**
* \internal
*
* Make the specified \a state current.
*
* The decoration maintains a double-buffered state. If a double-buffered property needs
* to be changed, the next state will be updated and the nextStateChanged() signal will be
* emitted to notify the compositor about it.
*
* When the next state gets applied is subject to compositor policies. For example, the
* compositor may apply the new state immediately, or it can synchronize double-buffered
* decoration state with double-buffered toplevel state.
*
* \sa currentState(), nextState(), createState()
*/
void apply(std::shared_ptr<DecorationState> state);
/**
* Returns the currently applied state.
*
* \sa apply()
*/
std::shared_ptr<DecorationState> currentState() const;
/**
* Returns the next state, i.e. the state that the decoration implementation wants to be current.
*
* \sa apply()
*/
std::shared_ptr<DecorationState> nextState() const;
/**
* Notifies the framework that the decoration state has changed. When the new state is applied
* is subject to compositor policies. For example, the compositor may re-configure the window
* and apply the new state when the window is repainted.
*/
void setState(std::function<void(DecorationState *state)> callback);
public Q_SLOTS:
void requestClose();
void requestToggleMaximization(Qt::MouseButtons buttons);
void requestMinimize();
void requestContextHelp();
void requestToggleOnAllDesktops();
void requestToggleShade();
void requestToggleKeepAbove();
void requestToggleKeepBelow();
#if KDECORATIONS3_ENABLE_DEPRECATED_SINCE(5, 21)
/**
* @deprecated
* @see requestShowWindowMenu(const QRect &rect)
*/
KDECORATIONS3_DEPRECATED_VERSION(5, 21, "Use Decoration::requestShowWindowMenu(QRect)")
void requestShowWindowMenu();
#endif
/**
* @param rect the location at which to show the window menu
*/
void requestShowWindowMenu(const QRect &rect);
void requestShowToolTip(const QString &text);
void requestHideToolTip();
void showApplicationMenu(int actionId);
void requestShowApplicationMenu(const QRect &rect, int actionId);
void update(const QRectF &rect);
void update();
/**
* This method gets invoked from the framework once the Decoration is created and
* completely setup.
*
* An inheriting class should override this method and perform all initialization in
* this method instead of the constructor.
*
* @return true if initialization has been successful,
* false otherwise (for example, a QML component could not be loaded)
**/
virtual bool init() = 0;
Q_SIGNALS:
void blurRegionChanged();
void bordersChanged();
void resizeOnlyBordersChanged();
void sectionUnderMouseChanged(Qt::WindowFrameSection);
void titleBarChanged();
void opaqueChanged(bool);
void shadowChanged(const std::shared_ptr<DecorationShadow> &shadow);
void damaged(const QRegion &region);
void currentStateChanged(std::shared_ptr<DecorationState> state);
void nextStateChanged(std::shared_ptr<DecorationState> state);
protected:
/**
* Constructor for the Decoration.
*
* The @p args are used by the decoration framework to pass meta information
* to the Decoration. An inheriting class is supposed to pass the args to the
* parent class.
*
* @param parent The parent of the Decoration
* @param args Additional arguments passed in from the framework
**/
explicit Decoration(QObject *parent, const QVariantList &args);
void setBorders(const QMarginsF &borders);
void setResizeOnlyBorders(const QMarginsF &borders);
void setBlurRegion(const QRegion &region);
/**
* An implementation has to invoke this method whenever the area
* containing the controls and caption changes.
* @param rect The new geometry of the titleBar in Decoration coordinates
**/
void setTitleBar(const QRectF &rect);
void setOpaque(bool opaque);
void setShadow(const std::shared_ptr<DecorationShadow> &shadow);
virtual void hoverEnterEvent(QHoverEvent *event);
virtual void hoverLeaveEvent(QHoverEvent *event);
virtual void hoverMoveEvent(QHoverEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);
/**
* Create a state container. The decoration implementation can override this method to attach
* its own properties to the decoration state.
*
* The default implementation simply creates an instance of the DecorationState type.
*
* \sa currentState(), nextState()
*/
virtual std::shared_ptr<DecorationState> createState();
private:
friend class DecorationButton;
class Private;
std::unique_ptr<Private> d;
};
} // namespace
Q_DECLARE_METATYPE(KDecoration3::Decoration *)
@@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decoration.h"
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
namespace KDecoration3
{
class Decoration;
class DecorationBridge;
class DecorationButton;
class DecoratedWindow;
class DecorationSettings;
class DecorationShadow;
class Q_DECL_HIDDEN Decoration::Private
{
public:
Private(Decoration *decoration, const QVariantList &args);
QMarginsF resizeOnlyBorders;
Qt::WindowFrameSection sectionUnderMouse;
void setSectionUnderMouse(Qt::WindowFrameSection section);
void updateSectionUnderMouse(const QPoint &mousePosition);
QRectF titleBar;
QRegion blurRegion;
void addButton(DecorationButton *button);
std::shared_ptr<DecorationSettings> settings;
DecorationBridge *bridge;
std::shared_ptr<DecoratedWindow> client;
bool opaque;
QList<DecorationButton *> buttons;
std::shared_ptr<DecorationShadow> shadow;
std::shared_ptr<DecorationState> next;
std::shared_ptr<DecorationState> current;
private:
Decoration *q;
};
} // namespace
@@ -0,0 +1,601 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationbutton.h"
#include "decoratedwindow.h"
#include "decoration.h"
#include "decoration_p.h"
#include "decorationbutton_p.h"
#include "decorationsettings.h"
#include <KLocalizedString>
#include <QDebug>
#include <QElapsedTimer>
#include <QGuiApplication>
#include <QHoverEvent>
#include <QStyleHints>
#include <QTimer>
#include <cmath>
#ifndef K_DOXYGEN
size_t qHash(const KDecoration3::DecorationButtonType &type, size_t seed)
{
return qHash(uint(type), seed);
}
#endif
namespace KDecoration3
{
DecorationButton::Private::Private(DecorationButtonType type, const QPointer<Decoration> &decoration, DecorationButton *parent)
: decoration(decoration)
, type(type)
, hovered(false)
, enabled(true)
, checkable(false)
, checked(false)
, visible(true)
, acceptedButtons(Qt::LeftButton)
, doubleClickEnabled(false)
, pressAndHold(false)
, q(parent)
, m_pressed(Qt::NoButton)
{
init();
}
DecorationButton::Private::~Private() = default;
void DecorationButton::Private::init()
{
auto c = decoration->window();
auto settings = decoration->settings();
switch (type) {
case DecorationButtonType::Menu:
QObject::connect(
q,
&DecorationButton::clicked,
decoration.data(),
[this](Qt::MouseButton button) {
Q_UNUSED(button)
decoration->requestShowWindowMenu(q->geometry().toRect());
},
Qt::QueuedConnection);
QObject::connect(q, &DecorationButton::doubleClicked, decoration.data(), &Decoration::requestClose, Qt::QueuedConnection);
QObject::connect(
settings.get(),
&DecorationSettings::closeOnDoubleClickOnMenuChanged,
q,
[this](bool enabled) {
doubleClickEnabled = enabled;
setPressAndHold(enabled);
},
Qt::QueuedConnection);
doubleClickEnabled = settings->isCloseOnDoubleClickOnMenu();
setPressAndHold(settings->isCloseOnDoubleClickOnMenu());
setAcceptedButtons(Qt::LeftButton | Qt::RightButton);
break;
case DecorationButtonType::ApplicationMenu:
setVisible(c->hasApplicationMenu());
setCheckable(true); // will be "checked" whilst the menu is opened
// FIXME TODO connect directly and figure out the button geometry/offset stuff
QObject::connect(
q,
&DecorationButton::clicked,
decoration.data(),
[this] {
decoration->requestShowApplicationMenu(q->geometry().toRect(), 0 /* actionId */);
},
Qt::QueuedConnection); //&Decoration::requestShowApplicationMenu, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::hasApplicationMenuChanged, q, &DecorationButton::setVisible);
QObject::connect(c, &DecoratedWindow::applicationMenuActiveChanged, q, &DecorationButton::setChecked);
break;
case DecorationButtonType::OnAllDesktops:
setVisible(settings->isOnAllDesktopsAvailable());
setCheckable(true);
setChecked(c->isOnAllDesktops());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleOnAllDesktops, Qt::QueuedConnection);
QObject::connect(settings.get(), &DecorationSettings::onAllDesktopsAvailableChanged, q, &DecorationButton::setVisible);
QObject::connect(c, &DecoratedWindow::onAllDesktopsChanged, q, &DecorationButton::setChecked);
break;
case DecorationButtonType::Minimize:
setEnabled(c->isMinimizeable());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestMinimize, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::minimizeableChanged, q, &DecorationButton::setEnabled);
break;
case DecorationButtonType::Maximize:
setEnabled(c->isMaximizeable());
setCheckable(true);
setChecked(c->isMaximized());
setAcceptedButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleMaximization, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::maximizeableChanged, q, &DecorationButton::setEnabled);
QObject::connect(c, &DecoratedWindow::maximizedChanged, q, &DecorationButton::setChecked);
break;
case DecorationButtonType::Close:
setEnabled(c->isCloseable());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestClose, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::closeableChanged, q, &DecorationButton::setEnabled);
break;
case DecorationButtonType::ContextHelp:
setVisible(c->providesContextHelp());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestContextHelp, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::providesContextHelpChanged, q, &DecorationButton::setVisible);
break;
case DecorationButtonType::KeepAbove:
setCheckable(true);
setChecked(c->isKeepAbove());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleKeepAbove, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::keepAboveChanged, q, &DecorationButton::setChecked);
break;
case DecorationButtonType::KeepBelow:
setCheckable(true);
setChecked(c->isKeepBelow());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleKeepBelow, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::keepBelowChanged, q, &DecorationButton::setChecked);
break;
case DecorationButtonType::Shade:
setEnabled(c->isShadeable());
setCheckable(true);
setChecked(c->isShaded());
QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleShade, Qt::QueuedConnection);
QObject::connect(c, &DecoratedWindow::shadedChanged, q, &DecorationButton::setChecked);
QObject::connect(c, &DecoratedWindow::shadeableChanged, q, &DecorationButton::setEnabled);
break;
case DecorationButtonType::Spacer:
setEnabled(false);
break;
default:
// nothing
break;
}
}
void DecorationButton::Private::setHovered(bool set)
{
if (hovered == set) {
return;
}
hovered = set;
Q_EMIT q->hoveredChanged(hovered);
}
void DecorationButton::Private::setEnabled(bool set)
{
if (enabled == set) {
return;
}
enabled = set;
Q_EMIT q->enabledChanged(enabled);
if (!enabled) {
setHovered(false);
if (isPressed()) {
m_pressed = Qt::NoButton;
Q_EMIT q->pressedChanged(false);
}
}
}
void DecorationButton::Private::setVisible(bool set)
{
if (visible == set) {
return;
}
visible = set;
Q_EMIT q->visibilityChanged(set);
if (!visible) {
setHovered(false);
if (isPressed()) {
m_pressed = Qt::NoButton;
Q_EMIT q->pressedChanged(false);
}
}
}
void DecorationButton::Private::setChecked(bool set)
{
if (!checkable || checked == set) {
return;
}
checked = set;
Q_EMIT q->checkedChanged(checked);
}
void DecorationButton::Private::setCheckable(bool set)
{
if (checkable == set) {
return;
}
if (!set) {
setChecked(false);
}
checkable = set;
Q_EMIT q->checkableChanged(checkable);
}
void DecorationButton::Private::setPressed(Qt::MouseButton button, bool pressed)
{
if (pressed) {
m_pressed = m_pressed | button;
} else {
m_pressed = m_pressed & ~button;
}
Q_EMIT q->pressedChanged(isPressed());
}
void DecorationButton::Private::setAcceptedButtons(Qt::MouseButtons buttons)
{
if (acceptedButtons == buttons) {
return;
}
acceptedButtons = buttons;
Q_EMIT q->acceptedButtonsChanged(acceptedButtons);
}
void DecorationButton::Private::startDoubleClickTimer()
{
if (!doubleClickEnabled) {
return;
}
if (!m_doubleClickTimer) {
m_doubleClickTimer = std::make_unique<QElapsedTimer>();
}
m_doubleClickTimer->start();
}
void DecorationButton::Private::invalidateDoubleClickTimer()
{
if (!m_doubleClickTimer) {
return;
}
m_doubleClickTimer->invalidate();
}
bool DecorationButton::Private::wasDoubleClick() const
{
if (!m_doubleClickTimer || !m_doubleClickTimer->isValid()) {
return false;
}
return !m_doubleClickTimer->hasExpired(QGuiApplication::styleHints()->mouseDoubleClickInterval());
}
void DecorationButton::Private::setPressAndHold(bool enable)
{
if (pressAndHold == enable) {
return;
}
pressAndHold = enable;
if (!pressAndHold) {
m_pressAndHoldTimer.reset();
}
}
void DecorationButton::Private::startPressAndHold()
{
if (!pressAndHold) {
return;
}
if (!m_pressAndHoldTimer) {
m_pressAndHoldTimer.reset(new QTimer());
m_pressAndHoldTimer->setSingleShot(true);
QObject::connect(m_pressAndHoldTimer.get(), &QTimer::timeout, q, [this]() {
q->clicked(Qt::LeftButton);
});
}
m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
}
void DecorationButton::Private::stopPressAndHold()
{
if (m_pressAndHoldTimer) {
m_pressAndHoldTimer->stop();
}
}
QString DecorationButton::Private::typeToString(DecorationButtonType type)
{
switch (type) {
case DecorationButtonType::Menu:
return i18n("More actions for this window");
case DecorationButtonType::ApplicationMenu:
return i18n("Application menu");
case DecorationButtonType::OnAllDesktops:
if (this->q->isChecked())
return i18n("On one desktop");
else
return i18n("On all desktops");
case DecorationButtonType::Minimize:
return i18n("Minimize");
case DecorationButtonType::Maximize:
if (this->q->isChecked())
return i18n("Restore");
else
return i18n("Maximize");
case DecorationButtonType::Close:
return i18n("Close");
case DecorationButtonType::ContextHelp:
return i18n("Context help");
case DecorationButtonType::Shade:
if (this->q->isChecked())
return i18n("Unshade");
else
return i18n("Shade");
case DecorationButtonType::KeepBelow:
if (this->q->isChecked())
return i18n("Don't keep below other windows");
else
return i18n("Keep below other windows");
case DecorationButtonType::KeepAbove:
if (this->q->isChecked())
return i18n("Don't keep above other windows");
else
return i18n("Keep above other windows");
default:
return QString();
}
}
DecorationButton::DecorationButton(DecorationButtonType type, Decoration *decoration, QObject *parent)
: QObject(parent)
, d(new Private(type, decoration, this))
{
decoration->d->addButton(this);
connect(this, &DecorationButton::geometryChanged, this, static_cast<void (DecorationButton::*)(const QRectF &)>(&DecorationButton::update));
auto updateSlot = static_cast<void (DecorationButton::*)()>(&DecorationButton::update);
connect(this, &DecorationButton::hoveredChanged, this, updateSlot);
connect(this, &DecorationButton::hoveredChanged, this, [this](bool hovered) {
if (hovered) {
// TODO: show tooltip if hovered and hide if not
const QString type = this->d->typeToString(this->type());
this->decoration()->requestShowToolTip(type);
} else {
this->decoration()->requestHideToolTip();
}
});
connect(this, &DecorationButton::pressedChanged, this, updateSlot);
connect(this, &DecorationButton::pressedChanged, this, [this](bool pressed) {
if (pressed) {
this->decoration()->requestHideToolTip();
}
});
connect(this, &DecorationButton::checkedChanged, this, updateSlot);
connect(this, &DecorationButton::enabledChanged, this, updateSlot);
connect(this, &DecorationButton::visibilityChanged, this, updateSlot);
connect(this, &DecorationButton::hoveredChanged, this, [this](bool hovered) {
if (hovered) {
Q_EMIT pointerEntered();
} else {
Q_EMIT pointerLeft();
}
});
connect(this, &DecorationButton::pressedChanged, this, [this](bool p) {
if (p) {
Q_EMIT pressed();
} else {
Q_EMIT released();
}
});
}
DecorationButton::~DecorationButton() = default;
void DecorationButton::update(const QRectF &rect)
{
decoration()->update(rect.isNull() ? geometry().toRect() : rect.toRect());
}
void DecorationButton::update()
{
update(QRectF());
}
QSizeF DecorationButton::size() const
{
return d->geometry.size();
}
bool DecorationButton::isPressed() const
{
return d->isPressed();
}
bool DecorationButton::isHovered() const
{
return d->hovered;
}
bool DecorationButton::isEnabled() const
{
return d->enabled;
}
bool DecorationButton::isChecked() const
{
return d->checked;
}
bool DecorationButton::isCheckable() const
{
return d->checkable;
}
bool DecorationButton::isVisible() const
{
return d->visible;
}
QRectF DecorationButton::geometry() const
{
return d->geometry;
}
Decoration *DecorationButton::decoration() const
{
return d->decoration;
}
Qt::MouseButtons DecorationButton::acceptedButtons() const
{
return d->acceptedButtons;
}
DecorationButtonType DecorationButton::type() const
{
return d->type;
}
void DecorationButton::setAcceptedButtons(Qt::MouseButtons value)
{
d->setAcceptedButtons(value);
}
void DecorationButton::setEnabled(bool value)
{
d->setEnabled(value);
}
void DecorationButton::setChecked(bool value)
{
d->setChecked(value);
}
void DecorationButton::setCheckable(bool value)
{
d->setCheckable(value);
}
void DecorationButton::setVisible(bool value)
{
d->setVisible(value);
}
void DecorationButton::setGeometry(const QRectF &geometry)
{
if (d->geometry == geometry) {
return;
}
d->geometry = geometry;
Q_EMIT geometryChanged(d->geometry);
}
bool DecorationButton::contains(const QPointF &pos) const
{
auto flooredPoint = QPoint(std::floor(pos.x()), std::floor(pos.y()));
return d->geometry.toRect().contains(flooredPoint);
}
bool DecorationButton::event(QEvent *event)
{
switch (event->type()) {
case QEvent::HoverEnter:
hoverEnterEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::HoverLeave:
hoverLeaveEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::HoverMove:
hoverMoveEvent(static_cast<QHoverEvent *>(event));
return true;
case QEvent::MouseButtonPress:
mousePressEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent *>(event));
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent *>(event));
return true;
default:
return QObject::event(event);
}
}
void DecorationButton::hoverEnterEvent(QHoverEvent *event)
{
if (!d->enabled || !d->visible || !contains(event->position())) {
return;
}
d->setHovered(true);
event->setAccepted(true);
}
void DecorationButton::hoverLeaveEvent(QHoverEvent *event)
{
if (!d->enabled || !d->visible || !d->hovered || contains(event->position())) {
return;
}
d->setHovered(false);
event->setAccepted(true);
}
void DecorationButton::hoverMoveEvent(QHoverEvent *event)
{
Q_UNUSED(event)
}
void DecorationButton::mouseMoveEvent(QMouseEvent *event)
{
if (!d->enabled || !d->visible || !d->hovered) {
return;
}
if (!contains(event->position())) {
d->setHovered(false);
event->setAccepted(true);
}
}
void DecorationButton::mousePressEvent(QMouseEvent *event)
{
if (!d->enabled || !d->visible || !contains(event->position()) || !d->acceptedButtons.testFlag(event->button())) {
return;
}
d->setPressed(event->button(), true);
event->setAccepted(true);
if (d->doubleClickEnabled && event->button() == Qt::LeftButton) {
// check for double click
if (d->wasDoubleClick()) {
event->setAccepted(true);
Q_EMIT doubleClicked();
}
d->invalidateDoubleClickTimer();
}
if (d->pressAndHold && event->button() == Qt::LeftButton) {
d->startPressAndHold();
}
}
void DecorationButton::mouseReleaseEvent(QMouseEvent *event)
{
if (!d->enabled || !d->visible || !d->isPressed(event->button())) {
return;
}
if (contains(event->position())) {
if (!d->pressAndHold || event->button() != Qt::LeftButton) {
Q_EMIT clicked(event->button());
} else {
d->stopPressAndHold();
}
}
d->setPressed(event->button(), false);
event->setAccepted(true);
if (d->doubleClickEnabled && event->button() == Qt::LeftButton) {
d->startDoubleClickTimer();
}
}
void DecorationButton::wheelEvent(QWheelEvent *event)
{
Q_UNUSED(event)
}
}
#include "moc_decorationbutton.cpp"
@@ -0,0 +1,179 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationdefines.h"
#include <kdecoration3/kdecoration3_export.h>
#include <QObject>
#include <QRect>
class QHoverEvent;
class QMouseEvent;
class QPainter;
class QWheelEvent;
namespace KDecoration3
{
class DecorationButtonPrivate;
class Decoration;
/**
* @brief A button to be used in a Decoration.
*
* The DecorationButton is a simple Button which can be used (but doesn't have to) in a Decoration.
* It takes care of the input handling and triggers the correct state change methods on the
* Decoration.
*
* This simplifies the handling of DecorationButtons. A Decoration implementation just needs to
* subclass DecorationButton and implement the paint method. Everything else is handled by the
* DecorationButton.
*
* For positioning the DecorationButtons it's recommended to use a DecorationButtonGroup.
*
* @see Decoration
* @see DecorationButtonGroup
**/
class KDECORATIONS3_EXPORT DecorationButton : public QObject
{
Q_OBJECT
/**
* Whether the DecorationButton is visible. By default this is @c true, OnAllDesktops and
* QuickHelp depend on the DecoratedWindow's state.
**/
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
/**
* Whether the DecorationButton is currently pressed.
**/
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
/**
* Whether the DecorationButton is currently hovered.
**/
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
/**
* Whether the DecorationButton is enabled. Only an enabled button accepts hover and mouse
* press events.
**/
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
/**
* Whether the DecorationButton can be checked. This is used for state aware DecorationButtons
* like Maximize, Shade, KeepAbove, KeepBelow and OnAllDesktops.
**/
Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged)
/**
* Whether the DecorationButton is checked. A DecorationButton can only be checked if the
* DecorationButton is checkable. Note: the checked state is not changed by clicking the
* DecorationButton. It gets changed if the DecoratedWindow changes it's state, though.
**/
Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY checkedChanged)
/**
* The geometry of the DecorationButton in Decoration-local coordinates.
**/
Q_PROPERTY(QRectF geometry READ geometry NOTIFY geometryChanged)
/**
* The mouse buttons the DecorationButton accepts. By default the Qt::LeftButton gets accepted,
* for some types more buttons are accepted.
**/
Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
public:
~DecorationButton() override;
QRectF geometry() const;
QSizeF size() const;
void setGeometry(const QRectF &geometry);
bool isVisible() const;
bool isPressed() const;
bool isHovered() const;
bool isEnabled() const;
bool isChecked() const;
bool isCheckable() const;
DecorationButtonType type() const;
/**
* Returns @c true if @p pos is inside of the button, otherwise returns @c false.
**/
bool contains(const QPointF &pos) const;
Qt::MouseButtons acceptedButtons() const;
void setAcceptedButtons(Qt::MouseButtons buttons);
/**
* Invoked for painting this DecorationButtons. Implementing sub-classes need to implement
* this method. The coordinate system of the QPainter is set to Decoration coordinates.
*
* This method will be invoked from the rendering thread.
*
* @param painter The QPainter to paint this DecorationButton.
* @param repaintArea The area which is going to be repainted in Decoration coordinates
**/
virtual void paint(QPainter *painter, const QRectF &repaintArea) = 0;
Decoration *decoration() const;
bool event(QEvent *event) override;
public Q_SLOTS:
void setEnabled(bool enabled);
void setCheckable(bool checkable);
void setChecked(bool checked);
void setVisible(bool visible);
/**
* Schedules a repaint of the DecorationButton.
* Calling update will eventually result in paint being invoked.
*
* @param rect The area to repaint in Decoration local coordinates, a null QRect updates the complete geometry
* @see paint
**/
void update(const QRectF &rect);
/**
* Schedules a repaint of the DecorationButton.
*
* Overloaded method for convenience.
**/
void update();
Q_SIGNALS:
void clicked(Qt::MouseButton);
void pressed();
void released();
void pointerEntered();
void pointerLeft();
void doubleClicked();
void pressedChanged(bool);
void hoveredChanged(bool);
void enabledChanged(bool);
void checkableChanged(bool);
void checkedChanged(bool);
void geometryChanged(const QRectF &);
void acceptedButtonsChanged(Qt::MouseButtons);
void visibilityChanged(bool);
protected:
explicit DecorationButton(DecorationButtonType type, Decoration *decoration, QObject *parent = nullptr);
virtual void hoverEnterEvent(QHoverEvent *event);
virtual void hoverLeaveEvent(QHoverEvent *event);
virtual void hoverMoveEvent(QHoverEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);
private:
class Private;
std::unique_ptr<Private> d;
};
} // namespace
#ifndef K_DOXYGEN
size_t KDECORATIONS3_EXPORT qHash(const KDecoration3::DecorationButtonType &type, size_t seed = 0);
#endif
Q_DECLARE_METATYPE(KDecoration3::DecorationButtonType)
@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationbutton.h"
#include <QPointer>
class QElapsedTimer;
class QTimer;
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
namespace KDecoration3
{
class Q_DECL_HIDDEN DecorationButton::Private
{
public:
explicit Private(DecorationButtonType type, const QPointer<Decoration> &decoration, DecorationButton *parent);
~Private();
bool isPressed() const
{
return m_pressed != Qt::NoButton;
}
bool isPressed(Qt::MouseButton button) const
{
return m_pressed.testFlag(button);
}
void setHovered(bool hovered);
void setPressed(Qt::MouseButton, bool pressed);
void setAcceptedButtons(Qt::MouseButtons buttons);
void setEnabled(bool enabled);
void setChecked(bool checked);
void setCheckable(bool checkable);
void setVisible(bool visible);
void startDoubleClickTimer();
void invalidateDoubleClickTimer();
bool wasDoubleClick() const;
void setPressAndHold(bool enable);
void startPressAndHold();
void stopPressAndHold();
QString typeToString(DecorationButtonType type);
QPointer<Decoration> decoration;
DecorationButtonType type;
QRectF geometry;
bool hovered;
bool enabled;
bool checkable;
bool checked;
bool visible;
Qt::MouseButtons acceptedButtons;
bool doubleClickEnabled;
bool pressAndHold;
private:
void init();
DecorationButton *q;
Qt::MouseButtons m_pressed;
std::unique_ptr<QElapsedTimer> m_doubleClickTimer;
std::unique_ptr<QTimer> m_pressAndHoldTimer;
};
}
@@ -0,0 +1,244 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationbuttongroup.h"
#include "decoration.h"
#include "decorationbuttongroup_p.h"
#include "decorationsettings.h"
#include <QDebug>
#include <QGuiApplication>
namespace KDecoration3
{
DecorationButtonGroup::Private::Private(Decoration *decoration, DecorationButtonGroup *parent)
: decoration(decoration)
, spacing(0.0)
, q(parent)
{
}
DecorationButtonGroup::Private::~Private() = default;
void DecorationButtonGroup::Private::setGeometry(const QRectF &geo)
{
if (geometry == geo) {
return;
}
geometry = geo;
Q_EMIT q->geometryChanged(geometry);
}
namespace
{
static bool s_layoutRecursion = false;
}
void DecorationButtonGroup::Private::updateLayout()
{
if (s_layoutRecursion) {
return;
}
s_layoutRecursion = true;
const QPointF &pos = geometry.topLeft();
// first calculate new size
qreal height = 0;
qreal width = 0;
for (auto it = buttons.constBegin(); it != buttons.constEnd(); ++it) {
if (!(*it)->isVisible()) {
continue;
}
height = qMax(height, qreal((*it)->size().height()));
width += (*it)->size().width();
if (it + 1 != buttons.constEnd()) {
width += spacing;
}
}
setGeometry(QRectF(pos, QSizeF(width, height)));
QGuiApplication* app = qobject_cast<QGuiApplication*>(QCoreApplication::instance());
const auto layoutDirection = app ? app->layoutDirection() : Qt::LeftToRight;
qreal leftPosition = pos.x();
qreal rightPosition = pos.x() + width;
if (layoutDirection == Qt::LeftToRight)
for (auto button : std::as_const(buttons)) {
if (!button->isVisible()) {
continue;
}
const auto size = button->size();
const auto buttonPos = QPointF(leftPosition, pos.y());
button->setGeometry(QRectF(buttonPos, size));
leftPosition += size.width() + spacing;
}
else if (layoutDirection == Qt::RightToLeft)
for (auto button : std::as_const(buttons)) {
if (!button->isVisible()) {
continue;
}
const auto size = button->size();
const auto buttonPos = QPointF(rightPosition - size.width(), pos.y());
button->setGeometry(QRectF(buttonPos, size));
rightPosition -= size.width() + spacing;
}
else {
qCritical() << "There's an unhandled layout direction! This is likely an issue of KDecoration3 not being updated to handle it\n"
<< "or the application having an invalid layout direction set. Either way, this is a critical bug.";
}
s_layoutRecursion = false;
}
DecorationButtonGroup::DecorationButtonGroup(Decoration *parent)
: QObject(parent)
, d(new Private(parent, this))
{
}
DecorationButtonGroup::DecorationButtonGroup(DecorationButtonGroup::Position type,
Decoration *parent,
std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator)
: QObject(parent)
, d(new Private(parent, this))
{
auto createButtons = [this, buttonCreator, type] {
const Qt::LayoutDirection layoutDirection = QGuiApplication::layoutDirection();
const DecorationSettings *settings = d->decoration->settings().get();
const auto &buttons =
(type == Position::Left) ?
(layoutDirection == Qt::LeftToRight ? settings->decorationButtonsLeft() : settings->decorationButtonsRight()) :
(layoutDirection == Qt::LeftToRight ? settings->decorationButtonsRight() : settings->decorationButtonsLeft());
for (DecorationButtonType type : buttons) {
if (DecorationButton *b = buttonCreator(type, d->decoration, this)) {
addButton(b);
}
}
};
createButtons();
auto changed = type == Position::Left ? &DecorationSettings::decorationButtonsLeftChanged : &DecorationSettings::decorationButtonsRightChanged;
connect(parent->settings().get(), changed, this, [this, createButtons] {
qDeleteAll(d->buttons);
d->buttons.clear();
createButtons();
});
}
DecorationButtonGroup::~DecorationButtonGroup() = default;
Decoration *DecorationButtonGroup::decoration() const
{
return d->decoration;
}
QRectF DecorationButtonGroup::geometry() const
{
return d->geometry;
}
bool DecorationButtonGroup::hasButton(DecorationButtonType type) const
{
// TODO: check for deletion of button
auto it = std::find_if(d->buttons.begin(), d->buttons.end(), [type](DecorationButton *button) {
return button->type() == type;
});
return it != d->buttons.end();
}
qreal DecorationButtonGroup::spacing() const
{
return d->spacing;
}
QPointF DecorationButtonGroup::pos() const
{
return d->geometry.topLeft();
}
void DecorationButtonGroup::setPos(const QPointF &pos)
{
if (d->geometry.topLeft() == pos) {
return;
}
d->setGeometry(QRectF(pos, d->geometry.size()));
d->updateLayout();
}
void DecorationButtonGroup::setSpacing(qreal spacing)
{
if (d->spacing == spacing) {
return;
}
d->spacing = spacing;
Q_EMIT spacingChanged(d->spacing);
d->updateLayout();
}
void DecorationButtonGroup::addButton(DecorationButton *button)
{
Q_ASSERT(button);
connect(button, &DecorationButton::visibilityChanged, this, [this]() {
d->updateLayout();
});
connect(button, &DecorationButton::geometryChanged, this, [this]() {
d->updateLayout();
});
d->buttons.append(button);
d->updateLayout();
}
QList<DecorationButton *> DecorationButtonGroup::buttons() const
{
return d->buttons;
}
void DecorationButtonGroup::removeButton(DecorationButtonType type)
{
bool needUpdate = false;
auto it = d->buttons.begin();
while (it != d->buttons.end()) {
if ((*it)->type() == type) {
it = d->buttons.erase(it);
needUpdate = true;
} else {
it++;
}
}
if (needUpdate) {
d->updateLayout();
}
}
void DecorationButtonGroup::removeButton(DecorationButton *button)
{
bool needUpdate = false;
auto it = d->buttons.begin();
while (it != d->buttons.end()) {
if (*it == button) {
it = d->buttons.erase(it);
needUpdate = true;
} else {
it++;
}
}
if (needUpdate) {
d->updateLayout();
}
}
void DecorationButtonGroup::paint(QPainter *painter, const QRectF &repaintArea)
{
const auto &buttons = d->buttons;
for (auto button : buttons) {
if (!button->isVisible()) {
continue;
}
button->paint(painter, repaintArea);
}
}
} // namespace
#include "moc_decorationbuttongroup.cpp"
@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationbutton.h"
#include <functional>
#include <kdecoration3/kdecoration3_export.h>
class QPainter;
namespace KDecoration3
{
class Decoration;
class DecorationButtonGroupPrivate;
/**
* @brief Helper class to layout DecorationButton.
*
* A Decoration normally has two groups of DecorationButtons: one left of the caption and one
* right of the caption. The DecorationButtonGroup helps in positioning the DecorationButtons in
* these groups and to update the position of each of the DecorationButtons whenever the state
* changes in a way that they should be repositioned.
*
* A DecorationButtonGroup is a visual layout element not accepting input events. As a visual
* element it provides a paint method allowing a sub class to provide custom painting for the
* DecorationButtonGroup.
**/
class KDECORATIONS3_EXPORT DecorationButtonGroup : public QObject
{
Q_OBJECT
/**
* The spacing to use between the DecorationButtons
**/
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
/**
* The geometry of the DecorationButtonGroup in Decoration-local coordinates.
* The size of the DecorationButtonGroup depends on the sizes of the individual
* DecorationButtons and the spacing.
**/
Q_PROPERTY(QRectF geometry READ geometry NOTIFY geometryChanged)
// TODO: pos must consider whether it's left or right
/**
* The top left Position of the DecorationButtonGroup. This property needs to be
* changed to reposition the DecorationButtonGroup. An update should normally be
* triggered after e.g. a state change like maximization.
**/
Q_PROPERTY(QPointF pos READ pos WRITE setPos NOTIFY posChanged)
public:
enum class Position {
Left,
Right,
};
explicit DecorationButtonGroup(Position type,
Decoration *parent,
std::function<DecorationButton *(DecorationButtonType, Decoration *, QObject *)> buttonCreator);
explicit DecorationButtonGroup(Decoration *parent);
~DecorationButtonGroup() override;
/**
* Paints the DecorationButtonGroup. This method should normally be invoked from the
* Decoration's paint method. Base implementation just calls the paint method on each
* of the DecorationButtons. Overwriting sub classes need to either call the base
* implementation or ensure that the DecorationButtons are painted.
*
* @param painter The QPainter which is used to paint this DecorationButtonGroup
* @param repaintArea The area which is going to be repainted in Decoration coordinates
**/
virtual void paint(QPainter *painter, const QRectF &repaintArea);
Decoration *decoration() const;
qreal spacing() const;
void setSpacing(qreal spacing);
QRectF geometry() const;
QPointF pos() const;
void setPos(const QPointF &pos);
/**
* Adds @p button to the DecorationButtonGroup and triggers a re-layout of all
* DecorationButtons.
**/
void addButton(DecorationButton *button);
/**
* Removes @p button from the DecorationButtonGroup and triggers a re-layout of all
* DecorationButtons.
**/
void removeButton(DecorationButton *button);
/**
* Removes all DecorationButtons with @p type from the DecorationButtonGroup and
* triggers a re-layout of all DecorationButtons.
**/
void removeButton(DecorationButtonType type);
/**
* @returns @c true if the DecorationButtonGroup contains a DecorationButton of @p type
**/
bool hasButton(DecorationButtonType type) const;
/**
* @returns All DecorationButtons in this DecorationButtonGroup
**/
QList<DecorationButton *> buttons() const;
Q_SIGNALS:
void spacingChanged(qreal);
void geometryChanged(const QRectF &);
void posChanged(const QPointF &);
private:
class Private;
std::unique_ptr<Private> d;
};
} // namespace
@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationbuttongroup.h"
#include <QList>
#include <QRectF>
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
namespace KDecoration3
{
class Decoration;
class Q_DECL_HIDDEN DecorationButtonGroup::Private
{
public:
explicit Private(Decoration *decoration, DecorationButtonGroup *parent);
~Private();
void setGeometry(const QRectF &geometry);
void updateLayout();
Decoration *decoration;
QRectF geometry;
QList<DecorationButton *> buttons;
qreal spacing;
private:
DecorationButtonGroup *q;
};
} // namespace
@@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
namespace KDecoration3
{
/**
* The DecorationButtonType is a helper type for the DecorationButton.
* A Decoration should provide a DecorationButton for each of the types,
* if it wants to provide further buttons it should use the Custom type.
* The DecorationButton gets configured depending on the type. E.g. the
* Close button gets disabled if the DecoratedWindow is not closeable.
**/
enum class DecorationButtonType {
/**
* The Menu button requests showing the window menu on left or right click.
**/
Menu,
/**
* The ApplicationMenu button requests showing the application's menu on left or right click.
*/
ApplicationMenu,
/**
* The OnAllDesktops button requests toggling the DecoratedWindow's on all desktops state.
* The DecoratedButton is only visible if multiple virtual desktops are available.
**/
OnAllDesktops,
/**
* The Minimize button requests minimizing the DecoratedWindow. The DecorationButton is only
* enabled if the DecoratedWindow is minimizeable.
**/
Minimize,
/**
* The Maximize button requests maximizing the DecoratedWindow. The DecorationButton is checkable
* and if the DecoratedWindow is maximized the DecorationButton is checked. The DecorationButton
* supports multiple mouse buttons to change horizontal, vertical and overall maximized state.
*
* The DecorationButton is only enabled if the DecoratedWindow is maximizeable.
**/
Maximize,
/**
* The Close button requests closing the DecoratedWindow. The DecorationButton is only enabled
* if the DecoratedWindow is closeable.
**/
Close,
/**
* The ContextHelp button requests entering the context help mode. The DecorationButton is only
* visible if the DecoratedWindow provides context help.
**/
ContextHelp,
/**
* The Shade button requests toggling the DecoratedWindow's shaded state. The DecoratedButton
* is only enabled if the DecoratedWindow is shadeable.
**/
Shade,
/**
* The KeepBelow button requests toggling the DecoratedWindow's keep below state.
**/
KeepBelow,
/**
* The KeepAbove button requests toggling the DecoratedWindow's keep above state.
**/
KeepAbove,
/**
* The Custom type allows a Decoration to provide custom DecorationButtons.
**/
Custom,
/**
* The Spacer button provides some space between buttons.
*/
Spacer,
};
/**
* Border sizes are a combination of visual and accessibility features.
* Larger borders should be used to increase the non title bar borders to
* make it easier to resize the decoration
**/
enum class BorderSize {
/**
* Border sizes of all non title bar sides should be set to 0.
**/
None,
/**
* Border sizes of the sides should be set to 0. Title bar and
* the border on opposite side of the title bar should follow the
* Normal settings.
**/
NoSides,
/**
* Borders should be smaller than Normal, e.g. a factor of 0.5.
**/
Tiny,
/**
* The default border size with borders on each side. This should
* be the base for calculating other border sizes.
**/
Normal,
/**
* Increased border sizes, considered a factor of 1.5.
**/
Large,
/**
* Increased border sizes, considered a factor of 2.0.
**/
VeryLarge,
/**
* Increased border sizes, considered a factor of 2.5.
**/
Huge,
/**
* Increased border sizes, considered a factor of 3.0.
**/
VeryHuge,
/**
* Increased border sizes, considered a factor of 5.0.
**/
Oversized,
};
/**
* Color groups are used for DecoratedWindow::color().
* @since 5.3
**/
enum class ColorGroup {
/**
* Inactive color, used for unfocused windows.
**/
Inactive,
/**
* Active color, used for focused windows.
**/
Active,
/**
* Warning color, can only be used with ColorRole::Foreground. If used with other roles,
* a invalid QColor is returned. It can be used for close buttons and is typically red.
**/
Warning,
};
/**
* Color roles are used for DecoratedWindow::color().
* @since 5.3
**/
enum class ColorRole {
/**
* The decoration's frame background color.
**/
Frame,
/**
* The decoration's title bar background color
**/
TitleBar,
/**
* The decoration's title bar forground color
**/
Foreground,
};
}
@@ -0,0 +1,96 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationsettings.h"
#include "private/decorationbridge.h"
#include "private/decorationsettingsprivate.h"
#include <QFontMetrics>
namespace KDecoration3
{
DecorationSettings::DecorationSettings(DecorationBridge *bridge, QObject *parent)
: QObject(parent)
, d(bridge->settings(this))
{
auto updateUnits = [this] {
int gridUnit = QFontMetrics(font()).boundingRect(QLatin1Char('M')).height();
;
if (gridUnit % 2 != 0) {
gridUnit++;
}
if (gridUnit != d->gridUnit()) {
d->setGridUnit(gridUnit);
Q_EMIT gridUnitChanged(gridUnit);
}
if (gridUnit != d->largeSpacing()) {
d->setSmallSpacing(qMax(2, (int)(gridUnit / 4))); // 1/4 of gridUnit, at least 2
d->setLargeSpacing(gridUnit); // msize.height
Q_EMIT spacingChanged();
}
};
updateUnits();
connect(this, &DecorationSettings::fontChanged, this, updateUnits);
}
DecorationSettings::~DecorationSettings() = default;
bool DecorationSettings::isOnAllDesktopsAvailable() const
{
return d->isOnAllDesktopsAvailable();
}
bool DecorationSettings::isAlphaChannelSupported() const
{
return d->isAlphaChannelSupported();
}
bool DecorationSettings::isCloseOnDoubleClickOnMenu() const
{
return d->isCloseOnDoubleClickOnMenu();
}
QList<DecorationButtonType> DecorationSettings::decorationButtonsLeft() const
{
return d->decorationButtonsLeft();
}
QList<DecorationButtonType> DecorationSettings::decorationButtonsRight() const
{
return d->decorationButtonsRight();
}
BorderSize DecorationSettings::borderSize() const
{
return d->borderSize();
}
QFont DecorationSettings::font() const
{
return d->font();
}
QFontMetricsF DecorationSettings::fontMetrics() const
{
return d->fontMetrics();
}
int DecorationSettings::gridUnit() const
{
return d->gridUnit();
}
int DecorationSettings::smallSpacing() const
{
return d->smallSpacing();
}
int DecorationSettings::largeSpacing() const
{
return d->largeSpacing();
}
}
#include "moc_decorationsettings.cpp"
@@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationbutton.h"
#include <kdecoration3/kdecoration3_export.h>
#include <QFontMetricsF>
#include <QObject>
#include <memory>
namespace KDecoration3
{
class DecorationBridge;
class DecorationSettingsPrivate;
/**
* @brief Common settings for the Decoration.
*
* This class gets injected into the Decoration and provides recommendations for the
* Decoration. The Decoration is suggested to honor the settings, but may decide that some
* settings don't fit the design and ignore them.
*
* @see Decoration
**/
class KDECORATIONS3_EXPORT DecorationSettings : public QObject
{
Q_OBJECT
/**
* Whether the feature to put a DecoratedWindow on all desktops is available.
*
* If this feature is not available a Decoration might decide to not show the
* DecorationButtonType::OnAllDesktops.
**/
Q_PROPERTY(bool onAllDesktopsAvailable READ isOnAllDesktopsAvailable NOTIFY onAllDesktopsAvailableChanged)
/**
* Whether the Decoration will be rendered with an alpha channel.
*
* If no alpha channel is available a Decoration should not use round borders.
**/
Q_PROPERTY(bool alphaChannelSupported READ isAlphaChannelSupported NOTIFY alphaChannelSupportedChanged)
/**
* Whether the Decoration should close the DecoratedWindow when double clicking on the
* DecorationButtonType::Menu.
**/
Q_PROPERTY(bool closeOnDoubleClickOnMenu READ isCloseOnDoubleClickOnMenu NOTIFY closeOnDoubleClickOnMenuChanged)
/**
* The suggested ordering of the decoration buttons on the left.
**/
Q_PROPERTY(QList<KDecoration3::DecorationButtonType> decorationButtonsLeft READ decorationButtonsLeft NOTIFY decorationButtonsLeftChanged)
/**
* The suggested ordering of the decoration buttons on the right.
**/
Q_PROPERTY(QList<KDecoration3::DecorationButtonType> decorationButtonsRight READ decorationButtonsRight NOTIFY decorationButtonsRightChanged)
/**
* The suggested border size.
**/
Q_PROPERTY(KDecoration3::BorderSize borderSize READ borderSize NOTIFY borderSizeChanged)
/**
* The fundamental unit of space that should be used for sizes, expressed in pixels.
* Given the screen has an accurate DPI settings, it corresponds to a millimeter
*/
Q_PROPERTY(int gridUnit READ gridUnit NOTIFY gridUnitChanged)
/**
* The recommended font for the Decoration's caption.
**/
Q_PROPERTY(QFont font READ font NOTIFY fontChanged)
/**
* smallSpacing is the amount of spacing that should be used around smaller UI elements,
* for example as spacing in Columns. Internally, this size depends on the size of
* the default font as rendered on the screen, so it takes user-configured font size and DPI
* into account.
*/
Q_PROPERTY(int smallSpacing READ smallSpacing NOTIFY spacingChanged)
/**
* largeSpacing is the amount of spacing that should be used inside bigger UI elements,
* for example between an icon and the corresponding text. Internally, this size depends on
* the size of the default font as rendered on the screen, so it takes user-configured font
* size and DPI into account.
*/
Q_PROPERTY(int largeSpacing READ largeSpacing NOTIFY spacingChanged)
public:
explicit DecorationSettings(DecorationBridge *bridge, QObject *parent = nullptr);
~DecorationSettings() override;
bool isOnAllDesktopsAvailable() const;
bool isAlphaChannelSupported() const;
bool isCloseOnDoubleClickOnMenu() const;
QList<DecorationButtonType> decorationButtonsLeft() const;
QList<DecorationButtonType> decorationButtonsRight() const;
BorderSize borderSize() const;
QFont font() const;
/**
* The fontMetrics for the recommended font.
* @see font
**/
QFontMetricsF fontMetrics() const;
int gridUnit() const;
int smallSpacing() const;
int largeSpacing() const;
Q_SIGNALS:
void onAllDesktopsAvailableChanged(bool);
void alphaChannelSupportedChanged(bool);
void closeOnDoubleClickOnMenuChanged(bool);
void decorationButtonsLeftChanged(const QList<KDecoration3::DecorationButtonType> &);
void decorationButtonsRightChanged(const QList<KDecoration3::DecorationButtonType> &);
void borderSizeChanged(KDecoration3::BorderSize size);
void fontChanged(const QFont &font);
void gridUnitChanged(int);
void spacingChanged();
/**
* This signal is emitted when the backend got reconfigured.
* If the plugin uses custom settings, it is recommended to re-read
* them after this signal got emitted.
**/
void reconfigured();
private:
const std::unique_ptr<DecorationSettingsPrivate> d;
};
}
Q_DECLARE_METATYPE(KDecoration3::BorderSize)
@@ -0,0 +1,173 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationshadow.h"
#include "decorationshadow_p.h"
namespace KDecoration3
{
DecorationShadow::Private::Private(DecorationShadow *parent)
: q(parent)
{
}
DecorationShadow::Private::~Private() = default;
DecorationShadow::DecorationShadow()
: QObject()
, d(new Private(this))
{
}
DecorationShadow::~DecorationShadow() = default;
QRectF DecorationShadow::topLeftGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(0, 0, d->innerShadowRect.left(), d->innerShadowRect.top());
}
QRectF DecorationShadow::topGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(d->innerShadowRect.left(), 0, d->innerShadowRect.width(), d->innerShadowRect.top());
}
QRectF DecorationShadow::topRightGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(d->innerShadowRect.left() + d->innerShadowRect.width(),
0,
d->shadow.width() - d->innerShadowRect.width() - d->innerShadowRect.left(),
d->innerShadowRect.top());
}
QRectF DecorationShadow::rightGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(d->innerShadowRect.left() + d->innerShadowRect.width(),
d->innerShadowRect.top(),
d->shadow.width() - d->innerShadowRect.width() - d->innerShadowRect.left(),
d->innerShadowRect.height());
}
QRectF DecorationShadow::bottomRightGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(d->innerShadowRect.left() + d->innerShadowRect.width(),
d->innerShadowRect.top() + d->innerShadowRect.height(),
d->shadow.width() - d->innerShadowRect.width() - d->innerShadowRect.left(),
d->shadow.height() - d->innerShadowRect.top() - d->innerShadowRect.height());
}
QRectF DecorationShadow::bottomGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(d->innerShadowRect.left(),
d->innerShadowRect.top() + d->innerShadowRect.height(),
d->innerShadowRect.width(),
d->shadow.height() - d->innerShadowRect.top() - d->innerShadowRect.height());
}
QRectF DecorationShadow::bottomLeftGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(0,
d->innerShadowRect.top() + d->innerShadowRect.height(),
d->innerShadowRect.left(),
d->shadow.height() - d->innerShadowRect.top() - d->innerShadowRect.height());
}
QRectF DecorationShadow::leftGeometry() const
{
if (d->innerShadowRect.isNull() || d->shadow.isNull()) {
return QRectF();
}
return QRectF(0, d->innerShadowRect.top(), d->innerShadowRect.left(), d->innerShadowRect.height());
}
#ifndef K_DOXYGEN
QImage DecorationShadow::shadow() const
{
return d->shadow;
}
QMarginsF DecorationShadow::padding() const
{
return d->padding;
}
QRectF DecorationShadow::innerShadowRect() const
{
return d->innerShadowRect;
}
qreal DecorationShadow::paddingTop() const
{
return d->padding.top();
}
qreal DecorationShadow::paddingBottom() const
{
return d->padding.bottom();
}
qreal DecorationShadow::paddingRight() const
{
return d->padding.right();
}
qreal DecorationShadow::paddingLeft() const
{
return d->padding.left();
}
void DecorationShadow::setShadow(const QImage &shadow)
{
if (d->shadow == shadow) {
return;
}
d->shadow = shadow;
Q_EMIT shadowChanged(d->shadow);
}
#endif
void DecorationShadow::setPadding(const QMarginsF &margins)
{
if (d->padding == margins) {
return;
}
d->padding = margins;
Q_EMIT paddingChanged();
}
void DecorationShadow::setInnerShadowRect(const QRectF &rect)
{
if (d->innerShadowRect == rect) {
return;
}
d->innerShadowRect = rect;
Q_EMIT innerShadowRectChanged();
}
}
#include "moc_decorationshadow.cpp"
@@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <kdecoration3/kdecoration3_export.h>
#include <QImage>
#include <QMargins>
#include <QObject>
namespace KDecoration3
{
class DecorationShadowPrivate;
/**
* @brief A wrapper to define the shadow around the Decoration.
*
* The shadow around the Decoration should not be rendered as part of the Decoration.
* Instead a DecorationShadow should be used. That way a backend can optimize the
* rendering of the shadow in a better way. If the shadow were part of the Decoration
* directly it would need to be updated when the rendering changes. By using a dedicated
* DecorationShadow the same shadow can be shared between multiple DecoratedWindows.
*
* The DecorationShadow consists of a shadow QImage which is composed of multiple parts:
* @li topLeft: rendered as it is
* @li top: stretched in x direction
* @li topRight: rendered as it is
* @li right: stretched in y direction
* @li bottomRight: rendered as it is
* @li bottom: stretched in x direction
* @li bottomLeft: rendered as it is
* @li left: stretched in y direction
*
* The sizes of these parts is denoted in the property innerShadowRect and the layout is the
* following:
* #######################################
* # topLeft # top # topRight #
* #######################################
* # left # # right #
* #######################################
* # bottomLeft # bottom # bottomRight #
* #######################################
*
* The innerShadowRect property is a QRect of the geometry of the areas not covered by any of the
* elements. This means that:
* @li x/y of the rect is the same as the size of the topLeft element
* @li width of the rect is the same as the width of the top and bottom element
* @li height of the rect is the same as the height of the left and the right element
* By that the actual sizes of all elements can be derived out of the size of the shadow image
* and the innerShadowRect.
*
* The position of the rendering depends on the values;
* @li paddingTop
* @li paddingRight
* @li paddingBottom
* @li paddingLeft
*
* The top left element is rendered with an offset of paddingLeft and paddingTop.
* The non-stretched elements are rendered in the size as specified, the area
* between two non-stretched elements (e.g. between topLeft and topRight) is filled
* by the element with one direction stretched and the other direction fixed at the
* corresponding padding value. E.g. the top element is stretched in x direction and
* fixed at paddingTop value. If stretching the side elements is not wanted one needs
* to provide a shadow image with those elements at a size that stretching is not
* required.
*
* If the padding values are smaller than the sizes of the shadow elements the shadow
* will overlap with the Decoration and be rendered behind the Decoration.
*
**/
class KDECORATIONS3_EXPORT DecorationShadow : public QObject
{
Q_OBJECT
Q_PROPERTY(QImage shadow READ shadow WRITE setShadow NOTIFY shadowChanged)
Q_PROPERTY(QRectF innerShadowRect READ innerShadowRect WRITE setInnerShadowRect NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF topLeftGeometry READ topLeftGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF topGeometry READ topGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF topRightGeometry READ topRightGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF rightGeometry READ rightGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF bottomRightGeometry READ bottomRightGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF bottomGeometry READ bottomGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF bottomLeftGeometry READ bottomLeftGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(QRectF leftGeometry READ leftGeometry NOTIFY innerShadowRectChanged)
Q_PROPERTY(qreal paddingTop READ paddingTop NOTIFY paddingChanged)
Q_PROPERTY(qreal paddingRight READ paddingRight NOTIFY paddingChanged)
Q_PROPERTY(qreal paddingBottom READ paddingBottom NOTIFY paddingChanged)
Q_PROPERTY(qreal paddingLeft READ paddingLeft NOTIFY paddingChanged)
Q_PROPERTY(QMarginsF padding READ padding WRITE setPadding NOTIFY paddingChanged)
public:
explicit DecorationShadow();
~DecorationShadow() override;
QImage shadow() const;
QRectF innerShadowRect() const;
QRectF topLeftGeometry() const;
QRectF topGeometry() const;
QRectF topRightGeometry() const;
QRectF rightGeometry() const;
QRectF bottomRightGeometry() const;
QRectF bottomGeometry() const;
QRectF bottomLeftGeometry() const;
QRectF leftGeometry() const;
qreal paddingTop() const;
qreal paddingRight() const;
qreal paddingBottom() const;
qreal paddingLeft() const;
QMarginsF padding() const;
void setShadow(const QImage &image);
void setInnerShadowRect(const QRectF &rect);
void setPadding(const QMarginsF &margins);
Q_SIGNALS:
void shadowChanged(const QImage &);
void innerShadowRectChanged();
void paddingChanged();
private:
class Private;
std::unique_ptr<Private> d;
};
}
Q_DECLARE_METATYPE(KDecoration3::DecorationShadow *)
@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "decorationshadow.h"
#include <QImage>
namespace KDecoration3
{
class Q_DECL_HIDDEN DecorationShadow::Private
{
public:
explicit Private(DecorationShadow *parent);
~Private();
QImage shadow;
QRectF innerShadowRect;
QMarginsF padding;
private:
DecorationShadow *q;
};
}
@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationthemeprovider.h"
class DecorationThemeMetaDataPrivate : public QSharedData
{
public:
QString visibleName;
QString themeName;
QString configurationName;
QString pluginId;
KDecoration3::BorderSize borderSize = KDecoration3::BorderSize::Normal;
};
using namespace KDecoration3;
DecorationThemeMetaData::DecorationThemeMetaData()
: d(new DecorationThemeMetaDataPrivate())
{
}
DecorationThemeMetaData::~DecorationThemeMetaData() = default;
QString DecorationThemeMetaData::visibleName() const
{
return d->visibleName;
}
void DecorationThemeMetaData::setVisibleName(const QString &name)
{
d->visibleName = name;
}
QString DecorationThemeMetaData::themeName() const
{
return d->themeName;
}
void DecorationThemeMetaData::setThemeName(const QString &name)
{
d->themeName = name;
}
QString DecorationThemeMetaData::configurationName() const
{
return d->configurationName;
}
void DecorationThemeMetaData::setConfigurationName(const QString &name)
{
d->configurationName = name;
}
void DecorationThemeMetaData::setBorderSize(KDecoration3::BorderSize size)
{
d->borderSize = size;
}
KDecoration3::BorderSize DecorationThemeMetaData::borderSize() const
{
return d->borderSize;
}
QString DecorationThemeMetaData::pluginId() const
{
return d->pluginId;
}
void DecorationThemeMetaData::setPluginId(const QString &id)
{
d->pluginId = id;
}
DecorationThemeProvider::DecorationThemeProvider(QObject *parent)
: QObject(parent)
{
}
DecorationThemeMetaData::DecorationThemeMetaData(const DecorationThemeMetaData &other) = default;
DecorationThemeMetaData &DecorationThemeMetaData::operator=(const DecorationThemeMetaData &other) = default;
#include "moc_decorationthemeprovider.cpp"
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "decorationdefines.h"
#include <QObject>
#include <QSharedDataPointer>
#include <kdecoration3/kdecoration3_export.h>
class KPluginMetaData;
class DecorationThemeMetaDataPrivate;
namespace KDecoration3
{
/**
* Class providing type-safe access to data of themes
*
* @since 5.23
* @author Alexander Lohnau
*/
class KDECORATIONS3_EXPORT DecorationThemeMetaData
{
public:
explicit DecorationThemeMetaData();
virtual ~DecorationThemeMetaData();
DecorationThemeMetaData(const DecorationThemeMetaData &other);
DecorationThemeMetaData &operator=(const DecorationThemeMetaData &other);
/// User-visible name of the theme
QString visibleName() const;
void setVisibleName(const QString &name);
/// Internal name of the theme
QString themeName() const;
void setThemeName(const QString &name);
/// Name of the kcm to configure the decoration theme
QString configurationName() const;
void setConfigurationName(const QString &name);
/// Border size of the decoration, this gets set based on the "recommendedBorderSize" key in the json metadata
/// @internal
KDecoration3::BorderSize borderSize() const;
void setBorderSize(KDecoration3::BorderSize size);
/// plugin id of theme provider
/// @see KPluginMetaData::pluginId
QString pluginId() const;
void setPluginId(const QString &id);
private:
QSharedDataPointer<DecorationThemeMetaDataPrivate> d;
};
/**
* Class to give the KWin decorationmodel access to the plugin's themes.
*
* @since 5.23
* @author Alexander Lohnau
*/
class KDECORATIONS3_EXPORT DecorationThemeProvider : public QObject
{
Q_OBJECT
public:
explicit DecorationThemeProvider(QObject *parent);
/**
* List containing information of supported themes
*/
virtual QList<DecorationThemeMetaData> themes() const = 0;
};
}
@@ -0,0 +1,58 @@
set(libkdecoration3Private_SRCS
decoratedwindowprivate.cpp
decoratedwindowprivate.h
decorationbridge.cpp
decorationbridge.h
decorationsettingsprivate.cpp
decorationsettingsprivate.h
)
add_library(kdecorations3private SHARED ${libkdecoration3Private_SRCS})
generate_export_header(
kdecorations3private
BASE_NAME
KDECORATIONS_PRIVATE
EXPORT_FILE_NAME
kdecoration3/private/kdecoration3_private_export.h
)
add_library(KDecoration3::KDecorationPrivate ALIAS kdecorations3private)
target_link_libraries(kdecorations3private
PUBLIC
Qt::Core
Qt::Gui
)
target_include_directories(kdecorations3private INTERFACE "$<INSTALL_INTERFACE:${KDECORATION3_INCLUDEDIR}>" )
set_target_properties(kdecorations3private PROPERTIES VERSION ${KDECORATION3_VERSION}
SOVERSION 2
EXPORT_NAME KDecoration3Private
)
ecm_generate_headers(KDecoration3Private_CamelCase_HEADERS
HEADER_NAMES
DecoratedWindowPrivate
DecorationBridge
DecorationSettingsPrivate
PREFIX
KDecoration3/Private
REQUIRED_HEADERS KDecoration3Private_HEADERS
)
install(FILES ${KDecoration3Private_CamelCase_HEADERS}
DESTINATION ${KDECORATION3_INCLUDEDIR}/KDecoration3/Private
COMPONENT Devel)
install(TARGETS kdecorations3private EXPORT KDecoration3Targets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/kdecoration3/private/kdecoration3_private_export.h
${KDecoration3Private_HEADERS}
DESTINATION
${KDECORATION3_INCLUDEDIR}/kdecoration3/private
COMPONENT
Devel
)
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decoratedwindowprivate.h"
#include <QColor>
namespace KDecoration3
{
class Q_DECL_HIDDEN DecoratedWindowPrivate::Private
{
public:
explicit Private(DecoratedWindow *client, Decoration *decoration);
DecoratedWindow *client;
Decoration *decoration;
};
DecoratedWindowPrivate::Private::Private(DecoratedWindow *client, Decoration *decoration)
: client(client)
, decoration(decoration)
{
}
DecoratedWindowPrivate::DecoratedWindowPrivate(DecoratedWindow *client, Decoration *decoration)
: d(new Private(client, decoration))
{
}
DecoratedWindowPrivate::~DecoratedWindowPrivate() = default;
Decoration *DecoratedWindowPrivate::decoration()
{
return d->decoration;
}
Decoration *DecoratedWindowPrivate::decoration() const
{
return d->decoration;
}
DecoratedWindow *DecoratedWindowPrivate::window()
{
return d->client;
}
QColor DecoratedWindowPrivate::color(ColorGroup group, ColorRole role) const
{
Q_UNUSED(role)
Q_UNUSED(group)
return QColor();
}
DecoratedWindowPrivateV2::DecoratedWindowPrivateV2(DecoratedWindow *client, Decoration *decoration)
: DecoratedWindowPrivate(client, decoration)
{
}
}
@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "../decorationdefines.h"
#include <kdecoration3/private/kdecoration3_private_export.h>
#include <QIcon>
#include <QString>
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
namespace KDecoration3
{
class Decoration;
class DecoratedWindow;
class KDECORATIONS_PRIVATE_EXPORT DecoratedWindowPrivate
{
public:
virtual ~DecoratedWindowPrivate();
virtual bool isActive() const = 0;
virtual QString caption() const = 0;
virtual bool isOnAllDesktops() const = 0;
virtual bool isShaded() const = 0;
virtual QIcon icon() const = 0;
virtual bool isMaximized() const = 0;
virtual bool isMaximizedHorizontally() const = 0;
virtual bool isMaximizedVertically() const = 0;
virtual bool isKeepAbove() const = 0;
virtual bool isKeepBelow() const = 0;
virtual bool isCloseable() const = 0;
virtual bool isMaximizeable() const = 0;
virtual bool isMinimizeable() const = 0;
virtual bool providesContextHelp() const = 0;
virtual bool isModal() const = 0;
virtual bool isShadeable() const = 0;
virtual bool isMoveable() const = 0;
virtual bool isResizeable() const = 0;
virtual qreal width() const = 0;
virtual qreal height() const = 0;
virtual QSizeF size() const = 0;
virtual QPalette palette() const = 0;
virtual Qt::Edges adjacentScreenEdges() const = 0;
virtual qreal scale() const = 0;
virtual qreal nextScale() const = 0;
virtual void requestShowToolTip(const QString &text) = 0;
virtual void requestHideToolTip() = 0;
virtual void requestClose() = 0;
virtual void requestToggleMaximization(Qt::MouseButtons buttons) = 0;
virtual void requestMinimize() = 0;
virtual void requestContextHelp() = 0;
virtual void requestToggleOnAllDesktops() = 0;
virtual void requestToggleShade() = 0;
virtual void requestToggleKeepAbove() = 0;
virtual void requestToggleKeepBelow() = 0;
virtual void requestShowWindowMenu(const QRect &rect) = 0;
Decoration *decoration();
Decoration *decoration() const;
virtual QColor color(ColorGroup group, ColorRole role) const;
virtual QString windowClass() const = 0;
virtual bool hasApplicationMenu() const = 0;
virtual bool isApplicationMenuActive() const = 0;
virtual void showApplicationMenu(int actionId) = 0;
virtual void requestShowApplicationMenu(const QRect &rect, int actionId) = 0;
protected:
explicit DecoratedWindowPrivate(DecoratedWindow *client, Decoration *decoration);
DecoratedWindow *window();
private:
class Private;
const std::unique_ptr<Private> d;
};
class KDECORATIONS_PRIVATE_EXPORT DecoratedWindowPrivateV2 : public DecoratedWindowPrivate
{
public:
virtual QString applicationMenuServiceName() const = 0;
virtual QString applicationMenuObjectPath() const = 0;
protected:
explicit DecoratedWindowPrivateV2(DecoratedWindow *client, Decoration *decoration);
};
} // namespace
@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationbridge.h"
Q_DECLARE_METATYPE(Qt::MouseButton)
namespace KDecoration3
{
DecorationBridge::DecorationBridge(QObject *parent)
: QObject(parent)
{
qRegisterMetaType<Qt::MouseButton>();
}
DecorationBridge::~DecorationBridge() = default;
}
#include "moc_decorationbridge.cpp"
@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QObject>
#include <memory>
#include <kdecoration3/private/kdecoration3_private_export.h>
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
class QRect;
namespace KDecoration3
{
class Decoration;
class DecorationSettings;
class DecorationSettingsPrivate;
class DecoratedWindow;
class DecoratedWindowPrivate;
class KDECORATIONS_PRIVATE_EXPORT DecorationBridge : public QObject
{
Q_OBJECT
public:
~DecorationBridge() override;
virtual std::unique_ptr<DecoratedWindowPrivate> createClient(DecoratedWindow *client, Decoration *decoration) = 0;
virtual std::unique_ptr<DecorationSettingsPrivate> settings(DecorationSettings *parent) = 0;
protected:
explicit DecorationBridge(QObject *parent = nullptr);
};
} // namespace
Q_DECLARE_METATYPE(KDecoration3::DecorationBridge *)
@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "decorationsettingsprivate.h"
#include <QFontDatabase>
namespace KDecoration3
{
class Q_DECL_HIDDEN DecorationSettingsPrivate::Private
{
public:
explicit Private(DecorationSettings *settings);
DecorationSettings *settings;
int gridUnit = -1;
int smallSpacing = -1;
int largeSpacing = -1;
};
DecorationSettingsPrivate::Private::Private(DecorationSettings *settings)
: settings(settings)
{
}
DecorationSettingsPrivate::DecorationSettingsPrivate(DecorationSettings *parent)
: d(new Private(parent))
{
}
DecorationSettingsPrivate::~DecorationSettingsPrivate()
{
}
DecorationSettings *DecorationSettingsPrivate::decorationSettings()
{
return d->settings;
}
const DecorationSettings *DecorationSettingsPrivate::decorationSettings() const
{
return d->settings;
}
QFont DecorationSettingsPrivate::font() const
{
return QFontDatabase::systemFont(QFontDatabase::TitleFont);
}
QFontMetricsF DecorationSettingsPrivate::fontMetrics() const
{
return QFontMetricsF(font());
}
int DecorationSettingsPrivate::gridUnit() const
{
return d->gridUnit;
}
int DecorationSettingsPrivate::smallSpacing() const
{
return d->smallSpacing;
}
int DecorationSettingsPrivate::largeSpacing() const
{
return d->largeSpacing;
}
void DecorationSettingsPrivate::setGridUnit(int unit)
{
d->gridUnit = unit;
}
void DecorationSettingsPrivate::setLargeSpacing(int spacing)
{
d->largeSpacing = spacing;
}
void DecorationSettingsPrivate::setSmallSpacing(int spacing)
{
d->smallSpacing = spacing;
}
}
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "../decorationdefines.h"
#include <QFont>
#include <QFontMetricsF>
#include <QList>
#include <kdecoration3/private/kdecoration3_private_export.h>
#include <memory>
//
// W A R N I N G
// -------------
//
// This file is not part of the KDecoration3 API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
namespace KDecoration3
{
class DecorationSettings;
class KDECORATIONS_PRIVATE_EXPORT DecorationSettingsPrivate
{
public:
virtual ~DecorationSettingsPrivate();
virtual bool isOnAllDesktopsAvailable() const = 0;
virtual bool isAlphaChannelSupported() const = 0;
virtual bool isCloseOnDoubleClickOnMenu() const = 0;
virtual QList<DecorationButtonType> decorationButtonsLeft() const = 0;
virtual QList<DecorationButtonType> decorationButtonsRight() const = 0;
virtual BorderSize borderSize() const = 0;
virtual QFont font() const;
virtual QFontMetricsF fontMetrics() const;
DecorationSettings *decorationSettings();
const DecorationSettings *decorationSettings() const;
int gridUnit() const;
int smallSpacing() const;
int largeSpacing() const;
void setGridUnit(int unit);
void setLargeSpacing(int spacing);
void setSmallSpacing(int spacing);
protected:
explicit DecorationSettingsPrivate(DecorationSettings *parent);
private:
class Private;
const std::unique_ptr<Private> d;
};
}
@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QPointF>
#include <QRectF>
#include <QSizeF>
namespace KDecoration3
{
/**
* @returns the pixel size with the provided scale
* @since 6.3
*/
inline qreal pixelSize(qreal scale)
{
return 1.0 / scale;
}
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid of the provided scale
* @since 6.3
*/
inline qreal snapToPixelGrid(qreal value, qreal scale)
{
return std::round(value * scale) / scale;
}
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid of the provided scale
* @since 6.3
*/
inline QPointF snapToPixelGrid(const QPointF &value, qreal scale)
{
return QPointF(snapToPixelGrid(value.x(), scale), snapToPixelGrid(value.y(), scale));
}
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid of the provided scale
* @since 6.3
*/
inline QSizeF snapToPixelGrid(const QSizeF &value, qreal scale)
{
return QSizeF(snapToPixelGrid(value.width(), scale), snapToPixelGrid(value.height(), scale));
}
/**
* snaps the logical geometry value to a fractional logical geometry value
* that aligns to the pixel grid of the provided scale
* @since 6.3
*/
inline QRectF snapToPixelGrid(const QRectF &value, qreal scale)
{
return QRectF(snapToPixelGrid(value.topLeft(), scale), snapToPixelGrid(value.bottomRight(), scale));
}
}