dear-imgui-rs 0.14.0

High-level Rust bindings to Dear ImGui v1.92.7 with docking, WGPU/GL backends, and extensions (ImPlot/ImPlot3D, ImNodes, ImGuizmo, file browser, reflection-based UI)
Documentation
use super::id::validate_font_id_for_atlas;
use super::validation::RASTERIZER_MULTIPLY_MAX;
use super::*;

#[test]
fn font_config_glyph_exclude_ranges_converts_and_terminates() {
    let cfg = FontConfig::new().glyph_exclude_ranges(&[(0x41, 0x5a)]);
    assert!(!cfg.raw.GlyphExcludeRanges.is_null());
    unsafe {
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(0), 0x41 as sys::ImWchar);
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(1), 0x5a as sys::ImWchar);
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(2), 0);
    }
}

#[test]
fn font_config_glyph_exclude_ranges_accepts_non_bmp_when_wchar32() {
    if std::mem::size_of::<sys::ImWchar>() != 4 {
        return;
    }
    let cfg = FontConfig::new().glyph_exclude_ranges(&[(0x1_0000, 0x1_0001)]);
    assert!(!cfg.raw.GlyphExcludeRanges.is_null());
    unsafe {
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(0), 0x1_0000 as sys::ImWchar);
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(1), 0x1_0001 as sys::ImWchar);
        assert_eq!(*cfg.raw.GlyphExcludeRanges.add(2), 0);
    }
}

#[test]
fn font_config_glyph_exclude_ranges_rejects_out_of_range() {
    let out_of_range = if std::mem::size_of::<sys::ImWchar>() == 2 {
        0x1_0000
    } else {
        0x11_0000
    };
    let res = std::panic::catch_unwind(|| {
        let _ = FontConfig::new().glyph_exclude_ranges(&[(out_of_range, out_of_range)]);
    });
    assert!(res.is_err());
}

#[test]
fn font_config_glyph_exclude_ranges_rejects_reversed_ranges() {
    let res = std::panic::catch_unwind(|| {
        let _ = FontConfig::new().glyph_exclude_ranges(&[(0x42, 0x41)]);
    });
    assert!(res.is_err());
}

#[test]
fn font_config_rejects_invalid_numeric_inputs() {
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().size_pixels(f32::NAN);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().size_pixels(-1.0);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().glyph_offset([0.0, f32::INFINITY]);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().glyph_min_advance_x(-1.0);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new()
                .glyph_min_advance_x(12.0)
                .glyph_max_advance_x(8.0);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().glyph_extra_advance_x(f32::NAN);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().rasterizer_multiply(-0.1);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().rasterizer_multiply(RASTERIZER_MULTIPLY_MAX * 2.0);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().rasterizer_density(0.0);
        })
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(|| {
            let _ = FontConfig::new().oversample_h(-1);
        })
        .is_err()
    );

    let cfg = FontConfig::new()
        .size_pixels(0.0)
        .glyph_offset([0.0, 0.0])
        .glyph_min_advance_x(0.0)
        .glyph_max_advance_x(f32::MAX)
        .glyph_extra_advance_x(-1.0)
        .rasterizer_multiply(256.0)
        .rasterizer_density(1.0)
        .oversample_h(0)
        .oversample_v(1);
    assert_eq!(cfg.raw.SizePixels, 0.0);
    assert_eq!(cfg.raw.GlyphExtraAdvanceX, -1.0);
    assert_eq!(cfg.raw.RasterizerMultiply, 256.0);
}

#[test]
fn discard_bakes_checks_unused_frame_count_before_ffi() {
    let mut atlas = FontAtlas::new();

    atlas.discard_bakes(0);
    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            atlas.discard_bakes(i32::MAX as usize + 1);
        }))
        .is_err()
    );
}

