egui_canvas 0.1.2

A TKinter-like canvas widget for egui.
Documentation
#[cfg(feature = "click")]
use crate::event;
use crate::holder;
use egui::{
    epaint::{Color32, CornerRadius, Rect, RectShape},
    Pos2, Response, Sense, Ui, Widget, WidgetInfo, WidgetType
};
#[cfg(feature = "click")]
use egui::PointerButton;
use std::{ops::Deref, sync::{Arc, Mutex}};
#[cfg(feature = "click")]
use std::ops::DerefMut;
#[cfg(not(feature = "click"))]
use std::marker::PhantomData;

pub struct CanvasWidget<'a> {
    shapes: Arc<Mutex<holder::ShapeMap>>,
    background_color: Option<Color32>,
    pos: Option<holder::PositionInfo>,
    needless_click_sense: bool,
    #[cfg(feature = "click")]
    click_handler: Arc<Mutex<Option<holder::ClickHandler<'a>>>>,
    #[cfg(feature = "click")]
    has_click_handler: bool,
    #[cfg(not(feature = "click"))]
    _marker: PhantomData<&'a ()>
}

#[cfg(not(feature = "click"))]
impl CanvasWidget<'_> {
    pub fn new(
        shapes: Arc<Mutex<holder::ShapeMap>>,
        background_color: Option<Color32>,
        pos: Option<holder::PositionInfo>,
        needless_click_sense: bool
    ) -> Self {
        Self {
            shapes, background_color, pos,
            needless_click_sense, _marker: PhantomData
        }
    }
}

#[cfg(feature = "click")]
impl<'a> CanvasWidget<'a> {
    pub fn new(
        shapes: Arc<Mutex<holder::ShapeMap>>,
        background_color: Option<Color32>,
        pos: Option<holder::PositionInfo>,
        needless_click_sense: bool,
        click_handler: Arc<Mutex<Option<holder::ClickHandler<'a>>>>,
        has_click_handler: bool
    ) -> Self {
        Self {
            shapes, background_color, pos, needless_click_sense,
            click_handler, has_click_handler
        }
    }
}

impl CanvasWidget<'_> {
    fn get_self_pos(&self, ui: &Ui) -> holder::PositionInfo {
        match self.pos {
            Some(pos) => pos,
            None => holder::PositionInfo::new(Pos2::ZERO, ui.available_size())
        }
    }

    #[cfg(feature = "click")]
    fn handle_click(&mut self, response: &Response, ui: &Ui) {
        if !self.has_click_handler { return; }
        let pos = response.interact_pointer_pos();
        if pos.is_none() { return; }
        let button = if response.clicked_by(PointerButton::Primary) {
            PointerButton::Primary
        } else if response.clicked_by(PointerButton::Secondary) {
            PointerButton::Secondary
        } else if response.clicked_by(PointerButton::Middle) {
            PointerButton::Middle
        } else if response.clicked_by(PointerButton::Extra1) {
            PointerButton::Extra1
        } else if response.clicked_by(PointerButton::Extra2) {
            PointerButton::Extra2
        } else { return; };
        let pos = pos.unwrap();
        let holder::PositionInfo{ pos: self_pos, size: self_size } = self.get_self_pos(ui);
        let self_other_corner = self_pos + self_size;
        if pos.x <= self_pos.x || pos.x >= self_other_corner.x ||
            pos.y <= self_pos.y || pos.y >= self_other_corner.y
        { return; }
        let mut lock = self.click_handler.lock().unwrap();
        let click_handler = lock.deref_mut().as_mut().unwrap();
        click_handler(event::ClickEvent::new(button, pos));
    }
}

impl Widget for CanvasWidget<'_> {
    #[cfg(not(feature = "click"))]
    fn ui(self, ui: &mut Ui) -> Response {
        let sense = if self.needless_click_sense {
            Sense::click()
        } else {
            Sense::hover()
        };
        let holder::PositionInfo { pos, size } = self.get_self_pos(ui);
        let rect = Rect { min: pos, max: pos + size };
        let response = ui.allocate_rect(rect, sense);
        response.widget_info(
            || WidgetInfo::new(WidgetType::Other)

        );
        if ui.is_visible() {
            if let Some(bg) = self.background_color {
                ui.painter().add(RectShape::filled(
                    rect, CornerRadius::ZERO, bg
                ));
            }
            for pred_arc in self.shapes.lock().expect(crate::LOCK_ERR_MSG).values() {
                let pred_lock = pred_arc.lock().unwrap();
                if let Some(shape) = pred_lock.deref().finalise(ui) {
                    ui.painter().add(shape);
                }
            }
        }
        response
    }

    #[cfg(feature = "click")]
    fn ui(mut self, ui: &mut Ui) -> Response {
        let sense = if self.has_click_handler || self.needless_click_sense {
            Sense::click()
        } else {
            Sense::hover()
        };
        let holder::PositionInfo { pos, size } = self.get_self_pos(ui);
        let rect = Rect { min: pos, max: pos + size };
        let response = ui.allocate_rect(rect, sense);
        response.widget_info(
            || WidgetInfo::new(WidgetType::Other)

        );
        if ui.is_visible() {
            self.handle_click(&response, ui);
            if let Some(bg) = self.background_color {
                ui.painter().add(RectShape::filled(
                    rect, CornerRadius::ZERO, bg
                ));
            }
            for pred_arc in self.shapes.lock().expect(crate::LOCK_ERR_MSG).values() {
                let pred_lock = pred_arc.lock().unwrap();
                if let Some(shape) = pred_lock.deref().finalise(ui) {
                    ui.painter().add(shape);
                }
            }
        }
        response
    }
}