From 42cef79c699c0f03b4bb99c4278169c48d9a5bd0 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 1 May 2024 19:06:08 +0400 Subject: Implement rounded window corners --- wiki/Configuration:-Window-Rules.md | 74 ++++++++++++++ wiki/examples/resize-custom-shader.frag | 164 -------------------------------- wiki/examples/resize_custom_shader.frag | 164 ++++++++++++++++++++++++++++++++ wiki/img/border-radius-clip.png | 3 + wiki/img/clip-to-geometry.png | 3 + wiki/img/different-corner-radius.png | 3 + wiki/img/geometry-corner-radius.png | 3 + 7 files changed, 250 insertions(+), 164 deletions(-) delete mode 100644 wiki/examples/resize-custom-shader.frag create mode 100644 wiki/examples/resize_custom_shader.frag create mode 100644 wiki/img/border-radius-clip.png create mode 100644 wiki/img/clip-to-geometry.png create mode 100644 wiki/img/different-corner-radius.png create mode 100644 wiki/img/geometry-corner-radius.png (limited to 'wiki') diff --git a/wiki/Configuration:-Window-Rules.md b/wiki/Configuration:-Window-Rules.md index 289f1b1c..6cdb2320 100644 --- a/wiki/Configuration:-Window-Rules.md +++ b/wiki/Configuration:-Window-Rules.md @@ -59,6 +59,9 @@ window-rule { // Same as focus-ring. } + geometry-corner-radius 12 + clip-to-geometry true + min-width 100 max-width 200 min-height 300 @@ -378,6 +381,77 @@ window-rule { } ``` +#### `geometry-corner-radius` + +Since: 0.1.6 + +Set the corner radius of the window. + +On its own, this setting will only affect the border and the focus ring—they will round their corners to match the geometry corner radius. +If you'd like to force-round the corners of the window itself, set `clip-to-geometry true` in addition to this setting. + +``` +window-rule { + geometry-corner-radius 12 +} +``` + +The radius is set in logical pixels, and controls the radius of the window itself, that is, the inner radius of the border: + +![](./img/geometry-corner-radius.png) + +Instead of one radius, you can set four, for each corner. +The order is the same as in CSS: top-left, top-right, bottom-right, bottom-left. + +``` +window-rule { + geometry-corner-radius 8 8 0 0 +} +``` + +This way, you can match GTK 3 applications which have square bottom corners: + +![](./img/different-corner-radius.png) + +#### `clip-to-geometry` + +Since: 0.1.6 + +Clips the window to its visual geometry. + +This will cut out any client-side window shadows, and also round window corners according to `geometry-corner-radius`. + +![](./img/clip-to-geometry.png) + +``` +window-rule { + clip-to-geometry true +} +``` + +Enable border, set `geometry-corner-radius` and `clip-to-geometry`, and you've got a classic setup: + +![](./img/border-radius-clip.png) + +``` +prefer-no-csd + +layout { + focus-ring { + off + } + + border { + width 2 + } +} + +window-rule { + geometry-corner-radius 12 + clip-to-geometry true +} +``` + #### Size Overrides You can amend the window's minimum and maximum size in logical pixels. diff --git a/wiki/examples/resize-custom-shader.frag b/wiki/examples/resize-custom-shader.frag deleted file mode 100644 index 71710a9f..00000000 --- a/wiki/examples/resize-custom-shader.frag +++ /dev/null @@ -1,164 +0,0 @@ -// Your shader must contain one function (see the bottom of this file). -// -// It should not contain any uniform definitions or anything else, as niri -// provides them for you. -// -// All symbols defined by niri will have a niri_ prefix, so don't use it for -// your own variables and functions. - -// The function that you must define looks like this: -vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { - vec4 color = /* ...compute the color... */; - return color; -} - -// It takes as input: -// -// * coords_curr_geo: coordinates of the current pixel relative to the current -// window geometry. -// -// These are homogeneous (the Z component is equal to 1) and scaled in such a -// way that the 0 to 1 coordinates lie within the current window geometry (in -// the middle of a resize). Pixels outside the window geometry will have -// coordinates below 0 or above 1. -// -// The window geometry is its "visible bounds" from the user's perspective. -// -// The shader runs over an area of unspecified size and location, so you must -// expect and handle coordinates outside the [0, 1] range. The area will be -// large enough to accomodate a crossfade effect. -// -// * size_curr_geo: size of the current window geometry in physical pixels. -// -// It is homogeneous (the Z component is equal to 1). -// -// The function must return the color of the pixel (with premultiplied alpha). -// The pixel color will be further processed by niri (for example, to apply the -// final opacity from window rules). - -// Now let's go over the uniforms that niri defines. - -// Previous (before resize) window texture. -uniform sampler2D niri_tex_prev; - -// Matrix that converts geometry coordinates into the previous window texture -// coordinates. -// -// The window texture can and will go outside the geometry (for client-side -// decoration shadows for example), which is why this matrix is necessary. -uniform mat3 niri_geo_to_tex_prev; - -// Next (after resize) window texture. -uniform sampler2D niri_tex_next; - -// Matrix that converts geometry coordinates into the next window texture -// coordinates. -uniform mat3 niri_geo_to_tex_next; - - -// Matrix that converts coords_curr_geo into coordinates inside the previous -// (before resize) window geometry. -uniform mat3 niri_curr_geo_to_prev_geo; - -// Matrix that converts coords_curr_geo into coordinates inside the next -// (after resize) window geometry. -uniform mat3 niri_curr_geo_to_next_geo; - - -// Unclamped progress of the resize. -// -// Goes from 0 to 1 but may overshoot and oscillate. -uniform float niri_progress; - -// Clamped progress of the resize. -// -// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not -// overshoot or oscillate. -uniform float niri_clamped_progress; - -// Now let's look at some examples. You can copy everything below this line -// into your custom-shader to experiment. - -// Example: fill the current geometry with a solid vertical gradient. -vec4 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) { - vec3 coords = coords_curr_geo; - vec4 color = vec4(0.0); - - // Paint only the area inside the current geometry. - if (0.0 <= coords.x && coords.x <= 1.0 - && 0.0 <= coords.y && coords.y <= 1.0) - { - vec4 from = vec4(1.0, 0.0, 0.0, 1.0); - vec4 to = vec4(0.0, 1.0, 0.0, 1.0); - color = mix(from, to, coords.y); - } - - return color; -} - -// Example: crossfade between previous and next texture, stretched to the -// current geometry. -vec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) { - // Convert coordinates into the texture space for sampling. - vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo; - vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st); - - // Convert coordinates into the texture space for sampling. - vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; - vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st); - - vec4 color = mix(color_prev, color_next, niri_clamped_progress); - return color; -} - -// Example: next texture, stretched to the current geometry. -vec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) { - vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; - vec4 color = texture2D(niri_tex_next, coords_tex_next.st); - return color; -} - -// Example: next texture, stretched to the current geometry if smaller, and -// cropped if bigger. -vec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) { - vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; - - vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; - vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo; - - // We can crop if the current window size is smaller than the next window - // size. One way to tell is by comparing to 1.0 the X and Y scaling - // coefficients in the current-to-next transformation matrix. - bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0; - bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0; - - vec3 coords = coords_stretch; - if (can_crop_by_x) - coords.x = coords_crop.x; - if (can_crop_by_y) - coords.y = coords_crop.y; - - vec4 color = texture2D(niri_tex_next, coords.st); - - // However, when we crop, we also want to crop out anything outside the - // current geometry. This is because the area of the shader is unspecified - // and usually bigger than the current geometry, so if we don't fill pixels - // outside with transparency, the texture will leak out. - // - // When stretching, this is not an issue because the area outside will - // correspond to client-side decoration shadows, which are already supposed - // to be outside. - if (can_crop_by_x && (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x)) - color = vec4(0.0); - if (can_crop_by_y && (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y)) - color = vec4(0.0); - - return color; -} - -// This is the function that you must define. -vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { - // You can pick one of the example functions or write your own. - return stretch_or_crop_next(coords_curr_geo, size_curr_geo); -} - diff --git a/wiki/examples/resize_custom_shader.frag b/wiki/examples/resize_custom_shader.frag new file mode 100644 index 00000000..71710a9f --- /dev/null +++ b/wiki/examples/resize_custom_shader.frag @@ -0,0 +1,164 @@ +// Your shader must contain one function (see the bottom of this file). +// +// It should not contain any uniform definitions or anything else, as niri +// provides them for you. +// +// All symbols defined by niri will have a niri_ prefix, so don't use it for +// your own variables and functions. + +// The function that you must define looks like this: +vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec4 color = /* ...compute the color... */; + return color; +} + +// It takes as input: +// +// * coords_curr_geo: coordinates of the current pixel relative to the current +// window geometry. +// +// These are homogeneous (the Z component is equal to 1) and scaled in such a +// way that the 0 to 1 coordinates lie within the current window geometry (in +// the middle of a resize). Pixels outside the window geometry will have +// coordinates below 0 or above 1. +// +// The window geometry is its "visible bounds" from the user's perspective. +// +// The shader runs over an area of unspecified size and location, so you must +// expect and handle coordinates outside the [0, 1] range. The area will be +// large enough to accomodate a crossfade effect. +// +// * size_curr_geo: size of the current window geometry in physical pixels. +// +// It is homogeneous (the Z component is equal to 1). +// +// The function must return the color of the pixel (with premultiplied alpha). +// The pixel color will be further processed by niri (for example, to apply the +// final opacity from window rules). + +// Now let's go over the uniforms that niri defines. + +// Previous (before resize) window texture. +uniform sampler2D niri_tex_prev; + +// Matrix that converts geometry coordinates into the previous window texture +// coordinates. +// +// The window texture can and will go outside the geometry (for client-side +// decoration shadows for example), which is why this matrix is necessary. +uniform mat3 niri_geo_to_tex_prev; + +// Next (after resize) window texture. +uniform sampler2D niri_tex_next; + +// Matrix that converts geometry coordinates into the next window texture +// coordinates. +uniform mat3 niri_geo_to_tex_next; + + +// Matrix that converts coords_curr_geo into coordinates inside the previous +// (before resize) window geometry. +uniform mat3 niri_curr_geo_to_prev_geo; + +// Matrix that converts coords_curr_geo into coordinates inside the next +// (after resize) window geometry. +uniform mat3 niri_curr_geo_to_next_geo; + + +// Unclamped progress of the resize. +// +// Goes from 0 to 1 but may overshoot and oscillate. +uniform float niri_progress; + +// Clamped progress of the resize. +// +// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not +// overshoot or oscillate. +uniform float niri_clamped_progress; + +// Now let's look at some examples. You can copy everything below this line +// into your custom-shader to experiment. + +// Example: fill the current geometry with a solid vertical gradient. +vec4 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords = coords_curr_geo; + vec4 color = vec4(0.0); + + // Paint only the area inside the current geometry. + if (0.0 <= coords.x && coords.x <= 1.0 + && 0.0 <= coords.y && coords.y <= 1.0) + { + vec4 from = vec4(1.0, 0.0, 0.0, 1.0); + vec4 to = vec4(0.0, 1.0, 0.0, 1.0); + color = mix(from, to, coords.y); + } + + return color; +} + +// Example: crossfade between previous and next texture, stretched to the +// current geometry. +vec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) { + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo; + vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st); + + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st); + + vec4 color = mix(color_prev, color_next, niri_clamped_progress); + return color; +} + +// Example: next texture, stretched to the current geometry. +vec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color = texture2D(niri_tex_next, coords_tex_next.st); + return color; +} + +// Example: next texture, stretched to the current geometry if smaller, and +// cropped if bigger. +vec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; + + vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; + vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo; + + // We can crop if the current window size is smaller than the next window + // size. One way to tell is by comparing to 1.0 the X and Y scaling + // coefficients in the current-to-next transformation matrix. + bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0; + bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0; + + vec3 coords = coords_stretch; + if (can_crop_by_x) + coords.x = coords_crop.x; + if (can_crop_by_y) + coords.y = coords_crop.y; + + vec4 color = texture2D(niri_tex_next, coords.st); + + // However, when we crop, we also want to crop out anything outside the + // current geometry. This is because the area of the shader is unspecified + // and usually bigger than the current geometry, so if we don't fill pixels + // outside with transparency, the texture will leak out. + // + // When stretching, this is not an issue because the area outside will + // correspond to client-side decoration shadows, which are already supposed + // to be outside. + if (can_crop_by_x && (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x)) + color = vec4(0.0); + if (can_crop_by_y && (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y)) + color = vec4(0.0); + + return color; +} + +// This is the function that you must define. +vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + // You can pick one of the example functions or write your own. + return stretch_or_crop_next(coords_curr_geo, size_curr_geo); +} + diff --git a/wiki/img/border-radius-clip.png b/wiki/img/border-radius-clip.png new file mode 100644 index 00000000..7c41fd72 --- /dev/null +++ b/wiki/img/border-radius-clip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6246413f95e5aa2d63db3fc8bc7726e45af3d20a95ea8f66614f4d62fa7a6624 +size 34239 diff --git a/wiki/img/clip-to-geometry.png b/wiki/img/clip-to-geometry.png new file mode 100644 index 00000000..1f57b013 --- /dev/null +++ b/wiki/img/clip-to-geometry.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88ca1517153755ee64b3fae4e67eced1f83873026ad1ad35c44b85f31fa8171d +size 33591 diff --git a/wiki/img/different-corner-radius.png b/wiki/img/different-corner-radius.png new file mode 100644 index 00000000..39818759 --- /dev/null +++ b/wiki/img/different-corner-radius.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e420523fff7891335ef9af5410d946abdd468f1e1dad88e0c189a2b21c9bc7f3 +size 11099 diff --git a/wiki/img/geometry-corner-radius.png b/wiki/img/geometry-corner-radius.png new file mode 100644 index 00000000..0cdee59d --- /dev/null +++ b/wiki/img/geometry-corner-radius.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ed3d553cae8948726ab03db3bf4ad9ef2dc9cf4593e24a96fad9d8820af949d +size 25777 -- cgit