mirror of
https://github.com/YaLTeR/niri.git
synced 2024-09-11 12:35:58 +03:00
Draw focus ring as border on SSD windows
With SSD we're reasonably sure that the window is a rectangle with no rounded corners, so a rectangular border should work.
This commit is contained in:
parent
b283f546aa
commit
1d66f1b81e
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -121,6 +121,12 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -1462,6 +1468,7 @@ name = "niri"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"arrayvec",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-io",
|
"async-io",
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
|
@ -8,6 +8,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.75" }
|
anyhow = { version = "1.0.75" }
|
||||||
|
arrayvec = "0.7.4"
|
||||||
async-channel = { version = "1.9.0", optional = true }
|
async-channel = { version = "1.9.0", optional = true }
|
||||||
async-io = { version = "1.13.0", optional = true }
|
async-io = { version = "1.13.0", optional = true }
|
||||||
bitflags = "2.4.0"
|
bitflags = "2.4.0"
|
||||||
|
@ -35,6 +35,7 @@ use std::mem;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||||
use smithay::backend::renderer::element::utils::{
|
use smithay::backend::renderer::element::utils::{
|
||||||
@ -46,6 +47,7 @@ use smithay::backend::renderer::ImportAll;
|
|||||||
use smithay::desktop::space::SpaceElement;
|
use smithay::desktop::space::SpaceElement;
|
||||||
use smithay::desktop::{layer_map_for_output, Window};
|
use smithay::desktop::{layer_map_for_output, Window};
|
||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
|
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
|
||||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
use smithay::render_elements;
|
use smithay::render_elements;
|
||||||
@ -74,6 +76,7 @@ pub trait LayoutElement: SpaceElement + PartialEq + Clone {
|
|||||||
fn min_size(&self) -> Size<i32, Logical>;
|
fn min_size(&self) -> Size<i32, Logical>;
|
||||||
fn max_size(&self) -> Size<i32, Logical>;
|
fn max_size(&self) -> Size<i32, Logical>;
|
||||||
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool;
|
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool;
|
||||||
|
fn has_ssd(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -188,9 +191,10 @@ pub struct Workspace<W: LayoutElement> {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FocusRing {
|
struct FocusRing {
|
||||||
buffer: SolidColorBuffer,
|
buffers: [SolidColorBuffer; 4],
|
||||||
location: Point<i32, Logical>,
|
locations: [Point<i32, Logical>; 4],
|
||||||
is_off: bool,
|
is_off: bool,
|
||||||
|
is_border: bool,
|
||||||
width: i32,
|
width: i32,
|
||||||
active_color: Color,
|
active_color: Color,
|
||||||
inactive_color: Color,
|
inactive_color: Color,
|
||||||
@ -328,48 +332,88 @@ impl LayoutElement for Window {
|
|||||||
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
|
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
|
||||||
self.toplevel().wl_surface() == wl_surface
|
self.toplevel().wl_surface() == wl_surface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_ssd(&self) -> bool {
|
||||||
|
self.toplevel().current_state().decoration_mode
|
||||||
|
== Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FocusRing {
|
impl FocusRing {
|
||||||
fn resize(&mut self, size: Size<i32, Logical>) {
|
fn update(
|
||||||
let size = size + Size::from((self.width * 2, self.width * 2));
|
&mut self,
|
||||||
self.buffer.resize(size);
|
win_pos: Point<i32, Logical>,
|
||||||
}
|
win_size: Size<i32, Logical>,
|
||||||
|
is_border: bool,
|
||||||
|
) {
|
||||||
|
if is_border {
|
||||||
|
self.buffers[0].resize((win_size.w + self.width * 2, self.width));
|
||||||
|
self.buffers[1].resize((win_size.w + self.width * 2, self.width));
|
||||||
|
self.buffers[2].resize((self.width, win_size.h));
|
||||||
|
self.buffers[3].resize((self.width, win_size.h));
|
||||||
|
|
||||||
fn reposition(&mut self, win_pos: Point<i32, Logical>) {
|
self.locations[0] = win_pos + Point::from((-self.width, -self.width));
|
||||||
let offset = Point::from((self.width, self.width));
|
self.locations[1] = win_pos + Point::from((-self.width, win_size.h));
|
||||||
self.location = win_pos - offset;
|
self.locations[2] = win_pos + Point::from((-self.width, 0));
|
||||||
|
self.locations[3] = win_pos + Point::from((win_size.w, 0));
|
||||||
|
} else {
|
||||||
|
let size = win_size + Size::from((self.width * 2, self.width * 2));
|
||||||
|
self.buffers[0].resize(size);
|
||||||
|
self.locations[0] = win_pos - Point::from((self.width, self.width));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.is_border = is_border;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_active(&mut self, is_active: bool) {
|
fn set_active(&mut self, is_active: bool) {
|
||||||
self.buffer.set_color(if is_active {
|
let color = if is_active {
|
||||||
self.active_color.into()
|
self.active_color.into()
|
||||||
} else {
|
} else {
|
||||||
self.inactive_color.into()
|
self.inactive_color.into()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
for buf in &mut self.buffers {
|
||||||
|
buf.set_color(color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, scale: Scale<f64>) -> Option<SolidColorRenderElement> {
|
fn render(&self, scale: Scale<f64>) -> impl Iterator<Item = SolidColorRenderElement> {
|
||||||
|
let mut rv = ArrayVec::<_, 4>::new();
|
||||||
|
|
||||||
if self.is_off {
|
if self.is_off {
|
||||||
return None;
|
return rv.into_iter();
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(SolidColorRenderElement::from_buffer(
|
let mut push = |buffer, location: Point<i32, Logical>| {
|
||||||
&self.buffer,
|
let elem = SolidColorRenderElement::from_buffer(
|
||||||
self.location.to_physical_precise_round(scale),
|
buffer,
|
||||||
scale,
|
location.to_physical_precise_round(scale),
|
||||||
1.,
|
scale,
|
||||||
Kind::Unspecified,
|
1.,
|
||||||
))
|
Kind::Unspecified,
|
||||||
|
);
|
||||||
|
rv.push(elem);
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.is_border {
|
||||||
|
for (buf, loc) in zip(&self.buffers, self.locations) {
|
||||||
|
push(buf, loc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push(&self.buffers[0], self.locations[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FocusRing {
|
impl FocusRing {
|
||||||
fn new(config: config::FocusRing) -> Self {
|
fn new(config: config::FocusRing) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer: SolidColorBuffer::new((0, 0), [0., 0., 0., 0.]),
|
buffers: Default::default(),
|
||||||
location: Point::from((0, 0)),
|
locations: Default::default(),
|
||||||
is_off: config.off,
|
is_off: config.off,
|
||||||
|
is_border: false,
|
||||||
width: config.width.into(),
|
width: config.width.into(),
|
||||||
active_color: config.active_color,
|
active_color: config.active_color,
|
||||||
inactive_color: config.inactive_color,
|
inactive_color: config.inactive_color,
|
||||||
@ -1679,14 +1723,14 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
let col = &self.columns[self.active_column_idx];
|
let col = &self.columns[self.active_column_idx];
|
||||||
let active_win = &col.windows[col.active_window_idx];
|
let active_win = &col.windows[col.active_window_idx];
|
||||||
let geom = active_win.geometry();
|
let geom = active_win.geometry();
|
||||||
|
let has_ssd = active_win.has_ssd();
|
||||||
|
|
||||||
let win_pos = Point::from((
|
let win_pos = Point::from((
|
||||||
self.column_x(self.active_column_idx) - view_pos,
|
self.column_x(self.active_column_idx) - view_pos,
|
||||||
col.window_y(col.active_window_idx),
|
col.window_y(col.active_window_idx),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.focus_ring.reposition(win_pos);
|
self.focus_ring.update(win_pos, geom.size, has_ssd);
|
||||||
self.focus_ring.resize(geom.size);
|
|
||||||
self.focus_ring.set_active(is_active);
|
self.focus_ring.set_active(is_active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2726,6 +2770,10 @@ mod tests {
|
|||||||
fn is_wl_surface(&self, _wl_surface: &WlSurface) -> bool {
|
fn is_wl_surface(&self, _wl_surface: &WlSurface) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_ssd(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arbitrary_bbox() -> impl Strategy<Value = Rectangle<i32, Logical>> {
|
fn arbitrary_bbox() -> impl Strategy<Value = Rectangle<i32, Logical>> {
|
||||||
|
Loading…
Reference in New Issue
Block a user