Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:40:48.544Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

dynamic_spacing.rs

168 lines · 6.4 KB · rust
1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4    LitInt, Token, parse::Parse, parse::ParseStream, parse_macro_input, punctuated::Punctuated,
5};
6
7struct DynamicSpacingInput {
8    values: Punctuated<DynamicSpacingValue, Token![,]>,
9}
10
11// The input for the derive macro is a list of values.
12//
13// When a single value is provided, the standard spacing formula is
14// used to derive the of spacing values.
15//
16// When a tuple of three values is provided, the values are used as
17// the spacing values directly.
18enum DynamicSpacingValue {
19    Single(LitInt),
20    Tuple(LitInt, LitInt, LitInt),
21}
22
23impl Parse for DynamicSpacingInput {
24    fn parse(input: ParseStream) -> syn::Result<Self> {
25        Ok(DynamicSpacingInput {
26            values: input.parse_terminated(DynamicSpacingValue::parse, Token![,])?,
27        })
28    }
29}
30
31impl Parse for DynamicSpacingValue {
32    fn parse(input: ParseStream) -> syn::Result<Self> {
33        if input.peek(syn::token::Paren) {
34            let content;
35            syn::parenthesized!(content in input);
36            let a: LitInt = content.parse()?;
37            content.parse::<Token![,]>()?;
38            let b: LitInt = content.parse()?;
39            content.parse::<Token![,]>()?;
40            let c: LitInt = content.parse()?;
41            Ok(DynamicSpacingValue::Tuple(a, b, c))
42        } else {
43            Ok(DynamicSpacingValue::Single(input.parse()?))
44        }
45    }
46}
47
48/// Derives the spacing method for the `DynamicSpacing` enum.
49pub fn derive_spacing(input: TokenStream) -> TokenStream {
50    let input = parse_macro_input!(input as DynamicSpacingInput);
51
52    let spacing_ratios: Vec<_> = input
53        .values
54        .iter()
55        .map(|v| {
56            let variant = match v {
57                DynamicSpacingValue::Single(n) => {
58                    format_ident!("Base{:02}", n.base10_parse::<u32>().unwrap())
59                }
60                DynamicSpacingValue::Tuple(_, b, _) => {
61                    format_ident!("Base{:02}", b.base10_parse::<u32>().unwrap())
62                }
63            };
64            match v {
65                DynamicSpacingValue::Single(n) => {
66                    let n = n.base10_parse::<f32>().unwrap();
67                    quote! {
68                        DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
69                            ::theme::UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX,
70                            ::theme::UiDensity::Default => #n / BASE_REM_SIZE_IN_PX,
71                            ::theme::UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX,
72                        }
73                    }
74                }
75                DynamicSpacingValue::Tuple(a, b, c) => {
76                    let a = a.base10_parse::<f32>().unwrap();
77                    let b = b.base10_parse::<f32>().unwrap();
78                    let c = c.base10_parse::<f32>().unwrap();
79                    quote! {
80                        DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
81                            ::theme::UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX,
82                            ::theme::UiDensity::Default => #b / BASE_REM_SIZE_IN_PX,
83                            ::theme::UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX,
84                        }
85                    }
86                }
87            }
88        })
89        .collect();
90
91    let (variant_names, doc_strings): (Vec<_>, Vec<_>) = input
92        .values
93        .iter()
94        .map(|v| {
95            let variant = match v {
96                DynamicSpacingValue::Single(n) => {
97                    format_ident!("Base{:02}", n.base10_parse::<u32>().unwrap())
98                }
99                DynamicSpacingValue::Tuple(_, b, _) => {
100                    format_ident!("Base{:02}", b.base10_parse::<u32>().unwrap())
101                }
102            };
103            let doc_string = match v {
104                DynamicSpacingValue::Single(n) => {
105                    let n = n.base10_parse::<f32>().unwrap();
106                    let compact = (n - 4.0).max(0.0);
107                    let comfortable = n + 4.0;
108                    format!(
109                        "`{}px`|`{}px`|`{}px (@16px/rem)` - Scales with the user's rem size.",
110                        compact, n, comfortable
111                    )
112                }
113                DynamicSpacingValue::Tuple(a, b, c) => {
114                    let a = a.base10_parse::<f32>().unwrap();
115                    let b = b.base10_parse::<f32>().unwrap();
116                    let c = c.base10_parse::<f32>().unwrap();
117                    format!(
118                        "`{}px`|`{}px`|`{}px (@16px/rem)` - Scales with the user's rem size.",
119                        a, b, c
120                    )
121                }
122            };
123            (quote!(#variant), quote!(#doc_string))
124        })
125        .unzip();
126
127    let expanded = quote! {
128        /// A dynamic spacing system that adjusts spacing based on
129        /// [UiDensity].
130        ///
131        /// The number following "Base" refers to the base pixel size
132        /// at the default rem size and spacing settings.
133        ///
134        /// When possible, [DynamicSpacing] should be used over manual
135        /// or built-in spacing values in places dynamic spacing is needed.
136        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
137        pub enum DynamicSpacing {
138            #(
139                #[doc = #doc_strings]
140                #variant_names,
141            )*
142        }
143
144        impl DynamicSpacing {
145            /// Returns the spacing ratio, should only be used internally.
146            fn spacing_ratio(&self, cx: &App) -> f32 {
147                const BASE_REM_SIZE_IN_PX: f32 = 16.0;
148                match self {
149                    #(#spacing_ratios,)*
150                }
151            }
152
153            /// Returns the spacing value in rems.
154            pub fn rems(&self, cx: &App) -> Rems {
155                rems(self.spacing_ratio(cx))
156            }
157
158            /// Returns the spacing value in pixels.
159            pub fn px(&self, cx: &App) -> Pixels {
160                let ui_font_size_f32: f32 = ::theme::theme_settings(cx).ui_font_size(cx).into();
161                px(ui_font_size_f32 * self.spacing_ratio(cx))
162            }
163        }
164    };
165
166    TokenStream::from(expanded)
167}
168
Served at tenant.openagents/omega Member data and write actions are omitted.