dear-imgui-rs 0.18.0

High-level Rust bindings to Dear ImGui v1.92.9b with docking, WGPU/GL backends, and extensions (ImPlot/ImPlot3D, ImNodes, ImGuizmo, file browser, reflection-based UI)
Documentation
use dear_imgui_rs as imgui;
use std::sync::{Mutex, OnceLock};

fn test_guard() -> std::sync::MutexGuard<'static, ()> {
    static GUARD: OnceLock<Mutex<()>> = OnceLock::new();
    GUARD.get_or_init(|| Mutex::new(())).lock().unwrap()
}

fn first_unknown_bit(known_bits: i32) -> i32 {
    (0..31)
        .map(|shift| 1_i32 << shift)
        .find(|candidate| known_bits & candidate == 0)
        .expect("test requires at least one spare positive flag bit")
}

#[test]
fn table_options_reject_non_independent_bits_before_ffi() {
    let _guard = test_guard();

    let mut ctx = imgui::Context::create();
    {
        let io = ctx.io_mut();
        io.set_display_size([800.0, 600.0]);
        io.set_delta_time(1.0 / 60.0);
    }
    ctx.font_atlas()
        .try_claim_legacy_renderer()
        .expect("legacy renderer font atlas should be available")
        .build();
    let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);

    let ui = ctx.frame();
    let sizing_in_flags =
        imgui::TableFlags::from_bits_retain(imgui::sys::ImGuiTableFlags_SizingFixedFit);

    let _ = ui.window("Table option flags").build(|| {
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                let _ = ui.begin_table_with_flags("sizing in flags", 1, sizing_in_flags);
            }))
            .is_err()
        );
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                let _ = ui.begin_table_with_sizing(
                    "negative inner width",
                    1,
                    imgui::TableFlags::SCROLL_X,
                    [100.0, 100.0],
                    -1.0,
                );
            }))
            .is_err()
        );

        let table = ui.begin_table_with_flags(
            "typed sizing",
            1,
            imgui::TableOptions::new().sizing_policy(imgui::TableSizingPolicy::FixedFit),
        );
        if let Some(table) = table {
            table.end();
        }
    });
}

#[test]
fn table_column_options_reject_non_independent_bits_before_ffi() {
    let _guard = test_guard();

    let mut ctx = imgui::Context::create();
    {
        let io = ctx.io_mut();
        io.set_display_size([800.0, 600.0]);
        io.set_delta_time(1.0 / 60.0);
    }
    ctx.font_atlas()
        .try_claim_legacy_renderer()
        .expect("legacy renderer font atlas should be available")
        .build();
    let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);

    let ui = ctx.frame();
    let width_in_flags =
        imgui::TableColumnFlags::from_bits_retain(imgui::sys::ImGuiTableColumnFlags_WidthFixed);
    let indent_in_flags =
        imgui::TableColumnFlags::from_bits_retain(imgui::sys::ImGuiTableColumnFlags_IndentDisable);
    let status_in_flags =
        imgui::TableColumnFlags::from_bits_retain(imgui::sys::ImGuiTableColumnFlags_IsEnabled);
    let private_in_flags = imgui::TableColumnFlags::from_bits_retain(
        imgui::sys::ImGuiTableColumnFlags_NoDirectResize_,
    );

    let _ = ui.window("Table column option flags").build(|| {
        let Some(_table) = ui.begin_table_with_flags(
            "columns",
            4,
            imgui::TableOptions::new().sizing_policy(imgui::TableSizingPolicy::FixedFit),
        ) else {
            return;
        };

        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                ui.table_setup_column("width in flags", width_in_flags, None);
            }))
            .is_err()
        );
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                ui.table_setup_column_with_indent("indent in flags", indent_in_flags, None, None);
            }))
            .is_err()
        );
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                ui.table_setup_column("status in flags", status_in_flags, None);
            }))
            .is_err()
        );
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                ui.table_setup_column("private in flags", private_in_flags, None);
            }))
            .is_err()
        );
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                ui.table_setup_column_fixed_width(
                    "nan width",
                    imgui::TableColumnFlags::NONE,
                    f32::NAN,
                );
            }))
            .is_err()
        );

        ui.table_setup_column_with_indent(
            "typed",
            imgui::TableColumnFlags::DISABLED
                | imgui::TableColumnFlags::DEFAULT_HIDE
                | imgui::TableColumnFlags::DEFAULT_SORT,
            Some(imgui::TableColumnWidth::Fixed(48.0)),
            Some(imgui::TableColumnIndent::Disable),
        );
    });
}

#[test]
fn table_column_flag_getter_preserves_unknown_raw_bits() {
    let _guard = test_guard();

    let mut ctx = imgui::Context::create();
    {
        let io = ctx.io_mut();
        io.set_display_size([800.0, 600.0]);
        io.set_delta_time(1.0 / 60.0);
    }
    ctx.font_atlas()
        .try_claim_legacy_renderer()
        .expect("legacy renderer font atlas should be available")
        .build();
    let _ = ctx.set_ini_filename::<std::path::PathBuf>(None);

    let ui = ctx.frame();
    let unknown = first_unknown_bit(imgui::TableColumnStateFlags::all().bits());

    let _ = ui.window("Table raw column flags").build(|| {
        let Some(_table) = ui.begin_table_with_flags(
            "columns",
            1,
            imgui::TableOptions::new().sizing_policy(imgui::TableSizingPolicy::FixedFit),
        ) else {
            return;
        };

        ui.table_setup_column("raw", imgui::TableColumnFlags::DEFAULT_SORT, None);
        ui.table_next_row();
        ui.table_set_column_index(imgui::TableColumnIndex::ZERO);

        unsafe {
            let table = imgui::sys::igGetCurrentTable();
            assert!(!table.is_null());
            let column = (*table).Columns.Data;
            assert!(!column.is_null());
            (*column).Flags |= unknown;
        }

        assert_eq!(
            ui.table_get_column_flags(imgui::TableColumnIndex::ZERO)
                .bits()
                & unknown,
            unknown
        );
    });
}