use crate::sys;
use dear_imgui_rs::{with_scratch_txt, with_scratch_txt_two};
use std::borrow::Cow;
use std::os::raw::c_char;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StyleVar {
PlotDefaultSize = sys::ImPlotStyleVar_PlotDefaultSize as i32,
PlotMinSize = sys::ImPlotStyleVar_PlotMinSize as i32,
PlotBorderSize = sys::ImPlotStyleVar_PlotBorderSize as i32,
MinorAlpha = sys::ImPlotStyleVar_MinorAlpha as i32,
MajorTickLen = sys::ImPlotStyleVar_MajorTickLen as i32,
MinorTickLen = sys::ImPlotStyleVar_MinorTickLen as i32,
MajorTickSize = sys::ImPlotStyleVar_MajorTickSize as i32,
MinorTickSize = sys::ImPlotStyleVar_MinorTickSize as i32,
MajorGridSize = sys::ImPlotStyleVar_MajorGridSize as i32,
MinorGridSize = sys::ImPlotStyleVar_MinorGridSize as i32,
PlotPadding = sys::ImPlotStyleVar_PlotPadding as i32,
LabelPadding = sys::ImPlotStyleVar_LabelPadding as i32,
LegendPadding = sys::ImPlotStyleVar_LegendPadding as i32,
LegendInnerPadding = sys::ImPlotStyleVar_LegendInnerPadding as i32,
LegendSpacing = sys::ImPlotStyleVar_LegendSpacing as i32,
MousePosPadding = sys::ImPlotStyleVar_MousePosPadding as i32,
AnnotationPadding = sys::ImPlotStyleVar_AnnotationPadding as i32,
FitPadding = sys::ImPlotStyleVar_FitPadding as i32,
DigitalPadding = sys::ImPlotStyleVar_DigitalPadding as i32,
DigitalSpacing = sys::ImPlotStyleVar_DigitalSpacing as i32,
}
pub struct StyleVarToken {
was_popped: bool,
}
impl StyleVarToken {
pub fn pop(mut self) {
if self.was_popped {
panic!("Attempted to pop a style var token twice.");
}
self.was_popped = true;
unsafe {
sys::ImPlot_PopStyleVar(1);
}
}
}
impl Drop for StyleVarToken {
fn drop(&mut self) {
if !self.was_popped {
unsafe {
sys::ImPlot_PopStyleVar(1);
}
}
}
}
pub struct StyleColorToken {
was_popped: bool,
}
impl StyleColorToken {
pub fn pop(mut self) {
if self.was_popped {
panic!("Attempted to pop a style color token twice.");
}
self.was_popped = true;
unsafe {
sys::ImPlot_PopStyleColor(1);
}
}
}
impl Drop for StyleColorToken {
fn drop(&mut self) {
if !self.was_popped {
unsafe {
sys::ImPlot_PopStyleColor(1);
}
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct PlotItemArrayStyle<'a> {
line_colors: Option<Cow<'a, [u32]>>,
fill_colors: Option<Cow<'a, [u32]>>,
marker_sizes: Option<Cow<'a, [f32]>>,
marker_line_colors: Option<Cow<'a, [u32]>>,
marker_fill_colors: Option<Cow<'a, [u32]>>,
}
impl<'a> PlotItemArrayStyle<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn with_line_colors(mut self, colors: &'a [u32]) -> Self {
self.line_colors = Some(Cow::Borrowed(colors));
self
}
pub fn with_fill_colors(mut self, colors: &'a [u32]) -> Self {
self.fill_colors = Some(Cow::Borrowed(colors));
self
}
pub fn with_marker_sizes(mut self, sizes: &'a [f32]) -> Self {
self.marker_sizes = Some(Cow::Borrowed(sizes));
self
}
pub fn with_marker_line_colors(mut self, colors: &'a [u32]) -> Self {
self.marker_line_colors = Some(Cow::Borrowed(colors));
self
}
pub fn with_marker_fill_colors(mut self, colors: &'a [u32]) -> Self {
self.marker_fill_colors = Some(Cow::Borrowed(colors));
self
}
fn apply_to_spec(&self, spec: &mut sys::ImPlotSpec_c) {
spec.LineColors = self
.line_colors
.as_ref()
.map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
spec.FillColors = self
.fill_colors
.as_ref()
.map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
spec.MarkerSizes = self
.marker_sizes
.as_ref()
.map_or(std::ptr::null_mut(), |sizes| sizes.as_ptr() as *mut _);
spec.MarkerLineColors = self
.marker_line_colors
.as_ref()
.map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
spec.MarkerFillColors = self
.marker_fill_colors
.as_ref()
.map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
}
}
pub fn with_next_plot_item_array_style<'a, R>(
style: PlotItemArrayStyle<'a>,
f: impl FnOnce() -> R,
) -> R {
let previous = crate::plots::take_next_plot_spec();
let mut spec = previous.unwrap_or_else(crate::plots::default_plot_spec);
style.apply_to_spec(&mut spec);
crate::plots::set_next_plot_spec(Some(spec));
let out = f();
if crate::plots::take_next_plot_spec().is_some() {
crate::plots::set_next_plot_spec(previous);
}
out
}
pub fn push_style_var_f32(var: StyleVar, value: f32) -> StyleVarToken {
unsafe {
sys::ImPlot_PushStyleVar_Float(var as sys::ImPlotStyleVar, value);
}
StyleVarToken { was_popped: false }
}
pub fn push_style_var_i32(var: StyleVar, value: i32) -> StyleVarToken {
unsafe {
sys::ImPlot_PushStyleVar_Int(var as sys::ImPlotStyleVar, value);
}
StyleVarToken { was_popped: false }
}
pub fn push_style_var_vec2(var: StyleVar, value: [f32; 2]) -> StyleVarToken {
unsafe {
sys::ImPlot_PushStyleVar_Vec2(
var as sys::ImPlotStyleVar,
sys::ImVec2_c {
x: value[0],
y: value[1],
},
);
}
StyleVarToken { was_popped: false }
}
pub fn push_style_color(element: crate::PlotColorElement, color: [f32; 4]) -> StyleColorToken {
unsafe {
let r = (color[0] * 255.0) as u32;
let g = (color[1] * 255.0) as u32;
let b = (color[2] * 255.0) as u32;
let a = (color[3] * 255.0) as u32;
let color_u32 = (a << 24) | (b << 16) | (g << 8) | r;
sys::ImPlot_PushStyleColor_U32(element as sys::ImPlotCol, color_u32);
}
StyleColorToken { was_popped: false }
}
pub fn push_colormap(preset: crate::Colormap) {
unsafe {
sys::ImPlot_PushColormap_PlotColormap(preset as sys::ImPlotColormap);
}
}
pub fn pop_colormap(count: i32) {
unsafe {
sys::ImPlot_PopColormap(count);
}
}
pub fn add_colormap(name: &str, colors: &[sys::ImVec4], qualitative: bool) -> sys::ImPlotColormap {
assert!(!name.contains('\0'), "colormap name contained NUL");
let count = i32::try_from(colors.len()).expect("colormap contained too many colors");
with_scratch_txt(name, |ptr| unsafe {
sys::ImPlot_AddColormap_Vec4Ptr(ptr, colors.as_ptr(), count, qualitative)
as sys::ImPlotColormap
})
}
pub fn show_style_editor() {
unsafe { sys::ImPlot_ShowStyleEditor(std::ptr::null_mut()) }
}
pub fn show_style_selector(label: &str) -> bool {
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe { sys::ImPlot_ShowStyleSelector(ptr) })
}
pub fn show_colormap_selector(label: &str) -> bool {
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_ShowColormapSelector(ptr)
})
}
pub fn show_input_map_selector(label: &str) -> bool {
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_ShowInputMapSelector(ptr)
})
}
pub fn map_input_default() {
unsafe { sys::ImPlot_MapInputDefault(sys::ImPlot_GetInputMap()) }
}
pub fn map_input_reverse() {
unsafe { sys::ImPlot_MapInputReverse(sys::ImPlot_GetInputMap()) }
}
pub fn colormap_scale(
label: &str,
scale_min: f64,
scale_max: f64,
height: f32,
cmap: Option<sys::ImPlotColormap>,
) {
let label = if label.contains('\0') { "" } else { label };
let size = sys::ImVec2_c { x: 0.0, y: height };
let fmt_ptr: *const c_char = std::ptr::null();
let flags: i32 = 0; with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_ColormapScale(
ptr,
scale_min,
scale_max,
size,
fmt_ptr,
flags,
cmap.unwrap_or(0),
)
})
}
pub fn colormap_slider(
label: &str,
t: &mut f32,
out_color: &mut sys::ImVec4,
format: Option<&str>,
cmap: sys::ImPlotColormap,
) -> bool {
let label = if label.contains('\0') { "" } else { label };
let format = format.filter(|s| !s.contains('\0'));
match format {
Some(fmt) => with_scratch_txt_two(label, fmt, |label_ptr, fmt_ptr| unsafe {
sys::ImPlot_ColormapSlider(
label_ptr,
t as *mut f32,
out_color as *mut sys::ImVec4,
fmt_ptr,
cmap,
)
}),
None => with_scratch_txt(label, |label_ptr| unsafe {
sys::ImPlot_ColormapSlider(
label_ptr,
t as *mut f32,
out_color as *mut sys::ImVec4,
std::ptr::null(),
cmap,
)
}),
}
}
pub fn colormap_button(label: &str, size: [f32; 2], cmap: sys::ImPlotColormap) -> bool {
let label = if label.contains('\0') { "" } else { label };
let sz = sys::ImVec2_c {
x: size[0],
y: size[1],
};
with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_ColormapButton(ptr, sz, cmap)
})
}
#[cfg(test)]
mod tests {
use super::{PlotItemArrayStyle, with_next_plot_item_array_style};
#[test]
fn next_plot_item_array_style_is_consumed_by_next_spec() {
let line_colors = [0x01020304u32, 0x05060708];
let fill_colors = [0x11121314u32];
let marker_sizes = [2.0f32, 4.0, 8.0];
with_next_plot_item_array_style(
PlotItemArrayStyle::new()
.with_line_colors(&line_colors)
.with_fill_colors(&fill_colors)
.with_marker_sizes(&marker_sizes),
|| {
let spec = crate::plots::plot_spec_from(7, 3, 16);
assert_eq!(spec.Flags, 7);
assert_eq!(spec.Offset, 3);
assert_eq!(spec.Stride, 16);
assert_eq!(spec.LineColors, line_colors.as_ptr() as *mut _);
assert_eq!(spec.FillColors, fill_colors.as_ptr() as *mut _);
assert_eq!(spec.MarkerSizes, marker_sizes.as_ptr() as *mut _);
},
);
let spec = crate::plots::plot_spec_from(0, 0, crate::IMPLOT_AUTO);
assert!(spec.LineColors.is_null());
assert!(spec.FillColors.is_null());
assert!(spec.MarkerSizes.is_null());
}
#[test]
fn next_plot_item_array_style_is_restored_if_unused() {
let line_colors = [0xAABBCCDDu32];
with_next_plot_item_array_style(
PlotItemArrayStyle::new().with_line_colors(&line_colors),
|| {},
);
let spec = crate::plots::plot_spec_from(0, 0, crate::IMPLOT_AUTO);
assert!(spec.LineColors.is_null());
}
}