use crate::presenter::Presenter;
use retroglyph_core::DrawCell;
use retroglyph_core::backend::{Cursor, Input, Output};
use retroglyph_core::event::{Event, MouseEvent, MouseEventKind};
use retroglyph_core::grid::Size;
use std::collections::VecDeque;
use std::time::Duration;
pub struct WindowBackend<P: Presenter> {
presenter: P,
events: VecDeque<Event>,
}
impl<P: Presenter> WindowBackend<P> {
#[must_use]
pub const fn new(presenter: P) -> Self {
Self {
presenter,
events: VecDeque::new(),
}
}
#[must_use]
pub const fn presenter(&self) -> &P {
&self.presenter
}
pub const fn presenter_mut(&mut self) -> &mut P {
&mut self.presenter
}
#[must_use]
pub fn into_presenter(self) -> P {
self.presenter
}
}
impl<P: Presenter> Output for WindowBackend<P> {
type Error = P::Error;
fn draw<'a, I>(&mut self, content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = DrawCell<'a>>,
{
self.presenter.draw(content)
}
fn draw_layers<'a, I>(&mut self, content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = DrawCell<'a>>,
{
self.presenter.draw_layers(content)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.presenter.flush()
}
fn size(&self) -> Size {
self.presenter.size()
}
fn clear(&mut self) -> Result<(), Self::Error> {
self.presenter.clear()
}
fn resize(&mut self, size: Size) {
self.presenter.resize(size);
}
fn needs_full_frame(&self) -> bool {
self.presenter.needs_full_frame()
}
fn composites_layers(&self) -> bool {
self.presenter.composites_layers()
}
}
impl<P: Presenter> Input for WindowBackend<P> {
fn poll_event(&mut self, _timeout: Duration) -> Option<Event> {
self.events.pop_front()
}
fn push_event(&mut self, event: Event) {
if let Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
}) = &event
&& let Some(
back @ Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
}),
) = self.events.back_mut()
{
*back = event;
return;
}
self.events.push_back(event);
}
}
impl<P: Presenter> Cursor for WindowBackend<P> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::presenter::WindowHandle;
use retroglyph_core::event::{KeyModifiers, MouseButton};
use retroglyph_core::grid::Pos;
use std::sync::Arc;
struct NullPresenter;
impl Output for NullPresenter {
type Error = core::convert::Infallible;
fn draw<'a, I>(&mut self, _content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = DrawCell<'a>>,
{
Ok(())
}
fn draw_layers<'a, I>(&mut self, _content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = DrawCell<'a>>,
{
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn size(&self) -> Size {
Size {
width: 4,
height: 2,
}
}
fn clear(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn resize(&mut self, _size: Size) {}
}
impl Presenter for NullPresenter {
type SurfaceError = core::convert::Infallible;
fn init_surface(
&mut self,
_window: Arc<dyn WindowHandle>,
) -> Result<(), Self::SurfaceError> {
Ok(())
}
fn resize_surface(&mut self, _width: u32, _height: u32) {}
fn present(&mut self) -> Result<(), Self::SurfaceError> {
Ok(())
}
fn cell_size(&self) -> (u32, u32) {
(8, 16)
}
}
fn moved(x: u16) -> Event {
Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
position: Pos { x, y: 0 },
pixel_position: None,
modifiers: KeyModifiers::NONE,
})
}
#[test]
fn consecutive_moved_events_coalesce_to_one() {
let mut backend = WindowBackend::new(NullPresenter);
for x in 0..1_000u16 {
backend.push_event(moved(x));
}
assert_eq!(backend.events.len(), 1);
assert_eq!(backend.poll_event(Duration::ZERO), Some(moved(999)));
assert_eq!(backend.poll_event(Duration::ZERO), None);
}
#[test]
fn non_moved_event_breaks_coalescing() {
let mut backend = WindowBackend::new(NullPresenter);
backend.push_event(moved(1));
backend.push_event(moved(2));
backend.push_event(Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
position: Pos { x: 2, y: 0 },
pixel_position: None,
modifiers: KeyModifiers::NONE,
}));
backend.push_event(moved(3));
assert_eq!(backend.events.len(), 3);
assert_eq!(backend.poll_event(Duration::ZERO), Some(moved(2)));
assert!(matches!(
backend.poll_event(Duration::ZERO),
Some(Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
..
}))
));
assert_eq!(backend.poll_event(Duration::ZERO), Some(moved(3)));
}
}