Skip to main content

whisker_css/prop/
effects.rs

1//! Visual-effect properties: opacity, visibility, overflow, shadow,
2//! filter, cursor, pointer-events, clip-path.
3
4use crate::css::Css;
5use crate::data_type::{Color, Length, LengthPercentage};
6use crate::keyword::{Cursor, Overflow, PointerEvents, Visibility};
7
8impl Css {
9    /// Sets `opacity`. Lynx clamps to `0.0..=1.0`. Default: `1`.
10    /// <https://lynxjs.org/api/css/properties/opacity>
11    pub fn opacity(self, v: f32) -> Self {
12        self.push_raw("opacity", crate::to_css::number_to_string(v))
13    }
14
15    /// Sets `visibility`. Lynx default: `visible`. `collapse` is not
16    /// supported.
17    /// <https://lynxjs.org/api/css/properties/visibility>
18    pub fn visibility(self, v: Visibility) -> Self {
19        self.push("visibility", v)
20    }
21
22    /// Sets `overflow`. Lynx accepts only `visible` and `hidden`.
23    /// <https://lynxjs.org/api/css/properties/overflow>
24    pub fn overflow(self, v: Overflow) -> Self {
25        self.push("overflow-x", v).push("overflow-y", v)
26    }
27
28    /// Sets `overflow-x`.
29    /// <https://lynxjs.org/api/css/properties/overflow-x>
30    pub fn overflow_x(self, v: Overflow) -> Self {
31        self.push("overflow-x", v)
32    }
33
34    /// Sets `overflow-y`.
35    /// <https://lynxjs.org/api/css/properties/overflow-y>
36    pub fn overflow_y(self, v: Overflow) -> Self {
37        self.push("overflow-y", v)
38    }
39
40    /// Sets `cursor`.
41    /// <https://lynxjs.org/api/css/properties/cursor>
42    pub fn cursor(self, v: Cursor) -> Self {
43        self.push("cursor", v)
44    }
45
46    /// Sets `pointer-events`.
47    /// <https://lynxjs.org/api/css/properties/pointer-events>
48    pub fn pointer_events(self, v: PointerEvents) -> Self {
49        self.push("pointer-events", v)
50    }
51
52    /// Sets `box-shadow` to a single shadow. Pass `None` for inset
53    /// to get an outer shadow.
54    /// <https://lynxjs.org/api/css/properties/box-shadow>
55    pub fn box_shadow(
56        self,
57        offset_x: Length,
58        offset_y: Length,
59        blur_radius: Length,
60        spread_radius: Length,
61        color: Color,
62    ) -> Self {
63        use crate::to_css::ToCss;
64        let mut s = String::new();
65        let _ = offset_x.to_css(&mut s);
66        s.push(' ');
67        let _ = offset_y.to_css(&mut s);
68        s.push(' ');
69        let _ = blur_radius.to_css(&mut s);
70        s.push(' ');
71        let _ = spread_radius.to_css(&mut s);
72        s.push(' ');
73        let _ = color.to_css(&mut s);
74        self.push_raw("box-shadow", s)
75    }
76
77    /// Sets an inset `box-shadow`.
78    /// <https://lynxjs.org/api/css/properties/box-shadow>
79    pub fn box_shadow_inset(
80        self,
81        offset_x: Length,
82        offset_y: Length,
83        blur_radius: Length,
84        spread_radius: Length,
85        color: Color,
86    ) -> Self {
87        use crate::to_css::ToCss;
88        let mut s = String::from("inset ");
89        let _ = offset_x.to_css(&mut s);
90        s.push(' ');
91        let _ = offset_y.to_css(&mut s);
92        s.push(' ');
93        let _ = blur_radius.to_css(&mut s);
94        s.push(' ');
95        let _ = spread_radius.to_css(&mut s);
96        s.push(' ');
97        let _ = color.to_css(&mut s);
98        self.push_raw("box-shadow", s)
99    }
100
101    /// Sets `filter` to a raw CSS filter list. Use raw because the
102    /// `<filter-function>` grammar (blur, drop-shadow, etc.) is rich
103    /// and rarely worth typing.
104    /// <https://lynxjs.org/api/css/properties/filter>
105    pub fn filter(self, value: impl Into<String>) -> Self {
106        self.push_raw("filter", value)
107    }
108
109    /// Sets `mask-image` to a raw CSS value (URL or gradient).
110    /// <https://lynxjs.org/api/css/properties/mask-image>
111    pub fn mask_image(self, value: impl Into<String>) -> Self {
112        self.push_raw("mask-image", value)
113    }
114
115    /// Sets `clip-path` to a raw CSS value.
116    /// <https://lynxjs.org/api/css/properties/clip-path>
117    pub fn clip_path(self, value: impl Into<String>) -> Self {
118        self.push_raw("clip-path", value)
119    }
120
121    /// Sets `caret-color`.
122    /// <https://lynxjs.org/api/css/properties/caret-color>
123    pub fn caret_color(self, v: Color) -> Self {
124        self.push("caret-color", v)
125    }
126
127    /// Sets `outline-width`.
128    /// <https://lynxjs.org/api/css/properties/outline-width>
129    pub fn outline_width(self, v: Length) -> Self {
130        self.push("outline-width", v)
131    }
132
133    /// Sets `outline-color`.
134    /// <https://lynxjs.org/api/css/properties/outline-color>
135    pub fn outline_color(self, v: Color) -> Self {
136        self.push("outline-color", v)
137    }
138
139    /// Sets `outline-style`.
140    /// <https://lynxjs.org/api/css/properties/outline-style>
141    pub fn outline_style(self, v: crate::keyword::BorderStyle) -> Self {
142        self.push("outline-style", v)
143    }
144
145    /// Sets `outline-offset`.
146    /// <https://lynxjs.org/api/css/properties/outline-offset>
147    pub fn outline_offset(self, v: Length) -> Self {
148        self.push("outline-offset", v)
149    }
150
151    /// Sets `caret-width`. Lynx accepts a length controlling the
152    /// rendered caret thickness.
153    pub fn caret_width(self, v: Length) -> Self {
154        self.push("caret-width", v)
155    }
156
157    /// Sets `-x-handle-color` — Lynx-only selection-handle color.
158    /// <https://lynxjs.org/api/css/properties/-x-handle-color>
159    pub fn x_handle_color(self, v: Color) -> Self {
160        self.push("-x-handle-color", v)
161    }
162
163    /// Sets `-x-handle-size` — Lynx-only selection-handle size.
164    /// <https://lynxjs.org/api/css/properties/-x-handle-size>
165    pub fn x_handle_size(self, v: impl Into<LengthPercentage>) -> Self {
166        self.push("-x-handle-size", v.into())
167    }
168
169    /// Sets `-x-auto-font-size` — Lynx-only auto font-size flag.
170    /// <https://lynxjs.org/api/css/properties/-x-auto-font-size>
171    pub fn x_auto_font_size(self, enabled: bool) -> Self {
172        self.push_raw("-x-auto-font-size", if enabled { "true" } else { "false" })
173    }
174
175    /// Sets `-x-auto-font-size-preset-sizes` — Lynx-only list of preset sizes.
176    /// <https://lynxjs.org/api/css/properties/-x-auto-font-size-preset-sizes>
177    pub fn x_auto_font_size_preset_sizes(self, sizes: impl IntoIterator<Item = Length>) -> Self {
178        use crate::to_css::ToCss;
179        let mut s = String::new();
180        let mut first = true;
181        for sz in sizes {
182            if !first {
183                s.push(' ');
184            }
185            let _ = sz.to_css(&mut s);
186            first = false;
187        }
188        self.push_raw("-x-auto-font-size-preset-sizes", s)
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use crate::data_type::Color;
195    use crate::ext::*;
196    use crate::keyword::*;
197    use crate::Css;
198
199    #[test]
200    fn opacity_full_range() {
201        assert_eq!(Css::new().opacity(0.0).to_string(), "opacity: 0;");
202        assert_eq!(Css::new().opacity(0.5).to_string(), "opacity: 0.5;");
203        assert_eq!(Css::new().opacity(1.0).to_string(), "opacity: 1;");
204    }
205
206    #[test]
207    fn visibility_keyword() {
208        assert_eq!(
209            Css::new().visibility(Visibility::Hidden).to_string(),
210            "visibility: hidden;"
211        );
212    }
213
214    #[test]
215    fn overflow_expands_to_both_axes() {
216        let s = Css::new().overflow(Overflow::Hidden);
217        assert_eq!(s.to_string(), "overflow-x: hidden; overflow-y: hidden;");
218    }
219
220    #[test]
221    fn overflow_axis_individual_override() {
222        let s = Css::new()
223            .overflow(Overflow::Hidden)
224            .overflow_y(Overflow::Visible);
225        assert_eq!(s.to_string(), "overflow-x: hidden; overflow-y: visible;");
226    }
227
228    #[test]
229    fn cursor_and_pointer_events() {
230        let s = Css::new()
231            .cursor(Cursor::Pointer)
232            .pointer_events(PointerEvents::None);
233        assert_eq!(s.to_string(), "cursor: pointer; pointer-events: none;");
234    }
235
236    #[test]
237    fn box_shadow_outer() {
238        let s = Css::new().box_shadow(
239            2.px(),
240            4.px(),
241            8.px(),
242            crate::data_type::Length::Zero,
243            Color::hex(0x000000),
244        );
245        assert_eq!(s.to_string(), "box-shadow: 2px 4px 8px 0 rgb(0, 0, 0);");
246    }
247
248    #[test]
249    fn box_shadow_inset() {
250        let s = Css::new().box_shadow_inset(
251            crate::data_type::Length::Zero,
252            crate::data_type::Length::Zero,
253            4.px(),
254            crate::data_type::Length::Zero,
255            Color::hex(0xFFFFFF),
256        );
257        assert_eq!(
258            s.to_string(),
259            "box-shadow: inset 0 0 4px 0 rgb(255, 255, 255);"
260        );
261    }
262
263    #[test]
264    fn filter_clip_path_mask_raw() {
265        let s = Css::new()
266            .filter("blur(4px)")
267            .clip_path("circle(50%)")
268            .mask_image("url(\"a.png\")");
269        assert_eq!(
270            s.to_string(),
271            "filter: blur(4px); clip-path: circle(50%); mask-image: url(\"a.png\");"
272        );
273    }
274
275    #[test]
276    fn outline_props() {
277        let s = Css::new()
278            .outline_width(1.px())
279            .outline_style(BorderStyle::Solid)
280            .outline_color(Color::hex(0xFF0000))
281            .outline_offset(2.px());
282        assert_eq!(
283            s.to_string(),
284            "outline-width: 1px; outline-style: solid; outline-color: rgb(255, 0, 0); outline-offset: 2px;"
285        );
286    }
287
288    #[test]
289    fn caret_props() {
290        let s = Css::new()
291            .caret_color(Color::hex(0xFF00FF))
292            .caret_width(2.px());
293        assert_eq!(
294            s.to_string(),
295            "caret-color: rgb(255, 0, 255); caret-width: 2px;"
296        );
297    }
298
299    #[test]
300    fn x_handle_props() {
301        let s = Css::new()
302            .x_handle_color(Color::hex(0x00FF00))
303            .x_handle_size(8.px());
304        assert_eq!(
305            s.to_string(),
306            "-x-handle-color: rgb(0, 255, 0); -x-handle-size: 8px;"
307        );
308    }
309
310    #[test]
311    fn x_auto_font_size_flag_and_presets() {
312        let s = Css::new()
313            .x_auto_font_size(true)
314            .x_auto_font_size_preset_sizes([12.px(), 14.px(), 16.px()]);
315        assert_eq!(
316            s.to_string(),
317            "-x-auto-font-size: true; -x-auto-font-size-preset-sizes: 12px 14px 16px;"
318        );
319    }
320}