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 f()
83 }
84
85 #[doc(alias = "GetFontTexUvWhitePixel")]
89 pub fn font_tex_uv_white_pixel(&self) -> [f32; 2] {
90 self.run_with_bound_context(|| unsafe {
91 let uv = crate::sys::igGetFontTexUvWhitePixel();
92 [uv.x, uv.y]
93 })
94 }
95
96 #[doc(alias = "SetWindowFontScale")]
100 pub fn set_window_font_scale(&self, scale: f32) {
101 assert_positive_finite_f32("Ui::set_window_font_scale()", "scale", scale);
102
103 self.run_with_bound_context(|| unsafe {
104 let window = crate::sys::igGetCurrentWindow();
105 if window.is_null() {
106 return;
107 }
108 (*window).FontWindowScale = scale;
109 crate::sys::igUpdateCurrentFontSize(0.0);
110 });
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 fn setup_context() -> crate::Context {
117 let mut ctx = crate::Context::create();
118 let _ = ctx.font_atlas().build();
119 ctx.io_mut().set_display_size([128.0, 128.0]);
120 ctx.io_mut().set_delta_time(1.0 / 60.0);
121 ctx
122 }
123
124 #[test]
125 fn set_window_font_scale_updates_current_window_state() {
126 let mut ctx = setup_context();
127 let ui = ctx.frame();
128
129 ui.window("font_scale_test").build(|| {
130 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
131 assert!(!window.is_null());
132 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
133
134 ui.set_window_font_scale(1.5);
135
136 assert_eq!(unsafe { (*window).FontWindowScale }, 1.5);
137 });
138 }
139
140 #[test]
141 fn font_runtime_size_setters_validate_before_ffi() {
142 let mut ctx = setup_context();
143 {
144 let ui = ctx.frame();
145
146 ui.window("font_size_token").build(|| {
147 let _font = ui.push_font_with_size(None, 18.0);
148 ui.text("font token is scoped");
149 });
150
151 ui.with_font_and_size(None, 0.0, || {
152 ui.text("closure helper is scoped");
153 });
154 }
155 let _ = ctx.render();
156
157 let ui = ctx.frame();
158
159 assert!(
160 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
161 let _ = ui.push_font_with_size(None, -1.0);
162 }))
163 .is_err()
164 );
165
166 ui.window("font_scale_invalid").build(|| {
167 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
168 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
169
170 assert!(
171 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
172 ui.set_window_font_scale(f32::INFINITY);
173 }))
174 .is_err()
175 );
176 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
177 });
178 }
179
180 #[test]
181 fn with_font_and_size_pops_after_panic() {
182 let mut ctx = setup_context();
183 {
184 let ui = ctx.frame();
185
186 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
187 ui.with_font_and_size(None, 18.0, || {
188 panic!("forced panic while font is pushed");
189 });
190 }));
191
192 assert!(result.is_err());
193 ui.text("frame remains balanced after panic");
194 }
195
196 let _ = ctx.render();
197 }
198
199 #[test]
200 fn push_font_with_size_distinguishes_preserved_and_overridden_sizes() {
201 let mut ctx = crate::Context::create();
202 let small = ctx
203 .font_atlas()
204 .add_font(&[crate::FontSource::default_font_with_size(13.0)]);
205 let large = ctx
206 .font_atlas()
207 .add_font(&[crate::FontSource::default_font_with_size(29.0)]);
208 let _ = ctx.font_atlas().build();
209 ctx.io_mut().set_display_size([128.0, 128.0]);
210 ctx.io_mut().set_delta_time(1.0 / 60.0);
211
212 let ui = ctx.frame();
213 assert_eq!(ui.current_font(), small);
214 assert_eq!(ui.current_font_size(), 13.0);
215
216 {
217 let _font = ui.push_font_with_size(Some(large), 0.0);
218 assert_eq!(ui.current_font(), large);
219 assert_eq!(ui.current_font_size(), 13.0);
220 }
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), 37.0);
226 assert_eq!(ui.current_font(), large);
227 assert_eq!(ui.current_font_size(), 37.0);
228 }
229 assert_eq!(ui.current_font(), small);
230 assert_eq!(ui.current_font_size(), 13.0);
231 }
232}