dear_imgui_rs/fonts/
mod.rs1pub mod atlas;
14mod baked;
15pub mod font;
16pub mod glyph;
17
18pub use atlas::*;
19pub use baked::*;
20pub use glyph::*;
21
22use crate::Ui;
23
24fn assert_non_negative_finite_f32(caller: &str, name: &str, value: f32) {
25 assert!(value.is_finite(), "{caller} {name} must be finite");
26 assert!(value >= 0.0, "{caller} {name} must be non-negative");
27}
28
29fn assert_positive_finite_f32(caller: &str, name: &str, value: f32) {
30 assert!(value.is_finite(), "{caller} {name} must be finite");
31 assert!(value > 0.0, "{caller} {name} must be positive");
32}
33
34impl Ui {
36 #[doc(alias = "GetFont")]
38 pub fn current_font(&self) -> FontId {
39 self.run_with_bound_context(|| unsafe {
40 FontId::from_font(crate::sys::igGetFont(), "Ui::current_font()")
41 })
42 }
43
44 #[doc(alias = "GetFontSize")]
46 pub fn current_font_size(&self) -> f32 {
47 self.run_with_bound_context(|| unsafe { crate::sys::igGetFontSize() })
48 }
49
50 #[doc(alias = "PushFont")]
61 pub fn push_font_with_size(
62 &self,
63 font: Option<FontId>,
64 size: f32,
65 ) -> crate::FontStackToken<'_> {
66 assert_non_negative_finite_f32("Ui::push_font_with_size()", "size", size);
67 self.run_with_bound_context(|| unsafe {
68 let font_ptr = font.map_or(std::ptr::null_mut(), |id| {
69 crate::fonts::validate_font_id_for_current_context(id, "Ui::push_font_with_size()")
70 });
71 crate::sys::igPushFont(font_ptr, size);
72 });
73 crate::FontStackToken::new(self)
74 }
75
76 pub fn with_font_and_size<F, R>(&self, font: Option<FontId>, size: f32, f: F) -> R
78 where
79 F: FnOnce() -> R,
80 {
81 let token = self.push_font_with_size(font, size);
82 let result = f();
83 drop(token);
84 result
85 }
86
87 #[doc(alias = "GetFontTexUvWhitePixel")]
91 pub fn font_tex_uv_white_pixel(&self) -> [f32; 2] {
92 self.run_with_bound_context(|| unsafe {
93 let uv = crate::sys::igGetFontTexUvWhitePixel();
94 [uv.x, uv.y]
95 })
96 }
97
98 #[doc(alias = "SetWindowFontScale")]
102 pub fn set_window_font_scale(&self, scale: f32) {
103 assert_positive_finite_f32("Ui::set_window_font_scale()", "scale", scale);
104
105 self.run_with_bound_context(|| unsafe {
106 let window = crate::sys::igGetCurrentWindow();
107 if window.is_null() {
108 return;
109 }
110 (*window).FontWindowScale = scale;
111 crate::sys::igUpdateCurrentFontSize(0.0);
112 });
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 fn setup_context() -> crate::Context {
119 let mut ctx = crate::Context::create();
120 ctx.font_atlas()
121 .try_claim_legacy_renderer()
122 .expect("legacy renderer font atlas should be available")
123 .build();
124 ctx.io_mut().set_display_size([128.0, 128.0]);
125 ctx.io_mut().set_delta_time(1.0 / 60.0);
126 ctx
127 }
128
129 #[test]
130 fn set_window_font_scale_updates_current_window_state() {
131 let mut ctx = setup_context();
132 let ui = ctx.frame();
133
134 ui.window("font_scale_test").build(|| {
135 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
136 assert!(!window.is_null());
137 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
138
139 ui.set_window_font_scale(1.5);
140
141 assert_eq!(unsafe { (*window).FontWindowScale }, 1.5);
142 });
143 }
144
145 #[test]
146 fn font_runtime_size_setters_validate_before_ffi() {
147 let mut ctx = setup_context();
148 {
149 let ui = ctx.frame();
150
151 ui.window("font_size_token").build(|| {
152 let _font = ui.push_font_with_size(None, 18.0);
153 ui.text("font token is scoped");
154 });
155
156 ui.with_font_and_size(None, 0.0, || {
157 ui.text("closure helper is scoped");
158 });
159 }
160 let _ = ctx.render_legacy();
161
162 let ui = ctx.frame();
163
164 assert!(
165 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
166 let _ = ui.push_font_with_size(None, -1.0);
167 }))
168 .is_err()
169 );
170
171 ui.window("font_scale_invalid").build(|| {
172 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
173 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
174
175 assert!(
176 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
177 ui.set_window_font_scale(f32::INFINITY);
178 }))
179 .is_err()
180 );
181 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
182 });
183 }
184
185 #[test]
186 fn with_font_and_size_pops_after_panic() {
187 let mut ctx = setup_context();
188 {
189 let ui = ctx.frame();
190
191 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
192 ui.with_font_and_size(None, 18.0, || {
193 panic!("forced panic while font is pushed");
194 });
195 }));
196
197 assert!(result.is_err());
198 ui.text("frame remains balanced after panic");
199 }
200
201 let _ = ctx.render_legacy();
202 }
203
204 #[test]
205 fn push_font_with_size_distinguishes_preserved_and_overridden_sizes() {
206 let mut ctx = crate::Context::create();
207 let small = ctx
208 .font_atlas()
209 .add_font(&[crate::FontSource::default_font_with_size(13.0)]);
210 let large = ctx
211 .font_atlas()
212 .add_font(&[crate::FontSource::default_font_with_size(29.0)]);
213 ctx.font_atlas()
214 .try_claim_legacy_renderer()
215 .expect("legacy renderer font atlas should be available")
216 .build();
217 ctx.io_mut().set_display_size([128.0, 128.0]);
218 ctx.io_mut().set_delta_time(1.0 / 60.0);
219
220 let ui = ctx.frame();
221 assert_eq!(ui.current_font(), small);
222 assert_eq!(ui.current_font_size(), 13.0);
223
224 {
225 let _font = ui.push_font_with_size(Some(large), 0.0);
226 assert_eq!(ui.current_font(), large);
227 assert_eq!(ui.current_font_size(), 13.0);
228 }
229 assert_eq!(ui.current_font(), small);
230 assert_eq!(ui.current_font_size(), 13.0);
231
232 {
233 let _font = ui.push_font_with_size(Some(large), 37.0);
234 assert_eq!(ui.current_font(), large);
235 assert_eq!(ui.current_font_size(), 37.0);
236 }
237 assert_eq!(ui.current_font(), small);
238 assert_eq!(ui.current_font_size(), 13.0);
239 }
240}