#[test]
fn font_atlas_rejects_glyph_metric_overrides_without_reference_size() {
    let mut atlas = FontAtlas::new();
    let cfg = FontConfig::new().glyph_offset([1.0, 0.0]);

    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let _ = atlas.add_font_from_memory_ttf(&[0u8; 10], 0.0, Some(&cfg), None);
        }))
        .is_err()
    );

    assert!(
        atlas
            .add_font_from_memory_ttf(&[0u8; 10], 13.0, Some(&cfg), None)
            .is_none()
    );

    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let cfg = FontConfig::new()
                .font_data_owned_by_atlas(false)
                .glyph_min_advance_x(4.0);
            let _ = atlas.add_font_with_config(&cfg);
        }))
        .is_err()
    );
}

#[test]
fn add_font_with_config_rejects_missing_font_source_before_ffi() {
    let mut atlas = FontAtlas::new();
    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let _ = atlas.add_font_with_config(&FontConfig::new());
        }))
        .is_err()
    );
}

#[test]
fn add_font_from_memory_ttf_rejects_too_small_buffers() {
    let mut ctx = crate::Context::create();
    let mut fonts = ctx.font_atlas_mut();
    assert!(
        fonts
            .add_font_from_memory_ttf(&[0u8; 10], 13.0, None, None)
            .is_none()
    );
}

#[test]
fn font_id_is_invalidated_by_clear_fonts_before_push_font_ffi() {
    let mut ctx = crate::Context::create();
    let font_id = {
        let mut fonts = ctx.font_atlas_mut();
        fonts.add_font(&[FontSource::default_font()])
    };
    {
        let mut fonts = ctx.font_atlas_mut();
        fonts.clear_fonts();
    }

    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        let _ = validate_font_id_for_current_context(font_id, "test stale FontId");
    }));

    assert!(result.is_err());
}

#[test]
fn font_id_from_another_atlas_is_rejected_before_push_font_ffi() {
    let mut ctx_a = crate::Context::create();
    let font_id = {
        let mut fonts = ctx_a.font_atlas_mut();
        fonts.add_font(&[FontSource::default_font()])
    };
    let suspended_a = ctx_a.suspend();

    let mut ctx_b = crate::Context::create();
    let _ = ctx_b.font_atlas_mut().build();
    ctx_b.io_mut().set_display_size([128.0, 128.0]);
    ctx_b.io_mut().set_delta_time(1.0 / 60.0);
    let ui = ctx_b.frame();

    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        let _token = ui.push_font(font_id);
    }));

    assert!(result.is_err());

    drop(ctx_b);
    drop(suspended_a);
}

#[test]
fn font_id_from_shared_atlas_is_valid_through_another_atlas_view() {
    let shared_atlas = SharedFontAtlas::create();
    let raw = *shared_atlas.0;
    let font_id = {
        let mut atlas = unsafe { FontAtlas::from_raw(raw) };
        atlas.add_font(&[FontSource::default_font()])
    };

    let _ = validate_font_id_for_atlas(font_id, raw, "test shared FontId");
}

#[test]
fn font_sources_reject_invalid_sizes_before_ffi() {
    let mut atlas = FontAtlas::new();

    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let _ = atlas.add_font(&[FontSource::default_font_with_size(f32::NAN)]);
        }))
        .is_err()
    );
    assert!(
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let _ = atlas.add_font(&[FontSource::ttf_data_with_size(&[0u8; 10], -1.0)]);
        }))
        .is_err()
    );
}

#[test]
fn set_texture_id_preserves_managed_tex_data_reference() {
    let mut ctx = crate::Context::create();
    let mut fonts = ctx.font_atlas_mut();
    let _ = fonts.build();

    let raw_tex_data = fonts.get_tex_data();
    assert!(!raw_tex_data.is_null());

    let texture_id = crate::texture::TextureId::new(0x1234);
    fonts.set_texture_id(texture_id);

    let mut tex_ref = fonts.get_tex_ref();
    assert_eq!(tex_ref._TexData, raw_tex_data);

    let resolved = unsafe { sys::ImTextureRef_GetTexID(&mut tex_ref) };
    assert_eq!(resolved, texture_id.id() as sys::ImTextureID);
}