Skip to main content

bern_ir/
corners.rs

1//! Named corner radii on one platform. `style!` names radii once
2//! (`corner { card: 22 }`) and gives other values on some platforms
3//! (`#[target(windows)] corner { card: 8 }`); elements and style rules use
4//! the names. Before a target generates, the names in elements become the
5//! numbers they have on its platform, and `window.menu_bar` what it is
6//! there.
7
8use crate::{Application, Expr, ExprKind, Node, Prop};
9use bern_protocol::Value;
10
11impl Application {
12    /// This application with every named corner of its elements at the
13    /// radius it has on `platform` (`macos`, `windows`, `browser`, …).
14    /// `window.menu_bar` holds on macOS, whose menu bar shows the
15    /// application's `MenuBar`, and nowhere else.
16    pub fn on_platform(&self, platform: &str) -> Application {
17        let mut app = self.with_menu_bar(platform == "macos");
18        if app.style.corners.is_empty() {
19            return app;
20        }
21        let style = std::mem::take(&mut app.style);
22        let radius = |name: &str, radius: f64| style.corner(name, platform).unwrap_or(radius);
23        for component in &mut app.components {
24            resolve_node(&mut component.body, &radius);
25        }
26        for window in &mut app.windows.windows {
27            resolve_node(&mut window.content, &radius);
28        }
29        app.style = style;
30        app
31    }
32}
33
34fn resolve_node(node: &mut Node, radius: &impl Fn(&str, f64) -> f64) {
35    match node {
36        Node::Element(element) => {
37            for prop in element.props.values_mut() {
38                let Prop::Expr { expr } = prop else {
39                    continue;
40                };
41                // A name alone is a number, as the radius written out is.
42                if let ExprKind::Corner {
43                    name,
44                    radius: value,
45                } = &expr.kind
46                {
47                    *prop = Prop::Number {
48                        value: radius(name, *value),
49                    };
50                    continue;
51                }
52                resolve_expr(expr, radius);
53            }
54            for child in &mut element.children {
55                resolve_node(child, radius);
56            }
57        }
58        Node::Component(_) => {}
59        Node::If(node) => {
60            for (_, body) in &mut node.branches {
61                for child in body {
62                    resolve_node(child, radius);
63                }
64            }
65            for child in &mut node.otherwise {
66                resolve_node(child, radius);
67            }
68        }
69        Node::IfLet(node) => {
70            for child in node.then.iter_mut().chain(&mut node.otherwise) {
71                resolve_node(child, radius);
72            }
73        }
74        Node::Match(node) => {
75            for arm in &mut node.arms {
76                for child in &mut arm.body {
77                    resolve_node(child, radius);
78                }
79            }
80        }
81    }
82}
83
84fn resolve_expr(expr: &mut Expr, radius: &impl Fn(&str, f64) -> f64) {
85    match &mut expr.kind {
86        ExprKind::Corner {
87            name,
88            radius: value,
89        } => {
90            expr.kind = ExprKind::Literal {
91                value: Value::Float(radius(name, *value)),
92            };
93        }
94        ExprKind::Literal { .. } | ExprKind::Binding { .. } => {}
95        ExprKind::Variant { fields, .. } => {
96            for field in fields {
97                resolve_expr(field, radius);
98            }
99        }
100        ExprKind::Not { expr } | ExprKind::Neg { expr } => resolve_expr(expr, radius),
101        ExprKind::Binary { lhs, rhs, .. } => {
102            resolve_expr(lhs, radius);
103            resolve_expr(rhs, radius);
104        }
105        ExprKind::If {
106            cond,
107            then,
108            otherwise,
109        } => {
110            resolve_expr(cond, radius);
111            resolve_expr(then, radius);
112            resolve_expr(otherwise, radius);
113        }
114    }
115}