use sys;
use sys::{ImDrawList, ImU32};
use super::Ui;
use crate::legacy::ImDrawCornerFlags;
use std::marker::PhantomData;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct ImColor(ImU32);
impl From<ImColor> for ImU32 {
fn from(color: ImColor) -> Self {
color.0
}
}
impl From<ImU32> for ImColor {
fn from(color: ImU32) -> Self {
ImColor(color)
}
}
impl From<[f32; 4]> for ImColor {
fn from(v: [f32; 4]) -> Self {
ImColor(unsafe { sys::igColorConvertFloat4ToU32(v.into()) })
}
}
impl From<(f32, f32, f32, f32)> for ImColor {
fn from(v: (f32, f32, f32, f32)) -> Self {
ImColor(unsafe { sys::igColorConvertFloat4ToU32(v.into()) })
}
}
impl From<[f32; 3]> for ImColor {
fn from(v: [f32; 3]) -> Self {
[v[0], v[1], v[2], 1.0].into()
}
}
impl From<(f32, f32, f32)> for ImColor {
fn from(v: (f32, f32, f32)) -> Self {
[v.0, v.1, v.2, 1.0].into()
}
}
pub struct WindowDrawList<'ui> {
draw_list: *mut ImDrawList,
_phantom: PhantomData<&'ui Ui<'ui>>,
}
static WINDOW_DRAW_LIST_LOADED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
impl<'ui> Drop for WindowDrawList<'ui> {
fn drop(&mut self) {
WINDOW_DRAW_LIST_LOADED.store(false, std::sync::atomic::Ordering::SeqCst);
}
}
impl<'ui> WindowDrawList<'ui> {
pub(crate) fn new(_: &Ui<'ui>) -> Self {
if WINDOW_DRAW_LIST_LOADED.load(std::sync::atomic::Ordering::SeqCst) {
panic!("WindowDrawList is already loaded! You can only load one instance of it!")
}
WINDOW_DRAW_LIST_LOADED.store(true, std::sync::atomic::Ordering::SeqCst);
Self {
draw_list: unsafe { sys::igGetWindowDrawList() },
_phantom: PhantomData,
}
}
pub fn channels_split<F: FnOnce(&ChannelsSplit)>(&self, channels_count: u32, f: F) {
unsafe { sys::ImDrawList_ChannelsSplit(self.draw_list, channels_count as i32) };
f(&ChannelsSplit {
draw_list: self,
channels_count,
});
unsafe { sys::ImDrawList_ChannelsMerge(self.draw_list) };
}
}
pub struct ChannelsSplit<'ui> {
draw_list: &'ui WindowDrawList<'ui>,
channels_count: u32,
}
impl<'ui> ChannelsSplit<'ui> {
pub fn set_current(&self, channel_index: u32) {
assert!(
channel_index < self.channels_count,
"Channel cannot be set! Provided channel index ({}) is higher than channel count ({}).",
channel_index,
self.channels_count
);
unsafe {
sys::ImDrawList_ChannelsSetCurrent(self.draw_list.draw_list, channel_index as i32)
};
}
}
impl<'ui> WindowDrawList<'ui> {
pub fn add_line<C>(&'ui self, p1: [f32; 2], p2: [f32; 2], c: C) -> Line<'ui>
where
C: Into<ImColor>,
{
Line::new(self, p1, p2, c)
}
pub fn add_rect<C>(&'ui self, p1: [f32; 2], p2: [f32; 2], c: C) -> Rect<'ui>
where
C: Into<ImColor>,
{
Rect::new(self, p1, p2, c)
}
pub fn add_rect_filled_multicolor<C1, C2, C3, C4>(
&self,
p1: [f32; 2],
p2: [f32; 2],
col_upr_left: C1,
col_upr_right: C2,
col_bot_right: C3,
col_bot_left: C4,
) where
C1: Into<ImColor>,
C2: Into<ImColor>,
C3: Into<ImColor>,
C4: Into<ImColor>,
{
unsafe {
sys::ImDrawList_AddRectFilledMultiColor(
self.draw_list,
p1.into(),
p2.into(),
col_upr_left.into().into(),
col_upr_right.into().into(),
col_bot_right.into().into(),
col_bot_left.into().into(),
);
}
}
pub fn add_triangle<C>(
&'ui self,
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
c: C,
) -> Triangle<'ui>
where
C: Into<ImColor>,
{
Triangle::new(self, p1, p2, p3, c)
}
pub fn add_circle<C>(&'ui self, center: [f32; 2], radius: f32, color: C) -> Circle<'ui>
where
C: Into<ImColor>,
{
Circle::new(self, center, radius, color)
}
pub fn add_text<C, T>(&self, pos: [f32; 2], col: C, text: T)
where
C: Into<ImColor>,
T: AsRef<str>,
{
use std::os::raw::c_char;
let text = text.as_ref();
unsafe {
let start = text.as_ptr() as *const c_char;
let end = (start as usize + text.len()) as *const c_char;
sys::ImDrawList_AddText(self.draw_list, pos.into(), col.into().into(), start, end)
}
}
pub fn add_bezier_curve<C>(
&'ui self,
pos0: [f32; 2],
cp0: [f32; 2],
cp1: [f32; 2],
pos1: [f32; 2],
color: C,
) -> BezierCurve<'ui>
where
C: Into<ImColor>,
{
BezierCurve::new(self, pos0, cp0, cp1, pos1, color)
}
pub fn with_clip_rect<F>(&self, min: [f32; 2], max: [f32; 2], f: F)
where
F: FnOnce(),
{
unsafe { sys::ImDrawList_PushClipRect(self.draw_list, min.into(), max.into(), false) }
f();
unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
}
pub fn with_clip_rect_intersect<F>(&self, min: [f32; 2], max: [f32; 2], f: F)
where
F: FnOnce(),
{
unsafe { sys::ImDrawList_PushClipRect(self.draw_list, min.into(), max.into(), true) }
f();
unsafe { sys::ImDrawList_PopClipRect(self.draw_list) }
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Line<'ui> {
p1: [f32; 2],
p2: [f32; 2],
color: ImColor,
thickness: f32,
draw_list: &'ui WindowDrawList<'ui>,
}
impl<'ui> Line<'ui> {
fn new<C>(draw_list: &'ui WindowDrawList, p1: [f32; 2], p2: [f32; 2], c: C) -> Self
where
C: Into<ImColor>,
{
Self {
p1,
p2,
color: c.into(),
thickness: 1.0,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn build(self) {
unsafe {
sys::ImDrawList_AddLine(
self.draw_list.draw_list,
self.p1.into(),
self.p2.into(),
self.color.into(),
self.thickness,
)
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Rect<'ui> {
p1: [f32; 2],
p2: [f32; 2],
color: ImColor,
rounding: f32,
flags: ImDrawCornerFlags,
thickness: f32,
filled: bool,
draw_list: &'ui WindowDrawList<'ui>,
}
impl<'ui> Rect<'ui> {
fn new<C>(draw_list: &'ui WindowDrawList, p1: [f32; 2], p2: [f32; 2], c: C) -> Self
where
C: Into<ImColor>,
{
Self {
p1,
p2,
color: c.into(),
rounding: 0.0,
flags: ImDrawCornerFlags::All,
thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn rounding(mut self, rounding: f32) -> Self {
self.rounding = rounding;
self
}
pub fn round_top_left(mut self, value: bool) -> Self {
self.flags.set(ImDrawCornerFlags::TopLeft, value);
self
}
pub fn round_top_right(mut self, value: bool) -> Self {
self.flags.set(ImDrawCornerFlags::TopRight, value);
self
}
pub fn round_bot_left(mut self, value: bool) -> Self {
self.flags.set(ImDrawCornerFlags::BotLeft, value);
self
}
pub fn round_bot_right(mut self, value: bool) -> Self {
self.flags.set(ImDrawCornerFlags::BotRight, value);
self
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn build(self) {
if self.filled {
unsafe {
sys::ImDrawList_AddRectFilled(
self.draw_list.draw_list,
self.p1.into(),
self.p2.into(),
self.color.into(),
self.rounding,
self.flags.bits(),
);
}
} else {
unsafe {
sys::ImDrawList_AddRect(
self.draw_list.draw_list,
self.p1.into(),
self.p2.into(),
self.color.into(),
self.rounding,
self.flags.bits(),
self.thickness,
);
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Triangle<'ui> {
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
color: ImColor,
thickness: f32,
filled: bool,
draw_list: &'ui WindowDrawList<'ui>,
}
impl<'ui> Triangle<'ui> {
fn new<C>(
draw_list: &'ui WindowDrawList,
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
c: C,
) -> Self
where
C: Into<ImColor>,
{
Self {
p1,
p2,
p3,
color: c.into(),
thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn build(self) {
if self.filled {
unsafe {
sys::ImDrawList_AddTriangleFilled(
self.draw_list.draw_list,
self.p1.into(),
self.p2.into(),
self.p3.into(),
self.color.into(),
)
}
} else {
unsafe {
sys::ImDrawList_AddTriangle(
self.draw_list.draw_list,
self.p1.into(),
self.p2.into(),
self.p3.into(),
self.color.into(),
self.thickness,
)
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct Circle<'ui> {
center: [f32; 2],
radius: f32,
color: ImColor,
num_segments: u32,
thickness: f32,
filled: bool,
draw_list: &'ui WindowDrawList<'ui>,
}
impl<'ui> Circle<'ui> {
pub fn new<C>(draw_list: &'ui WindowDrawList, center: [f32; 2], radius: f32, color: C) -> Self
where
C: Into<ImColor>,
{
Self {
center,
radius,
color: color.into(),
num_segments: 12,
thickness: 1.0,
filled: false,
draw_list,
}
}
pub fn num_segments(mut self, num_segments: u32) -> Self {
self.num_segments = num_segments;
self
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn filled(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn build(self) {
if self.filled {
unsafe {
sys::ImDrawList_AddCircleFilled(
self.draw_list.draw_list,
self.center.into(),
self.radius,
self.color.into(),
self.num_segments as i32,
)
}
} else {
unsafe {
sys::ImDrawList_AddCircle(
self.draw_list.draw_list,
self.center.into(),
self.radius,
self.color.into(),
self.num_segments as i32,
self.thickness,
)
}
}
}
}
#[must_use = "should call .build() to draw the object"]
pub struct BezierCurve<'ui> {
pos0: [f32; 2],
cp0: [f32; 2],
pos1: [f32; 2],
cp1: [f32; 2],
color: ImColor,
thickness: f32,
num_segments: Option<u32>,
draw_list: &'ui WindowDrawList<'ui>,
}
impl<'ui> BezierCurve<'ui> {
fn new<C>(
draw_list: &'ui WindowDrawList,
pos0: [f32; 2],
cp0: [f32; 2],
cp1: [f32; 2],
pos1: [f32; 2],
c: C,
) -> Self
where
C: Into<ImColor>,
{
Self {
pos0,
cp0,
cp1,
pos1,
color: c.into(),
thickness: 1.0,
num_segments: None,
draw_list,
}
}
pub fn thickness(mut self, thickness: f32) -> Self {
self.thickness = thickness;
self
}
pub fn num_segments(mut self, num_segments: u32) -> Self {
self.num_segments = Some(num_segments);
self
}
pub fn build(self) {
unsafe {
sys::ImDrawList_AddBezierCurve(
self.draw_list.draw_list,
self.pos0.into(),
self.cp0.into(),
self.cp1.into(),
self.pos1.into(),
self.color.into(),
self.thickness,
self.num_segments.unwrap_or(0) as i32,
)
}
}
}