use super::super::control::*;
use super::super::binding_canvas::*;
use super::super::resource_manager::*;
use canvas::*;
use binding::*;
use std::sync::*;
pub struct VirtualCanvas {
canvas_resources: Arc<ResourceManager<BindingCanvas>>,
tiles: Binding<Vec<Vec<Resource<BindingCanvas>>>>,
top_left: Binding<(u32, u32)>,
grid_size: Binding<(u32, u32)>,
tile_size: Binding<(f32, f32)>,
draw_region: Arc<Fn(f32, f32) -> Box<Fn(&mut GraphicsPrimitives) -> ()+Send+Sync>+Send+Sync>,
control: BindRef<Control>
}
impl VirtualCanvas {
pub fn new<DrawRegion: Fn(f32, f32) -> Box<Fn(&mut GraphicsPrimitives) -> ()+Send+Sync>+Send+Sync+'static>(canvas_resources: Arc<ResourceManager<BindingCanvas>>, draw_region: DrawRegion) -> VirtualCanvas {
let tiles = bind(vec![]);
let top_left = bind((0, 0));
let grid_size = bind((0, 0));
let tile_size = bind((256.0, 256.0));
let control = Self::make_control(&tiles, &top_left, &tile_size);
VirtualCanvas {
canvas_resources: canvas_resources,
tiles: tiles,
top_left: top_left,
grid_size: grid_size,
tile_size: tile_size,
draw_region: Arc::new(draw_region),
control: control
}
}
pub fn control(&self) -> BindRef<Control> {
BindRef::clone(&self.control)
}
pub fn virtual_scroll(&self, tile_size: (f32, f32), top_left: (u32, u32), grid_size: (u32, u32)) {
if self.tile_size.get() != tile_size {
self.tiles.clone().set(vec![]);
self.tile_size.clone().set(tile_size);
}
if self.top_left.get() != top_left {
self.reorder_tiles(self.top_left.get(), top_left);
self.top_left.clone().set(top_left);
}
if self.grid_size.get() != grid_size {
self.resize_tiles(grid_size);
self.grid_size.clone().set(grid_size);
}
}
fn make_tile(&self, pos: (u32, u32), tile_size: (f32, f32)) -> Resource<BindingCanvas> {
let (xpos, ypos) = pos;
let (width, height) = tile_size;
let xpos = width * (xpos as f32);
let ypos = height * (ypos as f32);
let draw_region = (self.draw_region)(xpos, ypos);
let new_canvas = BindingCanvas::with_drawing(move |gc| {
(*draw_region)(gc);
});
self.canvas_resources.register(new_canvas)
}
fn resize_tiles(&self, new_grid_size: (u32, u32)) {
let (left, top) = self.top_left.get();
let (width, height) = new_grid_size;
let mut tiles = self.tiles.get();
let tile_size = self.tile_size.get();
tiles.truncate(height as usize);
let mut ypos = top;
for ref mut row in tiles.iter_mut() {
row.truncate(width as usize);
while row.len() < width as usize {
let xpos = left + row.len() as u32;
row.push(self.make_tile((xpos, ypos), tile_size));
}
ypos += 1;
}
let mut ypos = top;
while tiles.len() < height as usize {
let new_row = (0..width).into_iter()
.map(|x| left + x)
.map(|xpos| self.make_tile((xpos, ypos), tile_size))
.collect();
tiles.push(new_row);
ypos += 1;
}
self.tiles.clone().set(tiles);
}
fn reorder_tiles(&self, old_top_left: (u32, u32), new_top_left: (u32, u32)) {
let (old_left, old_top) = old_top_left;
let (new_left, new_top) = new_top_left;
let (size_x, size_y) = self.grid_size.get();
let old_tiles = self.tiles.get();
let tile_size = self.tile_size.get();
let mut new_tiles = vec![];
let (old_left, old_top) = (old_left as i32, old_top as i32);
let (new_left, new_top) = (new_left as i32, new_top as i32);
for y in 0..size_y {
let y = y as i32;
let mut this_row = vec![];
let old_y = y - (old_top - new_top);
for x in 0..size_x {
let x = x as i32;
let old_x = x - (old_left - new_left);
if old_y >= 0 && old_y < old_tiles.len() as i32 {
let old_row = &old_tiles[old_y as usize];
if old_x >= 0 && old_x < old_row.len() as i32 {
this_row.push(old_row[old_x as usize].clone())
} else {
this_row.push(self.make_tile(((x+new_left) as u32, (y+new_top) as u32), tile_size));
}
} else {
this_row.push(self.make_tile(((x+new_left) as u32, (y+new_top) as u32), tile_size));
}
}
new_tiles.push(this_row);
}
self.tiles.clone().set(new_tiles);
}
fn make_control(tiles: &Binding<Vec<Vec<Resource<BindingCanvas>>>>, top_left: &Binding<(u32, u32)>, tile_size: &Binding<(f32, f32)>) -> BindRef<Control> {
let tiles = Binding::clone(tiles);
let top_left = Binding::clone(top_left);
let tile_size = Binding::clone(tile_size);
BindRef::new(&computed(move || {
let (tile_x, tile_y) = tile_size.get();
let (left, top) = top_left.get();
let tiles = tiles.get();
let (left, top) = (left as f32, top as f32);
let (left, top) = (left*tile_x, top*tile_y);
let tile_controls: Vec<_> = tiles.iter()
.zip((0..tiles.len()).into_iter())
.map(|(row, ypos)| (row, (ypos as f32) * tile_y + top))
.map(|(row, ypos)| row.iter()
.zip((0..row.len()).into_iter())
.map(|(cell, xpos)| (cell, (xpos as f32) * tile_x + left))
.map(move |(cell, xpos)| Control::canvas()
.with(Bounds {
x1: Position::At(xpos),
y1: Position::At(ypos),
x2: Position::At(xpos+tile_x),
y2: Position::At(ypos+tile_y)
})
.with(cell.clone())
)
)
.flat_map(|row| row)
.collect();
Control::cropping_container()
.with(Bounds::fill_all())
.with(tile_controls)
}))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn control_is_initially_empty() {
let resource_manager = Arc::new(ResourceManager::new());
let virtual_canvas = VirtualCanvas::new(resource_manager, |_, _| { Box::new(|_| { }) });
let control = virtual_canvas.control();
assert!(control.get().subcomponents().unwrap().len() == 0);
}
#[test]
fn initial_grid_generates_correctly() {
let resource_manager = Arc::new(ResourceManager::new());
let virtual_canvas = VirtualCanvas::new(resource_manager, |_, _| { Box::new(|_| { }) });
let control = virtual_canvas.control();
virtual_canvas.virtual_scroll((128.0, 128.0), (0, 0), (6, 2));
assert!(control.get().subcomponents().unwrap().len() == 12);
}
#[test]
fn grid_scrolls_correctly() {
let resource_manager = Arc::new(ResourceManager::new());
let virtual_canvas = VirtualCanvas::new(resource_manager, |_, _| { Box::new(|_| { }) });
let control = virtual_canvas.control();
virtual_canvas.virtual_scroll((128.0, 128.0), (0, 0), (6, 2));
assert!(control.get().subcomponents().unwrap().len() == 12);
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().x1 == Position::At(0.0));
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().y1 == Position::At(0.0));
virtual_canvas.virtual_scroll((128.0, 128.0), (2, 4), (6, 2));
assert!(control.get().subcomponents().unwrap().len() == 12);
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().x1 == Position::At(256.0));
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().x2 == Position::At(384.0));
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().y1 == Position::At(512.0));
assert!(control.get().subcomponents().unwrap()[0].bounding_box().unwrap().y2 == Position::At(640.0));
}
}