From 2fcfdd55f4cf6845851bff8461b0d0f6ed3cca3e Mon Sep 17 00:00:00 2001 From: Rodrigo Rivas Costa Date: Mon, 19 Dec 2022 23:58:39 +0100 Subject: [PATCH] Add callbacks to the draw_list. --- imgui/src/draw_list.rs | 52 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/imgui/src/draw_list.rs b/imgui/src/draw_list.rs index 2f7a097..3ca6eee 100644 --- a/imgui/src/draw_list.rs +++ b/imgui/src/draw_list.rs @@ -16,7 +16,7 @@ use bitflags::bitflags; use crate::{math::MintVec2, ImColor32}; -use sys::ImDrawList; +use sys::{ImDrawCmd, ImDrawList}; use super::Ui; use crate::render::renderer::TextureId; @@ -468,6 +468,14 @@ impl<'ui> DrawListMut<'ui> { ) -> ImageRounded<'_> { ImageRounded::new(self, texture_id, p_min, p_max, rounding) } + + /// Draw the specified callback. + /// + /// Note: if this DrawList is never rendered the callback will leak because DearImGui + /// does not provide a method to clean registered callbacks. + pub fn add_callback(&'ui self, callback: F) -> Callback<'ui, F> { + Callback::new(self, callback) + } } /// Represents a line about to be drawn @@ -1186,3 +1194,45 @@ impl<'ui> ImageRounded<'ui> { } } } + +#[must_use = "should call .build() to draw the object"] +pub struct Callback<'ui, F> { + draw_list: &'ui DrawListMut<'ui>, + callback: F, +} + +impl<'ui, F: FnOnce() + 'static> Callback<'ui, F> { + /// Typically constructed by [`DrawListMut::add_callback`] + pub fn new(draw_list: &'ui DrawListMut<'_>, callback: F) -> Self { + Callback { + draw_list, + callback, + } + } + /// Adds the callback to the draw-list so it will be run when the window is drawn + pub fn build(self) { + use std::os::raw::c_void; + // F is Sized, so *mut F must be a thin pointer. + let callback: *mut F = Box::into_raw(Box::new(self.callback)); + + unsafe { + sys::ImDrawList_AddCallback( + self.draw_list.draw_list, + Some(Self::run_callback), + callback as *mut c_void, + ); + } + } + unsafe extern "C" fn run_callback(_parent_list: *const ImDrawList, cmd: *const ImDrawCmd) { + // We are modifying through a C const pointer, but that should be harmless. + let cmd = &mut *(cmd as *mut ImDrawCmd); + // Consume the pointer and leave a NULL behind to avoid a double-free or + // calling twice an FnOnce. It should not happen, but better safe than sorry. + let callback = std::mem::replace(&mut cmd.UserCallbackData, std::ptr::null_mut()); + if callback.is_null() { + return; + } + let callback = Box::from_raw(callback as *mut F); + callback(); + } +}