aboutsummaryrefslogtreecommitdiff
path: root/src/layout/focus_ring.rs
blob: 6e2730b8d424bbc36f8ded287ea1990047be7d0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::iter::zip;

use arrayvec::ArrayVec;
use niri_config::{self, Color};
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::utils::{Logical, Point, Scale, Size};

#[derive(Debug)]
pub struct FocusRing {
    buffers: [SolidColorBuffer; 4],
    locations: [Point<i32, Logical>; 4],
    is_off: bool,
    is_border: bool,
    width: i32,
    active_color: Color,
    inactive_color: Color,
}

pub type FocusRingRenderElement = SolidColorRenderElement;

impl FocusRing {
    pub fn new(config: niri_config::FocusRing) -> Self {
        Self {
            buffers: Default::default(),
            locations: Default::default(),
            is_off: config.off,
            is_border: false,
            width: config.width.into(),
            active_color: config.active_color,
            inactive_color: config.inactive_color,
        }
    }

    pub fn update_config(&mut self, config: niri_config::FocusRing) {
        self.is_off = config.off;
        self.width = config.width.into();
        self.active_color = config.active_color;
        self.inactive_color = config.inactive_color;
    }

    pub fn update(
        &mut self,
        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));

            self.locations[0] = win_pos + Point::from((-self.width, -self.width));
            self.locations[1] = win_pos + Point::from((-self.width, win_size.h));
            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;
    }

    pub fn set_active(&mut self, is_active: bool) {
        let color = if is_active {
            self.active_color.into()
        } else {
            self.inactive_color.into()
        };

        for buf in &mut self.buffers {
            buf.set_color(color);
        }
    }

    pub fn render(&self, scale: Scale<f64>) -> impl Iterator<Item = FocusRingRenderElement> {
        let mut rv = ArrayVec::<_, 4>::new();

        if self.is_off {
            return rv.into_iter();
        }

        let mut push = |buffer, location: Point<i32, Logical>| {
            let elem = SolidColorRenderElement::from_buffer(
                buffer,
                location.to_physical_precise_round(scale),
                scale,
                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()
    }

    pub fn width(&self) -> i32 {
        self.width
    }

    pub fn is_off(&self) -> bool {
        self.is_off
    }
}