Refactor WindowDrawList into DrawListMut and fix #413.

- Rename WindowDrawList -> DrawListMut. It's not about window draw lists, but
  about background/foreground draw lists as well. The naming was not an easy
  choice, but seems like in rust it's a common convention to add a Mut suffix for
  mutable entities. Imgui-rs already has DrawList and it acts as an immutable
  reference type for rendering implementations to consume. Hence the name
  DrawListMut, which becomes a mutable reference to draw list with methods
  to modify it.
- Add Ui::get_foreground_draw_list(). Same as Ui::get_background_draw_list()
  but for foreground.
- Add draw_list example which shows the use of all three draw lists
  (window, bg, fg).
This commit is contained in:
nsf 2020-12-28 11:20:50 +05:00 committed by Thom Chiovoloni
parent 81e21f5853
commit 171d86aea1
3 changed files with 139 additions and 38 deletions

View File

@ -0,0 +1,90 @@
use imgui::*;
mod support;
// rect is [x, y, w, h]
fn draw_text_centered(
ui: &Ui,
draw_list: &DrawListMut,
rect: [f32; 4],
text: &ImStr,
color: [f32; 3],
) {
let text_size = ui.calc_text_size(text, false, 0.0);
let cx = (rect[2] - text_size[0]) / 2.0;
let cy = (rect[3] - text_size[1]) / 2.0;
draw_list.add_text([rect[0] + cx, rect[1] + cy], color, text);
}
fn main() {
let system = support::init(file!());
system.main_loop(move |_, ui| {
{
let bg_draw_list = ui.get_background_draw_list();
bg_draw_list
.add_circle([150.0, 150.0], 150.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&bg_draw_list,
[0.0, 0.0, 300.0, 300.0],
im_str!("background draw list"),
[0.0, 0.0, 0.0],
);
}
{
let [w, h] = ui.io().display_size;
let fg_draw_list = ui.get_foreground_draw_list();
fg_draw_list
.add_circle([w - 150.0, h - 150.0], 150.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&fg_draw_list,
[w - 300.0, h - 300.0, 300.0, 300.0],
im_str!("foreground draw list"),
[1.0, 0.0, 0.0],
);
}
Window::new(im_str!("Draw list"))
.size([300.0, 110.0], Condition::FirstUseEver)
.scroll_bar(false)
.build(ui, || {
ui.button(im_str!("random button"), [0.0, 0.0]);
let draw_list = ui.get_window_draw_list();
let o = ui.cursor_screen_pos();
let ws = ui.content_region_avail();
draw_list
.add_circle([o[0] + 10.0, o[1] + 10.0], 5.0, [1.0, 0.0, 0.0])
.thickness(4.0)
.build();
draw_list
.add_circle([o[0] + ws[0] - 10.0, o[1] + 10.0], 5.0, [0.0, 1.0, 0.0])
.thickness(4.0)
.build();
draw_list
.add_circle(
[o[0] + ws[0] - 10.0, o[1] + ws[1] - 10.0],
5.0,
[0.0, 0.0, 1.0],
)
.thickness(4.0)
.build();
draw_list
.add_circle([o[0] + 10.0, o[1] + ws[1] - 10.0], 5.0, [1.0, 1.0, 0.0])
.thickness(4.0)
.build();
draw_text_centered(
ui,
&draw_list,
[o[0], o[1], ws[0], ws[1]],
im_str!("window draw list"),
[1.0, 1.0, 1.0],
);
});
});
}

View File

