wayland_window/
theme.rs

1use {Location, UIButton};
2
3const DECORATION_SIZE: i32 = 8;
4const DECORATION_TOP_SIZE: i32 = 32;
5
6#[cfg(target_endian = "little")]
7macro_rules! auto_endian(
8    ($a: expr, $r: expr, $g: expr, $b: expr) => {
9        [$b, $g, $r, $a]
10    }
11);
12
13#[cfg(target_endian = "big")]
14macro_rules! auto_endian(
15    ($a: expr, $r: expr, $g: expr, $b: expr) => {
16        [$a, $r, $g, $b]
17    }
18);
19
20// defining the color scheme
21const INACTIVE_BORDER: [u8; 4] = auto_endian!(0xFF, 0x60, 0x60, 0x60);
22const ACTIVE_BORDER: [u8; 4] = auto_endian!(0xFF, 0x80, 0x80, 0x80);
23const RED_BUTTON_REGULAR: [u8; 4] = auto_endian!(0xFF, 0xB0, 0x40, 0x40);
24const RED_BUTTON_HOVER: [u8; 4] = auto_endian!(0xFF, 0xFF, 0x40, 0x40);
25const GREEN_BUTTON_REGULAR: [u8; 4] = auto_endian!(0xFF, 0x40, 0xB0, 0x40);
26const GREEN_BUTTON_HOVER: [u8; 4] = auto_endian!(0xFF, 0x40, 0xFF, 0x40);
27const YELLOW_BUTTON_REGULAR: [u8; 4] = auto_endian!(0xFF, 0xB0, 0xB0, 0x40);
28const YELLOW_BUTTON_HOVER: [u8; 4] = auto_endian!(0xFF, 0xFF, 0xFF, 0x40);
29const YELLOW_BUTTON_DISABLED: [u8; 4] = auto_endian!(0xFF, 0x80, 0x80, 0x20);
30
31/// Compute on which part of the window given point falls
32pub(crate) fn compute_location((x, y): (f64, f64), (w, h): (i32, i32)) -> Location {
33    if y <= DECORATION_TOP_SIZE as f64 {
34        // we are in the top part
35        if x <= DECORATION_SIZE as f64 {
36            Location::TopLeft
37        } else if x <= (w + DECORATION_SIZE) as f64 {
38            if y <= DECORATION_SIZE as f64 {
39                Location::Top
40            } else {
41                // check for buttons
42                if (w >= 24) && (x > (w + DECORATION_SIZE - 24) as f64) && (x <= (w + DECORATION_SIZE) as f64)
43                    && (y > DECORATION_SIZE as f64) && (y <= (DECORATION_SIZE + 16) as f64)
44                {
45                    Location::Button(UIButton::Close)
46                } else if (w >= 56) && (x > (w + DECORATION_SIZE - 56) as f64)
47                    && (x <= (w + DECORATION_SIZE - 32) as f64)
48                    && (y > DECORATION_SIZE as f64)
49                    && (y <= (DECORATION_SIZE + 16) as f64)
50                {
51                    Location::Button(UIButton::Maximize)
52                } else if (w >= 88) && (x > (w + DECORATION_SIZE - 88) as f64)
53                    && (x <= (w + DECORATION_SIZE - 64) as f64)
54                    && (y > DECORATION_SIZE as f64)
55                    && (y <= (DECORATION_SIZE + 16) as f64)
56                {
57                    Location::Button(UIButton::Minimize)
58                } else {
59                    Location::TopBar
60                }
61            }
62        } else {
63            Location::TopRight
64        }
65    } else if y <= (DECORATION_TOP_SIZE + h) as f64 {
66        if x <= DECORATION_SIZE as f64 {
67            Location::Left
68        } else if x <= (w + DECORATION_SIZE) as f64 {
69            Location::Inside
70        } else {
71            Location::Right
72        }
73    } else {
74        if x <= DECORATION_SIZE as f64 {
75            Location::BottomLeft
76        } else if x <= (w + DECORATION_SIZE) as f64 {
77            Location::Bottom
78        } else {
79            Location::BottomRight
80        }
81    }
82}
83
84/// Offset at which the contents should be drawn relative to the top-left
85/// corner of the decorations
86pub(crate) fn subsurface_offset() -> (i32, i32) {
87    (DECORATION_SIZE, DECORATION_TOP_SIZE)
88}
89
90/// Subtracts the border dimensions from the given dimensions.
91pub fn subtract_borders(width: i32, height: i32) -> (i32, i32) {
92    (
93        width - 2 * (DECORATION_SIZE as i32),
94        height - DECORATION_SIZE as i32 - DECORATION_TOP_SIZE as i32,
95    )
96}
97
98/// Adds the border dimensions to the given dimensions.
99pub fn add_borders(width: i32, height: i32) -> (i32, i32) {
100    (
101        width + 2 * (DECORATION_SIZE as i32),
102        height + DECORATION_SIZE as i32 + DECORATION_TOP_SIZE as i32,
103    )
104}
105
106/// Total number of pixels of the rectangle containing the whole
107/// decorated window
108pub(crate) fn pxcount(w: i32, h: i32) -> i32 {
109    (w + 2 * DECORATION_SIZE) * (h + DECORATION_SIZE + DECORATION_TOP_SIZE)
110}
111
112/// Draw the decorations on the rectangle
113///
114/// Actual contents of the window will be drawn on top
115pub(crate) fn draw_contents(canvas: &mut [u8], w: u32, h: u32, activated: bool, _maximized: bool,
116                            maximizable: bool, ptr_location: Location) {
117    let ds = DECORATION_SIZE as u32;
118    let dts = DECORATION_TOP_SIZE as u32;
119    let mut canvas = Canvas::new(w + 2 * ds, h + ds + dts, canvas);
120    // draw the borders
121    let border_rectangles = [
122        (0, 0, w + 2 * ds, dts+1),      // top rectangle
123        (0, dts, ds+1, h),              // left rectangle
124        (w + ds-1, dts, ds+1, h),         // right rectangle
125        (0, h + dts-1, w + 2 * ds, ds+1), // bottom rectangle
126    ];
127
128    // We've built an ImageBuffer from a raw &[u8] buffer, and the wayland spec
129    // explicitly says we should use native endiannes
130    // also we're doing ARGB (while image expects RGBA), though as long as we
131    // only blit pixels it's not very important
132
133    // fill these rectangles with grey
134    let border_color = if activated {
135        ACTIVE_BORDER
136    } else {
137        INACTIVE_BORDER
138    };
139    for &(x, y, w, h) in &border_rectangles {
140        for xx in x..(x + w) {
141            for yy in y..(y + h) {
142                canvas.put_pixel(xx, yy, border_color);
143            }
144        }
145    }
146
147    // draw the red close button
148    if w >= 24 {
149        let button_color = if let Location::Button(UIButton::Close) = ptr_location {
150            RED_BUTTON_HOVER
151        } else {
152            RED_BUTTON_REGULAR
153        };
154        for xx in (w + ds - 24)..(w + ds) {
155            for yy in ds..(ds + 16) {
156                canvas.put_pixel(xx, yy, button_color);
157            }
158        }
159    }
160
161    // draw the yellow maximize button
162    if w >= 56 {
163        let button_color = if maximizable {
164            if let Location::Button(UIButton::Maximize) = ptr_location {
165                YELLOW_BUTTON_HOVER
166            } else {
167                YELLOW_BUTTON_REGULAR
168            }
169        } else {
170            YELLOW_BUTTON_DISABLED
171        };
172        for xx in (w + ds - 56)..(w + ds - 32) {
173            for yy in ds..(ds + 16) {
174                canvas.put_pixel(xx, yy, button_color);
175            }
176        }
177    }
178
179    // draw the green minimize button
180    if w >= 88 {
181        let button_color = if let Location::Button(UIButton::Minimize) = ptr_location {
182            GREEN_BUTTON_HOVER
183        } else {
184            GREEN_BUTTON_REGULAR
185        };
186        for xx in (w + ds - 88)..(w + ds - 64) {
187            for yy in ds..(ds + 16) {
188                canvas.put_pixel(xx, yy, button_color);
189            }
190        }
191    }
192}
193
194struct Canvas<'a> {
195    width: u32,
196    contents: &'a mut [u8]
197}
198
199impl<'a> Canvas<'a> {
200    fn new(width: u32, height: u32, contents: &mut[u8]) -> Canvas {
201        debug_assert!(contents.len() == (width*height*4) as usize);
202        Canvas { width, contents }
203    }
204
205    #[inline]
206    fn put_pixel(&mut self, x: u32, y: u32, val: [u8; 4]) {
207        let idx = ((y*self.width + x)*4) as usize;
208        self.contents[idx + 0] = val[0];
209        self.contents[idx + 1] = val[1];
210        self.contents[idx + 2] = val[2];
211        self.contents[idx + 3] = val[3];
212    }
213}