use crate::{predicate::ShapePredicate, widget};
#[cfg(feature = "click")]
use crate::event;
use egui::{epaint::Color32, Pos2, Vec2};
use std::{collections::btree_map::{BTreeMap, Entry}, sync::{Arc, Mutex}};
#[cfg(not(feature = "click"))]
use std::marker::PhantomData;
#[cfg(feature = "click")]
pub type ClickHandler<'a> = Box<dyn FnMut(event::ClickEvent) + 'a + Sync + Send>;
pub type ShapeMap = BTreeMap<u32, Arc<Mutex<dyn ShapePredicate>>>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PositionInfo {
pub pos: Pos2,
pub size: Vec2
}
impl PositionInfo {
pub fn new(pos: Pos2, size: Vec2) -> Self {
PositionInfo { pos, size }
}
}
#[derive(Clone)]
pub struct Canvas<'a> {
shapes: Arc<Mutex<ShapeMap>>,
new_id: u32,
background_color: Option<Color32>,
pos: Option<PositionInfo>,
#[cfg(feature = "click")]
click_handler: Arc<Mutex<Option<ClickHandler<'a>>>>,
#[cfg(feature = "click")]
has_click_handler: bool,
#[cfg(not(feature = "click"))]
_marker: PhantomData<&'a ()>
}
impl Canvas<'_> {
pub fn new(shapes: ShapeMap) -> Self {
Self {
shapes: Arc::new(Mutex::new(shapes)),
new_id: 0,
background_color: None,
pos: None,
#[cfg(feature = "click")]
click_handler: Arc::new(Mutex::new(None)),
#[cfg(feature = "click")]
has_click_handler: false,
#[cfg(not(feature = "click"))]
_marker: PhantomData
}
}
}
impl Canvas<'_> {
pub fn add(&mut self, pred: Arc<Mutex<dyn ShapePredicate>>) -> Option<u32> {
let res = self.new_id;
self.new_id = self.new_id.checked_add(1)?;
self.shapes.lock().expect(crate::LOCK_ERR_MSG).insert(res, pred);
Some(res)
}
pub fn has(&self, id: u32) -> bool {
self.shapes.lock().expect(crate::LOCK_ERR_MSG).contains_key(&id)
}
pub fn shape_count(&self) -> u32 {
self.shapes.lock().expect(crate::LOCK_ERR_MSG).len() as u32
}
pub fn has_shapes(&self) -> bool {
!self.shapes.lock().expect(crate::LOCK_ERR_MSG).is_empty()
}
pub fn set(&mut self, id: u32, pred: Arc<Mutex<dyn ShapePredicate>>) -> Option<Arc<Mutex<dyn ShapePredicate>>> {
match self.shapes.lock().expect(crate::LOCK_ERR_MSG).entry(id) {
Entry::Occupied(mut entry) => {
Some(entry.insert(pred))
},
Entry::Vacant(_) => None
}
}
pub fn remove(&mut self, id: u32) -> Option<Arc<Mutex<dyn ShapePredicate>>> {
self.shapes.lock().expect(crate::LOCK_ERR_MSG).remove(&id)
}
}
impl Canvas<'_> {
pub fn set_background(&mut self, color: Option<Color32>) {
self.background_color = color;
}
pub fn set_pos(&mut self, pos: Pos2, size: Vec2) {
self.pos = Some(PositionInfo::new(pos, size));
}
pub fn remove_pos(&mut self) {
self.pos = None;
}
pub fn has_background(&self) -> bool {
self.background_color.is_some()
}
pub fn has_pos(&self) -> bool {
self.pos.is_some()
}
}
#[cfg(feature = "click")]
impl<'a> Canvas<'a> {
pub fn set_click_handler(&mut self, handler: ClickHandler<'a>) {
*self.click_handler.lock().expect(crate::LOCK_ERR_MSG) = Some(handler);
self.has_click_handler = true;
}
pub fn has_click_handler(&self) -> bool {
self.has_click_handler
}
}
impl<'a> Canvas<'a> {
pub fn widget(&self, needless_click_sense: bool) -> widget::CanvasWidget<'a> {
widget::CanvasWidget::new(
self.shapes.clone(), self.background_color, self.pos, needless_click_sense,
#[cfg(feature = "click")] self.click_handler.clone(),
#[cfg(feature = "click")] self.has_click_handler
)
}
}
impl Default for Canvas<'_> {
fn default() -> Self {
BTreeMap::new().into()
}
}
impl<'a> From<Canvas<'a>> for ShapeMap {
fn from(value: Canvas<'a>) -> Self {
value.shapes.lock().expect(crate::LOCK_ERR_MSG).clone()
}
}
impl From<ShapeMap> for Canvas<'_> {
fn from(value: ShapeMap) -> Self {
let mut canvas = Self::new(value);
canvas.set_background(Some(Color32::WHITE));
canvas
}
}
#[cfg(feature = "click")]
impl Drop for Canvas<'_> {
fn drop(&mut self) {
*self.click_handler.lock().expect(crate::LOCK_ERR_MSG) = None;
}
}