aboutsummaryrefslogtreecommitdiff
path: root/src/layout/focus_ring.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-05-01 19:06:08 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-05-02 14:27:53 +0400
commit42cef79c699c0f03b4bb99c4278169c48d9a5bd0 (patch)
tree630d18b49f3d93603a79bcfc5734dc773c6d0cb6 /src/layout/focus_ring.rs
parentd86df5025cfd26ef4a3c48acd8ee80555265ee53 (diff)
downloadniri-42cef79c699c0f03b4bb99c4278169c48d9a5bd0.tar.gz
niri-42cef79c699c0f03b4bb99c4278169c48d9a5bd0.tar.bz2
niri-42cef79c699c0f03b4bb99c4278169c48d9a5bd0.zip
Implement rounded window corners
Diffstat (limited to 'src/layout/focus_ring.rs')
-rw-r--r--src/layout/focus_ring.rs154
1 files changed, 124 insertions, 30 deletions
diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs
index f8a70d93..d52bb08e 100644
--- a/src/layout/focus_ring.rs
+++ b/src/layout/focus_ring.rs
@@ -1,30 +1,32 @@
+use std::cmp::{max, min};
use std::iter::zip;
use arrayvec::ArrayVec;
-use niri_config::GradientRelativeTo;
+use niri_config::{CornerRadius, GradientRelativeTo};
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
use crate::niri_render_elements;
-use crate::render_helpers::gradient::GradientRenderElement;
+use crate::render_helpers::border::BorderRenderElement;
use crate::render_helpers::renderer::NiriRenderer;
#[derive(Debug)]
pub struct FocusRing {
- buffers: [SolidColorBuffer; 4],
- locations: [Point<i32, Logical>; 4],
- sizes: [Size<i32, Logical>; 4],
+ buffers: [SolidColorBuffer; 8],
+ locations: [Point<i32, Logical>; 8],
+ sizes: [Size<i32, Logical>; 8],
full_size: Size<i32, Logical>,
is_active: bool,
is_border: bool,
+ radius: CornerRadius,
config: niri_config::FocusRing,
}
niri_render_elements! {
FocusRingRenderElement => {
SolidColor = SolidColorRenderElement,
- Gradient = GradientRenderElement,
+ Gradient = BorderRenderElement,
}
}
@@ -37,6 +39,7 @@ impl FocusRing {
full_size: Default::default(),
is_active: false,
is_border: false,
+ radius: Default::default(),
config,
}
}
@@ -45,24 +48,69 @@ impl FocusRing {
self.config = config;
}
- pub fn update(&mut self, win_size: Size<i32, Logical>, is_border: bool) {
+ pub fn update(&mut self, win_size: Size<i32, Logical>, is_border: bool, radius: CornerRadius) {
let width = i32::from(self.config.width);
self.full_size = win_size + Size::from((width * 2, width * 2));
+ let radius = radius.fit_to(self.full_size.w as f32, self.full_size.h as f32);
+
if is_border {
- self.sizes[0] = Size::from((win_size.w + width * 2, width));
- self.sizes[1] = Size::from((win_size.w + width * 2, width));
- self.sizes[2] = Size::from((width, win_size.h));
- self.sizes[3] = Size::from((width, win_size.h));
+ let top_left = max(width, radius.top_left.ceil() as i32);
+ let top_right = min(
+ self.full_size.w - top_left,
+ max(width, radius.top_right.ceil() as i32),
+ );
+ let bottom_left = min(
+ self.full_size.h - top_left,
+ max(width, radius.bottom_left.ceil() as i32),
+ );
+ let bottom_right = min(
+ self.full_size.h - top_right,
+ min(
+ self.full_size.w - bottom_left,
+ max(width, radius.bottom_right.ceil() as i32),
+ ),
+ );
+
+ // Top edge.
+ self.sizes[0] = Size::from((win_size.w + width * 2 - top_left - top_right, width));
+ self.locations[0] = Point::from((-width + top_left, -width));
+
+ // Bottom edge.
+ self.sizes[1] =
+ Size::from((win_size.w + width * 2 - bottom_left - bottom_right, width));
+ self.locations[1] = Point::from((-width + bottom_left, win_size.h));
+
+ // Left edge.
+ self.sizes[2] = Size::from((width, win_size.h + width * 2 - top_left - bottom_left));
+ self.locations[2] = Point::from((-width, -width + top_left));
+
+ // Right edge.
+ self.sizes[3] = Size::from((width, win_size.h + width * 2 - top_right - bottom_right));
+ self.locations[3] = Point::from((win_size.w, -width + top_right));
+
+ // Top-left corner.
+ self.sizes[4] = Size::from((top_left, top_left));
+ self.locations[4] = Point::from((-width, -width));
+
+ // Top-right corner.
+ self.sizes[5] = Size::from((top_right, top_right));
+ self.locations[5] = Point::from((win_size.w + width - top_right, -width));
+
+ // Bottom-right corner.
+ self.sizes[6] = Size::from((bottom_right, bottom_right));
+ self.locations[6] = Point::from((
+ win_size.w + width - bottom_right,
+ win_size.h + width - bottom_right,
+ ));
+
+ // Bottom-left corner.
+ self.sizes[7] = Size::from((bottom_left, bottom_left));
+ self.locations[7] = Point::from((-width, win_size.h + width - bottom_left));
for (buf, size) in zip(&mut self.buffers, self.sizes) {
buf.resize(size);
}
-
- self.locations[0] = Point::from((-width, -width));
- self.locations[1] = Point::from((-width, win_size.h));
- self.locations[2] = Point::from((-width, 0));
- self.locations[3] = Point::from((win_size.w, 0));
} else {
self.sizes[0] = self.full_size;
self.buffers[0].resize(self.sizes[0]);
@@ -70,6 +118,7 @@ impl FocusRing {
}
self.is_border = is_border;
+ self.radius = radius;
}
pub fn set_active(&mut self, is_active: bool) {
@@ -93,38 +142,83 @@ impl FocusRing {
scale: Scale<f64>,
view_size: Size<i32, Logical>,
) -> impl Iterator<Item = FocusRingRenderElement> {
- let mut rv = ArrayVec::<_, 4>::new();
+ let mut rv = ArrayVec::<_, 8>::new();
if self.config.off {
return rv.into_iter();
}
+ let border_width = -self.locations[0].y;
+
+ // If drawing as a border with width = 0, then there's nothing to draw.
+ if self.is_border && border_width == 0 {
+ return rv.into_iter();
+ }
+
let gradient = if self.is_active {
self.config.active_gradient
} else {
self.config.inactive_gradient
};
+ let color = if self.is_active {
+ self.config.active_color
+ } else {
+ self.config.inactive_color
+ };
- let full_rect = Rectangle::from_loc_and_size(location + self.locations[0], self.full_size);
+ let offset = Point::from((border_width, border_width));
+ let full_rect = Rectangle::from_loc_and_size(location - offset, self.full_size);
let view_rect = Rectangle::from_loc_and_size((0, 0), view_size);
+ let border_width = if self.is_border {
+ // HACK: increase the border width used for the inner rounded corners a tiny bit to
+ // reduce background bleed.
+ border_width as f32 + 0.5 / scale.x as f32
+ } else {
+ 0.
+ };
+ let shader = BorderRenderElement::shader(renderer);
+
let mut push = |buffer, location: Point<i32, Logical>, size: Size<i32, Logical>| {
- let elem = gradient.and_then(|gradient| {
+ let elem = if let Some(gradient) = gradient {
let gradient_area = match gradient.relative_to {
GradientRelativeTo::Window => full_rect,
GradientRelativeTo::WorkspaceView => view_rect,
};
- GradientRenderElement::new(
- renderer,
- scale,
- Rectangle::from_loc_and_size(location, size),
- gradient_area,
- gradient.from.into(),
- gradient.to.into(),
- ((gradient.angle as f32) - 90.).to_radians(),
- )
- .map(Into::into)
- });
+ shader.cloned().map(|shader| {
+ BorderRenderElement::new(
+ shader,
+ scale,
+ Rectangle::from_loc_and_size(location, size),
+ gradient_area,
+ gradient.from.into(),
+ gradient.to.into(),
+ ((gradient.angle as f32) - 90.).to_radians(),
+ full_rect,
+ border_width,
+ self.radius,
+ )
+ .into()
+ })
+ } else if self.radius != CornerRadius::default() {
+ shader.cloned().map(|shader| {
+ BorderRenderElement::new(
+ shader,
+ scale,
+ Rectangle::from_loc_and_size(location, size),
+ full_rect,
+ color.into(),
+ color.into(),
+ 0.,
+ full_rect,
+ border_width,
+ self.radius,
+ )
+ .into()
+ })
+ } else {
+ None
+ };
let elem = elem.unwrap_or_else(|| {
SolidColorRenderElement::from_buffer(