blob: b416ba9c6f426fbdf894275b5fc5206fe1c38a44 [file] [log] [blame]
use crate::coord::Shift;
use crate::drawing::area::IntoDrawingArea;
use crate::drawing::backend::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind};
use crate::drawing::DrawingArea;
use crate::style::{Color, RGBAColor, TextStyle};
use std::collections::VecDeque;
pub struct MockedBackend {
height: u32,
width: u32,
init_count: u32,
pub draw_count: u32,
pub num_draw_pixel_call: u32,
pub num_draw_line_call: u32,
pub num_draw_rect_call: u32,
pub num_draw_circle_call: u32,
pub num_draw_text_call: u32,
pub num_draw_path_call: u32,
pub num_fill_polygon_call: u32,
check_draw_pixel: VecDeque<Box<dyn FnMut(RGBAColor, BackendCoord)>>,
check_draw_line: VecDeque<Box<dyn FnMut(RGBAColor, u32, BackendCoord, BackendCoord)>>,
check_draw_rect: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, BackendCoord)>>,
check_draw_path: VecDeque<Box<dyn FnMut(RGBAColor, u32, Vec<BackendCoord>)>>,
check_draw_circle: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, u32)>>,
check_draw_text: VecDeque<Box<dyn FnMut(RGBAColor, &str, f64, BackendCoord, &str)>>,
check_fill_polygon: VecDeque<Box<dyn FnMut(RGBAColor, Vec<BackendCoord>)>>,
drop_check: Option<Box<dyn FnMut(&Self)>>,
}
macro_rules! def_set_checker_func {
(drop_check, $($param:ty),*) => {
pub fn drop_check<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
self.drop_check = Some(Box::new(check));
self
}
};
($name:ident, $($param:ty),*) => {
pub fn $name<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self {
self.$name.push_back(Box::new(check));
self
}
}
}
impl MockedBackend {
pub fn new(width: u32, height: u32) -> Self {
MockedBackend {
height,
width,
init_count: 0,
draw_count: 0,
num_draw_pixel_call: 0,
num_draw_line_call: 0,
num_draw_rect_call: 0,
num_draw_circle_call: 0,
num_draw_text_call: 0,
num_draw_path_call: 0,
num_fill_polygon_call: 0,
check_draw_pixel: vec![].into(),
check_draw_line: vec![].into(),
check_draw_rect: vec![].into(),
check_draw_path: vec![].into(),
check_draw_circle: vec![].into(),
check_draw_text: vec![].into(),
check_fill_polygon: vec![].into(),
drop_check: None,
}
}
def_set_checker_func!(check_draw_pixel, RGBAColor, BackendCoord);
def_set_checker_func!(check_draw_line, RGBAColor, u32, BackendCoord, BackendCoord);
def_set_checker_func!(
check_draw_rect,
RGBAColor,
u32,
bool,
BackendCoord,
BackendCoord
);
def_set_checker_func!(check_draw_path, RGBAColor, u32, Vec<BackendCoord>);
def_set_checker_func!(check_draw_circle, RGBAColor, u32, bool, BackendCoord, u32);
def_set_checker_func!(check_draw_text, RGBAColor, &str, f64, BackendCoord, &str);
def_set_checker_func!(drop_check, &Self);
def_set_checker_func!(check_fill_polygon, RGBAColor, Vec<BackendCoord>);
fn check_before_draw(&mut self) {
self.draw_count += 1;
//assert_eq!(self.init_count, self.draw_count);
}
}
#[derive(Debug)]
pub struct MockedError;
impl std::fmt::Display for MockedError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "MockedError")
}
}
impl std::error::Error for MockedError {}
impl DrawingBackend for MockedBackend {
type ErrorType = MockedError;
fn get_size(&self) -> (u32, u32) {
(self.width, self.height)
}
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
self.init_count += 1;
Ok(())
}
fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
self.init_count = 0;
self.draw_count = 0;
Ok(())
}
fn draw_pixel(
&mut self,
point: BackendCoord,
color: &RGBAColor,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_pixel_call += 1;
let color = color.to_rgba();
if let Some(mut checker) = self.check_draw_pixel.pop_front() {
checker(color, point);
if self.check_draw_pixel.is_empty() {
self.check_draw_pixel.push_back(checker);
}
}
Ok(())
}
fn draw_line<S: BackendStyle>(
&mut self,
from: BackendCoord,
to: BackendCoord,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_line_call += 1;
let color = style.as_color().to_rgba();
if let Some(mut checker) = self.check_draw_line.pop_front() {
checker(color, style.stroke_width(), from, to);
if self.check_draw_line.is_empty() {
self.check_draw_line.push_back(checker);
}
}
Ok(())
}
fn draw_rect<S: BackendStyle>(
&mut self,
upper_left: BackendCoord,
bottom_right: BackendCoord,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_rect_call += 1;
let color = style.as_color().to_rgba();
if let Some(mut checker) = self.check_draw_rect.pop_front() {
checker(color, style.stroke_width(), fill, upper_left, bottom_right);
if self.check_draw_rect.is_empty() {
self.check_draw_rect.push_back(checker);
}
}
Ok(())
}
fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_path_call += 1;
let color = style.as_color().to_rgba();
if let Some(mut checker) = self.check_draw_path.pop_front() {
checker(color, style.stroke_width(), path.into_iter().collect());
if self.check_draw_path.is_empty() {
self.check_draw_path.push_back(checker);
}
}
Ok(())
}
fn draw_circle<S: BackendStyle>(
&mut self,
center: BackendCoord,
radius: u32,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_draw_circle_call += 1;
let color = style.as_color().to_rgba();
if let Some(mut checker) = self.check_draw_circle.pop_front() {
checker(color, style.stroke_width(), fill, center, radius);
if self.check_draw_circle.is_empty() {
self.check_draw_circle.push_back(checker);
}
}
Ok(())
}
fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
self.check_before_draw();
self.num_fill_polygon_call += 1;
let color = style.as_color().to_rgba();
if let Some(mut checker) = self.check_fill_polygon.pop_front() {
checker(color, path.into_iter().collect());
if self.check_fill_polygon.is_empty() {
self.check_fill_polygon.push_back(checker);
}
}
Ok(())
}
fn draw_text(
&mut self,
text: &str,
style: &TextStyle,
pos: BackendCoord,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let font = &style.font;
let color = &style.color;
self.check_before_draw();
self.num_draw_text_call += 1;
let color = color.to_rgba();
if let Some(mut checker) = self.check_draw_text.pop_front() {
checker(color, font.get_name(), font.get_size(), pos, text);
if self.check_draw_text.is_empty() {
self.check_draw_text.push_back(checker);
}
}
Ok(())
}
}
impl Drop for MockedBackend {
fn drop(&mut self) {
let mut temp = None;
std::mem::swap(&mut temp, &mut self.drop_check);
if let Some(mut checker) = temp {
checker(self);
}
}
}
pub fn create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>(
width: u32,
height: u32,
setup: F,
) -> DrawingArea<MockedBackend, Shift> {
let mut backend = MockedBackend::new(width, height);
setup(&mut backend);
backend.into_drawing_area()
}