arcdps_imgui/
lib.rs

1#![cfg_attr(test, allow(clippy::float_cmp))]
2#![deny(rust_2018_idioms)]
3// #![deny(missing_docs)]
4
5pub extern crate imgui_sys as sys;
6
7use std::cell;
8use std::ffi::CStr;
9use std::os::raw::{c_char, c_void};
10use std::ptr;
11use std::str;
12use std::thread;
13
14pub use self::clipboard::*;
15pub use self::color::ImColor32;
16pub use self::context::*;
17pub use self::drag_drop::{DragDropFlags, DragDropSource, DragDropTarget};
18pub use self::draw_list::{ChannelsSplit, DrawListMut};
19pub use self::fonts::atlas::*;
20pub use self::fonts::font::*;
21pub use self::fonts::glyph::*;
22pub use self::fonts::glyph_ranges::*;
23pub use self::input::keyboard::*;
24pub use self::input::mouse::*;
25pub use self::input_widget::*;
26pub use self::io::*;
27pub use self::layout::*;
28pub use self::list_clipper::ListClipper;
29pub use self::plothistogram::PlotHistogram;
30pub use self::plotlines::PlotLines;
31pub use self::popups::*;
32pub use self::render::draw_data::*;
33pub use self::render::renderer::*;
34pub use self::stacks::*;
35pub use self::string::*;
36pub use self::style::*;
37
38#[cfg(feature = "tables-api")]
39pub use self::tables::*;
40pub use self::utils::*;
41pub use self::widget::color_editors::*;
42pub use self::widget::combo_box::*;
43pub use self::widget::drag::*;
44pub use self::widget::image::*;
45pub use self::widget::list_box::*;
46pub use self::widget::menu::*;
47pub use self::widget::misc::*;
48pub use self::widget::progress_bar::*;
49pub use self::widget::selectable::*;
50pub use self::widget::slider::*;
51pub use self::widget::tab::*;
52pub use self::widget::tree::*;
53pub use self::window::child_window::*;
54pub use self::window::*;
55use internal::RawCast;
56
57#[macro_use]
58mod string;
59
60#[macro_use]
61mod tokens;
62
63mod clipboard;
64pub mod color;
65mod columns;
66mod context;
67pub mod drag_drop;
68pub mod draw_list;
69mod fonts;
70mod input;
71mod input_widget;
72pub mod internal;
73mod io;
74mod layout;
75mod list_clipper;
76mod plothistogram;
77mod plotlines;
78mod popups;
79mod render;
80mod stacks;
81mod style;
82#[cfg(feature = "tables-api")]
83mod tables;
84#[cfg(test)]
85mod test;
86mod utils;
87mod widget;
88mod window;
89
90// Used by macros. Underscores are just to make it clear it's not part of the
91// public API.
92#[doc(hidden)]
93pub use core as __core;
94
95/// Returns the underlying Dear ImGui library version
96#[doc(alias = "GetVersion")]
97pub fn dear_imgui_version() -> &'static str {
98    unsafe {
99        let bytes = CStr::from_ptr(sys::igGetVersion()).to_bytes();
100        str::from_utf8_unchecked(bytes)
101    }
102}
103
104impl Context {
105    /// Returns the global imgui-rs time.
106    ///
107    /// Incremented by Io::delta_time every frame.
108    #[doc(alias = "GetTime")]
109    pub fn time(&self) -> f64 {
110        unsafe { sys::igGetTime() }
111    }
112    /// Returns the global imgui-rs frame count.
113    ///
114    /// Incremented by 1 every frame.
115    #[doc(alias = "GetFrameCount")]
116    pub fn frame_count(&self) -> i32 {
117        unsafe { sys::igGetFrameCount() }
118    }
119}
120
121/// A temporary reference for building the user interface for one frame
122#[derive(Debug)]
123pub struct Ui<'ui> {
124    ctx: &'ui Context,
125    font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
126    // imgui isn't mutli-threaded -- so no one will ever access twice.
127    buffer: cell::UnsafeCell<string::UiBuffer>,
128}
129
130impl<'ui> Ui<'ui> {
131    /// Creates an Ui object with a given context
132    pub fn from_ctx(ctx: &'ui Context) -> Self {
133        Self {
134            ctx,
135            font_atlas: None,
136            buffer: crate::UiBuffer::new(1024).into(),
137        }
138    }
139
140    /// Internal method to push a single text to our scratch buffer.
141    fn scratch_txt(&self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
142        unsafe {
143            let handle = &mut *self.buffer.get();
144            handle.scratch_txt(txt)
145        }
146    }
147
148    /// Internal method to push an option text to our scratch buffer.
149    fn scratch_txt_opt(&self, txt: Option<impl AsRef<str>>) -> *const sys::cty::c_char {
150        unsafe {
151            let handle = &mut *self.buffer.get();
152            handle.scratch_txt_opt(txt)
153        }
154    }
155
156    fn scratch_txt_two(
157        &self,
158        txt_0: impl AsRef<str>,
159        txt_1: impl AsRef<str>,
160    ) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
161        unsafe {
162            let handle = &mut *self.buffer.get();
163            handle.scratch_txt_two(txt_0, txt_1)
164        }
165    }
166
167    fn scratch_txt_with_opt(
168        &self,
169        txt_0: impl AsRef<str>,
170        txt_1: Option<impl AsRef<str>>,
171    ) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
172        unsafe {
173            let handle = &mut *self.buffer.get();
174            handle.scratch_txt_with_opt(txt_0, txt_1)
175        }
176    }
177
178    /// Returns an immutable reference to the inputs/outputs object
179    #[doc(alias = "GetIO")]
180    pub fn io(&self) -> &Io {
181        unsafe { &*(sys::igGetIO() as *const Io) }
182    }
183
184    /// Returns an immutable reference to the font atlas
185    pub fn fonts(&self) -> FontAtlasRef<'_> {
186        match self.font_atlas {
187            Some(ref font_atlas) => FontAtlasRef::Shared(font_atlas),
188            None => unsafe {
189                let fonts = &*(self.io().fonts as *const FontAtlas);
190                FontAtlasRef::Owned(fonts)
191            },
192        }
193    }
194    /// Returns a clone of the user interface style
195    pub fn clone_style(&self) -> Style {
196        *self.ctx.style()
197    }
198    /// Renders the frame and returns a reference to the resulting draw data
199    #[doc(alias = "Render", alias = "GetDrawData")]
200    pub fn render(self) -> &'ui DrawData {
201        unsafe {
202            sys::igRender();
203            &*(sys::igGetDrawData() as *mut DrawData)
204        }
205    }
206}
207
208impl<'a> Drop for Ui<'a> {
209    #[doc(alias = "EndFrame")]
210    fn drop(&mut self) {
211        if !thread::panicking() {
212            unsafe {
213                sys::igEndFrame();
214            }
215        }
216    }
217}
218
219/// # Demo, debug, information
220impl<'ui> Ui<'ui> {
221    /// Renders a demo window (previously called a test window), which demonstrates most
222    /// Dear Imgui features.
223    #[doc(alias = "ShowDemoWindow")]
224    pub fn show_demo_window(&self, opened: &mut bool) {
225        unsafe {
226            sys::igShowDemoWindow(opened);
227        }
228    }
229    /// Renders an about window.
230    ///
231    /// Displays the Dear ImGui version/credits, and build/system information.
232    #[doc(alias = "ShowAboutWindow")]
233    pub fn show_about_window(&self, opened: &mut bool) {
234        unsafe {
235            sys::igShowAboutWindow(opened);
236        }
237    }
238    /// Renders a metrics/debug window.
239    ///
240    /// Displays Dear ImGui internals: draw commands (with individual draw calls and vertices),
241    /// window list, basic internal state, etc.
242    #[doc(alias = "ShowMetricsWindow")]
243    pub fn show_metrics_window(&self, opened: &mut bool) {
244        unsafe {
245            sys::igShowMetricsWindow(opened);
246        }
247    }
248    /// Renders a style editor block (not a window) for the given `Style` structure
249    #[doc(alias = "ShowStyleEditor")]
250    pub fn show_style_editor(&self, style: &mut Style) {
251        unsafe {
252            sys::igShowStyleEditor(style.raw_mut());
253        }
254    }
255    /// Renders a style editor block (not a window) for the currently active style
256    #[doc(alias = "ShowStyleEditor")]
257    pub fn show_default_style_editor(&self) {
258        unsafe { sys::igShowStyleEditor(ptr::null_mut()) };
259    }
260    /// Renders a basic help/info block (not a window)
261    #[doc(alias = "ShowUserGuide")]
262    pub fn show_user_guide(&self) {
263        unsafe { sys::igShowUserGuide() };
264    }
265}
266
267/// Unique ID used by widgets
268#[derive(Copy, Clone, Debug, Eq, PartialEq)]
269pub enum Id<'a> {
270    Int(i32),
271    Str(&'a str),
272    Ptr(*const c_void),
273}
274
275impl From<i32> for Id<'static> {
276    #[inline]
277    fn from(i: i32) -> Self {
278        Id::Int(i)
279    }
280}
281
282impl<'a, T: ?Sized + AsRef<str>> From<&'a T> for Id<'a> {
283    #[inline]
284    fn from(s: &'a T) -> Self {
285        Id::Str(s.as_ref())
286    }
287}
288
289impl<T> From<*const T> for Id<'static> {
290    #[inline]
291    fn from(p: *const T) -> Self {
292        Id::Ptr(p as *const c_void)
293    }
294}
295
296impl<T> From<*mut T> for Id<'static> {
297    #[inline]
298    fn from(p: *mut T) -> Self {
299        Id::Ptr(p as *const T as *const c_void)
300    }
301}
302
303impl<'a> Id<'a> {
304    // this is used in the tables-api and possibly elsewhere,
305    // but not with just default features...
306    #[allow(dead_code)]
307    fn as_imgui_id(&self) -> sys::ImGuiID {
308        unsafe {
309            match self {
310                Id::Ptr(p) => sys::igGetID_Ptr(*p),
311                Id::Str(s) => {
312                    let s1 = s.as_ptr() as *const std::os::raw::c_char;
313                    let s2 = s1.add(s.len());
314                    sys::igGetID_StrStr(s1, s2)
315                }
316                Id::Int(i) => {
317                    let p = *i as *const std::os::raw::c_void;
318                    sys::igGetID_Ptr(p)
319                } // Id::ImGuiID(n) => *n,
320            }
321        }
322    }
323}
324
325impl<'a> Default for Id<'a> {
326    fn default() -> Self {
327        Self::Int(0)
328    }
329}
330
331// Widgets: Input
332impl<'ui> Ui<'ui> {
333    #[doc(alias = "InputText", alias = "InputTextWithHint")]
334    pub fn input_text<'p, L: AsRef<str>>(
335        &'ui self,
336        label: L,
337        buf: &'p mut String,
338    ) -> InputText<'ui, 'p, L> {
339        InputText::new(self, label, buf)
340    }
341    #[doc(alias = "InputText", alias = "InputTextMultiline")]
342    pub fn input_text_multiline<'p, L: AsRef<str>>(
343        &'ui self,
344        label: L,
345        buf: &'p mut String,
346        size: [f32; 2],
347    ) -> InputTextMultiline<'ui, 'p, L> {
348        InputTextMultiline::new(self, label, buf, size)
349    }
350    #[doc(alias = "InputFloat2")]
351    pub fn input_float<'p, L: AsRef<str>>(
352        &'ui self,
353        label: L,
354        value: &'p mut f32,
355    ) -> InputFloat<'ui, 'p, L> {
356        InputFloat::new(self, label, value)
357    }
358    pub fn input_float2<'p, L: AsRef<str>>(
359        &'ui self,
360        label: L,
361        value: &'p mut [f32; 2],
362    ) -> InputFloat2<'ui, 'p, L> {
363        InputFloat2::new(self, label, value)
364    }
365    #[doc(alias = "InputFloat3")]
366    pub fn input_float3<'p, L: AsRef<str>>(
367        &'ui self,
368        label: L,
369        value: &'p mut [f32; 3],
370    ) -> InputFloat3<'ui, 'p, L> {
371        InputFloat3::new(self, label, value)
372    }
373    #[doc(alias = "InputFloat4")]
374    pub fn input_float4<'p, L: AsRef<str>>(
375        &'ui self,
376        label: L,
377        value: &'p mut [f32; 4],
378    ) -> InputFloat4<'ui, 'p, L> {
379        InputFloat4::new(self, label, value)
380    }
381    #[doc(alias = "InputInt")]
382    pub fn input_int<'p, L: AsRef<str>>(
383        &'ui self,
384        label: L,
385        value: &'p mut i32,
386    ) -> InputInt<'ui, 'p, L> {
387        InputInt::new(self, label, value)
388    }
389    #[doc(alias = "InputInt2")]
390    pub fn input_int2<'p, L: AsRef<str>>(
391        &'ui self,
392        label: L,
393        value: &'p mut [i32; 2],
394    ) -> InputInt2<'ui, 'p, L> {
395        InputInt2::new(self, label, value)
396    }
397    #[doc(alias = "InputInt3")]
398    pub fn input_int3<'p, L: AsRef<str>>(
399        &'ui self,
400        label: L,
401        value: &'p mut [i32; 3],
402    ) -> InputInt3<'ui, 'p, L> {
403        InputInt3::new(self, label, value)
404    }
405    #[doc(alias = "InputInt4")]
406    pub fn input_int4<'p, L: AsRef<str>>(
407        &'ui self,
408        label: L,
409        value: &'p mut [i32; 4],
410    ) -> InputInt4<'ui, 'p, L> {
411        InputInt4::new(self, label, value)
412    }
413}
414
415create_token!(
416    /// Tracks a layout tooltip that can be ended by calling `.end()` or by dropping.
417    pub struct TooltipToken<'ui>;
418
419    /// Drops the layout tooltip manually. You can also just allow this token
420    /// to drop on its own.
421    drop { sys::igEndTooltip() }
422);
423
424/// # Tooltips
425impl<'ui> Ui<'ui> {
426    /// Construct a tooltip window that can have any kind of content.
427    ///
428    /// Typically used with `Ui::is_item_hovered()` or some other conditional check.
429    ///
430    /// # Examples
431    ///
432    /// ```
433    /// # use arcdps_imgui::*;
434    /// fn user_interface(ui: &Ui) {
435    ///     ui.text("Hover over me");
436    ///     if ui.is_item_hovered() {
437    ///         ui.tooltip(|| {
438    ///             ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!("I'm red!"));
439    ///         });
440    ///     }
441    /// }
442    /// ```
443    #[doc(alias = "BeginTooltip", alias = "EndTootip")]
444    pub fn tooltip<F: FnOnce()>(&self, f: F) {
445        unsafe { sys::igBeginTooltip() };
446        f();
447        unsafe { sys::igEndTooltip() };
448    }
449    /// Construct a tooltip window that can have any kind of content.
450    ///
451    /// Returns a `TooltipToken` that must be ended by calling `.end()`
452    #[doc(alias = "BeginTooltip")]
453    pub fn begin_tooltip(&self) -> TooltipToken<'_> {
454        unsafe { sys::igBeginTooltip() };
455        TooltipToken::new(self)
456    }
457    /// Construct a tooltip window with simple text content.
458    ///
459    /// Typically used with `Ui::is_item_hovered()` or some other conditional check.
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// # use arcdps_imgui::*;
465    /// fn user_interface(ui: &Ui) {
466    ///     ui.text("Hover over me");
467    ///     if ui.is_item_hovered() {
468    ///         ui.tooltip_text("I'm a tooltip!");
469    ///     }
470    /// }
471    /// ```
472    #[doc(alias = "BeginTooltip", alias = "EndTootip")]
473    pub fn tooltip_text<T: AsRef<str>>(&self, text: T) {
474        self.tooltip(|| self.text(text));
475    }
476}
477
478// Widgets: ListBox
479impl<'ui> Ui<'ui> {
480    #[doc(alias = "ListBox")]
481    pub fn list_box<'p, StringType: AsRef<str> + ?Sized>(
482        &self,
483        label: impl AsRef<str>,
484        current_item: &mut i32,
485        items: &'p [&'p StringType],
486        height_in_items: i32,
487    ) -> bool {
488        let (label_ptr, items_inner) = unsafe {
489            let handle = &mut *self.buffer.get();
490
491            handle.refresh_buffer();
492            let label_ptr = handle.push(label);
493
494            let items_inner: Vec<_> = items.iter().map(|&v| handle.push(v)).collect();
495
496            (label_ptr, items_inner)
497        };
498
499        unsafe {
500            sys::igListBox_Str_arr(
501                label_ptr,
502                current_item,
503                items_inner.as_ptr() as *mut *const c_char,
504                items_inner.len() as i32,
505                height_in_items,
506            )
507        }
508    }
509
510    // written out for the future times...
511    // #[doc(alias = "ListBox")]
512    // pub fn list_box_const<'p, StringType: AsRef<str> + ?Sized, const N: usize>(
513    //     &self,
514    //     label: impl AsRef<str>,
515    //     current_item: &mut i32,
516    //     items: [&'p StringType; N],
517    //     height_in_items: i32,
518    // ) -> bool {
519    //     let (label_ptr, items_inner) = unsafe {
520    //         let handle = &mut *self.buffer.get();
521
522    //         handle.refresh_buffer();
523    //         let label_ptr = handle.push(label);
524
525    //         let mut items_inner: [*const i8; N] = [std::ptr::null(); N];
526
527    //         for (i, item) in items.iter().enumerate() {
528    //             items_inner[i] = handle.push(item);
529    //         }
530
531    //         (label_ptr, items_inner)
532    //     };
533
534    //     unsafe {
535    //         sys::igListBoxStr_arr(
536    //             label_ptr,
537    //             current_item,
538    //             items_inner.as_ptr() as *mut *const c_char,
539    //             items_inner.len() as i32,
540    //             height_in_items,
541    //         )
542    //     }
543    // }
544}
545
546impl<'ui> Ui<'ui> {
547    #[doc(alias = "PlotLines")]
548    pub fn plot_lines<'p, Label: AsRef<str>>(
549        &'ui self,
550        label: Label,
551        values: &'p [f32],
552    ) -> PlotLines<'ui, 'p, Label> {
553        PlotLines::new(self, label, values)
554    }
555}
556
557impl<'ui> Ui<'ui> {
558    #[doc(alias = "PlotHistogram")]
559    pub fn plot_histogram<'p, Label: AsRef<str>>(
560        &'ui self,
561        label: Label,
562        values: &'p [f32],
563    ) -> PlotHistogram<'ui, 'p, Label> {
564        PlotHistogram::new(self, label, values)
565    }
566}
567
568impl<'ui> Ui<'ui> {
569    /// Calculate the size required for a given text string.
570    ///
571    /// This is the same as [calc_text_size_with_opts](Self::calc_text_size_with_opts)
572    /// with `hide_text_after_double_hash` set to false and `wrap_width` set to `-1.0`.
573    #[doc(alias = "CalcTextSize")]
574    pub fn calc_text_size<T: AsRef<str>>(&self, text: T) -> [f32; 2] {
575        self.calc_text_size_with_opts(text, false, -1.0)
576    }
577
578    /// Calculate the size required for a given text string.
579    ///
580    /// hide_text_after_double_hash allows the user to insert comments into their text, using a double hash-tag prefix.
581    /// This is a feature of imgui.
582    ///
583    /// wrap_width allows you to request a width at which to wrap the text to a newline for the calculation.
584    #[doc(alias = "CalcTextSize")]
585    pub fn calc_text_size_with_opts<T: AsRef<str>>(
586        &self,
587        text: T,
588        hide_text_after_double_hash: bool,
589        wrap_width: f32,
590    ) -> [f32; 2] {
591        let mut out = sys::ImVec2::zero();
592        let text = text.as_ref();
593
594        unsafe {
595            let start = text.as_ptr();
596            let end = start.add(text.len());
597
598            sys::igCalcTextSize(
599                &mut out,
600                start as *const c_char,
601                end as *const c_char,
602                hide_text_after_double_hash,
603                wrap_width,
604            )
605        };
606        out.into()
607    }
608}
609
610/// # Draw list for custom drawing
611impl<'ui> Ui<'ui> {
612    /// Get access to drawing API
613    ///
614    /// # Examples
615    ///
616    /// ```rust,no_run
617    /// # use arcdps_imgui::*;
618    /// fn custom_draw(ui: &Ui) {
619    ///     let draw_list = ui.get_window_draw_list();
620    ///     // Draw a line
621    ///     const WHITE: [f32; 3] = [1.0, 1.0, 1.0];
622    ///     draw_list.add_line([100.0, 100.0], [200.0, 200.0], WHITE).build();
623    ///     // Continue drawing ...
624    /// }
625    /// ```
626    ///
627    /// This function will panic if several instances of [`DrawListMut`]
628    /// coexist. Before a new instance is got, a previous instance should be
629    /// dropped.
630    ///
631    /// ```rust
632    /// # use arcdps_imgui::*;
633    /// fn custom_draw(ui: &Ui) {
634    ///     let draw_list = ui.get_window_draw_list();
635    ///     // Draw something...
636    ///
637    ///     // This second call will panic!
638    ///     let draw_list = ui.get_window_draw_list();
639    /// }
640    /// ```
641    #[must_use]
642    #[doc(alias = "GetWindowDrawList")]
643    pub fn get_window_draw_list(&'ui self) -> DrawListMut<'ui> {
644        DrawListMut::window(self)
645    }
646
647    #[must_use]
648    #[doc(alias = "GetBackgroundDrawList")]
649    pub fn get_background_draw_list(&'ui self) -> DrawListMut<'ui> {
650        DrawListMut::background(self)
651    }
652
653    #[must_use]
654    #[doc(alias = "GetForegroundDrawList")]
655    pub fn get_foreground_draw_list(&'ui self) -> DrawListMut<'ui> {
656        DrawListMut::foreground(self)
657    }
658}
659
660/// Condition for applying a setting
661#[repr(i8)]
662#[derive(Copy, Clone, Debug, Eq, PartialEq)]
663pub enum Condition {
664    /// Never apply the setting
665    Never = -1,
666    /// Always apply the setting
667    Always = sys::ImGuiCond_Always as i8,
668    /// Apply the setting once per runtime session (only the first call will succeed)
669    Once = sys::ImGuiCond_Once as i8,
670    /// Apply the setting if the object/window has no persistently saved data (no entry in .ini
671    /// file)
672    FirstUseEver = sys::ImGuiCond_FirstUseEver as i8,
673    /// Apply the setting if the object/window is appearing after being hidden/inactive (or the
674    /// first time)
675    Appearing = sys::ImGuiCond_Appearing as i8,
676}
677
678/// A cardinal direction
679#[repr(i32)]
680#[derive(Copy, Clone, Debug, Eq, PartialEq)]
681pub enum Direction {
682    None = sys::ImGuiDir_None,
683    Left = sys::ImGuiDir_Left,
684    Right = sys::ImGuiDir_Right,
685    Up = sys::ImGuiDir_Up,
686    Down = sys::ImGuiDir_Down,
687}