Skip to content

Commit

Permalink
Add callbacks to the draw_list.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigorc committed Jan 15, 2023
1 parent 90a4df3 commit 7c9cd5d
Showing 1 changed file with 49 additions and 1 deletion.
50 changes: 49 additions & 1 deletion imgui/src/draw_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -468,6 +468,11 @@ impl<'ui> DrawListMut<'ui> {
) -> ImageRounded<'_> {
ImageRounded::new(self, texture_id, p_min, p_max, rounding)
}

/// Draw the specified callback
pub fn add_callback<F: FnOnce() + 'static>(&'ui self, callback: F) -> Callback<'ui, F> {
Callback::new(self, callback)
}
}

/// Represents a line about to be drawn
Expand Down Expand Up @@ -1186,3 +1191,46 @@ 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,
}
}
/// Call the callback 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));

// Note that if the callback is never run the box will leak.
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();
}
}

0 comments on commit 7c9cd5d

Please sign in to comment.