dear_imgui_rs/fonts/
mod.rs1pub mod atlas;
7pub mod font;
8pub mod glyph;
9#[deprecated(
16 since = "0.2.0",
17 note = "ImGui 1.92+ recommends dynamic fonts with on-demand glyph loading; glyph ranges are kept for legacy compatibility"
18)]
19pub mod glyph_ranges;
20
21pub use atlas::*;
22pub use font::*;
23pub use glyph::*;
24#[allow(deprecated)]
25pub use glyph_ranges::*;
26
27use crate::Ui;
28
29fn assert_non_negative_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 non-negative");
32}
33
34fn assert_positive_finite_f32(caller: &str, name: &str, value: f32) {
35 assert!(value.is_finite(), "{caller} {name} must be finite");
36 assert!(value > 0.0, "{caller} {name} must be positive");
37}
38
39impl Ui {
41 #[doc(alias = "GetFont")]
43 pub fn current_font(&self) -> &Font {
44 self.run_with_bound_context(|| unsafe {
45 Font::from_raw(crate::sys::igGetFont() as *const _)
46 })
47 }
48
49 #[doc(alias = "GetFontSize")]
51 pub fn current_font_size(&self) -> f32 {
52 self.run_with_bound_context(|| unsafe { crate::sys::igGetFontSize() })
53 }
54
55 #[doc(alias = "PushFont")]
63 pub fn push_font_with_size(&self, font: Option<&Font>, size: f32) -> crate::FontStackToken<'_> {
64 assert_non_negative_finite_f32("Ui::push_font_with_size()", "size", size);
65 self.run_with_bound_context(|| unsafe {
66 let font_ptr = font.map_or(std::ptr::null_mut(), |f| {
67 crate::fonts::validate_font_for_current_context(f, "Ui::push_font_with_size()")
68 });
69 crate::sys::igPushFont(font_ptr, size);
70 });
71 crate::FontStackToken::new(self)
72 }
73
74 pub fn with_font_and_size<F, R>(&self, font: Option<&Font>, size: f32, f: F) -> R
76 where
77 F: FnOnce() -> R,
78 {
79 let _token = self.push_font_with_size(font, size);
80 f()
81 }
82
83 #[doc(alias = "GetFontTexUvWhitePixel")]
87 pub fn font_tex_uv_white_pixel(&self) -> [f32; 2] {
88 self.run_with_bound_context(|| unsafe {
89 let uv = crate::sys::igGetFontTexUvWhitePixel();
90 [uv.x, uv.y]
91 })
92 }
93
94 #[doc(alias = "SetWindowFontScale")]
98 pub fn set_window_font_scale(&self, scale: f32) {
99 assert_positive_finite_f32("Ui::set_window_font_scale()", "scale", scale);
100
101 self.run_with_bound_context(|| unsafe {
102 let window = crate::sys::igGetCurrentWindow();
103 if window.is_null() {
104 return;
105 }
106 (*window).FontWindowScale = scale;
107 crate::sys::igUpdateCurrentFontSize(0.0);
108 });
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 fn setup_context() -> crate::Context {
115 let mut ctx = crate::Context::create();
116 let _ = ctx.font_atlas_mut().build();
117 ctx.io_mut().set_display_size([128.0, 128.0]);
118 ctx.io_mut().set_delta_time(1.0 / 60.0);
119 ctx
120 }
121
122 #[test]
123 fn set_window_font_scale_updates_current_window_state() {
124 let mut ctx = setup_context();
125 let ui = ctx.frame();
126
127 ui.window("font_scale_test").build(|| {
128 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
129 assert!(!window.is_null());
130 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
131
132 ui.set_window_font_scale(1.5);
133
134 assert_eq!(unsafe { (*window).FontWindowScale }, 1.5);
135 });
136 }
137
138 #[test]
139 fn font_runtime_size_setters_validate_before_ffi() {
140 let mut ctx = setup_context();
141 {
142 let ui = ctx.frame();
143
144 ui.window("font_size_token").build(|| {
145 let _font = ui.push_font_with_size(None, 18.0);
146 ui.text("font token is scoped");
147 });
148
149 ui.with_font_and_size(None, 0.0, || {
150 ui.text("closure helper is scoped");
151 });
152 }
153 let _ = ctx.render();
154
155 let ui = ctx.frame();
156
157 assert!(
158 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
159 let _ = ui.push_font_with_size(None, -1.0);
160 }))
161 .is_err()
162 );
163
164 ui.window("font_scale_invalid").build(|| {
165 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
166 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
167
168 assert!(
169 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
170 ui.set_window_font_scale(f32::INFINITY);
171 }))
172 .is_err()
173 );
174 assert_eq!(unsafe { (*window).FontWindowScale }, 1.0);
175 });
176 }
177
178 #[test]
179 fn with_font_and_size_pops_after_panic() {
180 let mut ctx = setup_context();
181 {
182 let ui = ctx.frame();
183
184 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
185 ui.with_font_and_size(None, 18.0, || {
186 panic!("forced panic while font is pushed");
187 });
188 }));
189
190 assert!(result.is_err());
191 ui.text("frame remains balanced after panic");
192 }
193
194 let _ = ctx.render();
195 }
196}