Skip to main content

dear_imgui_rs/context/
fonts.rs

1use crate::fonts::{Font, FontAtlas, FontAtlasRef, SharedFontAtlas};
2use crate::sys;
3use std::marker::PhantomData;
4
5use super::Context;
6use super::binding::{CTX_MUTEX, with_bound_context};
7use super::core::ContextAliveToken;
8
9/// Tracks a font pushed through [`Context::push_font`].
10///
11/// The font stack entry is popped when the token is dropped or when
12/// [`Self::pop`] is called.
13#[must_use]
14pub struct ContextFontStackToken<'ctx> {
15    ctx: *mut sys::ImGuiContext,
16    ctx_alive: ContextAliveToken,
17    _phantom: PhantomData<&'ctx mut Context>,
18}
19
20impl<'ctx> ContextFontStackToken<'ctx> {
21    fn new(ctx: *mut sys::ImGuiContext, ctx_alive: ContextAliveToken) -> Self {
22        Self {
23            ctx,
24            ctx_alive,
25            _phantom: PhantomData,
26        }
27    }
28
29    /// Pops the font stack entry immediately.
30    #[doc(alias = "PopFont")]
31    pub fn pop(self) {}
32
33    /// Pops the font stack entry immediately.
34    #[doc(alias = "PopFont")]
35    pub fn end(self) {}
36}
37
38impl Drop for ContextFontStackToken<'_> {
39    fn drop(&mut self) {
40        if self.ctx.is_null() || !self.ctx_alive.is_alive() {
41            return;
42        }
43
44        let _guard = CTX_MUTEX.lock();
45        unsafe {
46            with_bound_context(self.ctx, || {
47                sys::igPopFont();
48            });
49        }
50    }
51}
52
53impl Context {
54    /// Push a font onto the font stack.
55    ///
56    /// Returns a token that pops the font stack when dropped.
57    #[doc(alias = "PushFont")]
58    pub fn push_font(&mut self, font: &Font) -> ContextFontStackToken<'_> {
59        let _guard = CTX_MUTEX.lock();
60        unsafe {
61            with_bound_context(self.raw, || {
62                let font_ptr =
63                    crate::fonts::validate_font_for_current_context(font, "Context::push_font()");
64                sys::igPushFont(font_ptr, 0.0);
65            });
66        }
67        ContextFontStackToken::new(self.raw, self.alive_token())
68    }
69
70    /// Get the current font
71    #[doc(alias = "GetFont")]
72    pub fn current_font(&self) -> &Font {
73        let _guard = CTX_MUTEX.lock();
74        unsafe { with_bound_context(self.raw, || Font::from_raw(sys::igGetFont() as *const _)) }
75    }
76
77    /// Get the current font size
78    #[doc(alias = "GetFontSize")]
79    pub fn current_font_size(&self) -> f32 {
80        let _guard = CTX_MUTEX.lock();
81        unsafe { with_bound_context(self.raw, || sys::igGetFontSize()) }
82    }
83
84    /// Get a read-only view of the font atlas from the IO structure.
85    ///
86    /// Use [`Context::font_atlas_mut`] or [`Context::fonts`] for loading fonts
87    /// or mutating atlas state.
88    pub fn font_atlas(&self) -> FontAtlasRef<'_> {
89        let _guard = CTX_MUTEX.lock();
90
91        // wasm32 import-style builds keep Dear ImGui state in a separate module
92        // and share linear memory. When the experimental font-atlas feature is
93        // enabled, we allow direct access to the atlas pointer, assuming the
94        // provider has been correctly configured via xtask.
95        #[cfg(all(target_arch = "wasm32", feature = "wasm-font-atlas-experimental"))]
96        unsafe {
97            let io = self.io_ptr("Context::font_atlas()");
98            let atlas_ptr = (*io).Fonts;
99            assert!(
100                !atlas_ptr.is_null(),
101                "ImGui IO Fonts pointer is null on wasm; provider not initialized?"
102            );
103            FontAtlasRef::from_raw(atlas_ptr)
104        }
105
106        // Default wasm path: keep this API disabled to avoid accidental UB.
107        #[cfg(all(target_arch = "wasm32", not(feature = "wasm-font-atlas-experimental")))]
108        {
109            panic!(
110                "font_atlas() is not supported on wasm32 targets without \
111                 `wasm-font-atlas-experimental` feature; \
112                 see docs/WASM.md for current limitations."
113            );
114        }
115
116        #[cfg(not(target_arch = "wasm32"))]
117        unsafe {
118            let io = self.io_ptr("Context::font_atlas()");
119            let atlas_ptr = (*io).Fonts;
120            FontAtlasRef::from_raw(atlas_ptr)
121        }
122    }
123
124    /// Get a mutable reference to the font atlas from the IO structure
125    pub fn font_atlas_mut(&mut self) -> FontAtlas {
126        let _guard = CTX_MUTEX.lock();
127
128        // wasm32 import-style builds keep Dear ImGui state in a separate module
129        // and share linear memory. When the experimental font-atlas feature is
130        // enabled, we allow direct access to the atlas pointer, assuming the
131        // provider has been correctly configured via xtask.
132        #[cfg(all(target_arch = "wasm32", feature = "wasm-font-atlas-experimental"))]
133        unsafe {
134            let io = self.io_ptr("Context::font_atlas_mut()");
135            let atlas_ptr = (*io).Fonts;
136            assert!(
137                !atlas_ptr.is_null(),
138                "ImGui IO Fonts pointer is null on wasm; provider not initialized?"
139            );
140            return FontAtlas::from_raw(atlas_ptr);
141        }
142
143        // Default wasm path: keep this API disabled to avoid accidental UB.
144        #[cfg(all(target_arch = "wasm32", not(feature = "wasm-font-atlas-experimental")))]
145        {
146            panic!(
147                "font_atlas_mut()/fonts() are not supported on wasm32 targets yet; \
148                 enable `wasm-font-atlas-experimental` to opt-in for experiments."
149            );
150        }
151
152        #[cfg(not(target_arch = "wasm32"))]
153        unsafe {
154            let io = self.io_ptr("Context::font_atlas_mut()");
155            let atlas_ptr = (*io).Fonts;
156            FontAtlas::from_raw(atlas_ptr)
157        }
158    }
159
160    /// Returns the font atlas (alias for font_atlas_mut)
161    ///
162    /// This provides compatibility with imgui-rs naming convention
163    pub fn fonts(&mut self) -> FontAtlas {
164        self.font_atlas_mut()
165    }
166
167    /// Attempts to clone the interior shared font atlas **if it exists**.
168    pub fn clone_shared_font_atlas(&mut self) -> Option<SharedFontAtlas> {
169        self.shared_font_atlas.clone()
170    }
171}