@ -53,26 +53,25 @@ impl From<(f32, f32, f32)> for ImColor {
/// Object implementing the custom draw API. /// Object implementing the custom draw API.
/// ///
/// Called from [`Ui::get_window_draw_list`]. No more than one instance of this /// Called from [`Ui::get_window_draw_list`], [`Ui::get_background_draw_list`] or [`Ui::get_foreground_draw_list`].
/// structure can live in a program at the same time. /// No more than one instance of this structure can live in a program at the same time.
/// The program will panic on creating a second instance. /// The program will panic on creating a second instance.
pub struct WindowDrawList<'ui> { pub struct DrawListMut<'ui> {
draw_list: *mut ImDrawList, draw_list: *mut ImDrawList,
_phantom: PhantomData<&'ui Ui<'ui>>, _phantom: PhantomData<&'ui Ui<'ui>>,
} }
static WINDOW_DRAW_LIST_LOADED: std::sync::atomic::AtomicBool = static DRAW_LIST_LOADED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
std::sync::atomic::AtomicBool::new(false);
impl<'ui> Drop for WindowDrawList<'ui> { impl<'ui> Drop for DrawListMut<'ui> {
fn drop(&mut self) { fn drop(&mut self) {
WINDOW_DRAW_LIST_LOADED.store(false, std::sync::atomic::Ordering::Release); DRAW_LIST_LOADED.store(false, std::sync::atomic::Ordering::Release);
} }
} }
impl<'ui> WindowDrawList<'ui> { impl<'ui> DrawListMut<'ui> {
pub(crate) fn new(_: &Ui<'ui>) -> Self { fn lock_draw_list() {
let already_loaded = WINDOW_DRAW_LIST_LOADED let already_loaded = DRAW_LIST_LOADED
.compare_exchange( .compare_exchange(
false, false,
true, true,
@ -81,21 +80,34 @@ impl<'ui> WindowDrawList<'ui> {
) )
.is_err(); .is_err();
if already_loaded { if already_loaded {
panic!("WindowDrawList is already loaded! You can only load one instance of it!") panic!("DrawListMut is already loaded! You can only load one instance of it!")
} }
}
pub(crate) fn window(_: &Ui<'ui>) -> Self {
Self::lock_draw_list();
Self { Self {
draw_list: unsafe { sys::igGetWindowDrawList() }, draw_list: unsafe { sys::igGetWindowDrawList() },
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
pub(crate) fn background(self) -> Self { pub(crate) fn background(_: &Ui<'ui>) -> Self {
Self::lock_draw_list();
Self { Self {
draw_list: unsafe { sys::igGetBackgroundDrawList() }, draw_list: unsafe { sys::igGetBackgroundDrawList() },
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
pub(crate) fn foreground(_: &Ui<'ui>) -> Self {
Self::lock_draw_list();
Self {
draw_list: unsafe { sys::igGetForegroundDrawList() },
_phantom: PhantomData,
}
}
/// Split into *channels_count* drawing channels. /// Split into *channels_count* drawing channels.
/// At the end of the closure, the channels are merged. The objects /// At the end of the closure, the channels are merged. The objects
/// are then drawn in the increasing order of their channel number, and not /// are then drawn in the increasing order of their channel number, and not
@ -127,9 +139,9 @@ impl<'ui> WindowDrawList<'ui> {
/// Represent the drawing interface within a call to [`channels_split`]. /// Represent the drawing interface within a call to [`channels_split`].
/// ///
/// [`channels_split`]: WindowDrawList::channels_split /// [`channels_split`]: DrawListMut::channels_split
pub struct ChannelsSplit<'ui> { pub struct ChannelsSplit<'ui> {
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
channels_count: u32, channels_count: u32,
} }
@ -151,7 +163,7 @@ impl<'ui> ChannelsSplit<'ui> {
} }
/// Drawing functions /// Drawing functions
impl<'ui> WindowDrawList<'ui> { impl<'ui> DrawListMut<'ui> {
/// Returns a line from point `p1` to `p2` with color `c`. /// Returns a line from point `p1` to `p2` with color `c`.
pub fn add_line<C>(&'ui self, p1: [f32; 2], p2: [f32; 2], c: C) -> Line<'ui> pub fn add_line<C>(&'ui self, p1: [f32; 2], p2: [f32; 2], c: C) -> Line<'ui>
where where
@ -291,11 +303,11 @@ pub struct Line<'ui> {
p2: [f32; 2], p2: [f32; 2],
color: ImColor, color: ImColor,
thickness: f32, thickness: f32,
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
} }
impl<'ui> Line<'ui> { impl<'ui> Line<'ui> {
fn new<C>(draw_list: &'ui WindowDrawList, p1: [f32; 2], p2: [f32; 2], c: C) -> Self fn new<C>(draw_list: &'ui DrawListMut, p1: [f32; 2], p2: [f32; 2], c: C) -> Self
where where
C: Into<ImColor>, C: Into<ImColor>,
{ {
@ -338,11 +350,11 @@ pub struct Rect<'ui> {
flags: ImDrawCornerFlags, flags: ImDrawCornerFlags,
thickness: f32, thickness: f32,
filled: bool, filled: bool,
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
} }
impl<'ui> Rect<'ui> { impl<'ui> Rect<'ui> {
fn new<C>(draw_list: &'ui WindowDrawList, p1: [f32; 2], p2: [f32; 2], c: C) -> Self fn new<C>(draw_list: &'ui DrawListMut, p1: [f32; 2], p2: [f32; 2], c: C) -> Self
where where
C: Into<ImColor>, C: Into<ImColor>,
{ {
@ -439,17 +451,11 @@ pub struct Triangle<'ui> {
color: ImColor, color: ImColor,
thickness: f32, thickness: f32,
filled: bool, filled: bool,
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
} }
impl<'ui> Triangle<'ui> { impl<'ui> Triangle<'ui> {
fn new<C>( fn new<C>(draw_list: &'ui DrawListMut, p1: [f32; 2], p2: [f32; 2], p3: [f32; 2], c: C) -> Self
draw_list: &'ui WindowDrawList,
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
c: C,
) -> Self
where where
C: Into<ImColor>, C: Into<ImColor>,
{ {
@ -512,11 +518,11 @@ pub struct Circle<'ui> {
num_segments: u32, num_segments: u32,
thickness: f32, thickness: f32,
filled: bool, filled: bool,
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
} }
impl<'ui> Circle<'ui> { impl<'ui> Circle<'ui> {
pub fn new<C>(draw_list: &'ui WindowDrawList, center: [f32; 2], radius: f32, color: C) -> Self pub fn new<C>(draw_list: &'ui DrawListMut, center: [f32; 2], radius: f32, color: C) -> Self
where where
C: Into<ImColor>, C: Into<ImColor>,
{ {
@ -588,12 +594,12 @@ pub struct BezierCurve<'ui> {
thickness: f32, thickness: f32,
/// If num_segments is not set, the bezier curve is auto-tessalated. /// If num_segments is not set, the bezier curve is auto-tessalated.
num_segments: Option<u32>, num_segments: Option<u32>,
draw_list: &'ui WindowDrawList<'ui>, draw_list: &'ui DrawListMut<'ui>,
} }
impl<'ui> BezierCurve<'ui> { impl<'ui> BezierCurve<'ui> {
fn new<C>( fn new<C>(
draw_list: &'ui WindowDrawList, draw_list: &'ui DrawListMut,
pos0: [f32; 2], pos0: [f32; 2],
cp0: [f32; 2], cp0: [f32; 2],
cp1: [f32; 2], cp1: [f32; 2],

View File

@ -10,6 +10,7 @@ use std::thread;
pub use self::clipboard::*; pub use self::clipboard::*;
pub use self::context::*; pub use self::context::*;
pub use self::draw_list::{ChannelsSplit, DrawListMut, ImColor};
pub use self::fonts::atlas::*; pub use self::fonts::atlas::*;
pub use self::fonts::font::*; pub use self::fonts::font::*;
pub use self::fonts::glyph::*; pub use self::fonts::glyph::*;
@ -46,12 +47,12 @@ pub use self::widget::tab::*;
pub use self::widget::tree::*; pub use self::widget::tree::*;
pub use self::window::child_window::*; pub use self::window::child_window::*;
pub use self::window::*; pub use self::window::*;
pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList};
use internal::RawCast; use internal::RawCast;
mod clipboard; mod clipboard;
mod columns; mod columns;
mod context; mod context;
mod draw_list;
mod fonts; mod fonts;
mod input; mod input;
mod input_widget; mod input_widget;
@ -72,7 +73,6 @@ mod test;
mod utils; mod utils;
mod widget; mod widget;
mod window; mod window;
mod window_draw_list;
/// Returns the underlying Dear ImGui library version /// Returns the underlying Dear ImGui library version
pub fn dear_imgui_version() -> &'static str { pub fn dear_imgui_version() -> &'static str {
@ -470,7 +470,7 @@ impl<'ui> Ui<'ui> {
/// } /// }
/// ``` /// ```
/// ///
/// This function will panic if several instances of [`WindowDrawList`] /// This function will panic if several instances of [`DrawListMut`]
/// coexist. Before a new instance is got, a previous instance should be /// coexist. Before a new instance is got, a previous instance should be
/// dropped. /// dropped.
/// ///
@ -485,13 +485,18 @@ impl<'ui> Ui<'ui> {
/// } /// }
/// ``` /// ```
#[must_use] #[must_use]
pub fn get_window_draw_list(&'ui self) -> WindowDrawList<'ui> { pub fn get_window_draw_list(&'ui self) -> DrawListMut<'ui> {
WindowDrawList::new(self) DrawListMut::window(self)
} }
#[must_use] #[must_use]
pub fn get_background_draw_list(&'ui self) -> WindowDrawList<'ui> { pub fn get_background_draw_list(&'ui self) -> DrawListMut<'ui> {
WindowDrawList::new(self).background() DrawListMut::background(self)
}
#[must_use]
pub fn get_foreground_draw_list(&'ui self) -> DrawListMut<'ui> {
DrawListMut::foreground(self)
} }
} }