use core::fmt;
use retroglyph_core::app::{App, Launch, RunOptions};
use retroglyph_core::terminal::Terminal;
use crate::backend::WindowBackend;
use crate::presenter_builder::PresenterBuilder;
use crate::winit::EventLoopError;
use crate::winit::run::{WindowConfig, run_app_on};
#[derive(Debug, Clone)]
pub struct Windowed<B> {
builder: B,
title: String,
}
impl<B: PresenterBuilder> Windowed<B> {
pub fn new(builder: B, title: impl Into<String>) -> Self {
Self {
builder,
title: title.into(),
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum WindowedLaunchError<E> {
Build(E),
EventLoop(EventLoopError),
}
impl<E: fmt::Display> fmt::Display for WindowedLaunchError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Build(err) => write!(f, "failed to build the presenter: {err}"),
Self::EventLoop(err) => write!(f, "windowed event loop failed: {err}"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for WindowedLaunchError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Build(err) => Some(err),
Self::EventLoop(err) => Some(err),
}
}
}
impl<B> Launch for Windowed<B>
where
B: PresenterBuilder,
{
type Backend = WindowBackend<B::Presenter>;
type Error = WindowedLaunchError<B::Error>;
fn launch<A>(self, app: A, options: RunOptions) -> Result<(), Self::Error>
where
A: App<Self::Backend> + 'static,
{
let presenter = self
.builder
.build_presenter()
.map_err(WindowedLaunchError::Build)?;
let config =
WindowConfig::fit(&presenter, self.title, None, true).with_run_options(options);
let terminal = Terminal::new(WindowBackend::new(presenter));
run_app_on(config, terminal, app).map_err(WindowedLaunchError::EventLoop)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_error_displays_and_sources_the_inner_error() {
let err: WindowedLaunchError<std::io::Error> =
WindowedLaunchError::Build(std::io::Error::other("bad grid"));
assert!(err.to_string().contains("bad grid"));
assert!(std::error::Error::source(&err).is_some());
}
#[test]
fn event_loop_error_displays_and_sources_the_inner_error() {
let err: WindowedLaunchError<std::io::Error> =
WindowedLaunchError::EventLoop(EventLoopError::RecreationAttempt);
assert!(err.to_string().contains("windowed event loop failed"));
assert!(std::error::Error::source(&err).is_some());
}
}