gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
//! Presentation layer.
//!
//! The `Presentation` trait defines required methods for getting input and
//! displaying `View` data.
//!
//! A concrete `Presentation` type instance is a member of the main `Interface`
//! structure.
//!
//! Calling `Interface::update()` will run `Presentation::get_input()` and
//! pass the `Input` to the focused `Controller` component for handling.
//!
//! Any changes to `View` component data will result in `Display` events being
//! generated and calling `Interface::display()` will run
//! `Presentation::display_view()` with those `Display` values collected in the
//! last update.

use crate::{Application, Interface, Tree};
use crate::tree::NodeId;
use crate::interface::{view, Element, View};

pub mod headless;
pub use headless::Headless;

// graphics
// TODO: crossterm backend
#[cfg(feature="curses")]
#[cfg_attr(docsrs, doc(cfg(feature="curses")))]
pub mod curses;
#[cfg(feature="curses")]
#[cfg_attr(docsrs, doc(cfg(feature="curses")))]
pub use self::curses::Curses;
#[cfg(feature="opengl")]
#[cfg_attr(docsrs, doc(cfg(feature="opengl")))]
pub mod opengl;
#[cfg(feature="opengl")]
#[cfg_attr(docsrs, doc(cfg(feature="opengl")))]
pub use self::opengl::Opengl;
// audio
#[cfg(feature="fmod")]
#[cfg_attr(docsrs, doc(cfg(feature="fmod")))]
pub mod fmod;
#[cfg(feature="fmod")]
#[cfg_attr(docsrs, doc(cfg(feature="fmod")))]
pub use self::fmod::Fmod;

/// Implements a means of getting input and displaying view data
pub trait Presentation {
  /// Overridable constructor allowing the presentation backend to do custom
  /// initialization of the interface, e.g. initializing the root screen element of a
  /// graphical interface.
  fn make_interface <A : Application> () -> Interface <A, Self> where Self : Sized {
    Interface::with_root (Element::default())
  }
  fn with_root (root : View, root_id : NodeId) -> Self;
  fn get_input (&mut self, input_buffer : &mut Vec <view::Input>);
  fn display_view <V : AsRef <View>> (&mut self,
    view_tree      : &Tree <V>,
    display_values : std::vec::Drain <(NodeId, view::Display)>);
}

/// Marker trait for backends with graphics capabilities
pub trait Graphics : Presentation { }
/// Marker trait for backends with audio capabilities
pub trait Audio    : Presentation { }
/// Get the graphics component from a capable backend
pub trait HasGraphics <G : Graphics> {
  fn graphics (&mut self) -> &mut G;
}
/// A presentation backend with both graphics and audio components
// TODO: HasAudio trait ?
#[derive(Debug)]
#[expect(clippy::type_complexity)]
pub struct Composite <G : Graphics, A : Audio> {
  pub graphics    : G,
  pub audio       : A,
  display_buffers : (Vec <(NodeId, view::Display)>, Vec <(NodeId, view::Display)>)
}
impl <G, A> Presentation for Composite <G, A> where
  G : Graphics,
  A : Audio
{
  /// Uses the graphical presentation backend to create an initialized interface
  fn make_interface <APP : Application> () -> Interface <APP, Self> where Self : Sized {
    let graphical = G::make_interface();
    let root      = graphical.root_node().data().view.clone();
    let root_id   = graphical.root_id().clone();
    graphical.swap_presentation (|graphics| {
      let audio           = A::with_root (root, root_id);
      let display_buffers = (vec![], vec![]);
      Composite { graphics, audio, display_buffers }
    })
  }
  fn with_root (root : View, root_id : NodeId) -> Self {
    let graphics        = G::with_root (root.clone(), root_id.clone());
    let audio           = A::with_root (root, root_id);
    let display_buffers = (vec![], vec![]);
    Composite { graphics, audio, display_buffers }
  }
  fn get_input (&mut self, input_buffer : &mut Vec <view::Input>) {
    self.graphics.get_input (input_buffer);
    self.audio.get_input (input_buffer);
  }
  fn display_view <V : AsRef <View>> (&mut self,
    view_tree      : &Tree <V>,
    display_values : std::vec::Drain <(NodeId, view::Display)>
  ) {
    self.display_buffers.0 = display_values.collect();
    self.display_buffers.1 = self.display_buffers.0.clone();
    self.graphics.display_view (view_tree, self.display_buffers.0.drain(..));
    self.audio.display_view    (view_tree, self.display_buffers.1.drain(..));
  }
}

impl <G, A> HasGraphics <G> for Composite <G, A> where
  G : Graphics,
  A : Audio
{
  fn graphics (&mut self) -> &mut G {
    &mut self.graphics
  }
}

impl <G : Graphics> HasGraphics <G> for G {
  fn graphics (&mut self) -> &mut G {
    self
  }
}