use std::fmt;
use crate::{
DrawError, DrawObjectKey, DrawObjectLibrary, ObjectId, ScreenBuffer, SpriteRegistry,
error::AppError,
input_handler::manager::{SubscriptionMessage, TargetScreen},
terminal_buffer::CellDrawer,
};
use common_stdx::{Point, Rect};
pub type ScreenKey = usize;
pub mod area_rect;
pub use area_rect::{AreaPoint, AreaRect};
pub struct Screen {
layer: usize,
id: ScreenKey,
area: AreaRect,
pub terminal_size: (u16, u16),
pub draw_objects: Vec<ObjectId>,
on_screen_select_callback: Option<Box<dyn FnMut() + 'static + Send>>,
}
impl fmt::Debug for Screen {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Screen")
.field("layer", &self.layer)
.field("id", &self.id)
.field("area", &self.area)
.field("terminal_size", &self.terminal_size)
.field("draw_objects", &self.draw_objects)
.field(
"on_screen_select_callback",
&self.on_screen_select_callback.is_some(),
)
.finish()
}
}
impl Screen {
pub fn new(area: AreaRect, layer: usize, id: ScreenKey, terminal_size: (u16, u16)) -> Self {
Screen {
layer,
id,
area,
terminal_size,
draw_objects: Vec::new(),
on_screen_select_callback: None,
}
}
pub fn handle_input_message(
&mut self,
m: &SubscriptionMessage,
obj_library: &mut DrawObjectLibrary,
) -> Result<(), DrawError> {
for drawable in &self.draw_objects {
if let Some(d_o) = obj_library.get_mut(&DrawObjectKey {
screen_id: self.id,
object_id: *drawable,
}) {
let d = d_o.drawable.as_mut();
match m {
SubscriptionMessage::Key { msg, screen } => {
if screen.targeting(self.id) {
d.on_key_press(*msg)?;
}
d.on_any_key_press(*msg, *screen)?;
}
SubscriptionMessage::Mouse { msg, screen } => {
if screen.targeting(self.id) {
d.on_mousekey_press(*msg)?;
}
d.on_any_mousekey_press(*msg, *screen)?;
}
_ => {}
}
}
}
Ok(())
}
pub fn change_screen_area(&mut self, new_area: AreaRect) {
self.area = new_area;
}
pub fn add_on_screen_select(&mut self, callback: Box<dyn FnMut() + 'static + Send>) {
self.on_screen_select_callback = Some(callback);
}
pub fn on_screen_select(
&mut self,
obj_library: &mut DrawObjectLibrary,
) -> Result<(), AppError> {
if let Some(callback) = &mut self.on_screen_select_callback {
(callback)();
}
for object_id in &self.draw_objects {
if let Some(obj) = obj_library.get_mut(&DrawObjectKey {
screen_id: self.id,
object_id: *object_id,
}) {
obj.drawable
.on_screen_select(TargetScreen::Screen(self.id))?;
}
}
Ok(())
}
pub fn change_screen_area_contents(
&mut self,
obj_library: &mut DrawObjectLibrary,
sprites: &SpriteRegistry,
) {
let mut combined_bbox: Option<Rect<i32>> = None;
for object_id in &self.draw_objects {
let key = DrawObjectKey {
screen_id: self.id,
object_id: *object_id,
};
let obj = match obj_library.get_mut(&key) {
Some(obj) => obj,
None => continue,
};
let bdc = match obj.drawable.draw(sprites) {
Ok(bdc) => bdc,
Err(_) => continue,
};
let bbox = bdc.get_bounding_box().normalized();
combined_bbox = Some(match combined_bbox {
None => bbox,
Some(acc) => Rect {
p1: Point {
x: acc.p1.x.min(bbox.p1.x),
y: acc.p1.y.min(bbox.p1.y),
},
p2: Point {
x: acc.p2.x.max(bbox.p2.x),
y: acc.p2.y.max(bbox.p2.y),
},
},
});
}
if let Some(bbox) = combined_bbox {
self.area = AreaRect::FromPoints(AreaPoint::Point(bbox.p1), AreaPoint::Point(bbox.p2))
}
}
pub fn change_screen_layer(&mut self, new_layer: usize) {
self.layer = new_layer;
}
pub fn layer(&self) -> usize {
self.layer
}
pub fn register_drawable(&mut self, obj_id: ObjectId, obj_library: &DrawObjectLibrary) {
if !self.draw_objects.contains(&obj_id)
&& obj_library.all_objects.contains_key(&DrawObjectKey {
screen_id: self.id,
object_id: obj_id,
})
{
self.draw_objects.push(obj_id);
}
}
pub fn deregister_drawable(&mut self, obj_id: ObjectId) {
if let Some(pos) = self.draw_objects.iter().position(|&id| id == obj_id) {
self.draw_objects.remove(pos);
}
}
pub fn render_drawable<B>(
&mut self,
object_id: ObjectId,
screen_buffer: &mut B,
obj_library: &mut DrawObjectLibrary,
sprites: &SpriteRegistry,
) -> Result<(), DrawError>
where
B: ScreenBuffer,
B::Drawer: CellDrawer,
{
if !self.draw_objects.contains(&object_id) {
return Ok(());
}
let opt_obj = obj_library.get_mut(&DrawObjectKey {
screen_id: self.id,
object_id,
});
if let Some(obj) = opt_obj {
let rect = &self.area.area_to_rect(&self.terminal_size);
screen_buffer.add_to_buffer(obj, object_id, self.layer, rect, sprites)?;
} else {
return Err(DrawError::DrawableHandleNotFound {
screen_id: self.id,
obj_id: object_id,
});
};
Ok(())
}
pub fn rect(&self) -> Rect<i32> {
self.area.area_to_rect(&self.terminal_size)
}
pub fn remove_drawable<B>(
&mut self,
object_id: ObjectId,
screen_buffer: &mut B,
obj_library: &mut DrawObjectLibrary,
sprites: &SpriteRegistry,
) -> Result<(), DrawError>
where
B: ScreenBuffer,
B::Drawer: CellDrawer,
{
let opt_obj = obj_library.get_mut(&DrawObjectKey {
screen_id: self.id,
object_id,
});
if let Some(obj) = opt_obj {
let rect = &self.area.area_to_rect(&self.terminal_size);
screen_buffer.remove_from_buffer(obj, object_id, sprites, rect);
} else {
return Err(DrawError::DrawableHandleNotFound {
screen_id: self.id,
obj_id: object_id,
});
};
Ok(())
}
pub fn remove_all<B>(
&mut self,
screen_buffer: &mut B,
obj_library: &mut DrawObjectLibrary,
sprites: &SpriteRegistry,
) -> Result<(), DrawError>
where
B: ScreenBuffer,
B::Drawer: CellDrawer,
{
let obj_ids: Vec<_> = self.draw_objects.to_vec();
for obj_id in obj_ids {
self.remove_drawable(obj_id, screen_buffer, obj_library, sprites)?;
}
Ok(())
}
pub fn render_all<B>(
&mut self,
screen_buffer: &mut B,
obj_library: &mut DrawObjectLibrary,
sprites: &SpriteRegistry,
) -> Result<(), DrawError>
where
B: ScreenBuffer,
B::Drawer: CellDrawer,
{
let obj_ids: Vec<_> = self.draw_objects.to_vec();
for obj_id in obj_ids {
self.render_drawable(obj_id, screen_buffer, obj_library, sprites)?;
}
Ok(())
}
}