Skip to content

Commit fdc1302

Browse files
committed
widgets: add ClippingRectangle
1 parent 68ba500 commit fdc1302

9 files changed

+168
-2
lines changed

BUILD.md

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Quickshell has a set of base dependencies you will always need, names vary by di
4141
- `cmake`
4242
- `qt6base`
4343
- `qt6declarative`
44+
- `qtshadertools` (build-time only)
4445
- `pkg-config`
4546
- `cli11`
4647

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ if (NOT CMAKE_BUILD_TYPE)
9191
set(CMAKE_BUILD_TYPE Debug)
9292
endif()
9393

94-
set(QT_FPDEPS Gui Qml Quick QuickControls2 Widgets)
94+
set(QT_FPDEPS Gui Qml Quick QuickControls2 Widgets ShaderTools)
9595

9696
include(cmake/pch.cmake)
9797

default.nix

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
cmake,
99
ninja,
1010
qt6,
11+
spirv-tools,
1112
cli11,
1213
breakpad,
1314
jemalloc,
@@ -45,6 +46,8 @@
4546
nativeBuildInputs = with pkgs; [
4647
cmake
4748
ninja
49+
qt6.qtshadertools
50+
spirv-tools
4851
qt6.wrapQtAppsHook
4952
pkg-config
5053
] ++ (lib.optionals withWayland [

src/widgets/CMakeLists.txt

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1-
qt_add_library(quickshell-widgets STATIC)
1+
qt_add_library(quickshell-widgets STATIC
2+
cliprect.cpp
3+
)
24

35
qt_add_qml_module(quickshell-widgets
46
URI Quickshell.Widgets
57
VERSION 0.1
68
QML_FILES
79
IconImage.qml
10+
ClippingRectangle.qml
11+
)
12+
13+
qt6_add_shaders(quickshell-widgets "widgets-cliprect"
14+
NOHLSL NOMSL BATCHABLE PRECOMPILE OPTIMIZED QUIET
15+
PREFIX "/Quickshell/Widgets"
16+
FILES shaders/cliprect.frag
17+
OUTPUTS shaders/cliprect.frag.qsb
18+
)
19+
20+
qt6_add_shaders(quickshell-widgets "widgets-cliprect-ub"
21+
NOHLSL NOMSL BATCHABLE PRECOMPILE OPTIMIZED QUIET
22+
PREFIX "/Quickshell/Widgets"
23+
FILES shaders/cliprect.frag
24+
OUTPUTS shaders/cliprect-ub.frag.qsb
25+
DEFINES CONTENT_UNDER_BORDER
826
)
927

1028
install_qml_module(quickshell-widgets)

src/widgets/ClippingRectangle.qml

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import QtQuick
2+
3+
///! Rectangle capable of clipping content inside its border.
4+
/// > [!WARNING] This type requires at least Qt 6.7.
5+
///
6+
/// This is a specialized version of @@QtQuick.Rectangle that clips content
7+
/// inside of its border, including rounded rectangles. It costs more than
8+
/// @@QtQuick.Rectangle, so it should not be used unless you need to clip
9+
/// items inside of it to the border.
10+
Item {
11+
id: root
12+
13+
/// If content should be displayed underneath the border.
14+
///
15+
/// Defaults to false, does nothing if the border is opaque.
16+
property bool contentUnderBorder: false;
17+
/// If the content item should be resized to fit inside the border.
18+
///
19+
/// Defaults to `!contentUnderBorder`. Most useful when combined with
20+
/// `anchors.fill: parent` on an item passed to the ClippingRectangle.
21+
property bool contentInsideBorder: !root.contentUnderBorder;
22+
/// If the rectangle should be antialiased.
23+
///
24+
/// Defaults to true if any corner has a non-zero radius, otherwise false.
25+
property /*bool*/alias antialiasing: rectangle.antialiasing;
26+
/// The background color of the rectangle, which goes under its content.
27+
property /*color*/alias color: shader.backgroundColor;
28+
/// See @@QtQuick.Rectangle.border.
29+
property clippingRectangleBorder border;
30+
/// Radius of all corners. Defaults to 0.
31+
property /*real*/alias radius: rectangle.radius
32+
/// Radius of the top left corner. Defaults to @@radius.
33+
property /*real*/alias topLeftRadius: rectangle.topLeftRadius
34+
/// Radius of the top right corner. Defaults to @@radius.
35+
property /*real*/alias topRightRadius: rectangle.topRightRadius
36+
/// Radius of the bottom left corner. Defaults to @@radius.
37+
property /*real*/alias bottomLeftRadius: rectangle.bottomLeftRadius
38+
/// Radius of the bottom right corner. Defaults to @@radius.
39+
property /*real*/alias borromRightRadius: rectangle.bottomRightRadius
40+
41+
/// Visual children of the ClippingRectangle's @@contentItem. (`list<Item>`).
42+
///
43+
/// See @@QtQuick.Item.children for details.
44+
default property alias children: contentItem.children;
45+
/// The item containing the rectangle's content.
46+
/// There is usually no reason to use this directly.
47+
readonly property alias contentItem: contentItem;
48+
49+
Rectangle {
50+
id: rectangle
51+
anchors.fill: root
52+
color: "#ffff0000"
53+
border.color: "#ff00ff00"
54+
border.pixelAligned: root.border.pixelAligned
55+
border.width: root.border.width
56+
layer.enabled: true
57+
visible: false
58+
}
59+
60+
Item {
61+
id: contentItemContainer
62+
anchors.fill: root
63+
layer.enabled: true
64+
visible: false
65+
66+
Item {
67+
id: contentItem
68+
anchors.fill: parent
69+
anchors.margins: root.contentInsideBorder ? root.border.width : 0
70+
}
71+
}
72+
73+
ShaderEffect {
74+
id: shader
75+
anchors.fill: root
76+
fragmentShader: `qrc:/Quickshell/Widgets/shaders/cliprect${root.contentUnderBorder ? "-ub" : ""}.frag.qsb`
77+
property Rectangle rect: rectangle;
78+
property color backgroundColor;
79+
property color borderColor: root.border.color;
80+
property Item content: contentItemContainer;
81+
}
82+
}

src/widgets/cliprect.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "cliprect.hpp" // NOLINT

src/widgets/cliprect.hpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <qcolor.h>
4+
#include <qmetaobject.h>
5+
#include <qnamespace.h>
6+
#include <qqmlintegration.h>
7+
#include <qtmetamacros.h>
8+
9+
class ClippingRectangleBorder {
10+
Q_GADGET;
11+
Q_PROPERTY(QColor color MEMBER color);
12+
Q_PROPERTY(bool pixelAligned MEMBER pixelAligned);
13+
Q_PROPERTY(int width MEMBER width);
14+
QML_VALUE_TYPE(clippingRectangleBorder);
15+
16+
public:
17+
QColor color = Qt::black;
18+
bool pixelAligned = true;
19+
int width = 0;
20+
};

src/widgets/module.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ name = "Quickshell.Widgets"
22
description = "Bundled widgets"
33
qml_files = [
44
"IconImage.qml",
5+
"ClippingRectangle.qml",
56
]
67
-----

src/widgets/shaders/cliprect.frag

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#version 440
2+
layout(location = 0) in vec2 qt_TexCoord0;
3+
layout(location = 0) out vec4 fragColor;
4+
5+
layout(std140, binding = 0) uniform buf {
6+
mat4 qt_Matrix;
7+
float qt_Opacity;
8+
vec4 backgroundColor;
9+
vec4 borderColor;
10+
};
11+
12+
layout(binding = 1) uniform sampler2D rect;
13+
layout(binding = 2) uniform sampler2D content;
14+
15+
vec4 overlay(vec4 base, vec4 overlay) {
16+
if (overlay.a == 0.0) return base;
17+
18+
float baseMul = 1.0 - overlay.a;
19+
float newAlpha = overlay.a + base.a * baseMul;
20+
vec3 rgb = (overlay.rgb * overlay.a + base.rgb * base.a * baseMul) / newAlpha;
21+
return vec4(rgb, newAlpha);
22+
}
23+
24+
void main() {
25+
vec4 contentColor = texture(content, qt_TexCoord0.xy);
26+
vec4 rectColor = texture(rect, qt_TexCoord0.xy);
27+
28+
#ifdef CONTENT_UNDER_BORDER
29+
float contentAlpha = rectColor.a;
30+
#else
31+
float contentAlpha = rectColor.r;
32+
#endif
33+
34+
float borderAlpha = rectColor.g;
35+
36+
vec4 innerColor = overlay(backgroundColor, contentColor) * contentAlpha;
37+
vec4 borderColor = borderColor * borderAlpha;
38+
39+
fragColor = (innerColor * (1.0 - borderColor.a) + borderColor) * qt_Opacity;
40+
}

0 commit comments

Comments
 (0)